mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-22 09:11:56 +00:00
fixes to videoplayer
This commit is contained in:
parent
42daa4decc
commit
19b6e6b3d5
3 changed files with 198 additions and 113 deletions
|
|
@ -90,6 +90,14 @@ const VideoPlayer: React.FC = () => {
|
||||||
const screenData = Dimensions.get('screen');
|
const screenData = Dimensions.get('screen');
|
||||||
const [screenDimensions, setScreenDimensions] = useState(screenData);
|
const [screenDimensions, setScreenDimensions] = useState(screenData);
|
||||||
|
|
||||||
|
// iPad-specific fullscreen handling
|
||||||
|
const isIPad = Platform.OS === 'ios' && (screenData.width > 1000 || screenData.height > 1000);
|
||||||
|
const shouldUseFullscreen = isIPad;
|
||||||
|
|
||||||
|
// Use window dimensions for iPad instead of screen dimensions
|
||||||
|
const windowData = Dimensions.get('window');
|
||||||
|
const effectiveDimensions = shouldUseFullscreen ? windowData : screenData;
|
||||||
|
|
||||||
const [paused, setPaused] = useState(false);
|
const [paused, setPaused] = useState(false);
|
||||||
const [currentTime, setCurrentTime] = useState(0);
|
const [currentTime, setCurrentTime] = useState(0);
|
||||||
const [duration, setDuration] = useState(0);
|
const [duration, setDuration] = useState(0);
|
||||||
|
|
@ -116,8 +124,8 @@ const VideoPlayer: React.FC = () => {
|
||||||
const openingScaleAnim = useRef(new Animated.Value(0.8)).current;
|
const openingScaleAnim = useRef(new Animated.Value(0.8)).current;
|
||||||
const backgroundFadeAnim = useRef(new Animated.Value(1)).current;
|
const backgroundFadeAnim = useRef(new Animated.Value(1)).current;
|
||||||
const [isBuffering, setIsBuffering] = useState(false);
|
const [isBuffering, setIsBuffering] = useState(false);
|
||||||
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
const [vlcAudioTracks, setVlcAudioTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{id: number, name: string, language?: string}>>([]);
|
const [vlcTextTracks, setVlcTextTracks] = useState<Array<{ id: number, name: string, language?: string }>>([]);
|
||||||
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
const [isPlayerReady, setIsPlayerReady] = useState(false);
|
||||||
const progressAnim = useRef(new Animated.Value(0)).current;
|
const progressAnim = useRef(new Animated.Value(0)).current;
|
||||||
const progressBarRef = useRef<View>(null);
|
const progressBarRef = useRef<View>(null);
|
||||||
|
|
@ -140,6 +148,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
const [customSubtitles, setCustomSubtitles] = useState<SubtitleCue[]>([]);
|
const [customSubtitles, setCustomSubtitles] = useState<SubtitleCue[]>([]);
|
||||||
const [currentSubtitle, setCurrentSubtitle] = useState<string>('');
|
const [currentSubtitle, setCurrentSubtitle] = useState<string>('');
|
||||||
const [subtitleSize, setSubtitleSize] = useState<number>(DEFAULT_SUBTITLE_SIZE);
|
const [subtitleSize, setSubtitleSize] = useState<number>(DEFAULT_SUBTITLE_SIZE);
|
||||||
|
const [subtitleBackground, setSubtitleBackground] = useState<boolean>(true);
|
||||||
const [useCustomSubtitles, setUseCustomSubtitles] = useState<boolean>(false);
|
const [useCustomSubtitles, setUseCustomSubtitles] = useState<boolean>(false);
|
||||||
const [isLoadingSubtitles, setIsLoadingSubtitles] = useState<boolean>(false);
|
const [isLoadingSubtitles, setIsLoadingSubtitles] = useState<boolean>(false);
|
||||||
const [availableSubtitles, setAvailableSubtitles] = useState<WyzieSubtitle[]>([]);
|
const [availableSubtitles, setAvailableSubtitles] = useState<WyzieSubtitle[]>([]);
|
||||||
|
|
@ -224,25 +233,47 @@ const VideoPlayer: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (videoAspectRatio && screenDimensions.width > 0 && screenDimensions.height > 0) {
|
if (videoAspectRatio && effectiveDimensions.width > 0 && effectiveDimensions.height > 0) {
|
||||||
const styles = calculateVideoStyles(
|
const styles = calculateVideoStyles(
|
||||||
videoAspectRatio * 1000,
|
videoAspectRatio * 1000,
|
||||||
1000,
|
1000,
|
||||||
screenDimensions.width,
|
effectiveDimensions.width,
|
||||||
screenDimensions.height
|
effectiveDimensions.height
|
||||||
);
|
);
|
||||||
setCustomVideoStyles(styles);
|
setCustomVideoStyles(styles);
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Screen dimensions changed, recalculated styles:`, styles);
|
logger.log(`[VideoPlayer] Screen dimensions changed, recalculated styles:`, styles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [screenDimensions, videoAspectRatio]);
|
}, [effectiveDimensions, videoAspectRatio]);
|
||||||
|
|
||||||
|
// Force landscape orientation immediately when component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
const lockOrientation = async () => {
|
||||||
|
try {
|
||||||
|
await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
|
||||||
|
logger.log('[VideoPlayer] Locked to landscape orientation');
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('[VideoPlayer] Failed to lock orientation:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Lock orientation immediately
|
||||||
|
lockOrientation();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Unlock orientation when component unmounts
|
||||||
|
ScreenOrientation.unlockAsync().catch(() => {
|
||||||
|
// Ignore unlock errors
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = Dimensions.addEventListener('change', ({ screen }) => {
|
const subscription = Dimensions.addEventListener('change', ({ screen }) => {
|
||||||
setScreenDimensions(screen);
|
setScreenDimensions(screen);
|
||||||
});
|
});
|
||||||
const initializePlayer = () => {
|
const initializePlayer = async () => {
|
||||||
StatusBar.setHidden(true, 'none');
|
StatusBar.setHidden(true, 'none');
|
||||||
enableImmersiveMode();
|
enableImmersiveMode();
|
||||||
startOpeningAnimation();
|
startOpeningAnimation();
|
||||||
|
|
@ -250,10 +281,6 @@ const VideoPlayer: React.FC = () => {
|
||||||
initializePlayer();
|
initializePlayer();
|
||||||
return () => {
|
return () => {
|
||||||
subscription?.remove();
|
subscription?.remove();
|
||||||
const unlockOrientation = async () => {
|
|
||||||
await ScreenOrientation.unlockAsync();
|
|
||||||
};
|
|
||||||
unlockOrientation();
|
|
||||||
disableImmersiveMode();
|
disableImmersiveMode();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -461,7 +488,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
isSeeking.current = false;
|
isSeeking.current = false;
|
||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
logger.log(`[VideoPlayer] Android seek completed to ${timeInSeconds.toFixed(2)}s`);
|
logger.log(`[VideoPlayer] Android seek completed to ${timeInSeconds.toFixed(2)}s`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -685,22 +712,53 @@ const VideoPlayer: React.FC = () => {
|
||||||
}),
|
}),
|
||||||
]).start();
|
]).start();
|
||||||
|
|
||||||
// Longer delay to ensure Trakt sync completes
|
// Cleanup and navigate back
|
||||||
setTimeout(() => {
|
const cleanup = async () => {
|
||||||
ScreenOrientation.unlockAsync().then(() => {
|
try {
|
||||||
disableImmersiveMode();
|
// Unlock orientation first
|
||||||
navigation.goBack();
|
await ScreenOrientation.unlockAsync();
|
||||||
}).catch(() => {
|
logger.log('[VideoPlayer] Orientation unlocked');
|
||||||
// Fallback: navigate even if orientation unlock fails
|
} catch (orientationError) {
|
||||||
disableImmersiveMode();
|
logger.warn('[VideoPlayer] Failed to unlock orientation:', orientationError);
|
||||||
navigation.goBack();
|
}
|
||||||
});
|
|
||||||
}, 500); // Increased from 100ms to 500ms
|
// Disable immersive mode
|
||||||
|
disableImmersiveMode();
|
||||||
|
|
||||||
|
// Navigate back with proper handling for fullscreen modal
|
||||||
|
try {
|
||||||
|
if (navigation.canGoBack()) {
|
||||||
|
navigation.goBack();
|
||||||
|
} else {
|
||||||
|
// Fallback: navigate to main tabs if can't go back
|
||||||
|
navigation.navigate('MainTabs');
|
||||||
|
}
|
||||||
|
logger.log('[VideoPlayer] Navigation completed');
|
||||||
|
} catch (navError) {
|
||||||
|
logger.error('[VideoPlayer] Navigation error:', navError);
|
||||||
|
// Last resort: try to navigate to home
|
||||||
|
navigation.navigate('MainTabs');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delay to ensure Trakt sync completes and animations finish
|
||||||
|
setTimeout(cleanup, 500);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[VideoPlayer] Error syncing to Trakt before closing:', error);
|
logger.error('[VideoPlayer] Error syncing to Trakt before closing:', error);
|
||||||
// Navigate anyway even if sync fails
|
// Navigate anyway even if sync fails
|
||||||
disableImmersiveMode();
|
disableImmersiveMode();
|
||||||
navigation.goBack();
|
try {
|
||||||
|
await ScreenOrientation.unlockAsync();
|
||||||
|
} catch (orientationError) {
|
||||||
|
// Ignore orientation unlock errors
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigation.canGoBack()) {
|
||||||
|
navigation.goBack();
|
||||||
|
} else {
|
||||||
|
navigation.navigate('MainTabs');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -864,7 +922,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
|
|
||||||
const togglePlayback = () => {
|
const togglePlayback = () => {
|
||||||
if (vlcRef.current) {
|
if (vlcRef.current) {
|
||||||
setPaused(!paused);
|
setPaused(!paused);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -912,6 +970,10 @@ const VideoPlayer: React.FC = () => {
|
||||||
saveSubtitleSize(newSize);
|
saveSubtitleSize(newSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleSubtitleBackground = () => {
|
||||||
|
setSubtitleBackground(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pendingSeek && isPlayerReady && isVideoLoaded && duration > 0) {
|
if (pendingSeek && isPlayerReady && isVideoLoaded && duration > 0) {
|
||||||
logger.log(`[VideoPlayer] Player ready after source change, seeking to position: ${pendingSeek.position}s out of ${duration}s total`);
|
logger.log(`[VideoPlayer] Player ready after source change, seeking to position: ${pendingSeek.position}s out of ${duration}s total`);
|
||||||
|
|
@ -1019,13 +1081,21 @@ const VideoPlayer: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, {
|
<View style={[
|
||||||
width: screenDimensions.width,
|
styles.container,
|
||||||
height: screenDimensions.height,
|
shouldUseFullscreen ? {
|
||||||
position: 'absolute',
|
// iPad fullscreen: use flex layout instead of absolute positioning
|
||||||
top: 0,
|
flex: 1,
|
||||||
left: 0,
|
width: '100%',
|
||||||
}]}>
|
height: '100%',
|
||||||
|
} : {
|
||||||
|
// iPhone: use absolute positioning with screen dimensions
|
||||||
|
width: screenDimensions.width,
|
||||||
|
height: screenDimensions.height,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
}]}>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
styles.openingOverlay,
|
styles.openingOverlay,
|
||||||
|
|
@ -1210,6 +1280,7 @@ const VideoPlayer: React.FC = () => {
|
||||||
useCustomSubtitles={useCustomSubtitles}
|
useCustomSubtitles={useCustomSubtitles}
|
||||||
currentSubtitle={currentSubtitle}
|
currentSubtitle={currentSubtitle}
|
||||||
subtitleSize={subtitleSize}
|
subtitleSize={subtitleSize}
|
||||||
|
subtitleBackground={subtitleBackground}
|
||||||
zoomScale={zoomScale}
|
zoomScale={zoomScale}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -1246,11 +1317,13 @@ const VideoPlayer: React.FC = () => {
|
||||||
selectedTextTrack={selectedTextTrack}
|
selectedTextTrack={selectedTextTrack}
|
||||||
useCustomSubtitles={useCustomSubtitles}
|
useCustomSubtitles={useCustomSubtitles}
|
||||||
subtitleSize={subtitleSize}
|
subtitleSize={subtitleSize}
|
||||||
|
subtitleBackground={subtitleBackground}
|
||||||
fetchAvailableSubtitles={fetchAvailableSubtitles}
|
fetchAvailableSubtitles={fetchAvailableSubtitles}
|
||||||
loadWyzieSubtitle={loadWyzieSubtitle}
|
loadWyzieSubtitle={loadWyzieSubtitle}
|
||||||
selectTextTrack={selectTextTrack}
|
selectTextTrack={selectTextTrack}
|
||||||
increaseSubtitleSize={increaseSubtitleSize}
|
increaseSubtitleSize={increaseSubtitleSize}
|
||||||
decreaseSubtitleSize={decreaseSubtitleSize}
|
decreaseSubtitleSize={decreaseSubtitleSize}
|
||||||
|
toggleSubtitleBackground={toggleSubtitleBackground}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SourcesModal
|
<SourcesModal
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native';
|
||||||
export const styles = StyleSheet.create({
|
export const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: '#000',
|
backgroundColor: '#000',
|
||||||
|
flex: 1,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
|
|
||||||
|
|
@ -770,9 +770,20 @@ const AppNavigator = ({ initialRouteName }: { initialRouteName?: keyof RootStack
|
||||||
options={{
|
options={{
|
||||||
animation: 'slide_from_right',
|
animation: 'slide_from_right',
|
||||||
animationDuration: Platform.OS === 'android' ? 200 : 300,
|
animationDuration: Platform.OS === 'android' ? 200 : 300,
|
||||||
|
// Force fullscreen presentation on iPad
|
||||||
|
presentation: Platform.OS === 'ios' ? 'fullScreenModal' : 'card',
|
||||||
|
// Disable gestures during video playback
|
||||||
|
gestureEnabled: false,
|
||||||
|
// Ensure proper orientation handling
|
||||||
|
orientation: 'landscape',
|
||||||
contentStyle: {
|
contentStyle: {
|
||||||
backgroundColor: '#000000', // Pure black for video player
|
backgroundColor: '#000000', // Pure black for video player
|
||||||
},
|
},
|
||||||
|
// iPad-specific fullscreen options
|
||||||
|
...(Platform.OS === 'ios' && {
|
||||||
|
statusBarHidden: true,
|
||||||
|
statusBarAnimation: 'none',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue