mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-21 19:29:00 +03:00
Compare commits
2
Commits
dev
...
8b340a09fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b340a09fa | ||
|
|
25dcd25751 |
@@ -26,7 +26,6 @@ from frigate.plus import PlusApi
|
||||
from frigate.util.builtin import (
|
||||
deep_merge,
|
||||
get_ffmpeg_arg_list,
|
||||
load_labels,
|
||||
)
|
||||
from frigate.util.config import (
|
||||
CURRENT_CONFIG_VERSION,
|
||||
@@ -638,17 +637,12 @@ class FrigateConfig(FrigateBaseModel):
|
||||
if self.ffmpeg.hwaccel_args == "auto":
|
||||
self.ffmpeg.hwaccel_args = auto_detect_hwaccel()
|
||||
|
||||
# Populate global audio filters for all audio labels
|
||||
all_audio_labels = {
|
||||
label
|
||||
for label in load_labels("/audio-labelmap.txt", prefill=521).values()
|
||||
if label
|
||||
}
|
||||
|
||||
# Populate global audio filters from listen. Existing user-defined
|
||||
# entries for labels not in listen are preserved but unused at runtime.
|
||||
if self.audio.filters is None:
|
||||
self.audio.filters = {}
|
||||
|
||||
for key in sorted(all_audio_labels - self.audio.filters.keys()):
|
||||
for key in sorted(set(self.audio.listen) - self.audio.filters.keys()):
|
||||
self.audio.filters[key] = AudioFilterConfig()
|
||||
|
||||
self.audio.filters = dict(sorted(self.audio.filters.items()))
|
||||
@@ -840,7 +834,9 @@ class FrigateConfig(FrigateBaseModel):
|
||||
if camera_config.audio.filters is None:
|
||||
camera_config.audio.filters = {}
|
||||
|
||||
for key in sorted(all_audio_labels - camera_config.audio.filters.keys()):
|
||||
for key in sorted(
|
||||
set(camera_config.audio.listen) - camera_config.audio.filters.keys()
|
||||
):
|
||||
camera_config.audio.filters[key] = AudioFilterConfig()
|
||||
|
||||
camera_config.audio.filters = dict(
|
||||
|
||||
@@ -10,7 +10,7 @@ from ruamel.yaml.constructor import DuplicateKeyError
|
||||
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
||||
from frigate.const import MODEL_CACHE_DIR
|
||||
from frigate.detectors import DetectorTypeEnum
|
||||
from frigate.util.builtin import deep_merge, load_labels
|
||||
from frigate.util.builtin import deep_merge
|
||||
|
||||
|
||||
class TestConfig(unittest.TestCase):
|
||||
@@ -309,16 +309,11 @@ class TestConfig(unittest.TestCase):
|
||||
}
|
||||
|
||||
frigate_config = FrigateConfig(**config)
|
||||
all_audio_labels = {
|
||||
label
|
||||
for label in load_labels("/audio-labelmap.txt", prefill=521).values()
|
||||
if label
|
||||
assert set(frigate_config.cameras["back"].audio.filters.keys()) == {
|
||||
"speech",
|
||||
"yell",
|
||||
}
|
||||
|
||||
assert all_audio_labels.issubset(
|
||||
set(frigate_config.cameras["back"].audio.filters.keys())
|
||||
)
|
||||
|
||||
def test_override_audio_filters(self):
|
||||
config = {
|
||||
"mqtt": {"host": "mqtt"},
|
||||
@@ -345,7 +340,8 @@ class TestConfig(unittest.TestCase):
|
||||
frigate_config = FrigateConfig(**config)
|
||||
assert "speech" in frigate_config.cameras["back"].audio.filters
|
||||
assert frigate_config.cameras["back"].audio.filters["speech"].threshold == 0.9
|
||||
assert "babbling" in frigate_config.cameras["back"].audio.filters
|
||||
assert "yell" in frigate_config.cameras["back"].audio.filters
|
||||
assert "babbling" not in frigate_config.cameras["back"].audio.filters
|
||||
|
||||
def test_inherit_object_filters(self):
|
||||
config = {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
modifySchemaForSection,
|
||||
getEffectiveDefaultsForSection,
|
||||
sanitizeOverridesForSection,
|
||||
synthesizeMissingObjectFilters,
|
||||
synthesizeMissingFilters,
|
||||
} from "./section-special-cases";
|
||||
import { getSectionValidation } from "../section-validations";
|
||||
import { useConfigOverride } from "@/hooks/use-config-override";
|
||||
@@ -370,7 +370,7 @@ export function ConfigSection({
|
||||
return {};
|
||||
}
|
||||
|
||||
return synthesizeMissingObjectFilters(
|
||||
return synthesizeMissingFilters(
|
||||
sectionPath,
|
||||
rawSectionValue,
|
||||
modifiedSchema ?? undefined,
|
||||
|
||||
@@ -128,22 +128,31 @@ export function getEffectiveDefaultsForSection(
|
||||
return schemaDefaults;
|
||||
}
|
||||
|
||||
// Sections whose `filters` dict is keyed by a sibling list field. The backend
|
||||
// auto-populates these filters at config init but doesn't re-run after profile
|
||||
// merges, so we synthesize the missing entries on the frontend.
|
||||
const FILTER_SECTIONS: Record<string, { listField: string }> = {
|
||||
objects: { listField: "track" },
|
||||
audio: { listField: "listen" },
|
||||
};
|
||||
|
||||
/**
|
||||
* Add default filter entries for any label in `objects.track` that isn't
|
||||
* already in `objects.filters`, so each tracked label gets a collapsible.
|
||||
* The backend only auto-populates filters at config init, not after profile
|
||||
* merges.
|
||||
* Add default filter entries for any label in the section's list field
|
||||
* (e.g. `objects.track`, `audio.listen`) that isn't already in `filters`, so
|
||||
* each label gets a collapsible. The backend only auto-populates filters at
|
||||
* config init, not after profile merges.
|
||||
*/
|
||||
export function synthesizeMissingObjectFilters(
|
||||
export function synthesizeMissingFilters(
|
||||
sectionPath: string,
|
||||
data: unknown,
|
||||
sectionSchema: RJSFSchema | undefined,
|
||||
): unknown {
|
||||
if (sectionPath !== "objects") return data;
|
||||
const sectionConfig = FILTER_SECTIONS[sectionPath];
|
||||
if (!sectionConfig) return data;
|
||||
if (!isJsonObject(data)) return data;
|
||||
|
||||
const trackValue = (data as JsonObject).track;
|
||||
if (!Array.isArray(trackValue) || trackValue.length === 0) return data;
|
||||
const listValue = (data as JsonObject)[sectionConfig.listField];
|
||||
if (!Array.isArray(listValue) || listValue.length === 0) return data;
|
||||
|
||||
const properties = (sectionSchema as { properties?: Record<string, unknown> })
|
||||
?.properties;
|
||||
@@ -160,7 +169,7 @@ export function synthesizeMissingObjectFilters(
|
||||
|
||||
const newFilters: JsonObject = { ...existingFilters };
|
||||
let added = false;
|
||||
for (const label of trackValue) {
|
||||
for (const label of listValue) {
|
||||
if (typeof label !== "string") continue;
|
||||
if (Object.prototype.hasOwnProperty.call(newFilters, label)) continue;
|
||||
newFilters[label] = (
|
||||
|
||||
@@ -60,7 +60,7 @@ export function getLifecycleItemDescription(
|
||||
} else {
|
||||
title = t("trackingDetails.lifecycleItemDesc.attribute.other", {
|
||||
ns: "views/explore",
|
||||
label: lifecycleItem.data.label,
|
||||
label: getTranslatedLabel(lifecycleItem.data.label),
|
||||
attribute: getTranslatedLabel(
|
||||
lifecycleItem.data.attribute.replaceAll("_", " "),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user