fix(player): hide ad-heavy WebView behind opaque cover until stream scraped
User bug: opening a WebView-fallback scene (yespornvip etc.) shows the host's ad-heavy page while INJECTED_JS auto-plays + scrapes the stream url in the background. User sees ads instead of a loading state. Render an opaque cover (theme.bg + spinner "Loading video…") over the WebView while !extractedUrl. The WebView is still laid out and painted underneath, so media keeps playing (autoplay via mediaPlaybackRequiresUserAction=false) and the performance-scan picks up the CDN url — but the user only ever sees a loading screen, then the native player. Applies to every WebView-fallback host. Safety: if no stream is scraped within 15s (host needs a real tap to start), reveal the WebView so the user can interact manually — no worse than before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bb4a17ae79
commit
00ea8c3fd4
1 changed files with 31 additions and 0 deletions
|
|
@ -988,6 +988,17 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
const [resolveError, setResolveError] = React.useState<string | null>(null);
|
const [resolveError, setResolveError] = React.useState<string | null>(null);
|
||||||
const [skipResolve, setSkipResolve] = React.useState(false);
|
const [skipResolve, setSkipResolve] = React.useState(false);
|
||||||
const [resolveAttempted, setResolveAttempted] = React.useState(false);
|
const [resolveAttempted, setResolveAttempted] = React.useState(false);
|
||||||
|
// Anti-ads: trzymamy nieprzezroczysty cover NA WebView dopóki INJECTED_JS nie
|
||||||
|
// wyciągnie URL — user nie widzi ad-heavy strony hostera (bug yespornvip "otwiera
|
||||||
|
// reklamy" 2026-05-31). WebView gra pod spodem (jest malowany, media leci, scrape
|
||||||
|
// łapie CDN). Jeśli po REVEAL_AFTER_MS autoplay nie zaskoczył (host wymaga gestu) —
|
||||||
|
// odsłaniamy WebView żeby user mógł ręcznie tapnąć play (graceful fallback).
|
||||||
|
const [revealEmbed, setRevealEmbed] = React.useState(false);
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (extractedUrl) return;
|
||||||
|
const t = setTimeout(() => setRevealEmbed(true), 15000);
|
||||||
|
return () => clearTimeout(t);
|
||||||
|
}, [extractedUrl]);
|
||||||
|
|
||||||
// Stage 0.8: mobile-side hoster resolvery. Mobile IP usera unika Cloudflare
|
// Stage 0.8: mobile-side hoster resolvery. Mobile IP usera unika Cloudflare
|
||||||
// Turnstile / CAPTCHA gate który blokuje Hetzner VPS — embed page renderuje
|
// Turnstile / CAPTCHA gate który blokuje Hetzner VPS — embed page renderuje
|
||||||
|
|
@ -1206,6 +1217,12 @@ function EmbedWebViewPlayer({ params }: { params: RouteParams }) {
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{!extractedUrl && !revealEmbed && (
|
||||||
|
<View style={styles.coverOverlay}>
|
||||||
|
<ActivityIndicator color={theme.fg} size="large" />
|
||||||
|
<Text style={styles.overlayText}>Loading video…</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{extractedUrl && (
|
{extractedUrl && (
|
||||||
<Pressable
|
<Pressable
|
||||||
style={styles.extractBanner}
|
style={styles.extractBanner}
|
||||||
|
|
@ -1241,6 +1258,20 @@ const styles = StyleSheet.create({
|
||||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||||
paddingHorizontal: 32,
|
paddingHorizontal: 32,
|
||||||
},
|
},
|
||||||
|
// Nieprzezroczysty cover na WebView — ukrywa ad-heavy stronę hostera podczas gdy
|
||||||
|
// INJECTED_JS auto-play + scrape działają pod spodem. Solidne tło (nie alpha) =
|
||||||
|
// zero podglądu reklam.
|
||||||
|
coverOverlay: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: theme.bg,
|
||||||
|
paddingHorizontal: 32,
|
||||||
|
},
|
||||||
overlayText: { color: theme.fg, marginTop: 12, fontSize: 13, textAlign: 'center' },
|
overlayText: { color: theme.fg, marginTop: 12, fontSize: 13, textAlign: 'center' },
|
||||||
errorTitle: { color: theme.bad, fontSize: 18, fontWeight: '700', marginBottom: 8 },
|
errorTitle: { color: theme.bad, fontSize: 18, fontWeight: '700', marginBottom: 8 },
|
||||||
errorBody: { color: theme.fg, fontSize: 14, marginBottom: 16, textAlign: 'center' },
|
errorBody: { color: theme.fg, fontSize: 14, marginBottom: 16, textAlign: 'center' },
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue