goon/app/extractors/tubes/kporner8.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

80 lines
2.8 KiB
Python

"""8kporner.com, JWPlayer sources → direct mp4 na OK.ru CDN. Dodany 2026-07-27.
**Pułapka, o którą łatwo się rozbić:** JSON-LD `contentUrl` na tej stronie to ZAWSZE
144p (256x144, sprawdzone). Prawdziwe jakości siedzą wyłącznie w tablicy `sources`
JWPlayera. Skopiowanie wzorca z `hqfap.py` (który czyta `contentUrl`) wysłałoby
każdemu userowi 144p.
Fetch idzie przez `?link1=watch&id=<id>`, bo kanoniczna forma `/watch/<slug>_<id>.html`
jest CF-blokowana (403, również przez proxy).
CDN to `vd*.okcdn.ru` / `ok6-*.vkuser.net`, ta sama rodzina co 4k69/hqfap: `srcIp`
jest w URL-u, ale NIE jest egzekwowane, więc token działa cross-IP (mobile_direct).
**Limit przepustowości:** OK.ru dławi pojedyncze połączenie do ~2,1 Mbps. 1080p
potrzebuje 3,8 Mbps, 2K 6,2, a 4K 13,8, czyli nie dociągną się w czasie rzeczywistym
i dałyby „loading forever" (ten sam objaw co przy 4K na fullmovies). Dlatego oddajemy
maksymalnie 720p (1,5 Mbps), zgodnie z decyzją podjętą już dla 4k69.
"""
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://8kporner.com"
_SCENE_ID_RE = re.compile(r"_(\d+)\.html")
# Pary z JWPlayer setupu: "file":"<url>","label":"720p"
_SOURCE_RE = re.compile(
r'"file"\s*:\s*"(https?://[^"]+)"\s*,\s*"label"\s*:\s*"([^"]+)"', re.IGNORECASE
)
# Powyżej 720p CDN nie nadąża (patrz docstring).
_MAX_HEIGHT = 720
def _height(label: str) -> int:
lab = (label or "").strip().lower()
if lab.startswith("4k"):
return 2160
if lab.startswith("2k"):
return 1440
m = re.match(r"(\d{3,4})", lab)
return int(m.group(1)) if m else 0
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
# Kanoniczny URL → forma omijająca CF.
m = _SCENE_ID_RE.search(page_url)
fetch_url = f"{_BASE}/?link1=watch&id={m.group(1)}" if m else page_url
html_text = fetch_tube_html(fetch_url, timeout=timeout)
seen: set[str] = set()
out: list[StreamSource] = []
for sm in _SOURCE_RE.finditer(html_text):
url = sm.group(1).replace("&amp;", "&")
label = sm.group(2).strip()
h = _height(label)
if url in seen or h == 0 or h > _MAX_HEIGHT:
continue
seen.add(url)
out.append(
StreamSource(
link=url,
type="mp4",
quality=label,
referer=_BASE + "/",
# srcIp nieegzekwowane (jak 4k69) → telefon gra direct z CDN.
raw={"mobile_direct_ok": True},
)
)
if not out:
log.info("8kporner: brak sources <=720p na %s", page_url)
return None
out.sort(key=lambda s: _height(s.quality or ""), reverse=True)
return out