100 Commits
Author SHA1 Message Date
Josh HawkinsandGitHub 52f50a7396 Tweaks (#24418)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* don't display audio transcription provider message as health notice

* show remote provider for audio transcription in health pane

* adjust trigger and notifications messages to be consistent with the rest of the settings UI

* disable save buttons when there are no changes in config editor

* fix audio manager crash when a camera is added at runtime

The audio processor and the camera maintainer both poll the same `add` config update on their own one second timers, and the maintainer is what creates `camera_metrics[name]`. When the audio processor got there first it looked the new camera up before that entry existed, and the `KeyError` took down the whole `frigate.audio_manager` process. Whether it happens depends purely on which poll fires first, so cloning a camera from the UI fails or succeeds at random. `spawn_if_needed` now skips a camera whose metrics aren't there yet and picks it up on the next poll, the same way it already waits on a late ffmpeg update.

`AudioEventMaintainer` holds the `CameraMetrics` object now instead of indexing the manager dict on every audio chunk, which drops the IPC round trips and means a removed camera can't `KeyError` out of `detect_audio` after the maintainer pops the entry. The audio process is also registered with the watchdog, since a crash there previously left audio detection dead for every camera until a full restart, and it now receives the shared `DataProcessorMetrics` so `AudioTranscriptionRealTimeProcessor` gets the same type as the other real time processors.

* fix stationary max_frames dropping other tracked objects

When `max_frames` was set for a label, deregistering one object rebuilt norfair's list with a filter that kept an object only if it was both not the target and already on its way out, so every other healthy object of that label was dropped along with it. Any car leaving the frame took the rest of the cars with it and they came back as new tracked objects a few frames later. The filter now removes only the target, and objects that are expiring are still reaped by norfair on the next update.

* fix test

* fix skip_motion_threshold permanently disabling motion detection

The skip check returned before the two `accumulateWeighted` calls at the end of `detect`, so a skipped frame never made it into the background and setting `calibrating` there only picked a faster alpha for calls that never ran. `avg_frame` starts as an all zero image and a normally lit scene differs from black across nearly the whole frame, so the cameras I tested measure 0.84 to 0.98 against it. Any `skip_motion_threshold` below that number skips the first frame, leaves the background black, and skips every frame after it. Motion detection is dead for that camera until the setting is removed or Frigate restarts, with no motion boxes, no motion recordings, and no regions for the tracker since the detector stays calibrating.

Startup isn't the only way in. `update_mask` zeroes the background on any motion config change, and once a camera has calibrated the first IR switch or PTZ move freezes the background on the old scene, so it can't transition to the new one, which is the case the option exists for. The frame is now blended in before the early return at the same 0.2 alpha the calibrating path uses elsewhere, so a large scene change is still suppressed while the background catches up, about a second on a 5 fps camera, and then motion comes back.

* dump ffmpeg logs on every restart

The record watchdog restarted ffmpeg without flushing its `LogPipe`, so a camera whose recording segments went stale never showed a single line of ffmpeg output. The dump now happens in `start_or_restart_ffmpeg` right after the stop, which covers the stale record path, the record crash path, and the audio restart. `reset_capture_thread` and the audio `log_and_restart` fallback keep their own dumps since both pass `ffmpeg_process=None`.

* dump ffmpeg logs once per restart

The audio restart path dumped the log pipe itself before calling the helper, so the restart dump printed a second "last 100 lines" heading over an already drained deque and split the tail that `stop_ffmpeg` flushed into its own section. The heading is now only printed when there's something under it, and the audio path leaves the dump to the restart so each failure produces one section.

* keep all logpipe dumps consistent
2026-09-20 12:45:24 -06:00
Josh HawkinsandGitHub 3d08bbe520 Miscellaneous fixes (#24402)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* check for a valid frame before using its shape

With the camera offline, no preview frame, and `camera-error.jpg` missing, `latest_frame` read `frame.shape` before its `frame is None` check, so it raised `AttributeError` and answered 500 with a traceback instead of the intended "Unable to get valid frame". The check now runs first.

* fix the has_clip self-heal for events with no recordings

`vod_event` looked for a `(body, 404)` tuple, but `vod_ts` returns a `JSONResponse`, so the check never matched and an old event whose recordings are gone kept offering a clip that can't play. It now checks the response status code.

* return 403 for a snapshot or thumbnail on another camera

The broad `except Exception` handlers in `event_snapshot` and `event_thumbnail` caught the `HTTPException` from `require_camera_access`, so a restricted user asking for another camera's snapshot got a 404 instead of a 403, and for an object still being tracked the snapshot was rendered before the check ran. Both endpoints now look up the event and check access in their own block, the way the other endpoints do, so a denial propagates.

* find DST transitions to the second

`get_dst_transitions` probed the offset once every 24 hours from the start time and reported a change at the first probe after it, up to a day late, so events, review items and recordings near a transition were grouped into days with the old offset. A transition after the last daily probe wasn't found at all. The end of the range is probed too now, and a probe that sees the offset change bisects the interval to the second of the transition.

* don't run page shortcuts for keys a dialog already handled

Radix dismisses a dialog on Escape from a capture-phase keydown listener and calls `preventDefault()` without stopping propagation, so `useKeyboardListener` still ran the page's Escape shortcut: cancelling the delete dialog in the face library or a classification model also cleared the whole selection. Keys another shortcut hook handled still get through, since their listener order changes with every render.

* fix train image filtering for a class with a dash

The backend writes a class with a `-` as `_` in train file names, since it splits those names on `-`, while a dataset folder keeps the dash. Filtering the Train grid by `half-open` compared it with `half_open` and hid every attempt. Both sides are normalized the same way now.

* don't edit a chat message while a reply streams

The edit button stayed active while a reply streamed. `submitConversation` returns early while loading, but the message bubble still closed its editor, so the edit was silently lost. The edit button is hidden while a reply streams, and an editor that's already open keeps its draft with send disabled until the reply ends.

* fix restart failing under non-root

restart_frigate() called psutil.Process(1).terminate() to signal s6-svscan, but s6-svscan runs as root while frigate runs as uid 1000, so the call raised AccessDenied. That exception escaped every caller: the UI restart button dropped its websocket client, MQTT restart and Save & Restart just logged and did nothing, and the watchdog crashed its own monitoring thread on a dead detector. This catches AccessDenied and falls through to the existing SIGINT branch, which exits the process for s6 to restart it.

* show runtime overrides in the settings form

The settings form read a camera section's saved config value, but its dependent warnings (audio transcription requiring audio detection, snapshots requiring detect, etc.) read the live config instead. A runtime toggle from the live view, MQTT, or an active profile can turn a section off without touching yaml, and that override persists across restarts, so the Enable switch showed on while the warning said the feature wasn't enabled. This adds an "Overridden (Live)" badge to any field whose live value differs from what's saved, and swaps the affected warnings to runtime-specific wording when a runtime override is the actual cause instead of the config.

* fix mobile overflowing icons in system due to new health pane

* fix genai settings keeping a stale model and dropping roles after save

Switching a GenAI entry's provider left the previous provider's model selected, so saving wrote a model the new provider doesn't serve. llama.cpp can't find that model in `/v1/models`, so the backend reported every capability as false for the entry, and once the save refetched `genai/models` the roles widget stripped `transcribe` from the form on its own. The section showed unsaved changes right after saving, and saving again would have dropped the role. Switching provider now clears the model, and the roles widget only strips a role for a model or provider picked in the form, since the entry-level capability flags only describe the saved model. A selected role stays visible when the provider can't confirm it, so it can still be switched off. The llama.cpp model list also no longer repeats a model whose alias matches its id, which is what `--alias` produces.

* close onvif sessions on shutdown

`OnvifController.close()` only stopped its event loop, so the aiohttp sessions each `ONVIFCamera` holds and the `_poll_config_updates` task were left to be garbage collected during interpreter shutdown, when their warnings can no longer be logged. Every restart ended with a run of `Unclosed client session` and `Task was destroyed but it is pending!` logging errors, which only became visible once restart started exiting the process itself under non-root. `close()` now closes each camera's client and cancels the tasks on the loop before stopping it.

* fixes

* fixes
2026-09-18 07:33:23 -06:00
Josh HawkinsandGitHub 10a0d5ea37 Improve UI zone operations (#24376)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* improve zone renaming

Zone rename now saves as one JSON body via config/set, moving required_zones and profile overrides instead of leaving stale references

* fixes
2026-09-16 12:07:05 -06:00
Josh HawkinsandGitHub 64d6366ac4 Live streaming tech selection (#24374)
* allow users to select live streaming technology

* fix webrtc being downgraded to mse on load

`useUserPersistence` seeds state with the default and loads asynchronously, so the first render always used `mse` instead of the saved choice, and `useWebRTCGloballyAvailable` reports `checking` until the probe settles and re-enters that state on every consumer mount, so a saved `webrtc` was rewritten to `mse` even after the probe had already passed. On Safari the MSE player then timed out and latched the jsmpeg fallback. A pending probe now counts as available, the player waits on `autoLive` until the stored preferences load, and `handleError` gates on the mode in use since the fallback flag no longer implies webrtc is untried. A rejected IndexedDB read also resolves `loaded` now, so a blocked store can't leave the player waiting forever.

* add support for configurable ICE servers in WebRTC player

* add mic error state, fix dialog overwriting saved choice and dashboard ignoring stream

* tweaks
2026-09-16 09:12:06 -05:00
Josh HawkinsandGitHub 79ca18d439 fix build warnings from tailwind, fonts, and fast refresh (#24359)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
fix duplicated styles, move fonts to src/assets/fonts for vite to bundle (nginx already rewrites correctly), and fix fast refresh for PreviewController, auth context/provider, and statusbar context
2026-09-15 15:33:09 -06:00
Josh HawkinsandGitHub cabdffea20 Update more web deps and reformat with updated prettier (#24358)
* update date-fns, i18next, react-dropzone, react-markdown and @types/node

react-i18next 17 peers `i18next >= 26.2.0`, so the two move together. react-day-picker already depends on date-fns 4, so date-fns now dedupes to a single copy. react-dropzone 20 declares `node >=22` in `engines`, but npm only warns on Node 20 and nothing in the build needs Node 22.

* update js-yaml, @hookform/resolvers and prettier

js-yaml 5 has no default export, so `DictAsYamlField` imports `dump`, `load` and `YAMLException` by name. @hookform/resolvers 5 types `zodResolver` with the schema's input and output types separately, and fields with defaults are optional on input, so the zone and classification model forms pass both types to `useForm`. zod's range now starts at 3.25, which resolvers 5 requires.

* format with prettier 3.9
2026-09-15 15:18:49 -06:00
Josh HawkinsandGitHub ba41c90c07 Remove unused deps (#24355)
* remove unused web test deps

Nothing runs vitest. The CI step that called `npm run test` is commented out, `web/__test__/` was deleted in https://github.com/blakeblackshear/frigate/pull/8983 so `setupFiles` points at a missing file, and there are no unit tests, so `npx vitest run` only picks up the Playwright specs and fails. jsdom, `@testing-library/jest-dom`, msw and fake-indexeddb were only there for vitest.

* update contributing docs

* remove unused deps
2026-09-15 14:02:02 -06:00
Josh HawkinsandGitHub 0c52a3175d update radix, react, konva and other web dependencies (#24354)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
konva 10.5 removed the private `Node._lastPos`, so `PolygonCanvas` now reads the dragged point from `getAbsolutePosition()`, which returns the position konva just applied. monaco-yaml 5.5 takes formatter options instead of a boolean for `format`. The radix packages move together so every shared primitive stays a single copy under the `react-slot` and `compose-refs` overrides.
2026-09-15 12:39:31 -05:00
Josh HawkinsandGitHub 4c648f8147 bump copy-to-clipboard to 4.0.2 (#24353) 2026-09-15 12:03:20 -05:00
Josh HawkinsandGitHub 4100383738 Miscellaneous fixes (#24352)
* fix recordings unavailable endpoint when no params are provided

we already import the datetime class directly, so those attribute lookups raised AttributeError and the request returned 500

* reject JWTs whose role is no longer in the config

`/auth` trusted the role inside the JWT and re-signed it on refresh without checking the config, so a user whose restricted role was deleted kept a session carrying a role that isn't in `auth.roles`. The media, clip, recording, export and go2rtc checks treat a missing role the same as a role with no camera list, so that session could open every camera. `/auth` now returns 401 for a token whose role isn't configured, which sends the user back through login, and login already falls back to `viewer` for a role that's gone.

* delete the deleted camera group's layout, not the open one's

Deleting a camera group removed the layout of the group being viewed, because the dialog's delete was bound to `${activeGroup}-draggable-layout`. It also cleared the saved group, and both ran before the `config/set` request whether or not it succeeded, so deleting one group while viewing another lost the open group's layout and left the deleted group's layout behind. The deleted group's own layout is now removed after a successful save, and the saved group is only cleared when it was the open group.

* don't block API when querying PTZ info

camera_ptz_info is async but waited on the ONVIF controller's future with future.result(), blocking the API event loop for as long as a slow or unreachable ONVIF camera took to answer (including reconnect attempts), so every other async request stalled with it. Await the future with asyncio.wrap_future instead. The coroutine runs on the controller's own loop and thread, so this cannot deadlock.

* for custom exports, only allow admin users to add to existing cases

follows the existing convention where attaching an export to an existing case is admin-only on `POST /export/{camera}/...` and `POST /exports/batch`

* drop pending edits for a camera or profile that no longer exists

* match cached preview frames to their camera exactly

https://github.com/blakeblackshear/frigate/pull/22594 added a trailing `-` to the `preview_{camera}` prefix so `camera` stopped matching `camera2`'s frames, but camera names can contain `-`, so `front` still matched `front-door`'s. After a restart, `front`'s preview recorder deleted this hour's frames of a matching camera that sorted before it and added the timestamps of one that sorted after it, so ffmpeg was asked for files that don't exist and that hour's preview was lost. The offline fallback for `latest.jpg` could also return `front-door`'s frame for `front`, even to a user without access to `front-door`, and a short export could take its fallback thumbnail from the other camera. These now compare the full camera name taken from the file name.
2026-09-15 07:48:30 -05:00
Josh HawkinsandGitHub fa30a7e1ae Replace react-logviewer with virtua (#24334)
* bump react-logviewer to 6.5.5

* use virtua for UI logs
2026-09-15 05:44:04 -06:00
Josh HawkinsandGitHub eecc43ccef bump rjsf to 6.10.0 and add e2e test (#24332)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-09-14 13:36:27 -05:00
Josh HawkinsandGitHub caa6edecac Migrate web to ESLint 10 flat config (#24326)
* migrate web to eslint 10 flat config

ESLint 10 dropped `.eslintrc` support, so `.eslintrc.cjs` is replaced with `eslint.config.js` and the lint scripts no longer pass `--ext` or `--ignore-path`. typescript-eslint moves to 8, react-hooks to 7, and react-refresh to 0.5, and the unused jest and vitest-globals plugins are removed. Lint behaves as it did before: catch variables aren't checked, unused disable directives aren't reported, and rules newly added to the recommended sets are off until the code passes them. typescript-eslint 8 flags constants used only in `typeof`, so those are now exported, or replaced with a union type where the export would trip react-refresh.

* fix lint findings from the eslint 10 recommended rules

Remove the rule overrides from the flat config migration and fix what they were hiding. Unused catch bindings are dropped, 20 disable directives that suppressed nothing are removed (react-hooks 5.2 and 7.1.1 report identical exhaustive-deps findings with inline config ignored), dead initial values are dropped, short-circuit calls become if statements or optional calls, rethrown errors pass `cause`, and the disabled "No recordings" tooltip in `ReviewTimeline` is removed along with its memo and the `getRecordingAvailability` prop. The 3 react-refresh warnings for files that export contexts or classes are left for a later refactor.
2026-09-14 07:53:45 -06:00
Josh HawkinsandGitHub acc740a976 Update deps (#24324)
* update docusaurus to 3.10.2

* bump @types/node to 25.9.6 and ES2022

target ES2022, which already includes ES2020 and ES2021.String, so the lib list was also trimmed

* bump vite to 8.3.0 and vitest to 4.1.11

Swap `@vitejs/plugin-react-swc` for `@vitejs/plugin-react` and add `esbuild` as a devDependency, since `vite-plugin-monaco-editor` requires it and Vite 8 no longer ships it. `keepNames` moves to `build.rolldownOptions.output` because Vite 8 ignores the `esbuild` block. Rolldown's minifier now writes the preload helper's base path as a template literal instead of a double-quoted string, so the nginx `sub_filter` for `return"/BASE_PATH/"` stopped matching and lazy-loaded chunks and their CSS were requested from a literal `/BASE_PATH/` under Home Assistant ingress. The rule now matches the backtick form.

* bump apexcharts to 7.3.0 and react-apexcharts to 2.1.1

Under Vite 8, a default import from a CommonJS package resolves to its whole `module.exports` when `package.json` has `"type": "module"`, so react-apexcharts 1.4.1 handed React an object and every chart crashed. 2.x ships an ESM build. apexcharts 7 no longer sets `window.ApexCharts`, so the chart components now import it for `ApexCharts.exec`, and `ApexAxisChartSeries` is derived in `types/graph.ts` because it's no longer a global type.

* remove unused immer dep

* remove unused cython pin from tensorrt requirements

The pin was added alongside tensorrt 8.5.3 and cuda-python 11.8, which needed Cython to build, and both have since been dropped from this file. Nothing imports Cython at runtime, and `pip3 wheel` runs with build isolation, so any source build gets its own build dependencies. The pin only installed an unused Cython wheel into the TensorRT image.

* require node 20.19 for docs
2026-09-14 07:16:31 -06:00
Josh HawkinsandGitHub 3931ab74a8 fix mypy errors from types-peewee 4.0 (#24323)
types-peewee 4.0 types model fields precisely, so 17 `type: ignore` comments and 2 `cast(str, ...)` calls are no longer needed. Its stubs type `.namedtuples()` and `.dicts()` queries as returning model instances, so the review cleanup reads namedtuple fields by name and the storage usage query casts its dict rows. `start_time` is declared `DateTimeField` but stores unix timestamps, so two reads cast it like `debug_replay.py` already does. `Export` gets an annotation for the `export_case_id` attribute peewee adds at runtime.
2026-09-14 07:52:23 -05:00
06ff2ced5d bump cryptography to 46 and pin py-vapid to 1.9.4 (#24292)
Co-authored-by: t <t@t>
2026-09-13 15:52:19 -06:00
4b71cb76dd bump i18next-http-backend to 4.0.2 (#24290)
Co-authored-by: t <t@t>
2026-09-13 15:05:41 -06:00
Josh HawkinsandGitHub a9eb286db9 Tweaks (#24260)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* update versions in discussion templates

* make /run writable by the runtime user under docker's user
2026-09-12 17:07:02 -05:00
Josh HawkinsandGitHub 9cba1c2963 remove deepstack detector and all references to it (#24259)
the 0.18 release notes indicated this was being removed in 0.19
2026-09-12 16:20:50 -05:00
Josh HawkinsandGitHub d59c28e53a Add command menu to frontend (#24256)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* add command menu to quickly jump between pages, cameras, settings, and quick actions

* access tweaks
2026-09-12 10:57:17 -06:00
Josh HawkinsandGitHub 4861a668b1 Add error boundary to frontend (#24255)
* add error boundary

on frontend errors, show a page-level recovery panel and a separate compact strip for the sidebar/status/bottombar so failures there don't take down the rest of the page

* tweaks

* fix test
2026-09-12 10:32:22 -06:00
Josh HawkinsandGitHub dcfe5307c9 fix CI smoke test (#24254) 2026-09-12 10:12:57 -06:00
Josh HawkinsandNicolas Mowen 37f338d5d6 fix save_attempts trimming (#24246) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 7bc32fd4c9 Refactor Notices and System Health pane (#24243)
* refactor notices

* show startup message for enrichments in health pane

* tweaks
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen e3029beba5 Report skipped detections in status bar (#24223)
* report skipped detections in the system notices pane

* revert notices and move to status bar

* shorten string
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 70ce193e09 Improve System Health pane (#24188)
* build out system health pane

* tweaks

* fixes

* fix notice link so it opens the correct camera

* tweak language
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 6d33b31bc6 show disk space reclaimed by media sync (#24189) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen d6b93301e2 Miscellaneous fixes (#24179)
* rename migration

* use ASC composite index for event camera and start_time

the planner still picks for newest-first single-camera queries but doesn't slow down full per-camera scans on a cold cache

* pause playback while the timeline range handles are up

* reduce multi camera seeded export range to 30m

allows both handlebars to fit within most desktop windows
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen f3a31e2fb4 Add a notice registry and System Health tab (#24178)
* add a notice registry and System Health tab

Problems Frigate detects on its own (ffmpeg crash loops, stuck detectors, failed model downloads, recordings deleted before their retention period) only ever existed as log lines. This adds a `NoticeRegistry` in the main process backed by two tables, an `update_notice` IPC topic so producers in other processes can reach it through the dispatcher, an admin-only API and websocket topic, and a Health tab that lists them. Kinds declare their own mode, severity, and category in one place: state notices are resolved by their producer, event notices are dismissed by the user.

* treat a prerelease as behind its final release

* fix notices clearing early

* rename menu items and update docs

* don't resolve the update notice on a failed version lookup
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 6bd6d3383f fix hardware stats crash when audio transcription is enabled (#24164) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 9eef369dd0 fix latched loading spinner after cancelling a timeline selection (#24162)
isLoading and isBuffering only clear on playback progress, and scrubbing holds the player paused, so a source rebuild during a timeline selection left them set with nothing able to clear them. Cancelling made them visible as a spinner over an already-loaded frame, which stayed until the next manual seek. Leaving a scrub now clears them when the source is loaded and the element holds a frame.
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 53b04f44ef Fix preview players at the hour rollover (#24157)
* fix preview players at the hour rollover

* clean up
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 8370e205da add network isolation docs (#24148) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 9416e74aeb fix frozen time bounds on the recordings API (#24121)
`after` and `before` defaulted to `datetime.now()` in the function signature, so they were evaluated once at import. Requests that omitted them got a window ending at process start. They now resolve in the handler.
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen e2da7aae99 Add a deny option for the proxy default role (#24145)
* backend

* frontend

* docs

* fix none default role casing and name reserved roles in the error

* reserve every casing of none as a role name
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 41bc1a5844 Container security hardening (phase 4) (#24140)
* Support read-only rootfs with self-signed certs in /config/tls

* Support read-only rootfs in s6 and pre-compile bytecode

* Assert read-only rootfs support in CI

* Document hardened read-only deployment

* keep certsync's cert selection identical to nginx's

* note the uid trade-off in user: mode

* fail fast when EXTRA_GROUPS or a missing media volume meets read_only

* keep nosuid and nodev on the /run tmpfs

* support read_only in the default mode

* don't take go2rtc down when the homekit file isn't writable

* lead with the hardware consequence of switching to user:

* refuse to write TLS material through a symlink as root

* note that memryx writes models to the root filesystem

* certsync watches whichever cert path nginx loaded
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 3be59c9c18 Container security hardening (phase 3, breaking) (#24081)
* Run the frigate service as the frigate user

* Run go2rtc as its own restricted user

* Run nginx as the frigate user with writable state in /tmp/nginx

* Disable bandwidth stats gracefully when not running as root

* Hand TensorRT model cache ownership to the runtime user

* Document non-root operation and per-hardware device access

* Create /media/frigate after the ownership sweep

* Assert non-root services, JWT migration, and escape hatch in CI

* only write the sweep sentinel when a media volume is mounted

* tolerate homekit config chown failures in the go2rtc run script

* chown the s6 log pipe so non-root nginx can reopen /dev/stdout

* set HOME to /config for non-root services

* run smoke nginx -t and the write probe as the runtime user

* re-own the nginx shm cache on service restart

* discard stdout for the unprivileged smoke nginx -t

* unwrap hard-wrapped prose in the installation docs

* report progress during the ownership sweep

* document EXTRA_GROUPS as the only device access path for dropped services

* expand the non-root device access docs with diagnosis steps and udev rules

* document network storage ownership and the remaining detector hardware

* skip lost+found during the ownership sweep

* hand /tmp/cache to the runtime user before services start

* make bundled models readable by the runtime user

* reload nginx by signaling the master instead of parsing its config as root

* harden root writes into unprivileged-owned paths

Restrict the sweep sentinel to a mount at or below /media/frigate so a
parent /media mount cannot bless a later-shadowed volume. Rebuild
/tmp/nginx root-owned each start so root's cp and tempio writes cannot
follow a symlink an unprivileged nginx planted in the previous run.

* collapse the duplicated sentinel comment

* add a service-runs-as-root helper for granular root services

* validate FRIGATE_ROOT_SERVICES and fail fast on unknown names

* let services listed in FRIGATE_ROOT_SERVICES skip the privilege drop

* record the root-services mode in the sentinel and sweep small trees each boot

* cache the runtime ids in the ownership helper

* chown recordings, previews, and exports to the runtime user at create

* chown the database files after init

* recommend FRIGATE_ROOT_SERVICES in the bandwidth stats warning

* assert granular root services in CI

* document FRIGATE_ROOT_SERVICES

* own every directory level created for a recording segment

* clear the cached runtime ids when ownership tests finish

* skip missing media paths in the per-boot ownership sweep

* clarify granular root services docs

* clean up

* install acl for device access grants

* grant runtime users access to mapped device nodes at boot

* assert device access grants in CI

* document automatic device access grants

* stop telling users device access needs host side setup

* clarify the non-root docs

* link the migration script to the repo

* group the manual device setup under one section

* harden against symlink attacks

/config is owned by the unprivileged runtime user after the ownership sweep, so root operations on files there could be redirected by a planted symlink.

- go2rtc HomeKit setup: replace the root yq/jq normalization and chown with an O_NOFOLLOW helper (prepare_homekit.py), so a symlink at go2rtc_homekit.yml can't redirect a root write or chown onto another file
- go2rtc binary override: ignore /config/go2rtc whenever the service runs as root, so a planted binary can't exec as root under FRIGATE_ROOT_SERVICES
- sweep sentinel: read and write it through safe-sentinel, which trusts only a root-owned regular file and never follows a symlink, so it can't be forged to skip the migration or symlinked to clobber a root file
- ownership sweep: chown with -execdir so a parent directory swapped for a symlink mid-walk can't redirect the chown out of the volume
- validate inputs: restrict DEVICE_ACL_PATHS to /dev, require nonzero numeric EXTRA_GROUPS, and reject PUID/PGID that collide with the go2rtc ids
- docs: correct the TLS key ownership note to match what actually happens

* tweak docs

* stop the ownership sweep chasing entries other mechanisms own

* keep custom binaries out of root services only under granular root
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 1693415375 switch nginx-vod-module to the maintained dio-az fork (#24123)
The `v1.x` line is the same muxed fMP4 code as Kaltura's 1.31 with fixes backported, so the mapping JSON, manifest routes, and ffmpeg consumers are unchanged. The `MAX_CLIPS` patch applies as-is and the HEVC workaround is rewritten for the fork's reformatted source.

`vod_hls_version 6` is now explicit because the fork replaced Kaltura's automatic version calculation with a directive that defaults to 4 and only warns when fmp4 needs 6, so playlists were being stamped `EXT-X-VERSION:4` while carrying `EXT-X-MAP`. The `error_page 502 =404` hack is gone: https://github.com/kaltura/nginx-vod-module/issues/468 is a `vod_mode remote` bug and we're `mapped`, so those 502s were really `_vod_response` returning 404 upstream. The fork maps that through now, and the hack was also turning real 5xx into "no recordings".
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen aaf18b81b2 Fix LPR vehicle message for multiple models (#24119)
* use the camera's model for the lpr vehicle check

`FrigateConfig.model` became `models[]` in the detector refactor, so this didn't compile on 0.19. Each camera has its own detector/model pair now, so the check resolves the camera's scene with `getModelForCamera` instead of looking at every model.

* use camera config model

* fix export test
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 98706482f5 Add Apple compatibility switch to the camera wizard (#24115)
* add apple compatibility switch to the camera wizard

* don't require every record stream to be h265
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen fb8ab56c41 Add onboarding wizard for new installations (#24102)
* add onboarding wizard for new users

* resolve hwaccel per camera and clarify recording retention

The hwaccel step listed every preset Frigate ships, so an Intel box was offered Raspberry Pi and Rockchip decoding, and the codec specific presets (`preset-intel-qsv-h264` vs `-h265`) were offered as global values that break as soon as two cameras use different codecs. `/hardware/hwaccel` now returns the decoding families the probed hardware can actually use, each carrying a preset per codec, and the wizard resolves the family against the detect stream codec the camera wizard already probed: one global `ffmpeg.hwaccel_args` when every camera agrees, per-camera `cameras.<name>.ffmpeg.hwaccel_args` when they don't. The global stays on `auto` in that case so cameras added later still resolve at startup. A gen13+ Intel machine keeps its QuickSync recommendation with mixed h264 and h265 cameras instead of dropping to vaapi.

The recording step's "Days to retain recordings" only wrote alert and detection retention, and the storage estimate under it assumed continuous recording. It now asks what to record in plain language, writes `record.continuous.days` to match, shows the estimate only for continuous, and drops the spinner arrows on the number input.

* clean up

* add light/dark mode icon switcher

* use yml as default config file extension when not found

* i18n tweaks

* gate the setup wizard on cameras instead of a config key

* render setup wizard steps by key

* share the setup wizard e2e helpers and mock users

* add an account step to the setup wizard

* add setup wizard account step e2e coverage

* cover the account step's restart behavior

* button consistency

* fix test

* docs

* fixes
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 96013a0487 Fix 500 when an event thumbnail file is empty (#24110)
* fix 500 when an event thumbnail file is empty

* fix test
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 0aa086eefd Fix inconsistent export download filenames (#24111)
* fix inconsistent export download filenames

Zip entries in a case download were named from `Export.name`, the friendly display name, while an individual download uses the file name on disk. The two have always been formatted differently, so one export came out as `front_door_20260823_020615-20260823_020734_abc123.mp4` on its own and `front door 2026-08-23 020615 2026-08-23 020734.mp4` inside a zip. Zip entries now use the on-disk file name, and renaming an export renames its file, so there's only one name to download under. The rename is blocked while ffmpeg still holds the file.

* cap filename length and catch duplicate names

* fix export rename and stop blocking the event loop

* move the rename rollback off the event loop

* no awaits
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 53fb6c8daa Recording fixes (#24072)
* pin genai review frames to the main stream

* retain previews as long as either stream has recordings

* watch sub stream recording health separately from main

* reject record_sub on the same input as record and document the role

* derive recording paths from the cache segment timestamp

Recording paths carry one second of resolution, but since sub stream recording start times are resolved to fractional wall clock, anchored to the cache file mtime and chained to the previous segment's end. A stream cutting segments faster than once a second resolves consecutive segments into the same second, so two rows collide on the unique path index and the batch insert fails. The cache segment name is unique per camera stream and second by construction because ffmpeg names segments with strftime, so the recording path is now built from that timestamp while the row keeps the resolved start time. This also restores the path semantics from before sub stream recording, when start times came straight from the cache filename.

Nothing derives times from recording paths: playback offsets, stream switching, and export all use the row's start time, which is unchanged, and the recordings sync matches files by exact path string.

* keep the rest of a recording batch when one row conflicts

* only publish record_sub status when a sub stream is configured

* don't shadow camera_cfg when publishing empty cache streams

* back off restarts when a recording stream goes stale

* give the shared sub stream grace on any capture thread reset

* include segment details in recording discard warnings
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 40793b8478 Container security hardening (phase 2) (#24068)
* Create frigate and go2rtc runtime users in the image

* Add single fix-ownership helper for volume permission migration

* Add init-usermod oneshot for PUID and PGID remapping

* Chown newly created runtime directories to the frigate user

* Run sentinel-guarded ownership sweep during prepare

* Add host-side volume permission migration script

* Guard log directory ownership for user-mode startup

* Fall back to plain s6-log when running without root

* Assert PUID remapping and sweep sentinel in CI smoke test

* Skip the ownership sweep in the devcontainer

* Pin FRIGATE_RUN_AS_ROOT in ownership tests

* Do not record the sweep as complete when a chown failed

* Validate PUID and PGID in the migration script

* Treat a failed ownership scan as an incomplete sweep

* Reject PUID and PGID of 0 during remapping

* Handle symlinks, dry runs, and sentinel write failures in the sweep

* Treat an absent sweep root as an incomplete sweep
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen c004d0a1c9 Container security hardening (phase 1) (#24061)
* Verify s6-overlay downloads against pinned checksums

* Verify go2rtc download against pinned checksums

The v1.9.14 release publishes no checksums file, just the bare per-platform binaries, so these digests come from a one-time fetch rather than upstream. That pins the artifact against later substitution, which is the realistic threat for a version we stay on for months, but it does not verify the original download. The stage moves from `ADD --link` to a script because `ADD --checksum` can't express an architecture-dependent URL.

* Verify main image downloads against pinned checksums

Covers everything the main image downloads on the default path: tempio, the hailort runtime tarball and wheel, the six ffmpeg builds, the libedgetpu deb, and the thirteen Intel driver debs. The hailort tarball was streamed straight into `tar`, which can't be verified before extraction, so it downloads to `/tmp` first. The three ffmpeg blocks per arch collapse into one `install_ffmpeg` helper since they only differed by URL and install dir, and the Intel debs go through a `fetch_intel_deb` helper for the same reason.

The Intel debs are the ones that mattered most here. They're installed as root with `dpkg` on the default amd64 path and had no verification at all. compute-runtime publishes a `ww<week>.sum` asset with every release and npu-driver published `checksum.sha256` on v1.19.0, so those eight digests came from upstream rather than from us. intel-graphics-compiler and level-zero publish none, so those five and everything else here come from a one-time fetch, which pins the artifact against later substitution but doesn't verify the original download. The comment above the map says which is which and how to refresh them, since npu-driver has stopped publishing sums since v1.19.0 and that provenance won't survive the next bump.

Still unpinned: `get-pip.py`, which is a rolling URL where a digest would just break the build on pypa's next edit, and the per-variant artifacts for Axera, Synaptics, and Jetson. apt repositories are out of scope since apt already verifies signatures.

* Restrict generated TLS key permissions

OpenSSL 3.x already writes the key at 600 on its own, so this pins the guarantee rather than fixing an observed leak: the mode no longer depends on the openssl version or the umask the service happens to start with. Only the generated pair is touched. User-mounted certs take the other branch and are never chmod'd, which matters when they're mounted read-only.

* Add security headers and server_tokens off

Adds `X-Content-Type-Options: nosniff` and `Referrer-Policy: strict-origin-when-cross-origin`, and turns off nginx version disclosure.

No `X-Frame-Options` and no CSP `frame-ancestors`. HA's Webpage card and iframe panels frame Frigate's own address cross-origin, and either header would break them silently with nothing in Frigate's logs to explain it. Ingress is same-origin and would survive `SAMEORIGIN`, but Frigate can't tell the two apart from inside the container. `security_headers.conf` is a plain file in the image rather than a generated one, so anyone who does want framing restrictions can bind-mount it.

`add_header` doesn't inherit into a block that declares its own, so the include goes in per block, all nine of them, including the four nested static-asset locations that serve the JS bundles. Those are the ones nosniff actually matters for.

The run script now reads `get_nginx_settings.py` once into a variable instead of shelling out per template. That script imports the frigate config machinery, which is noticeable on an SBC.

Not fixed here: `listen.conf` is included at server level and carries `Strict-Transport-Security`, so those same nine blocks already drop HSTS under TLS today. Folding it into this file would change existing TLS behavior on nine paths, so it needs its own PR.

* Restrict go2rtc config file permissions

* Log failed login attempts with source address

Failed logins returned a bare 401 and left nothing behind, so credential stuffing was invisible unless you were already watching nginx access logs. Both failure branches now log a warning with the attempted username and the client address.

The address comes from `get_remote_addr()`, the same helper the login rate limiter keys on, so the two agree on who the client is and the trusted-proxy handling is consistent. Logging a raw `x-forwarded-for` instead would let an attacker forge the source address in the very log line meant to catch them.

The response is unchanged and identical either way. Which factor failed is only visible in the log, never to the client, and the password is never logged.

* Recommend least-privilege container options in install docs

The compose generator pushed `privileged: true` into every file it produced, no matter what hardware you picked, and it's the default tab on the install page so it's what most people copy. It now emits `security_opt: no-new-privileges:true` instead, and only adds `privileged: true` for hardware that actually needs it, with the reason inline. MemryX is the only one today, since it needs to reach the max-manager. Rockchip and Synaptics only want privileged during initial setup and their documented end state is device mappings, so neither gets it.

`no-new-privileges` merges into the same `security_opt` block as any device-specific entries, so Rockchip still gets its `apparmor=unconfined` and `systempaths=unconfined` without a duplicate key.

The static example now has `privileged` commented out, and there's a short section on the options worth adding, with a note that `cap_drop: ALL` breaks `telemetry.stats.network_bandwidth` since nethogs needs NET_ADMIN/NET_RAW.

* Add amd64 container smoke test to CI

Boots the built amd64 image against a minimal config and asserts the two security headers, that the Server header no longer carries a version, that no frame-ancestors is present, that nginx accepts its own config, and the two file modes. This is also the harness the rest of the hardening work extends.

The two negative assertions are written as `if grep; then exit 1; fi` rather than `! grep`. Bash exempts a negated command from `set -e`, so the `!` form would have passed even with the version and frame-ancestors both present, which is the opposite of what a regression net is for.
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 396a2156b2 Tweaks (#24067)
* improve keyframes messages

* don't pad the labelmap with unknown

`load_labels()` prefilled 91 `unknown` entries before reading the label file, so any model with fewer than 91 classes kept that padding in `merged_labelmap` and `unknown` showed up as a selectable object type in the objects settings UI. The padding only existed so `RemoteObjectDetector.detect` could index the labelmap without a KeyError, and it didn't even cover the empty-file case or Frigate+, which never had a prefill. Both lookups now skip class ids the labelmap doesn't name and warn once per id.
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 853840dfd4 Add secrets.yaml and unify variable substitution sources (#24044)
* add secrets.yaml and merge substitution sources by precedence

FRIGATE_ENV_VARS was built once at import from container env and /run/secrets, and the environment_vars validator overwrote it unconditionally, so the block beat the deployment and nothing could be re-read. Sources are now separate dicts merged lowest to highest (environment_vars, secrets.yaml, container env, credentials directory), re-read at the top of every parse, and a collision warns once naming the winner. An undefined {FRIGATE_*} raises a ValueError subclass so pydantic reports the field instead of a KeyError traceback.

* use the shared substitution namespace in go2rtc config

The generator rebuilt the namespace itself from os.environ and a hardcoded /run/secrets, so it never saw environment_vars or CREDENTIALS_DIRECTORY, and str.format made any stray brace fatal. It now installs the FRIGATE_ names from environment_vars and substitutes streams the same way every other field does.

* read the exec override from an import time snapshot

environment_vars is exported into os.environ, and is_go2rtc_arbitrary_exec_allowed read os.environ live, so the config file could enable exec sources. Snapshot the variable at import, which runs before any config is loaded.

* docs

* clarify docs
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen c9c6a923ef add recognized plate picker to lpr known plates in settings (#24059) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen a16ca285f7 fix clip download deadlock from unread ffmpeg stderr (#24032)
ffmpeg's stderr was piped but never read, so recording segments that generate more than 64 KB of ffmpeg warnings blocked ffmpeg mid-write, stranding the streaming thread and its anyio threadpool token for good. Enough of those and every sync endpoint stops responding until restart. The trigger is how noisy the segments are, not how long the clip is.

Send stderr to a temp file instead, and guarantee ffmpeg teardown and playlist cleanup on every exit path, including client disconnect.

Also fixes two bugs the deadlock hid: the failure branch was unreachable because returncode is None mid-loop, so the playlist file leaked and ffmpeg's logs were never reported. Playlist files now get a unique name so concurrent requests for one range cannot delete each other's input.

Extracts the terminate helper motion search already had into frigate/util/ffmpeg.py, now shared by both streaming call sites.
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 46796fe9ef fix the model lookup KeyError for cameras added at runtime (#24026) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 257a05a7e2 Add import/export for camera group layouts and per-camera streaming settings (#24025)
* add import/export for camera group layouts and streaming settings

Camera group layouts and per-camera streaming settings are stored in the browser's IndexedDB, so they are tied to a single browser on a single device. Users with more than one device have to rebuild every group layout and re-pick every camera's stream settings by hand, and clearing browser data loses the work.

Add a Backup & Restore card to Settings > UI Settings that exports these settings to a JSON file and imports that file on another device. Import shows a confirmation dialog with per-section counts, switches for layouts, streaming settings, and UI preferences, and warnings about camera groups or cameras in the file that are not on this server.

Server-side storage is deliberately avoided. These are per-device presentation settings: a layout arranged for a desktop is wrong on a tablet, and continuous full-resolution streams that are free on a wired LAN are not on a phone. An explicit file moves settings only when the user chooses to move them.

Implementation notes:

- web/src/utils/uiSettingsTransfer.ts owns a registry of transferable IndexedDB keys. Each entry records whether the key is user-namespaced, matching which persistence hook wrote it, plus a zod schema for its value.
- Only registry-known keys are ever written, and only when their value passes that schema. The file format deliberately lets unknown keys survive parsing, so this filter is what prevents a hand-edited file from writing arbitrary storage keys or out-of-range values.
- Export falls back to the legacy un-namespaced key, because the username migration runs lazily on first mount of each owning hook.
- Streaming settings merge per group rather than replacing the whole map, so groups configured only on the receiving device survive.
- Import writes storage and then reloads, because useUserPersistence reads a key only on mount and StreamingSettingsProvider would otherwise write its stale in-memory state back over the import.
- playbackBandwidthEstimate, frigate-search-history, and live-layout are excluded: the first two are measurements and user data rather than preferences, and live-layout's default is derived from the device.

* merge imported streaming settings per camera instead of per group
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 3f20209339 Base emergency cleanup on the streams a camera is currently recording (#24022)
* gate emergency cleanup bandwidth on the streams a camera currently records

* settle bandwidth samples per stream instead of per camera

* fix mypy
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 6ba9dd92e4 Show main and sub stream usage separately in Storage Metrics (#24015)
* backend

* frontend

* docs

* test

* report null instead of 0 for a stream with no cached bandwidth sample
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen ad04101c76 Refactor MQTT (#24010)
* refactor mqtt so that Frigate owns the transport lifecycle instead of delegating it to paho

* release the shutdown barrier on worker crash and replay retained publishes the broker never acked

* collapse in-flight retained values by topic and release the shutdown barrier from a finally

* replay the outage buffer before the publish queue so newer values are not reverted
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen f4cf1e1539 Refactor birdseye activity modes as a list and add alerts/detections (#24012)
* backend

* tests

* frontend and i18n

* e2e test schema

* docs
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen e77155a4f1 Improve History's seek startup time and recordings query performance (#24011)
* serve a segment startup ladder so seeks begin playing sooner

nginx-vod was handed one 10s segment per recording file, so every playlist start had to download and decode a full segment before the first frame. Declare real keyframe data per clip and let nginx cut short leading segments from it.

- add vod_bootstrap_segment_durations 1000/2000/4000 so each playlist starts with 1s/2s/4s segments before settling at 10s
- emit real clip-relative keyFrameDurations (plus firstKeyFrameOffset when nonzero) from the recording keyframe index; rows without an index keep the whole-clip declaration, the only safe cut without keyframe knowledge
- drop the manifest's segment_duration field, which was always inert: nginx-vod parses only camelCase segmentDuration
- rebuild the player source at the seek target, quantized to a 10s grid, so the ladder applies to every seek and seek URLs stay repeatable for nginx's mapping and response caches
- route the seek model, in-range checks, and the stale-report guard through the source window rather than the chunk range
- bridge repositioning seeks (>2s from the last played timestamp) through the preview player and hold the release anchor one commit, so neither path paints a stale frame
- clear a pending loading timer before replacing it; an orphaned timer escaped onPlaying's clearTimeout and flashed loading mid-playback

* keep recordings queries on their indexes

Several recordings queries degraded into full scans or large sorts on big databases: the planner ignored index order, or the query shape gave it nothing tight to seek on. Reshape them into bounded seeks and add the composite index the per-stream lookups need.

- index recordings on (camera, stream_type, start_time DESC) and drop the (camera, stream_type) index it supersedes
- walk the recordings summary day by day with EXISTS probes and per-camera MIN/MAX seeks, skipping ahead over empty gaps instead of bucketing every row for the requested cameras
- run the summary endpoint on the event loop rather than the threadpool
- bound the unavailable-recordings query by start_time per camera and merge the results in Python
- bound the expire query's start_time so it seeks the retention window instead of scanning a camera's whole history
- enumerate deleted cameras with one index seek each rather than a camera NOT IN (...) scan
- compute bandwidth with segment_size filtered in a CASE projection; as a WHERE predicate it baited the planner into the (camera, segment_size) index plus a full sort of the camera's history
- fall back to a 1000-segment window when the recent 100 are all zero-size, so an ingest glitch doesn't report zero bandwidth
- limit the needs_refresh count instead of counting every segment
- cover sub-only and sparse calendar days, midnight-spanning day attribution, multi-camera gap merging, deleted-camera expiry, and zero-size segment runs

* fix mypy
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 50e7b76eb5 Enable PTZ control setup in the Add Camera Wizard (#23444)
* add ptz controls to camera via wizard when onvif has already been probed

* i18n

* add e2e test

* backend add and remove subscriber

* tweaks

* turn on switch by default if pan and/or tilt capability is available

* fix test
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 1498231eb9 Add sub stream recording with adaptive quality playback (#24009)
* add sub stream recording with adaptive quality playback

Optionally record a second, lower bitrate stream alongside the main
recording stream via a `record_sub` input role and `record.sub` config block, with its own retention windows.
Recordings rows now carry the stream type plus the media details needed to serve both streams from one manifest: video codec, audio presence, audio codec and rate, and a record-time keyframe index.

Playback resolves coverage across both streams and merges them into a single VOD sequence, falling back to a discontinuity manifest with per-clip init segments when the media signatures differ. The player exposes a quality selector, and an auto governor picks the stream from stall time, bandwidth, codec support, and the save-data hint.

* fix tests and i18n
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen f7c5500ea8 stop creating a config subscriber per capture thread (#24002) 2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen d78f6a7a98 Guard lookups when adding/deleting cameras at runtime (#23994)
* Guard object processor queue handlers against unknown cameras

* Skip embeddings post processing for removed cameras

* End review segments for removed cameras

* Drop queued autotracker moves for removed cameras

* Release tracked event thumbnails when skipping a removed camera

* Add locked accessors for camera states

* Read camera states through the processor accessors

* Guard output and recording paths against cameras not yet known

* Resolve camera state once in ONVIF, notification, and transcription paths
2026-09-12 07:30:04 -06:00
Josh HawkinsandNicolas Mowen 7841d41bea Fix birdseye layout overlap with mixed landscape/portrait cameras (#22917)
* fix birdseye layout calculation

replace the two pass layout with a single pass pixel space algorithm

* add test
2026-09-12 07:30:04 -06:00
Josh HawkinsandGitHub 4ac92dac72 Sync PWA status bar theme-color with resolved app theme (#24203)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* sync PWA status bar theme-color with resolved app theme

* update tags on login page too

* track system theme preference

* revert
2026-09-05 15:57:39 -06:00
Josh HawkinsandGitHub 287fc42404 Miscellaneous fixes (#24172)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* fix frigate+ submission state bleeding onto the next tracked object

* add Korean

* fix tests
2026-09-03 06:44:18 -05:00
Josh HawkinsandGitHub a529656a90 Fix jumping timeline handles in debug replay range selection (#24158)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* fix jumping timeline handles in debug replay range selection

The timeline mirrored the export range into local state that was never reset when the range cleared, so starting a second selection after cancelling one left the stale mirror disagreeing with the new range. The sync effect and the draggable position effect then rewrote each other's values every commit until React hit its update limit. The handles now write the range directly.

* add mobile test
2026-09-01 20:17:29 -05:00
Josh HawkinsandGitHub d37dff1b49 Docs update (#24131)
* fix incorrect backchannel docs

`#backchannel=0` is a native rtsp source param, not an ffmpeg one, and the troubleshooting warning had it listed as ffmpeg-only. Any `#` modifier on a bare `rtsp://` source turns the backchannel off unless the URL explicitly contains `#backchannel=1`, so a dedicated two-way talk stream can't carry `#video=h264` or anything else.

* add rotation faq
2026-08-29 07:01:11 -06:00
Josh HawkinsandGitHub ca18b8dc13 fix export case download with non-ascii names (#24100)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-08-26 14:42:34 -06:00
Josh HawkinsandGitHub 5197881ef7 Add more vehicle types to default attribute map (#24097)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* run lpr on more vehicle types by default

before, a config change to attribute_map was required

* logging tweaks

* remove arg

* use attributes for frontend check

* docs

* only check thumbnail attributes the object can have
2026-08-26 08:23:15 -06:00
Josh HawkinsandGitHub 18c77faea5 fix classification attribute access for viewers (not custom roles) (#24092)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-08-25 14:40:26 -05:00
Josh HawkinsandGitHub 271051f15b don't migrate embeddings as a valid role for genai providers (#24086) 2026-08-24 12:05:57 -06:00
Josh HawkinsandGitHub 41bc24cce4 use extended graph optimization for jinav2 (#24079)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
The CUDA execution provider returns an identical vector for every image when jina-clip-v2 is built below ORT_ENABLE_EXTENDED, so every thumbnail embedding written on a GPU was the same normalized garbage and semantic search returned the same results for any query. Reproduced on two different NVIDIA cards, across onnxruntime 1.22 and 1.24, and on both the 0.17 and 0.18 CUDA stacks, so it isn't specific to any of those. ORT_ENABLE_ALL isn't an option because it fails to build on CPU with a SimplifiedLayerNormFusion error, leaving EXTENDED as the only level that works on both providers. jinav1 is unaffected and stays on BASIC.
2026-08-24 09:13:16 -05:00
Josh HawkinsandGitHub 0254a11874 Docs tweaks (#24074)
* add recording validation message explanations to docs

* tweaks
2026-08-24 06:04:40 -06:00
Josh HawkinsandGitHub 036bae4ea9 Return a specific 404 when starting a debug replay with no recordings in range (#24024)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
2026-08-18 10:08:52 -05:00
Josh HawkinsandGitHub 77fc2ce174 Miscellaneous fixes (0.18 beta) (#24016)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* fix classification drawer closing instead of scrolling when list is long on mobile

* add qwen3.8 to genai docs

* add titles to more clearly separate model types
2026-08-18 07:01:22 -06:00
Josh HawkinsandGitHub 8425a76558 Miscellaneous fixes (0.18 beta) (#23993)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* subscribe to add in webpush

* add docs for detector cpu usage

* rebuild notification camera access when a camera is added at runtime

* document how frigate shows CPU usage metrics

* add faq about version key in config
2026-08-16 12:39:28 -06:00
Josh HawkinsandGitHub 11f8786459 sanitize user-supplied path components (#23990)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
sanitize_filename leaves ".." intact and collapses variants like "..:" and "..*" to "..", so filesystem paths built from face names, classification model/category names, image ids, and trigger data could escape their base directory. Route every such site through new frigate/util/path.py helpers (safe_join, sanitize_path_component, sanitize_contained_path), which reject traversal and verify containment.

Worst case was DELETE /classification/{name}, which rmtree'd /media/frigate and /config while returning 200.

Important to note that all affected endpoints already require admin permission, so this sould be considered hardening rather than fixing exploitable code.
2026-08-13 21:59:46 -05:00
Josh HawkinsandGitHub 812e5308a3 fix notification suspend state lost on page reload (#23989)
<camera>/notifications/suspended arrives as a string over the live connection but as a number in the camera_activity snapshot, and the truthiness guard dropped the numeric 0, so a camera with notifications off rendered as active after a reload. Normalize to a string and derive isSuspended instead of storing it.
2026-08-13 16:51:44 -06:00
Josh HawkinsandGitHub fd98977506 Categorize manual events as alerts when their label is an alert label (#23981)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* Categorize manual events as alerts when their label is an alert label

* tweak docs
2026-08-13 11:16:02 -06:00
Josh HawkinsandGitHub c70a0802b8 filter dedicated LPR plates before creating the event (#23977) 2026-08-13 05:44:31 -06:00
Josh HawkinsandGitHub aff9799451 Don't require a restart to enable GenAI descriptions (#23964)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* create GenAI post processors when a camera enables GenAI at runtime

* fix types
2026-08-12 08:55:34 -05:00
Josh HawkinsandGitHub c75611b4df Multi-export UI fixes (#23959)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* multi export fixes

* i18n

* new tests
2026-08-11 10:11:47 -06:00
Josh HawkinsandGitHub 0735a8ac75 Docs updates (#23947)
* misc docs updates

* add warning about proxies to 5000 for notifications
2026-08-10 15:54:41 -06:00
Josh HawkinsandGitHub 2599795ab0 add faq to notifications docs (#23939) 2026-08-08 11:12:13 -06:00
Josh HawkinsandGitHub 344efb6bc1 Miscellaneous fixes (0.18 beta) (#23934)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* add host npu requirements to docs

* allow toggling live audio transcription via mqtt

* improve spacing consistency on mobile drawers

* fix clearing the region grid not surviving a restart
2026-08-08 07:20:09 -06:00
Josh HawkinsandGitHub e73a14db5d Miscellaneous fixes (0.18 beta) (#23898)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* update homekit docs

* update dictionary

* preserve function names in production builds

adds only 162kb gzipped/450k unzipped to the bundle

* margin tweak

* fix maximum update depth exceeded when dragging the timeline handlebar

Dragging the handlebar, especially quickly or with fast direction changes, could exceed React's nested update limit and unmount the whole app, leaving a blank screen. Motion search was worst affected.

The drag loop committed a new time into React state on every animation frame. Edge auto-scrolling mutates scrollTop each iteration, so the value always differed and React's same-value bail-out never engaged, letting the update chain run to the limit of 50. Pace those commits to one per 100ms and flush the pending value on release, so the drop position is still exact. The handlebar position and label are written to the DOM directly and remain at frame rate.

useUserInteraction dispatched state on every scroll and touchmove event; only commit on the leading edge.

Motion search also passed fresh array literals for the timeline's events, motion events and unavailable ranges, giving the segment memo and the drag effect new dependencies on every render. Both views also passed an inline arrow for onHandlebarDraggingChange, which is an effect dependency that calls setState.

* Verify motion search jobs belong to the requested camera

* Apply persisted profile and runtime overrides before workers start

Worker processes are handed a copy of the config when they start and only learn about later changes from the config_updater broadcast, which is plain ZMQ PUB/SUB with no queue, ack, or retained value, so a message published before a subscriber has connected is dropped and never re-sent. The persisted profile and the runtime camera toggles were restored only by that broadcast, at the very end of startup, so a worker that lost the race kept its yaml values for the rest of the session: audio detection kept running on a camera whose audio had been toggled off, even though /api/config, the UI, and the runtime state file all showed it disabled. Split both restores into a config half and a publish half. ProfileManager.restore_persisted_profile_to_config() and Dispatcher.reapply_runtime_state_to_config() now run right after init_profile_manager(), before the first worker starts, so every worker is handed a config that already carries both layers. ProfileManager.restore_persisted_profile() and Dispatcher.restore_runtime_state() still run at the end of startup: the recording, review, and embeddings processes start before the dispatcher exists, so the broadcast remains their only channel, and MQTT needs the retained switch states. Both config passes have to stay after init_profile_manager(), which snapshots the config as the no-profile base that deactivation resets to.

* End timeline drags on touchcancel
2026-08-05 07:40:24 -05:00
Josh HawkinsandGitHub 4883e20898 Pin the internal auth port to the value nginx bound at startup (#23909)
/auth grants anonymous admin to any request whose X-Server-Port matches networking.listen.internal, but it read that port off the live config while nginx binds its listeners once at container start and never reloads them, so any path that swaps the running config could move the trusted port without nginx moving with it. Saving networking.listen.internal equal to the external port applied immediately despite the restart-required warning, which handed unauthenticated admin to everything reaching the external port. Snapshot the port at app creation and compare against that instead, and reject a config whose two listeners share a port number, which nginx would refuse to start with anyway.
2026-08-05 07:39:56 -05:00
Josh HawkinsandGitHub 33c00a27e4 crop motion previews to the selected filter region (#23903)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
When a motion region filter is active, zoom each preview clip into the outer bounds of the selected cells instead of showing the full frame. Tiles take on the aspect ratio of the cropped region, clamped to avoid slivers when the selection is a single row or column, so the grid stays uniform. A "Crop to filter" switch in the preview settings turns this off and restores the previous 16:9 tiles. The transform is applied to a wrapper holding both the media and the dim overlay canvas so the motion heatmap stays registered to the pixels.

Fix the region filter grid, which mapped cells onto a hardcoded 16:9 box while the snapshot was letterboxed inside it with object-contain. Heatmap cells are indexed against the detect frame, so on a 4:3 camera every painted cell was off by up to 12.5% of the frame width, and the true left and right edges of the image could only be reached by painting the black bars. The grid box now takes the camera's detect aspect ratio, capped at 65dvh tall so 4:3 and portrait cameras do not overflow the dialog.
2026-08-04 08:07:06 -06:00
Josh HawkinsandGitHub 3b14ec0c87 Miscellaneous fixes (0.18 beta) (#23892)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* update network requirements docs for keras weights download

* fix manual PTZ relative moves permanently stopping object detection

* document available camera set features and link profiles docs to the API

* fix stale stream name field when switching cameras

The live streams and known plates fields rendered the map key as an uncontrolled input, so switching cameras left the previous camera's stream name on screen and would rename the wrong key if that stale text was committed. Both now use a shared MapKeyInput that resyncs with the form data and commits per keystroke, except while the typed name belongs to another entry, so the section is marked modified without waiting for blur.
2026-08-03 08:18:28 -05:00
Josh HawkinsandGitHub 4f2a297745 remove all references to degirum in frigate (#23882)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
the company ceased operations on 1 Aug 2026
2026-08-01 08:00:36 -06:00
Josh HawkinsandGitHub b848c90f02 Fix wrong box format passed to cv2.dnn.NMSBoxes (#23876)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-07-31 08:57:23 -05:00
Josh HawkinsandGitHub f1cc0e49d4 Miscellaneous fixes (0.18 beta) (#23873)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* improve display of gpu graphs in system metrics

* docs tweaks

* Only hide cameras with ui.dashboard disabled from the All Cameras dashboard

The settings camera selector and zone editor also filtered on ui.dashboard, so hiding a camera from the dashboard made its zones and masks uneditable in the UI (GH 23870). Drop those filters and correct the field title, help text, and reference docs to describe what the option actually does

* hide cameras with ui.review disabled from the Motion tab and the review summaries

The Motion tab built its own camera list that never checked ui.review, so a hidden camera still got a preview tile, and its motion and overlap queries fell back to every allowed camera. The review and recordings summaries had the same gap: they are aggregate day counts that can't be filtered client side, so a hidden camera kept contributing to the severity tab counts and calendar indicators while its items were absent from the list. Filter the motion camera list on ui.review and query all four endpoints with the visible camera list instead of letting the backend default to all, and skip the summary queries until the config resolves so the counts don't briefly render as zero.

* Scope every review page query to the cameras visible in review

The segments and the summary counts were derived from different camera sets: the list was fetched for all cameras and filtered client side, while the summaries were fetched for the visible cameras only when no explicit camera filter was set. A ?cameras= link can name a camera hidden from review, which left the count above zero with an empty list, pinning the new items to review popover open and making the auto refresh effect loop. Intersect an explicit camera selection with the visible list rather than trusting it, pass that to the segment and summary queries alike, and drop the now redundant client side filter, which the raw segments handed to the history view were bypassing anyway.
2026-07-30 17:20:41 -05:00
Josh HawkinsandGitHub 7ed7ed56cf Miscellaneous fixes (0.18 beta) (#23854)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* fix watchdog process restarts reverting to the boot config

/api/config/set parses a new FrigateConfig and swaps the API and dispatcher onto it, but FrigateApp.config was never rebound, so the watchdog factories rebuilt a crashed process from the config as of startup. Fix is to read through a ConfigHolder that the swap updates.

* fix birdseye camera overrides being clobbered by a global mode change

A global birdseye save published only the global object, leaving the output process to infer which cameras were inheriting by comparing against the previous global mode. That cannot tell an inherited value from an explicit one that happens to match, so it overwrote the override until a restart. Publish the per-camera values the config parse already resolved instead.

* Reject non-finite numbers in GenAI review descriptions

A model returning NaN for confidence or potential_threat_level slipped through the model_construct fallback, which skips validation, and was written into the review segment's JSON data. NaN is not valid JSON, so every subsequent /review request failed with "Out of range float values are not JSON compliant", blanking the review page for any time range containing the poisoned row.

* restore fused DetectionOutput in the OpenVINO SSD model conversion

* fix rgb swap issue for face dataset testing script
2026-07-29 08:39:01 -06:00
860772f9f4 Miscellaneous fixes (0.18 beta) (#23828)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* widen the logger name field in the per-process log level settings

* add details to timestamp error faq

* tweak genai docs

* tweak vector language

* Combine Qwen3.5 and Qwen3.6 listings

* fix openvino yolox detector crashing on every detection

The intermediate (N, 7) array in the yolox branch shadowed the pre-allocated (20, 6) detections buffer, so writing a detection into it raised "could not broadcast input array from shape (6,) into shape (7,)" on the first frame with anything above the confidence threshold. An empty frame also returned a (0, 7) array instead of the (20, 6) buffer.

Regressed in #13794, which renamed the intermediate from dets to detections as part of a cspell cleanup. Broken since 0.15.0.

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-07-28 11:02:15 -06:00
Josh HawkinsandGitHub 7e08f7b821 pin react-zoom-pan-pinch to 3.4.4 (same as 0.17.x) (#23818)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-07-25 18:50:41 -06:00
Josh HawkinsandGitHub 49e0ad93c2 Add AI policy docs (#23805)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* add frigate github AI policy

* update language

* add tldr
2026-07-25 11:22:05 -06:00
Josh HawkinsandGitHub 12dd242151 Miscellaneous fixes (0.18 beta) (#23809)
* fix calendars greying out the current day after midnight

The cutoff for disabling future days was computed with setHours(getHours() + 24, -1, 0, 0), which is not "24 hours from now" but tomorrow at the current hour minus one minute. Between 00:00 and 00:59 that lands back on today, and react-day-picker matches range matchers by calendar day, so today itself was disabled, leaving the export dialog's start time stuck on the previous day. TimezoneAwareCalendar also added the configured timezone's raw UTC offset instead of its difference from the browser's, widening the broken window to several hours in negative-offset zones and letting future days through in positive-offset ones. Derive the current date in the display timezone once, then build each cutoff in the space its calendar uses: ReviewActivityCalendar passes timeZone to react-day-picker so its day cells are TZDate and need a real instant, while TimezoneAwareCalendar is handed pre-shifted dates and needs a local one. Also corrects the today prop, which was off by the browser's offset, and the truthiness check that treated a configured timezone of UTC as unset.

* pin react-zoom-pan-pinch to 3.6.1

3.7.0 attaches a ResizeObserver to the transform wrapper and content unconditionally and clamps the pan position into the current bounds on every resize. The history player hides itself with display:none while scrubbing and while a new hour of recordings loads, so the observer measures it as 0x0, collapses the bounds to zero, and snaps a zoomed in view back to the top left corner. Zoom scale survives, only the position is lost.

That observer was only created for centerOnInit in 3.4.4 through 3.6.1 and 4.0.0 reverted it again, so 3.7.0 is the only affected release. The caret is what picked it up during the React 19 upgrade, so pin the version exactly.

Reported in #23807
2026-07-25 07:19:58 -06:00
Josh HawkinsandGitHub a573ea49bf update icons and i18n for 2026.2 frigate+ labels (#23803)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-07-24 16:15:58 -05:00
9f918362e9 Miscellaneous fixes (0.18 beta) (#23790)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* recreate review thumbnail directory before writing and log write failures

cleanup's remove_empty_directories() can rmdir an empty clips/review, after which thumbnail writes silently fail. Ensure the directory exists before both cv2.imwrite calls and check their return value

* add docs for add camera wizard

* Handle indefinite events when a segment needs to forcibly be ended for a ceamera

* update keyframe interval article link

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-07-24 10:08:21 -06:00
Josh HawkinsandGitHub 168cbea9ea Docs tweaks (#23787)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
* docs fixes

* backend tweaks

* regenerate i18n

* tweak genai

* add config overrides section

* add common errors

* add suggestions for rebuilding a corrupt database
2026-07-22 11:13:58 -05:00
Josh HawkinsandGitHub c0cf08ab4a Miscellaneous fixes (0.18 beta) (#23763)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
2026-07-21 06:44:33 -06:00