From 1a1ec8cf91c4cb8848b10e55c1a0a5c4e9725c46 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 13 Oct 2025 17:18:04 -0600 Subject: [PATCH] Refresh recordings when data is stale (#20470) * Refresh recordings when data is stale * Fix * Improve checks * Increase time to 10 minutes --- .../player/dynamic/DynamicVideoController.ts | 4 ++ web/src/pages/Events.tsx | 1 + web/src/views/recording/RecordingView.tsx | 38 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/web/src/components/player/dynamic/DynamicVideoController.ts b/web/src/components/player/dynamic/DynamicVideoController.ts index 0683481c6..d4d7d4a2b 100644 --- a/web/src/components/player/dynamic/DynamicVideoController.ts +++ b/web/src/components/player/dynamic/DynamicVideoController.ts @@ -62,6 +62,10 @@ export class DynamicVideoController { this.playerController.pause(); } + isPlaying(): boolean { + return !this.playerController.paused && !this.playerController.ended; + } + seekToTimestamp(time: number, play: boolean = false) { if (time < this.timeRange.after || time > this.timeRange.before) { this.timeToStart = time; diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 656d5c7c6..f4bc75a5a 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -484,6 +484,7 @@ export default function Events() { timeRange={selectedTimeRange} filter={reviewFilter} updateFilter={onUpdateFilter} + refreshData={reloadData} /> ); } diff --git a/web/src/views/recording/RecordingView.tsx b/web/src/views/recording/RecordingView.tsx index 522dac370..776adb4f2 100644 --- a/web/src/views/recording/RecordingView.tsx +++ b/web/src/views/recording/RecordingView.tsx @@ -68,6 +68,8 @@ import { CameraNameLabel } from "@/components/camera/CameraNameLabel"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; import { GenAISummaryDialog } from "@/components/overlay/chip/GenAISummaryChip"; +const DATA_REFRESH_TIME = 600000; // 10 minutes + type RecordingViewProps = { startCamera: string; startTime: number; @@ -78,6 +80,7 @@ type RecordingViewProps = { allPreviews?: Preview[]; filter?: ReviewFilter; updateFilter: (newFilter: ReviewFilter) => void; + refreshData?: () => void; }; export function RecordingView({ startCamera, @@ -89,6 +92,7 @@ export function RecordingView({ allPreviews, filter, updateFilter, + refreshData, }: RecordingViewProps) { const { t } = useTranslation(["views/events"]); const { data: config } = useSWR("config"); @@ -192,6 +196,40 @@ export function RecordingView({ } }, [selectedRangeIdx, chunkedTimeRange]); + // visibility tracking for refreshing stale data + + const lastVisibilityTime = useRef(Date.now()); + + useEffect(() => { + const handleVisibilityChange = () => { + if (document.visibilityState === "visible") { + const now = Date.now(); + const timeSinceLastVisible = now - lastVisibilityTime.current; + + // Only refresh if user was away for a while + // and the video is not currently playing + if ( + timeSinceLastVisible >= DATA_REFRESH_TIME && + refreshData && + mainControllerRef.current && + !mainControllerRef.current.isPlaying() + ) { + refreshData(); + } + + lastVisibilityTime.current = now; + } else { + lastVisibilityTime.current = Date.now(); + } + }; + + document.addEventListener("visibilitychange", handleVisibilityChange); + + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, [refreshData]); + // scrubbing and timeline state const [scrubbing, setScrubbing] = useState(false);