"""rule34video.com — KVS `get_file` ZWIĄZANY Z IP. Dodany 2026-08-13. Silnik to KVS, ale bez zaciemniania: flashvars trzymają cztery gotowe URL-e, bez `function/0/` i bez potrzeby dekodowania `license_code`: video_url: …/get_file/51//4543000/4543893/4543893_360.mp4/?v-acctoken=… video_alt_url: … _480p.mp4 (video_alt_url_text: '480p') video_alt_url2: … _720p.mp4 (video_alt_url2_text: '720p') video_alt_url3: … _1080p.mp4 (video_alt_url3_text: '1080p') **`v-acctoken` jest związany z IP tego, kto pobrał STRONĘ.** Test krzyżowy 2026-08-13: URL wygenerowany przez proxy i pobrany z VPS-a → **403**; ten sam URL pobrany z IP, które go wygenerowało → 206, `ftypisom`, 70 MB. Po zdekodowaniu (`4533|163838|0|`) adresu nie widać wprost, w odróżnieniu od fpoxxx, gdzie IP siedziało w treści tokenu — czyli samo oglądanie tokenu NIE wystarcza, żeby to wykryć, trzeba zrobić test krzyżowy. Dlatego **NIE ma tu `mobile_direct_ok`**: URL zresolwowany na VPS-ie jest dla telefonu martwy. Zamiast tego oddajemy go jako zwykły link `get_file`, a resztę robi istniejąca maszyneria po stronie telefonu: `SceneDetailScreen` wykrywa `isGetFileUrl()` i JESZCZE PRZED wejściem do playera pobiera stronę z telefonu (token dla IP telefonu), a `getfileResolver` wyciąga z niej flashvars. Ten sam mechanizm obsługuje hdporn.gg i fullmovies.xxx, tam różnicą jest tylko `` zamiast flashvars. Backend zwraca więc URL-e po to, żeby apka znała listę jakości i etykiety, nie po to, żeby je odtwarzać z VPS-a. """ from __future__ import annotations import logging import re from app.extractors._fetch import fetch_tube_html from app.extractors._models import StreamSource log = logging.getLogger(__name__) _BASE = "https://rule34video.com" _URL_RE = re.compile(r"(video_(?:alt_)?url\d*)\s*:\s*'(https?://[^']+\.mp4[^']*)'") _LABEL_RE = re.compile(r"(video_(?:alt_)?url\d*)_text\s*:\s*'([^']{1,12})'") #: Kolejność od najlepszej jakości. 4K nie występuje, serwis kończy na 1080p. _QUALITY_RANK = {"1080p": 0, "720p": 1, "480p": 2, "360p": 3} def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None: html_text = fetch_tube_html(page_url, timeout=timeout) labels = dict(_LABEL_RE.findall(html_text)) out: list[StreamSource] = [] seen: set[str] = set() for var, link in _URL_RE.findall(html_text): if link in seen: continue seen.add(link) out.append( StreamSource( link=link, type="mp4", quality=labels.get(var), referer=_BASE + "/", # Bez `mobile_direct_ok`: token jest IP-bound, telefon resolwuje sam. raw={}, ) ) if not out: log.info("rule34video: brak video_url we flashvars na %s", page_url) return None out.sort(key=lambda s: _QUALITY_RANK.get((s.quality or "").lower(), 99)) return out