mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-03-11 17:45:38 +00:00
feat: Implement profilespecific watch progress syncing with upsert logic and trigger progress saving on pause and seek completion.
This commit is contained in:
parent
81d528e0f6
commit
32e93ea25d
4 changed files with 36 additions and 21 deletions
|
|
@ -768,7 +768,17 @@ const AndroidVideoPlayer: React.FC = () => {
|
||||||
onProgress={handleProgress}
|
onProgress={handleProgress}
|
||||||
onSeek={(data) => {
|
onSeek={(data) => {
|
||||||
playerState.isSeeking.current = false;
|
playerState.isSeeking.current = false;
|
||||||
if (data.currentTime) traktAutosync.handleProgressUpdate(data.currentTime, playerState.duration, true);
|
if (data.currentTime) {
|
||||||
|
if (id && type && playerState.duration > 0) {
|
||||||
|
void storageService.setWatchProgress(id, type, {
|
||||||
|
currentTime: data.currentTime,
|
||||||
|
duration: playerState.duration,
|
||||||
|
lastUpdated: Date.now(),
|
||||||
|
addonId: currentStreamProvider
|
||||||
|
}, episodeId);
|
||||||
|
}
|
||||||
|
traktAutosync.handleProgressUpdate(data.currentTime, playerState.duration, true);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onEnd={() => {
|
onEnd={() => {
|
||||||
if (modals.showEpisodeStreamsModal) return;
|
if (modals.showEpisodeStreamsModal) return;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import { useTraktAutosync } from '../../hooks/useTraktAutosync';
|
||||||
import { useMetadata } from '../../hooks/useMetadata';
|
import { useMetadata } from '../../hooks/useMetadata';
|
||||||
import { usePlayerGestureControls } from '../../hooks/usePlayerGestureControls';
|
import { usePlayerGestureControls } from '../../hooks/usePlayerGestureControls';
|
||||||
import stremioService from '../../services/stremioService';
|
import stremioService from '../../services/stremioService';
|
||||||
|
import { storageService } from '../../services/storageService';
|
||||||
import { logger } from '../../utils/logger';
|
import { logger } from '../../utils/logger';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
@ -227,7 +228,15 @@ const KSPlayerCore: React.FC = () => {
|
||||||
currentTime,
|
currentTime,
|
||||||
duration,
|
duration,
|
||||||
isSeeking,
|
isSeeking,
|
||||||
isMounted
|
isMounted,
|
||||||
|
onSeekComplete: (timeInSeconds) => {
|
||||||
|
if (!id || !type || duration <= 0) return;
|
||||||
|
void storageService.setWatchProgress(id, type, {
|
||||||
|
currentTime: timeInSeconds,
|
||||||
|
duration,
|
||||||
|
lastUpdated: Date.now()
|
||||||
|
}, episodeId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const watchProgress = useWatchProgress(
|
const watchProgress = useWatchProgress(
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ interface PlayerControlsConfig {
|
||||||
duration: number;
|
duration: number;
|
||||||
isSeeking: MutableRefObject<boolean>;
|
isSeeking: MutableRefObject<boolean>;
|
||||||
isMounted: MutableRefObject<boolean>;
|
isMounted: MutableRefObject<boolean>;
|
||||||
|
onSeekComplete?: (timeInSeconds: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePlayerControls = (config: PlayerControlsConfig) => {
|
export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
|
|
@ -27,7 +28,8 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
currentTime,
|
currentTime,
|
||||||
duration,
|
duration,
|
||||||
isSeeking,
|
isSeeking,
|
||||||
isMounted
|
isMounted,
|
||||||
|
onSeekComplete
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
// iOS seeking helpers
|
// iOS seeking helpers
|
||||||
|
|
@ -54,6 +56,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
|
|
||||||
// Actually perform the seek
|
// Actually perform the seek
|
||||||
playerRef.current.seek(timeInSeconds);
|
playerRef.current.seek(timeInSeconds);
|
||||||
|
onSeekComplete?.(timeInSeconds);
|
||||||
|
|
||||||
// Debounce the seeking state reset
|
// Debounce the seeking state reset
|
||||||
seekTimeoutRef.current = setTimeout(() => {
|
seekTimeoutRef.current = setTimeout(() => {
|
||||||
|
|
@ -62,7 +65,7 @@ export const usePlayerControls = (config: PlayerControlsConfig) => {
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
}, [duration, paused, playerRef, isSeeking, isMounted]);
|
}, [duration, paused, playerRef, isSeeking, isMounted, onSeekComplete]);
|
||||||
|
|
||||||
const skip = useCallback((seconds: number) => {
|
const skip = useCallback((seconds: number) => {
|
||||||
seekToTime(currentTime + seconds);
|
seekToTime(currentTime + seconds);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { AppState, AppStateStatus } from 'react-native';
|
import { AppState } from 'react-native';
|
||||||
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';
|
||||||
|
|
@ -19,10 +19,9 @@ export const useWatchProgress = (
|
||||||
const [savedDuration, setSavedDuration] = useState<number | null>(null);
|
const [savedDuration, setSavedDuration] = useState<number | null>(null);
|
||||||
const [initialPosition, setInitialPosition] = useState<number | null>(null);
|
const [initialPosition, setInitialPosition] = useState<number | null>(null);
|
||||||
const [showResumeOverlay, setShowResumeOverlay] = useState(false);
|
const [showResumeOverlay, setShowResumeOverlay] = useState(false);
|
||||||
const [progressSaveInterval, setProgressSaveInterval] = useState<NodeJS.Timeout | null>(null);
|
|
||||||
|
|
||||||
const { settings: appSettings } = useSettings();
|
const { settings: appSettings } = useSettings();
|
||||||
const initialSeekTargetRef = useRef<number | null>(null);
|
const initialSeekTargetRef = useRef<number | null>(null);
|
||||||
|
const wasPausedRef = useRef<boolean>(paused);
|
||||||
|
|
||||||
// Values refs for unmount cleanup
|
// Values refs for unmount cleanup
|
||||||
const currentTimeRef = useRef(currentTime);
|
const currentTimeRef = useRef(currentTime);
|
||||||
|
|
@ -126,22 +125,16 @@ export const useWatchProgress = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save Interval
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && type && !paused && duration > 0) {
|
if (wasPausedRef.current !== paused) {
|
||||||
if (progressSaveInterval) clearInterval(progressSaveInterval);
|
const becamePaused = paused;
|
||||||
|
wasPausedRef.current = paused;
|
||||||
const interval = setInterval(() => {
|
if (becamePaused) {
|
||||||
saveWatchProgress();
|
void saveWatchProgress();
|
||||||
}, 10000);
|
}
|
||||||
|
|
||||||
setProgressSaveInterval(interval);
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
setProgressSaveInterval(null);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}, [id, type, paused, currentTime, duration]);
|
}, [paused]);
|
||||||
|
|
||||||
// Unmount Save - deferred to allow navigation to complete first
|
// Unmount Save - deferred to allow navigation to complete first
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue