From 8a9ebe92921d5a06a3b5228a526c6961c29acd03 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Fri, 20 Jun 2025 16:39:47 -0600 Subject: [PATCH] Fixes (#18795) * Catch error when regex is invalid * Fix i18n label * Mobile camera drawer i18n fixes * additional frame cache debug logs * Add Romanian * Fix exports thumbnail path * Improve clip buffer and remove outdated comments --------- Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> --- frigate/api/media.py | 2 +- frigate/camera/state.py | 12 ++++++++ .../common/license_plate/mixin.py | 30 +++++++++++-------- frigate/record/export.py | 2 +- .../overlay/detail/SearchDetailDialog.tsx | 15 ++++++---- web/src/lib/const.ts | 1 + web/src/views/live/LiveCameraView.tsx | 6 ++-- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/frigate/api/media.py b/frigate/api/media.py index a82d7f617..9cb00ae0b 100644 --- a/frigate/api/media.py +++ b/frigate/api/media.py @@ -598,7 +598,7 @@ def recording_clip( if clip.start_time < start_ts: file.write(f"inpoint {int(start_ts - clip.start_time)}\n") - # if this is the ending clip and end trim is enabled, add an outpoint + # if this is the ending clip, add an outpoint if clip.end_time > end_ts: file.write(f"outpoint {int(end_ts - clip.start_time)}\n") diff --git a/frigate/camera/state.py b/frigate/camera/state.py index a3fc55718..4338dbcb7 100644 --- a/frigate/camera/state.py +++ b/frigate/camera/state.py @@ -350,6 +350,7 @@ class CameraState: removed_obj = tracked_objects[id] if "end_time" not in removed_obj.obj_data: removed_obj.obj_data["end_time"] = frame_time + logger.debug(f"{self.name}: end callback for object {id}") for c in self.callbacks["end"]: c(self.name, removed_obj, frame_name) @@ -441,6 +442,17 @@ class CameraState: for t in self.frame_cache.keys() if t not in current_thumb_frames and t not in current_best_frames ] + if len(thumb_frames_to_delete) > 0: + logger.debug(f"{self.name}: Current frame cache contents:") + for k, v in self.frame_cache.items(): + logger.debug(f" frame time: {k}, object id: {v['object_id']}") + for obj_id, obj in tracked_objects.items(): + thumb_time = ( + obj.thumbnail_data["frame_time"] if obj.thumbnail_data else None + ) + logger.debug( + f"{self.name}: Tracked object {obj_id} thumbnail frame_time: {thumb_time}" + ) for t in thumb_frames_to_delete: object_id = self.frame_cache[t].get("object_id", "unknown") logger.debug(f"{self.name}: Deleting {t} from frame cache for {object_id}") diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py index ae070688a..2c68ce374 100644 --- a/frigate/data_processing/common/license_plate/mixin.py +++ b/frigate/data_processing/common/license_plate/mixin.py @@ -1500,18 +1500,24 @@ class LicensePlateProcessingMixin: # Determine subLabel based on known plates, use regex matching # Default to the detected plate, use label name if there's a match - sub_label = next( - ( - label - for label, plates in self.lpr_config.known_plates.items() - if any( - re.match(f"^{plate}$", top_plate) - or distance(plate, top_plate) <= self.lpr_config.match_distance - for plate in plates - ) - ), - None, - ) + try: + sub_label = next( + ( + label + for label, plates in self.lpr_config.known_plates.items() + if any( + re.match(f"^{plate}$", top_plate) + or distance(plate, top_plate) <= self.lpr_config.match_distance + for plate in plates + ) + ), + None, + ) + except re.error: + logger.error( + f"{camera}: Invalid regex in known plates configuration: {self.lpr_config.known_plates}" + ) + sub_label = None # If it's a known plate, publish to sub_label if sub_label is not None: diff --git a/frigate/record/export.py b/frigate/record/export.py index ffd774c39..0d3f96da0 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -126,7 +126,7 @@ class RecordingExporter(threading.Thread): minutes = int(diff / 60) seconds = int(diff % 60) ffmpeg_cmd = [ - "7.0", + "/usr/lib/ffmpeg/7.0/bin/ffmpeg", # hardcode path for exports thumbnail due to missing libwebp support "-hide_banner", "-loglevel", "warning", diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 521ddde66..db2d82a6a 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -48,7 +48,7 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; -import { ReviewSegment } from "@/types/review"; +import { REVIEW_PADDING, ReviewSegment } from "@/types/review"; import { useNavigate } from "react-router-dom"; import Chip from "@/components/indicators/Chip"; import { capitalizeAll } from "@/utils/stringUtil"; @@ -1229,11 +1229,14 @@ export function VideoTab({ search }: VideoTabProps) { const { data: reviewItem } = useSWR([ `review/event/${search.id}`, ]); - const endTime = useMemo(() => search.end_time ?? Date.now() / 1000, [search]); - // subtract 2 seconds from start_time to account for keyframes and any differences in the record/detect streams - // to help the start of the event from not being completely cut off - const source = `${baseUrl}vod/${search.camera}/start/${search.start_time - 2}/end/${endTime}/index.m3u8`; + const clipTimeRange = useMemo(() => { + const startTime = search.start_time - REVIEW_PADDING; + const endTime = (search.end_time ?? Date.now() / 1000) + REVIEW_PADDING; + return `start/${startTime}/end/${endTime}`; + }, [search]); + + const source = `${baseUrl}vod/${search.camera}/${clipTimeRange}/index.m3u8`; return ( <> @@ -1272,7 +1275,7 @@ export function VideoTab({ search }: VideoTabProps) { diff --git a/web/src/lib/const.ts b/web/src/lib/const.ts index 36103f77c..4bb41941e 100644 --- a/web/src/lib/const.ts +++ b/web/src/lib/const.ts @@ -6,6 +6,7 @@ export const supportedLanguageKeys = [ "de", "it", "ca", + "ro", "nl", "nb-NO", "zh-CN", diff --git a/web/src/views/live/LiveCameraView.tsx b/web/src/views/live/LiveCameraView.tsx index b0a95718a..b972e1a39 100644 --- a/web/src/views/live/LiveCameraView.tsx +++ b/web/src/views/live/LiveCameraView.tsx @@ -1578,7 +1578,7 @@ function FrigateCameraFeatures({
{!isRestreamed && (
- +
@@ -1596,7 +1596,7 @@ function FrigateCameraFeatures({
- {t("streaming.restreaming.desc", { + {t("streaming.restreaming.desc.title", { ns: "components/dialog", })}
@@ -1606,7 +1606,7 @@ function FrigateCameraFeatures({ rel="noopener noreferrer" className="inline" > - {t("streaming.restreaming.readTheDocumentation", { + {t("streaming.restreaming.desc.readTheDocumentation", { ns: "components/dialog", })}