From 8a143b428426c61104c63c4a58eae2e1d5b3aa95 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 19 May 2025 15:43:22 -0500 Subject: [PATCH] Fixes (#18304) * fix recordings check * Only calculate inpoint offset for beginning of hour segment * Cleanup * Fix seeking * add Czech * explore i18n fix --------- Co-authored-by: Nicolas Mowen --- .cspell/frigate-dictionary.txt | 1 + web/public/locales/en/views/explore.json | 1 + web/src/components/player/HlsVideoPlayer.tsx | 2 +- .../player/dynamic/DynamicVideoController.ts | 6 ++++- .../player/dynamic/DynamicVideoPlayer.tsx | 17 ++++-------- web/src/lib/const.ts | 5 ++-- web/src/utils/videoUtil.ts | 26 +++++++++++++++++++ web/src/views/explore/ExploreView.tsx | 13 ++-------- 8 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 web/src/utils/videoUtil.ts diff --git a/.cspell/frigate-dictionary.txt b/.cspell/frigate-dictionary.txt index e9405c9f7..6e66a4704 100644 --- a/.cspell/frigate-dictionary.txt +++ b/.cspell/frigate-dictionary.txt @@ -109,6 +109,7 @@ imdecode imencode imread imwrite +inpoint interp iostat iotop diff --git a/web/public/locales/en/views/explore.json b/web/public/locales/en/views/explore.json index 13cc494ea..4837835e5 100644 --- a/web/public/locales/en/views/explore.json +++ b/web/public/locales/en/views/explore.json @@ -1,6 +1,7 @@ { "documentTitle": "Explore - Frigate", "generativeAI": "Generative AI", + "exploreMore": "Explore more {{label}} objects", "exploreIsUnavailable": { "title": "Explore is Unavailable", "embeddingsReindexing": { diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 775159dc1..7a147939f 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -230,7 +230,7 @@ export default function HlsVideoPlayer({ hotKeys={hotKeys} onPlayPause={onPlayPause} onSeek={(diff) => { - const currentTime = getVideoTime(); + const currentTime = videoRef.current?.currentTime; if (!videoRef.current || !currentTime) { return; diff --git a/web/src/components/player/dynamic/DynamicVideoController.ts b/web/src/components/player/dynamic/DynamicVideoController.ts index a820c8aae..0683481c6 100644 --- a/web/src/components/player/dynamic/DynamicVideoController.ts +++ b/web/src/components/player/dynamic/DynamicVideoController.ts @@ -2,6 +2,7 @@ import { Recording } from "@/types/record"; import { DynamicPlayback } from "@/types/playback"; import { PreviewController } from "../PreviewPlayer"; import { TimeRange, ObjectLifecycleSequence } from "@/types/timeline"; +import { calculateInpointOffset } from "@/utils/videoUtil"; type PlayerMode = "playback" | "scrubbing"; @@ -42,7 +43,10 @@ export class DynamicVideoController { newPlayback(newPlayback: DynamicPlayback) { this.recordings = newPlayback.recordings; this.timeRange = newPlayback.timeRange; - this.inpointOffset = this.timeRange.after - this.recordings[0].start_time; + this.inpointOffset = calculateInpointOffset( + this.timeRange.after, + this.recordings[0], + ); if (this.timeToStart) { this.seekToTimestamp(this.timeToStart); diff --git a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx index be7221906..450dd1a17 100644 --- a/web/src/components/player/dynamic/DynamicVideoPlayer.tsx +++ b/web/src/components/player/dynamic/DynamicVideoPlayer.tsx @@ -13,6 +13,7 @@ import { VideoResolutionType } from "@/types/live"; import axios from "axios"; import { cn } from "@/lib/utils"; import { useTranslation } from "react-i18next"; +import { calculateInpointOffset } from "@/utils/videoUtil"; /** * Dynamically switches between video playback and scrubbing preview player. @@ -197,18 +198,10 @@ export default function DynamicVideoPlayer({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [controller, recordings]); - /** the HLS endpoint returns the vod segments with the first - * segment of the hour trimmed, meaning it will start at - * the beginning of the hour, cutting off any difference - * that the segment has. - */ - const inpointOffset = useMemo(() => { - if (!recordingParams || !recordings) { - return 0; - } - - return recordingParams.after - recordings[0].start_time; - }, [recordingParams, recordings]); + const inpointOffset = useMemo( + () => calculateInpointOffset(recordingParams.after, (recordings || [])[0]), + [recordingParams, recordings], + ); return ( <> diff --git a/web/src/lib/const.ts b/web/src/lib/const.ts index b4d94d28a..0afd0979d 100644 --- a/web/src/lib/const.ts +++ b/web/src/lib/const.ts @@ -6,10 +6,11 @@ export const supportedLanguageKeys = [ "it", "nl", "nb-NO", - "tr", - "pl", "zh-CN", "yue-Hant", "ru", + "tr", + "pl", "uk", + "cs", ]; diff --git a/web/src/utils/videoUtil.ts b/web/src/utils/videoUtil.ts new file mode 100644 index 000000000..9f22e19d7 --- /dev/null +++ b/web/src/utils/videoUtil.ts @@ -0,0 +1,26 @@ +import { Recording } from "@/types/record"; + +/** the HLS endpoint returns the vod segments with the first + * segment of the hour trimmed, meaning it will start at + * the beginning of the hour, cutting off any difference + * that the segment has. + */ +export function calculateInpointOffset( + timeRangeStart: number | undefined, + firstRecordingSegment: Recording | undefined, +): number { + if (!timeRangeStart || !firstRecordingSegment) { + return 0; + } + + // if the first recording segment does not cross over + // the beginning of the time range then there is no offset + if ( + firstRecordingSegment.start_time < timeRangeStart && + firstRecordingSegment.end_time > timeRangeStart + ) { + return timeRangeStart - firstRecordingSegment.start_time; + } + + return 0; +} diff --git a/web/src/views/explore/ExploreView.tsx b/web/src/views/explore/ExploreView.tsx index 712edcd8a..afe5001af 100644 --- a/web/src/views/explore/ExploreView.tsx +++ b/web/src/views/explore/ExploreView.tsx @@ -190,8 +190,8 @@ function ThumbnailRow({ /> - - + + {t("exploreMore", { label: objectType })} @@ -283,12 +283,3 @@ function ExploreThumbnailImage({ ); } - -function ExploreMoreLink({ objectType }: { objectType: string }) { - const formattedType = objectType.replaceAll("_", " "); - const label = formattedType.endsWith("s") - ? `${formattedType}es` - : `${formattedType}s`; - - return
Explore More {label}
; -}