"""Wybór najlepszego iframe-hostera z page HTML tube'a typu wrapper. Tube'y typu siska/perverzija/latestpornvideo nie hostują player'a same — embedują zewnętrznych hosterów (luluvid, doodporn, mixdrop, streamtape, ...). Detail page zawiera typowo 2-5 iframes: - ad-iframes (willingcease.com, popads, discord, about:blank) - dead hosters (streamtape malware, openload offline) - file-hosters (rapidgator, nitroflare — premium walled) - "fake" hosters typu playmogo (niszowe forki, brak extractora) - real KVS hosters (luluvid, doodporn, mixdrop, voe — `extract_stream_from_hoster` radzi sobie z nimi przez KVS markers + yt-dlp generic) `extract_best_iframe()` filtruje śmieci i preferuje hostery known-working z naszej implementacji. Używane przez scrapery (siska, latestpornvideo, perverzija) ŚW 2-etapowym scrape: page listing → URL sceny → fetch detail → pick iframe → save jako `embed_url` w RawPlaybackSource. """ from __future__ import annotations import re from app.extractors.tubes._embed_iframe import DEAD_HOSTER_RE # Ad / popup iframes — popunder networks + Discord widget + about:blank. # Niektóre tube'y embedują kilka takich PRZED prawdziwym player'em żeby # zmonetyzować ruch — naszego extractora trzeba przeskoczyć żeby trafić na video. _AD_IFRAME_RE = re.compile( r"willingcease\.com" r"|popads|popunder|adsterra" r"|discord\.com/widget" r"|about:blank", re.IGNORECASE, ) # Hostery których NIGDY nie obsłużymy direct extract (premium / file storage / # proprietary players bez KVS / playmogo-style niche). Mobile WebView może je # otworzyć, ale priorytet w pick'u to im trafić jako fallback NA OSTATNIM # miejscu, nie pierwszym. _LOW_PRIORITY_HOSTERS_RE = re.compile( r"rapidgator|nitroflare|filer\.net|frdl\." r"|playmogo\.com" # generic player redirect, brak ext r"|easyvidplayer|embedseek|upns\.online|seekplayer|rpmplay", re.IGNORECASE, ) # Hostery które MAMY potwierdzone że działają z `extract_stream_from_hoster`. # Kolejność = priority list (lewy = najbardziej preferowany). _PREFERRED_HOSTERS = ( "luluvid", "lulustream", "doodporn", "doodstream", "dood.la", "mixdrop", "voe.sx", # czasem działa ) def _iframe_priority(url: str) -> int: """Lower = better. 0..N dla preferred, 500 dla nieznanych, 1000 low-priority.""" if _LOW_PRIORITY_HOSTERS_RE.search(url): return 1000 for i, h in enumerate(_PREFERRED_HOSTERS): if h in url.lower(): return i return 500 _IFRAME_SRC_RE = re.compile(r']+src=["\']([^"\']+)["\']', re.IGNORECASE) def extract_best_iframe(html: str, *, base_url: str | None = None) -> str | None: """Zwraca URL pierwszego iframe NIE-ad, NIE-dead, posortowanego po priority. `base_url` używany do resolwowania protocol-relative (//host/path) i relative URL-i (/embed/xxx) na absolute. Jeśli None i iframe jest relative, jest pomijany. """ if not html: return None urls: list[str] = [] for m in _IFRAME_SRC_RE.finditer(html): src = m.group(1).strip() if not src or src.lower() == "about:blank": continue if src.startswith("//"): src = "https:" + src elif src.startswith("/") and base_url: from urllib.parse import urljoin src = urljoin(base_url, src) urls.append(src) if not urls: return None clean = [] for u in urls: if _AD_IFRAME_RE.search(u): continue if DEAD_HOSTER_RE.search(u): continue clean.append(u) if not clean: return None clean.sort(key=_iframe_priority) return clean[0]