mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-06-02 01:53:10 -05:00
* add embedded mode to BaseSection so parents can host the save action * add optional action slot to current Frigate+ model summary * add w-full to action slot flex wrapper for explicit width contract * i18n * merged detectors and model settings view * fix document title * Embed detector form in merged settings view * add detection model card with tabs and custom model embed * add Frigate+ model selector with filter popover to merged page * Add mismatch banner and gate save on detector and model compatibility * Wire atomic save, restart toast, and undo on detectors and model page * Clear child pending data on undo * route merged detectors and model view in settings * trim Frigate+ page to account-only and remove old detection model view * basic e2e * Fix unsaved-changes guard, custom path leak, and post-failure cache resync * Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch * Hide Plus/Custom tabs when Frigate+ is not enabled * Detect active Plus model via model.plus.id instead of path prefix * Sync state back to snapshot when child form un-modifies and remount on undo * Always require restart on save since model changes also need one * Wrap Frigate+ model selector in SplitCardRow with label and description * rename tab * update docs * sync top-level model with default detector's resolved model when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap * revert to cpu detector by default use openvino cpu for new configs only * add defaults
121 lines
4.3 KiB
Python
121 lines
4.3 KiB
Python
"""Map config section keys to Settings UI navigation paths."""
|
|
|
|
# Derived from web/src/pages/Settings.tsx section mappings
|
|
# and web/public/locales/en/views/settings.json menu labels.
|
|
#
|
|
# Format: section_key -> (group_label, page_label)
|
|
# Navigation path: "Settings > {group_label} > {page_label}"
|
|
|
|
GLOBAL_NAV: dict[str, tuple[str, str]] = {
|
|
"detect": ("Global configuration", "Object detection"),
|
|
"ffmpeg": ("Global configuration", "FFmpeg"),
|
|
"record": ("Global configuration", "Recording"),
|
|
"snapshots": ("Global configuration", "Snapshots"),
|
|
"motion": ("Global configuration", "Motion detection"),
|
|
"objects": ("Global configuration", "Objects"),
|
|
"review": ("Global configuration", "Review"),
|
|
"audio": ("Global configuration", "Audio events"),
|
|
"live": ("Global configuration", "Live playback"),
|
|
"timestamp_style": ("Global configuration", "Timestamp style"),
|
|
"notifications": ("Notifications", "Notifications"),
|
|
}
|
|
|
|
CAMERA_NAV: dict[str, tuple[str, str]] = {
|
|
"detect": ("Camera configuration", "Object detection"),
|
|
"ffmpeg": ("Camera configuration", "FFmpeg"),
|
|
"record": ("Camera configuration", "Recording"),
|
|
"snapshots": ("Camera configuration", "Snapshots"),
|
|
"motion": ("Camera configuration", "Motion detection"),
|
|
"objects": ("Camera configuration", "Objects"),
|
|
"review": ("Camera configuration", "Review"),
|
|
"audio": ("Camera configuration", "Audio events"),
|
|
"audio_transcription": ("Camera configuration", "Audio transcription"),
|
|
"notifications": ("Camera configuration", "Notifications"),
|
|
"live": ("Camera configuration", "Live playback"),
|
|
"birdseye": ("Camera configuration", "Birdseye"),
|
|
"face_recognition": ("Camera configuration", "Face recognition"),
|
|
"lpr": ("Camera configuration", "License plate recognition"),
|
|
"mqtt": ("Camera configuration", "MQTT"),
|
|
"onvif": ("Camera configuration", "ONVIF"),
|
|
"ui": ("Camera configuration", "Camera UI"),
|
|
"timestamp_style": ("Camera configuration", "Timestamp style"),
|
|
}
|
|
|
|
ENRICHMENT_NAV: dict[str, tuple[str, str]] = {
|
|
"semantic_search": ("Enrichments", "Semantic search"),
|
|
"genai": ("Enrichments", "Generative AI"),
|
|
"face_recognition": ("Enrichments", "Face recognition"),
|
|
"lpr": ("Enrichments", "License plate recognition"),
|
|
"classification": ("Enrichments", "Object classification"),
|
|
"audio_transcription": ("Enrichments", "Audio transcription"),
|
|
}
|
|
|
|
SYSTEM_NAV: dict[str, tuple[str, str]] = {
|
|
"go2rtc_streams": ("System", "go2rtc streams"),
|
|
"database": ("System", "Database"),
|
|
"mqtt": ("System", "MQTT"),
|
|
"tls": ("System", "TLS"),
|
|
"auth": ("System", "Authentication"),
|
|
"networking": ("System", "Networking"),
|
|
"proxy": ("System", "Proxy"),
|
|
"ui": ("System", "UI"),
|
|
"logger": ("System", "Logging"),
|
|
"environment_vars": ("System", "Environment variables"),
|
|
"telemetry": ("System", "Telemetry"),
|
|
"birdseye": ("System", "Birdseye"),
|
|
"detectors": ("System", "Detectors and model"),
|
|
"model": ("System", "Detectors and model"),
|
|
}
|
|
|
|
# All known top-level config section keys
|
|
ALL_CONFIG_SECTIONS = (
|
|
set(GLOBAL_NAV)
|
|
| set(CAMERA_NAV)
|
|
| set(ENRICHMENT_NAV)
|
|
| set(SYSTEM_NAV)
|
|
| {"cameras"}
|
|
)
|
|
|
|
|
|
def get_nav_path(section_key: str, level: str = "global") -> str | None:
|
|
"""Get the full navigation path for a config section.
|
|
|
|
Args:
|
|
section_key: Config section key (e.g., "record")
|
|
level: "global", "camera", "enrichment", or "system"
|
|
|
|
Returns:
|
|
NavPath string like "Settings > Global configuration > Recording",
|
|
or None if not found.
|
|
"""
|
|
nav_tables = {
|
|
"global": GLOBAL_NAV,
|
|
"camera": CAMERA_NAV,
|
|
"enrichment": ENRICHMENT_NAV,
|
|
"system": SYSTEM_NAV,
|
|
}
|
|
|
|
table = nav_tables.get(level)
|
|
if table is None:
|
|
return None
|
|
|
|
entry = table.get(section_key)
|
|
if entry is None:
|
|
return None
|
|
|
|
group, page = entry
|
|
return f"Settings > {group} > {page}"
|
|
|
|
|
|
def detect_level(section_key: str) -> str:
|
|
"""Detect whether a config section is global, camera, enrichment, or system."""
|
|
if section_key in SYSTEM_NAV:
|
|
return "system"
|
|
if section_key in ENRICHMENT_NAV:
|
|
return "enrichment"
|
|
if section_key in GLOBAL_NAV:
|
|
return "global"
|
|
if section_key in CAMERA_NAV:
|
|
return "camera"
|
|
return "global"
|