fixed heating and rerender of playercontrols

This commit is contained in:
tapframe 2026-01-10 15:24:18 +05:30
parent 0df9493551
commit 75602a8658
5 changed files with 77 additions and 40 deletions

View file

@ -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');

View file

@ -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;

View file

@ -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);
};

View file

@ -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}

View file

@ -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) => {