goon/app/extractors/tubes/yespornvip.py
jtrzupek 81090ca8d2 fix(extractors): mixdrop hardening, yespornvip extractor, freshporno revert
Mixdrop (bug #3/#10 czarny ekran): wymagane UA+Accept headers (bez nich shell
bez P.A.C.K.E.R.). Detect dead-video page -> raise HosterDead zamiast None
(mobile dostaje skip-to-next sygnal). Dispatch regex obejmuje nowy canonical
domain `miixdrop` (double-i).

Yespornvip (bug #1): nowy KVS engine extractor. Origin `tube:yespornvip`
istnial w playback_sources ale brak handlera w _REGISTRY -> try_extract None.
Flashvars `video_url: 'function/0/<get_file_url>'`, function/0 to passthrough.
480p mp4 z mobile_direct_ok=True.

Freshporno (bug #9 revert): wrocony na _vps_blocked_fallback (WebView path).
Krotko-zywy switch na native extract z force_proxy=True cofniety bo app idzie
publicznie - VPS bandwidth/anonimowosc priorytet nad UX flicker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:23:37 +02:00

77 lines
2.6 KiB
Python

"""yesporn.vip — KVS engine direct stream extractor.
User bug-report 2026-05-27: "Yespornvip dalej nie działa". Origin `tube:yespornvip`
istniał w playback_sources ale brak wpisu w `_REGISTRY` → `try_extract()` zwracał
None → mobile player no-source.
Detail page sceny linkuje do `/embed/<id>` w iframe. Embed page renderuje KVS
player z `flashvars`:
- `video_url: 'function/0/https://yesporn.vip/get_file/<srv>/<token>/<bucket>/<id>/<id>.mp4/?embed=true'`
- `event_reporting2: 'https://yesporn.vip/get_file/.../<id>.mp4/'` (analytics ping
URL, ale jest valid get_file)
- `video_url_text: '480p'` — quality label dla video_url
`function/0/` to KVS player JS dekoder prefix — dla type 0 to passthrough,
URL po prefixie jest bezpośrednio użyteczny.
Single-quality (480p) z embed bo wyższe (`video_alt_url`) to redirect URLs
(`video_alt_url_redirect: '1'`), nie direct streamy. CDN time-bound signed,
mobile gra direct.
"""
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://yesporn.vip"
# `video_url: 'function/0/https://.../get_file/...mp4/?embed=true'` lub bez prefixu
_VIDEO_URL_RE = re.compile(
r"video_url\s*:\s*'(?:function/0/)?(https?://[^']+/get_file/[^']+\.mp4/?[^']*)'",
re.IGNORECASE,
)
_QUALITY_RE = re.compile(r"video_url_text\s*:\s*'([^']*)'", re.IGNORECASE)
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
if "/embed/" not in page_url:
# Detail page → derive embed URL via /video/<id>/<slug>/ → /embed/<id>.
m = re.search(r"/video/(\d+)/", page_url)
if m:
embed_url = f"{_BASE}/embed/{m.group(1)}"
else:
log.info("yespornvip: cannot derive embed from %s", page_url)
return None
else:
embed_url = page_url
html = fetch_tube_html(embed_url, timeout=timeout)
m = _VIDEO_URL_RE.search(html)
if not m:
log.info("yespornvip: no video_url in flashvars on %s", embed_url)
return None
url = m.group(1)
# `?embed=true` parametr — get_file z embed=true może zwracać HTML wrapper
# zamiast 302 do CDN. Zostawiamy bo player tak go używa, ale jeśli 302 nie
# wskoczy poprawnie to fallback usunie param.
quality = None
q_match = _QUALITY_RE.search(html)
if q_match:
quality = q_match.group(1).strip() or None
return [
StreamSource(
link=url,
type="mp4",
quality=quality,
referer=_BASE + "/",
raw={"mobile_direct_ok": True},
)
]