diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index d9f0977cf..7fde436ad 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -43,7 +43,7 @@ type HlsVideoPlayerProps = { fullscreen: boolean; frigateControls?: boolean; inpointOffset?: number; - onClipEnded?: () => void; + onClipEnded?: (currentTime: number) => void; onPlayerLoaded?: () => void; onTimeUpdate?: (time: number) => void; onPlaying?: () => void; @@ -387,7 +387,11 @@ export default function HlsVideoPlayer({ } } }} - onEnded={onClipEnded} + onEnded={() => { + if (onClipEnded) { + onClipEnded(getVideoTime() ?? 0); + } + }} onError={(e) => { if ( !hlsRef.current && diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index 35e51c9a0..836203ca7 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -14,6 +14,7 @@ import axios from "axios"; import { cn } from "@/lib/utils"; import { useTranslation } from "react-i18next"; import { calculateInpointOffset } from "@/utils/videoUtil"; +import { isFirefox } from "react-device-detect"; /** * Dynamically switches between video playback and scrubbing preview player. @@ -223,6 +224,33 @@ export default function DynamicVideoPlayer({ [recordingParams, recordings], ); + const onValidateClipEnd = useCallback( + (currentTime: number) => { + if (!onClipEnded || !controller || !recordings) { + return; + } + + if (!isFirefox) { + onClipEnded(); + } + + // Firefox has a bug where clipEnded can be called prematurely due to buffering + // we need to validate if the current play-point is truly at the end of available recordings + + const lastRecordingTime = recordings.at(-1)?.start_time; + + if ( + !lastRecordingTime || + controller.getProgress(currentTime) < lastRecordingTime + ) { + return; + } + + onClipEnded(); + }, + [onClipEnded, controller, recordings], + ); + return ( <> { if (isScrubbing) { playerRef.current?.pause();