From cdfffedc44cb70cc3aba3b7bd4170f4c94a4f5b0 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:50:15 -0500 Subject: [PATCH] frontend --- web/public/locales/en/views/settings.json | 12 +- .../settings/ClassificationSettingsView.tsx | 172 +++++++++++------- 2 files changed, 111 insertions(+), 73 deletions(-) diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index 4a7693416..f6c5b2e99 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -87,9 +87,15 @@ "title": "Semantic Search", "desc": "Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.", "readTheDocumentation": "Read the Documentation", - "reindexOnStartup": { - "label": "Re-Index On Startup", - "desc": "Re-indexing will reprocess all thumbnails and descriptions (if enabled) and apply the embeddings on each startup. Don't forget to disable the option after restarting!" + "reindexNow": { + "label": "Reindex Now", + "desc": "Reindexing will regenerate embeddings for all tracked object. This process runs in the background and may max out your CPU and take a fair amount of time depending on the number of tracked objects you have.", + "confirmTitle": "Confirm Reindexing", + "confirmDesc": "Are you sure you want to reindex all tracked object embeddings? This process will run in the background but it may max out your CPU and take a fair amount of time. You can watch the progress on the Explore page.", + "confirmButton": "Reindex", + "success": "Reindexing started successfully.", + "alreadyInProgress": "Reindexing is already in progress.", + "error": "Failed to start reindexing: {{errorMessage}}" }, "modelSize": { "label": "Model Size", diff --git a/web/src/views/settings/ClassificationSettingsView.tsx b/web/src/views/settings/ClassificationSettingsView.tsx index 24c3a9107..d12008f8f 100644 --- a/web/src/views/settings/ClassificationSettingsView.tsx +++ b/web/src/views/settings/ClassificationSettingsView.tsx @@ -21,11 +21,21 @@ import { SelectTrigger, } from "@/components/ui/select"; import { Trans, useTranslation } from "react-i18next"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { buttonVariants } from "@/components/ui/button"; type ClassificationSettings = { search: { enabled?: boolean; - reindex?: boolean; model_size?: SearchModelSize; }; face: { @@ -48,39 +58,22 @@ export default function ClassificationSettingsView({ useSWR("config"); const [changedValue, setChangedValue] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [isReindexDialogOpen, setIsReindexDialogOpen] = useState(false); const { addMessage, removeMessage } = useContext(StatusBarMessagesContext)!; const [classificationSettings, setClassificationSettings] = useState({ - search: { - enabled: undefined, - reindex: undefined, - model_size: undefined, - }, - face: { - enabled: undefined, - model_size: undefined, - }, - lpr: { - enabled: undefined, - }, + search: { enabled: undefined, model_size: undefined }, + face: { enabled: undefined, model_size: undefined }, + lpr: { enabled: undefined }, }); const [origSearchSettings, setOrigSearchSettings] = useState({ - search: { - enabled: undefined, - reindex: undefined, - model_size: undefined, - }, - face: { - enabled: undefined, - model_size: undefined, - }, - lpr: { - enabled: undefined, - }, + search: { enabled: undefined, model_size: undefined }, + face: { enabled: undefined, model_size: undefined }, + lpr: { enabled: undefined }, }); useEffect(() => { @@ -89,32 +82,26 @@ export default function ClassificationSettingsView({ setClassificationSettings({ search: { enabled: config.semantic_search.enabled, - reindex: config.semantic_search.reindex, model_size: config.semantic_search.model_size, }, face: { enabled: config.face_recognition.enabled, model_size: config.face_recognition.model_size, }, - lpr: { - enabled: config.lpr.enabled, - }, + lpr: { enabled: config.lpr.enabled }, }); } setOrigSearchSettings({ search: { enabled: config.semantic_search.enabled, - reindex: config.semantic_search.reindex, model_size: config.semantic_search.model_size, }, face: { enabled: config.face_recognition.enabled, model_size: config.face_recognition.model_size, }, - lpr: { - enabled: config.lpr.enabled, - }, + lpr: { enabled: config.lpr.enabled }, }); } // we know that these deps are correct @@ -125,10 +112,7 @@ export default function ClassificationSettingsView({ newConfig: Partial, ) => { setClassificationSettings((prevConfig) => ({ - search: { - ...prevConfig.search, - ...newConfig.search, - }, + search: { ...prevConfig.search, ...newConfig.search }, face: { ...prevConfig.face, ...newConfig.face }, lpr: { ...prevConfig.lpr, ...newConfig.lpr }, })); @@ -141,10 +125,8 @@ export default function ClassificationSettingsView({ axios .put( - `config/set?semantic_search.enabled=${classificationSettings.search.enabled ? "True" : "False"}&semantic_search.reindex=${classificationSettings.search.reindex ? "True" : "False"}&semantic_search.model_size=${classificationSettings.search.model_size}&face_recognition.enabled=${classificationSettings.face.enabled ? "True" : "False"}&face_recognition.model_size=${classificationSettings.face.model_size}&lpr.enabled=${classificationSettings.lpr.enabled ? "True" : "False"}`, - { - requires_restart: 0, - }, + `config/set?semantic_search.enabled=${classificationSettings.search.enabled ? "True" : "False"}&semantic_search.model_size=${classificationSettings.search.model_size}&face_recognition.enabled=${classificationSettings.face.enabled ? "True" : "False"}&face_recognition.model_size=${classificationSettings.face.model_size}&lpr.enabled=${classificationSettings.lpr.enabled ? "True" : "False"}`, + { requires_restart: 0 }, ) .then((res) => { if (res.status === 200) { @@ -156,9 +138,7 @@ export default function ClassificationSettingsView({ } else { toast.error( t("classification.toast.error", { errorMessage: res.statusText }), - { - position: "top-center", - }, + { position: "top-center" }, ); } }) @@ -169,9 +149,7 @@ export default function ClassificationSettingsView({ "Unknown error"; toast.error( t("toast.save.error.title", { errorMessage, ns: "common" }), - { - position: "top-center", - }, + { position: "top-center" }, ); }) .finally(() => { @@ -191,6 +169,43 @@ export default function ClassificationSettingsView({ removeMessage("search_settings", "search_settings"); }, [origSearchSettings, removeMessage]); + const onReindex = useCallback(() => { + setIsLoading(true); + + axios + .put("/reindex") + .then((res) => { + if (res.status === 202) { + toast.success(t("classification.semanticSearch.reindexNow.success"), { + position: "top-center", + }); + } else { + toast.error( + t("classification.semanticSearch.reindexNow.error", { + errorMessage: res.statusText, + }), + { position: "top-center" }, + ); + } + }) + .catch((error) => { + const errorMessage = + error.response?.data?.message || + error.response?.data?.detail || + "Unknown error"; + toast.error( + t("classification.semanticSearch.reindexNow.error", { + errorMessage, + }), + { position: "top-center" }, + ); + }) + .finally(() => { + setIsLoading(false); + setIsReindexDialogOpen(false); + }); + }, [t]); + useEffect(() => { if (changedValue) { addMessage( @@ -262,28 +277,18 @@ export default function ClassificationSettingsView({ -
-
- { - handleClassificationConfigChange({ - search: { reindex: isChecked }, - }); - }} - /> -
- -
-
+
+
- classification.semanticSearch.reindexOnStartup.desc + classification.semanticSearch.reindexNow.desc
@@ -316,9 +321,7 @@ export default function ClassificationSettingsView({ value={classificationSettings.search.model_size} onValueChange={(value) => handleClassificationConfigChange({ - search: { - model_size: value as SearchModelSize, - }, + search: { model_size: value as SearchModelSize }, }) } > @@ -346,6 +349,35 @@ export default function ClassificationSettingsView({
+ + + + + {t("classification.semanticSearch.reindexNow.confirmTitle")} + + + + classification.semanticSearch.reindexNow.confirmDesc + + + + + setIsReindexDialogOpen(false)}> + {t("button.cancel", { ns: "common" })} + + + {t("classification.semanticSearch.reindexNow.confirmButton")} + + + + +