"""Wspolna sonda dla pomiaru i przemialu martwych zrodel. Trzymamy to w jednym miejscu, zeby bramka w `tube_reap_dead.py` sprawdzala DOKLADNIE ta sama sonde, ktora potem mieli baze. Gdyby to byly dwie kopie, bramka moglaby przepuscic przemial oparty na innym zachowaniu niz to, ktore przetestowala. """ from __future__ import annotations import re import time from urllib.parse import urlsplit, urlunsplit from app.extractors import try_extract from app.extractors._models import HosterDead, TubePageError ALIVE, DEAD, UNKNOWN = "alive", "dead", "unknown" _SPACING_SEC = 0.4 # Ciag cyfr dosc dlugi, zeby byl numerem sceny, a nie fragmentem tytulu. _NUM_RE = re.compile(r"\d{4,}") # Segment wygladajacy na identyfikator, a nie na tytul: bez mysinikow i dosc dlugi, # za to z cyframi (np. eporner `000goU7Teov`, xnxx `video-10003hb8`). _ID_SEG_RE = re.compile(r"^[A-Za-z0-9_.-]{6,}$") _ZNACZNIK = "99899989" def bogus_url(url: str) -> str | None: """Prawdziwy adres -> adres sceny, ktora na pewno nie istnieje. Sluzy do kontroli negatywnej: jesli taki adres wraca ZYWY, to znaczy, ze dla tego tuba w ogole nie widzimy smierci i jego wynik trzeba odrzucic, a nie czytac jako "brak trupow". Pierwsza wersja podmieniala po prostu ostatni ciag cyfr, przez co nie budowala nic dla tubow BEZ numeru sceny w adresie (`hentaigasm.com/1-funkan-dake-.../`, `mypornerleak.com/1st-double-vag-.../`). Takie tuby wychodzily w raporcie jako "0/0", czyli niezmierzone, a bylo ich okolo 950 tys. zrodel. Dlatego kolejno: 1. numer w parametrach zapytania (`siska.video/video.php?videoID=100007`), 2. segment sciezki bedacy identyfikatorem (`/video/39776/`, `/386900.html`, `/hd-porn/000goU7Teov/`, `/video-10003hb8/`), 3. gdy identyfikatora nie ma, tub routuje po samym slugu, wiec psujemy slug. Kolejnosc ma znaczenie: slug psujemy DOPIERO na koncu, bo czesc tubow routuje po samym numerze i sluga nie sprawdza, wiec zepsuty slug oddalby 200 i falszywie wygladal na slepote sondy. """ parts = urlsplit(url) if parts.query and _NUM_RE.search(parts.query): q = _NUM_RE.sub(_ZNACZNIK, parts.query, count=1) return urlunsplit((parts.scheme, parts.netloc, parts.path, q, parts.fragment)) segs = parts.path.split("/") idx = [i for i, s in enumerate(segs) if s] if not idx: return None def _rozbij(seg: str) -> tuple[str, str]: rdzen, _, ext = seg.rpartition(".") return (rdzen, ext) if rdzen else (seg, "") def _zloz(i: int, nowy: str, ext: str) -> str: segs[i] = f"{nowy}.{ext}" if ext else nowy return urlunsplit( (parts.scheme, parts.netloc, "/".join(segs), parts.query, parts.fragment) ) # Przebieg 1: segment bedacy CALY identyfikatorem (`/video/39776/`, `/386900.html`, # `/hd-porn/000goU7Teov/`). Segmenty czysto literowe pomijamy, bo to nazwa trasy # (`videos`, `episodes`, `watch`). Podmiana trasy sprawdzalaby, czy tub obsluguje # dany adres, a nie czy istnieje scena, wiec tub oddajacy 404 na zla trase i 200 na # nieistniejaca scene FALSZYWIE zdalby bramke. To jedyny kierunek bledu, ktory boli. for i in reversed(idx): rdzen, ext = _rozbij(segs[i]) if "-" in rdzen or not _ID_SEG_RE.match(rdzen): continue if rdzen.isdigit() or (rdzen.isalnum() and any(c.isdigit() for c in rdzen)): nowy = ( _NUM_RE.sub(_ZNACZNIK, rdzen, count=1) if _NUM_RE.search(rdzen) else rdzen[:-2] + "zq" ) return _zloz(i, nowy, ext) # Przebieg 2: identyfikator wtopiony w slug (`/video-10003hb8/`, # `/videos/01082023-a-bj-to-remember/`, `..._17800085.html`). for i in reversed(idx): rdzen, ext = _rozbij(segs[i]) if _NUM_RE.search(rdzen): return _zloz(i, _NUM_RE.sub(_ZNACZNIK, rdzen, count=1), ext) # Przebieg 3: identyfikatora nie ma, tub routuje po samym slugu, wiec psujemy slug. last = idx[-1] rdzen, _, ext = segs[last].rpartition(".") if rdzen: segs[last] = f"{rdzen}-zqx9nieistnieje.{ext}" else: segs[last] = f"{segs[last]}-zqx9nieistnieje" return urlunsplit((parts.scheme, parts.netloc, "/".join(segs), parts.query, parts.fragment)) def probe(sitetag: str, url: str) -> tuple[str, str]: """(werdykt, szczegol) dla jednego adresu. Szczegol sluzy tylko do raportu. Idzie przez `try_extract`, czyli ten sam kod, ktorego uzywa aplikacja przy odtwarzaniu, wiec sonda nie moze "widziec" czegos innego niz user. DEAD pada TYLKO na twardych sygnalach: HTTP 404/410 albo `HosterDead` (soft-404, np. sxyprn "Post Not Found"). Wszystko inne, lacznie z pustym wynikiem ekstraktora, to UNKNOWN i zrodla nikt nie rusza. Odwrotne ustawienie schowaloby zywe sceny po cichej zmianie markupu, tak jak przy porntrex (6479 scen). """ time.sleep(_SPACING_SEC) try: out = try_extract(sitetag, url) except TubePageError as e: if e.status_code in (404, 410): return DEAD, f"HTTP {e.status_code}" # 403, 5xx, rate limit: NIE jest dowodem na nic. return UNKNOWN, f"HTTP {e.status_code}" except HosterDead: return DEAD, "soft-404" except Exception as e: return UNKNOWN, type(e).__name__ return (ALIVE, "") if out else (UNKNOWN, "ekstraktor zwrocil pusto")