From d5989c91a8449c067220025ba2f7a8457f9205fd Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 27 Mar 2024 07:22:28 -0600 Subject: [PATCH] Make deleting review items delete recordings as well (#10707) * Make deleting review items delete recordings as well * Fix wrong camera bug --- frigate/api/review.py | 46 +++++++++++++++++-- .../components/filter/ReviewActionGroup.tsx | 3 +- .../player/PreviewThumbnailPlayer.tsx | 4 +- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/frigate/api/review.py b/frigate/api/review.py index 5074d6df2..d3a49de9f 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -3,6 +3,7 @@ import logging from datetime import datetime, timedelta from functools import reduce +from pathlib import Path import pandas as pd from flask import Blueprint, jsonify, make_response, request @@ -62,6 +63,7 @@ def review(): .order_by(ReviewSegment.start_time.desc()) .limit(limit) .dicts() + .iterator() ) return jsonify([r for r in review]) @@ -334,15 +336,53 @@ def set_not_reviewed(id): ) -@ReviewBp.route("/reviews/", methods=("DELETE",)) -def delete_reviews(ids: str): - list_of_ids = ids.split(",") +@ReviewBp.route("/reviews/delete", methods=("POST",)) +def delete_reviews(): + json: dict[str, any] = request.get_json(silent=True) or {} + list_of_ids = json.get("ids", "") if not list_of_ids or len(list_of_ids) == 0: return make_response( jsonify({"success": False, "message": "Not a valid list of ids"}), 404 ) + reviews = ( + ReviewSegment.select( + ReviewSegment.camera, + ReviewSegment.start_time, + ReviewSegment.end_time, + ) + .where(ReviewSegment.id << list_of_ids) + .dicts() + .iterator() + ) + recording_ids = [] + + for review in reviews: + start_time = review["start_time"] + end_time = review["end_time"] + camera_name = review["camera"] + recordings = ( + Recordings.select(Recordings.id, Recordings.path) + .where( + Recordings.start_time.between(start_time, end_time) + | Recordings.end_time.between(start_time, end_time) + | ( + (start_time > Recordings.start_time) + & (end_time < Recordings.end_time) + ) + ) + .where(Recordings.camera == camera_name) + .dicts() + .iterator() + ) + + for recording in recordings: + Path(recording["path"]).unlink(missing_ok=True) + recording_ids.append(recording["id"]) + + # delete recordings and review segments + Recordings.delete().where(Recordings.id << recording_ids).execute() ReviewSegment.delete().where(ReviewSegment.id << list_of_ids).execute() return make_response(jsonify({"success": True, "message": "Delete reviews"}), 200) diff --git a/web/src/components/filter/ReviewActionGroup.tsx b/web/src/components/filter/ReviewActionGroup.tsx index c3df52881..bc1951f75 100644 --- a/web/src/components/filter/ReviewActionGroup.tsx +++ b/web/src/components/filter/ReviewActionGroup.tsx @@ -29,8 +29,7 @@ export default function ReviewActionGroup({ }, [selectedReviews, setSelectedReviews, pullLatestData]); const onDelete = useCallback(async () => { - const idList = selectedReviews.join(","); - await axios.delete(`reviews/${idList}`); + await axios.post(`reviews/delete`, { ids: selectedReviews }); setSelectedReviews([]); pullLatestData(); }, [selectedReviews, setSelectedReviews, pullLatestData]); diff --git a/web/src/components/player/PreviewThumbnailPlayer.tsx b/web/src/components/player/PreviewThumbnailPlayer.tsx index 95b6bd6b2..3f2ed212a 100644 --- a/web/src/components/player/PreviewThumbnailPlayer.tsx +++ b/web/src/components/player/PreviewThumbnailPlayer.tsx @@ -111,7 +111,9 @@ export default function PreviewThumbnailPlayer({ } else { // the second preview is longer, return the second if it exists if (firstIndex < allPreviews.length - 1) { - return allPreviews[firstIndex + 1]; + return allPreviews.find( + (preview, idx) => idx > firstIndex && preview.camera == review.camera, + ); } return undefined;