Commit graph

53 commits

Author SHA1 Message Date
goon-foss
81f0074d34 fix(vjav): bramka jakosci tytulu + wlasna iteracja stron
Po naprawie dostepu vjav ruszyl, ale zaczal sypac smieciem: pomiar na 295 scenach z
doby po fixie dal 78,6 proc. tytulow slug-concat i ZERO przypisanej obsady. Takie
sceny sa w apce nieodnajdywalne - nie maja ani tytulu, ani performera.

Bramka odrzuca cztery wzorce: prefiks id_JAV, hash x1x..., kod typu
QCT062-KPUDYLMLBL913295279 oraz ciagi bez samoglosek/spacji (walniecie w klawiature).
Zmierzone na probce 6000 tytulow: odrzuca 80 proc. swiezych i 27 proc. starych, przy
39 przepuszczonych na 200 obejrzanych - zrodlo nie jest wyzerowane.

WAZNE, znalezione przy pomiarze pierwszej wersji: str.isalpha() jest prawdziwe dla
kana i kanji, a japonski nie stawia spacji, wiec reguly "za malo samoglosek" i "brak
spacji" odrzucalyby PRAWDZIWE tytuly na serwisie JAV - 708 z 708 tytulow CJK z probki.
Stad jawny wyjatek _has_cjk. Kontrola jednostkowa: 8/8 przypadkow, w tym dwa CJK.

Drugi blad, wprowadzony przez sama bramke i zlapany dopiero pilotem: strona 1 jest w
100 proc. smieciowa, wiec po odsianiu zwracala [], a bazowe latest_scenes traktuje
pusta strone jako koniec katalogu i konczylo caly run. Zrodlo dawalo seen: 0 mimo 39
dobrych scen na stronach 2-5. Wlasna iteracja rozstrzyga wyczerpanie po dlugosci listy
id, nie po liczbie scen po filtrze. To ta sama pulapka co w _load_ids.

Pilot po obu poprawkach: seen 39 (wszystkie skipped - weszly przed bramka).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 14:25:00 +02:00
goon-foss
532c7e2bb4 fix(vjav): 429 parsowany jako XML, zrodlo stalo 12 dni bez zadnego bledu
Audyt po fixie streamporn.vip. vjav (77 tys. zrodel) nie dodal nic od 2026-07-22 i
NIE zglaszal bledu - crawl_page oddawal 0 scen, licznik pokazywal errors: 0.

Przyczyna: vjav odcial IP VPS-a. Sprawdzone 2026-08-03 - sitemap, strona glowna i
/latest-updates/ daja 429 przy trzech probach z rzedu, a przez Bright Data 200 od
pierwszej. Kod tego nie widzial, bo browser_get NIE rzuca na 4xx, wiec 343-bajtowa
tresc bledu byla parsowana jako XML: zero sitemap, zero id.

Drugi blad, ktory to ukryl: przy niepowodzeniu _load_ids ustawialo _sorted_ids = [],
a crawl_page tlumaczy [] na "koniec katalogu" (poprawny stan), nie na awarie. Teraz
przy bledzie zostaje None, ktore crawl_page zwraca jako transient-fail.

Fix: proxy Bright Data na sitemap i API metadanych (ten sam host, ten sam 429), jawne
sprawdzanie statusu, [] tylko dla realnie pustego katalogu.

Po fixie crawl_page oddaje 120 scen zamiast 0. Nowej podazy to jeszcze nie dalo
(wszystkie 120 to pozycje sprzed blokady, skipped na niezmienionym hashu) - sitemap
vjav nie przesunal sie od czasu odciecia. Do obserwacji. Uwaga na jakosc: w tej
partii 58 proc. tytulow to slug-concat typu 0008363_JAV_..., przy 2,9 proc. w calym
katalogu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:36:25 +02:00
goon-foss
21d51ca691 chore(hqfap): zdjete zrodlo - Cloudflare JS-challenge nie do przejscia
Some checks are pending
Backend tests / test (push) Waiting to run
Druga i ostateczna deaktywacja (pierwsza 2026-06-25, wtedy stub-pliki). Strona
wdrozyla Cloudflare z JS-challenge na /watch/. Sprawdzone TRZY drogi, wszystkie 403:
VPS (Hetzner), residential IP z pelnym kompletem naglowkow przegladarki, oraz
Bright Data ISP proxy. Czyli blokada NIE jest po IP - wymaga wykonania JS.
Przelaczenie na WebView (commit 140764b) tez nie wystarczylo: challenge pokazuje sie
userowi zamiast przechodzic samoczynnie (zgloszenia 92e9133e / 60576a84 / 244131ba).
Telemetria: ZERO prob odtworzenia od 27.07, czyli realnie martwe od tygodnia.

Utrzymanie wymagaloby headless-przegladarki na VPS - nieproporcjonalne do zysku,
decyzja uzytkownika: usunac. Wyrejestrowany scraper (browse) i extractor;
61238 playback_sources oznaczone dead_at. Uwaga: hqporner to INNE zrodlo o mylnie
podobnej nazwie - nietkniete, dziala.
2026-08-02 18:01:19 +02:00
goon-foss
7a40ff70ba fix(performers): czlon <3 znaki degenerowal filtr slug-matchu (over-attribution)
Some checks are pending
Backend tests / test (push) Waiting to run
Bug-report 986d3018 ('O co chodzi z tym Lady Vi? To wcale nie jest aktorka z tych
filmow'). Search-scrapery filtruja wyniki po tokenach nazwy >=3 znaki, wiec czlon
2-znakowy wypada: 'Lady Vi' -> {lady}, i  degeneruje sie do 'slug zawiera
lady' = KAZDA scena z tym slowem. Dla nazw gdzie WSZYSTKIE czlony sa krotkie
('Jj Jj', 'Mi Su') zbior tokenow jest PUSTY, a
przepuszcza wtedy absolutnie wszystko.

Audyt: Lady Vi miala 116 przypisan z tube, z czego 115 pasowalo tylko przez 'lady'
a realnie o niej byla 1. Wyczyszczone (zostaly kanoniczne stashdb + 1 trafione).

Fix: gdy nazwa ma czlon <3 znaki, wymagamy dodatkowo CALEJ nazwy sklejonej bez
separatorow ('lady-vi-...' -> 'ladyvi'). Zweryfikowane: lady-vi-hot-scene przyjmie,
lady-sonia-milf i busty-lady-next-door odrzuci.
2026-07-28 09:52:41 +02:00
goon-foss
b485511f3f feat(fullporner): browse scraper pelnych scen + extractor bez tokenu
Pelne sceny, nie klipy: mediana ~33 min, zero ponizej 10 min, ~140-240 dziennie,
listing chronologiczny mimo naglowka Featured.

Extractor sklada URL sam, bez tokenu i bez wygasania: id w iframe jest zapisane
wspak, a koncowka to maska bitowa jakosci (1/2/4/8 = 360/480/720/1080). Id bywa
numeryczne albo szesnastkowe, a to drugie to ~25% katalogu. Blokada CDN jest na
naglowku (bez UA 403, bez Referera 404), NIE na fingerprincie TLS, wiec telefon
przechodzi -> mobile_direct_ok. Zweryfikowane 206 + faststart do 1080p.

Metadane cienkie: brak studia, mediana 3 tagi, tytuly przepisane pod SEO. Za to
data jako unix timestamp i dlugosc co do sekundy. Bramka na obsade, bo sceny bez
performera scalily sie 0/23 w dry-runie.

Phash miniatury liczony celowo i to on robi robote: dry-run resolvera bez niego
dawal 31% scalen, pilot z nim dal 65% (seen 62, new 22, updated 40, errors 0).
85 z 89 performerow ma ref tpdb/stashdb.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 12:04:34 +02:00
goon-foss
ea489454c0 feat(sexu): scraper + extractor HLS, ale bez rejestracji w ingescie
Metadane ma najlepsze z tej serii: kanal = realne studio na 97% scen, 96%
performerow z refem tpdb/stashdb, dokladna dlugosc w sekundach, swiezosc w
godzinach, 120 scen na stronie listingu.

Nie wlaczamy go jednak do ALL_BROWSE_SCRAPERS, bo jednostka tresci jest zla:
to klipy 5-10 minut, nie sceny. Mediana 571 s na stronie 30 i 459 s na 200,
zero filmow >=20 min w calym katalogu. Pilot na 240 scenach dal 1 canonical
match, bo dlugosc i tytul, czyli sygnaly resolvera, sa przepisane pod tube.
Przy ~340 uploadach dziennie to ~10k nierozwiazywalnych wierszy miesiecznie.

Kod zostaje kompletny i zweryfikowany: POST /api/video-info przez Bright Data
(CF blokuje sam POST z VPS), HLS playerData.src zamiast mp4 sources (te maja
ip= requestera w tokenie), master konczy sie .mp4 wiec lapie go istniejacy
hls_needs_passthrough. Gdyby serwis zaczal wrzucac pelne sceny, wystarczy
dopisac klase z powrotem do rejestru.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 11:42:05 +02:00
goon-foss
f5b7a45ac0 feat(8kporner): browse scraper za obejściem CF + extractor JWPlayer sources
Sitemapa i /watch/*.html są za Cloudflare, paginacja zwraca stronę 1, a load-more
chce logowania, więc katalog buduje się z homepage (~115 scen), a detale lecą przez
?link1=watch&id=. Bramka na obsadę przepuszcza 62/115; z tego 97% performerów ma
ref tpdb/stashdb, a 60% scen dopina się do tego, co już mamy.

Extractor czyta tablicę sources JWPlayera, nie JSON-LD contentUrl (ten jest zawsze
144p). Cap 720p, bo okcdn dławi połączenie do ~2,1 Mbps. Stream przenośny cross-IP
(206 bez Referera, faststart) → mobile_direct_ok.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 11:15:32 +02:00
goon-foss
b3e1092175 feat(pornmike): browse scraper behind a cast gate + direct-mp4 extractor
pornmike.com puts everything in JSON-LD @graph -> ItemPage.mainEntity
(name, duration, uploadDate, actor[], genre[], keywords, description,
thumbnail), so the parser reads one JSON blob instead of scraping markup.
Cast is clean: the whole scene page carries exactly as many /pornstar/
links as there are actors, none of the sidebar pollution that got xxxfiles
rejected. 82% of sampled performers already carry a tpdb/stashdb ref and
19-20 of 20 channels are studios we already know.

Ingest is gated on a non-empty actor[]: 23% of the catalog has no cast and
those scenes could neither be attributed nor deduped. Tags and categories
come only from JSON-LD (keywords + genre), never from the HTML, which
carries 38 /category/ and 22 /tag/ nav and sidebar links per page.
Pagination is ?p=N only: the /N/ form 404s and ?page=N is silently ignored,
returning page 1.

Playback is the simplest in the portfolio: a plain <source> mp4 on twincdn
with no token, no query string and no expiry. Verified 206 on a Range
request from the VPS and from another machine in another country, both
without a Referer, so it is neither hotlink-guarded nor IP-bound and the
phone streams it directly.

Two honest caveats recorded in the module docstring: these are 5-12 minute
clips (median ~487s against 1800-2400s for the tubes we accepted), so for
the ~20% of the catalog from Tushy/Blacked Raw/Milfy/Anilos they will sit
as a short shadow next to full canonical scenes; and uploadDate is the
tube's import date, not the studio release, so pages past the second are
marked backfill.

Pilot ingest of 3 pages: 127 seen, 49 merged into existing scenes, 66 new,
0 errors; on page one the gate passed 42 of 62 links with zero scenes
missing cast, duration or studio.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-07-27 10:42:39 +02:00
goon-foss
d7442187c9 feat(fullvideosporn): browse scraper behind a cast gate + TXXX extractor
fullvideosporn.com is sextu.com rebranded, not a clone of fullmovies.xxx
(different engine, different catalog, 0/140 title overlap with our
fullmoviesxxx corpus).

The site is only worth ingesting behind a gate: 65-75% of its catalog has
no performer at all, and those scenes also carry SEO-spun titles, so they
would never match canonical and would land as empty orphans. So we ingest
only scenes with at least one performer. That keeps ~25-35% of the catalog
(verified: 19 of 60 on page one) where the signal is good, since 88-89% of
the performer names in the research sample already resolve to a canonical
performer in our DB.

Three site-specific traps, all handled:
- Titles come from the player's vit:"..." field, not og:title/h1, which are
  sometimes an AI SEO rewrite rather than the real scene title.
- Cast is read only from the <h3>Porn-stars:</h3> section; the page carries
  ~22 videos.php?q= links overall but only 1-2 real performers, the same
  pollution that got xxxfiles rejected. Porn Site / Porn Categories are
  separate h3 blocks and are parsed per-section so they don't bleed.
- Every fetch passes a cookie gate: a fresh session gets HTTP 429 plus a
  small JS challenge, so we read the cookie out of it and retry on the same
  session. Hence the custom crawl_page instead of the base browser_get.

The TXXX video_url decoder moved out of vjav into _txxx.py since both tubes
share the engine; vjav keeps an alias and was re-verified after the move.
Playback resolves videofile.php -> decode -> get_file -> 302 -> znvcdn, and
the final CDN URL is portable cross-IP so the phone streams it directly.
Note for future debugging: the VPS itself gets 429 from that CDN because of
datacenter IP reputation, so playback health-checks run from the VPS will
be falsely negative.

Pilot ingest of 2 pages: 39 seen, 11 merged into existing scenes, 28 new,
0 errors; 0/19 without cast or duration on the sampled page.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-07-27 08:56:23 +02:00
goon-foss
d716b0c75d feat(pornbusy): browse scraper + loadvid POST-manifest producer
pornbusy.com exposes every field as a discrete itemprop node (title,
duration, uploadDate, thumbnail, description) plus a dedicated
<div id="video-actors"> cast block with no sidebar pollution. Measured
orphan risk is low: 80% of a 100-scene sample have a performer that
already carries a tpdb/stashdb ref, and 21% of titles strongly match
scenes we already hold. No studio field exists on the site.

Homepage pagination is broken (pages 1/2/3 return an identical set), so
the listing is driven off sitemap_index.xml -> 10 post-sitemaps sorted by
lastmod, chunked in crawl_page the same way the PlayTube base does it.

Playback dispatches on embedURL and covers ~69% of the catalog: the
seekplayer family (already handled by the engine after the earlier host
regex widening) and zpi.cx (the embedURL is the file itself), plus loadvid
at ~41%, which needed new plumbing.

loadvid hands back the CONTENT of an m3u8 over POST /videos/resolve-token
(CSRF + videoToken from the embed page) and has no manifest URL at all:
GET on that endpoint is 405 and the guessable .m3u8 paths are 404. So
make_token grew an optional `producer` marker and /proxy/hls calls the
producer instead of GETting a URL. Segments in the returned manifest are
absolute and fully portable (verified: 206 on a Range request with no
headers, no referer, no token), so the phone still pulls them straight
from the CDN and only the manifest travels through the VPS.

upload18 (~12%) and the tail are deliberately left unresolved: their token
embeds the fetcher's /24, so resolving server-side would force the whole
video through the VPS.

Verified: 19 scenes/page, 19/19 with duration, 17/19 with cast, 19/19 with
tags (names read from the title attribute since an icon element precedes
the anchor text), playback 8/8 via loadvid, and /proxy/hls returning a
116KB manifest with 869 absolute segments. Pilot ingest of 3 pages: 59
seen, 35 merged into existing scenes, 24 new, 0 errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-07-27 08:45:00 +02:00
goon-foss
2bf9179467 feat(galaxyporn): browse scraper + extractor; widen seekplayer host regex
galaxyporn.net carries paysite rips with a dedicated <div id="video-actors">
cast block (no sidebar pollution), studio + date in the title prefix, healthy
/page/N/ pagination. Measured orphan risk is low: 92% of a 75-performer
sample already have a tpdb/stashdb ref in our DB and 79% of titles match
scenes we already hold.

seekplayer_engine host regex widened to cover seekplays|4meplayer|ezplayer|
seeks|upn and the pro|cloud|one TLDs. This is the same engine (identical
AES key/IV and /api/v1/video endpoint), just newer domains; the whitelist is
additive so existing hosts keep matching (verified, including that
upns.evil.com is still rejected). Unlocks all four galaxyporn player hosts
and the same family on other sites.

Two things the extractor had to get right:
- Call seekplayer_engine directly rather than via extract_stream_from_hoster:
  the wrapper verifies the resulting URL and the hotlink-guarded HLS 403s, so
  it discarded correctly decoded streams.
- Referer must be the PLAYER origin, not galaxyporn.net. With the site referer
  the manifest 403s; with the player origin it returns 200. Verified end to
  end: manifest 200 -> variant 200 (570 segments) -> segment 206.

Duration is absent from the HTML, JSON-LD and player payload, and a NULL
duration would silently hide scenes behind the min_duration_sec filter (the
porntrex incident), so it is computed from the player's thumbnail.vtt last
cue. The player API throttles bursts, hence the retry/backoff: that took
missing durations from 9/21 down to 3/21. The value runs ~2% short of the
true length (one thumbnail interval), which beats having none.

Pilot: 21 scenes/page with studio 21/21 and clean cast (max 3), ingest of
2 pages = 42 seen, 34 new, 8 merged, 0 errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-07-26 18:23:13 +02:00
goon-foss
5a0b62c3e4 feat(youperv): browse scraper + direct-mp4 extractor
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>
2026-07-26 17:57:24 +02:00
goon-foss
0c5148b6e8 feat(ingest): browse-latest scraper dla hqporner
Some checks are pending
Backend tests / test (push) Waiting to run
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.
2026-07-26 16:41:47 +02:00
goon-foss
7afb332fc8 fix(playback+ingest): realny proxy fallback gdy CDN nieosiagalny + porntrex duration
Some checks are pending
Backend tests / test (push) Waiting to run
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.
2026-07-26 16:34:24 +02:00
goon-foss
8c3ad2b09a feat(jav): supjav.com scraper + extractor (embed-aggregator, 4th JAV source)
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>
2026-07-10 14:10:43 +02:00
goon-foss
5b31459a26 feat(jav): vjav.com scraper + extractor (TXXX-network JAV source)
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>
2026-07-10 13:13:35 +02:00
goon-foss
4df1e01b31 feat(jav): separate JAV section (tab + feed gating) + enable javflix ingest
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>
2026-07-10 11:16:31 +02:00
goon-foss
d8d00295e1 feat(jav): javflix.cc scraper + extractor (first JAV source, gated off main feed)
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>
2026-07-10 10:58:34 +02:00
goon-foss
b66817175a feat(hqfap): restore hqfap.com scraper + extractor (site back on new CDN)
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>
2026-07-07 23:53:43 +02:00
goon-foss
cb22174657 fix(thumbnails): watchporn thumbs at ingest + backfill; SceneDetail uses live sxyprn thumb
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>
2026-07-07 23:21:59 +02:00
goon-foss
015bd5bb7a feat(watchporn): re-enable as KVS browse scraper + native mp4 extractor
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>
2026-07-02 14:32:36 +02:00
goon-foss
ce85d7d92e chore(ingest): hard-remove hqfap + 4k69 (entire CDN library gone)
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>
2026-06-25 11:07:47 +02:00
goon-foss
7177eff504 feat(ingest): add xnxx browse scraper (JSON-LD only, alongside search)
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 &comma;/&excl; 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>
2026-06-24 15:52:32 +02:00
goon-foss
ce8a9a309a feat(ingest): add youporn browse scraper (JSON-LD only, alongside search)
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>
2026-06-24 15:47:58 +02:00
goon-foss
d78670a516 feat(ingest): add browse scrapers for porntrex + mypornerleak (alongside search)
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>
2026-06-24 15:41:22 +02:00
goon-foss
1f62f7880f feat(ingest): revive porndish — search→WP REST API browse
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>
2026-06-24 15:09:27 +02:00
goon-foss
64a5dd8a3d feat(ingest): revive perverzija — search→WP REST API browse
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>
2026-06-22 13:10:16 +02:00
goon-foss
b5c3c6c7ff feat(sources): remove 0dayxx + pornditt + pornhat entirely
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>
2026-06-22 12:23:29 +02:00
goon-foss
e00a2a55d3 feat(ingest): revive fpoxxx — search→browse (KVS /new-N/)
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>
2026-06-22 12:04:05 +02:00
goon-foss
2d73fc66d9 feat(sources): remove pornhub + redtube entirely
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>
2026-06-22 11:55:08 +02:00
goon-foss
781a2adb80 feat(ingest): disable hqfap/4k69 (broken playback), latestpornvideo → browse
- 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>
2026-06-22 09:34:47 +02:00
goon-foss
ef3fa1e2ab feat(siska): convert to browse scraper, re-enable (search broken site-side)
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>
2026-06-20 16:25:11 +02:00
goon-foss
2235111c50 investigate(siska): keep disabled — site search is broken (ignores query)
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>
2026-06-20 16:15:02 +02:00
goon-foss
289844ec5a fix(latestpornvideo): revive search via /actor/ listing + metadata
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>
2026-06-16 23:20:02 +02:00
goon-foss
ad77dc918e fix(mypornerleak): revive search via /actor/ listing + metadata
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>
2026-06-16 23:16:02 +02:00
goon-foss
074db3bf49 fix(sxyland): revive search via /actor/ pages + rich metadata
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>
2026-06-16 23:11:44 +02:00
goon-foss
a769c83e81 fix(sxyprn): revive search via performer pages + rich metadata
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>
2026-06-16 22:58:52 +02:00
goon-foss
33e8581b9f fix(scrapers): freshporno browse from /latest-updates/ not homepage root
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>
2026-06-15 09:59:40 +02:00
goon-foss
1d4d6fd09c docs: correct Bright Data proxy type (ISP, flat-rate not per-GB)
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>
2026-06-10 19:18:40 +02:00
goon-foss
4919802e91 feat(superporn): browse scraper via Bright Data residential proxy
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>
2026-06-10 18:47:45 +02:00
goon-foss
35c052cd58 feat(tubes): add 4k69 + neporn browse scrapers, shared PlayTube base
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>
2026-06-10 18:15:13 +02:00
goon-foss
a919514fd0 feat(hqfap): browse scraper + native mp4 extractor (~120k scenes)
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>
2026-06-10 17:51:04 +02:00
goon-foss
e50e0c5886 fix(xvideos): parse model name from nested span.name — recover 0-performer scenes
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>
2026-06-08 10:13:21 +02:00
goon-foss
606a483893 fix(hqporner): require ALL query tokens in slug — stop performer over-attribution
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>
2026-06-08 09:28:18 +02:00
goon-foss
d146303434 refactor(ingest): rename scraper Source name "pornapp" -> "tube-scraper"
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>
2026-06-07 16:54:55 +02:00
goon-foss
98531caf14 feat(scrapers): extract tags + description from porndish scene pages
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>
2026-06-06 21:32:10 +02:00
goon-foss
671bd1d5a7 feat(deep-crawl): xvideos browse source (capped) + per-tube page cap
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>
2026-06-03 11:16:44 +02:00
goon-foss
c0c75ad3ce feat(deep-crawl): eporner via JSON API as SSR-rich source (Phase 2b alternative)
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>
2026-06-03 10:37:20 +02:00
goon-foss
a3720494c9 fix(movies): paradisehill delta date-granularity + browse cadence docs
- 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>
2026-06-01 17:00:10 +02:00
goon-foss
fc4060310f feat(ingest): SQL phash match, tag inference + backfill, clip-store skip, browse tubes, watchdog
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>
2026-06-01 15:07:35 +02:00