"""fullporner.com — browse scraper. Dodany 2026-07-27. Pełne sceny, nie klipy: mediana długości 2067 s na stronie 1, 1911 s na 40 i 1969 s na 300, zero filmów poniżej 10 minut w próbce. To jest różnica, przez którą sexu odpadł, a ten wchodzi. Listing jest ściśle chronologiczny, mimo że nagłówek mówi „Featured Videos" (strona 1-3 = dziś, 10 = doba, 50 = 9 dni, 500 = 86 dni), 24 sceny na stronę. Daje to ~140-240 pełnych scen dziennie i katalog co najmniej 12 tys. scen wstecz. **Metadane są cienkie i trzeba to wiedzieć wprost.** Nie ma studia. Tagów jest mediana 3. Tytuły są w 100% przepisane pod SEO, więc nie niosą nic z tytułu studyjnego („POV Blowjob Queen Takes It Hard and Deep" tam, gdzie kanon ma „Blacked – Dylan Moore – Petite Blonde Squirt"). Za to data jest podana jako **unix timestamp** (dokładna, nie względna) i długość co do sekundy. Dry-run resolvera na 120 scenach (w rollbacku): 37 (31%) dopięło się do scen, które już mamy, przez `composite_auto` — composite radzi sobie mimo złych tytułów, bo waży performera i długość. Reszta to nowa podaż. Zmierzony przeciek duplikatów, po odjęciu tła z kontroli na przesuniętej długości: ~15% katalogu. Liczymy phash miniaturki właśnie po to, żeby późniejszy `bulk_dedup` miał czym te duplikaty łapać. **Bramka na obsadę.** Sceny bez performera (19%) w dry-runie scaliły się w 0 na 23 przypadków — bez performera composite nie ma na czym pracować, a studia tu nie ma i tytuł jest zmyślony, więc taka scena nie ma ŻADNEGO sygnału kanonicznego. Wchodzą tylko sceny z obsadą. Playback: `xiaoshenke.net` (extractor `fullporner`), bezpośredni mp4 do 1080p bez tokenu i bez wygasania. """ from __future__ import annotations import html as html_mod import logging import re from datetime import UTC, datetime from app.connectors.base import ( RawFingerprint, RawPerformer, RawPlaybackSource, RawScene, RawTag, ) from app.connectors.direct_scrapers._browse_base import ( BaseBrowseScraper, compute_thumbnail_phash, ) from app.normalize.text import slugify log = logging.getLogger(__name__) _BASE = "https://fullporner.com" _THUMB = "https://imgs.xiaoshenke.net/thumb/{}.jpg" _SCENE_HREF_RE = re.compile(r'href="(/watch/[a-f0-9]+)"') _TITLE_RE = re.compile(r"]*>\s*([^<]+?)\s*") # `1785081937
36:33
` — data i długość # siedzą obok siebie w `video-info`, więc bierzemy je jednym wzorcem. _INFO_RE = re.compile( r'class="create">(\d+)\s*\s*
\s*([0-9:]+)\s*
' ) _PERF_BLOCK_RE = re.compile(r"\s*Pornstars?:\s*(.*?)

", re.IGNORECASE | re.DOTALL) _PERF_RE = re.compile(r'href="/pornstar/[^"]+"[^>]*>\s*([^<]+?)\s*') _TAG_BLOCK_RE = re.compile(r'class="[^"]*tag-link[^"]*"[^>]*>(.*?)

', re.DOTALL) _TAG_RE = re.compile(r'href="/category/[^"]+"[^>]*>#\s*([^<]+?)\s*') # `