decorate_channel ignored every provider-declared ytdlpCommand except
pimpbunny's. Send the declared command for freepornvideosxxx and missav,
where plain yt-dlp is verified to fail; other channels stay unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
Raw requester calls did not adopt the remembered FlareSolverr user agent,
so a RedGifs token minted under it was rejected on every gif lookup.
Tag/creator pages that mix non-RedGifs sources are refilled from later
cursor batches instead of coming back empty.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
The thumb proxy dropped the preview query string, rejected extensionless
getVideoPreview paths and warmed up through the slow fallback chain on
every image. check.py now verifies that a proxied video.url resolves
instead of failing it outright.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
xxdbx assets are now /id.jpg, not //host/..; prefixing https: produced
https:/id.jpg. cumlouder's unknown-tag 404 went through the requester's
mirror/FlareSolverr fallbacks (146s); read the raw response and return
an empty list at once while still retrying 5xx.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
Deleted lulustream files stayed listed and 502'd. myvidplay embeds lost
their host and were resolved against lulustream; route them through the
doodstream proxy (now allowing myvidplay/playmogo) with the host kept.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
The site serves one results page per searchgate POST, so later searches
and page 2 returned nothing on the shared cookie session. Use an isolated
session per (query, page). check.py now retries a refused HEAD (403/404/
405/501) with a ranged GET before reporting a URL as unreachable.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
Sponsored channel cards carry a stale data-video-id that 404s; use the
card link target. The signed pix-cdn77 thumbnail hosts 403 without a
Referer, so route them (tube8, ph-shorties) through a new host-restricted
aylo-thumb proxy. Also correct the pornhub-thumb route that sat under a
doubled /proxy prefix.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
The API ignores page= (every page 2+ repeated page 1), searches shared a
single cache entry, image-only posts were listed as unplayable videos, and
gzip/br bodies were not decoded because wreq had no decompression features.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
r.jina.ai now answers 403/429, so every request returned nothing. The
site serves plain HTML to the emulating client; parse cards, detail rows
and option lists from HTML, and return empty for no-hit searches (the
site renders a recommendation tray under the notice).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
The site moved to article.video-card; the old front-video-card parser found
nothing and the unbounded detail fan-out timed out. The page URL now
resolves natively in yt-dlp, so it is returned directly.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
- Implement fullporner.com provider with search, pagination, categories, pornstars
- Add xiaoshenke.net proxy for Cloudflare-protected embed videos
- Proxy fetches signed URLs via FlareSolverr and streams video content
- Fix quality bitmask mapping (1=360p, 2=480p, 4=720p, 8=1080p)
- Support HEAD/GET/Range for video streaming
Co-Authored-By: Claude Code <noreply@anthropic.com>
Beeg's API now returns file.id as a number (u64) instead of a string.
The code expected a string, causing all videos to be skipped.
Fixed by accepting both string and numeric file.id values.
Also updated to handle the new signed HLS URL flow:
- Fetch play_url per video to get signed manifest
- Parse key=xxx,end=xxx,limit=xxx/path response format
- Construct HLS URL with required query parameters
- Add Referer header for playback
Co-Authored-By: Claude Code <noreply@anthropic.com>
- coomer.rs: only emit a `formats` entry for actual video extensions.
Image-only posts (very common - many creators post photos, not just
videos) were getting a `formats[0]` pointing at a static jpg/png, which
a player would try and fail to stream as video; the preview URL already
covers showing the image.
- coomer.rs tests: fix stale fixtures that baked a `/data/` prefix into
`file.path`, which doesn't match the real API's path shape (the code
prepends `/data/` itself) and was doubling it into `/data/data/...`.
This was masking the formats bug above until the fixture was corrected.
- check.py: coomer.st/data/... 302s to a load-balanced nN.coomer.st CDN
node; every node tested (n1/n2/n3) is a hard connect-timeout from this
sandbox's egress while coomer.st itself is a normal 200 - the same
IP-reputation-style CDN block already documented for ripnsfw's
doodstream/lulustream edges. Added `.coomer.st` to the CF-protected
suffix allowlist, and made the url/thumb checks follow redirects before
testing it (previously only the format-url check did).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTqf6orbHZ9rFFpVpcgzcR
- wowxxx: site migrated wow.xxx -> wowxxx.to; update BASE_URL and stop
pinning Version::HTTP_11, which mismatched the TLS handshake's h2 ALPN
and got scored as a bot (same root cause already fixed for jable.tv)
- porndish: recognize playmogo.com iframes as the same Doodstream-clone
embed protocol as myvidplay.com (confirmed live) instead of falling
through to a naive branch that pointed formats[0] at the unresolved,
Cloudflare-protected iframe URL; kept in sync in both providers/porndish.rs
and proxies/porndish.rs since single-provider builds can't share code
across that boundary
- noodlemagazine: entity-decode the thumb URL pulled from a raw
data-original="..." attribute - undecoded `&` in the query string
was getting the CDN to 400 the request
- requester.rs: cap the previously-unbounded HTTP 429 retry loop at 3
attempts before falling through to the existing Jina/FlareSolverr
fallback, instead of retrying forever with no bound
Also fixes check.py: the thumbnail check never consulted the
CF-protected-host allowlist (only the page-url check did), and adds
missav.ws/surrit.com, tik.porn/image-cdn.tik.porn, vrporn.com, and
www.wowxxx.to/img.wowxxx.to to that allowlist - all four providers
already work fine through the app's real client; the checker's plain
probes (and its yt-dlp invocation, which never applies a channel's
declared ytdlpCommand impersonation flag) just can't replicate that.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTqf6orbHZ9rFFpVpcgzcR
- shooshtime: update parse_list_videos selectors for the site's redesigned
markup (div.card / a.title / a.image img / div.duration / div.list div.box
/ div.name a.text)
- hdporn92: mirror the site's own links.hls4||hls3||hls2 fallback and scan
every P.A.C.K.E.R. block instead of assuming the first one is the player
block; prefer a genuine .m3u8 candidate over a decoy .txt one
- camsoda: replace naive first-`{`/last-`}` JSON extraction with a proper
balanced-brace scan so trailing wrapper markup can't corrupt the parse
("key must be a string")
- sextb: fix mirror_url() double-prefixing the scheme when building Jina
mirror URLs (produced a malformed https://r.jina.ai/http://https://... URL)
- supjav: switch curl_cffi impersonation from chrome to safari18_0 - Cloudflare
blocks the chrome/firefox profiles outright but passes Safari
- hentaimama: resolve every card on a listing page (not just the first
perPage) and truncate after filtering, so a bulk "Coming soon" placeholder
batch at the front of the feed no longer empties the whole page; report a
provider error if literally every candidate fails
Refreshes graphify's graph and docs/provider-catalog.md with the new
technical details for all six providers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTqf6orbHZ9rFFpVpcgzcR
melonstube is a meta-search aggregator that redirects out to ~120
third-party hosts. A live crawl of /new, /popular, and /rating
(~3,350 out-links) found the previous "assume yt-dlp can resolve it"
fallback was hiding real failures on the highest-traffic hosts:
- eporner.com is ~34% of all melonstube traffic by itself, and like
the eporner channel (fixed earlier), serves a restricted preview
clip without its hash/xhr dance.
- fapcat.com/momvids.com 403 a plain fetch (Cloudflare).
- Several others just needed real extraction instead of a guess.
Per-card enrichment now dispatches by destination host to:
- Eporner: reuses eporner's own hash/xhr + Referer resolution, moved
to src/util/eporner_resolve.rs so both providers' single-provider
builds still compile (HOT_TUB_PROVIDER=<id> only compiles the one
selected provider module; src/util/* always compiles).
- Kvs (pornfreenow, analdin, babepump, w1mp, fetishshrine, pervclips,
xozilla, ok.xxx, ebonypeek, xgroovy): a shared get_file extractor,
the same KVS backend several other Hot Tub providers already scrape.
- KvsCfGated (fapcat, momvids): same extractor via the Safari18
bypass client melonstube.com itself already needs.
- XhamsterMirror (xh.partners): a genuine xHamster mirror instance,
Referer built from the actual resolved host.
- SomePorn: HLS playlist URL built from the page's video id directly.
- Pornone: direct <source> tag extraction.
flirt4free.com (a live-cam page, not a VOD) moved into the dropped-
host bucket alongside the existing junk/join.* hosts.
Enrichment is bounded (concurrency, a 10s per-card timeout, and
capped to the requested perPage rather than the full ~240-card page)
so a slow or rate-limited destination can't stall the response.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>