"""fullvideosporn.com (= sextu.com pod nową marką) — TXXX network, direct mp4. To NIE jest klon fullmovies.xxx (inny silnik, inny katalog — 0/140 pokrycia tytułów). **Bramka cookie**: pierwszy request z nowej sesji dostaje HTTP 429 + 342-bajtowy JS challenge ustawiający ciasteczko (`document.cookie = 'PxeA3f=; max-age=600'`). Wystarczy wyregexować parę nazwa=wartość i powtórzyć request — bez proxy, bez przeglądarki. Ciasteczko żyje 600 s, więc trzymamy je w sesji modułu. Stream (identyczny jak vjav, rodzina TXXX): GET /api/videofile.php?video_id=&lifetime=8640000 → [{"format":"_lq.mp4","video_url":"",...}] dekod (`_txxx.decode_video_url`) → `/get_file/...` (relatywny, prepend host) GET get_file bez follow → 302 → `https://sextuN.znvcdn.com/t=.../....mp4` `_lq.mp4` w nazwie myli — to realnie 1280x720 (~1,29 Mbps), jedyny dostępny format. **Cross-IP**: finalny URL CDN wybity z IP VPS gra z residential (206, `video/mp4`, bez Referera) → token NIE jest IP-bound, `mobile_direct_ok`. ALE sam VPS dostaje od CDN 429 (reputacja IP datacenter), więc **health-check playbacku z VPS będzie fałszywie negatywny** — nie traktuj tego jako regresji. """ from __future__ import annotations import json import logging import re from urllib.parse import urlparse from app.extractors._fetch import _DEFAULT_IMPERSONATE, _DEFAULT_UA, _HAS_CURL_CFFI from app.extractors._models import StreamSource from app.extractors.tubes import _txxx log = logging.getLogger(__name__) _BASE = "https://fullvideosporn.com" _VIDEO_ID_RE = re.compile(r"/video/(\d+)/") # `