diff --git a/frigate/api/event.py b/frigate/api/event.py index 0ecb9ddbd..525fb9515 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -251,6 +251,26 @@ def events(): return jsonify(list(events)) +@EventBp.route("/event_ids") +def event_ids(): + idString = request.args.get("ids") + ids = idString.split(",") + + if not ids: + return make_response( + jsonify({"success": False, "message": "Valid list of ids must be sent"}), + 400, + ) + + try: + events = Event.select().where(Event.id << ids).dicts().iterator() + return jsonify(list(events)) + except Exception: + return make_response( + jsonify({"success": False, "message": "Events not found"}), 400 + ) + + @EventBp.route("/events/search") def events_search(): query = request.args.get("query", type=str) diff --git a/web/src/components/overlay/detail/ReviewDetailDialog.tsx b/web/src/components/overlay/detail/ReviewDetailDialog.tsx index db7c372b0..1dedd328e 100644 --- a/web/src/components/overlay/detail/ReviewDetailDialog.tsx +++ b/web/src/components/overlay/detail/ReviewDetailDialog.tsx @@ -7,6 +7,8 @@ import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { getIconForLabel } from "@/utils/iconUtil"; import { useApiHost } from "@/api"; import { ReviewSegment } from "@/types/review"; +import { Event } from "@/types/event"; +import { useMemo } from "react"; type ReviewDetailDialogProps = { review?: ReviewSegment; @@ -24,6 +26,18 @@ export default function ReviewDetailDialog({ // data + const { data: events } = useSWR( + review ? ["event_ids", { ids: review.data.detections.join(",") }] : null, + ); + + const hasMismatch = useMemo(() => { + if (!review || !events) { + return false; + } + + return events.length != review?.data.detections.length; + }, [review, events]); + const formattedDate = useFormattedTimestamp( review?.start_time ?? 0, config?.ui.time_format == "24hour" @@ -54,34 +68,6 @@ export default function ReviewDetailDialog({
-
-
Labels
-
- {[ - ...new Set([ - ...(review.data.objects || []), - ...(review.data.sub_labels || []), - ...(review.data.audio || []), - ]), - ] - .filter( - (item) => - item !== undefined && !item.includes("-verified"), - ) - .sort() - .map((obj) => { - return ( -
- {getIconForLabel(obj, "size-3 text-white")} - {obj} -
- ); - })} -
-
Camera
@@ -94,26 +80,70 @@ export default function ReviewDetailDialog({
- {review.data.detections.map((eventId) => { - return ( - - ); - })} +
+
Objects
+
+ {events?.map((event) => { + return ( +
+ {getIconForLabel(event.label, "size-3 text-white")} + {event.sub_label ?? event.label} ( + {Math.round(event.data.top_score * 100)}%) +
+ ); + })} +
+
+
+
Zones
+
+ {review.data.zones.map((zone) => { + return ( +
+ {zone.replaceAll("_", " ")} +
+ ); + })} +
+
+ {hasMismatch && ( +
+ Some objects that were detected are not included in this list + because the object does not have a snapshot +
+ )} +
+ {events?.map((event) => { + return ( + + ); + })} +
)} diff --git a/web/vite.config.ts b/web/vite.config.ts index 98a9afde1..cb1a580bf 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -4,7 +4,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import monacoEditorPlugin from "vite-plugin-monaco-editor"; -const proxyHost = process.env.PROXY_HOST || "192.168.50.106:5002"; +const proxyHost = process.env.PROXY_HOST || "localhost:5000"; // https://vitejs.dev/config/ export default defineConfig({