mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-28 11:53:00 +00:00
seek fix
This commit is contained in:
parent
19438ff1d5
commit
9504d48607
7 changed files with 64 additions and 49 deletions
|
|
@ -145,7 +145,9 @@ class MPVView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekTo(positionSeconds: Double) {
|
fun seekTo(positionSeconds: Double) {
|
||||||
|
Log.d(TAG, "seekTo called: positionSeconds=$positionSeconds, isMpvInitialized=$isMpvInitialized")
|
||||||
if (isMpvInitialized) {
|
if (isMpvInitialized) {
|
||||||
|
Log.d(TAG, "Executing MPV seek command: seek $positionSeconds absolute")
|
||||||
MPVLib.command(arrayOf("seek", positionSeconds.toString(), "absolute"))
|
MPVLib.command(arrayOf("seek", positionSeconds.toString(), "absolute"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,9 +84,12 @@ class MpvPlayerViewManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun receiveCommand(view: MPVView, commandId: String?, args: ReadableArray?) {
|
override fun receiveCommand(view: MPVView, commandId: String?, args: ReadableArray?) {
|
||||||
|
android.util.Log.d("MpvPlayerViewManager", "receiveCommand: $commandId, args: $args")
|
||||||
when (commandId) {
|
when (commandId) {
|
||||||
"seek" -> {
|
"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" -> {
|
"setAudioTrack" -> {
|
||||||
args?.getInt(0)?.let { view.setAudioTrack(it) }
|
args?.getInt(0)?.let { view.setAudioTrack(it) }
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import { ErrorModal } from './modals/ErrorModal';
|
||||||
|
|
||||||
// Android-specific components
|
// Android-specific components
|
||||||
import { VideoSurface } from './android/components/VideoSurface';
|
import { VideoSurface } from './android/components/VideoSurface';
|
||||||
|
import { MpvPlayerRef } from './android/MpvPlayer';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
|
|
@ -75,6 +76,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const useVLC = (Platform.OS === 'android' && forceVlc);
|
const useVLC = (Platform.OS === 'android' && forceVlc);
|
||||||
|
|
||||||
const videoRef = useRef<any>(null);
|
const videoRef = useRef<any>(null);
|
||||||
|
const mpvPlayerRef = useRef<MpvPlayerRef>(null);
|
||||||
const vlcHook = useVlcPlayer(useVLC, playerState.paused, playerState.currentTime);
|
const vlcHook = useVlcPlayer(useVLC, playerState.paused, playerState.currentTime);
|
||||||
const tracksHook = usePlayerTracks(
|
const tracksHook = usePlayerTracks(
|
||||||
useVLC,
|
useVLC,
|
||||||
|
|
@ -103,7 +105,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
const setupHook = usePlayerSetup(playerState.setScreenDimensions, setVolume, setBrightness, playerState.paused);
|
const setupHook = usePlayerSetup(playerState.setScreenDimensions, setVolume, setBrightness, playerState.paused);
|
||||||
|
|
||||||
const controlsHook = usePlayerControls(
|
const controlsHook = usePlayerControls(
|
||||||
videoRef,
|
mpvPlayerRef, // Use mpvPlayerRef for MPV player
|
||||||
vlcHook.vlcPlayerRef,
|
vlcHook.vlcPlayerRef,
|
||||||
useVLC,
|
useVLC,
|
||||||
playerState.paused,
|
playerState.paused,
|
||||||
|
|
@ -168,6 +170,13 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
if (!playerState.isMounted.current) return;
|
if (!playerState.isMounted.current) return;
|
||||||
|
|
||||||
const videoDuration = data.duration;
|
const videoDuration = data.duration;
|
||||||
|
console.log('[AndroidVideoPlayer] handleLoad called:', {
|
||||||
|
duration: videoDuration,
|
||||||
|
initialPosition: watchProgress.initialPosition,
|
||||||
|
showResumeOverlay: watchProgress.showResumeOverlay,
|
||||||
|
initialSeekTarget: watchProgress.initialSeekTargetRef?.current
|
||||||
|
});
|
||||||
|
|
||||||
if (videoDuration > 0) {
|
if (videoDuration > 0) {
|
||||||
playerState.setDuration(videoDuration);
|
playerState.setDuration(videoDuration);
|
||||||
if (id && type) {
|
if (id && type) {
|
||||||
|
|
@ -204,9 +213,17 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
playerState.setIsVideoLoaded(true);
|
playerState.setIsVideoLoaded(true);
|
||||||
openingAnimation.completeOpeningAnimation();
|
openingAnimation.completeOpeningAnimation();
|
||||||
|
|
||||||
// Handle Resume
|
// Handle Resume - check both initialPosition and initialSeekTargetRef
|
||||||
if (watchProgress.initialPosition && !watchProgress.showResumeOverlay) {
|
const resumeTarget = watchProgress.initialPosition || watchProgress.initialSeekTargetRef?.current;
|
||||||
controlsHook.seekToTime(watchProgress.initialPosition);
|
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]);
|
}, [id, type, episodeId, useVLC, playerState.isMounted, watchProgress.initialPosition]);
|
||||||
|
|
||||||
|
|
@ -385,6 +402,7 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
onBuffer={(buf) => playerState.setIsBuffering(buf.isBuffering)}
|
onBuffer={(buf) => playerState.setIsBuffering(buf.isBuffering)}
|
||||||
onTracksUpdate={vlcHook.handleVlcTracksUpdate}
|
onTracksUpdate={vlcHook.handleVlcTracksUpdate}
|
||||||
vlcPlayerRef={vlcHook.vlcPlayerRef}
|
vlcPlayerRef={vlcHook.vlcPlayerRef}
|
||||||
|
mpvPlayerRef={mpvPlayerRef}
|
||||||
videoRef={videoRef}
|
videoRef={videoRef}
|
||||||
pinchRef={useRef(null)}
|
pinchRef={useRef(null)}
|
||||||
onPinchGestureEvent={() => { }}
|
onPinchGestureEvent={() => { }}
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,7 @@ const MpvPlayer = forwardRef<MpvPlayerRef, MpvPlayerProps>((props, ref) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[MpvPlayer] Rendering native component with:', {
|
// Debug logging removed to prevent console spam
|
||||||
source: props.source?.substring(0, 50) + '...',
|
|
||||||
paused: props.paused ?? true,
|
|
||||||
volume: props.volume ?? 1.0,
|
|
||||||
rate: props.rate ?? 1.0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLoad = (event: any) => {
|
const handleLoad = (event: any) => {
|
||||||
console.log('[MpvPlayer] Native onLoad event:', event?.nativeEvent);
|
console.log('[MpvPlayer] Native onLoad event:', event?.nativeEvent);
|
||||||
|
|
@ -72,11 +67,7 @@ const MpvPlayer = forwardRef<MpvPlayerRef, MpvPlayerProps>((props, ref) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgress = (event: any) => {
|
const handleProgress = (event: any) => {
|
||||||
const data = event?.nativeEvent;
|
props.onProgress?.(event?.nativeEvent);
|
||||||
if (data && Math.floor(data.currentTime) % 5 === 0) {
|
|
||||||
console.log('[MpvPlayer] Native onProgress event:', data);
|
|
||||||
}
|
|
||||||
props.onProgress?.(data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEnd = (event: any) => {
|
const handleEnd = (event: any) => {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import React from 'react';
|
import React, { useCallback, memo } from 'react';
|
||||||
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
||||||
import { PinchGestureHandler } from 'react-native-gesture-handler';
|
import { PinchGestureHandler } from 'react-native-gesture-handler';
|
||||||
import MpvPlayer, { MpvPlayerRef } from '../MpvPlayer';
|
import MpvPlayer, { MpvPlayerRef } from '../MpvPlayer';
|
||||||
import { styles } from '../../utils/playerStyles';
|
import { styles } from '../../utils/playerStyles';
|
||||||
import { ResizeModeType } from '../../utils/playerTypes';
|
import { ResizeModeType } from '../../utils/playerTypes';
|
||||||
import { logger } from '../../../../utils/logger';
|
|
||||||
|
|
||||||
interface VideoSurfaceProps {
|
interface VideoSurfaceProps {
|
||||||
processedStreamUrl: string;
|
processedStreamUrl: string;
|
||||||
|
|
@ -77,13 +76,7 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
|
||||||
// Use the actual stream URL
|
// Use the actual stream URL
|
||||||
const streamUrl = currentStreamUrl || processedStreamUrl;
|
const streamUrl = currentStreamUrl || processedStreamUrl;
|
||||||
|
|
||||||
console.log('[VideoSurface] Rendering with:', {
|
// Debug logging removed to prevent console spam
|
||||||
streamUrl: streamUrl?.substring(0, 50) + '...',
|
|
||||||
paused,
|
|
||||||
volume,
|
|
||||||
playbackSpeed,
|
|
||||||
screenDimensions,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLoad = (data: { duration: number; width: number; height: number }) => {
|
const handleLoad = (data: { duration: number; width: number; height: number }) => {
|
||||||
console.log('[VideoSurface] onLoad received:', data);
|
console.log('[VideoSurface] onLoad received:', data);
|
||||||
|
|
@ -97,10 +90,6 @@ export const VideoSurface: React.FC<VideoSurfaceProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProgress = (data: { currentTime: number; duration: number }) => {
|
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({
|
onProgress({
|
||||||
currentTime: data.currentTime,
|
currentTime: data.currentTime,
|
||||||
playableDuration: data.currentTime,
|
playableDuration: data.currentTime,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { useRef, useCallback } from 'react';
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import { logger } from '../../../../utils/logger';
|
import { logger } from '../../../../utils/logger';
|
||||||
|
|
||||||
const DEBUG_MODE = false;
|
const DEBUG_MODE = true; // Temporarily enable for debugging seek
|
||||||
const END_EPSILON = 0.3;
|
const END_EPSILON = 0.3;
|
||||||
|
|
||||||
export const usePlayerControls = (
|
export const usePlayerControls = (
|
||||||
videoRef: any,
|
mpvPlayerRef: any,
|
||||||
vlcPlayerRef: any,
|
vlcPlayerRef: any,
|
||||||
useVLC: boolean,
|
useVLC: boolean,
|
||||||
paused: boolean,
|
paused: boolean,
|
||||||
|
|
@ -26,39 +26,46 @@ export const usePlayerControls = (
|
||||||
const seekToTime = useCallback((rawSeconds: number) => {
|
const seekToTime = useCallback((rawSeconds: number) => {
|
||||||
const timeInSeconds = Math.max(0, Math.min(rawSeconds, duration > 0 ? duration - END_EPSILON : rawSeconds));
|
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 (useVLC) {
|
||||||
if (vlcPlayerRef.current && duration > 0) {
|
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);
|
vlcPlayerRef.current.seek(timeInSeconds);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (videoRef.current && duration > 0 && !isSeeking.current) {
|
// MPV Player
|
||||||
if (DEBUG_MODE) logger.log(`[usePlayerControls] Seeking to ${timeInSeconds}`);
|
if (mpvPlayerRef.current && duration > 0) {
|
||||||
|
console.log(`[usePlayerControls][MPV] Seeking to ${timeInSeconds}`);
|
||||||
|
|
||||||
isSeeking.current = true;
|
isSeeking.current = true;
|
||||||
|
mpvPlayerRef.current.seek(timeInSeconds);
|
||||||
|
|
||||||
if (Platform.OS === 'ios') {
|
// Reset seeking flag after a delay
|
||||||
iosWasPausedDuringSeekRef.current = paused;
|
|
||||||
if (!paused) setPaused(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually perform the seek
|
|
||||||
videoRef.current.seek(timeInSeconds);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isMounted.current && isSeeking.current) {
|
if (isMounted.current) {
|
||||||
isSeeking.current = false;
|
isSeeking.current = false;
|
||||||
if (Platform.OS === 'ios' && iosWasPausedDuringSeekRef.current === false) {
|
|
||||||
setPaused(false);
|
|
||||||
iosWasPausedDuringSeekRef.current = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 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) => {
|
const skip = useCallback((seconds: number) => {
|
||||||
|
console.log('[usePlayerControls] skip called:', { seconds, currentTime, newTime: currentTime + seconds });
|
||||||
seekToTime(currentTime + seconds);
|
seekToTime(currentTime + seconds);
|
||||||
}, [currentTime, seekToTime]);
|
}, [currentTime, seekToTime]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,22 @@ export const useWatchProgress = (
|
||||||
if (id && type) {
|
if (id && type) {
|
||||||
try {
|
try {
|
||||||
const savedProgress = await storageService.getWatchProgress(id, type, episodeId);
|
const savedProgress = await storageService.getWatchProgress(id, type, episodeId);
|
||||||
|
console.log('[useWatchProgress] Loaded saved progress:', savedProgress);
|
||||||
|
|
||||||
if (savedProgress) {
|
if (savedProgress) {
|
||||||
const progressPercent = (savedProgress.currentTime / savedProgress.duration) * 100;
|
const progressPercent = (savedProgress.currentTime / savedProgress.duration) * 100;
|
||||||
|
console.log('[useWatchProgress] Progress percent:', progressPercent);
|
||||||
|
|
||||||
if (progressPercent < 85) {
|
if (progressPercent < 85) {
|
||||||
setResumePosition(savedProgress.currentTime);
|
setResumePosition(savedProgress.currentTime);
|
||||||
setSavedDuration(savedProgress.duration);
|
setSavedDuration(savedProgress.duration);
|
||||||
|
|
||||||
if (appSettings.alwaysResume) {
|
if (appSettings.alwaysResume) {
|
||||||
|
console.log('[useWatchProgress] Always resume enabled, setting initial position:', savedProgress.currentTime);
|
||||||
setInitialPosition(savedProgress.currentTime);
|
setInitialPosition(savedProgress.currentTime);
|
||||||
initialSeekTargetRef.current = 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 {
|
} else {
|
||||||
setShowResumeOverlay(true);
|
setShowResumeOverlay(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue