mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-26 18:48:57 +03:00
fix latched loading spinner after cancelling a timeline selection (#24162)
isLoading and isBuffering only clear on playback progress, and scrubbing holds the player paused, so a source rebuild during a timeline selection left them set with nothing able to clear them. Cancelling made them visible as a spinner over an already-loaded frame, which stayed until the next manual seek. Leaving a scrub now clears them when the source is loaded and the element holds a frame.
This commit is contained in:
committed by
Nicolas Mowen
parent
d183f03fee
commit
9eef369dd0
@@ -158,6 +158,7 @@ export default function DynamicVideoPlayer({
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isBuffering, setIsBuffering] = useState(false);
|
const [isBuffering, setIsBuffering] = useState(false);
|
||||||
const loadingTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
const loadingTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
|
const prevCameraRef = useRef(camera);
|
||||||
|
|
||||||
// Don't set source until recordings load - we need accurate startPosition
|
// Don't set source until recordings load - we need accurate startPosition
|
||||||
// to avoid hls.js clamping to video end when startPosition exceeds duration
|
// to avoid hls.js clamping to video end when startPosition exceeds duration
|
||||||
@@ -166,13 +167,34 @@ export default function DynamicVideoPlayer({
|
|||||||
// start at correct time
|
// start at correct time
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// isLoading/isBuffering only clear on playback progress, which a
|
||||||
|
// paused scrub never reaches. A camera switch is excluded: its old
|
||||||
|
// source still reads as settled while the new one is fetched
|
||||||
|
const scrubExit = prevCameraRef.current === camera;
|
||||||
|
prevCameraRef.current = camera;
|
||||||
|
const settled = () =>
|
||||||
|
sourceLoadedRef.current &&
|
||||||
|
(playerRef.current?.readyState ?? 0) >=
|
||||||
|
HTMLMediaElement.HAVE_CURRENT_DATA;
|
||||||
|
|
||||||
|
if (!isScrubbing && scrubExit && settled()) {
|
||||||
|
setIsLoading(false);
|
||||||
|
setIsBuffering(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isScrubbing) {
|
if (!isScrubbing) {
|
||||||
// never overwrite a pending timer: an orphaned one escapes
|
// never overwrite a pending timer: an orphaned one escapes
|
||||||
// onPlaying's clearTimeout and flashes loading mid-playback
|
// onPlaying's clearTimeout and flashes loading mid-playback
|
||||||
if (loadingTimeoutRef.current) {
|
if (loadingTimeoutRef.current) {
|
||||||
clearTimeout(loadingTimeoutRef.current);
|
clearTimeout(loadingTimeoutRef.current);
|
||||||
}
|
}
|
||||||
loadingTimeoutRef.current = setTimeout(() => setIsLoading(true), 1000);
|
loadingTimeoutRef.current = setTimeout(() => {
|
||||||
|
// re-checked here: readyState drops while a seek loads
|
||||||
|
if (scrubExit && settled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsLoading(true);
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user