"""youperv.com — direct mp4 extractor (fluidplayer). Dodany 2026-07-26. Scene page ma zwykły `` — BEZ tokena, bez expiry, bez KVS/DoodStream/iframe. URL zawiera spacje (tytuł w ścieżce), więc trzeba go za-quote'ować przed podaniem playerowi. CDN ma hotlink-guard na **Referer**: goły Range → 403 nginx, Range + Referer `https://youperv.com/` + browser UA → 206 `video/mp4` (zweryfikowane cross-IP z VPS). Nie jest IP-bound, więc `mobile_direct_ok` → telefon gra prosto z CDN, zero proxy i zero WebView. playback.py dokleja Referer+UA z `referer=` do nagłówków StreamLink. """ from __future__ import annotations import logging import re from urllib.parse import quote, urlsplit, urlunsplit from app.extractors._fetch import fetch_tube_html from app.extractors._models import StreamSource log = logging.getLogger(__name__) _BASE = "https://youperv.com" _SOURCE_RE = re.compile( r']+src="([^"]+\.(?:mp4|m4v)[^"]*)"', re.IGNORECASE ) def _encode_url(url: str) -> str: """Spacje/znaki specjalne w ścieżce → percent-encoding (ExoPlayer inaczej odpada). Query zostawiamy nietknięte.""" parts = urlsplit(url) return urlunsplit( (parts.scheme, parts.netloc, quote(parts.path, safe="/%"), parts.query, parts.fragment) ) def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None: html_text = fetch_tube_html(page_url, timeout=timeout) m = _SOURCE_RE.search(html_text) if not m: log.info("youperv: no mp4 on %s", page_url) return None return [ StreamSource( link=_encode_url(m.group(1).strip()), type="mp4", referer=_BASE + "/", # Token brak; CDN pilnuje tylko Referera (cross-IP 206) → mobile direct. raw={"mobile_direct_ok": True}, ) ]