Fix: Player orientation now correctly resets when exiting video player

This commit is contained in:
tapframe 2025-12-25 12:35:07 +05:30
parent 0a1e008d5f
commit a61c1e6456
9 changed files with 77 additions and 39 deletions

13
package-lock.json generated
View file

@ -51,7 +51,7 @@
"expo-glass-effect": "~0.1.4",
"expo-haptics": "~15.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-localization": "~17.0.7",
"expo-navigation-bar": "~5.0.10",
@ -6512,17 +6512,6 @@
"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": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz",

View file

@ -51,6 +51,7 @@
"expo-glass-effect": "~0.1.4",
"expo-haptics": "~15.0.7",
"expo-intent-launcher": "~13.0.7",
"expo-keep-awake": "~15.0.8",
"expo-linear-gradient": "~15.0.7",
"expo-localization": "~17.0.7",
"expo-navigation-bar": "~5.0.10",
@ -103,4 +104,4 @@
"xcode": "^3.0.1"
},
"private": true
}
}

View file

@ -10,13 +10,14 @@ import {
usePlayerState,
usePlayerModals,
useSpeedControl,
useOpeningAnimation
useOpeningAnimation,
useWatchProgress
} from './hooks';
// Android-specific hooks
import { usePlayerSetup } from './android/hooks/usePlayerSetup';
import { usePlayerTracks } from './android/hooks/usePlayerTracks';
import { useWatchProgress } from './android/hooks/useWatchProgress';
import { usePlayerControls } from './android/hooks/usePlayerControls';
import { useNextEpisode } from './android/hooks/useNextEpisode';

View file

@ -16,6 +16,7 @@ import EpisodesModal from './modals/EpisodesModal';
import { EpisodeStreamsModal } from './modals/EpisodeStreamsModal';
import { ErrorModal } from './modals/ErrorModal';
import CustomSubtitles from './subtitles/CustomSubtitles';
import ResumeOverlay from './modals/ResumeOverlay';
import { SpeedActivatedOverlay, PauseOverlay, GestureControls } from './components';
// Platform-specific components
@ -30,7 +31,8 @@ import {
usePlayerTracks,
useCustomSubtitles,
usePlayerControls,
usePlayerSetup
usePlayerSetup,
useWatchProgress
} from './hooks';
// Platform-specific hooks
@ -137,6 +139,15 @@ const KSPlayerCore: React.FC = () => {
isMounted
});
const watchProgress = useWatchProgress(
id, type, episodeId,
currentTime,
duration,
paused,
traktAutosync,
controls.seekToTime
);
// Gestures
const fadeAnim = useRef(new Animated.Value(1)).current;
@ -157,9 +168,6 @@ const KSPlayerCore: React.FC = () => {
const [brightness, setBrightnessState] = useState(0.5);
const [isSliderDragging, setIsSliderDragging] = useState(false);
// Watch Progress State
const [initialPosition, setInitialPosition] = useState<number | null>(routeInitialPosition || null);
// Shared Gesture Hook
const gestureControls = usePlayerGestureControls({
volume: volume,
@ -173,7 +181,8 @@ const KSPlayerCore: React.FC = () => {
setScreenDimensions,
setVolume: setVolumeState,
setBrightness: setBrightnessState,
isOpeningAnimationComplete: openingAnim.isOpeningAnimationComplete
isOpeningAnimationComplete: openingAnim.isOpeningAnimationComplete,
paused: paused
});
// Refs for Logic
@ -328,9 +337,10 @@ const KSPlayerCore: React.FC = () => {
openingAnim.completeOpeningAnimation();
// Initial Seek
if (initialPosition && initialPosition > 0) {
const resumeTarget = routeInitialPosition || watchProgress.initialPosition || watchProgress.initialSeekTargetRef?.current;
if (resumeTarget && resumeTarget > 0 && !watchProgress.showResumeOverlay && data.duration > 0) {
setTimeout(() => {
controls.seekToTime(initialPosition);
controls.seekToTime(resumeTarget);
}, 500);
}
@ -602,6 +612,23 @@ const KSPlayerCore: React.FC = () => {
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 */}
<PauseOverlay
visible={paused && !showControls}

View file

@ -3,6 +3,7 @@ import { StatusBar, Platform, Dimensions, AppState } from 'react-native';
import RNImmersiveMode from 'react-native-immersive-mode';
import * as NavigationBar from 'expo-navigation-bar';
import * as Brightness from 'expo-brightness';
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
import { logger } from '../../../../utils/logger';
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
@ -19,6 +20,19 @@ export const usePlayerSetup = (
const originalSystemBrightnessModeRef = useRef<number | null>(null);
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 () => {
if (Platform.OS === 'android') {
// Standard immersive mode

View file

@ -19,3 +19,4 @@ export { usePlayerSetup } from './usePlayerSetup';
// Content
export { useNextEpisode } from './useNextEpisode';
export { useWatchProgress } from './useWatchProgress';

View file

@ -46,10 +46,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
isSeeking.current = true;
// iOS optimization: pause while seeking for smoother experience
if (Platform.OS === 'ios') {
iosWasPausedDuringSeekRef.current = paused;
if (!paused) setPaused(true);
}
// Actually perform the seek
playerRef.current.seek(timeInSeconds);
@ -59,10 +56,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
if (isMounted.current && isSeeking.current) {
isSeeking.current = false;
// Resume if it was playing (iOS specific)
if (Platform.OS === 'ios' && iosWasPausedDuringSeekRef.current === false) {
setPaused(false);
iosWasPausedDuringSeekRef.current = null;
}
}
}, 500);
}

View file

@ -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 { StatusBar, Dimensions, AppState, InteractionManager, Platform } from 'react-native';
import * as Brightness from 'expo-brightness';
import * as ScreenOrientation from 'expo-screen-orientation';
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
import { logger } from '../../../utils/logger';
import { useFocusEffect } from '@react-navigation/native';
@ -15,6 +11,7 @@ interface PlayerSetupConfig {
setVolume: (vol: number) => void;
setBrightness: (bri: number) => void;
isOpeningAnimationComplete: boolean;
paused: boolean;
}
export const usePlayerSetup = (config: PlayerSetupConfig) => {
@ -22,9 +19,23 @@ export const usePlayerSetup = (config: PlayerSetupConfig) => {
setScreenDimensions,
setVolume,
setBrightness,
isOpeningAnimationComplete
isOpeningAnimationComplete,
paused
} = 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 enableImmersiveMode = () => {

View file

@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { storageService } from '../../../../services/storageService';
import { logger } from '../../../../utils/logger';
import { useSettings } from '../../../../hooks/useSettings';
import { storageService } from '../../../services/storageService';
import { logger } from '../../../utils/logger';
import { useSettings } from '../../../hooks/useSettings';
export const useWatchProgress = (
id: string | undefined,