From 7c3e093fcccfc21e464f07d93399f46c62c71624 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:07:23 -0600 Subject: [PATCH 1/7] catch failed image embedding in triggers --- frigate/api/event.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frigate/api/event.py b/frigate/api/event.py index 13886af13..c084a8971 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -1753,7 +1753,7 @@ def create_trigger_embedding( body.data, (base64.b64encode(thumbnail).decode("ASCII")) ) - if embedding is None: + if not embedding: return JSONResponse( content={ "success": False, @@ -1888,7 +1888,7 @@ def update_trigger_embedding( body.data, (base64.b64encode(thumbnail).decode("ASCII")) ) - if embedding is None: + if not embedding: return JSONResponse( content={ "success": False, From 4850c04c0c570d52f22d2682a6b4f1ea3d2127f3 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:46:16 -0600 Subject: [PATCH 2/7] move scrollbar to edge on platform aware dialog drawers --- .../components/filter/SearchFilterGroup.tsx | 18 ++++++++---------- .../overlay/dialog/PlatformAwareDialog.tsx | 4 ++-- web/src/components/settings/SearchSettings.tsx | 9 ++++----- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/web/src/components/filter/SearchFilterGroup.tsx b/web/src/components/filter/SearchFilterGroup.tsx index d049480c6..3c44cad0c 100644 --- a/web/src/components/filter/SearchFilterGroup.tsx +++ b/web/src/components/filter/SearchFilterGroup.tsx @@ -314,11 +314,10 @@ function GeneralFilterButton({ { if (!open) { @@ -510,11 +509,10 @@ function SortTypeButton({ { if (!open) { diff --git a/web/src/components/overlay/dialog/PlatformAwareDialog.tsx b/web/src/components/overlay/dialog/PlatformAwareDialog.tsx index ab69e1288..e0ec6efa9 100644 --- a/web/src/components/overlay/dialog/PlatformAwareDialog.tsx +++ b/web/src/components/overlay/dialog/PlatformAwareDialog.tsx @@ -44,8 +44,8 @@ export default function PlatformAwareDialog({ return ( {trigger} - - {content} + +
{content}
); diff --git a/web/src/components/settings/SearchSettings.tsx b/web/src/components/settings/SearchSettings.tsx index 74301ee1d..54d1f801a 100644 --- a/web/src/components/settings/SearchSettings.tsx +++ b/web/src/components/settings/SearchSettings.tsx @@ -136,11 +136,10 @@ export default function ExploreSettings({ { setOpen(open); From 1950f367f01883e6b84d9a1b563b633a7b5d38c1 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:46:27 -0600 Subject: [PATCH 3/7] add i18n key --- web/public/locales/en/views/explore.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/public/locales/en/views/explore.json b/web/public/locales/en/views/explore.json index 19559a43a..5335aa5ac 100644 --- a/web/public/locales/en/views/explore.json +++ b/web/public/locales/en/views/explore.json @@ -61,7 +61,8 @@ "header": { "zones": "Zones", "ratio": "Ratio", - "area": "Area" + "area": "Area", + "score": "Score" } }, "annotationSettings": { From d43473867b1051e2f84eccb2efebc4fee06dc937 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:04:00 -0600 Subject: [PATCH 4/7] show negotiated mse codecs in console on error --- docs/docs/configuration/live.md | 2 +- web/src/components/player/MsePlayer.tsx | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/docs/configuration/live.md b/docs/docs/configuration/live.md index 8c310634a..be467f7f8 100644 --- a/docs/docs/configuration/live.md +++ b/docs/docs/configuration/live.md @@ -232,7 +232,7 @@ When your browser runs into problems playing back your camera streams, it will l - **mse-decode** - What it means: The browser reported a decoding error while trying to play the stream, which usually is a result of a codec incompatibility or corrupted frames. - - What to try: Ensure your camera/restream is using H.264 video and AAC audio (these are the most compatible). If your camera uses a non-standard audio codec, configure `go2rtc` to transcode the stream to AAC. Try another browser (some browsers have stricter MSE/codec support) and, for iPhone, ensure you're on iOS 17.1 or newer. + - What to try: Check the browser console for the supported and negotiated codecs. Ensure your camera/restream is using H.264 video and AAC audio (these are the most compatible). If your camera uses a non-standard audio codec, configure `go2rtc` to transcode the stream to AAC. Try another browser (some browsers have stricter MSE/codec support) and, for iPhone, ensure you're on iOS 17.1 or newer. - Possible console messages from the player code: diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index 576fc93d6..b7d1aab01 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -94,12 +94,19 @@ function MSEPlayer({ console.error( `${camera} - MSE error '${error}': ${description} See the documentation: https://docs.frigate.video/configuration/live/#live-player-error-messages`, ); + if (mseCodecRef.current) { // eslint-disable-next-line no-console - console.error(`${camera} - MSE codec in use: ${mseCodecRef.current}`); + console.error( + `${camera} - Browser negotiated codecs: ${mseCodecRef.current}`, + ); + // eslint-disable-next-line no-console + console.error(`${camera} - Supported codecs: ${CODECS.join(", ")}`); } onError?.(error); }, + // we know that these deps are correct + // eslint-disable-next-line react-hooks/exhaustive-deps [camera, onError], ); From 8ceeb4ffb3db39c882963a554ad9ee1e08de1ffd Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 24 Nov 2025 15:52:09 -0700 Subject: [PATCH 5/7] try changing rocm --- docker/rocm/rocm.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/rocm/rocm.hcl b/docker/rocm/rocm.hcl index 6a52b9578..0e9ae2c5c 100644 --- a/docker/rocm/rocm.hcl +++ b/docker/rocm/rocm.hcl @@ -2,7 +2,7 @@ variable "AMDGPU" { default = "gfx900" } variable "ROCM" { - default = "7.1" + default = "7.1.0" } variable "HSA_OVERRIDE_GFX_VERSION" { default = "" From ede07f43cb16140156cc34246c4b5584d72e77a3 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 24 Nov 2025 15:52:22 -0700 Subject: [PATCH 6/7] Improve toast consistency --- web/public/locales/en/components/dialog.json | 1 + web/src/components/card/ReviewCard.tsx | 2 +- .../components/classification/wizard/Step3ChooseExamples.tsx | 4 +++- web/src/components/overlay/ExportDialog.tsx | 2 +- web/src/components/overlay/MobileReviewSettingsDrawer.tsx | 4 +++- web/src/pages/FaceLibrary.tsx | 1 + web/src/views/classification/ModelTrainingView.tsx | 4 ++++ web/src/views/events/EventView.tsx | 4 +++- 8 files changed, 17 insertions(+), 5 deletions(-) diff --git a/web/public/locales/en/components/dialog.json b/web/public/locales/en/components/dialog.json index a40e62db7..a56c2b1da 100644 --- a/web/public/locales/en/components/dialog.json +++ b/web/public/locales/en/components/dialog.json @@ -53,6 +53,7 @@ "selectOrExport": "Select or Export", "toast": { "success": "Successfully started export. View the file in the exports page.", + "view": "View", "error": { "failed": "Failed to start export: {{error}}", "endTimeMustAfterStartTime": "End time must be after start time", diff --git a/web/src/components/card/ReviewCard.tsx b/web/src/components/card/ReviewCard.tsx index 8fc4024db..e8d8121a8 100644 --- a/web/src/components/card/ReviewCard.tsx +++ b/web/src/components/card/ReviewCard.tsx @@ -87,7 +87,7 @@ export default function ReviewCard({ position: "top-center", action: ( - + ), }); diff --git a/web/src/components/classification/wizard/Step3ChooseExamples.tsx b/web/src/components/classification/wizard/Step3ChooseExamples.tsx index e4c157526..6e4311cec 100644 --- a/web/src/components/classification/wizard/Step3ChooseExamples.tsx +++ b/web/src/components/classification/wizard/Step3ChooseExamples.tsx @@ -148,7 +148,9 @@ export default function Step3ChooseExamples({ // Step 3: Kick off training await axios.post(`/classification/${step1Data.modelName}/train`); - toast.success(t("wizard.step3.trainingStarted")); + toast.success(t("wizard.step3.trainingStarted"), { + closeButton: true, + }); setIsTraining(true); }, [step1Data, step2Data, t], diff --git a/web/src/components/overlay/ExportDialog.tsx b/web/src/components/overlay/ExportDialog.tsx index 832e9faa9..976b20042 100644 --- a/web/src/components/overlay/ExportDialog.tsx +++ b/web/src/components/overlay/ExportDialog.tsx @@ -97,7 +97,7 @@ export default function ExportDialog({ position: "top-center", action: ( - + ), }); diff --git a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx index d7f0c7ae6..78827a99e 100644 --- a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx +++ b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx @@ -106,7 +106,9 @@ export default function MobileReviewSettingsDrawer({ position: "top-center", action: ( - + ), }, diff --git a/web/src/pages/FaceLibrary.tsx b/web/src/pages/FaceLibrary.tsx index 78e0e5760..439a9bde4 100644 --- a/web/src/pages/FaceLibrary.tsx +++ b/web/src/pages/FaceLibrary.tsx @@ -808,6 +808,7 @@ function FaceAttemptGroup({ if (resp.status == 200) { toast.success(t("toast.success.trainedFace"), { position: "top-center", + closeButton: true, }); onRefresh(); } diff --git a/web/src/views/classification/ModelTrainingView.tsx b/web/src/views/classification/ModelTrainingView.tsx index 53328e0e2..d6d03c6cc 100644 --- a/web/src/views/classification/ModelTrainingView.tsx +++ b/web/src/views/classification/ModelTrainingView.tsx @@ -104,12 +104,14 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { if (modelState == "complete") { toast.success(t("toast.success.trainedModel"), { position: "top-center", + closeButton: true, }); setWasTraining(false); refreshDataset(); } else if (modelState == "failed") { toast.error(t("toast.error.trainingFailed"), { position: "top-center", + closeButton: true, }); setWasTraining(false); } @@ -182,6 +184,7 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { setWasTraining(true); toast.success(t("toast.success.trainingModel"), { position: "top-center", + closeButton: true, }); } }) @@ -193,6 +196,7 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { toast.error(t("toast.error.trainingFailedToStart", { errorMessage }), { position: "top-center", + closeButton: true, }); }); }, [model, t]); diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index d054dabc9..417c3231d 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -219,7 +219,9 @@ export default function EventView({ position: "top-center", action: ( - + ), }, From 93de8471d8948f745035e52efb3d9b244d4e888c Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 25 Nov 2025 07:14:25 -0600 Subject: [PATCH 7/7] add attribute area and score to detail stream tooltip --- web/src/components/timeline/DetailStream.tsx | 109 ++++++++++++++++--- 1 file changed, 91 insertions(+), 18 deletions(-) diff --git a/web/src/components/timeline/DetailStream.tsx b/web/src/components/timeline/DetailStream.tsx index d133aa05a..be01eb16e 100644 --- a/web/src/components/timeline/DetailStream.tsx +++ b/web/src/components/timeline/DetailStream.tsx @@ -681,22 +681,62 @@ function LifecycleItem({ }) : ""; - const ratio = - Array.isArray(item?.data.box) && item?.data.box.length >= 4 - ? (aspectRatio * (item?.data.box[2] / item?.data.box[3])).toFixed(2) - : "N/A"; - const areaPx = - Array.isArray(item?.data.box) && item?.data.box.length >= 4 - ? Math.round( - (config?.cameras[item?.camera]?.detect?.width ?? 0) * - (config?.cameras[item?.camera]?.detect?.height ?? 0) * - (item?.data.box[2] * item?.data.box[3]), - ) - : undefined; - const areaPct = - Array.isArray(item?.data.box) && item?.data.box.length >= 4 - ? (item?.data.box[2] * item?.data.box[3]).toFixed(4) - : undefined; + const ratio = useMemo( + () => + Array.isArray(item?.data.box) && item?.data.box.length >= 4 + ? (aspectRatio * (item?.data.box[2] / item?.data.box[3])).toFixed(2) + : "N/A", + [aspectRatio, item], + ); + + const areaPx = useMemo( + () => + Array.isArray(item?.data.box) && item?.data.box.length >= 4 + ? Math.round( + (config?.cameras[item?.camera]?.detect?.width ?? 0) * + (config?.cameras[item?.camera]?.detect?.height ?? 0) * + (item?.data.box[2] * item?.data.box[3]), + ) + : undefined, + [config, item], + ); + + const areaPct = useMemo( + () => + Array.isArray(item?.data.box) && item?.data.box.length >= 4 + ? (item?.data.box[2] * item?.data.box[3]).toFixed(4) + : undefined, + [item], + ); + + const attributeAreaPx = useMemo( + () => + Array.isArray(item?.data.attribute_box) && + item?.data.attribute_box.length >= 4 + ? Math.round( + (config?.cameras[item?.camera]?.detect?.width ?? 0) * + (config?.cameras[item?.camera]?.detect?.height ?? 0) * + (item?.data.attribute_box[2] * item?.data.attribute_box[3]), + ) + : undefined, + [config, item], + ); + + const attributeAreaPct = useMemo( + () => + Array.isArray(item?.data.attribute_box) && + item?.data.attribute_box.length >= 4 + ? (item?.data.attribute_box[2] * item?.data.attribute_box[3]).toFixed(4) + : undefined, + [item], + ); + + const score = useMemo(() => { + if (item?.data?.score !== undefined) { + return (item.data.score * 100).toFixed(0) + "%"; + } + return "N/A"; + }, [item?.data?.score]); return (
+
+ + {t("trackingDetails.lifecycleItemDesc.header.score")} + + {score} +
+
{t("trackingDetails.lifecycleItemDesc.header.ratio")} @@ -742,7 +789,13 @@ function LifecycleItem({
- {t("trackingDetails.lifecycleItemDesc.header.area")} + {t("trackingDetails.lifecycleItemDesc.header.area")}{" "} + {attributeAreaPx !== undefined && + attributeAreaPct !== undefined && ( + + ({getTranslatedLabel(item.data.label)}) + + )} {areaPx !== undefined && areaPct !== undefined ? ( @@ -754,6 +807,26 @@ function LifecycleItem({ N/A )}
+ + {attributeAreaPx !== undefined && + attributeAreaPct !== undefined && ( +
+ + {t("trackingDetails.lifecycleItemDesc.header.area")}{" "} + {attributeAreaPx !== undefined && + attributeAreaPct !== undefined && ( + + ({getTranslatedLabel(item.data.attribute)}) + + )} + + + {attributeAreaPx} {t("pixels", { ns: "common" })}{" "} + ยท{" "} + {attributeAreaPct}% + +
+ )}
@@ -820,7 +893,7 @@ function ObjectTimeline({ }, [config, fullTimeline, review]); if (isValidating && (!timeline || timeline.length === 0)) { - return ; + return ; } if (!timeline || timeline.length === 0) {