Mobile / OTA: - Enable Expo Updates (app.json + AndroidManifest) → api.goon-foss.org - Bump 0.1.6 → 0.1.9 (build.gradle, app.json, appVersion.ts, main.py /version) - backend.ts: default public backend auto-connect (no manual login) WebView fallback fix (PlayerScreen INJECTED_JS): - Auto-dismiss cookie/consent gates (hqporner et al. blocked kt_player init) - Context-scoped: only clicks consent buttons inside cookie/gdpr containers - Retry window for <source>.src polling raised 5→15 ticks (post-dismiss init) Resolver: - Series-position + modifier mismatch detector (Episode 2≠4, BTS/unedited) → composite_score hard-reject / cap; wired into scene_score + bulk_dedup - aggregator-mode candidate query: LIMIT 500 + title-match ordering Connectors: - porndoe.com browse scraper (JSON-LD VideoObject) — theporndude audit pilot landing: APK links → goon-v0.1.9.apk Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
"""freshporno.org — KVS engine, BEZ `<source>` tagów.
|
|
|
|
Page używa kt_player (KVS Flash + JS legacy player) — URLs są wewnątrz JavaScript
|
|
flashvars JSON (`video_url: 'function/0/<URL>'`) i w `<a href="...?download=true">`
|
|
linkach z labelem "MP4 720p" / "MP4 480p".
|
|
|
|
Bierzemy anchor pattern bo ma WSZYSTKIE quality z explicit labelem (vs flashvars
|
|
ma tylko main+alt, max 2 jakości). `<a href="...get_file/...mp4/?download=true...">MP4 <q>p, ...`
|
|
|
|
Sidebar suggested videos używają `data-preview="...get_file/.../<id>_preview.mp4"` —
|
|
inny pattern (nie `<a href>`), więc anchor regex je naturalnie pomija.
|
|
|
|
CDN token IP-bound do VPS — mobile dostanie 403 na direct, fallback proxy działa.
|
|
get_file 302 → `cdn4.freshporno.org/remote_control.php?...&file=<path>` direct mp4
|
|
(nie HLS). Type='mp4'.
|
|
"""
|
|
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__)
|
|
|
|
# `<a href="<URL>?download=true...">MP4 <quality>p, <size>` — main + alt streams.
|
|
_ANCHOR_QUALITY_RE = re.compile(
|
|
r'<a\s+[^>]*href="(?P<url>https?://[^"]+/get_file/[^"]+\.mp4/)\?download=true[^"]*"'
|
|
r'[^>]*>\s*MP4\s+(?P<q>\d{3,4}p)',
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
|
|
def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | None:
|
|
html = fetch_tube_html(page_url, timeout=timeout)
|
|
|
|
seen_keys: set[str] = set()
|
|
result: list[StreamSource] = []
|
|
for m in _ANCHOR_QUALITY_RE.finditer(html):
|
|
url = m.group("url")
|
|
quality = m.group("q")
|
|
# Dedupe po basename (path bez query string).
|
|
basename = url.rstrip("/").split("/")[-1]
|
|
if basename in seen_keys:
|
|
continue
|
|
seen_keys.add(basename)
|
|
# `force_proxy=True` (2026-05-20): freshporno get_file 302 → cdn4.freshporno.org
|
|
# IP-bound (cv= HMAC token). Mobile direct = 403/SSL fail → fallback proxy
|
|
# generuje "mrugnięcie" (user bug 743eefbf "najpierw strona potem video").
|
|
# Force_proxy wymusza mobile użycie proxied URL od razu — bez flickera +
|
|
# natywny ExoPlayer + quality picker zachowane.
|
|
result.append(StreamSource(
|
|
link=url, type="mp4", quality=quality,
|
|
raw={"force_proxy": True},
|
|
))
|
|
|
|
if not result:
|
|
log.info("freshporno: no MP4 anchor matches on %s", page_url)
|
|
return None
|
|
|
|
def _quality_key(s: StreamSource) -> int:
|
|
if not s.quality:
|
|
return -1
|
|
try:
|
|
return int(s.quality.rstrip("p"))
|
|
except ValueError:
|
|
return -1
|
|
|
|
result.sort(key=_quality_key, reverse=True)
|
|
return result
|