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
This commit is contained in:
Josh Hawkins
2026-09-12 07:30:04 -06:00
committed by Nicolas Mowen
parent 27a40a507b
commit 257a05a7e2
6 changed files with 1351 additions and 5 deletions
+36 -2
View File
@@ -179,6 +179,31 @@
"desc": "Streaming settings for each camera group are stored in your browser's local storage.",
"clearAll": "Clear All Streaming Settings"
},
"backupRestore": {
"title": "Backup & Restore",
"transfer": {
"label": "Device Settings File",
"desc": "Camera group layouts, streaming settings, and UI preferences are stored in your browser. Export them to a file to back them up or to move them to another device.",
"export": "Export Settings",
"import": "Import Settings"
},
"importDialog": {
"title": "Import Settings",
"desc": "Choose what to apply from this file. Frigate will reload when the import finishes.",
"exportedFrom": "Exported {{date}} from Frigate config version {{version}}",
"layouts_one": "Camera group layouts ({{count}} group)",
"layouts_other": "Camera group layouts ({{count}} groups)",
"streaming_one": "Streaming settings ({{count}} camera)",
"streaming_other": "Streaming settings ({{count}} cameras)",
"preferences_one": "UI preferences ({{count}} setting)",
"preferences_other": "UI preferences ({{count}} settings)",
"unknownGroups_one": "Camera group {{groups}} is not on this server. Its settings will be saved but unused.",
"unknownGroups_other": "Camera groups {{groups}} are not on this server. Their settings will be saved but unused.",
"unknownCameras_one": "Camera {{cameras}} is not on this server. Its streaming settings will be ignored.",
"unknownCameras_other": "Cameras {{cameras}} are not on this server. Their streaming settings will be ignored.",
"confirm": "Import"
}
},
"recordingsViewer": {
"title": "Recordings Viewer",
"defaultPlaybackRate": {
@@ -198,11 +223,20 @@
"toast": {
"success": {
"clearStoredLayout": "Cleared stored layout for {{cameraName}}",
"clearStreamingSettings": "Cleared streaming settings for all camera groups."
"clearStreamingSettings": "Cleared streaming settings for all camera groups.",
"exportUiSettings": "Exported settings",
"importUiSettings": "Imported settings"
},
"error": {
"clearStoredLayoutFailed": "Failed to clear stored layout: {{errorMessage}}",
"clearStreamingSettingsFailed": "Failed to clear streaming settings: {{errorMessage}}"
"clearStreamingSettingsFailed": "Failed to clear streaming settings: {{errorMessage}}",
"exportUiSettingsFailed": "Failed to export settings",
"importNothingToApply": "Failed to import settings: file contains no settings",
"importInvalidJson": "Failed to import settings: file is not valid JSON",
"importWrongType": "Failed to import settings: file is not a Frigate settings export",
"importUnsupportedVersion": "Failed to import settings: file requires a newer version of Frigate",
"importInvalidSchema": "Failed to import settings: file is malformed",
"importUiSettingsFailed": "Failed to import settings"
}
}
},