From 63984c1bc988daba9a17f1201013ee8b00ae43e9 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 12:28:47 -0600 Subject: [PATCH 01/10] If recordings don't exist mark as no recordings --- web/src/components/player/dynamic/DynamicVideoPlayer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index a40d521eac..4e6be66620 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -168,6 +168,7 @@ export default function DynamicVideoPlayer({ useEffect(() => { if (!controller || !recordings) { + setNoRecording(true); return; } From daa6d7672c26acfc3da63a56b44c7f8ae0f8f01c Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 12:47:25 -0600 Subject: [PATCH 02/10] Fix reloading recordings failing --- .../player/dynamic/DynamicVideoPlayer.tsx | 5 +- web/src/pages/Events.tsx | 4 +- web/src/views/events/RecordingView.tsx | 268 ++++++++++-------- 3 files changed, 151 insertions(+), 126 deletions(-) diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index 4e6be66620..aded14ec9e 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -168,7 +168,10 @@ export default function DynamicVideoPlayer({ useEffect(() => { if (!controller || !recordings) { - setNoRecording(true); + if (recordings?.length == 0) { + setNoRecording(true); + } + return; } diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index ce646f5bda..0326fe3dc6 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -101,7 +101,7 @@ export default function Events() { // review paging - const [beforeTs, setBeforeTs] = useState(Date.now() / 1000); + const [beforeTs, setBeforeTs] = useState(Math.ceil(Date.now() / 1000)); const last24Hours = useMemo(() => { return { before: beforeTs, after: getHoursAgo(24) }; }, [beforeTs]); @@ -455,5 +455,5 @@ export default function Events() { function getHoursAgo(hours: number): number { const now = new Date(); now.setHours(now.getHours() - hours); - return now.getTime() / 1000; + return Math.ceil(now.getTime() / 1000); } diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index c1b30b98eb..9737ad31d7 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -46,6 +46,7 @@ import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record"; import { useResizeObserver } from "@/hooks/resize-observer"; import { cn } from "@/lib/utils"; import { useFullscreen } from "@/hooks/use-fullscreen"; +import ActivityIndicator from "@/components/indicators/activity-indicator"; const SEGMENT_DURATION = 30; @@ -107,7 +108,7 @@ export function RecordingView({ return chunk.after <= startTime && chunk.before >= startTime; }), ); - const currentTimeRange = useMemo( + const currentTimeRange = useMemo( () => chunkedTimeRange[selectedRangeIdx], [selectedRangeIdx, chunkedTimeRange], ); @@ -171,6 +172,10 @@ export function RecordingView({ ); useEffect(() => { + if (!currentTimeRange) { + return; + } + if (scrubbing || exportRange) { if ( currentTime > currentTimeRange.before + 60 || @@ -198,6 +203,10 @@ export function RecordingView({ const manuallySetCurrentTime = useCallback( (time: number) => { + if (!currentTimeRange) { + return; + } + setCurrentTime(time); if (currentTimeRange.after <= time && currentTimeRange.before >= time) { @@ -210,6 +219,10 @@ export function RecordingView({ ); useEffect(() => { + if (!currentTimeRange) { + return; + } + if (!scrubbing) { if (Math.abs(currentTime - playerTime) > 10) { if ( @@ -471,143 +484,152 @@ export function RecordingView({ -
+ {currentTimeRange ? (
- { - setPlayerTime(timestamp); - setCurrentTime(timestamp); - Object.values(previewRefs.current ?? {}).forEach((prev) => - prev.scrubToTimestamp(Math.floor(timestamp)), - ); - }} - onClipEnded={onClipEnded} - onControllerReady={(controller) => { - mainControllerRef.current = controller; - }} - isScrubbing={scrubbing || exportMode == "timeline"} - setFullResolution={setFullResolution} - toggleFullscreen={toggleFullscreen} - containerRef={mainLayoutRef} - /> -
- {isDesktop && (
-
- {allCameras.map((cam) => { - if (cam == mainCamera || cam == "birdseye") { - return; - } - - return ( -
- { - previewRefs.current[cam] = controller; - controller.scrubToTimestamp(startTime); - }} - onClick={() => onSelectCamera(cam)} - /> -
- ); - })} -
+ { + setPlayerTime(timestamp); + setCurrentTime(timestamp); + Object.values(previewRefs.current ?? {}).forEach((prev) => + prev.scrubToTimestamp(Math.floor(timestamp)), + ); + }} + onClipEnded={onClipEnded} + onControllerReady={(controller) => { + mainControllerRef.current = controller; + }} + isScrubbing={scrubbing || exportMode == "timeline"} + setFullResolution={setFullResolution} + toggleFullscreen={toggleFullscreen} + containerRef={mainLayoutRef} + />
- )} + {isDesktop && ( +
+
+ {allCameras.map((cam) => { + if (cam == mainCamera || cam == "birdseye") { + return; + } + + return ( +
+ { + previewRefs.current[cam] = controller; + controller.scrubToTimestamp(startTime); + }} + onClick={() => onSelectCamera(cam)} + /> +
+ ); + })} +
+
+ )} +
+
- -
+ ) : ( +
+
+ No recordings or previews found for this time +
+
+ )}
); } From f61a2b9801f92a17c0b94f964ffef77b50bd305d Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:00:00 -0600 Subject: [PATCH 03/10] Fix mark items not clearing selected --- web/src/views/events/EventView.tsx | 4 ++++ web/src/views/events/RecordingView.tsx | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index 8b4569be0f..adfb082066 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -395,6 +395,7 @@ export default function EventView({ markAllItemsAsReviewed={markAllItemsAsReviewed} onSelectReview={onSelectReview} onSelectAllReviews={onSelectAllReviews} + setSelectedReviews={setSelectedReviews} pullLatestData={pullLatestData} /> )} @@ -437,6 +438,7 @@ type DetectionReviewProps = { markAllItemsAsReviewed: (currentItems: ReviewSegment[]) => void; onSelectReview: (review: ReviewSegment, ctrl: boolean) => void; onSelectAllReviews: () => void; + setSelectedReviews: (reviewIds: string[]) => void; pullLatestData: () => void; }; function DetectionReview({ @@ -455,6 +457,7 @@ function DetectionReview({ markAllItemsAsReviewed, onSelectReview, onSelectAllReviews, + setSelectedReviews, pullLatestData, }: DetectionReviewProps) { const reviewTimelineRef = useRef(null); @@ -692,6 +695,7 @@ function DetectionReview({ className="text-white" variant="select" onClick={() => { + setSelectedReviews([]); markAllItemsAsReviewed(currentItems ?? []); }} > diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 9737ad31d7..5cd945b05b 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -46,7 +46,6 @@ import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record"; import { useResizeObserver } from "@/hooks/resize-observer"; import { cn } from "@/lib/utils"; import { useFullscreen } from "@/hooks/use-fullscreen"; -import ActivityIndicator from "@/components/indicators/activity-indicator"; const SEGMENT_DURATION = 30; From 677f9d6a883990690b7f0bf88ee0c43c92e0a89d Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:08:20 -0600 Subject: [PATCH 04/10] Cleanup --- web/src/views/events/RecordingView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 5cd945b05b..3895539e19 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -46,6 +46,7 @@ import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record"; import { useResizeObserver } from "@/hooks/resize-observer"; import { cn } from "@/lib/utils"; import { useFullscreen } from "@/hooks/use-fullscreen"; +import { LuFolderX } from "react-icons/lu"; const SEGMENT_DURATION = 30; @@ -624,7 +625,8 @@ export function RecordingView({
) : (
-
+
+ No recordings or previews found for this time
From a58850076b7166b7025bbf114185dc246233a218 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:18:26 -0600 Subject: [PATCH 05/10] Default to last full hour when error occurs --- web/src/views/events/RecordingView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 3895539e19..fb99ff7941 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -109,7 +109,9 @@ export function RecordingView({ }), ); const currentTimeRange = useMemo( - () => chunkedTimeRange[selectedRangeIdx], + () => + chunkedTimeRange[selectedRangeIdx] ?? + chunkedTimeRange[chunkedTimeRange.length - 1], [selectedRangeIdx, chunkedTimeRange], ); const reviewFilterList = useMemo(() => { From ae691c30b4b576e8bfd5184871dcf90d00595227 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:19:02 -0600 Subject: [PATCH 06/10] Remove check --- web/src/views/events/RecordingView.tsx | 264 ++++++++++++------------- 1 file changed, 123 insertions(+), 141 deletions(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index fb99ff7941..8427701718 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -108,7 +108,7 @@ export function RecordingView({ return chunk.after <= startTime && chunk.before >= startTime; }), ); - const currentTimeRange = useMemo( + const currentTimeRange = useMemo( () => chunkedTimeRange[selectedRangeIdx] ?? chunkedTimeRange[chunkedTimeRange.length - 1], @@ -174,10 +174,6 @@ export function RecordingView({ ); useEffect(() => { - if (!currentTimeRange) { - return; - } - if (scrubbing || exportRange) { if ( currentTime > currentTimeRange.before + 60 || @@ -221,10 +217,6 @@ export function RecordingView({ ); useEffect(() => { - if (!currentTimeRange) { - return; - } - if (!scrubbing) { if (Math.abs(currentTime - playerTime) > 10) { if ( @@ -486,153 +478,143 @@ export function RecordingView({
- {currentTimeRange ? ( +
+ { + setPlayerTime(timestamp); + setCurrentTime(timestamp); + Object.values(previewRefs.current ?? {}).forEach((prev) => + prev.scrubToTimestamp(Math.floor(timestamp)), + ); + }} + onClipEnded={onClipEnded} + onControllerReady={(controller) => { + mainControllerRef.current = controller; + }} + isScrubbing={scrubbing || exportMode == "timeline"} + setFullResolution={setFullResolution} + toggleFullscreen={toggleFullscreen} + containerRef={mainLayoutRef} + /> +
+ {isDesktop && (
- { - setPlayerTime(timestamp); - setCurrentTime(timestamp); - Object.values(previewRefs.current ?? {}).forEach((prev) => - prev.scrubToTimestamp(Math.floor(timestamp)), - ); - }} - onClipEnded={onClipEnded} - onControllerReady={(controller) => { - mainControllerRef.current = controller; - }} - isScrubbing={scrubbing || exportMode == "timeline"} - setFullResolution={setFullResolution} - toggleFullscreen={toggleFullscreen} - containerRef={mainLayoutRef} - /> -
- {isDesktop && ( -
-
- {allCameras.map((cam) => { - if (cam == mainCamera || cam == "birdseye") { - return; - } - - return ( -
+ {allCameras.map((cam) => { + if (cam == mainCamera || cam == "birdseye") { + return; + } + + return ( +
+ { + previewRefs.current[cam] = controller; + controller.scrubToTimestamp(startTime); }} - > - { - previewRefs.current[cam] = controller; - controller.scrubToTimestamp(startTime); - }} - onClick={() => onSelectCamera(cam)} - /> -
- ); - })} -
-
- )} -
-
- -
- ) : ( -
-
- - No recordings or previews found for this time + onClick={() => onSelectCamera(cam)} + /> +
+ ); + })} +
+
+ )}
- )} + +
); } From 475fa4f078b0c7dfa8206522e698a237627af169 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:22:42 -0600 Subject: [PATCH 07/10] Cleanup --- web/src/views/events/RecordingView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 8427701718..db2ac9ccbd 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -46,7 +46,6 @@ import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record"; import { useResizeObserver } from "@/hooks/resize-observer"; import { cn } from "@/lib/utils"; import { useFullscreen } from "@/hooks/use-fullscreen"; -import { LuFolderX } from "react-icons/lu"; const SEGMENT_DURATION = 30; From d7fae79f5262e680076a084eb7f9993ccc7794bf Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 13:31:35 -0600 Subject: [PATCH 08/10] Handle empty recordings list case --- web/src/components/player/dynamic/DynamicVideoPlayer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index aded14ec9e..caa709430f 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -167,7 +167,7 @@ export default function DynamicVideoPlayer({ ); useEffect(() => { - if (!controller || !recordings) { + if (!controller || !recordings?.length) { if (recordings?.length == 0) { setNoRecording(true); } From 5beb6a751dc13743e2996f3b37d2c1dd9cef9f3d Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 14:05:42 -0600 Subject: [PATCH 09/10] Ensure that the start time is within the time range --- web/src/views/events/RecordingView.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index db2ac9ccbd..1de3a690f1 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -84,7 +84,11 @@ export function RecordingView({ const previewRowRef = useRef(null); const previewRefs = useRef<{ [camera: string]: PreviewController }>({}); - const [playbackStart, setPlaybackStart] = useState(startTime); + const [playbackStart, setPlaybackStart] = useState( + startTime >= timeRange.after && startTime <= timeRange.before + ? startTime + : timeRange.before - 60, + ); const mainCameraReviewItems = useMemo( () => reviewItems?.filter((cam) => cam.camera == mainCamera) ?? [], From 22660993d4af662540b913a3e18fd9e237ff676a Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 12 Aug 2024 14:10:48 -0600 Subject: [PATCH 10/10] Catch other reset cases --- web/src/views/events/RecordingView.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index 1de3a690f1..1bc82d9e9d 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -430,7 +430,18 @@ export function RecordingView({ filterList={reviewFilterList} showReviewed setShowReviewed={() => {}} - onUpdateFilter={updateFilter} + onUpdateFilter={(newFilter) => { + // if we are resetting the date to last 24 hours + // then we need to reset the playbackStart time + if ( + filter?.before != undefined && + newFilter?.before == undefined + ) { + setPlaybackStart(Date.now() / 1000 - 360); + } + + updateFilter(newFilter); + }} setMotionOnly={() => {}} /> )}