goon/app/extractors/tubes/pornxp.py
goon-foss a4486980a4 style: myslniki em/en z komentarzy i docstringow (calosc repo)
1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt,
' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety;
zakresy 0–5 i A–Z na dywiz.

Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika
stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu
komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach,
czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu,
tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana.

Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik
byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli
do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie).

Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika
to zero zmiany.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:51:57 +02:00

54 lines
1.7 KiB
Python

"""pornxp.ph, direct mp4 sources extractor.
Detail page ma `<video>` z multiple `<source>` różnych jakości:
- `<source src="//sv.porn-xp.com/.../360.mp4">`
- `<source src="//sv.porn-xp.com/.../720.mp4">`
URL protocol-relative (`//`), normalize do `https:`. CDN może być IP-bound (token
w path), playback.py i tak proxifies. Type='mp4'.
"""
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__)
# `<source src="(?:https:)?//sv.porn-xp.com/.../(360|720|1080).mp4">`
_SOURCE_RE = re.compile(
r'<source\s+src="(?P<url>(?:https?:)?//[^"]+/(?P<q>\d{3,4})\.mp4)"',
re.IGNORECASE,
)
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
html = fetch_tube_html(page_url, timeout=timeout)
seen: set[str] = set()
result: list[StreamSource] = []
# sv.porn-xp.com URL ma signed token w path, IP-bound. Mobile direct → 403,
# od razu używaj proxy żeby uniknąć mrugnięcia.
proxy_flag = {"force_proxy": True}
for m in _SOURCE_RE.finditer(html):
url = m.group("url")
if url.startswith("//"):
url = "https:" + url
if url in seen:
continue
seen.add(url)
result.append(StreamSource(link=url, type="mp4", quality=f"{m.group('q')}p", raw=proxy_flag))
if not result:
log.info("pornxp: no <source> tags on %s", page_url)
return None
# Sort by quality desc (720p > 360p)
def _q(s: StreamSource) -> int:
try:
return int((s.quality or "0").rstrip("p"))
except ValueError:
return 0
result.sort(key=_q, reverse=True)
return result