From 011ad8eda7ebd536b4f932e20484f7c6b11c7365 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:54:09 -0500 Subject: [PATCH] Miscellaneous fixes (#23017) * add ui to camera config update topics enum * add mqtt to camera config update enum * ensure cleanup runs when an event end skips post-processing * end any in-progress audio events when audio detection is disabled we already end in-progress audio events when we disable a camera, but this mirrors that logic for specifically disabling audio detection * Improve GenAI metadata * fix invalid recording segment topic being misrouted to the valid handler * Add confidence default to avoid unnecessary field causing issues --------- Co-authored-by: Nicolas Mowen --- frigate/config/camera/updater.py | 2 ++ frigate/data_processing/post/types.py | 4 ++-- frigate/embeddings/maintainer.py | 6 ++++++ frigate/events/audio.py | 12 ++++++++++++ frigate/genai/__init__.py | 3 ++- frigate/video/ffmpeg.py | 12 ++++++------ 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/frigate/config/camera/updater.py b/frigate/config/camera/updater.py index 1965f3813..a07d51b8e 100644 --- a/frigate/config/camera/updater.py +++ b/frigate/config/camera/updater.py @@ -20,6 +20,7 @@ class CameraConfigUpdateEnum(str, Enum): ffmpeg = "ffmpeg" live = "live" motion = "motion" # includes motion and motion masks + mqtt = "mqtt" notifications = "notifications" objects = "objects" object_genai = "object_genai" @@ -33,6 +34,7 @@ class CameraConfigUpdateEnum(str, Enum): lpr = "lpr" snapshots = "snapshots" timestamp_style = "timestamp_style" + ui = "ui" zones = "zones" diff --git a/frigate/data_processing/post/types.py b/frigate/data_processing/post/types.py index 41887a20e..0344c7616 100644 --- a/frigate/data_processing/post/types.py +++ b/frigate/data_processing/post/types.py @@ -27,7 +27,7 @@ class ReviewMetadata(BaseModel): ) title: str = Field( max_length=80, - description="A short title characterizing what took place and where, under 10 words.", + description="Under 10 words. Name the apparent purpose or outcome of the activity together with the location involved. Do not narrate or list the sequence of actions step by step.", ) scene: str = Field( min_length=150, @@ -36,7 +36,7 @@ class ReviewMetadata(BaseModel): ) shortSummary: str = Field( min_length=70, - max_length=100, + max_length=120, description="A brief 2-sentence summary of the scene, suitable for notifications.", ) confidence: float = Field( diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index 33bef38f2..1a998febe 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -517,10 +517,16 @@ class EmbeddingMaintainer(threading.Thread): try: event: Event = Event.get(Event.id == event_id) except DoesNotExist: + for processor in self.post_processors: + if isinstance(processor, ObjectDescriptionProcessor): + processor.cleanup_event(event_id) continue # Skip the event if not an object if event.data.get("type") != "object": + for processor in self.post_processors: + if isinstance(processor, ObjectDescriptionProcessor): + processor.cleanup_event(event_id) continue # Extract valid thumbnail diff --git a/frigate/events/audio.py b/frigate/events/audio.py index f6c41fa30..6a22b2251 100644 --- a/frigate/events/audio.py +++ b/frigate/events/audio.py @@ -205,6 +205,7 @@ class AudioEventMaintainer(threading.Thread): self.transcription_thread.start() self.was_enabled = camera.enabled + self.was_audio_enabled = camera.audio.enabled def detect_audio(self, audio: np.ndarray) -> None: if not self.camera_config.audio.enabled or self.stop_event.is_set(): @@ -363,6 +364,17 @@ class AudioEventMaintainer(threading.Thread): time.sleep(0.1) continue + audio_enabled = self.camera_config.audio.enabled + if audio_enabled != self.was_audio_enabled: + if not audio_enabled: + self.logger.debug( + f"Disabling audio detections for {self.camera_config.name}, ending events" + ) + self.requestor.send_data( + EXPIRE_AUDIO_ACTIVITY, self.camera_config.name + ) + self.was_audio_enabled = audio_enabled + self.read_audio() if self.audio_listener: diff --git a/frigate/genai/__init__.py b/frigate/genai/__init__.py index 20bf1d6fb..e26b50757 100644 --- a/frigate/genai/__init__.py +++ b/frigate/genai/__init__.py @@ -201,9 +201,10 @@ Each line represents a detection state, not necessarily unique individuals. The except json.JSONDecodeError as je: logger.error("Failed to parse review description JSON: %s", je) return None - # observations is required on the model; fill an empty default + # observations and confidence are required on the model; fill an empty default # if the response omitted it so attribute access stays safe. raw.setdefault("observations", []) + raw.setdefault("confidence", 0.0) metadata = ReviewMetadata.model_construct(**raw) except Exception as e: logger.error( diff --git a/frigate/video/ffmpeg.py b/frigate/video/ffmpeg.py index d30dc3b18..cc5b9a32b 100644 --- a/frigate/video/ffmpeg.py +++ b/frigate/video/ffmpeg.py @@ -317,16 +317,16 @@ class CameraWatchdog(threading.Thread): if camera != self.config.name: continue - if topic.endswith(RecordingsDataTypeEnum.valid.value): - self.logger.debug( - f"Latest valid recording segment time on {camera}: {segment_time}" - ) - self.latest_valid_segment_time = segment_time - elif topic.endswith(RecordingsDataTypeEnum.invalid.value): + if topic.endswith(RecordingsDataTypeEnum.invalid.value): self.logger.warning( f"Invalid recording segment detected for {camera} at {segment_time}" ) self.latest_invalid_segment_time = segment_time + elif topic.endswith(RecordingsDataTypeEnum.valid.value): + self.logger.debug( + f"Latest valid recording segment time on {camera}: {segment_time}" + ) + self.latest_valid_segment_time = segment_time elif topic.endswith(RecordingsDataTypeEnum.latest.value): if segment_time is not None: self.latest_cache_segment_time = segment_time