From c54ea1d591af773b0f4e4f06e5adb6e24a27dfcf Mon Sep 17 00:00:00 2001 From: tapframe Date: Sat, 25 Oct 2025 18:17:55 +0530 Subject: [PATCH] KSPlayer url encoding logic improvements --- src/components/player/KSPlayerCore.tsx | 72 +++++++------------------- src/screens/StreamsScreen.tsx | 28 +++++++++- 2 files changed, 45 insertions(+), 55 deletions(-) diff --git a/src/components/player/KSPlayerCore.tsx b/src/components/player/KSPlayerCore.tsx index 0a8be547..611ff5f6 100644 --- a/src/components/player/KSPlayerCore.tsx +++ b/src/components/player/KSPlayerCore.tsx @@ -49,6 +49,13 @@ const KSPlayerCore: React.FC = () => { const route = useRoute>(); const { uri, headers, streamProvider } = route.params as any; + console.log('[KSPlayerCore] Received navigation params:', { + uri, + headers, + headersKeys: headers ? Object.keys(headers) : [], + streamProvider + }); + const navigation = useNavigation(); // KSPlayer is active only on iOS for MKV streams @@ -199,57 +206,7 @@ const KSPlayerCore: React.FC = () => { const nextIdx = (idx + 1) % speedOptions.length; setPlaybackSpeed(speedOptions[nextIdx]); }, [playbackSpeed, speedOptions]); - // Smart URL processing for KSPlayer compatibility - const processUrlForKsPlayer = (url: string): string => { - try { - // Validate URL first - const urlObj = new URL(url); - - // Only decode if the URL appears to be double-encoded - // Be more conservative - only check for clear double-encoding indicators - - // Check 1: %25 indicates double-encoded % character - const hasDoubleEncodedPercent = url.includes('%25'); - - // Check 2: Only flag %2F + // if encoded slashes appear in the path/domain part - // (not just in query params where they might be legitimate base64/etc) - const hasProblematicEncodedSlashes = (() => { - const beforeQuery = url.split('?')[0]; // Get URL before query params - return beforeQuery.includes('%2F') && beforeQuery.includes('//'); - })(); - - // Check 3: Only flag %3A + :// if colons are encoded in the scheme - const hasProblematicEncodedColons = (() => { - const schemeEnd = url.indexOf('://'); - if (schemeEnd === -1) return false; - const schemePart = url.substring(0, schemeEnd); - return schemePart.includes('%3A'); - })(); - - const hasDoubleEncoding = hasDoubleEncodedPercent || - hasProblematicEncodedSlashes || - hasProblematicEncodedColons; - - if (hasDoubleEncoding) { - logger.log('[VideoPlayer] Detected double-encoded URL, decoding once'); - return decodeURIComponent(url); - } - - // For URLs with special characters in query params, ensure proper encoding - if (urlObj.search) { - const searchParams = new URLSearchParams(urlObj.search); - urlObj.search = searchParams.toString(); - return urlObj.toString(); - } - - return url; - } catch (e) { - logger.warn('[VideoPlayer] URL processing failed, using original:', e); - return url; - } - }; - - const [currentStreamUrl, setCurrentStreamUrl] = useState(processUrlForKsPlayer(uri)); + const [currentStreamUrl, setCurrentStreamUrl] = useState(uri); const [isChangingSource, setIsChangingSource] = useState(false); const [showErrorModal, setShowErrorModal] = useState(false); const [errorDetails, setErrorDetails] = useState(''); @@ -371,6 +328,15 @@ const KSPlayerCore: React.FC = () => { } } }, [metadata]); + + // Log video source configuration with headers + useEffect(() => { + console.log('[KSPlayerCore] Video source configured with:', { + uri: currentStreamUrl, + hasHeaders: !!(headers && Object.keys(headers).length > 0), + headers: headers && Object.keys(headers).length > 0 ? headers : undefined + }); + }, [currentStreamUrl, headers]); // Resolve current episode description for series const currentEpisodeDescription = (() => { try { @@ -2353,8 +2319,8 @@ const KSPlayerCore: React.FC = () => { // Set pending seek state setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying }); - // Update the stream URL and details immediately (process URL for KSPlayer) - setCurrentStreamUrl(processUrlForKsPlayer(newStream.url)); + // Update the stream URL and details immediately + setCurrentStreamUrl(newStream.url); setCurrentQuality(newQuality); setCurrentStreamProvider(newProvider); setCurrentStreamName(newStreamName); diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index 83e92bcf..564ea5de 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -756,6 +756,30 @@ export const StreamsScreen = () => { }, [type, id, currentEpisode?.season_number, currentEpisode?.episode_number]); const navigateToPlayer = useCallback(async (stream: Stream, options?: { forceVlc?: boolean; headers?: Record }) => { + // Filter headers for Vidrock - only send essential headers + const filterHeadersForVidrock = (headers: Record | undefined): Record | undefined => { + if (!headers) return undefined; + + // Only keep essential headers for Vidrock + const essentialHeaders: Record = {}; + if (headers['User-Agent']) essentialHeaders['User-Agent'] = headers['User-Agent']; + if (headers['Referer']) essentialHeaders['Referer'] = headers['Referer']; + if (headers['Origin']) essentialHeaders['Origin'] = headers['Origin']; + + return Object.keys(essentialHeaders).length > 0 ? essentialHeaders : undefined; + }; + + const finalHeaders = filterHeadersForVidrock(options?.headers || stream.headers); + + // Add logging here + console.log('[StreamsScreen] Navigating to player with headers:', { + streamHeaders: stream.headers, + optionsHeaders: options?.headers, + filteredHeaders: finalHeaders, + streamUrl: stream.url, + streamName: stream.name || stream.title + }); + // Add 50ms delay before navigating to player await new Promise(resolve => setTimeout(resolve, 50)); @@ -826,8 +850,8 @@ export const StreamsScreen = () => { year: metadata?.year, streamProvider: streamProvider, streamName: streamName, - // Always prefer stream.headers; player will use these for requests - headers: options?.headers || stream.headers || undefined, + // Use filtered headers for Vidrock compatibility + headers: finalHeaders, // Android will use this to choose VLC path; iOS ignores forceVlc, id,