diff --git a/mobile/src/lib/getfileResolver.ts b/mobile/src/lib/getfileResolver.ts index 9af8bb5..96544d7 100644 --- a/mobile/src/lib/getfileResolver.ts +++ b/mobile/src/lib/getfileResolver.ts @@ -20,6 +20,59 @@ export function isGetFileUrl(url: string): boolean { return /\/get_file\//.test(url) && !/\/proxy\//.test(url); } +const _SOURCE_RE = /]*?(?:title|label)=['"]?([^'">]*)/gi; +const _SKIP_Q = /2160|1440|4k/i; + +/** + * Pełny resolve dla hdporn.gg/fullmovies.xxx OD STRONY (nie od get_file URL). + * + * KLUCZOWE (z logów urządzenia 2026-06-06): get_file jest zbindowany do IP tego kto + * załadował STRONĘ. Backend (VPS) ładuje stronę → get_file dla IP VPS → telefon fetchuje + * → 410. Dlatego telefon musi sam załadować stronę (phone IP) → dostać phone-bound + * get_file → follow 302 → fpvcdn z IP telefonu → ExoPlayer gra. Pomija 4K (martwe na fpvcdn). + * + * `quality` = preferowana etykieta z backendu (np. "720p"); fallback = pierwszy nie-4K. + * Zwraca finalny CDN URL lub null (caller wtedy gra oryginał = proxy fallback). + */ +export async function resolveGetFilePage( + pageUrl: string, + quality?: string | null, + referer?: string, +): Promise { + let html: string; + try { + const r = await fetch(pageUrl, { headers: { 'User-Agent': UA, Accept: 'text/html' } }); + if (!r.ok) return null; + html = await r.text(); + } catch { + return null; + } + const wantQ = (quality || '').replace(/\s+/g, '').toLowerCase(); + let pick: string | null = null; + let m: RegExpExecArray | null; + _SOURCE_RE.lastIndex = 0; + while ((m = _SOURCE_RE.exec(html)) !== null) { + const q = (m[2] || '').trim(); + if (_SKIP_Q.test(q)) continue; + let u = m[1]; + if (u.startsWith('//')) u = 'https:' + u; + if (wantQ && q && q.replace(/\s+/g, '').toLowerCase() === wantQ) { + pick = u; + break; // dokładne dopasowanie jakości + } + if (!pick) pick = u; // pierwszy nie-4K jako fallback + } + if (!pick) return null; + const ref = referer || (() => { + try { + return new URL(pageUrl).origin + '/'; + } catch { + return undefined; + } + })(); + return resolveGetFile(pick, ref); +} + /** * Follow get_file 302 → finalny CDN URL (z IP telefonu). Zwraca finalny URL gdy * dojdzie do nie-get_file Location, oryginalny URL gdy get_file serwuje direct (2xx), @@ -42,8 +95,8 @@ export async function resolveGetFile(url: string, referer?: string): Promise= 300 && r.status < 400) { - const loc = r.headers.get('location'); if (!loc) return null; try { cur = new URL(loc, cur).toString(); @@ -56,7 +109,9 @@ export async function resolveGetFile(url: string, referer?: string): Promise= 200 && r.status < 300) { return cur; // get_file serwuje direct, bez redirectu — grywalne } - return null; // 4xx/5xx + // status 0 / opaqueredirect (RN okhttp czasem dla redirect:'manual') albo 4xx/5xx — + // nie da się odczytać celu, caller zagra oryginał (proxy fallback). + return null; } return cur; } diff --git a/mobile/src/screens/SceneDetailScreen.tsx b/mobile/src/screens/SceneDetailScreen.tsx index 8ee5c72..f15c97a 100644 --- a/mobile/src/screens/SceneDetailScreen.tsx +++ b/mobile/src/screens/SceneDetailScreen.tsx @@ -20,7 +20,7 @@ import { import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useClient } from '../ClientContext'; -import { isGetFileUrl, resolveGetFile } from '../lib/getfileResolver'; +import { isGetFileUrl, resolveGetFilePage } from '../lib/getfileResolver'; import type { RootStackParamList } from '../navigation'; import { theme } from '../theme'; import type { PlaybackSource, SceneOut, StreamLink } from '../types'; @@ -483,9 +483,12 @@ function PlaybackButton({ // fallback proxy → „mignięcie". Resolvujemy redirect TU (na telefonie → fpvcdn z IP // telefonu) i podajemy ExoPlayerowi finalny URL — bez błędu/migotania/proxy. Fail = // gramy oryginał (obecne zachowanie z fallbackiem). ~100-300ms (get_file 302 szybki). + // hdporn.gg/fullmovies.xxx: backendowy get_file jest zbindowany do IP VPS (page-loader) + // → telefon dostaje 410 → fallback proxy (mignięcie). Re-fetchujemy STRONĘ na telefonie + // (phone-bound get_file) → fpvcdn z IP telefonu → ExoPlayer gra direct (bez proxy/migotania). if (isDirect && isGetFileUrl(initialUrl)) { const ref = link.headers?.Referer || (refererHost ? `https://${refererHost}/` : undefined); - const resolved = await resolveGetFile(initialUrl, ref); + const resolved = await resolveGetFilePage(source.page_url, link.quality, ref); if (resolved) initialUrl = resolved; }