NIE jest re-hostem hentaimamy i to bylo pierwsze, co sprawdzilem. Wspolny odcinek (Shoujo Ramune 7) lezy na obu stronach, ale to zupelnie inne pliki: hentaimama serwuje gdvid.info/S/shoujo-ramune-7.mp4 w jednej jakosci bez etykiety, a tutaj jest download.xanimeporn.com z jawnym wyborem 1080p (222 MB) i 720p (84 MB). Listing idzie z RSS-a i to jedyna droga, ktora dziala. Sprawdzone trzy inne: - /page/2/ i /page/3/ oddaja ten sam zestaw co strona 1 (28 z 32 wspolnych), czyli paginacja zepsuta jak przy 8kporner - /uncensored/ i /censored/ stronicuja poprawnie, ale NIE sa chronologiczne: pierwsze pozycje z 2024, zakres 2015-2026. Browse-latest nigdy nie zobaczylby nowego odcinka, czyli pulapka z freshporno. Pierwsza wersja konektora wlasnie tak dzialala i dopiero test na danych to pokazal - /hentai-list/ ma caly katalog (1048) na jednej stronie, ale nie jest chronologiczny Drugi filtr tez wymuszony przez dane: slugi gatunkow (/futanari/, /big-tits/) sa takimi samymi slugami najwyzszego poziomu jak odcinki i wchodzily jako pozycje bez daty i miniaturki. _parse_detail wymaga teraz postid w klasie body, ktory maja tylko pojedyncze wpisy. Ten sam identyfikator jest potrzebny extractorowi. Playback: Fluid Player z PUSTYM <source src=''>, wideo wstrzykiwane w runtime, wiec w HTML nie ma zadnego linku. Wyjsciem jest zakladka Downloads: videos.xanimeporn.com/download.php?id=<postId>&quality=. Oddajemy download.php, NIE docelowy plik, zeby token generowal sie przy odtwarzaniu i nie wietrzal (lekcja z watchporn). Bez Cloudflare i bez proxy, w odroznieniu od hentaimamy. Pilot: seen 50, new 50, errors 0. Metadane: 100 procent z data, miniaturka, kategoria i seria jako tagi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
2.4 KiB
Python
60 lines
2.4 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 `download.php`, a NIE docelowego pliku.** Endpoint przekierowuje na
|
|
`download.xanimeporn.com/...?s=<sig>&e=<epoch>`, czyli URL z tokenem i wygasaniem.
|
|
Zwracając `download.php` zostawiamy generowanie tokenu na moment odtwarzania: telefon
|
|
idzie za przekierowaniem i dostaje świeży podpis, więc nic nie wietrzeje. To ta sama
|
|
lekcja co przy watchporn, gdzie zapisany statyczny URL po czasie padał.
|
|
|
|
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
|
|
|
|
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")
|
|
|
|
|
|
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)
|
|
|
|
return [
|
|
StreamSource(
|
|
link=f"https://videos.xanimeporn.com/download.php?id={post_id}&quality={q}",
|
|
type="mp4",
|
|
quality=q,
|
|
referer=_BASE + "/",
|
|
# Zweryfikowane 206 + faststart z VPS-a; telefon sam podąża za 302 i
|
|
# dostaje własny, świeży token.
|
|
raw={"mobile_direct_ok": True},
|
|
)
|
|
for q in _QUALITIES
|
|
]
|