diff --git a/frigate/api/classification.py b/frigate/api/classification.py index e5b9d8ed1..0a2763a93 100644 --- a/frigate/api/classification.py +++ b/frigate/api/classification.py @@ -243,3 +243,45 @@ def get_lpr_debug(request: Request): lpr_images[image] = image return lpr_images + + +@router.post("/lpr/debug/delete") +def delete_lpr_debug(request: Request, body: dict = None): + """Delete LPR debug images.""" + if not request.app.frigate_config.lpr.enabled: + return JSONResponse( + status_code=400, + content={"message": "LPR is not enabled.", "success": False}, + ) + + json: dict[str, any] = body or {} + list_of_ids = json.get("ids", []) + + if not list_of_ids: + return JSONResponse( + content={"success": False, "message": "Not a valid list of ids"}, + status_code=404, + ) + + lpr_dir = os.path.join(CLIPS_DIR, "lpr") + if not os.path.exists(lpr_dir): + return JSONResponse( + status_code=404, + content={"message": "LPR debug directory not found", "success": False}, + ) + + try: + for image_id in list_of_ids: + image_path = os.path.join(lpr_dir, sanitize_filename(image_id)) + if os.path.exists(image_path): + os.remove(image_path) + + return JSONResponse( + content={"success": True, "message": "Successfully deleted LPR debug images."}, + status_code=200, + ) + except Exception as e: + return JSONResponse( + content={"success": False, "message": f"Failed to delete LPR debug images: {str(e)}"}, + status_code=500, + ) diff --git a/web/src/pages/LPRDebug.tsx b/web/src/pages/LPRDebug.tsx index 36bf6c663..5411c79e3 100644 --- a/web/src/pages/LPRDebug.tsx +++ b/web/src/pages/LPRDebug.tsx @@ -3,11 +3,15 @@ import ActivityIndicator from "@/components/indicators/activity-indicator"; import LPRDetailDialog from "@/components/overlay/dialog/LPRDetailDialog"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Toaster } from "@/components/ui/sonner"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { FrigateConfig } from "@/types/frigateConfig"; import { Event } from "@/types/event"; -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import useSWR from "swr"; import { useFormattedTimestamp } from "@/hooks/use-date-utils"; +import { LuTrash2 } from "react-icons/lu"; +import axios from "axios"; +import { toast } from "sonner"; export default function LPRDebug() { const { data: config } = useSWR("config"); @@ -41,7 +45,7 @@ export default function LPRDebug() {
{lprAttempts.map((attempt: string) => ( - + ))}
@@ -51,9 +55,10 @@ export default function LPRDebug() { type LPRAttemptProps = { attempt: string; config: FrigateConfig; + onRefresh: () => void; }; -function LPRAttempt({ attempt, config }: LPRAttemptProps) { +function LPRAttempt({ attempt, config, onRefresh }: LPRAttemptProps) { const [showDialog, setShowDialog] = useState(false); const data = useMemo(() => { const parts = attempt.split("-"); @@ -74,6 +79,30 @@ function LPRAttempt({ attempt, config }: LPRAttemptProps) { config?.ui.timezone, ); + const onDelete = useCallback(() => { + axios + .post(`/lpr/debug/delete`, { ids: [attempt] }) + .then((resp) => { + if (resp.status == 200) { + toast.success(`Successfully deleted LPR debug image.`, { + position: "top-center", + }); + onRefresh(); + } + }) + .catch((error) => { + if (error.response?.data?.message) { + toast.error(`Failed to delete: ${error.response.data.message}`, { + position: "top-center", + }); + } else { + toast.error(`Failed to delete: ${error.message}`, { + position: "top-center", + }); + } + }); + }, [attempt, onRefresh]); + return ( <> -
setShowDialog(true)} - > -
+
+
setShowDialog(true)} + >
-
+
{data.plate}
{Number.parseFloat(data.score) * 100}%
+ {event && ( +
+ {timestamp} +
+ )} +
+
+ + + + + Delete Image +
- {event && ( -
- {timestamp} -
- )}
diff --git a/web/src/types/frigateConfig.ts b/web/src/types/frigateConfig.ts index 4b293de29..026dca45b 100644 --- a/web/src/types/frigateConfig.ts +++ b/web/src/types/frigateConfig.ts @@ -469,4 +469,9 @@ export interface FrigateConfig { }; ui: UiConfig; + + lpr: { + enabled: boolean; + threshold: number; + }; }