goon/app/extractors/tubes/hentaiplay.py
goon-foss fcfac697cf feat(hentai): konektor hentaiplay.net - czwarte zrodlo wertykalu
CZWARTY niezalezny host: pliki na hentaiplanet.info, podczas gdy hentaimama ma
gdvid.info, xanimeporn download.xanimeporn.com, a hentaigasm hgasm<N>.com. Cztery
strony, cztery osobne pule plikow - zadna nie re-hostuje pozostalych, mimo ze katalogi
odcinkow mocno sie pokrywaja.

Metadane z RSS-a, tak jak przy hentaigasm i z tego samego powodu: /genre/ w HTML strony
to 2084 linki, najwieksza chmura z sidebara w calym wertykale (hentaigasm mial 38,
hentaimama 118). Feed daje date, tytul i realne kategorie. Odsiewamy przy tym pozycje
czysto techniczne (Hentai Anime, New Hentai, English Subbed) oraz rocznik, bo to nie sa
tagi tresci. /page/2/ nie zwraca odcinkow, /feed/?paged=N stronicuje chronologicznie.

Playback: mp4 wprost w HTML (video.js), bez AJAX-a i bez Cloudflare. Pulapka: serwis
nazywa miniaturki od pliku wideo (…-RAW.mp4_snapshot_07.59.jpg), wiec naiwne szukanie
.mp4 lapie zarowno wideo, jak i obrazek. Wymagamy URL-a KONCZACEGO sie na .mp4 i
odrzucamy wp-content/uploads.

Druga pulapka, zlapana dopiero testem: og:image na tej stronie NIE ISTNIEJE, miniaturka
jest tylko w JSON-LD i z escapowanymi ukosnikami. Pierwsza wersja dawala 12/12 odcinkow
bez miniaturki.

Zweryfikowane z VPS: 206 na Range, ftypmp42, 78 MB, dziala BEZ Referera i bez tokenu.
Serwer oddaje content-type application/octet-stream, ale sciezka konczy sie .mp4, wiec
ExoPlayer wybierze Mp4Extractor po rozszerzeniu.

Pilot: seen 48, new 48, errors 0. Komplet dat i miniaturek.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 15:31:21 +02:00

57 lines
2.3 KiB
Python

"""hentaiplay.net — bezpośredni mp4 z odtwarzacza video.js. Dodany 2026-08-04.
Link do pliku siedzi wprost w HTML strony odcinka (player to video.js z
`vjs.zencdn.net`), bez AJAX-a i bez Cloudflare — podobnie jak przy [[hentaigasm]],
w odróżnieniu od [[hentaimama]] (POST za CF) i [[xanimeporn]] (pusty `<source>`).
**Trzeba odsiać miniaturki, nie tylko szukać `.mp4`.** Serwis nazywa zrzuty klatek
od pliku wideo, więc w HTML leżą obok siebie:
https://hentaiplanet.info/Ushiro-no-Shoumen-Kamui-san-Episode-4-RAW.mp4 ← wideo
…/wp-content/uploads/Ushiro-…-RAW.mp4_snapshot_07.59.296-160x90.jpg ← miniaturka
Naiwne `\\.mp4` łapie oba. Dlatego wymagamy, żeby URL KOŃCZYŁ się na `.mp4`, i dodatkowo
odrzucamy `wp-content/uploads` (miniaturki serwisu).
Zweryfikowane z VPS-a: 206 na Range, `ftypmp42` w pierwszym KB (faststart), 78 MB,
działa BEZ Referera i bez tokenu. Serwer oddaje `content-type: application/octet-stream`,
ale ścieżka kończy się `.mp4`, więc ExoPlayer i tak wybierze `Mp4Extractor` po
rozszerzeniu. Stąd `mobile_direct_ok`.
"""
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://hentaiplay.net"
#: Tylko URL-e KOŃCZĄCE się na .mp4 — patrz pułapka z miniaturkami w docstringu.
_MP4_RE = re.compile(r"https?://[A-Za-z0-9._~%+-]+(?:/[A-Za-z0-9._~%+-]+)*\.mp4(?=[\"'\s<>]|$)")
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
html_text = fetch_tube_html(page_url, timeout=timeout)
seen: set[str] = set()
out: list[StreamSource] = []
for link in _MP4_RE.findall(html_text):
if "wp-content/uploads" in link or link in seen:
continue
seen.add(link)
out.append(
StreamSource(
link=link,
type="mp4",
quality=None, # serwis publikuje jedną rendycję, bez etykiety
referer=_BASE + "/",
# Zweryfikowane: 206 bez Referera, bez tokenu, faststart.
raw={"mobile_direct_ok": True},
)
)
if not out:
log.info("hentaiplay: brak linku mp4 na %s", page_url)
return None
return out