From 580e4db53508c6287b6b96796f0155f4a399adcc Mon Sep 17 00:00:00 2001 From: goon-foss Date: Tue, 28 Jul 2026 12:16:18 +0200 Subject: [PATCH] feat(perverzija): natywny HLS zamiast WebView z reklamami Bug-reports 40ec88cc / 92e9133e ('zacina sie na pierwszych sekundach i robi loop'). Trace CDP wykazal: iframe pervl4.xtremestream.xyz/player/index.php?data= ma w HTML WYLACZNIE preroll/midroll (opencdn.b-cdn.net) - to one lecialy w petli. Wideo player dociaga dopiero w runtime: GET /player/xs1.php?data=&q=<480|720> -> GOTOWY manifest HLS (#EXTM3U, VOD, segmenty ~4,17s z PELNYMI URL-ami do pervl4.xspcdn{01..10}.sa.com/cdn/down/...). Segmenty maja rozszerzenie .html (kamuflaz), ale to zwykly HLS - nic nie trzeba deszyfrowac ani skladac. Nowy extractor zwraca oba warianty jako type='m3u8' z referer=index.php?data= (taki Referer wysyla przegladarka; bez niego ryzyko 403). Zweryfikowane na prodzie: 2 zrodla, oba HTTP 200 i #EXTM3U (44245 / 41142 B). Uwaga: sciezka manifestu nie konczy sie na .m3u8, wiec musi isc przez /proxy/hls passthrough (casus pornhat) - segmenty i tak leca direct z CDN. --- app/extractors/__init__.py | 7 ++- app/extractors/tubes/perverzija.py | 72 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 app/extractors/tubes/perverzija.py diff --git a/app/extractors/__init__.py b/app/extractors/__init__.py index 74b0098..cfcdb86 100644 --- a/app/extractors/__init__.py +++ b/app/extractors/__init__.py @@ -34,6 +34,7 @@ from app.extractors.tubes import ( galaxyporn, hdporngg, hqfap, + perverzija, hqporner, fullporner, kporner8, @@ -188,7 +189,11 @@ _REGISTRY: dict[str, Callable[[str], list[StreamSource] | None]] = { # 0dayxx + pornditt + pornhat — USUNIĘTE CAŁKOWICIE 2026-06-22 (user request): orphan # factories (0–0.2% canonical match), zastępujemy lepszymi źródłami. Dane skasowane. # CF-protected tube — curl_cffi w fetch_tube_html bypassa JA3, embed-iframe pattern. - "perverzijacom": _embed_iframe.extract, + # perverzija — natywny HLS (2026-07-28). Player xtremestream ma w HTML tylko + # preroll/midroll (grały w pętli, bug 40ec88cc/92e9133e), a wideo dociąga runtime: + # xs1.php?data=&q=<480|720> zwraca GOTOWY manifest HLS z pełnymi URL-ami + # segmentów. Zamiast WebView oddajemy m3u8 wprost. + "perverzijacom": perverzija.extract, # Special: WebView-only (Yii2 session-bound player). "paradisehillcc": paradisehill.extract, # PornDoe — dołączony 2026-05-21 (theporndude audit). Stream URL nie inline w diff --git a/app/extractors/tubes/perverzija.py b/app/extractors/tubes/perverzija.py new file mode 100644 index 0000000..6ddc10d --- /dev/null +++ b/app/extractors/tubes/perverzija.py @@ -0,0 +1,72 @@ +"""perverzija.com (tube.perverzija.com) — natywny HLS zamiast WebView. + +Ustalone trace'em CDP (2026-07-28, bug-reports 40ec88cc / 92e9133e "zacina się na +pierwszych sekundach i robi loop"): + +Strona osadza iframe `pervl4.xtremestream.xyz/player/index.php?data=`. Samo źródło +tego iframe'a NIE zawiera streamu (jest tam wyłącznie preroll/midroll z opencdn.b-cdn.net +— to one leciały w pętli). Player dociąga wideo dopiero w runtime: + + GET /player/xs1.php?data=&q=<480|720> → GOTOWY manifest HLS (#EXTM3U, VOD, + segmenty po ~4,17 s z PEŁNYMI URL-ami do pervl4.xspcdn{01..10}.sa.com/cdn/down/...) + +Segmenty mają rozszerzenie `.html` (kamuflaż), ale to zwykły HLS — nic nie trzeba +deszyfrować ani składać. Wystarczy oddać URL `xs1.php` jako `type='m3u8'`. + +WAŻNE (mobile): ścieżka manifestu NIE kończy się na `.m3u8`, a expo-video bez contentType +potraktowałby ją jako progressive i padł — dokładnie casus pornhat. Dlatego manifest musi +iść przez `/proxy/hls` (passthrough); segmenty i tak lecą direct z CDN, więc przez VPS +przechodzi ~41 KB, nie wideo. + +`referer` celowo wskazuje na `index.php?data=` — taki Referer wysyła przeglądarka +przy wywołaniu xs1.php i bez niego jest ryzyko 403. +""" +from __future__ import annotations + +import logging +import re + +from app.extractors._models import StreamSource +from app.extractors import browser_get + +log = logging.getLogger(__name__) + +# iframe playera: https://pervl4.xtremestream.xyz/player/index.php?data=<32 hex> +_PLAYER_IFRAME_RE = re.compile( + r"https?://(?P[a-z0-9\-]+\.xtremestream\.[a-z]+)/player/index\.php\?data=(?P[0-9a-f]{16,64})", + re.IGNORECASE, +) + +# Player wystawia te dwie jakości (trace: xs1.php?...&q=480 i &q=720). Kolejność = +# preferencja (najlepsza pierwsza), tak jak w pozostałych ekstraktorach. +_QUALITIES = ("720", "480") + + +def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None: + try: + res = browser_get(page_url, timeout=timeout) + html = res.text if hasattr(res, "text") else str(res) + except Exception as e: + log.info("perverzija page fetch failed %s: %s", page_url, e) + return None + + m = _PLAYER_IFRAME_RE.search(html) + if not m: + log.info("perverzija: brak iframe xtremestream na %s", page_url) + return None + + host, vid = m.group("host"), m.group("id") + player_url = f"https://{host}/player/index.php?data={vid}" + + out: list[StreamSource] = [] + for q in _QUALITIES: + out.append( + StreamSource( + link=f"https://{host}/player/xs1.php?data={vid}&q={q}", + quality=f"{q}p", + type="m3u8", + referer=player_url, + raw={"extractor": "perverzija", "player": player_url, "q": q}, + ) + ) + return out or None