From f111514090874738beab79384bce852d97faed61 Mon Sep 17 00:00:00 2001 From: tapframe Date: Sun, 14 Sep 2025 16:14:18 +0530 Subject: [PATCH] double encoded url fix --- src/components/player/VideoPlayer.tsx | 43 +++++++++++++++++++++++---- src/screens/StreamsScreen.tsx | 41 ++++++++++++++++--------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/components/player/VideoPlayer.tsx b/src/components/player/VideoPlayer.tsx index 99aa1fe7..874b1b14 100644 --- a/src/components/player/VideoPlayer.tsx +++ b/src/components/player/VideoPlayer.tsx @@ -47,8 +47,26 @@ const VideoPlayer: React.FC = () => { const route = useRoute>(); const { streamProvider, uri, headers, forceVlc } = route.params as any; - // Check if the stream is from Xprime - const isXprimeStream = streamProvider === 'xprime' || streamProvider === 'Xprime'; + // Check if the stream is from Xprime (by provider name or URL pattern) + const isXprimeStream = streamProvider === 'xprime' || streamProvider === 'Xprime' || + (uri && /flutch.*\.workers\.dev|fsl\.fastcloud\.casa|xprime/i.test(uri)); + + // Xprime-specific headers for better compatibility (from local-scrapers-repo) + const getXprimeHeaders = () => { + if (!isXprimeStream) return {}; + return { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5', + 'Accept-Language': 'en-US,en;q=0.9', + 'Accept-Encoding': 'identity', + 'Origin': 'https://xprime.tv', + 'Referer': 'https://xprime.tv/', + 'Sec-Fetch-Dest': 'video', + 'Sec-Fetch-Mode': 'no-cors', + 'Sec-Fetch-Site': 'cross-site', + 'DNT': '1' + } as any; + }; // Check if the file format is MKV const lowerUri = (uri || '').toLowerCase(); @@ -61,6 +79,8 @@ const VideoPlayer: React.FC = () => { // - Android devices // - Xprime streams on any platform // - Non-MKV files on iOS (unless forceVlc is set) + // Use VideoPlayer (VLC) for: + // - MKV files on iOS (unless forceVlc is set) const shouldUseAndroidPlayer = Platform.OS === 'android' || isXprimeStream || (Platform.OS === 'ios' && !isMkvFile && !forceVlc); if (__DEV__) { logger.log('[VideoPlayer] Player selection:', { @@ -203,7 +223,20 @@ const VideoPlayer: React.FC = () => { const [isLoadingSubtitleList, setIsLoadingSubtitleList] = useState(false); const [showSourcesModal, setShowSourcesModal] = useState(false); const [availableStreams, setAvailableStreams] = useState<{ [providerId: string]: { streams: any[]; addonName: string } }>(passedAvailableStreams || {}); - const [currentStreamUrl, setCurrentStreamUrl] = useState(uri); + // Decode URLs for VLC compatibility - VLC has issues with encoded URLs + const decodeUrlForVlc = (url: string): string => { + try { + // Always decode URLs for VLC as it has trouble with encoded characters + const decoded = decodeURIComponent(url); + logger.log('[VideoPlayer] Decoded URL for VLC:', { original: url, decoded }); + return decoded; + } catch (e) { + logger.warn('[VideoPlayer] URL decoding failed, using original:', e); + return url; + } + }; + + const [currentStreamUrl, setCurrentStreamUrl] = useState(decodeUrlForVlc(uri)); const [isChangingSource, setIsChangingSource] = useState(false); const [showErrorModal, setShowErrorModal] = useState(false); const [errorDetails, setErrorDetails] = useState(''); @@ -1913,8 +1946,8 @@ const VideoPlayer: React.FC = () => { // Set pending seek state setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying }); - // Update the stream URL and details immediately - setCurrentStreamUrl(newStream.url); + // Update the stream URL and details immediately (decode URL for VLC) + setCurrentStreamUrl(decodeUrlForVlc(newStream.url)); setCurrentQuality(newQuality); setCurrentStreamProvider(newProvider); setCurrentStreamName(newStreamName); diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index 6245fc68..d12c8444 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -926,16 +926,24 @@ export const StreamsScreen = () => { const streamName = stream.name || stream.title || 'Unnamed Stream'; const streamProvider = stream.addonId || stream.addonName || stream.name; - // Determine if we should force VLC on iOS based on provider-declared formats (e.g., MKV) + // Determine if we should force VLC on iOS based on actual stream format (not provider capability) let forceVlc = !!options?.forceVlc; try { - const providerId = stream.addonId || (stream as any).addon; - if (Platform.OS === 'ios' && providerId && !forceVlc) { - forceVlc = await localScraperService.supportsFormat(providerId, 'mkv'); - logger.log(`[StreamsScreen] Provider '${providerId}' MKV support -> ${forceVlc}`); + if (Platform.OS === 'ios' && !forceVlc) { + // Check if the actual stream is an MKV file + const lowerUri = (stream.url || '').toLowerCase(); + const contentType = (stream.headers && ((stream.headers as any)['Content-Type'] || (stream.headers as any)['content-type'])) || ''; + const isMkvByHeader = typeof contentType === 'string' && contentType.includes('matroska'); + const isMkvByPath = lowerUri.includes('.mkv') || /[?&]ext=mkv\b/.test(lowerUri) || /format=mkv\b/.test(lowerUri) || /container=mkv\b/.test(lowerUri); + const isMkvFile = Boolean(isMkvByHeader || isMkvByPath); + + if (isMkvFile) { + forceVlc = true; + logger.log(`[StreamsScreen] Stream is MKV format -> forcing VLC`); + } } } catch (e) { - logger.warn('[StreamsScreen] MKV support detection failed:', e); + logger.warn('[StreamsScreen] Stream format detection failed:', e); } @@ -997,19 +1005,24 @@ export const StreamsScreen = () => { } catch (_e) {} return; } - // If provider declares MKV support, force the in-app VLC-based player on iOS + // If stream is actually MKV format, force the in-app VLC-based player on iOS try { - const providerId = stream.addonId || (stream as any).addon; - if (Platform.OS === 'ios' && providerId) { - const providerRequiresVlc = await localScraperService.supportsFormat(providerId, 'mkv'); - if (providerRequiresVlc) { - logger.log(`[StreamsScreen] Forcing in-app VLC for provider '${providerId}' on iOS due to MKV support`); - navigateToPlayer(stream); + if (Platform.OS === 'ios') { + // Check if the actual stream is an MKV file + const lowerUri = (stream.url || '').toLowerCase(); + const contentType = (stream.headers && ((stream.headers as any)['Content-Type'] || (stream.headers as any)['content-type'])) || ''; + const isMkvByHeader = typeof contentType === 'string' && contentType.includes('matroska'); + const isMkvByPath = lowerUri.includes('.mkv') || /[?&]ext=mkv\b/.test(lowerUri) || /format=mkv\b/.test(lowerUri) || /container=mkv\b/.test(lowerUri); + const isMkvFile = Boolean(isMkvByHeader || isMkvByPath); + + if (isMkvFile) { + logger.log(`[StreamsScreen] Stream is MKV format -> forcing VLC on iOS`); + navigateToPlayer(stream, { forceVlc: true }); return; } } } catch (err) { - logger.warn('[StreamsScreen] MKV pre-check failed:', err); + logger.warn('[StreamsScreen] Stream format pre-check failed:', err); } // iOS: very short MKV detection race; never block longer than MKV_HEAD_TIMEOUT_MS