@@ -24,6 +24,7 @@ This is the current implementation inventory as of this snapshot of the repo. Us
| `heavyfetish` | `fetish-kink` | no | no | Direct media handling. |
| `heavyfetish` | `fetish-kink` | no | no | Direct media handling. |
| `hdporn92` | `mainstream-tube` | no | no | WordPress retrotube-theme aggregator for hdporn92.com (network studio releases — MyPervyFamily, MYLF, StepSiblings, …). Latest feed `/` (page 1) and `/page/N/` (page N≥2); search `/?s={query}` (page 1) and `/page/N/?s={query}` (page N≥2) — the path with the `s` parameter must come after the `page/N` segment, not before. Pagination is by `article.loop-video` cards; the post id comes from the `post-{N}` CSS class. Two-step enrichment (page URL + Referer-locked m3u8 in `formats`): detail page → `<IFRAME SRC="https://morencius.com/embed/{id}">` (case-insensitive; the page emits an uppercase `<IFRAME SRC=...>` tag) → morencius embed → embedded `eval(function(p,a,c,k,e,d){…}(p,a,c,k.split('|')))` P.A.C.K.E.R. obfuscation block → deobfuscate in pure Rust (reverse-iterate the `c`-word `|`-delimited dictionary, base-`a` numeric tokens, `\b<token>\b` regex substitution) → `"hls4":"<value>"` field in the decoded JS gives the morencius stream URL `https://morencius.com/stream/{token}/{secret}/{expiry_ts}/{file_id}/master.m3u8`; `duration:"<value>"` gives the runtime. `video.url` is the hdporn92 page (no yt-dlp extractor exists for the site); `formats[0]` is the morencius `m3u8` carrying `Referer: https://morencius.com/embed/{id}` + a desktop Chrome `User-Agent` — these headers must travel with the manifest, sub-playlists, and segment requests. **Known limitation:** the morencius m3u8 path contains an `expiry_ts` segment (~12h from page render) — the cache TTL (`cacheDuration: 1800`, same as fyptt) caps the staleness window. Real playback still requires the webview to click through the morencius ad overlay; the manifest itself resolves to a 1280x720 HLS variant via `yt-dlp -j --referer <embed-url>`. No `/api/uploaders` (no uploader identity on the listing). |
| `hdporn92` | `mainstream-tube` | no | no | WordPress retrotube-theme aggregator for hdporn92.com (network studio releases — MyPervyFamily, MYLF, StepSiblings, …). Latest feed `/` (page 1) and `/page/N/` (page N≥2); search `/?s={query}` (page 1) and `/page/N/?s={query}` (page N≥2) — the path with the `s` parameter must come after the `page/N` segment, not before. Pagination is by `article.loop-video` cards; the post id comes from the `post-{N}` CSS class. Two-step enrichment (page URL + Referer-locked m3u8 in `formats`): detail page → `<IFRAME SRC="https://morencius.com/embed/{id}">` (case-insensitive; the page emits an uppercase `<IFRAME SRC=...>` tag) → morencius embed → embedded `eval(function(p,a,c,k,e,d){…}(p,a,c,k.split('|')))` P.A.C.K.E.R. obfuscation block → deobfuscate in pure Rust (reverse-iterate the `c`-word `|`-delimited dictionary, base-`a` numeric tokens, `\b<token>\b` regex substitution) → `"hls4":"<value>"` field in the decoded JS gives the morencius stream URL `https://morencius.com/stream/{token}/{secret}/{expiry_ts}/{file_id}/master.m3u8`; `duration:"<value>"` gives the runtime. `video.url` is the hdporn92 page (no yt-dlp extractor exists for the site); `formats[0]` is the morencius `m3u8` carrying `Referer: https://morencius.com/embed/{id}` + a desktop Chrome `User-Agent` — these headers must travel with the manifest, sub-playlists, and segment requests. **Known limitation:** the morencius m3u8 path contains an `expiry_ts` segment (~12h from page render) — the cache TTL (`cacheDuration: 1800`, same as fyptt) caps the staleness window. Real playback still requires the webview to click through the morencius ad overlay; the manifest itself resolves to a 1280x720 HLS variant via `yt-dlp -j --referer <embed-url>`. No `/api/uploaders` (no uploader identity on the listing). |
| `hentaihaven` | `hentai-animation` | no | no | HTML scraper for hentaihaven.xxx (WordPress/Madara theme), Cloudflare-protected so the provider is gated behind `FLARE_URL` in `skip_reason_for_provider` (mod.rs); the shared requester clears CF directly (wreq Firefox136 emulation currently passes for the listing/search/watch/episode/`player.php` GETs) and falls back to Jina/FlareSolverr. Latest feed `/hentai/page/{N}/`, search `/?s={query}` (search is single-page — page>1 returns empty); listing/search cards link to series watch pages `https://hentaihaven.xxx/watch/{slug}/`. Per-series media resolution (the UUID exists nowhere in page HTML, so enrichment is unavoidable): watch page → episode links `…/watch/{slug}/episode-K` (in `manga-chapters-holder`) → episode page → `<iframe src="…/wp-content/plugins/player-logic/player.php?data=…">` → `player.php` → `<meta name="x-secure-token" content="sha512-…">` → decode token (strip `sha512-`, then 3× of rot13→base64-decode, then `JSON.parse`) → `{en, iv, uri, hot_domains, …}` → POST `…/wp-content/plugins/player-logic/api.php` with `action=zarat_get_data_player_ajax&a={en}&b={iv}` (urlencoded; this one POST uses a dedicated `wreq` Chrome137 client, not the shared requester) → `{"status":true,"data":{"sources":[{"src":"…m3u8"}],"isOctopus":bool}}`. A multi-episode series collapses into one `VideoItem` titled `"… (N Episodes)"` with one `m3u8``VideoFormat` per episode (`format_note`/`format_id` = "Episode K"); each format carries `Referer`/`Origin: https://hentaihaven.xxx` + a Firefox `User-Agent`. `video.url` is the `watch/{slug}/` page (no yt-dlp extractor exists for the site, so `formats` are populated rather than relying on `video.url`). Two CDN shapes are returned: newer content-addressed `octopusmanifest.org/{uuid}/playlist.m3u8` (no token, portable across IPs) and older signed `master-lengs.org/api/v3/hh/{slug}/master.m3u8?hash=…` (~2.5h). **Gotcha:** both CDNs (same IP) aggressively per-IP rate-limit/ban with a TCP RST on 80/443 once tripped — looks like "host down" but is an IP ban; browsers play fine over HTTP/3 (QUIC) while TCP clients (curl/yt-dlp/wreq) get refused, so segment fetches can fail from a tripped IP even though the manifest URL is valid. Tags from the series "Genre(s)" block; `views` from the "Viewed … Total" counter; thumbnails (`img.hentaihaven.xxx`) load directly (no proxy/referer). Resolution is slow (each listing page = ~25 series × multi-episode player-API calls), so the provider is DB-first: it fetches the listing once for the ordered watch URLs, serves already-resolved `VideoItem`s from the `videos` SQLite table instantly (`db::upsert_video` to avoid duplicate-row staleness), and `spawn_refresh`es the whole page in the background (in-memory `VideoCache` soft-TTL 1h / hard-TTL 24h, per-listing in-flight guard). No `/api/uploaders` (no uploader identity), no proxy. |
| `hentaihaven` | `hentai-animation` | no | no | HTML scraper for hentaihaven.xxx (WordPress/Madara theme), Cloudflare-protected so the provider is gated behind `FLARE_URL` in `skip_reason_for_provider` (mod.rs); the shared requester clears CF directly (wreq Firefox136 emulation currently passes for the listing/search/watch/episode/`player.php` GETs) and falls back to Jina/FlareSolverr. Latest feed `/hentai/page/{N}/`, search `/?s={query}` (search is single-page — page>1 returns empty); listing/search cards link to series watch pages `https://hentaihaven.xxx/watch/{slug}/`. Per-series media resolution (the UUID exists nowhere in page HTML, so enrichment is unavoidable): watch page → episode links `…/watch/{slug}/episode-K` (in `manga-chapters-holder`) → episode page → `<iframe src="…/wp-content/plugins/player-logic/player.php?data=…">` → `player.php` → `<meta name="x-secure-token" content="sha512-…">` → decode token (strip `sha512-`, then 3× of rot13→base64-decode, then `JSON.parse`) → `{en, iv, uri, hot_domains, …}` → POST `…/wp-content/plugins/player-logic/api.php` with `action=zarat_get_data_player_ajax&a={en}&b={iv}` (urlencoded; this one POST uses a dedicated `wreq` Chrome137 client, not the shared requester) → `{"status":true,"data":{"sources":[{"src":"…m3u8"}],"isOctopus":bool}}`. A multi-episode series collapses into one `VideoItem` titled `"… (N Episodes)"` with one `m3u8``VideoFormat` per episode (`format_note`/`format_id` = "Episode K"); each format carries `Referer`/`Origin: https://hentaihaven.xxx` + a Firefox `User-Agent`. `video.url` is the `watch/{slug}/` page (no yt-dlp extractor exists for the site, so `formats` are populated rather than relying on `video.url`). Two CDN shapes are returned: newer content-addressed `octopusmanifest.org/{uuid}/playlist.m3u8` (no token, portable across IPs) and older signed `master-lengs.org/api/v3/hh/{slug}/master.m3u8?hash=…` (~2.5h). **Gotcha:** both CDNs (same IP) aggressively per-IP rate-limit/ban with a TCP RST on 80/443 once tripped — looks like "host down" but is an IP ban; browsers play fine over HTTP/3 (QUIC) while TCP clients (curl/yt-dlp/wreq) get refused, so segment fetches can fail from a tripped IP even though the manifest URL is valid. Tags from the series "Genre(s)" block; `views` from the "Viewed … Total" counter; thumbnails (`img.hentaihaven.xxx`) load directly (no proxy/referer). Resolution is slow (each listing page = ~25 series × multi-episode player-API calls), so the provider is DB-first: it fetches the listing once for the ordered watch URLs, serves already-resolved `VideoItem`s from the `videos` SQLite table instantly (`db::upsert_video` to avoid duplicate-row staleness), and `spawn_refresh`es the whole page in the background (in-memory `VideoCache` soft-TTL 1h / hard-TTL 24h, per-listing in-flight guard). No `/api/uploaders` (no uploader identity), no proxy. |
| `hentaimama` | `hentai-animation` | no | no | WordPress/DooPlay-theme provider for hentaimama.io (series `/tvshows/{slug}/` each holding numbered `/episodes/{slug}-episode-N/` posts). Latest feed is the `episodes` post-type archive: `/episodes/` (page N: `/episodes/page/N/`), 24 cards/page, each already carrying the numeric post id (`id="post-{N}"`/`rel="{N}"`), title (`div.season_m``span.b`+`span.c`, joined with a space — the two spans are adjacent text nodes with no whitespace between them), thumb, and rating, so the latest feed needs only one more request per item (see media resolution below), no page-2 detail fetch. Search (`/?s=query`, page N: `/page/N/?s=query`), genre archives (`/genre/{slug}/`, 118+ curated genres background-loaded from `a.genreitem` on `/genres-filter/` into the `categories` filter option, sanitized out of `/api/status` like other providers' catalogs but honored in `/api/videos`; bare keywords matching a loaded genre title route here, otherwise `genre:`/`cat:`/`category:` prefixes), and studio archives (`/studio/{slug}/` via `studio:`/`uploader:` prefix, naive-slugified — no background catalog) all only expose **series** cards (`article.series-card` / `a.sc-poster`), not individual episodes, so each match is resolved to its single most-recent episode (fetch the series page's `a.dt-se-item` list, take the last href, then resolve that episode page) — richer results (tags, rating, exact title, `uploadedAt`) than the latest feed, at the cost of two extra requests instead of one. Media resolution (same for every path): `POST /wp-admin/admin-ajax.php` with `action=get_player_contents&a={post_id}&i=1` returns a JSON array of iframe-HTML strings; the populated slot's `src` is `hentaimama.io/?dt_embed=rtmp&p={base64}&ep={id}` where the base64 `p` param decodes directly to the storage path (e.g. `P/{file}.mp4`) on a **second, unrelated mirror host**`gdvid.info` — so the direct, non-expiring, Referer-free `https://gdvid.info/{path}` format URL is built locally from the AJAX response with no extra fetch; a `dt_embed=cu` fallback mirror (`na-01.javprovider.com`, signed/expiring `?st=&e=` token) exists but is only used as a last resort (fetch the iframe `src` page itself and pull the jwplayer `file:` value) if the primary parse fails, since it needs an extra request. `video.url` is the episode page (not yt-dlp-resolvable — the player loads via the AJAX handshake, not static HTML), so `formats[0]` carries the `gdvid.info` mp4. Thumbnails (`wp-content/uploads/...webp`) load directly, no proxy/Referer. No `/api/uploaders` (studio pages exist but have no per-provider profile schema confirmed; no background studio catalog for the same reason). No proxy needed. |
| `hentaitv` | `hentai-animation` | no | yes | Next.js hentai site (hentai.tv) backed by a clean JSON API: `GET /api/browse?page=N&sort=<Label>&genres=<ExactName>` (`{videos:[28],total,pages}`, real pagination) and `GET /api/search?q=Q` (`{videos:[...]}`, single-page — `page` is ignored, so page>1 returns empty). Unlike `animeidhentai`, browse honors both `sort` (labels `Most Recent`/`Most Viewed`/`Trending`, mapped from option ids `new`/`views`/`trending`) and `genres` (the **exact case-sensitive** stored genre name, e.g. `Big Boobs`, `incest`), so genre archives go through `/api/browse?genres=` and paginate. The 68-genre catalogue (exact names) is background-loaded from the `/browse` page HTML (`"genres":[{"name","count"}]`, not exposed by the JSON API) and powers the `categories` filter plus keyword→genre routing. Each episode JSON has `slug`, `title`/`ep`, `tags[]`, `views`, `rating` (0-10 → ×10), `duration` ("MM:SS"), `brand` (studio → `uploader`), `thumb`/`backdrop`/`cover` (relative, served from `hentai.tv/uploads/...`, no referer), and `embedUrl=https://nhplayer.com/v/{embedId}/`. `video.url` is the reachable watch page `https://hentai.tv/hentai/{slug}`; `genre:`/`cat:`/`category:` prefixes and bare keywords that exactly match a genre route to the genre archive, everything else to search. Playback shares the **same nhplayer→`r2.1hanime.com` signed-CDN backend as `animeidhentai`**: `/proxy/hentaitv/{embedId}.mp4` is a redirect proxy that replicates nhplayer's PoW+DOM challenge (`player.php`→`player-core-v2.php`→`get-video-url-v2.php`, SHA-256-first-byte-zero PoW, ≥700ms dwell, fixed fingerprint) to mint a signed `?verify=<ts>-<sig>` URL — HEAD→200, GET→302 to the CDN URL (cached 150s). The CF wall is JA3-based not IP-based, so the signed URL is verifiable from anywhere with `yt-dlp --impersonate chrome` even though plain `curl`/`wreq` get 403. `src/proxies/hentaitv.rs` is a near-copy of `src/proxies/animeidhentai.rs` (only `SITE_REFERER` differs). No `/api/uploaders` (brand is studio-only). |
| `hentaitv` | `hentai-animation` | no | yes | Next.js hentai site (hentai.tv) backed by a clean JSON API: `GET /api/browse?page=N&sort=<Label>&genres=<ExactName>` (`{videos:[28],total,pages}`, real pagination) and `GET /api/search?q=Q` (`{videos:[...]}`, single-page — `page` is ignored, so page>1 returns empty). Unlike `animeidhentai`, browse honors both `sort` (labels `Most Recent`/`Most Viewed`/`Trending`, mapped from option ids `new`/`views`/`trending`) and `genres` (the **exact case-sensitive** stored genre name, e.g. `Big Boobs`, `incest`), so genre archives go through `/api/browse?genres=` and paginate. The 68-genre catalogue (exact names) is background-loaded from the `/browse` page HTML (`"genres":[{"name","count"}]`, not exposed by the JSON API) and powers the `categories` filter plus keyword→genre routing. Each episode JSON has `slug`, `title`/`ep`, `tags[]`, `views`, `rating` (0-10 → ×10), `duration` ("MM:SS"), `brand` (studio → `uploader`), `thumb`/`backdrop`/`cover` (relative, served from `hentai.tv/uploads/...`, no referer), and `embedUrl=https://nhplayer.com/v/{embedId}/`. `video.url` is the reachable watch page `https://hentai.tv/hentai/{slug}`; `genre:`/`cat:`/`category:` prefixes and bare keywords that exactly match a genre route to the genre archive, everything else to search. Playback shares the **same nhplayer→`r2.1hanime.com` signed-CDN backend as `animeidhentai`**: `/proxy/hentaitv/{embedId}.mp4` is a redirect proxy that replicates nhplayer's PoW+DOM challenge (`player.php`→`player-core-v2.php`→`get-video-url-v2.php`, SHA-256-first-byte-zero PoW, ≥700ms dwell, fixed fingerprint) to mint a signed `?verify=<ts>-<sig>` URL — HEAD→200, GET→302 to the CDN URL (cached 150s). The CF wall is JA3-based not IP-based, so the signed URL is verifiable from anywhere with `yt-dlp --impersonate chrome` even though plain `curl`/`wreq` get 403. `src/proxies/hentaitv.rs` is a near-copy of `src/proxies/animeidhentai.rs` (only `SITE_REFERER` differs). No `/api/uploaders` (brand is studio-only). |
| `homoxxx` | `gay-male` | no | no | Gay category grouping example. |
| `homoxxx` | `gay-male` | no | no | Gay category grouping example. |
| `hotbunny` | `ai` | no | yes (thumbs only) | JSON-API provider for hotbunny.ai (AI-generated hentai community). Feeds: `GET /api/post/feed?limit=N` (hot), `/api/post/feed/popular`, `/api/post/feed/recents`, `/api/post/category/{cuid}?limit=N`, `/api/post/search?query=Q&limit=N`, `/api/post/author/{name}/all?limit=N`. Pagination: fetch `page × per_page` items in one call and slice (cursor-based API, but limit works up to 200 total). Key fields: `id`, `imageUrl` (relative thumbnail path on CDN), `authorName`, `likes` (→ `views`), `sharedVideos[].videoUrl` (first video path, relative to CDN), `width`/`height`, `galleries[].label` (used as title prefix). 15 hardcoded categories from `/api/categories` (stable CUIDs). `video.url` is the post page `https://hotbunny.ai/post/{id}` (React SPA, not yt-dlp-resolvable); `formats[0].url` = `https://assets.hotbunny.ai/{sharedVideos[0].videoUrl}` with `Referer: https://hotbunny.ai/` header. `assets.hotbunny.ai` is Cloudflare bot-management protected (returns 404 to plain curl/yt-dlp; iOS AVFoundation passes with real Apple TLS — no proxy wrapping needed for video). Thumbnails proxied via `/proxy/hotbunny-thumb/...` to inject the required `Referer`. Supports `uploader:`/`author:`, `cat:`/`category:`/`#tag` query shortcuts, `categories` filter option, and `sort` option (feed/popular/recent). |
| `hotbunny` | `ai` | no | yes (thumbs only) | JSON-API provider for hotbunny.ai (AI-generated hentai community). Feeds: `GET /api/post/feed?limit=N` (hot), `/api/post/feed/popular`, `/api/post/feed/recents`, `/api/post/category/{cuid}?limit=N`, `/api/post/search?query=Q&limit=N`, `/api/post/author/{name}/all?limit=N`. Pagination: fetch `page × per_page` items in one call and slice (cursor-based API, but limit works up to 200 total). Key fields: `id`, `imageUrl` (relative thumbnail path on CDN), `authorName`, `likes` (→ `views`), `sharedVideos[].videoUrl` (first video path, relative to CDN), `width`/`height`, `galleries[].label` (used as title prefix). 15 hardcoded categories from `/api/categories` (stable CUIDs). `video.url` is the post page `https://hotbunny.ai/post/{id}` (React SPA, not yt-dlp-resolvable); `formats[0].url` = `https://assets.hotbunny.ai/{sharedVideos[0].videoUrl}` with `Referer: https://hotbunny.ai/` header. `assets.hotbunny.ai` is Cloudflare bot-management protected (returns 404 to plain curl/yt-dlp; iOS AVFoundation passes with real Apple TLS — no proxy wrapping needed for video). Thumbnails proxied via `/proxy/hotbunny-thumb/...` to inject the required `Referer`. Supports `uploader:`/`author:`, `cat:`/`category:`/`#tag` query shortcuts, `categories` filter option, and `sort` option (feed/popular/recent). |
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.