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({
/>