1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt, ' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety; zakresy 0–5 i A–Z na dywiz. Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach, czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu, tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana. Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie). Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika to zero zmiany. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.9 KiB
Python
69 lines
2.9 KiB
Python
"""galaxyporn.net, iframe → seekplayer engine. Dodany 2026-07-26.
|
|
|
|
Scene page ma jeden `<iframe src="https://<host>/#<hash>">` rodziny seekplayer
|
|
(galaxy.upns.online / galaxy.4meplayer.pro / sport.seekplays.com / news.upns.pro,
|
|
100% próbki 50 scen z całej głębokości archiwum). Silnik mamy już w repo
|
|
(`hosters/seekplayer_engine.py`, ten sam AES key/IV + `/api/v1/video?id=`), więc
|
|
oddajemy iframe do generycznego `extract_stream_from_hoster` i nic nie dublujemy.
|
|
|
|
HLS jest hotlink-guarded na Referer (bez niego 403 na master i na segmentach),
|
|
a token podpisany pod IP fetchera → manifest i segmenty idą przez `/proxy/hls`.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
from urllib.parse import urlparse
|
|
|
|
from app.extractors._fetch import fetch_tube_html
|
|
from app.extractors._models import StreamSource
|
|
from app.extractors.hosters import seekplayer_engine
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
_BASE = "https://galaxyporn.net"
|
|
_IFRAME_RE = re.compile(r'<iframe[^>]+src="([^"]+)"', re.IGNORECASE)
|
|
|
|
|
|
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
|
|
html_text = fetch_tube_html(page_url, timeout=timeout)
|
|
m = _IFRAME_RE.search(html_text)
|
|
if not m:
|
|
log.info("galaxyporn: no iframe on %s", page_url)
|
|
return None
|
|
iframe_src = m.group(1).strip()
|
|
if iframe_src.startswith("//"):
|
|
iframe_src = "https:" + iframe_src
|
|
|
|
# Wołamy silnik BEZPOŚREDNIO, nie przez `extract_stream_from_hoster`: wrapper
|
|
# weryfikuje wynikowy URL, a HLS galaxyporn jest hotlink-guarded (403 bez
|
|
# Referera) → wrapper kasował poprawnie zdekodowany stream (sprawdzone: engine
|
|
# zwracał m3u8, wrapper None dla tych samych scen).
|
|
sources = seekplayer_engine.extract(iframe_src, timeout=timeout)
|
|
if not sources:
|
|
# Nie oddajemy iframe'a jako type='hoster', seekplayer to SPA, w WebView
|
|
# user zobaczy pusty player. Lepiej None (źródło jako nierozwiązane).
|
|
log.info("galaxyporn: seekplayer resolve failed for %s", iframe_src)
|
|
return None
|
|
|
|
# Referer MUSI być hostem PLAYERA (np. https://galaxy.4meplayer.pro/), NIE
|
|
# galaxyporn.net, zweryfikowane: ten sam manifest z Refererem galaxyporn = 403,
|
|
# z Refererem playera = 200 + lista wariantów. Silnik ustawia go poprawnie, więc
|
|
# go zachowujemy; fallback liczymy z hosta iframe'a.
|
|
player_origin = f"https://{urlparse(iframe_src).hostname}/"
|
|
out: list[StreamSource] = []
|
|
for s in sources:
|
|
raw = dict(s.raw or {})
|
|
# Token HLS jest Referer+IP-bound → manifest+segmenty przez /proxy/hls
|
|
# (bez mobile_direct_ok, inaczej telefon dostanie 403).
|
|
raw["proxy_no_verify"] = True
|
|
out.append(
|
|
StreamSource(
|
|
link=s.link,
|
|
type=s.type or ("m3u8" if ".m3u8" in s.link.lower() else "mp4"),
|
|
quality=s.quality,
|
|
referer=s.referer or player_origin,
|
|
raw=raw,
|
|
)
|
|
)
|
|
return out
|