mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 00:32:04 +00:00
Fix: Player orientation now correctly resets when exiting video player
This commit is contained in:
parent
0a1e008d5f
commit
a61c1e6456
9 changed files with 77 additions and 39 deletions
13
package-lock.json
generated
13
package-lock.json
generated
|
|
@ -51,7 +51,7 @@
|
||||||
"expo-glass-effect": "~0.1.4",
|
"expo-glass-effect": "~0.1.4",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-intent-launcher": "~13.0.7",
|
"expo-intent-launcher": "~13.0.7",
|
||||||
"expo-libvlc-player": "^2.2.3",
|
"expo-keep-awake": "~15.0.8",
|
||||||
"expo-linear-gradient": "~15.0.7",
|
"expo-linear-gradient": "~15.0.7",
|
||||||
"expo-localization": "~17.0.7",
|
"expo-localization": "~17.0.7",
|
||||||
"expo-navigation-bar": "~5.0.10",
|
"expo-navigation-bar": "~5.0.10",
|
||||||
|
|
@ -6512,17 +6512,6 @@
|
||||||
"react": "*"
|
"react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-libvlc-player": {
|
|
||||||
"version": "2.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/expo-libvlc-player/-/expo-libvlc-player-2.2.5.tgz",
|
|
||||||
"integrity": "sha512-Hl0XiRNK5iwPMDRWYouA7+Xzf804GZ/AMVTU87ktUlQMU5bgTUFgmi8QjlOLGEF5LpVp7LDFfQwsDpXP1ggpag==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"expo": "*",
|
|
||||||
"react": "*",
|
|
||||||
"react-native": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/expo-linear-gradient": {
|
"node_modules/expo-linear-gradient": {
|
||||||
"version": "15.0.8",
|
"version": "15.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz",
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
"expo-glass-effect": "~0.1.4",
|
"expo-glass-effect": "~0.1.4",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-intent-launcher": "~13.0.7",
|
"expo-intent-launcher": "~13.0.7",
|
||||||
|
"expo-keep-awake": "~15.0.8",
|
||||||
"expo-linear-gradient": "~15.0.7",
|
"expo-linear-gradient": "~15.0.7",
|
||||||
"expo-localization": "~17.0.7",
|
"expo-localization": "~17.0.7",
|
||||||
"expo-navigation-bar": "~5.0.10",
|
"expo-navigation-bar": "~5.0.10",
|
||||||
|
|
@ -103,4 +104,4 @@
|
||||||
"xcode": "^3.0.1"
|
"xcode": "^3.0.1"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,14 @@ import {
|
||||||
usePlayerState,
|
usePlayerState,
|
||||||
usePlayerModals,
|
usePlayerModals,
|
||||||
useSpeedControl,
|
useSpeedControl,
|
||||||
useOpeningAnimation
|
useOpeningAnimation,
|
||||||
|
useWatchProgress
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
// Android-specific hooks
|
// Android-specific hooks
|
||||||
import { usePlayerSetup } from './android/hooks/usePlayerSetup';
|
import { usePlayerSetup } from './android/hooks/usePlayerSetup';
|
||||||
import { usePlayerTracks } from './android/hooks/usePlayerTracks';
|
import { usePlayerTracks } from './android/hooks/usePlayerTracks';
|
||||||
import { useWatchProgress } from './android/hooks/useWatchProgress';
|
|
||||||
import { usePlayerControls } from './android/hooks/usePlayerControls';
|
import { usePlayerControls } from './android/hooks/usePlayerControls';
|
||||||
import { useNextEpisode } from './android/hooks/useNextEpisode';
|
import { useNextEpisode } from './android/hooks/useNextEpisode';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import EpisodesModal from './modals/EpisodesModal';
|
||||||
import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal';
|
import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal';
|
||||||
import { ErrorModal } from './modals/ErrorModal';
|
import { ErrorModal } from './modals/ErrorModal';
|
||||||
import CustomSubtitles from './subtitles/CustomSubtitles';
|
import CustomSubtitles from './subtitles/CustomSubtitles';
|
||||||
|
import ResumeOverlay from './modals/ResumeOverlay';
|
||||||
import { SpeedActivatedOverlay, PauseOverlay, GestureControls } from './components';
|
import { SpeedActivatedOverlay, PauseOverlay, GestureControls } from './components';
|
||||||
|
|
||||||
// Platform-specific components
|
// Platform-specific components
|
||||||
|
|
@ -30,7 +31,8 @@ import {
|
||||||
usePlayerTracks,
|
usePlayerTracks,
|
||||||
useCustomSubtitles,
|
useCustomSubtitles,
|
||||||
usePlayerControls,
|
usePlayerControls,
|
||||||
usePlayerSetup
|
usePlayerSetup,
|
||||||
|
useWatchProgress
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
|
||||||
// Platform-specific hooks
|
// Platform-specific hooks
|
||||||
|
|
@ -137,6 +139,15 @@ const KSPlayerCore: React.FC = () => {
|
||||||
isMounted
|
isMounted
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const watchProgress = useWatchProgress(
|
||||||
|
id, type, episodeId,
|
||||||
|
currentTime,
|
||||||
|
duration,
|
||||||
|
paused,
|
||||||
|
traktAutosync,
|
||||||
|
controls.seekToTime
|
||||||
|
);
|
||||||
|
|
||||||
// Gestures
|
// Gestures
|
||||||
const fadeAnim = useRef(new Animated.Value(1)).current;
|
const fadeAnim = useRef(new Animated.Value(1)).current;
|
||||||
|
|
||||||
|
|
@ -157,9 +168,6 @@ const KSPlayerCore: React.FC = () => {
|
||||||
const [brightness, setBrightnessState] = useState(0.5);
|
const [brightness, setBrightnessState] = useState(0.5);
|
||||||
const [isSliderDragging, setIsSliderDragging] = useState(false);
|
const [isSliderDragging, setIsSliderDragging] = useState(false);
|
||||||
|
|
||||||
// Watch Progress State
|
|
||||||
const [initialPosition, setInitialPosition] = useState<number | null>(routeInitialPosition || null);
|
|
||||||
|
|
||||||
// Shared Gesture Hook
|
// Shared Gesture Hook
|
||||||
const gestureControls = usePlayerGestureControls({
|
const gestureControls = usePlayerGestureControls({
|
||||||
volume: volume,
|
volume: volume,
|
||||||
|
|
@ -173,7 +181,8 @@ const KSPlayerCore: React.FC = () => {
|
||||||
setScreenDimensions,
|
setScreenDimensions,
|
||||||
setVolume: setVolumeState,
|
setVolume: setVolumeState,
|
||||||
setBrightness: setBrightnessState,
|
setBrightness: setBrightnessState,
|
||||||
isOpeningAnimationComplete: openingAnim.isOpeningAnimationComplete
|
isOpeningAnimationComplete: openingAnim.isOpeningAnimationComplete,
|
||||||
|
paused: paused
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refs for Logic
|
// Refs for Logic
|
||||||
|
|
@ -328,9 +337,10 @@ const KSPlayerCore: React.FC = () => {
|
||||||
openingAnim.completeOpeningAnimation();
|
openingAnim.completeOpeningAnimation();
|
||||||
|
|
||||||
// Initial Seek
|
// Initial Seek
|
||||||
if (initialPosition && initialPosition > 0) {
|
const resumeTarget = routeInitialPosition || watchProgress.initialPosition || watchProgress.initialSeekTargetRef?.current;
|
||||||
|
if (resumeTarget && resumeTarget > 0 && !watchProgress.showResumeOverlay && data.duration > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
controls.seekToTime(initialPosition);
|
controls.seekToTime(resumeTarget);
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -602,6 +612,23 @@ const KSPlayerCore: React.FC = () => {
|
||||||
speed={speedControl.holdToSpeedValue}
|
speed={speedControl.holdToSpeedValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ResumeOverlay
|
||||||
|
showResumeOverlay={watchProgress.showResumeOverlay}
|
||||||
|
resumePosition={watchProgress.resumePosition}
|
||||||
|
duration={watchProgress.savedDuration || duration}
|
||||||
|
title={title}
|
||||||
|
season={season}
|
||||||
|
episode={episode}
|
||||||
|
handleResume={() => {
|
||||||
|
watchProgress.setShowResumeOverlay(false);
|
||||||
|
if (watchProgress.resumePosition) controls.seekToTime(watchProgress.resumePosition);
|
||||||
|
}}
|
||||||
|
handleStartFromBeginning={() => {
|
||||||
|
watchProgress.setShowResumeOverlay(false);
|
||||||
|
controls.seekToTime(0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Pause Overlay */}
|
{/* Pause Overlay */}
|
||||||
<PauseOverlay
|
<PauseOverlay
|
||||||
visible={paused && !showControls}
|
visible={paused && !showControls}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { StatusBar, Platform, Dimensions, AppState } from 'react-native';
|
||||||
import RNImmersiveMode from 'react-native-immersive-mode';
|
import RNImmersiveMode from 'react-native-immersive-mode';
|
||||||
import * as NavigationBar from 'expo-navigation-bar';
|
import * as NavigationBar from 'expo-navigation-bar';
|
||||||
import * as Brightness from 'expo-brightness';
|
import * as Brightness from 'expo-brightness';
|
||||||
|
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
|
||||||
import { logger } from '../../../../utils/logger';
|
import { logger } from '../../../../utils/logger';
|
||||||
import { useFocusEffect } from '@react-navigation/native';
|
import { useFocusEffect } from '@react-navigation/native';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
@ -19,6 +20,19 @@ export const usePlayerSetup = (
|
||||||
const originalSystemBrightnessModeRef = useRef<number | null>(null);
|
const originalSystemBrightnessModeRef = useRef<number | null>(null);
|
||||||
const isAppBackgrounded = useRef(false);
|
const isAppBackgrounded = useRef(false);
|
||||||
|
|
||||||
|
// Prevent screen sleep while playing
|
||||||
|
// Prevent screen sleep while playing
|
||||||
|
useEffect(() => {
|
||||||
|
if (!paused) {
|
||||||
|
activateKeepAwakeAsync();
|
||||||
|
} else {
|
||||||
|
deactivateKeepAwake();
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
deactivateKeepAwake();
|
||||||
|
};
|
||||||
|
}, [paused]);
|
||||||
|
|
||||||
const enableImmersiveMode = async () => {
|
const enableImmersiveMode = async () => {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
// Standard immersive mode
|
// Standard immersive mode
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,4 @@ export { usePlayerSetup } from './usePlayerSetup';
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
export { useNextEpisode } from './useNextEpisode';
|
export { useNextEpisode } from './useNextEpisode';
|
||||||
|
export { useWatchProgress } from './useWatchProgress';
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
isSeeking.current = true;
|
isSeeking.current = true;
|
||||||
|
|
||||||
// iOS optimization: pause while seeking for smoother experience
|
// iOS optimization: pause while seeking for smoother experience
|
||||||
if (Platform.OS === 'ios') {
|
|
||||||
iosWasPausedDuringSeekRef.current = paused;
|
|
||||||
if (!paused) setPaused(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually perform the seek
|
// Actually perform the seek
|
||||||
playerRef.current.seek(timeInSeconds);
|
playerRef.current.seek(timeInSeconds);
|
||||||
|
|
@ -59,10 +56,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
if (isMounted.current && isSeeking.current) {
|
if (isMounted.current && isSeeking.current) {
|
||||||
isSeeking.current = false;
|
isSeeking.current = false;
|
||||||
// Resume if it was playing (iOS specific)
|
// Resume if it was playing (iOS specific)
|
||||||
if (Platform.OS === 'ios' && iosWasPausedDuringSeekRef.current === false) {
|
|
||||||
setPaused(false);
|
|
||||||
iosWasPausedDuringSeekRef.current = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
/**
|
|
||||||
* Shared Player Setup Hook
|
|
||||||
* Used by both Android (VLC) and iOS (KSPlayer) players
|
|
||||||
* Handles StatusBar, orientation, brightness, and app state
|
|
||||||
*/
|
|
||||||
import { useEffect, useRef, useCallback } from 'react';
|
import { useEffect, useRef, useCallback } from 'react';
|
||||||
import { StatusBar, Dimensions, AppState, InteractionManager, Platform } from 'react-native';
|
import { StatusBar, Dimensions, AppState, InteractionManager, Platform } from 'react-native';
|
||||||
import * as Brightness from 'expo-brightness';
|
import * as Brightness from 'expo-brightness';
|
||||||
import * as ScreenOrientation from 'expo-screen-orientation';
|
import * as ScreenOrientation from 'expo-screen-orientation';
|
||||||
|
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
|
||||||
import { logger } from '../../../utils/logger';
|
import { logger } from '../../../utils/logger';
|
||||||
import { useFocusEffect } from '@react-navigation/native';
|
import { useFocusEffect } from '@react-navigation/native';
|
||||||
|
|
||||||
|
|
@ -15,6 +11,7 @@ interface PlayerSetupConfig {
|
||||||
setVolume: (vol: number) => void;
|
setVolume: (vol: number) => void;
|
||||||
setBrightness: (bri: number) => void;
|
setBrightness: (bri: number) => void;
|
||||||
isOpeningAnimationComplete: boolean;
|
isOpeningAnimationComplete: boolean;
|
||||||
|
paused: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePlayerSetup = (config: PlayerSetupConfig) => {
|
export const usePlayerSetup = (config: PlayerSetupConfig) => {
|
||||||
|
|
@ -22,9 +19,23 @@ export const usePlayerSetup = (config: PlayerSetupConfig) => {
|
||||||
setScreenDimensions,
|
setScreenDimensions,
|
||||||
setVolume,
|
setVolume,
|
||||||
setBrightness,
|
setBrightness,
|
||||||
isOpeningAnimationComplete
|
isOpeningAnimationComplete,
|
||||||
|
paused
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
|
// Prevent screen sleep while playing
|
||||||
|
// Prevent screen sleep while playing
|
||||||
|
useEffect(() => {
|
||||||
|
if (!paused) {
|
||||||
|
activateKeepAwakeAsync();
|
||||||
|
} else {
|
||||||
|
deactivateKeepAwake();
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
deactivateKeepAwake();
|
||||||
|
};
|
||||||
|
}, [paused]);
|
||||||
|
|
||||||
const isAppBackgrounded = useRef(false);
|
const isAppBackgrounded = useRef(false);
|
||||||
|
|
||||||
const enableImmersiveMode = () => {
|
const enableImmersiveMode = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { storageService } from '../../../../services/storageService';
|
import { storageService } from '../../../services/storageService';
|
||||||
import { logger } from '../../../../utils/logger';
|
import { logger } from '../../../utils/logger';
|
||||||
import { useSettings } from '../../../../hooks/useSettings';
|
import { useSettings } from '../../../hooks/useSettings';
|
||||||
|
|
||||||
export const useWatchProgress = (
|
export const useWatchProgress = (
|
||||||
id: string | undefined,
|
id: string | undefined,
|
||||||
Loading…
Reference in a new issue