mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-27 17:17:40 +03:00
renaming and deletion fixes
This commit is contained in:
parent
efe5864ebc
commit
c9e320c313
@ -1647,24 +1647,14 @@ def update_trigger_embedding(
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
old = list(
|
||||
Trigger.select(Trigger.camera, Trigger.name, Trigger.data)
|
||||
.where(Trigger.camera == camera, Trigger.name == name)
|
||||
.dicts()
|
||||
.iterator()
|
||||
)
|
||||
if not old:
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": False,
|
||||
"message": f"Trigger {camera}:{name} not found",
|
||||
},
|
||||
status_code=404,
|
||||
)
|
||||
# Check if trigger exists for upsert
|
||||
trigger = Trigger.get_or_none(Trigger.camera == camera, Trigger.name == name)
|
||||
|
||||
context.delete_trigger_thumbnail(camera, old[0]["data"])
|
||||
if trigger:
|
||||
# Update existing trigger
|
||||
if trigger.data != body.data: # Delete old thumbnail only if data changes
|
||||
context.delete_trigger_thumbnail(camera, trigger.data)
|
||||
|
||||
updated = (
|
||||
Trigger.update(
|
||||
data=body.data,
|
||||
model=request.app.frigate_config.semantic_search.model,
|
||||
@ -1672,18 +1662,19 @@ def update_trigger_embedding(
|
||||
threshold=body.threshold,
|
||||
triggering_event_id="",
|
||||
last_triggered=None,
|
||||
)
|
||||
.where(Trigger.camera == camera, Trigger.name == name)
|
||||
.execute()
|
||||
)
|
||||
|
||||
if updated == 0:
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": False,
|
||||
"message": f"Trigger {camera}:{name} not found",
|
||||
},
|
||||
status_code=404,
|
||||
).where(Trigger.camera == camera, Trigger.name == name).execute()
|
||||
else:
|
||||
# Create new trigger (for rename case)
|
||||
Trigger.create(
|
||||
camera=camera,
|
||||
name=name,
|
||||
type=body.type,
|
||||
data=body.data,
|
||||
threshold=body.threshold,
|
||||
model=request.app.frigate_config.semantic_search.model,
|
||||
embedding=np.array(embedding, dtype=np.float32).tobytes(),
|
||||
triggering_event_id="",
|
||||
last_triggered=None,
|
||||
)
|
||||
|
||||
if body.type == "thumbnail":
|
||||
|
||||
@ -196,6 +196,7 @@ class WebPushClient(Communicator): # type: ignore[misc]
|
||||
# notification action enabled
|
||||
if (
|
||||
not self.config.cameras[camera].notifications.enabled
|
||||
or name not in self.config.cameras[camera].semantic_search.triggers
|
||||
or "notification"
|
||||
not in self.config.cameras[camera]
|
||||
.semantic_search.triggers[name]
|
||||
|
||||
@ -81,7 +81,9 @@ class SemanticTriggerProcessor(PostProcessorApi):
|
||||
|
||||
for trigger in triggers:
|
||||
if (
|
||||
not self.config.cameras[camera]
|
||||
trigger["name"]
|
||||
not in self.config.cameras[camera].semantic_search.triggers
|
||||
or not self.config.cameras[camera]
|
||||
.semantic_search.triggers[trigger["name"]]
|
||||
.enabled
|
||||
):
|
||||
|
||||
@ -681,7 +681,7 @@
|
||||
},
|
||||
"deleteTrigger": {
|
||||
"title": "Delete Trigger",
|
||||
"desc": "Are you sure you want to delete the trigger <strong>{{triggerName}}</strong> from camera {{camera}}? This action cannot be undone."
|
||||
"desc": "Are you sure you want to delete the trigger <strong>{{triggerName}}</strong>? This action cannot be undone."
|
||||
},
|
||||
"form": {
|
||||
"name": {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
@ -40,6 +40,7 @@ type CreateTriggerDialogProps = {
|
||||
show: boolean;
|
||||
trigger: Trigger | null;
|
||||
selectedCamera: string;
|
||||
isLoading: boolean;
|
||||
onCreate: (
|
||||
enabled: boolean,
|
||||
name: string,
|
||||
@ -56,12 +57,12 @@ export default function CreateTriggerDialog({
|
||||
show,
|
||||
trigger,
|
||||
selectedCamera,
|
||||
isLoading,
|
||||
onCreate,
|
||||
onEdit,
|
||||
onCancel,
|
||||
}: CreateTriggerDialogProps) {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const existingTriggerNames = useMemo(() => {
|
||||
@ -112,7 +113,6 @@ export default function CreateTriggerDialog({
|
||||
});
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
setIsLoading(true);
|
||||
if (trigger) {
|
||||
onEdit({ ...values });
|
||||
} else {
|
||||
@ -125,7 +125,6 @@ export default function CreateTriggerDialog({
|
||||
values.actions,
|
||||
);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -9,10 +9,12 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Trans } from "react-i18next";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
|
||||
type DeleteTriggerDialogProps = {
|
||||
show: boolean;
|
||||
triggerName: string;
|
||||
isLoading: boolean;
|
||||
onCancel: () => void;
|
||||
onDelete: () => void;
|
||||
};
|
||||
@ -20,6 +22,7 @@ type DeleteTriggerDialogProps = {
|
||||
export default function DeleteTriggerDialog({
|
||||
show,
|
||||
triggerName,
|
||||
isLoading,
|
||||
onCancel,
|
||||
onDelete,
|
||||
}: DeleteTriggerDialogProps) {
|
||||
@ -48,16 +51,25 @@ export default function DeleteTriggerDialog({
|
||||
aria-label={t("button.cancel", { ns: "common" })}
|
||||
onClick={onCancel}
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{t("button.cancel", { ns: "common" })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
aria-label={t("button.delete", { ns: "common" })}
|
||||
className="flex flex-1"
|
||||
className="flex flex-1 text-white"
|
||||
onClick={onDelete}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{t("button.delete", { ns: "common" })}
|
||||
{isLoading ? (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<ActivityIndicator />
|
||||
<span>{t("button.delete", { ns: "common" })}</span>
|
||||
</div>
|
||||
) : (
|
||||
t("button.delete", { ns: "common" })
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -60,7 +60,7 @@ export default function DeleteUserDialog({
|
||||
<Button
|
||||
variant="destructive"
|
||||
aria-label={t("button.delete", { ns: "common" })}
|
||||
className="flex flex-1"
|
||||
className="flex flex-1 text-white"
|
||||
onClick={onDelete}
|
||||
>
|
||||
{t("button.delete", { ns: "common" })}
|
||||
|
||||
@ -476,6 +476,7 @@ function LibrarySelector({
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="text-white"
|
||||
onClick={() => {
|
||||
if (confirmDelete) {
|
||||
handleDeleteFace(confirmDelete);
|
||||
|
||||
@ -431,6 +431,7 @@ function LibrarySelector({
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="text-white"
|
||||
onClick={() => {
|
||||
if (confirmDelete) {
|
||||
handleDeleteFace(confirmDelete);
|
||||
|
||||
@ -169,6 +169,7 @@ export default function TriggerView({
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
setShowCreate(false);
|
||||
});
|
||||
},
|
||||
[t, updateConfig, selectedCamera, setUnsavedChanges],
|
||||
@ -185,7 +186,6 @@ export default function TriggerView({
|
||||
) => {
|
||||
setUnsavedChanges(true);
|
||||
saveToConfig({ enabled, name, type, data, threshold, actions }, false);
|
||||
setShowCreate(false);
|
||||
},
|
||||
[saveToConfig, setUnsavedChanges],
|
||||
);
|
||||
@ -193,18 +193,40 @@ export default function TriggerView({
|
||||
const onEdit = useCallback(
|
||||
(trigger: Trigger) => {
|
||||
setUnsavedChanges(true);
|
||||
setIsLoading(true);
|
||||
if (selectedTrigger?.name && selectedTrigger.name !== trigger.name) {
|
||||
// Handle rename by deleting old trigger
|
||||
// Handle rename: delete old trigger, update config, then save new trigger
|
||||
axios
|
||||
.delete(
|
||||
`/trigger/embedding/${selectedCamera}/${selectedTrigger.name}`,
|
||||
)
|
||||
.then((embeddingResponse) => {
|
||||
if (embeddingResponse.data.success) {
|
||||
return saveToConfig(trigger, true);
|
||||
} else {
|
||||
if (!embeddingResponse.data.success) {
|
||||
throw new Error(embeddingResponse.data.message);
|
||||
}
|
||||
const deleteConfigBody: ConfigSetBody = {
|
||||
requires_restart: 0,
|
||||
config_data: {
|
||||
cameras: {
|
||||
[selectedCamera]: {
|
||||
semantic_search: {
|
||||
triggers: {
|
||||
[selectedTrigger.name]: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
update_topic: `config/cameras/${selectedCamera}/semantic_search`,
|
||||
};
|
||||
return axios.put("config/set", deleteConfigBody);
|
||||
})
|
||||
.then((configResponse) => {
|
||||
if (configResponse.status !== 200) {
|
||||
throw new Error(configResponse.statusText);
|
||||
}
|
||||
// Save new trigger
|
||||
saveToConfig(trigger, false);
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorMessage =
|
||||
@ -218,9 +240,9 @@ export default function TriggerView({
|
||||
setIsLoading(false);
|
||||
});
|
||||
} else {
|
||||
// Regular update without rename
|
||||
saveToConfig(trigger, true);
|
||||
}
|
||||
setShowCreate(false);
|
||||
setSelectedTrigger(null);
|
||||
},
|
||||
[t, saveToConfig, selectedCamera, selectedTrigger, setUnsavedChanges],
|
||||
@ -254,7 +276,6 @@ export default function TriggerView({
|
||||
.put("config/set", configBody)
|
||||
.then((configResponse) => {
|
||||
if (configResponse.status === 200) {
|
||||
setShowDelete(false);
|
||||
updateConfig();
|
||||
toast.success(
|
||||
t("triggers.toast.success.deleteTrigger", { name }),
|
||||
@ -282,6 +303,7 @@ export default function TriggerView({
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
setShowDelete(false);
|
||||
setIsLoading(false);
|
||||
});
|
||||
},
|
||||
@ -439,7 +461,7 @@ export default function TriggerView({
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
className="h-8 px-2"
|
||||
className="h-8 px-2 text-white"
|
||||
onClick={() => {
|
||||
setSelectedTrigger(trigger);
|
||||
setShowDelete(true);
|
||||
@ -472,6 +494,7 @@ export default function TriggerView({
|
||||
show={showCreate}
|
||||
trigger={selectedTrigger}
|
||||
selectedCamera={selectedCamera}
|
||||
isLoading={isLoading}
|
||||
onCreate={onCreate}
|
||||
onEdit={onEdit}
|
||||
onCancel={() => {
|
||||
@ -483,6 +506,7 @@ export default function TriggerView({
|
||||
<DeleteTriggerDialog
|
||||
show={showDelete}
|
||||
triggerName={selectedTrigger?.name ?? ""}
|
||||
isLoading={isLoading}
|
||||
onCancel={() => {
|
||||
setShowDelete(false);
|
||||
setSelectedTrigger(null);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user