From 407817a3b1d73949a584d81826cafb415f04cd0d Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 31 May 2026 08:51:32 -0500 Subject: [PATCH] Motion search fixes (#23359) * improve error parsing and increase skip default * improve motion search layout to match tracking details * implement draw and move mode on mobile * update motion search docs * language tweaks * improve tips * note actions menu --- docs/docs/configuration/review.md | 20 ++++- frigate/api/motion_search.py | 4 +- web/public/locales/en/views/motionSearch.json | 4 +- .../motion-search/MotionSearchDialog.tsx | 83 +++++++++++++++++-- .../motion-search/MotionSearchROICanvas.tsx | 6 +- .../views/motion-search/MotionSearchView.tsx | 10 ++- 6 files changed, 115 insertions(+), 12 deletions(-) diff --git a/docs/docs/configuration/review.md b/docs/docs/configuration/review.md index 9f160c9487..00b18219ce 100644 --- a/docs/docs/configuration/review.md +++ b/docs/docs/configuration/review.md @@ -153,7 +153,7 @@ Clicking a preview clip seeks the recording player to that timestamp so you can Motion Search lets you scan recorded footage for changes inside a region of interest you draw on the camera. Unlike Motion Previews, which surfaces what Frigate's motion detector flagged in real time, Motion Search re-analyzes the saved recordings, so it can find changes that were missed (for example, an object that appeared while motion detection was paused by `lightning_threshold`, or in a region that is normally motion-masked). -To start a search, click the kebab menu on a camera in the page and choose **Motion Search**. In the dialog: +To start a search, open the Actions menu in History or click the kebab menu on a camera in the page and choose **Motion Search**. In the dialog: 1. Pick the camera and time range to scan. 2. Draw a polygon on the camera frame to define the region of interest. @@ -170,3 +170,21 @@ To start a search, click the kebab menu on a camera in the { if (!selectedCamera) return undefined; return config.cameras[selectedCamera]; }, [config, selectedCamera]); + const aspectRatio = useMemo(() => { + if (!cameraConfig) { + return 16 / 9; + } + + return cameraConfig.detect.width / cameraConfig.detect.height; + }, [cameraConfig]); + + // Determine camera aspect ratio category + const cameraAspect = useMemo(() => { + if (!aspectRatio) { + return "normal"; + } else if (aspectRatio > ASPECT_WIDE_LAYOUT) { + return "wide"; + } else if (aspectRatio < ASPECT_PORTRAIT_LAYOUT) { + return "tall"; + } else { + return "normal"; + } + }, [aspectRatio]); + const polygonClosed = useMemo( () => !isDrawingROI && polygonPoints.length >= 3, [isDrawingROI, polygonPoints.length], @@ -144,6 +168,7 @@ export default function MotionSearchDialog({ useEffect(() => { setImageLoaded(false); + setPanMode(false); }, [selectedCamera]); const Overlay = isDesktop ? Dialog : Drawer; @@ -218,7 +243,13 @@ export default function MotionSearchDialog({ )} - +
-
+
{selectedCamera && cameraConfig ? (
) : ( @@ -282,11 +322,41 @@ export default function MotionSearchDialog({ {polygonClosed && }
+ {!isDesktop && ( + + + + + + {panMode + ? t("polygonControls.moveMode") + : t("polygonControls.drawMode")} + + + )}