mobile: page-side get_file resolve for hdporngg/fullmovies (native, no proxy/flicker)
Device logs (not assumptions) pinned the real cause of the hdporngg/fullmovies flicker: the backend returns a get_file URL, but get_file is bound to the IP that loaded the *page*. The backend (VPS) loads the page, so the get_file is VPS-bound; the phone fetching that get_file gets HTTP 410 -> ExoPlayer errors -> falls back to the proxy via nav.replace (the "flicker"), and ends up streaming through the proxy. (My earlier "stateless/portable" test was from the VPS — same IP as the page load — so it wrongly showed 206.) Fix: when the direct_url is a get_file, the phone re-fetches the *page* itself (resolveGetFilePage on source.page_url) so the get_file is bound to the phone IP, picks the requested quality skipping 4K (dead on fpvcdn), follows to the CDN, and hands ExoPlayer a working URL. On failure it keeps the original (proxy fallback). Verified on device: [getfile] page-resolve -> get_file 206 -> ExoPlayer PLAYING, position advancing, no error/proxy/flicker, real video frame rendered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e5b6e8968c
commit
a5ec6ca991
2 changed files with 62 additions and 4 deletions
|
|
@ -20,6 +20,59 @@ export function isGetFileUrl(url: string): boolean {
|
|||
return /\/get_file\//.test(url) && !/\/proxy\//.test(url);
|
||||
}
|
||||
|
||||
const _SOURCE_RE = /<source\s+src=['"]([^'"]+\/get_file\/[^'"]+\.mp4[^'"]*)['"][^>]*?(?: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<string | null> {
|
||||
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<str
|
|||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (r.status >= 300 && r.status < 400) {
|
||||
const loc = r.headers.get('location');
|
||||
if (r.status >= 300 && r.status < 400) {
|
||||
if (!loc) return null;
|
||||
try {
|
||||
cur = new URL(loc, cur).toString();
|
||||
|
|
@ -56,7 +109,9 @@ export async function resolveGetFile(url: string, referer?: string): Promise<str
|
|||
if (r.status >= 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue