From befc00d44af56e6a1799eb6e08b5eb4a953a7d1b Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 10 Apr 2024 09:36:49 -0600 Subject: [PATCH] Update review maintainer to save events when ongoing --- frigate/api/review.py | 14 ++++++++++++-- frigate/review/maintainer.py | 33 ++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/frigate/api/review.py b/frigate/api/review.py index 03d9dad91..39beafc6b 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -27,10 +27,18 @@ def review(): before = request.args.get("before", type=float, default=datetime.now().timestamp()) after = request.args.get( - "after", type=float, default=(datetime.now() - timedelta(hours=18)).timestamp() + "after", type=float, default=(datetime.now() - timedelta(hours=24)).timestamp() ) - clauses = [((ReviewSegment.start_time > after) & (ReviewSegment.end_time < before))] + clauses = [ + ( + (ReviewSegment.start_time > after) + & ( + (ReviewSegment.end_time.is_null(True)) + | (ReviewSegment.end_time < before) + ) + ) + ] if cameras != "all": camera_list = cameras.split(",") @@ -45,6 +53,7 @@ def review(): for label in filtered_labels: label_clauses.append( (ReviewSegment.data["objects"].cast("text") % f'*"{label}"*') + | ((ReviewSegment.data["audio"].cast("text") % f'*"{label}"*')) ) label_clause = reduce(operator.or_, label_clauses) @@ -94,6 +103,7 @@ def review_summary(): for label in filtered_labels: label_clauses.append( (ReviewSegment.data["objects"].cast("text") % f'*"{label}"*') + | ((ReviewSegment.data["audio"].cast("text") % f'*"{label}"*')) ) label_clause = reduce(operator.or_, label_clauses) diff --git a/frigate/review/maintainer.py b/frigate/review/maintainer.py index 5cbd3a476..06fba8fdd 100644 --- a/frigate/review/maintainer.py +++ b/frigate/review/maintainer.py @@ -66,6 +66,7 @@ class PendingReviewSegment: # thumbnail self.frame = np.zeros((THUMB_HEIGHT * 3 // 2, THUMB_WIDTH), np.uint8) self.frame_active_count = 0 + self.frame_path = os.path.join(CLIPS_DIR, f"thumb-{self.camera}-{self.id}.jpg") def update_frame( self, camera_config: CameraConfig, frame, objects: list[TrackedObject] @@ -98,19 +99,19 @@ class PendingReviewSegment: color_frame, dsize=(width, THUMB_HEIGHT), interpolation=cv2.INTER_AREA ) - def end(self) -> dict: - path = os.path.join(CLIPS_DIR, f"thumb-{self.camera}-{self.id}.jpg") - if self.frame is not None: - cv2.imwrite(path, self.frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60]) + cv2.imwrite( + self.frame_path, self.frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60] + ) + def get_data(self, ended: bool) -> dict: return { ReviewSegment.id: self.id, ReviewSegment.camera: self.camera, ReviewSegment.start_time: self.start_time, - ReviewSegment.end_time: self.last_update, + ReviewSegment.end_time: self.last_update if ended else None, ReviewSegment.severity: self.severity.value, - ReviewSegment.thumb_path: path, + ReviewSegment.thumb_path: self.frame_path, ReviewSegment.data: { "detections": list(set(self.detections.keys())), "objects": list(set(self.detections.values())), @@ -141,9 +142,20 @@ class ReviewSegmentMaintainer(threading.Thread): self.stop_event = stop_event + def update_segment(self, segment: PendingReviewSegment) -> None: + """Update segment.""" + seg_data = segment.get_data(ended=False) + self.requestor.send_data(UPSERT_REVIEW_SEGMENT, seg_data) + self.requestor.send_data( + "reviews", + json.dumps( + {"type": "update", "review": {k.name: v for k, v in seg_data.items()}} + ), + ) + def end_segment(self, segment: PendingReviewSegment) -> None: """End segment.""" - seg_data = segment.end() + seg_data = segment.get_data(ended=True) self.requestor.send_data(UPSERT_REVIEW_SEGMENT, seg_data) self.requestor.send_data( "reviews", @@ -179,6 +191,7 @@ class ReviewSegmentMaintainer(threading.Thread): ) segment.update_frame(camera_config, yuv_frame, active_objects) self.frame_manager.close(frame_id) + self.update_segment(segment) for object in active_objects: if not object["sub_label"]: @@ -263,6 +276,7 @@ class ReviewSegmentMaintainer(threading.Thread): camera_config, yuv_frame, active_objects ) self.frame_manager.close(frame_id) + self.update_segment(self.active_review_segments[camera]) elif len(motion) >= 20: self.active_review_segments[camera] = PendingReviewSegment( camera, @@ -398,6 +412,11 @@ class ReviewSegmentMaintainer(threading.Thread): "end_time" ] + self.config_subscriber.stop() + self.requestor.stop() + self.detection_subscriber.stop() + logger.info("Exiting review maintainer...") + def get_active_objects( frame_time: float, camera_config: CameraConfig, all_objects: list[TrackedObject]