VideoPlayer code cleanup

This commit is contained in:
tapframe 2025-09-17 20:23:46 +05:30
parent d0719fabec
commit c767de12aa
4 changed files with 87 additions and 89 deletions

View file

@ -75,9 +75,6 @@ const AndroidVideoPlayer: React.FC = () => {
backdrop
} = route.params;
// 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));
// Check if the stream is HLS (m3u8 playlist)
const isHlsStream = (url: string) => {
@ -99,37 +96,11 @@ const AndroidVideoPlayer: React.FC = () => {
} as any;
};
// Xprime-specific headers for better compatibility (from local-scrapers-repo)
const getXprimeHeaders = () => {
if (!isXprimeStream) return {};
const xprimeHeaders = {
'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;
logger.log('[AndroidVideoPlayer] Applying Xprime headers for stream:', uri);
return xprimeHeaders;
};
// Get appropriate headers based on stream type
const getStreamHeaders = () => {
// For Xprime streams, be more flexible - only use HLS headers if it actually looks like HLS
if (isXprimeStream) {
if (isHlsStream(currentStreamUrl)) {
logger.log('[AndroidVideoPlayer] Xprime HLS stream detected, applying HLS headers');
return getXprimeHeaders();
} else {
logger.log('[AndroidVideoPlayer] Xprime MP4 stream detected, using default headers');
return Platform.OS === 'android' ? defaultAndroidHeaders() : defaultIosHeaders();
}
} else if (isHlsStream(currentStreamUrl)) {
// Use HLS headers for HLS streams, default headers for everything else
if (isHlsStream(currentStreamUrl)) {
logger.log('[AndroidVideoPlayer] Detected HLS stream, applying HLS headers');
return getHlsHeaders();
}
@ -1535,12 +1506,8 @@ const AndroidVideoPlayer: React.FC = () => {
return;
}
// Detect Xprime provider to enable a one-shot silent retry (warms upstream/cache)
const providerName = ((currentStreamProvider || streamProvider || '') as string).toLowerCase();
const isXprimeProvider = providerName.includes('xprime');
// One-shot, silent retry without showing error UI
if (isXprimeProvider && retryAttemptRef.current < 1) {
if (retryAttemptRef.current < 1) {
retryAttemptRef.current = 1;
// Cache-bust to force a fresh fetch and warm upstream
const addRetryParam = (url: string) => {
@ -1548,7 +1515,7 @@ const AndroidVideoPlayer: React.FC = () => {
return `${url}${sep}rn_retry_ts=${Date.now()}`;
};
const bustedUrl = addRetryParam(currentStreamUrl);
logger.warn('[AndroidVideoPlayer] Silent retry for Xprime with cache-busted URL');
logger.warn('[AndroidVideoPlayer] Silent retry with cache-busted URL');
// Ensure no modal is visible
if (errorTimeoutRef.current) {
clearTimeout(errorTimeoutRef.current);

View file

@ -46,57 +46,23 @@ import * as Brightness from 'expo-brightness';
const VideoPlayer: React.FC = () => {
const insets = useSafeAreaInsets();
const route = useRoute<RouteProp<RootStackParamList, 'Player'>>();
const { streamProvider, uri, headers, forceVlc } = route.params as any;
// 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));
safeDebugLog("Stream detection", {
uri,
streamProvider,
isXprimeStream,
platform: Platform.OS
});
// Xprime-specific headers for better compatibility (from local-scrapers-repo)
const getXprimeHeaders = () => {
if (!isXprimeStream) return {};
const xprimeHeaders = {
'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;
logger.log('[VideoPlayer] Applying Xprime headers for stream:', uri);
return xprimeHeaders;
};
const { uri, headers, forceVlc, streamProvider } = route.params as any;
// Detect if stream is MKV format
const isMkvFile = isMkvStream(uri, headers);
// Use AndroidVideoPlayer for:
// - Android devices
// - Xprime streams (unless it's MKV on iOS - allow VLC for better compatibility)
// - Non-MKV files on iOS (unless forceVlc is set)
// Use VideoPlayer (VLC) for:
// - MKV files on iOS (unless forceVlc is set, even for Xprime)
// - MKV files on iOS (unless forceVlc is set)
const shouldUseAndroidPlayer = Platform.OS === 'android' ||
(isXprimeStream && !(Platform.OS === 'ios' && isMkvFile)) ||
(Platform.OS === 'ios' && !isMkvFile && !forceVlc);
safeDebugLog("Player selection logic", {
platform: Platform.OS,
isXprimeStream,
isMkvFile,
forceVlc,
xprimeException: isXprimeStream && Platform.OS === 'ios' && isMkvFile,
shouldUseAndroidPlayer
});
if (shouldUseAndroidPlayer) {
@ -1176,6 +1142,38 @@ const VideoPlayer: React.FC = () => {
}
if (data.textTracks && data.textTracks.length > 0) {
setVlcTextTracks(data.textTracks);
// Auto-select English subtitle track if available
if (selectedTextTrack === -1 && !useCustomSubtitles && data.textTracks.length > 0) {
if (DEBUG_MODE) {
logger.log(`[VideoPlayer] Available subtitle tracks:`, data.textTracks.map((track: any) => ({
id: track.id,
index: track.index,
name: track.name,
language: track.language
})));
}
// Look for English track first
const englishTrack = data.textTracks.find((track: any) => {
const lang = (track.language || '').toLowerCase();
const name = (track.name || '').toLowerCase();
return lang === 'english' || lang === 'en' || lang === 'eng' ||
name.includes('english') || name.includes('en');
});
if (englishTrack) {
// Try different ID fields that VLC might use
const trackId = englishTrack.id !== undefined ? englishTrack.id :
englishTrack.index !== undefined ? englishTrack.index : 0;
setSelectedTextTrack(trackId);
if (DEBUG_MODE) {
logger.log(`[VideoPlayer] Auto-selected English subtitle track: ${englishTrack.name || 'Unknown'} (ID: ${trackId})`);
}
} else if (DEBUG_MODE) {
logger.log(`[VideoPlayer] No English subtitle track found, keeping subtitles disabled`);
}
}
}
setIsVideoLoaded(true);
@ -2557,13 +2555,12 @@ const VideoPlayer: React.FC = () => {
ref={vlcRef}
style={[styles.video, customVideoStyles, { transform: [{ scale: zoomScale }] }]}
source={(() => {
// Use Xprime headers if detected, otherwise use headers from route params
const finalHeaders = getXprimeHeaders() || headers;
const sourceWithHeaders = finalHeaders ? {
// Use route headers for VLC streams
const sourceWithHeaders = headers && Object.keys(headers).length > 0 ? {
uri: currentStreamUrl,
headers: finalHeaders
headers: headers
} : { uri: currentStreamUrl };
return sourceWithHeaders;
})()}
paused={paused}
@ -2576,7 +2573,7 @@ const VideoPlayer: React.FC = () => {
onPaused={onPaused}
resizeMode={resizeMode as any}
audioTrack={selectedAudioTrack ?? undefined}
textTrack={useCustomSubtitles ? -1 : (selectedTextTrack ?? undefined)}
textTrack={useCustomSubtitles ? -1 : (selectedTextTrack >= 0 ? selectedTextTrack : -1)}
autoAspectRatio
volume={volume / 100}
/>

View file

@ -288,6 +288,10 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
<View style={{ gap: 8 }}>
{vlcTextTracks.map((track) => {
const isSelected = selectedTextTrack === track.id && !useCustomSubtitles;
// Debug logging for subtitle selection
if (__DEV__ && vlcTextTracks.length > 0) {
console.log('[SubtitleModals] Track:', track.id, track.name, 'Selected:', selectedTextTrack, 'isSelected:', isSelected, 'useCustom:', useCustomSubtitles);
}
return (
<TouchableOpacity
key={track.id}

View file

@ -18,6 +18,7 @@ import { RootStackParamList } from '../navigation/AppNavigator';
import { Meta, stremioService } from '../services/stremioService';
import { useTheme } from '../contexts/ThemeContext';
import { Image } from 'expo-image';
import { BlurView } from 'expo-blur';
import { MaterialIcons } from '@expo/vector-icons';
import { logger } from '../utils/logger';
import { useCustomCatalogNames } from '../hooks/useCustomCatalogNames';
@ -193,12 +194,25 @@ const createStyles = (colors: any) => StyleSheet.create({
top: 10,
right: 10,
backgroundColor: 'rgba(0,0,0,0.7)',
borderRadius: 0,
borderRadius: 10,
paddingHorizontal: 10,
paddingVertical: 6,
flexDirection: 'row',
alignItems: 'center',
},
badgeBlur: {
position: 'absolute',
top: 10,
right: 10,
borderRadius: 10,
overflow: 'hidden',
},
badgeContent: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 10,
paddingVertical: 6,
},
badgeText: {
fontSize: 11,
fontWeight: '600',
@ -584,15 +598,31 @@ const CatalogScreen: React.FC<CatalogScreenProps> = ({ route, navigation }) => {
/>
{type === 'movie' && nowPlayingMovies.has(item.id) && (
<View style={styles.badgeContainer}>
<MaterialIcons
name="theaters"
size={12}
color={colors.white}
style={{ marginRight: 4 }}
/>
<Text style={styles.badgeText}>In Theaters</Text>
</View>
Platform.OS === 'ios' ? (
<View style={styles.badgeBlur}>
<BlurView intensity={40} tint={isDarkMode ? 'dark' : 'light'} style={{ borderRadius: 10 }}>
<View style={styles.badgeContent}>
<MaterialIcons
name="theaters"
size={12}
color={colors.white}
style={{ marginRight: 4 }}
/>
<Text style={styles.badgeText}>In Theaters</Text>
</View>
</BlurView>
</View>
) : (
<View style={styles.badgeContainer}>
<MaterialIcons
name="theaters"
size={12}
color={colors.white}
style={{ marginRight: 4 }}
/>
<Text style={styles.badgeText}>In Theaters</Text>
</View>
)
)}
</TouchableOpacity>
);