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

77 lines
3.1 KiB
Python

"""fullporner.com, bezpośredni mp4 z xiaoshenke.net. Dodany 2026-07-27.
Najprostszy i najtrwalszy stream w całym portfolio: URL buduje się deterministycznie,
**bez tokenu, bez podpisu i bez wygasania**. Nie ma tu nic do resolvowania po stronie
hostera, wystarczy przeczytać dwie liczby z iframe'a.
Strona sceny osadza `<iframe src="//xiaoshenke.net/video/4458723/14">`, a player robi
z tego:
var id = "4458723".split('').reverse().join(''); // id mediów zapisane WSPAK
function btq(f) { 1→360, 2→480, 4→720, 8→1080 } // /14 = 8+4+2 = 1080,720,480
v.media = `//${location.hostname}/vid/${id}/${quality}`
czyli `https://xiaoshenke.net/vid/3278544/1080`. Że id jest odwrócone, potwierdza
miniaturka z listingu: `imgs.xiaoshenke.net/thumb/3278544.jpg`.
**Warunek dostępu to Referer + jakikolwiek realistyczny User-Agent.** Sprawdzone
osobno, bo to przesądza o `mobile_direct_ok`: zwykły httpx (a więc NIE fingerprint
TLS Chrome'a) z Refererem i UA ExoPlayera dostaje 206, a ten sam request bez UA
dostaje 403. Blokada jest więc na nagłówku, nie na fingerprincie, i telefon ją
przechodzi, `playback.py` wysyła oba nagłówki.
Zweryfikowane: 1080p = 1,51 GB, 720p = 754 MB, 206 na Range, `ftypisom` w pierwszym
KB (faststart, seek działa od razu). Przy błędzie player dokleja `/b` (backup CDN),
nie używamy tego, bo podstawowy URL działa, ale gdyby zaczęło sypać 404, to jest
pierwsza rzecz do sprawdzenia.
"""
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://fullporner.com"
# Id bywa numeryczne (`4458723`) ALBO szesnastkowe (`a6487a97766a6`), to drugie to
# ~25% katalogu i przy `\d+` wypadało z ingestu. Odwracanie działa dla obu, bo player
# robi zwykły reverse stringa.
_IFRAME_RE = re.compile(r'src="(?://)?[^"]*xiaoshenke\.net/video/([0-9a-f]+)/(\d+)"')
# Maska bitowa z URL-a iframe'a, 1:1 z `btq()` w playerze.
_QUALITY_BITS = ((1, 360), (2, 480), (4, 720), (8, 1080))
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
html_text = fetch_tube_html(page_url, timeout=timeout)
m = _IFRAME_RE.search(html_text)
if not m:
log.info("fullporner: brak iframe xiaoshenke na %s", page_url)
return None
media_id = m.group(1)[::-1] # id w iframe jest wspak
mask = int(m.group(2))
out: list[StreamSource] = []
for bit, height in _QUALITY_BITS:
if not mask & bit:
continue
out.append(
StreamSource(
link=f"https://xiaoshenke.net/vid/{media_id}/{height}",
type="mp4",
quality=f"{height}p",
# Bez Referera CDN oddaje 404, bez UA 403, oba dokłada playback.py.
referer=_BASE + "/",
raw={"mobile_direct_ok": True},
)
)
if not out:
log.info("fullporner: maska jakości %s nie dała żadnej rendycji na %s", mask, page_url)
return None
out.sort(key=lambda s: int((s.quality or "0p")[:-1]), reverse=True)
return out