goon/app/extractors/tubes/xanimeporn.py
goon-foss 45fbe0412e fix: hentaimama i xanime graja, filmy mangoporn z hosterami, ostatni link filmu chroniony
hentaimama: nowy zapis id posta i 5 playerow w admin-ajax, adresy w JSON-ie;
bierzemy mp4 z gdvid (3 jakosci, bez tokenu), POST najpierw bez proxy.
Ogon "Watch Online Free in HD" w tytulach ucinany.
xanimeporn: przekierowanie download.php rozwiazywane na serwerze, apka
dostawala .php, otwierala WebView i pobierala plik zamiast grac.
Blad resolve (extractor pusty, HTTP strony) trafia do playback_events.

Filmy: tytul z okruszkow (motyw dooplay losuje "Watch"/"Now Playing:"),
linki nitroflare/rapidgator nie sa juz brane za wideo, sam landing idzie
do WebView, a przytrzymanie w apce nie zabija ostatniego linku filmu,
jesli serwer widzi, ze zyje. Skrypty naprawcze: tytuly dooplay,
tytuly i miniatury hentaimama, repair_dooplay_movies dla streampornvip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:12:35 +02:00

80 lines
3.3 KiB
Python

"""xanimeporn.com — mp4 z endpointu pobierania. Dodany 2026-08-04.
Player to Fluid Player z **pustym `<source src=''>`** — źródło wstrzykiwane jest w
runtime, a skrypt inicjalizujący obsługuje wyłącznie fullscreen i analitykę. W statycznym
HTML nie ma ani jednego linku do wideo, więc wygląda to na ślepy zaułek.
Wyjściem jest zakładka „Downloads", która niesie:
https://videos.xanimeporn.com/download.php?id=<postId>&quality=1080p
https://videos.xanimeporn.com/download.php?id=<postId>&quality=720p
`postId` siedzi w klasie `<body>` (`postid-69951`), więc nie trzeba parsować zakładki —
składamy URL sami dla obu jakości.
**Oddajemy docelowy plik, nie `download.php` (zmiana 2026-10-15).** Endpoint przekierowuje
na `download.xanimeporn.com/videos/<q>/...mp4?s=<sig>&e=<epoch>` z `Content-Disposition:
attachment`. Wczesniej oddawalismy `download.php`, zeby telefon sam bral swiezy podpis, ale
apka po `.php` w adresie uznawala link za strone playera i otwierala go w WebView, ktory
POBIERAL plik do Pobranych zamiast grac (zgloszenie 54b17aed; od dodania serwisu ani jedno
odtworzenie). Podpis zyje ok. 2 h i nie jest zwiazany z IP (adres z VPS-a gra z domowego),
a resolve dzieje sie przy kliknieciu, wiec rozwiazujemy przekierowanie tutaj. Gdy sie nie
uda, zostaje `download.php` jako zapas.
Zweryfikowane z VPS-a: 1080p 222 MB i 720p 84 MB, oba 206 na Range z `ftypisom`/`ftypmp42`
w pierwszym KB (faststart). Wymaga Referera i UA, oba dokłada `playback.py`. Bez
Cloudflare i bez proxy, w odróżnieniu od hentaimamy.
"""
from __future__ import annotations
import logging
import re
import httpx
from app.extractors._fetch import fetch_tube_html
from app.extractors._models import StreamSource
log = logging.getLogger(__name__)
_BASE = "https://xanimeporn.com"
_POST_ID_RE = re.compile(r"\bpostid-(\d+)\b")
#: Serwis publikuje dokładnie te dwie jakości; kolejność = preferencja.
_QUALITIES = ("1080p", "720p")
_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
html_text = fetch_tube_html(page_url, timeout=timeout)
m = _POST_ID_RE.search(html_text)
if not m:
log.info("xanimeporn: brak postid w klasie body na %s", page_url)
return None
post_id = m.group(1)
out: list[StreamSource] = []
for q in _QUALITIES:
link = f"https://videos.xanimeporn.com/download.php?id={post_id}&quality={q}"
try:
res = httpx.get(
link,
headers={"User-Agent": _UA, "Referer": _BASE + "/"},
follow_redirects=False,
timeout=15.0,
)
location = res.headers.get("location") or ""
if res.status_code in (301, 302, 303, 307, 308) and location.startswith("http"):
link = location
except Exception as e:
log.info("xanimeporn: przekierowanie %s %s nie wyszlo: %s", post_id, q, e)
out.append(
StreamSource(
link=link,
type="mp4",
quality=q,
referer=_BASE + "/",
# Zweryfikowane 206 + faststart, podpis nie jest zwiazany z IP.
raw={"mobile_direct_ok": True},
)
)
return out