diff --git a/app/extractors/__init__.py b/app/extractors/__init__.py index 7d3a173..81ea051 100644 --- a/app/extractors/__init__.py +++ b/app/extractors/__init__.py @@ -32,6 +32,7 @@ from app.extractors.tubes import ( hqporner, latestpornvideo, paradisehill, + porn00, pornditt, pornhat, porntrex, @@ -116,15 +117,14 @@ _REGISTRY: dict[str, Callable[[str], list[StreamSource] | None]] = { # Teraz backend-resolve jak yespornvip/pornditt (_kvs używa curl_cffi chrome). # Native, multi-quality, zero proxy/WebView. (zweryfikowane na emulatorze przed deploy) "freshpornoorg": freshporno.extract, - # porn00 — KVS engine. 2026-06-01 cross-IP re-test (task #20): get_file 302 → - # `fe.porn00.org/videos/.../.mp4?token=&expires=` zwraca 403 z residential - # IP → token IP-bound do resolvera (VPS), NIE portable jak yespornvip/pornditt. - # Backend-resolve nie daje mobile-playable URL bez proxy, a video-proxy odpada - # (public app, feedback Jana). Per polityka "IP-bound CDN → WebView": switch z - # porn00.extract (force_proxy=True, łamało no-proxy) na _vps_blocked_fallback. - # Ad-risk z bug-reportów 5037b3e3/e8e3198b złagodzony przez ad-filter (31d9076: - # AD_HOSTS + coverOverlay + INJECTED_JS skip ad-CDN). - "porn00org": _vps_blocked_fallback.extract, + # porn00 — KVS (plain get_file + license). 2026-06-04 DevTools + cross-IP re-test + # NAPRAWIA błąd z #20: finalny fe.porn00.org/...?token=&expires= jest PORTABLE + # (token time-bound nie IP-bound — Bright Data residential proxy z innego IP → 206) + # ale wymaga browser-TLS (curl_cffi chrome → 206; plain curl → 403). W #20 + # testowałem finalny URL plain-curl-em → 403 → błędnie „IP-bound" → WebView. + # Teraz backend-resolve przez _kvs (curl_cffi chrome), native multi-quality, + # ZERO proxy (wcześniej force_proxy łamał no-proxy). Same mechanizm co freshporno. + "porn00org": porn00.extract, # pornxp — ` //sr.porn-xp.com//.../720.mp4` (redirect → xpxp.eu). # 2026-06-01 (task #20): 403 cross-IP → token w path IP-bound. WebView only. "pornxpph": _vps_blocked_fallback.extract, diff --git a/app/extractors/tubes/_kvs.py b/app/extractors/tubes/_kvs.py index 3a5ed94..6a7c8b8 100644 --- a/app/extractors/tubes/_kvs.py +++ b/app/extractors/tubes/_kvs.py @@ -28,8 +28,15 @@ from app.extractors._models import StreamSource log = logging.getLogger(__name__) +# Łapie OBA formaty flashvars: +# - `function/0/https://.../get_file//...` (yespornvip/pornditt/freshporno — +# zakodowany, dekodowany przez real_url) +# - `https://.../get_file/...` (porn00 — już zdekodowany plain get_file; real_url +# przepuszcza go bez zmian) +# W obu przypadkach finał to get_file → 302 → CDN (follow in-session). _URL_RE = re.compile( - r"(video(?:_alt)?_url\d*)\s*:\s*[\"'](function/0/[^\"']+)[\"']", + r"(video(?:_alt)?_url\d*)\s*:\s*[\"']" + r"(function/0/[^\"']+|https?://[^\"']*?/get_file/[^\"']*)[\"']", re.IGNORECASE, ) _TEXT_RE = re.compile( diff --git a/app/extractors/tubes/porn00.py b/app/extractors/tubes/porn00.py index e480460..6e85a3f 100644 --- a/app/extractors/tubes/porn00.py +++ b/app/extractors/tubes/porn00.py @@ -1,50 +1,24 @@ -"""porn00.org — KVS engine extractor. +"""porn00.org — KVS (kt_player) direct stream extractor. Patrz app/extractors/tubes/_kvs.py. -Detail page wbudowuje stream URLs w JS flashvars block: - - `video_url: 'https://.../get_file/.../.mp4/?v-acctoken=...'` (default, 360p) - - `video_alt_url: 'https://.../get_file/.../_720p.mp4/?v-acctoken=...'` (alt, 720p) +flashvars `video_url`/`video_alt_url` to PLAIN get_file (już zdekodowany, bez function/0): +`https://www.porn00.org/get_file/3//.../.mp4` + `license_code`. _kvs._URL_RE +łapie też ten format; real_url przepuszcza plain bez dekodowania; follow 302 → +`fe.porn00.org/videos/.../.mp4?token=&expires=` (206 video/mp4). -CDN token (`v-acctoken=...`) jest IP-bound do VPS, mobile direct fetch → 403. -playback.py wraps URL przez stream_proxy z `Referer: ` — działa. - -Get_file → 302 → direct mp4 (jak freshporno). Type='mp4'. +2026-06-04 (DevTools + cross-IP re-test, naprawia błąd z #20): finalny CDN jest +**portable cross-IP** (token time-bound nie IP-bound — zweryfikowane przez Bright Data +residential proxy: get_file z jednego IP, fetch finalnego z innego IP → 206). W #20 +testowałem finalny URL plain-curl-em → 403 → błędnie „IP-bound" → WebView. Wymaga +browser-TLS (curl_cffi chrome → 206; plain curl → 403). _kvs używa curl_cffi chrome, +mobile (ExoPlayer) gra direct. Native, multi-quality, ZERO proxy (wcześniej force_proxy). """ from __future__ import annotations -import logging -import re - -from app.extractors._fetch import fetch_tube_html from app.extractors._models import StreamSource +from app.extractors.tubes import _kvs -log = logging.getLogger(__name__) - -_VIDEO_URL_RE = re.compile( - r"""video_url:\s*['"]([^'"]+\.mp4[^'"]*)['"]""", re.IGNORECASE, -) -_VIDEO_ALT_URL_RE = re.compile( - r"""video_alt_url:\s*['"]([^'"]+\.mp4[^'"]*)['"]""", re.IGNORECASE, -) +_BASE = "https://www.porn00.org" def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None: - html = fetch_tube_html(page_url, timeout=timeout) - result: list[StreamSource] = [] - - # Preferujemy alt (720p) przed default (360p). - # CDN token `v-acctoken` jest IP-bound do VPS. Mobile direct fetch ZAWSZE → 403, - # więc oznacz force_proxy żeby player od razu używał proxified URL bez prób direct. - # Bez tego: każdy playback = "mrugnięcie" (direct fail → fallback na proxy). - proxy_flag = {"force_proxy": True} - if (m := _VIDEO_ALT_URL_RE.search(html)): - result.append(StreamSource(link=m.group(1), type="mp4", quality="720p", raw=proxy_flag)) - if (m := _VIDEO_URL_RE.search(html)): - url = m.group(1) - if not result or result[0].link != url: - result.append(StreamSource(link=url, type="mp4", quality="360p", raw=proxy_flag)) - - if not result: - log.info("porn00: no video_url flashvars on %s", page_url) - return None - - return result + return _kvs.resolve_kvs(page_url, base_url=_BASE, timeout=timeout)