mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-26 19:06:11 +01:00
Implement deleting
This commit is contained in:
parent
c3b8110a42
commit
b7877ff3f9
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from pathvalidate import sanitize_filename
|
||||||
|
|
||||||
from fastapi import APIRouter, Request, UploadFile
|
from fastapi import APIRouter, Request, UploadFile
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@ -46,8 +47,8 @@ async def register_face(request: Request, name: str, file: UploadFile):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/faces")
|
@router.post("/faces/{name}/delete")
|
||||||
def deregister_faces(request: Request, body: dict = None):
|
def deregister_faces(request: Request, name: str, body: dict = None):
|
||||||
json: dict[str, any] = body or {}
|
json: dict[str, any] = body or {}
|
||||||
list_of_ids = json.get("ids", "")
|
list_of_ids = json.get("ids", "")
|
||||||
|
|
||||||
@ -58,7 +59,9 @@ def deregister_faces(request: Request, body: dict = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
context: EmbeddingsContext = request.app.embeddings
|
context: EmbeddingsContext = request.app.embeddings
|
||||||
context.delete_face_ids(list_of_ids)
|
context.delete_face_ids(
|
||||||
|
name, map(lambda file: sanitize_filename(file), list_of_ids)
|
||||||
|
)
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=({"success": True, "message": "Successfully deleted faces."}),
|
content=({"success": True, "message": "Successfully deleted faces."}),
|
||||||
status_code=200,
|
status_code=200,
|
||||||
|
@ -14,7 +14,7 @@ from setproctitle import setproctitle
|
|||||||
|
|
||||||
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum, EmbeddingsRequestor
|
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum, EmbeddingsRequestor
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.const import CONFIG_DIR
|
from frigate.const import CONFIG_DIR, FACE_DIR
|
||||||
from frigate.db.sqlitevecq import SqliteVecQueueDatabase
|
from frigate.db.sqlitevecq import SqliteVecQueueDatabase
|
||||||
from frigate.models import Event
|
from frigate.models import Event
|
||||||
from frigate.util.builtin import serialize
|
from frigate.util.builtin import serialize
|
||||||
@ -209,8 +209,13 @@ class EmbeddingsContext:
|
|||||||
|
|
||||||
return self.db.execute_sql(sql_query).fetchall()
|
return self.db.execute_sql(sql_query).fetchall()
|
||||||
|
|
||||||
def delete_face_ids(self, ids: list[str]) -> None:
|
def delete_face_ids(self, face: str, ids: list[str]) -> None:
|
||||||
self.db.delete_embeddings_face(ids)
|
folder = os.path.join(FACE_DIR, face)
|
||||||
|
for id in ids:
|
||||||
|
file_path = os.path.join(folder, id)
|
||||||
|
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.unlink(file_path)
|
||||||
|
|
||||||
def update_description(self, event_id: str, description: str) -> None:
|
def update_description(self, event_id: str, description: str) -> None:
|
||||||
self.requestor.send_data(
|
self.requestor.send_data(
|
||||||
|
@ -3,13 +3,16 @@ import Chip from "@/components/indicators/Chip";
|
|||||||
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
|
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||||
import useOptimisticState from "@/hooks/use-optimistic-state";
|
import useOptimisticState from "@/hooks/use-optimistic-state";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import axios from "axios";
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { isDesktop } from "react-device-detect";
|
import { isDesktop } from "react-device-detect";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { LuTrash } from "react-icons/lu";
|
import { LuTrash } from "react-icons/lu";
|
||||||
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@ -60,6 +63,8 @@ export default function FaceLibrary() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col p-2">
|
<div className="flex size-full flex-col p-2">
|
||||||
<div className="relative flex h-11 w-full items-center justify-between">
|
<div className="relative flex h-11 w-full items-center justify-between">
|
||||||
|
<Toaster />
|
||||||
|
|
||||||
<ScrollArea className="w-full whitespace-nowrap">
|
<ScrollArea className="w-full whitespace-nowrap">
|
||||||
<div ref={tabsRef} className="flex flex-row">
|
<div ref={tabsRef} className="flex flex-row">
|
||||||
<ToggleGroup
|
<ToggleGroup
|
||||||
@ -108,7 +113,7 @@ export default function FaceLibrary() {
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
{pageToggle && (
|
{pageToggle && (
|
||||||
<div className="flex gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{faceImages.map((image: string) => (
|
{faceImages.map((image: string) => (
|
||||||
<FaceImage name={pageToggle} image={image} />
|
<FaceImage name={pageToggle} image={image} />
|
||||||
))}
|
))}
|
||||||
@ -125,6 +130,27 @@ type FaceImageProps = {
|
|||||||
function FaceImage({ name, image }: FaceImageProps) {
|
function FaceImage({ name, image }: FaceImageProps) {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
|
const onDelete = useCallback(() => {
|
||||||
|
axios
|
||||||
|
.post(`/faces/${name}/delete`, { ids: [image] })
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.status == 200) {
|
||||||
|
toast.error(`Successfully deleted face.`, { position: "top-center" });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [name, image]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative h-40"
|
className="relative h-40"
|
||||||
@ -134,7 +160,10 @@ function FaceImage({ name, image }: FaceImageProps) {
|
|||||||
>
|
>
|
||||||
{hovered && (
|
{hovered && (
|
||||||
<div className="absolute right-1 top-1">
|
<div className="absolute right-1 top-1">
|
||||||
<Chip className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500">
|
<Chip
|
||||||
|
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
|
||||||
|
onClick={() => onDelete()}
|
||||||
|
>
|
||||||
<LuTrash className="size-4 fill-destructive text-destructive" />
|
<LuTrash className="size-4 fill-destructive text-destructive" />
|
||||||
</Chip>
|
</Chip>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user