youperv.com (DataLife Engine) carries paysite rips titled
"Studio - Performer - Title" (71% of a 132-title sample), ~60-70 new
scenes/day. Browse the homepage + /page/N/ (19 scenes/page, no overlap);
scene URL is /<category>/<id>-<slug>.html.
Cast is scoped to the fmeta block (up to the Related section): the whole
page carries 19-26 /xfsearch/pornstar/ links but only 1-2 are the actual
cast, so without scoping this would repeat the page-wide pollution that
got xxxfiles rejected. Studio comes from the title prefix, guarded so a
performer name is never mistaken for a studio. Duration, ISO release date,
per-scene tags and thumbnail all come from the same block.
Playback is a plain <source> mp4 (files.klubnichka-hd.com) with no token
or expiry, but the CDN hotlink-guards on Referer: bare Range gets 403,
Range + Referer + browser UA gets 206 cross-IP from the VPS. So the
extractor returns it with referer + mobile_direct_ok and the phone streams
straight from the CDN, no WebView and no proxy. Path is percent-encoded
because the filenames contain spaces.
Deep-crawl capped at 2000 pages: beyond that (<=09.2023) the catalog turns
into generic amateur uploads with no performers and dead CDN files.
Verified: 19 scenes/page with studio+cast+duration+date, max 2 performers
per scene (pollution guard holds), 0/19 missing duration, playback 206
video/mp4. Pilot ingest 3 pages: 57 seen, 39 attached to existing canonical
scenes, 18 new, 0 errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
hqporner byl TYLKO w ALL_DIRECT_SCRAPERS (search po nazwisku performera), wiec
swieze sceny wchodzily wylacznie gdy worker trafil na pasujacego performera →
0 nowych scen w 7 dni (audit 2026-07-26, user: 'nie widze nowych scen z hqporner'),
mimo ze tube publikuje codziennie.
HQPornerBrowseScraper: listing / + ?p=N (~50 scen/strone), detail /hdporn/<id>-<slug>.
Metadane z detail page: tytul z <title> (Title Case; <h1> jest lowercase), dlugosc z
meta description ('Video duration is 46min 58sec'), performerki z /actress/ (display
name, nie slug), tagi z /category/, miniaturka fastporndelivery + phash (z Refererem,
bo CDN bez niego 403).
external_id identyczny jak w HQPornerScraper.search (hqpornercom:<url>) → obie sciezki
trafiaja w te sama scene zamiast robic duplikat. Tagi jakosciowe (1080p-porn, 4k-porn)
odfiltrowane, bo to atrybut pliku a nie tag tresci.
Zweryfikowane na zywo: 50 scen z page 1, dur 2818s/1138s/1805s, performerzy+tagi+phash.
1) eporner/phone-resolve: gdy strona parsuje sie OK, ale CDN jest nieosiagalny z
sieci usera (audit: siec zglaszajacego nulluje vid-*.eporner.com -> 0.0.0.0, a
www.eporner.com dziala), phone re-resolve zwracal TEN SAM URL, a lancuch fallback
nie ratowal: dla phone-resolved linkow fallbackProxyUrl === url (resolver ustawia
stream_url = direct_url), wiec krok proxy byl no-opem i szlo prosto do WebView =
strona hostera z reklamami ('zaladowala sie reklama zamiast wideo'). Teraz po
nieudanym phone-resolve bierzemy PRAWDZIWE proxy z backendu (VPS pobiera swoim
DNS-em). Tylko na sciezce bledu. Eporner globalnie zdrowy (89-100%, ~100 urzadzen
dziennie), wiec to fix dla userow z blokowanym CDN, nie regresja globalna.
2) porntrex zmienil markup kafelka: class="duration">MM:SS< ->
<div class="durations"><i class="fa fa-clock-o"></i> MM:SS</div>. Stary wzorzec
przestal matchowac -> duration_sec=NULL dla ~95% swiezych scen (6479/6815 w 7 dni),
a mobile domyslnie filtruje min_duration_sec=60, wiec te sceny byly NIEWIDOCZNE
('nie widze nowych scen z porntrex'). Wzorzec obsluguje oba warianty + H:MM:SS.
Zweryfikowane na zywym listingu: 84/85 kafelkow, 20:45 -> 1245s.
Fourth JAV vertical source (origin tube:supjav, gated to JAV tab via JAV_ORIGINS).
Browse: CF-blocks datacenter IPs, so listing goes through the Bright Data ISP proxy
with retry (the proxy rotates IPs and CF only lets some through). Homepage-latest only
(~66 newest); deeper /page/N/ stays CF-403 even via proxy. Metadata parsed from the
listing cards (title, thumbnail, JAV code, year/month) rather than per-post details,
which are flaky and slow through the proxy.
Stream: supjav hides the real hoster behind a per-server data-link (hex). base.js loads
lk1.supremejav.com/supjav.php?l=<data-link>, which reverses the hex string and fetches
?c=<reversed>, 302-ing to the hoster (TV->turbovid, FST->fc2stream, ST->streamtape,
VOE->voe). The extractor reproduces that: fetch detail via proxy, reverse each data-link,
resolve through lk1 (reachable direct from the server), return the hosters as type=hoster
so the phone resolves them (dood/filemoon native, the rest via the WebView fallback on the
residential IP). lk1 needs no proxy; only the detail fetch does.
Backend-only, no mobile change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second JAV vertical source (origin tube:vjav, gated to JAV tab via JAV_ORIGINS).
Browse: SPA has no server-rendered listing, so id-walk over sitemap_vids
(newest = highest video id) feeding the rich JSON metadata API
(/api/json/video/1/<floor>/<id>/<id>.json): title, duration, post_date,
channel->studio, models->performers, categories+tags.
Stream: videofile.php returns video_url in two obfuscation layers, Cyrillic
homoglyphs (M/C/A/E) over a custom base64 alphabet (comma->slash, tilde->pad,
dash->plus). Decoded get_file is absolute (old shared-txxx videos) or relative
(new vjav-infra, prepend host); adding f=video.m3u8 yields a portable, time-bound
ahcdn HLS (referer=none whitelisted, not IP-bound). Returned as m3u8 +
mobile_direct_ok so playback routes it through /proxy/hls: manifest passthrough,
segments direct from the phone (verified 206 cross-IP, 0 VPS video bandwidth).
Backend-only, no mobile change (JAV tab + jav param already shipped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JAV is a distinct vertical (user decision): Asian codes/titles that do not dedup
against the western catalog, so they must not flood the main feed.
Backend (scenes.py): JAV_ORIGINS = {tube:javflix, tube:javguru, tube:vjav,
tube:supjav}; list_scenes gains a `jav` param. Default (jav=false) excludes any
scene with a live JAV-origin source; jav=true returns only those. The cached
default-count and _is_pure_default also exclude JAV so the main feed count matches.
JavflixScraper is now registered in ALL_BROWSE_SCRAPERS (scheduled ingest lands in
the JAV section, gated). Scraper hardened: requires a real server button
(class="myLink") so static pages (Terms/FAQ) are skipped, and unescapes HTML
entities in the title.
Mobile: a "JAV" top tab reuses ScenesScreen with { jav: true } (route param ->
listScenes jav=true). The 60s minimum-duration default is disabled in the JAV tab
because javflix does not expose duration (NULL >= 60 would hide the whole section).
Verified on prod: 16 javflix scenes appear only in the JAV feed and are excluded
from the 2.29M main feed; playback resolves to voe/doodstream/emturbovid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First site of the JAV vertical (user request, separate section). javflix.cc is a
WordPress JAV aggregator, VPS-reachable and server-rendered. Scraper parses itemprop
metadata (title, thumbnail, uploadDate; no duration) and the JAV code (BKD-368 style)
as a tag. Playback: server buttons are <a class="myLink" href="<embed>"> (streamtape,
voe, doodstream, emturbovid) whose href sits in the raw HTML; the generic _embed_iframe
extractor already resolves those via its anchor-hoster pattern, so javflix registers
under sitetag "javflix" with a thin wrapper that drops the players.mp4 placeholder.
Added emturbovid to the anchor-hoster host list.
Verified end-to-end on prod: scrape a listing -> RawScene with metadata + code tag,
resolve -> emturbovid/voe/doodstream hoster sources (all Goon-playable).
NOT registered in ALL_BROWSE_SCRAPERS yet. JAV is a separate vertical (user decision)
that must be gated out of the main scenes feed before ingest so it does not flood the
western catalog. Next: feed gating (exclude JAV origins by default) + a mobile JAV tab
+ on-device playback test, then enable scheduled ingest. jav.guru / vjav / supjav
follow the same pattern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hqfap was hard-removed 2026-06-25 when its entire CDN library started serving a
fixed /upload/videos/video_down.mp4 "server down" stub for every scene. The site
is back and now serves real video from a new CDN (d*.vstor.top/whlvid/...). Probe
of the live sitemap: 5/6 sampled scenes return a real mp4 (0 stubs), and the CDN
is portable cross-IP (HTTP 206 from a residential IP and from the VPS with the
hqfap referer).
Restores the PlayTube CMS browse scraper (HQFapScraper, sitetag hqfapcom) and the
JSON-LD contentUrl extractor, re-registered in both registries. The extractor now
sets mobile_direct_ok=True (vstor.top is not matched by _TIME_BOUND_CDN_RE, but is
verified portable) so mobile plays direct with zero proxy. The video_down.mp4 stub
guard is kept defensively. 4k69 stays removed (not re-checked).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bug-report clusters about missing thumbnails:
1. watchporn scenes had no thumbnail on the list, only appearing after opening
SceneDetail (which auto-enriches og:image). Coverage was 1.3% (463/36,915).
The browse scraper never captured a thumbnail. KVS stores the poster at a fixed
derivable path (contents/videos_screenshots/<id//1000*1000>/<id>/preview.jpg,
verified 8/8 loading). Scraper now sets thumbnail_url (og:image, else derived);
backfilled 36,687 existing rows -> 100% coverage.
2. SceneDetail showed no thumb where the list showed one: the mobile detail picks
the first source with a thumbnail_url (origin ASC often puts sxyprncom first),
and sxyprn/trafficdeposit stored thumbs rot to 404. The list already swaps those
for a live resolver (/proxy/sxyprn-thumb/), but the detail builder did not. It
now applies the same live-resolver swap and nulls other rotting thumbs so the
detail lands on a working image.
Both backend-only, no OTA needed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
watchporn.to was disabled 2026-05-12 (all iframes were DoodStream/CAPTCHA
-> black-screen WebView). The site has since been rebuilt as a KVS tube:
the DoodStream hosters are gone, scenes now expose flashvars get_file
direct mp4. Rewrote the dead ?s= search scraper as a /latest-updates/
browse scraper (og:title, JSON-LD duration/uploadDate, /models/ performers,
/tags/ tags, /categories/ studio) and added a KVS get_file extractor
(porntrex-style same-session 302 resolve; token not IP-bound, plays from
VPS). Moved to ALL_BROWSE_SCRAPERS, extractor swapped from _embed_iframe.
Verified: 35 fresh scenes/page (today), multi-performer, playback 1080p mp4 206.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-check 2026-06-25 across the full id range confirmed both PlayTube tubes
serve only the fixed `/upload/videos/video_down.mp4` "server down" stub, never
a real file: hqfap 0/80 real (79 stub, 1 none), 4k69 0/40 real (38 stub, 2
none). Both were disabled 2026-06-22; CDN never came back, so removing entirely
(mirrors the pornhub/redtube/0dayxx/pornditt/pornhat removals).
Removed the extractor registry entries (hqfapcom, 4k69com) + module files and
the browse scrapers + imports. Prod DB data deleted separately (28,398
solo-orphan scenes + 46,196 playback_sources). `_playtube.py` kept: superporn
and neporn still use its JSON-LD helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Browse over /best/<YYYY-MM>/<page> (SSR; xnxx has no clean /new/ and its homepage is
JS-rendered) for a latest-feed freshness signal next to the performer-driven search
scraper. JSON-LD VideoObject only — xnxx detail (unlike its xvideos twin) doesn't
expose /models/ or /tags/ in SSR, so performers/tags come via canonical merge + the
search scraper. Title is html.unescaped (JSON-LD ships ,/! entities).
xhamster and sxyprn intentionally left search-only: xhamster Cloudflare-blocks the
VPS on listing pages (1KB challenge), sxyprn has no clean SSR listing (IP-bound) —
a flaky browse scraper would be worse than the working search + 168h watchdog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Browse over /browse/time/?page=<n> (SSR) for guaranteed latest-feed freshness next to
the existing performer-driven search scraper. JSON-LD VideoObject only (title /
duration / uploadDate / thumbnail) — deliberately NOT scraping performers/tags from
the detail page: JSON-LD has no actor field and the /pornstar//category links are
sidebar-polluted with no scene-scoped container, so a naive regex attached the same
2 pornstars to every scene. Performers/tags come via canonical merge + the search
scraper instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both were search-only — fresh only as long as the performer queue cycles and the
site search keeps working. Added browse scrapers next to the existing search ones
(xvideos/eporner pattern: search keeps performer back-catalog coverage, browse
guarantees latest-feed freshness → watchdog 48h instead of 168h):
- porntrex: KVS /latest-updates/<n>/ (title + thumb + phash)
- mypornerleak: WP REST /wp-json/wp/v2/posts?_embed=1 (title + date + studio from
category + performers from the actors taxonomy)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Watchdog flagged porndish as frozen (search ?s= stopped yielding new scenes
2026-05-07, 1151h). It's WordPress and the VPS can reach it, so converted to a browse
scraper over the WP REST API (/wp-json/wp/v2/posts?_embed=1), same pattern as
perverzija: title, date, featured thumbnail, studio (category — FreeUseFantasy /
I Have A Wife / … paysite content) and tags. Performers via canonical merge. Playback
unchanged (embed iframe → phone-side). 60 fresh scenes on first crawl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Search (?s=) started returning 429 and the homepage is JS-rendered (no post links in
raw HTML), so the old search scraper got 0 (frozen since 2026-05-07). perverzija is
WordPress and the VPS can reach it (200, not CF-blocked), so converted to a browse
scraper over the WP REST API (/wp-json/wp/v2/posts?_embed=1): one structured call per
page gives title, date, featured thumbnail, studio (category — DadCrush/FamilyStrokes/
… TeamSkeet-family paysite re-ups) and genre tags. Performers via canonical merge
(stars taxonomy isn't REST-exposed; title carries names). Playback unchanged (embed
iframe → phone-side). 15 fresh + 45 refreshed on first crawl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three orphan-factory tubes (0–0.2% canonical match — auto-screenshot thumbs and
slug titles that never match TPDB/StashDB) — to be replaced by better sources.
Removed scrapers (files + imports), extractors (registry + modules), the pornhat
entry from tag-enrichment priority lists and the 0dayxx display override, and purged
the DB (19,003 playback_sources + 9,904 solo-orphan scenes; shared mirror scenes keep
their other sources). The pornhat-based enrich_studio endpoint stays as a graceful
no-op (no pornhat sources → returns no studio).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fpo.xxx is a KVS site, not WordPress, so the old `?s=` search scraper matched
nothing (frozen since 2026-05-07). Converted to a browse scraper reading /new-<n>/
(title + duration + thumbnail + phash from the listing tile; performers via canonical
merge). Playback was already phone-side (KVS). 32 fresh scenes on first crawl.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both scrapers were disabled since 2026-05-12 (~0.4% canonical match — mostly short
amateur clips that never match studio content); their data sat frozen. Removed for
good: deleted the extractor registry entries, scraper files and imports, dropped them
from the tag-enrichment priority lists, and purged the DB (17,906 playback_sources +
122 scenes that had no other source; mirror scenes shared with other tubes just lost
the ph/rt link).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- hqfap + 4k69: both ingested fresh but playback is dead (hqfap serves a fixed
~3MB "server down" stub for every scene; 4k69 resolves no playable URL).
Removed from ALL_BROWSE_SCRAPERS so no new dead sources get ingested; existing
live playback_sources marked dead in prod (scenes drop out of has_playback /
Sites). Extractors kept in registry for easy re-enable if the hosts recover.
- latestpornvideo: was a performer-search scraper, so it never picked up the
site's "latest" feed — users saw a stale set. Converted to a browse scraper
reading /page/N/ (studio+date from title/thumb, category tags; performers via
canonical merge). Moved DIRECT → BROWSE list.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
siska's ?s= search ignores the query (returns latest regardless), so the performer-driven search scraper always yielded 0 and was disabled. Rewrote SiskaScraper as a latest-browse scraper (BaseBrowseScraper, /page/<n>/) and moved it to ALL_BROWSE_SCRAPERS. The listing tile carries everything (no detail fetch): title, duration (MM:SS span), thumbnail (img data-src), performer + studio (img alt 'Performer - Title - Studio'), category (thumbnail path). Playback unchanged: fresh videos embed playmogo + luluvid, resolved phone-side via _embed_iframe. Verified ingest: 26 seen / 11 new / 15 updated / 0 errors — and 15 updated means siska scenes match existing canonical scenes, adding playback coverage rather than orphans. Now covered by the browse ingest-watchdog (48h) and the 6h browse-latest + deep-crawl jobs. Old self-player videos (player.siska.video -> cfglobalcdn, ~2018) are dead and age out.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revisited siska re-enable (user fa4083a2). Findings: (1) fresh siska videos (videoID 227xxx) embed playmogo + luluvid and ARE phone-resolvable; updated siska.py scene regex + extractor path to the current video.php?videoID= format (old /<slug>/ format is gone). (2) BUT siska's ?s=<query> search is broken site-side — it returns the latest videos regardless of query (angela white == riley reid == homepage), so as a performer-driven BaseSearchScraper it always yields 0 (title token filter rejects everything). Reviving siska would require converting it to a browse/latest scraper (changes ingest character) — left as a decision. Old self-player videos (player.siska.video -> cfglobalcdn) are dead. Scraper stays disabled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Old regex matched junk (/wp-json etc.), not scenes (scenes are /<post_id>/).
Frozen since 06-13. Rewrote search() to scrape the /actor/<slug>/ listing
and parse <article> cards: scene URL, title, performers + tags from the
class (actors-*/tag-*/category-*, dropping performer-name fragment tags),
thumbnail. Studio + release date parsed from the "<Studio>-YYYY-MM-DD"
thumbnail filename, with a title-prefix "<Studio> YY MM DD" fallback.
Multi-performer works; no duration in listing; playback unchanged (hoster).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Content moved to the w8.mypornerleak.com (wN) load-balancer subdomain, so
the old bare-domain scene regex matched nothing (frozen since 05-07).
Rewrote search() to scrape the canonical /actor/<slug>/ listing: scene
URL (wN host normalized to canonical for stable dedup), title, duration,
performers and category-tags from the <article> class (actors-*/category-*),
thumbnail. No studio (OnlyFans/amateur leaks have none). Multi-performer
works; playback unchanged (hoster, phone-side).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sxyland dropped the /<numeric_id>/<slug>/ scene URL format for /<slug>/,
so the old regex matched nothing (frozen since 06-07). Rewrote search()
to use the performer page /actor/<slug>/ and fetch each scene for full
metadata: all performers (with co-stars, from /actor/ links), tags
(scoped to the scene's tags-list, not the sidebar), duration + upload
date (itemprop), studio from the title prefix (BraZZers/MilfCoach/... ,
guarded so a performer-name prefix isn't mistaken for a studio). Junk
nav pages (Terms of Use etc.) are dropped via a no-duration-and-no-tags
guard. Verified: clean studio/performers/tags in DB, 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sxyprn ingest was frozen since 05-07: the old ?type=videos&query= endpoint
returns trending (not performer-filtered), so the strict token filter
correctly dropped everything -> 0 ingest. Real "search" is the performer
page /<First-Last>.html. Rewrote search() to scrape those cards: clean
performer (the query, avoids sxyprn's Dallas/Rae name fragmentation),
studio (channel subcat), tags (#hashtags), duration, thumbnail. Token
filter now runs on the card title so only genuine matches attach the
performer. Verified: Lana Rhoades/Riley Reid/Angela White return results,
metadata persists in DB (studio e.g. Vixen, 10-31 tags/scene), playback
mp4 206.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The homepage root / is a KVS page with cache-control: no-store and a fresh PHPSESSID per request; the server rotates its featured block and on a cold session can serve an old set instead of the newest scenes. Result: browse-latest skipped everything for 3 days (root served 20 May content), no new freshporno scenes since 12 Jun (user report). Switch _listing_url to the explicit date-sorted /latest-updates/ feed (pagination /latest-updates/N/), which is not subject to that rotation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
It is an ISP proxy (static ISP IPs, flat billing), not residential —
so HTML-ingest bandwidth is free and the full deep-crawl is fine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
superporn hard-blocks the VPS IP with Cloudflare 403 on every TLS
impersonation, so HTML ingest routes through Bright Data residential
(BRIGHTDATA_PROXY_URL, parsed in config). First scraper to use a proxy:
optional _proxy on the browse base, threaded into browser_get.
JSON-LD VideoObject (title/desc/uploadDate/thumb/duration) + pornstar
and category chips; superporn double-encodes HTML entities so titles
are unescaped twice. Thumbnails fetch fine from the VPS (no proxy).
Playback stays off-proxy: the <source> mp4 token is IP-bound to the
fetcher, so resolve is phone-side via WebView (extractor superporncom
-> _vps_blocked_fallback), same as porndoe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4k69.com (~65k scenes): same PlayTube CMS as hqfap - common logic moved
to _playtube.py (sitemap catalog, JSON-LD, pills). Studio classified by
matching category pills against the studios index page. Streams are
get_file (fullmovies family) returned unresolved with mobile_direct,
2160p skipped.
neporn.com: KVS engine, latest-updates listing, JSON-LD + video:duration
meta, performers from models links with flashvars video_tags fallback
for fresh uploads. Resolve via _kvs; final URL portable cross-IP.
superporn.com rejected: Cloudflare 403 from VPS on all TLS impersonations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PlayTube CMS. Sitemap-based pagination (listing has no GET paging),
JSON-LD VideoObject metadata, pornstar/category pills, " Clips"
categories mapped to studio. Direct mp4 (cdnde.com/okcdn.ru), tokens
time-bound and portable cross-IP, so mobile plays direct.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
xvideos renders the scene's models as `<a href="/models/slug">...<span class="name">
Display Name</span>...`. The old _MODEL_RE wanted text immediately after the anchor
`>` and never matched current markup → browse-scraped scenes landed with 0 performers
(bug-report 2026-06-07: "no actors, but Rebecca Johnson is on the page"). New regex
captures slug + nested span.name, bounded within the anchor. + backfill script for the
~11.9k existing zero-performer xvideos scenes (54% have a real /models/ link; resolver
merges names to canonical by name_normalized).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hqporner search post-filter kept a scene if its slug contained ANY query token
(>=3 chars). For multi-word performer names this matched on a single common token
(e.g. "anna","mia"), so the performer-driven ingest attributed the scene to EVERY
performer sharing that token — scenes accumulated up to 503 wrong performers
(hqporner = 5659 of 5897 scenes with >30 performers; bug-reports 2026-06-07).
Switch ANY->ALL: the slug must contain every query token, requiring a full name
match before attribution. Single-word names still work. Precision over recall —
144 wrong performers is far worse than missing a few loose matches.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The umbrella Source.name for all direct tube scrapers (deep-crawl, browse-latest,
performer-driven) was "pornapp" — a misleading leftover from the removed external
porn-app API. It read like a dependency on a third-party "pornapp" service; it is
not — these are our own scrapers hitting 25+ tubes directly (kind=scraper,
origin tube:<sitetag>). Renamed to "tube-scraper" via a single SCRAPER_SOURCE_NAME
constant; DB row renamed in place (UPDATE name, same id) so all ingest_runs +
external_records history stays linked. No behavior change — external_id keying
(sitetag:url) and dedup are unaffected.
NOTE: playback_sources.origin "pornapp:<sitetag>" prefix is a separate legacy
format (resolve_playback parses it) and is intentionally left untouched.
Verified on prod: row renamed (0 stray "pornapp"), new runs land on "tube-scraper".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
porndish-only scenes had no tags and no description — the scraper only derived a
title from the URL slug. The scene page (g1/bimber WP theme) carries both: a
<p class="entry-tags"> list of /video2/<slug>/ links (the "#" tags the user sees,
categories + co-performers) and a prose description <p> in .entry-content.
Override _fetch_scene_metadata in PornDishScraper to pull both from one page
fetch. Extend the base hook to accept an optional 4th return element
(description) and thread it into RawScene.description — backward compatible with
the existing 3-tuple (pornhat). Strips leading embed-button labels
("Video Player N", "Server N") from the prose. Verified on live scenes: clean
tag lists + real descriptions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
xvideos SSR's JSON-LD VideoObject (duration/title/uploadDate) + on-page /models/ (perf)
+ /tags/. Sample: median ~10.5min, 93% >=3min. Pilot (2 pages): 29 new, 100% playable +
visible + tagged (performers sparse — xvideos 'new' is amateur-heavy; /models/ tagged
mostly on studio rips).
- XVideosBrowseScraper (JSON-LD + page-parse models/tags), in ALL_BROWSE_SCRAPERS.
- deep_crawl._PAGE_CAP: per-sitetag depth cap; xvideoscom=1800 (~newest 50k). At the cap
the tube is marked exhausted (reset -> incremental re-sweep) so a mega-tube cannot
monopolize the round-robin or balloon the DB.
- ported yesporn.py into the public repo (was prod-only, like hdporngg) ending the
__init__ public/prod divergence.
youporn rejected: JSON-LD lacks actor/keywords, its /pornstar//category/ links are A-Z
nav not scene-specific. xhamster: 429/Cloudflare from the VPS IP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
porntrex/hqporner rejected for deep-crawl: KVS sites with no SSR metadata (77% of
existing porntrex has no duration -> invisible under the app's >=60 filter). eporner
instead exposes a public JSON API (api/v2/video/search) returning title + length_sec
+ keywords + added per video; ~100k videos, ~100/page, no per-scene detail fetch.
- BaseBrowseScraper.crawl_page(page): factored out of latest_scenes; returns None
(transient fail) / [] (catalog end) / [scenes]. API subclasses override it.
- deep_crawl drives via crawl_page (supports HTML-listing AND API sources).
- EpornerApiScraper: crawl_page hits the eporner API -> RawScene with duration+tags+
date+thumb+playback; registered in ALL_BROWSE_SCRAPERS.
- Pilot (2 API pages): 192 new, 100% playable + tagged + visible (>=60); the <180s
trailer filter dropped 6 short clips.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- paradisehill.fetch_movies compared release_date coerced to midnight against the
`since` timestamp, so the chronological crawl stopped at the first upload dated
the same calendar day as `since` and silently dropped most new movies (0-2 seen
per run; Movies tab stalled). Compare by DATE with a 1-day grace instead; idempotent
external_records upsert dedups the re-fetched recent window.
- scripts/backfill_paradisehill_movies.py: one-off no-delta deep crawl to recover the
backlog missed during the bug (idempotent, resumable).
- docs: correct stale 'raz dziennie/24h' browse-latest comments to 6h (4x/day), the
actual configured cadence (config.py sched_browse_latest_hours=6).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolver/perf:
- find_by_phash_within: nearest match via Postgres bit_count over bit(64) XOR
instead of Python scan of all phash fingerprints (~20x faster per scene;
unblocks long delta runs that were killed mid-run before since advanced).
Scheduler/reliability:
- reap ingest_runs stuck in 'running' on worker startup (killed_by_restart).
- smoke_test: per-source ingest health, stuck-run and browse-freshness checks
-> Sentry; exclude killed_by_restart from the failed-run alarm.
Tags (ingest with tags + fill blanks):
- wire infer_tag_slugs into normalize_scene so tube scenes get title-inferred
tags (was dead code); union with connector tags.
- scripts/backfill_inferred_tags.py: keyset/batched/idempotent backfill for
existing tagless scenes (playable tag coverage 16% -> ~52%).
Clip-store:
- skip ManyVids/IWantClips/Clips4Sale/... from canonical sources at ingest
(GOON_SKIP_CLIP_STORE, default on) — permanent orphans, ~56% of canonical
ingest, never have a free-tube playback source.
Browse tubes:
- enable fullmovies + hdporn.gg: studio parsed from title prefix instead of
the /networks/ sidebar (which always yielded the first listed network);
drop phash compute (pilot: 0% canonical hit within Hamming 5 — auto-screenshots),
matching relies on title/performer/duration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
filemoon (+ mirrory kerapoxy/lvturbo/emturbovid/bysezoxexe/bysezejataos)
nie umarł — ~2026-05 zrobił rebrand na Vite SPA "Byse Frontend". Stary
P.A.C.K.E.R.-JWPlayer embed zniknął, więc backend uznał go za martwego i
wpisał na DEAD_HOSTER_RE. RE bundla index-ChwZgmXV.js (2026-05-22):
POST /api/videos/<code>/embed/playback body {"fingerprint":{}}
→ {"playback":{"key_parts":[..],"iv":..,"payload":..}}
→ key=concat(b64url(key_parts)); AES-256-GCM(key,iv,payload) → JSON
→ sources[*].url = HLS master.m3u8
Browser-attestation jest opcjonalny — pusty fingerprint wystarcza.
Stream URL jest IP-bound (token wiąże się z IP requestera), więc resolve
musi iść z urządzenia użytkownika (jak doodstream.ts / packerHoster.ts).
- mobile/src/lib/aesGcm.ts — pure-JS AES-256-GCM decrypt (RN/Hermes nie
ma Web Crypto); S-box liczony z GF(2^8), GHASH weryfikuje tag.
Zweryfikowane przeciw cryptography (Python) na 2 payloadach.
- mobile/src/lib/filemoonHoster.ts — resolver: POST playback → decrypt →
pick best source. E2E test: filemoon.to/e + /d + bysezoxexe.com mirror.
- PlayerScreen: filemoon w resolve useEffect obok doodstream/packer.
- backend: filemoon poza DEAD_HOSTER_RE; hoster.py early-return → przelot
jako type='hoster' do mobile resolvera (server-side resolve bezcelowy,
bo URL IP-bound do VPS).
- direct_scrapers: poprawiony błędny komentarz "filemoon shutdown".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Goon — self-hosted aggregator for adult-content scene metadata.
Indexes scenes from TPDB, StashDB, and 30+ public adult tube sites.
Cross-source deduplication via perceptual hash + Levenshtein distance.
FastAPI backend + APScheduler worker + React Native (Expo) mobile client.
FOSS, ad-free, donation-funded. See README for details.