From 9504d48607e0e771f9844a45c9fa5828bdd2b0ae Mon Sep 17 00:00:00 2001 From: tapframe Date: Mon, 22 Dec 2025 21:40:11 +0530 Subject: [PATCH] seek fix --- .../main/java/com/nuvio/app/mpv/MPVView.kt | 2 + .../com/nuvio/app/mpv/MpvPlayerViewManager.kt | 5 ++- src/components/player/AndroidVideoPlayer.tsx | 26 +++++++++-- src/components/player/android/MpvPlayer.tsx | 13 +----- .../android/components/VideoSurface.tsx | 15 +------ .../player/android/hooks/usePlayerControls.ts | 45 +++++++++++-------- .../player/android/hooks/useWatchProgress.ts | 7 ++- 7 files changed, 64 insertions(+), 49 deletions(-) diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt index e97fb31..88b8a96 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt @@ -145,7 +145,9 @@ class MPVView @JvmOverloads constructor( } fun seekTo(positionSeconds: Double) { + Log.d(TAG, "seekTo called: positionSeconds=$positionSeconds, isMpvInitialized=$isMpvInitialized") if (isMpvInitialized) { + Log.d(TAG, "Executing MPV seek command: seek $positionSeconds absolute") MPVLib.command(arrayOf("seek", positionSeconds.toString(), "absolute")) } } diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt index 822e529..45b055e 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt @@ -84,9 +84,12 @@ class MpvPlayerViewManager( } override fun receiveCommand(view: MPVView, commandId: String?, args: ReadableArray?) { + android.util.Log.d("MpvPlayerViewManager", "receiveCommand: $commandId, args: $args") when (commandId) { "seek" -> { - args?.getDouble(0)?.let { view.seekTo(it) } + val position = args?.getDouble(0) + android.util.Log.d("MpvPlayerViewManager", "Seek command received: position=$position") + position?.let { view.seekTo(it) } } "setAudioTrack" -> { args?.getInt(0)?.let { view.setAudioTrack(it) } diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 72a19b2..55c93b1 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -40,6 +40,7 @@ import { ErrorModal } from './modals/ErrorModal'; // Android-specific components import { VideoSurface } from './android/components/VideoSurface'; +import { MpvPlayerRef } from './android/MpvPlayer'; // Utils import { logger } from '../../utils/logger'; @@ -75,6 +76,7 @@ const AndroidVideoPlayer: React.FC = () => { const useVLC = (Platform.OS === 'android' && forceVlc); const videoRef = useRef(null); + const mpvPlayerRef = useRef(null); const vlcHook = useVlcPlayer(useVLC, playerState.paused, playerState.currentTime); const tracksHook = usePlayerTracks( useVLC, @@ -103,7 +105,7 @@ const AndroidVideoPlayer: React.FC = () => { const setupHook = usePlayerSetup(playerState.setScreenDimensions, setVolume, setBrightness, playerState.paused); const controlsHook = usePlayerControls( - videoRef, + mpvPlayerRef, // Use mpvPlayerRef for MPV player vlcHook.vlcPlayerRef, useVLC, playerState.paused, @@ -168,6 +170,13 @@ const AndroidVideoPlayer: React.FC = () => { if (!playerState.isMounted.current) return; const videoDuration = data.duration; + console.log('[AndroidVideoPlayer] handleLoad called:', { + duration: videoDuration, + initialPosition: watchProgress.initialPosition, + showResumeOverlay: watchProgress.showResumeOverlay, + initialSeekTarget: watchProgress.initialSeekTargetRef?.current + }); + if (videoDuration > 0) { playerState.setDuration(videoDuration); if (id && type) { @@ -204,9 +213,17 @@ const AndroidVideoPlayer: React.FC = () => { playerState.setIsVideoLoaded(true); openingAnimation.completeOpeningAnimation(); - // Handle Resume - if (watchProgress.initialPosition && !watchProgress.showResumeOverlay) { - controlsHook.seekToTime(watchProgress.initialPosition); + // Handle Resume - check both initialPosition and initialSeekTargetRef + const resumeTarget = watchProgress.initialPosition || watchProgress.initialSeekTargetRef?.current; + if (resumeTarget && resumeTarget > 0 && !watchProgress.showResumeOverlay && videoDuration > 0) { + console.log('[AndroidVideoPlayer] Seeking to resume position:', resumeTarget, 'duration:', videoDuration); + // Use a small delay to ensure the player is ready, then seek directly + setTimeout(() => { + if (mpvPlayerRef.current) { + console.log('[AndroidVideoPlayer] Calling mpvPlayerRef.current.seek directly'); + mpvPlayerRef.current.seek(Math.min(resumeTarget, videoDuration - 0.5)); + } + }, 200); } }, [id, type, episodeId, useVLC, playerState.isMounted, watchProgress.initialPosition]); @@ -385,6 +402,7 @@ const AndroidVideoPlayer: React.FC = () => { onBuffer={(buf) => playerState.setIsBuffering(buf.isBuffering)} onTracksUpdate={vlcHook.handleVlcTracksUpdate} vlcPlayerRef={vlcHook.vlcPlayerRef} + mpvPlayerRef={mpvPlayerRef} videoRef={videoRef} pinchRef={useRef(null)} onPinchGestureEvent={() => { }} diff --git a/src/components/player/android/MpvPlayer.tsx b/src/components/player/android/MpvPlayer.tsx index df1868f..5b9dd1e 100644 --- a/src/components/player/android/MpvPlayer.tsx +++ b/src/components/player/android/MpvPlayer.tsx @@ -59,12 +59,7 @@ const MpvPlayer = forwardRef((props, ref) => { ); } - console.log('[MpvPlayer] Rendering native component with:', { - source: props.source?.substring(0, 50) + '...', - paused: props.paused ?? true, - volume: props.volume ?? 1.0, - rate: props.rate ?? 1.0, - }); + // Debug logging removed to prevent console spam const handleLoad = (event: any) => { console.log('[MpvPlayer] Native onLoad event:', event?.nativeEvent); @@ -72,11 +67,7 @@ const MpvPlayer = forwardRef((props, ref) => { }; const handleProgress = (event: any) => { - const data = event?.nativeEvent; - if (data && Math.floor(data.currentTime) % 5 === 0) { - console.log('[MpvPlayer] Native onProgress event:', data); - } - props.onProgress?.(data); + props.onProgress?.(event?.nativeEvent); }; const handleEnd = (event: any) => { diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 8b98dcb..9a02f73 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -1,10 +1,9 @@ -import React from 'react'; +import React, { useCallback, memo } from 'react'; import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native'; import { PinchGestureHandler } from 'react-native-gesture-handler'; import MpvPlayer, { MpvPlayerRef } from '../MpvPlayer'; import { styles } from '../../utils/playerStyles'; import { ResizeModeType } from '../../utils/playerTypes'; -import { logger } from '../../../../utils/logger'; interface VideoSurfaceProps { processedStreamUrl: string; @@ -77,13 +76,7 @@ export const VideoSurface: React.FC = ({ // Use the actual stream URL const streamUrl = currentStreamUrl || processedStreamUrl; - console.log('[VideoSurface] Rendering with:', { - streamUrl: streamUrl?.substring(0, 50) + '...', - paused, - volume, - playbackSpeed, - screenDimensions, - }); + // Debug logging removed to prevent console spam const handleLoad = (data: { duration: number; width: number; height: number }) => { console.log('[VideoSurface] onLoad received:', data); @@ -97,10 +90,6 @@ export const VideoSurface: React.FC = ({ }; const handleProgress = (data: { currentTime: number; duration: number }) => { - // Log every 5 seconds to avoid spam - if (Math.floor(data.currentTime) % 5 === 0) { - console.log('[VideoSurface] onProgress:', data); - } onProgress({ currentTime: data.currentTime, playableDuration: data.currentTime, diff --git a/src/components/player/android/hooks/usePlayerControls.ts b/src/components/player/android/hooks/usePlayerControls.ts index 6befc3c..83bc421 100644 --- a/src/components/player/android/hooks/usePlayerControls.ts +++ b/src/components/player/android/hooks/usePlayerControls.ts @@ -2,11 +2,11 @@ import { useRef, useCallback } from 'react'; import { Platform } from 'react-native'; import { logger } from '../../../../utils/logger'; -const DEBUG_MODE = false; +const DEBUG_MODE = true; // Temporarily enable for debugging seek const END_EPSILON = 0.3; export const usePlayerControls = ( - videoRef: any, + mpvPlayerRef: any, vlcPlayerRef: any, useVLC: boolean, paused: boolean, @@ -26,39 +26,46 @@ export const usePlayerControls = ( const seekToTime = useCallback((rawSeconds: number) => { const timeInSeconds = Math.max(0, Math.min(rawSeconds, duration > 0 ? duration - END_EPSILON : rawSeconds)); + console.log('[usePlayerControls] seekToTime called:', { + rawSeconds, + timeInSeconds, + useVLC, + hasMpvRef: !!mpvPlayerRef?.current, + hasVlcRef: !!vlcPlayerRef?.current, + duration, + isSeeking: isSeeking.current + }); + if (useVLC) { if (vlcPlayerRef.current && duration > 0) { - if (DEBUG_MODE) logger.log(`[usePlayerControls][VLC] Seeking to ${timeInSeconds}`); + logger.log(`[usePlayerControls][VLC] Seeking to ${timeInSeconds}`); vlcPlayerRef.current.seek(timeInSeconds); } } else { - if (videoRef.current && duration > 0 && !isSeeking.current) { - if (DEBUG_MODE) logger.log(`[usePlayerControls] Seeking to ${timeInSeconds}`); + // MPV Player + if (mpvPlayerRef.current && duration > 0) { + console.log(`[usePlayerControls][MPV] Seeking to ${timeInSeconds}`); isSeeking.current = true; + mpvPlayerRef.current.seek(timeInSeconds); - if (Platform.OS === 'ios') { - iosWasPausedDuringSeekRef.current = paused; - if (!paused) setPaused(true); - } - - // Actually perform the seek - videoRef.current.seek(timeInSeconds); - + // Reset seeking flag after a delay setTimeout(() => { - if (isMounted.current && isSeeking.current) { + if (isMounted.current) { isSeeking.current = false; - if (Platform.OS === 'ios' && iosWasPausedDuringSeekRef.current === false) { - setPaused(false); - iosWasPausedDuringSeekRef.current = null; - } } }, 500); + } else { + console.log('[usePlayerControls][MPV] Cannot seek - ref or duration invalid:', { + hasRef: !!mpvPlayerRef?.current, + duration + }); } } - }, [useVLC, duration, paused, setPaused, videoRef, vlcPlayerRef, isSeeking, isMounted]); + }, [useVLC, duration, paused, setPaused, mpvPlayerRef, vlcPlayerRef, isSeeking, isMounted]); const skip = useCallback((seconds: number) => { + console.log('[usePlayerControls] skip called:', { seconds, currentTime, newTime: currentTime + seconds }); seekToTime(currentTime + seconds); }, [currentTime, seekToTime]); diff --git a/src/components/player/android/hooks/useWatchProgress.ts b/src/components/player/android/hooks/useWatchProgress.ts index fa61406..8f38d98 100644 --- a/src/components/player/android/hooks/useWatchProgress.ts +++ b/src/components/player/android/hooks/useWatchProgress.ts @@ -40,17 +40,22 @@ export const useWatchProgress = ( if (id && type) { try { const savedProgress = await storageService.getWatchProgress(id, type, episodeId); + console.log('[useWatchProgress] Loaded saved progress:', savedProgress); + if (savedProgress) { const progressPercent = (savedProgress.currentTime / savedProgress.duration) * 100; + console.log('[useWatchProgress] Progress percent:', progressPercent); if (progressPercent < 85) { setResumePosition(savedProgress.currentTime); setSavedDuration(savedProgress.duration); if (appSettings.alwaysResume) { + console.log('[useWatchProgress] Always resume enabled, setting initial position:', savedProgress.currentTime); setInitialPosition(savedProgress.currentTime); initialSeekTargetRef.current = savedProgress.currentTime; - seekToTime(savedProgress.currentTime); + // Don't call seekToTime here - duration is 0 + // The seek will be handled in handleLoad callback } else { setShowResumeOverlay(true); }