mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-24 02:02:09 +00:00
KSPlayer url encoding logic improvements
This commit is contained in:
parent
24c0bb5247
commit
c54ea1d591
2 changed files with 45 additions and 55 deletions
|
|
@ -49,6 +49,13 @@ const KSPlayerCore: React.FC = () => {
|
||||||
const route = useRoute<RouteProp<RootStackParamList, 'PlayerIOS'>>();
|
const route = useRoute<RouteProp<RootStackParamList, 'PlayerIOS'>>();
|
||||||
const { uri, headers, streamProvider } = route.params as any;
|
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<RootStackNavigationProp>();
|
const navigation = useNavigation<RootStackNavigationProp>();
|
||||||
|
|
||||||
// KSPlayer is active only on iOS for MKV streams
|
// KSPlayer is active only on iOS for MKV streams
|
||||||
|
|
@ -199,57 +206,7 @@ const KSPlayerCore: React.FC = () => {
|
||||||
const nextIdx = (idx + 1) % speedOptions.length;
|
const nextIdx = (idx + 1) % speedOptions.length;
|
||||||
setPlaybackSpeed(speedOptions[nextIdx]);
|
setPlaybackSpeed(speedOptions[nextIdx]);
|
||||||
}, [playbackSpeed, speedOptions]);
|
}, [playbackSpeed, speedOptions]);
|
||||||
// Smart URL processing for KSPlayer compatibility
|
const [currentStreamUrl, setCurrentStreamUrl] = useState<string>(uri);
|
||||||
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<string>(processUrlForKsPlayer(uri));
|
|
||||||
const [isChangingSource, setIsChangingSource] = useState<boolean>(false);
|
const [isChangingSource, setIsChangingSource] = useState<boolean>(false);
|
||||||
const [showErrorModal, setShowErrorModal] = useState(false);
|
const [showErrorModal, setShowErrorModal] = useState(false);
|
||||||
const [errorDetails, setErrorDetails] = useState<string>('');
|
const [errorDetails, setErrorDetails] = useState<string>('');
|
||||||
|
|
@ -371,6 +328,15 @@ const KSPlayerCore: React.FC = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [metadata]);
|
}, [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
|
// Resolve current episode description for series
|
||||||
const currentEpisodeDescription = (() => {
|
const currentEpisodeDescription = (() => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -2353,8 +2319,8 @@ const KSPlayerCore: React.FC = () => {
|
||||||
// Set pending seek state
|
// Set pending seek state
|
||||||
setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying });
|
setPendingSeek({ position: savedPosition, shouldPlay: wasPlaying });
|
||||||
|
|
||||||
// Update the stream URL and details immediately (process URL for KSPlayer)
|
// Update the stream URL and details immediately
|
||||||
setCurrentStreamUrl(processUrlForKsPlayer(newStream.url));
|
setCurrentStreamUrl(newStream.url);
|
||||||
setCurrentQuality(newQuality);
|
setCurrentQuality(newQuality);
|
||||||
setCurrentStreamProvider(newProvider);
|
setCurrentStreamProvider(newProvider);
|
||||||
setCurrentStreamName(newStreamName);
|
setCurrentStreamName(newStreamName);
|
||||||
|
|
|
||||||
|
|
@ -756,6 +756,30 @@ export const StreamsScreen = () => {
|
||||||
}, [type, id, currentEpisode?.season_number, currentEpisode?.episode_number]);
|
}, [type, id, currentEpisode?.season_number, currentEpisode?.episode_number]);
|
||||||
|
|
||||||
const navigateToPlayer = useCallback(async (stream: Stream, options?: { forceVlc?: boolean; headers?: Record<string, string> }) => {
|
const navigateToPlayer = useCallback(async (stream: Stream, options?: { forceVlc?: boolean; headers?: Record<string, string> }) => {
|
||||||
|
// Filter headers for Vidrock - only send essential headers
|
||||||
|
const filterHeadersForVidrock = (headers: Record<string, string> | undefined): Record<string, string> | undefined => {
|
||||||
|
if (!headers) return undefined;
|
||||||
|
|
||||||
|
// Only keep essential headers for Vidrock
|
||||||
|
const essentialHeaders: Record<string, string> = {};
|
||||||
|
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
|
// Add 50ms delay before navigating to player
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
|
||||||
|
|
@ -826,8 +850,8 @@ export const StreamsScreen = () => {
|
||||||
year: metadata?.year,
|
year: metadata?.year,
|
||||||
streamProvider: streamProvider,
|
streamProvider: streamProvider,
|
||||||
streamName: streamName,
|
streamName: streamName,
|
||||||
// Always prefer stream.headers; player will use these for requests
|
// Use filtered headers for Vidrock compatibility
|
||||||
headers: options?.headers || stream.headers || undefined,
|
headers: finalHeaders,
|
||||||
// Android will use this to choose VLC path; iOS ignores
|
// Android will use this to choose VLC path; iOS ignores
|
||||||
forceVlc,
|
forceVlc,
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue