mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
VideoPlayer code cleanup
This commit is contained in:
parent
d0719fabec
commit
c767de12aa
4 changed files with 87 additions and 89 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue