diff --git a/app/connectors/direct_scrapers/watchporn.py b/app/connectors/direct_scrapers/watchporn.py index 839a566..4ba5526 100644 --- a/app/connectors/direct_scrapers/watchporn.py +++ b/app/connectors/direct_scrapers/watchporn.py @@ -32,6 +32,21 @@ _SCENE_URL_RE = re.compile(r'href="(https://watchporn\.to/video/\d+/[a-z0-9\-]+/ _MODEL_RE = re.compile(r'href="https://watchporn\.to/models/([a-z0-9\-]+)/"[^>]*>([^<]+)', re.IGNORECASE) _TAG_RE = re.compile(r'href="https://watchporn\.to/tags/([a-z0-9\-]+)/"[^>]*>([^<]+)', re.IGNORECASE) _CAT_RE = re.compile(r'href="https://watchporn\.to/categories/([a-z0-9\-]+)/"[^>]*>([^<]+)', re.IGNORECASE) +# Nowy motyw od ok. 2026-10-10 (prefiks klas `wp-`). Obsada sceny siedzi TYLKO w bloku +# `
`, nazwa w `` (nie w tekscie linku), +# a karty "related" ponizej maja wlasne linki `/models/` z nazwa w tekscie. Stary globalny +# regex gubil prawdziwa obsade (pusty tekst linku) i dokladal performerow z kart related: +# srednio 12,5 na scene zamiast 2 (10 do 13.10, 193 sceny). Kategorie to teraz gatunki +# (chipy `wp-vchip`), studio jest w linku `/sites//` i w JSON-LD "publisher". +_NEW_THEME_MARK = 'class="wp-vi__panel"' +_CASTROW_RE = re.compile(r'
(.*?)
', re.IGNORECASE | re.DOTALL) +_CAST_RE = re.compile( + r'href="https://watchporn\.to/models/([a-z0-9\-]+)/"[^>]*>.*?([^<]+)', + re.IGNORECASE | re.DOTALL, +) +_CHIPS_RE = re.compile(r'
(.*?)
', re.IGNORECASE | re.DOTALL) +_SITE_RE = re.compile(r'href="https://watchporn\.to/sites/([a-z0-9\-]+)/"', re.IGNORECASE) +_PUBLISHER_RE = re.compile(r'"publisher"\s*:\s*\{[^}]*?"name"\s*:\s*"([^"]+)"', re.DOTALL) _DUR_RE = re.compile(r'"duration"\s*:\s*"([^"]+)"') _UPLOAD_RE = re.compile(r'"uploadDate"\s*:\s*"([^"]+)"') _VIDEO_ID_RE = re.compile(r"/video/(\d+)/") @@ -74,9 +89,18 @@ class WatchPornScraper(BaseBrowseScraper): um = _UPLOAD_RE.search(detail_html) release_date = _parse_iso_date(um.group(1)) if um else None + new_theme = _NEW_THEME_MARK in detail_html + performers: list[RawPerformer] = [] seen_p: set[str] = set() - for m in _MODEL_RE.finditer(detail_html): + if new_theme: + # Tylko blok obsady; brak bloku = scena bez obsady, NIE wracamy do globalnego + # regexu, bo ten zbiera karty related. + crm = _CASTROW_RE.search(detail_html) + cast_iter = _CAST_RE.finditer(crm.group(1)) if crm else iter(()) + else: + cast_iter = _MODEL_RE.finditer(detail_html) + for m in cast_iter: slug = m.group(1) name = html.unescape(m.group(2)).strip() if slug.isdigit() or slug in seen_p or not name: @@ -86,26 +110,45 @@ class WatchPornScraper(BaseBrowseScraper): tags: list[RawTag] = [] seen_t: set[str] = set() - for m in _TAG_RE.finditer(detail_html): - slug = m.group(1) - name = html.unescape(m.group(2)).strip() + + def _add_tag(slug: str, raw_name: str) -> None: + name = html.unescape(raw_name).strip().lstrip("#").strip() if slug in seen_t or not name: - continue + return seen_t.add(slug) tags.append(RawTag(external_id=f"{self.sitetag}:tag:{slug}", name=name, slug=slug)) + for m in _TAG_RE.finditer(detail_html): + _add_tag(m.group(1), m.group(2)) + if new_theme: + # Kategorie-chipy sceny (Blowjob, MILF...) to gatunki, ida do tagow. + chm = _CHIPS_RE.search(detail_html) + if chm: + for m in _CAT_RE.finditer(chm.group(1)): + _add_tag(m.group(1), m.group(2)) + thumbnail_url = ( meta_content(detail_html, property="og:image") or _derive_thumb(scene_url) ) studio: RawStudio | None = None - cm = _CAT_RE.search(detail_html) - if cm: - cname = html.unescape(cm.group(2)).strip() - if cname: - studio = RawStudio( - external_id=f"{self.sitetag}:studio:{slugify(cname)}", name=cname, slug=slugify(cname) - ) + if new_theme: + sm = _SITE_RE.search(detail_html) + if sm: + pm = _PUBLISHER_RE.search(detail_html) + cname = html.unescape(pm.group(1)).strip() if pm else sm.group(1).replace("-", " ").title() + if cname: + studio = RawStudio( + external_id=f"{self.sitetag}:studio:{slugify(cname)}", name=cname, slug=slugify(cname) + ) + else: + cm = _CAT_RE.search(detail_html) + if cm: + cname = html.unescape(cm.group(2)).strip() + if cname: + studio = RawStudio( + external_id=f"{self.sitetag}:studio:{slugify(cname)}", name=cname, slug=slugify(cname) + ) return RawScene( external_id=f"{self.sitetag}:{scene_url}",