Dedykowane resolvery: xtremestream + porntrex KVS
xtremestream (perverzija): - extract_stream_from_hoster special-case: embed /player/index.php?data=<H> → m3u8 master = /player/xs1.php?data=<H> (z inline JS m3u8_loader_url) - Wcześniej brak packera/file w videojs HTML → WebView fallback porntrex (KVS) — VPS znów ma dostęp 2026-05-22: - Nowy app/extractors/tubes/porntrex.py — flashvars video_url/_alt_url → get_file URLs (480/720/1080p) - get_file 302 → CDN time-bound signed (expires+md5, NIE IP-bound) → mobile_direct_ok=True, mobile gra direct, zero VPS bandwidth - _REGISTRY: porntrexcom _vps_blocked_fallback → porntrex.extract bysezoxexe (latestpornvideo 2nd embed) — filemoon-rebrand Vite SPA, wymaga osobnego RE; latestpornvideo i tak działa przez luluvid. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
feef312e8d
commit
28273eda02
3 changed files with 98 additions and 5 deletions
|
|
@ -34,6 +34,7 @@ from app.extractors.tubes import (
|
|||
paradisehill,
|
||||
porn00,
|
||||
pornhat,
|
||||
porntrex,
|
||||
pornxp,
|
||||
sxyprn,
|
||||
)
|
||||
|
|
@ -69,11 +70,10 @@ _REGISTRY: dict[str, Callable[[str], list[StreamSource] | None]] = {
|
|||
"xvideoscom": _ytdlp.extract,
|
||||
"xnxxcom": _ytdlp.extract,
|
||||
"youporncom": _ytdlp.extract,
|
||||
# porntrex KVS get_file — `kt_ips=<vps_ip>` cookie + single-use token (410 po reuse).
|
||||
# CDN IP-bound do VPS, mobile direct = 403. Switch na _vps_blocked_fallback:
|
||||
# mobile WebView z phone IP → KVS player JS dekoduje video.src → INJECTED_JS scrape.
|
||||
# 137k scen oszczędzone z VPS bandwidth (largest single saving).
|
||||
"porntrexcom": _vps_blocked_fallback.extract,
|
||||
# porntrex KVS — 2026-05-22 VPS znów dociera (HTTP 200). Dedykowany extractor:
|
||||
# flashvars `video_url` → `get_file` 302 → CDN time-bound signed URL
|
||||
# (`expires`+`md5`, NIE IP-bound) → mobile gra direct, zero VPS bandwidth.
|
||||
"porntrexcom": porntrex.extract,
|
||||
# VPS-blocked tubes — KVS / Cloudflare blokuje Hetzner IP, ale działają z residential
|
||||
# IP (potwierdzone Chrome DevTools MCP 2026-05-15). Mobile WebView + INJECTED_JS
|
||||
# (PlayerScreen.tsx:805) skanuje <video>.src + XHR — łapie URL po decode-ie player JS.
|
||||
|
|
|
|||
|
|
@ -176,6 +176,17 @@ def extract_stream_from_hoster(
|
|||
if sources:
|
||||
return sources[0].link
|
||||
# Fall through to generic logic gdyby dedicated zwrócił None.
|
||||
# xtremestream (perverzija): videojs+hls.js player. Embed `/player/index.php?data=<HASH>`
|
||||
# — m3u8 master playlist serwowany przez `/player/xs1.php?data=<HASH>` (ten sam HASH,
|
||||
# inny endpoint; potwierdzone w inline JS `m3u8_loader_url`). Brak packera/file: w
|
||||
# HTML → generic logic zwracała None → WebView fallback. Trywialna podmiana endpointu.
|
||||
_xtr = re.search(
|
||||
r"(https?://[^/]*xtremestream\.[a-z]+/player/)index\.php(\?data=[0-9a-f]+)",
|
||||
iframe_url,
|
||||
re.IGNORECASE,
|
||||
)
|
||||
if _xtr:
|
||||
return f"{_xtr.group(1)}xs1.php{_xtr.group(2)}"
|
||||
# Streamtape: 4 `document.getElementById(...).innerHTML = prefix + (...).substring(N)`
|
||||
# assignmenty, z czego 2 są DECOY z połamanym hostname. Dedicated decode picks
|
||||
# correct one + builds `/get_video?id=...&token=...` URL.
|
||||
|
|
|
|||
82
app/extractors/tubes/porntrex.py
Normal file
82
app/extractors/tubes/porntrex.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
"""porntrex.com — KVS engine direct stream extractor.
|
||||
|
||||
2026-05-22: VPS Hetzner IP znów dociera do porntrex (HTTP 200) — wcześniej blokada
|
||||
trzymała `porntrexcom` na `_vps_blocked_fallback`. Patrz [[goon_porntrex_vps_unblocked]].
|
||||
|
||||
KVS player: detail page ma `flashvars` z `video_url` / `video_alt_url` / `video_alt_url2`
|
||||
(480p / 720p / 1080p), każdy to `get_file/<srv>/<token>/<path>.mp4/` URL.
|
||||
|
||||
`get_file` 302 → `cdn.pcdn.cloudswitches.com/...mp4?expires=<ts>&md5=<sig>` — to
|
||||
**time-bound signed URL** (nie IP-bound). Mobile ExoPlayer może pobrać get_file
|
||||
(follow 302) i grać direct z CDN — zero VPS bandwidth. Stąd `mobile_direct_ok=True`.
|
||||
|
||||
Token w `get_file` URL bywa single-use (410 po reuse), więc NIE resolvujemy 302 na
|
||||
backendzie — oddajemy get_file URL, mobile zużywa token sam przy pierwszym fetchu.
|
||||
"""
|
||||
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://www.porntrex.com"
|
||||
|
||||
# flashvars: `video_url: 'https://.../get_file/...mp4/'` + `video_url_text: '480p'`.
|
||||
# Warianty: video_url, video_alt_url, video_alt_url2, video_alt_url3...
|
||||
_URL_RE = re.compile(
|
||||
r"(video(?:_alt)?_url\d*)\s*:\s*'(https?://[^']+/get_file/[^']+)'",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_TEXT_RE = re.compile(
|
||||
r"(video(?:_alt)?_url\d*)_text\s*:\s*'([^']*)'",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def _quality_rank(label: str | None) -> int:
|
||||
"""`1080p` → 1080, `720p HD` → 720. Do sortowania malejąco."""
|
||||
if not label:
|
||||
return -1
|
||||
m = re.search(r"(\d{3,4})\s*p", label, re.IGNORECASE)
|
||||
return int(m.group(1)) if m else -1
|
||||
|
||||
|
||||
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
|
||||
html = fetch_tube_html(page_url, timeout=timeout)
|
||||
|
||||
# Mapa <var_name> → quality label (np. video_alt_url → "720p HD").
|
||||
quality_by_var: dict[str, str] = {}
|
||||
for m in _TEXT_RE.finditer(html):
|
||||
quality_by_var[m.group(1).lower()] = m.group(2).strip()
|
||||
|
||||
seen: set[str] = set()
|
||||
result: list[StreamSource] = []
|
||||
for m in _URL_RE.finditer(html):
|
||||
var_name = m.group(1).lower()
|
||||
url = m.group(2)
|
||||
if url in seen:
|
||||
continue
|
||||
seen.add(url)
|
||||
quality = quality_by_var.get(var_name)
|
||||
result.append(
|
||||
StreamSource(
|
||||
link=url,
|
||||
type="mp4",
|
||||
quality=quality or None,
|
||||
referer=_BASE + "/",
|
||||
# CDN po 302 jest time-bound (expires+md5), nie IP-bound —
|
||||
# mobile gra direct z get_file, zero VPS proxy bandwidth.
|
||||
raw={"mobile_direct_ok": True},
|
||||
)
|
||||
)
|
||||
|
||||
if not result:
|
||||
log.info("porntrex: no KVS video_url in flashvars on %s", page_url)
|
||||
return None
|
||||
|
||||
result.sort(key=lambda s: _quality_rank(s.quality), reverse=True)
|
||||
return result
|
||||
Loading…
Add table
Reference in a new issue