mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
fixed heating and rerender of playercontrols
This commit is contained in:
parent
0df9493551
commit
75602a8658
5 changed files with 77 additions and 40 deletions
|
|
@ -643,6 +643,46 @@ const KSPlayerCore: React.FC = () => {
|
|||
controls.seekToTime(value);
|
||||
};
|
||||
|
||||
// Progress update throttling:
|
||||
// - Always keep a ref updated (cheap, no re-render)
|
||||
// - Only push to React state at a lower frequency (prevents full-tree re-render storms)
|
||||
const currentTimeRef = useRef<number>(currentTime);
|
||||
const bufferedRef = useRef<number>(buffered);
|
||||
const lastUiProgressUpdateAtRef = useRef<number>(0);
|
||||
useEffect(() => {
|
||||
currentTimeRef.current = currentTime;
|
||||
}, [currentTime]);
|
||||
useEffect(() => {
|
||||
bufferedRef.current = buffered;
|
||||
}, [buffered]);
|
||||
|
||||
const handleProgress = useCallback((d: any) => {
|
||||
const t = d?.currentTime ?? 0;
|
||||
const newBuffered = d?.buffered ?? 0;
|
||||
|
||||
currentTimeRef.current = t;
|
||||
lastPlaybackTimeRef.current = t || 0;
|
||||
|
||||
// Keep buffered updates coarse to avoid churn
|
||||
if (Math.abs(newBuffered - bufferedRef.current) > 1) {
|
||||
bufferedRef.current = newBuffered;
|
||||
setBuffered(newBuffered);
|
||||
}
|
||||
|
||||
// Don't fight the slider while the user is dragging it
|
||||
if (isSliderDragging) return;
|
||||
|
||||
const now = Date.now();
|
||||
const needsTightUi =
|
||||
showControls || paused || customSubs.useCustomSubtitles;
|
||||
const minIntervalMs = needsTightUi ? 250 : 1000;
|
||||
|
||||
if (now - lastUiProgressUpdateAtRef.current < minIntervalMs) return;
|
||||
lastUiProgressUpdateAtRef.current = now;
|
||||
|
||||
setCurrentTime(t);
|
||||
}, [isSliderDragging, paused, showControls, customSubs.useCustomSubtitles, setBuffered, setCurrentTime]);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: '#000000' }}>
|
||||
<StatusBar hidden={true} />
|
||||
|
|
@ -695,17 +735,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
hasAviOSFailed.current = false;
|
||||
}
|
||||
}}
|
||||
onProgress={(d) => {
|
||||
if (!isSliderDragging) {
|
||||
setCurrentTime(d.currentTime);
|
||||
}
|
||||
lastPlaybackTimeRef.current = d.currentTime || 0;
|
||||
// Only update buffered if it changed by more than 0.5s to reduce re-renders
|
||||
const newBuffered = d.buffered || 0;
|
||||
if (Math.abs(newBuffered - buffered) > 0.5) {
|
||||
setBuffered(newBuffered);
|
||||
}
|
||||
}}
|
||||
onProgress={handleProgress}
|
||||
onEnd={async () => {
|
||||
setCurrentTime(duration);
|
||||
await traktAutosync.handlePlaybackEnd(duration, duration, 'ended');
|
||||
|
|
@ -749,16 +779,7 @@ const KSPlayerCore: React.FC = () => {
|
|||
: ({ type: 'disabled' } as SelectedTrack))
|
||||
}
|
||||
onLoad={onLoad}
|
||||
onProgress={(d) => {
|
||||
if (!isSliderDragging) {
|
||||
setCurrentTime(d.currentTime);
|
||||
}
|
||||
lastPlaybackTimeRef.current = d.currentTime || 0;
|
||||
const newBuffered = d.buffered || 0;
|
||||
if (Math.abs(newBuffered - buffered) > 0.5) {
|
||||
setBuffered(newBuffered);
|
||||
}
|
||||
}}
|
||||
onProgress={handleProgress}
|
||||
onEnd={async () => {
|
||||
setCurrentTime(duration);
|
||||
await traktAutosync.handlePlaybackEnd(duration, duration, 'ended');
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ interface PlayerControlsProps {
|
|||
useExoPlayer?: boolean;
|
||||
}
|
||||
|
||||
export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
||||
const PlayerControlsInner: React.FC<PlayerControlsProps> = ({
|
||||
showControls,
|
||||
fadeAnim,
|
||||
paused,
|
||||
|
|
@ -625,4 +625,15 @@ export const PlayerControls: React.FC<PlayerControlsProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
// Key optimization:
|
||||
// When controls are hidden, we don't need to re-render this tree on every progress tick.
|
||||
// This removes a large chunk of unnecessary work (and heat) during playback.
|
||||
export const PlayerControls = React.memo(
|
||||
PlayerControlsInner,
|
||||
(prev, next) => {
|
||||
if (!prev.showControls && !next.showControls) return true;
|
||||
return false; // when visible, allow normal updates
|
||||
}
|
||||
);
|
||||
|
||||
export default PlayerControls;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const useWatchProgress = (
|
|||
const [savedDuration, setSavedDuration] = useState<number | null>(null);
|
||||
const [initialPosition, setInitialPosition] = useState<number | null>(null);
|
||||
const [showResumeOverlay, setShowResumeOverlay] = useState(false);
|
||||
const [progressSaveInterval, setProgressSaveInterval] = useState<NodeJS.Timeout | null>(null);
|
||||
const progressSaveIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const { settings: appSettings } = useSettings();
|
||||
const initialSeekTargetRef = useRef<number | null>(null);
|
||||
|
|
@ -119,7 +119,7 @@ export const useWatchProgress = (
|
|||
};
|
||||
try {
|
||||
await storageService.setWatchProgress(id, type, progress, episodeId);
|
||||
await traktAutosync.handleProgressUpdate(currentTimeRef.current, durationRef.current);
|
||||
await traktAutosyncRef.current.handleProgressUpdate(currentTimeRef.current, durationRef.current);
|
||||
} catch (error) {
|
||||
logger.error('[useWatchProgress] Error saving watch progress:', error);
|
||||
}
|
||||
|
|
@ -128,20 +128,26 @@ export const useWatchProgress = (
|
|||
|
||||
// Save Interval
|
||||
useEffect(() => {
|
||||
if (id && type && !paused && duration > 0) {
|
||||
if (progressSaveInterval) clearInterval(progressSaveInterval);
|
||||
// Important: do NOT depend on currentTime here; we use refs for currentTime/duration.
|
||||
// Otherwise we recreate the interval on every progress tick, causing heavy re-renders.
|
||||
if (progressSaveIntervalRef.current) {
|
||||
clearInterval(progressSaveIntervalRef.current);
|
||||
progressSaveIntervalRef.current = null;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (id && type && !paused && duration > 0) {
|
||||
progressSaveIntervalRef.current = setInterval(() => {
|
||||
saveWatchProgress();
|
||||
}, 10000);
|
||||
|
||||
setProgressSaveInterval(interval);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
setProgressSaveInterval(null);
|
||||
};
|
||||
}
|
||||
}, [id, type, paused, currentTime, duration]);
|
||||
|
||||
return () => {
|
||||
if (progressSaveIntervalRef.current) {
|
||||
clearInterval(progressSaveIntervalRef.current);
|
||||
progressSaveIntervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [id, type, paused, duration, episodeId, addonId]);
|
||||
|
||||
// Unmount Save - deferred to allow navigation to complete first
|
||||
useEffect(() => {
|
||||
|
|
@ -151,7 +157,7 @@ export const useWatchProgress = (
|
|||
setTimeout(() => {
|
||||
if (id && type && durationRef.current > 0) {
|
||||
saveWatchProgress();
|
||||
traktAutosync.handlePlaybackEnd(currentTimeRef.current, durationRef.current, 'unmount');
|
||||
traktAutosyncRef.current.handlePlaybackEnd(currentTimeRef.current, durationRef.current, 'unmount');
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@ export const AVPlayerSurface: React.FC<AVPlayerSurfaceProps> = ({
|
|||
onEnd={onEnd}
|
||||
onError={onError}
|
||||
onBuffer={(b: any) => onBuffer(!!b?.isBuffering)}
|
||||
progressUpdateInterval={250}
|
||||
// 250ms progress updates can cause excessive JS work and device heating.
|
||||
// We throttle UI updates in JS anyway; a slightly higher interval reduces event pressure.
|
||||
progressUpdateInterval={500}
|
||||
// Keep background behavior consistent with the rest of the player logic
|
||||
playInBackground={false}
|
||||
playWhenInactive={true}
|
||||
|
|
|
|||
|
|
@ -97,10 +97,7 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
// Debug: log textTrack prop changes
|
||||
React.useEffect(() => {
|
||||
console.log('[KSPlayerSurface] textTrack prop changed to:', textTrack);
|
||||
}, [textTrack]);
|
||||
// Note: avoid debug logging here; this component receives frequent updates during playback.
|
||||
|
||||
// Handle buffering - MPVPlayerComponent exposes buffering only via events we wire up
|
||||
const handleBuffering = (data: any) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue