mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-18 17:14:26 +03:00
Add rename api
This commit is contained in:
parent
39fbd8a76f
commit
754ae4db37
@ -109,16 +109,22 @@ 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", [])
|
||||||
|
|
||||||
if not list_of_ids or len(list_of_ids) == 0:
|
if not list_of_ids:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=({"success": False, "message": "Not a valid list of ids"}),
|
content={"success": False, "message": "Not a valid list of ids"},
|
||||||
status_code=404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
face_dir = os.path.join(FACE_DIR, name)
|
face_dir = os.path.join(FACE_DIR, name)
|
||||||
|
|
||||||
|
if not os.path.exists(face_dir):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=404,
|
||||||
|
content={"message": f"Face '{name}' not found", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
context: EmbeddingsContext = request.app.embeddings
|
context: EmbeddingsContext = request.app.embeddings
|
||||||
context.delete_face_ids(
|
context.delete_face_ids(
|
||||||
name, map(lambda file: sanitize_filename(file), list_of_ids)
|
name, map(lambda file: sanitize_filename(file), list_of_ids)
|
||||||
@ -130,12 +136,12 @@ def deregister_faces(request: Request, name: str, body: dict = None):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to remove directory {face_dir}: {str(e)}")
|
logger.error(f"Failed to remove directory {face_dir}: {str(e)}")
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=({"success": False, "message": f"Failed to remove directory: {str(e)}"}),
|
content={"success": False, "message": f"Failed to remove directory: {str(e)}"},
|
||||||
status_code=500,
|
status_code=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=({"success": True, "message": "Successfully deleted faces."}),
|
content={"success": True, "message": "Successfully deleted faces."},
|
||||||
status_code=200,
|
status_code=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -155,3 +161,60 @@ def create_face(name: str):
|
|||||||
status_code=200,
|
status_code=200,
|
||||||
content={"message": "Successfully created face", "success": True},
|
content={"message": "Successfully created face", "success": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/faces/{name}/rename")
|
||||||
|
def rename_face(request: Request, name: str, body: dict = None):
|
||||||
|
"""Rename a face directory."""
|
||||||
|
if not request.app.frigate_config.face_recognition.enabled:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={"message": "Face recognition is not enabled.", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
json: dict[str, any] = body or {}
|
||||||
|
new_name = json.get("new_name")
|
||||||
|
|
||||||
|
if not new_name:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={"message": "New name is required", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
old_folder = os.path.join(FACE_DIR, name)
|
||||||
|
new_folder = os.path.join(FACE_DIR, new_name)
|
||||||
|
|
||||||
|
if not os.path.exists(old_folder):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=404,
|
||||||
|
content={"message": f"Face '{name}' not found", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
if os.path.exists(new_folder):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={"message": f"Face '{new_name}' already exists", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Use atomic operation when possible
|
||||||
|
try:
|
||||||
|
os.rename(old_folder, new_folder)
|
||||||
|
except OSError:
|
||||||
|
# Fallback to copy+delete if rename fails
|
||||||
|
shutil.copytree(old_folder, new_folder)
|
||||||
|
shutil.rmtree(old_folder)
|
||||||
|
|
||||||
|
context: EmbeddingsContext = request.app.embeddings
|
||||||
|
context.clear_face_classifier()
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=200,
|
||||||
|
content={"message": "Successfully renamed face", "success": True},
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to rename face: {str(e)}")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=500,
|
||||||
|
content={"message": f"Failed to rename face: {str(e)}", "success": False},
|
||||||
|
)
|
||||||
|
|||||||
@ -423,49 +423,6 @@ class FaceProcessor(RealTimeProcessorApi):
|
|||||||
"message": "Internal server error",
|
"message": "Internal server error",
|
||||||
"success": False,
|
"success": False,
|
||||||
}
|
}
|
||||||
elif topic == "rename_face":
|
|
||||||
old_name = request_data.get("old_name")
|
|
||||||
new_name = request_data.get("new_name")
|
|
||||||
|
|
||||||
if not self.__validate_face_name(new_name):
|
|
||||||
return {
|
|
||||||
"message": "Invalid new face name",
|
|
||||||
"success": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
old_folder = os.path.join(FACE_DIR, old_name)
|
|
||||||
new_folder = os.path.join(FACE_DIR, new_name)
|
|
||||||
|
|
||||||
if not os.path.exists(old_folder):
|
|
||||||
return {
|
|
||||||
"message": f"Face '{old_name}' not found",
|
|
||||||
"success": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.path.exists(new_folder):
|
|
||||||
return {
|
|
||||||
"message": f"Face name '{new_name}' already exists",
|
|
||||||
"success": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
shutil.copytree(old_folder, new_folder)
|
|
||||||
shutil.rmtree(old_folder)
|
|
||||||
|
|
||||||
# Clear and rebuild classifier with new names
|
|
||||||
self.__clear_classifier()
|
|
||||||
self.__build_classifier()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"message": "Successfully renamed face",
|
|
||||||
"success": True,
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to rename face: {str(e)}")
|
|
||||||
return {
|
|
||||||
"message": f"Failed to rename face: {str(e)}",
|
|
||||||
"success": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
def expire_object(self, object_id: str):
|
def expire_object(self, object_id: str):
|
||||||
if object_id in self.detected_faces:
|
if object_id in self.detected_faces:
|
||||||
|
|||||||
@ -164,28 +164,8 @@ export default function FaceLibrary() {
|
|||||||
|
|
||||||
setIsRenaming(true);
|
setIsRenaming(true);
|
||||||
try {
|
try {
|
||||||
await axios.post(`/faces/${renameData.newName}/create`);
|
await axios.post(`/faces/${renameData.oldName}/rename`, {
|
||||||
|
new_name: renameData.newName
|
||||||
const oldFaceImages = faceData[renameData.oldName] || [];
|
|
||||||
const copyPromises = oldFaceImages.map(async (image: string) => {
|
|
||||||
const response = await fetch(`${baseUrl}clips/faces/${renameData.oldName}/${image}`);
|
|
||||||
const blob = await response.blob();
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', new File([blob], image));
|
|
||||||
formData.append('cropped', 'true');
|
|
||||||
|
|
||||||
return axios.post(`/faces/${renameData.newName}`, formData, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(copyPromises);
|
|
||||||
|
|
||||||
await axios.post(`/faces/${renameData.oldName}/delete`, {
|
|
||||||
ids: oldFaceImages.length ? oldFaceImages : ['dummy']
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setRenameDialog(false);
|
setRenameDialog(false);
|
||||||
@ -201,7 +181,7 @@ export default function FaceLibrary() {
|
|||||||
} finally {
|
} finally {
|
||||||
setIsRenaming(false);
|
setIsRenaming(false);
|
||||||
}
|
}
|
||||||
}, [renameData, faceData, refreshFaces]);
|
}, [renameData, refreshFaces]);
|
||||||
|
|
||||||
const deleteFace = useCallback(async () => {
|
const deleteFace = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -210,7 +190,9 @@ export default function FaceLibrary() {
|
|||||||
ids: images.length ? images : ['dummy']
|
ids: images.length ? images : ['dummy']
|
||||||
});
|
});
|
||||||
setRenameDialog(false);
|
setRenameDialog(false);
|
||||||
setPageToggle(faces[0]);
|
|
||||||
|
const nextFace = faces.find(face => face !== renameData.oldName) || null;
|
||||||
|
setPageToggle(nextFace);
|
||||||
await refreshFaces();
|
await refreshFaces();
|
||||||
toast.success("Successfully deleted face", { position: "top-center" });
|
toast.success("Successfully deleted face", { position: "top-center" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user