feat(supjav): resolve voe hoster to direct stream instead of WebView
Some checks failed
Backend tests / test (push) Has been cancelled

supjav is an embed-aggregator (voe/streamtape/turbovid/fc2stream). It was playing
through the WebView fallback because none of its hosters had a native phone-side
resolver wired into the scene player. voe.sx does have one (voeResolver.ts, already
used for movies) — it just was not wired for scenes.

- PlayerScreen: wire isVoeUrl/resolveVoePage into the hoster routing (like
  dood/filemoon), so a voe embed resolves on the phone to a direct m3u8
  (token bound to the phone /16, plays direct, no WebView, no proxy).
- supjav extractor: return voe first (sorted by URL), and stop putting the raw
  hostname in `quality` (the digit in "fc2stream" was parsed as quality 2 and won
  _pick_best) — use a clean digit-free server label instead.
- SceneDetailScreen: for hoster-only scenes, open the first embed (the extractor
  puts the natively-resolvable one first) rather than _pick_best's choice.

Verified: supjav's voe embed resolves to a real HLS manifest (200,
application/vnd.apple.mpegurl, #EXTM3U). WebView stays only as last-resort fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-07-11 19:49:52 +02:00
parent 39374c0e49
commit 7e2807b42e
4 changed files with 31 additions and 2 deletions

View file

@ -98,15 +98,23 @@ def extract(page_url: str, *, timeout: float = 60.0) -> list[StreamSource] | Non
continue
seen.add(hoster)
host = hoster.split("/")[2] if "://" in hoster else hoster
# Label serwera bez cyfr (host "fc2stream.tv" psułby _quality_to_int → "2" jako
# jakość → zły `best`). Zostawiamy skróconą, bezcyfrową nazwę do pickera.
label = re.sub(r"\.[a-z]+$", "", host.split(".")[0]) if host else None
label = re.sub(r"\d+", "", label).upper() if label else None
out.append(
StreamSource(
link=hoster,
type="hoster",
quality=host,
quality=label or None,
referer="https://supjav.com/",
)
)
if not out:
log.info("supjav: żaden data-link nie rozwiązał się na hoster (%s)", page_url)
return None
# voe.sx PIERWSZY (sort po URL, niezależnie od label) — telefon resolwuje go do
# bezpośredniego m3u8 (voeResolver.ts, phone-IP-bound → gra direct, zero WebView).
# Reszta (turbovid/fc2stream/streamtape) bez phone-side resolvera → WebView, więc niżej.
out.sort(key=lambda s: 0 if "//voe." in s.link or ".voe." in s.link else 1)
return out

View file

@ -16,6 +16,13 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
{
id: '2026-07-11',
date: 'July 2026',
items: [
'JAV and other voe-hosted videos now play directly in the built-in player instead of falling back to the web view, faster start and smoother playback.',
],
},
{
id: '2026-07-10b',
date: 'July 2026',

View file

@ -1389,6 +1389,7 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
const { isDoodStream, resolveDoodStream } = await import('../lib/doodstream');
const { isPackerHoster, resolvePackerHoster } = await import('../lib/packerHoster');
const { isFilemoonHoster, resolveFilemoonHoster } = await import('../lib/filemoonHoster');
const { isVoeUrl, resolveVoePage } = await import('../lib/voeResolver');
let result: { url?: string; headers?: Record<string, string>; error?: string } | null = null;
if (isDoodStream(url)) {
@ -1400,6 +1401,15 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
} else if (isFilemoonHoster(url)) {
setResolveStatus('pending');
result = await resolveFilemoonHoster(url, source);
} else if (isVoeUrl(url)) {
// voe.sx (supjav i inne): resolwujemy NA TELEFONIE do direct m3u8 (token bound
// do /16 telefonu → gra direct, zero WebView/proxy). Pusto → WebView fallback.
setResolveStatus('pending');
const voeLinks = await resolveVoePage(url);
const best = voeLinks[0];
if (best) {
result = { url: best.direct_url || best.stream_url, headers: best.headers };
}
}
setResolveAttempted(true);

View file

@ -665,7 +665,11 @@ function PlaybackButton({
// Player JS hostera może też ujawnić finalny m3u8 — auto-extract w PlayerScreen
// pokaże banner "Otwórz w native playerze".
if (directLinks.length === 0) {
const target = res.best?.embed_url || embedLinks[0].embed_url!;
// Preferuj PIERWSZY embed z listy (ekstraktor sortuje resolvowalne natywnie
// hostery na przód, np. supjav → voe.sx pierwszy → Player resolwuje go do direct
// m3u8 zamiast WebView). res.best bywa gorszy (multi-hoster: _pick_best nie wie
// który jest phone-resolvowalny). Pozostałe hostery są w quality-pickerze.
const target = embedLinks[0]?.embed_url || res.best?.embed_url!;
let refererHost: string | undefined;
try {
refererHost = new URL(source.page_url).hostname;