diff --git a/frigate/events.py b/frigate/events.py index fb9e58225..ee44340f9 100644 --- a/frigate/events.py +++ b/frigate/events.py @@ -3,7 +3,10 @@ import logging import os import queue import threading + + from pathlib import Path +from typing import Any from peewee import fn @@ -58,6 +61,98 @@ class EventProcessor(threading.Thread): self.events_in_process: Dict[str, Event] = {} self.stop_event = stop_event + def handle_object_detection( + self, event_type: str, camera: str, event_data: dict[str, Any] + ) -> None: + """handle tracked object event updates.""" + # if this is the first message, just store it and continue, its not time to insert it in the db + if should_update_db(self.events_in_process[event_data["id"]], event_data): + camera_config = self.config.cameras[camera] + event_config: EventsConfig = camera_config.record.events + width = camera_config.detect.width + height = camera_config.detect.height + first_detector = list(self.config.detectors.values())[0] + + start_time = event_data["start_time"] - event_config.pre_capture + end_time = ( + None + if event_data["end_time"] is None + else event_data["end_time"] + event_config.post_capture + ) + # score of the snapshot + score = ( + None + if event_data["snapshot"] is None + else event_data["snapshot"]["score"] + ) + # detection region in the snapshot + region = ( + None + if event_data["snapshot"] is None + else to_relative_box( + width, + height, + event_data["snapshot"]["region"], + ) + ) + # bounding box for the snapshot + box = ( + None + if event_data["snapshot"] is None + else to_relative_box( + width, + height, + event_data["snapshot"]["box"], + ) + ) + + # keep these from being set back to false because the event + # may have started while recordings and snapshots were enabled + # this would be an issue for long running events + if self.events_in_process[event_data["id"]]["has_clip"]: + event_data["has_clip"] = True + if self.events_in_process[event_data["id"]]["has_snapshot"]: + event_data["has_snapshot"] = True + + event = { + Event.id: event_data["id"], + Event.label: event_data["label"], + Event.camera: camera, + Event.start_time: start_time, + Event.end_time: end_time, + Event.zones: list(event_data["entered_zones"]), + Event.thumbnail: event_data["thumbnail"], + Event.has_clip: event_data["has_clip"], + Event.has_snapshot: event_data["has_snapshot"], + Event.model_hash: first_detector.model.model_hash, + Event.model_type: first_detector.model.model_type, + Event.detector_type: first_detector.type, + Event.data: { + "area": event_data.get("area", 0), + "box": box, + "ratio": event_data.get("ratio", width / height), + "region": region, + "score": score, + "top_score": event_data["top_score"], + }, + } + + ( + Event.insert(event) + .on_conflict( + conflict_target=[Event.id], + update=event, + ) + .execute() + ) + + # update the stored copy for comparison on future update messages + self.events_in_process[event_data["id"]] = event_data + + if event_type == "end": + del self.events_in_process[event_data["id"]] + self.event_processed_queue.put((event_data["id"], camera)) + def run(self) -> None: # set an end_time on events without an end_time on startup Event.update(end_time=Event.start_time + 30).where( @@ -66,7 +161,9 @@ class EventProcessor(threading.Thread): while not self.stop_event.is_set(): try: - event_type, camera, event_data = self.event_queue.get(timeout=1) + source_type, event_type, camera, event_data = self.event_queue.get( + timeout=1 + ) except queue.Empty: continue @@ -75,104 +172,19 @@ class EventProcessor(threading.Thread): self.timeline_queue.put( ( camera, - TimelineSourceEnum.tracked_object, + source_type, event_type, self.events_in_process.get(event_data["id"]), event_data, ) ) - # if this is the first message, just store it and continue, its not time to insert it in the db - if event_type == "start": - self.events_in_process[event_data["id"]] = event_data - continue + if source_type == TimelineSourceEnum.tracked_object: + if event_type == "start": + self.events_in_process[event_data["id"]] = event_data + continue - if should_update_db(self.events_in_process[event_data["id"]], event_data): - camera_config = self.config.cameras[camera] - event_config: EventsConfig = camera_config.record.events - width = camera_config.detect.width - height = camera_config.detect.height - first_detector = list(self.config.detectors.values())[0] - - start_time = event_data["start_time"] - event_config.pre_capture - end_time = ( - None - if event_data["end_time"] is None - else event_data["end_time"] + event_config.post_capture - ) - # score of the snapshot - score = ( - None - if event_data["snapshot"] is None - else event_data["snapshot"]["score"] - ) - # detection region in the snapshot - region = ( - None - if event_data["snapshot"] is None - else to_relative_box( - width, - height, - event_data["snapshot"]["region"], - ) - ) - # bounding box for the snapshot - box = ( - None - if event_data["snapshot"] is None - else to_relative_box( - width, - height, - event_data["snapshot"]["box"], - ) - ) - - # keep these from being set back to false because the event - # may have started while recordings and snapshots were enabled - # this would be an issue for long running events - if self.events_in_process[event_data["id"]]["has_clip"]: - event_data["has_clip"] = True - if self.events_in_process[event_data["id"]]["has_snapshot"]: - event_data["has_snapshot"] = True - - event = { - Event.id: event_data["id"], - Event.label: event_data["label"], - Event.camera: camera, - Event.start_time: start_time, - Event.end_time: end_time, - Event.zones: list(event_data["entered_zones"]), - Event.thumbnail: event_data["thumbnail"], - Event.has_clip: event_data["has_clip"], - Event.has_snapshot: event_data["has_snapshot"], - Event.model_hash: first_detector.model.model_hash, - Event.model_type: first_detector.model.model_type, - Event.detector_type: first_detector.type, - Event.data: { - "area": event_data.get("area", 0), - "box": box, - "ratio": event_data.get("ratio", width / height), - "region": region, - "score": score, - "top_score": event_data["top_score"], - } - } - - ( - Event.insert(event) - .on_conflict( - conflict_target=[Event.id], - update=event, - ) - .execute() - ) - - # update the stored copy for comparison on future update messages - self.events_in_process[event_data["id"]] = event_data - - if event_type == "end": - del self.events_in_process[event_data["id"]] - self.event_processed_queue.put((event_data["id"], camera)) + self.handle_object_detection(event_type, camera, event_data) # set an end_time on events without an end_time before exiting Event.update(end_time=datetime.datetime.now().timestamp()).where(