TRZECI niezalezny host w wertykale. Te same odcinki co hentaimama i xanimeporn, ale znowu inne pliki: tutaj hgasm<N>.com/<Tytul Ze Spacjami>.mp4, tam gdvid.info/N/... i download.xanimeporn.com/videos/1080p/[1080p]<id>.mp4. Zadna z trzech stron nie re-hostuje pozostalych. Metadane w CALOSCI z RSS-a i to nie skrot tylko koniecznosc: - strona odcinka NIE MA daty publikacji w zadnym standardowym miejscu (ani JSON-LD, ani article:published_time, ani <time>), a feed ma pubDate - /genre/ w HTML to 38 linkow, czyli kolejna chmura z sidebara; feed daje 5 realnych kategorii, gdzie pierwsza to nazwa serii - /page/2/ nie zwraca odcinkow w ogole, a /feed/?paged=N stronicuje chronologicznie Playback najprostszy z calej trojki: konfiguracja JWPlayera z gotowym file: wprost w HTML, bez AJAX-a i bez Cloudflare. Sciezka zawiera SPACJE, wiec kodujemy sama sciezke, zostawiajac schemat i host. Host ROTUJE miedzy odcinkami (hgasm1/hgasm3), wiec czytamy go ze strony zamiast wpisywac na sztywno. Zweryfikowane z VPS: 206 na Range, ftypisom, 198 MB, dziala BEZ Referera i bez tokenu, wiec URL nie wygasa. mobile_direct_ok. Pilot: seen 107, new 107, errors 0. Metadane: 100 procent z data, miniaturka i tagami. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
"""hentaigasm.com — bezpośredni mp4 z konfiguracji JWPlayera. Dodany 2026-08-04.
|
|
|
|
Najprostszy z całego wertykału hentai. Konfiguracja playera siedzi WPROST w HTML strony
|
|
odcinka, bez AJAX-a i bez Cloudflare:
|
|
|
|
jwplayer("player_01").setup({
|
|
file: "https://hgasm1.com/Nuki Nuki Zupposism 2 Subbed.mp4",
|
|
image: "https://hgasm1.com/thumbnail/Nuki Nuki Zupposism 2 Subbed.jpg",
|
|
...
|
|
})
|
|
|
|
Dla porównania: [[hentaimama]] trzyma to za POST-em na `admin-ajax` schowanym za
|
|
Cloudflare, a [[xanimeporn]] ma Fluid Player z pustym `<source>` i realny link dopiero
|
|
w zakładce „Downloads".
|
|
|
|
**Ścieżka zawiera SPACJE** i trzeba je zakodować — bez tego request się wywala. Kodujemy
|
|
tylko część po hoście, żeby nie ruszyć schematu ani kropek w domenie.
|
|
|
|
Zweryfikowane z VPS-a: 206 na Range, `ftypisom` w pierwszym KB (faststart), 198 MB,
|
|
działa BEZ Referera i bez tokenu, więc URL nie wygasa. Stąd `mobile_direct_ok`.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
from urllib.parse import quote, urlsplit, urlunsplit
|
|
|
|
from app.extractors._fetch import fetch_tube_html
|
|
from app.extractors._models import StreamSource
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
_BASE = "https://hentaigasm.com"
|
|
_JW_FILE_RE = re.compile(r"file:\s*[\"'](https?://[^\"']+\.mp4)[\"']")
|
|
|
|
|
|
def _encode(url: str) -> str:
|
|
"""Zakoduj spacje w ścieżce, zostawiając schemat i host nietknięte."""
|
|
parts = urlsplit(url)
|
|
return urlunsplit(
|
|
(parts.scheme, parts.netloc, quote(parts.path), parts.query, parts.fragment)
|
|
)
|
|
|
|
|
|
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 raw in _JW_FILE_RE.findall(html_text):
|
|
link = _encode(raw.strip())
|
|
if 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("hentaigasm: brak file: w konfiguracji JWPlayera na %s", page_url)
|
|
return None
|
|
return out
|