mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-07 05:55:27 +03:00
sync object filter entries with tracked labels in camera config form
Filter sub-collapsibles in the camera Objects section are driven by `filters` dict keys, but profile merges and live track-switch edits don't add matching entries, so newly tracked labels (like from a profile override) had no collapsible. Synthesize default filter entries from `track` in the form data so every tracked label renders a collapsible; baseline data also gets the synthesized entries, so save payloads are unchanged.
This commit is contained in:
parent
9e3f42444b
commit
b317f6b8ad
@ -22,6 +22,7 @@ import {
|
|||||||
modifySchemaForSection,
|
modifySchemaForSection,
|
||||||
getEffectiveDefaultsForSection,
|
getEffectiveDefaultsForSection,
|
||||||
sanitizeOverridesForSection,
|
sanitizeOverridesForSection,
|
||||||
|
synthesizeMissingObjectFilters,
|
||||||
} from "./section-special-cases";
|
} from "./section-special-cases";
|
||||||
import { getSectionValidation } from "../section-validations";
|
import { getSectionValidation } from "../section-validations";
|
||||||
import { useConfigOverride } from "@/hooks/use-config-override";
|
import { useConfigOverride } from "@/hooks/use-config-override";
|
||||||
@ -357,15 +358,19 @@ export function ConfigSection({
|
|||||||
return get(config, sectionPath);
|
return get(config, sectionPath);
|
||||||
}, [config, cameraName, sectionPath, effectiveLevel, profileName]);
|
}, [config, cameraName, sectionPath, effectiveLevel, profileName]);
|
||||||
|
|
||||||
const rawFormData = useMemo(() => {
|
const rawFormData = useMemo<ConfigSectionData>(() => {
|
||||||
if (!config) return {};
|
if (!config) return {};
|
||||||
|
|
||||||
if (rawSectionValue === undefined || rawSectionValue === null) {
|
if (rawSectionValue === undefined || rawSectionValue === null) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawSectionValue;
|
return synthesizeMissingObjectFilters(
|
||||||
}, [config, rawSectionValue]);
|
sectionPath,
|
||||||
|
rawSectionValue,
|
||||||
|
modifiedSchema ?? undefined,
|
||||||
|
) as ConfigSectionData;
|
||||||
|
}, [config, rawSectionValue, sectionPath, modifiedSchema]);
|
||||||
|
|
||||||
// When editing a profile, hide fields that require a restart since they
|
// When editing a profile, hide fields that require a restart since they
|
||||||
// cannot take effect via profile switching alone.
|
// cannot take effect via profile switching alone.
|
||||||
@ -387,7 +392,7 @@ export function ConfigSection({
|
|||||||
const baseData = modifiedSchema
|
const baseData = modifiedSchema
|
||||||
? applySchemaDefaults(modifiedSchema, rawFormData)
|
? applySchemaDefaults(modifiedSchema, rawFormData)
|
||||||
: rawFormData;
|
: rawFormData;
|
||||||
return sanitizeSectionData(baseData);
|
return sanitizeSectionData(baseData as ConfigSectionData);
|
||||||
}, [rawFormData, modifiedSchema, sanitizeSectionData]);
|
}, [rawFormData, modifiedSchema, sanitizeSectionData]);
|
||||||
|
|
||||||
const baselineSnapshot = useMemo(() => {
|
const baselineSnapshot = useMemo(() => {
|
||||||
@ -506,7 +511,11 @@ export function ConfigSection({
|
|||||||
setPendingOverrides(undefined);
|
setPendingOverrides(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sanitizedData = sanitizeSectionData(data as ConfigSectionData);
|
const sanitizedData = synthesizeMissingObjectFilters(
|
||||||
|
sectionPath,
|
||||||
|
sanitizeSectionData(data as ConfigSectionData),
|
||||||
|
modifiedSchema ?? undefined,
|
||||||
|
) as ConfigSectionData;
|
||||||
const nextBaselineFormData = baselineSnapshot;
|
const nextBaselineFormData = baselineSnapshot;
|
||||||
const overrides = buildOverrides(
|
const overrides = buildOverrides(
|
||||||
sanitizedData,
|
sanitizedData,
|
||||||
@ -546,6 +555,8 @@ export function ConfigSection({
|
|||||||
setPendingOverrides,
|
setPendingOverrides,
|
||||||
setDirtyOverrides,
|
setDirtyOverrides,
|
||||||
baselineSnapshot,
|
baselineSnapshot,
|
||||||
|
sectionPath,
|
||||||
|
modifiedSchema,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -105,6 +105,51 @@ export function getEffectiveDefaultsForSection(
|
|||||||
return schemaDefaults;
|
return schemaDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 or live track edits.
|
||||||
|
*/
|
||||||
|
export function synthesizeMissingObjectFilters(
|
||||||
|
sectionPath: string,
|
||||||
|
data: unknown,
|
||||||
|
sectionSchema: RJSFSchema | undefined,
|
||||||
|
): unknown {
|
||||||
|
if (sectionPath !== "objects") return data;
|
||||||
|
if (!isJsonObject(data)) return data;
|
||||||
|
|
||||||
|
const trackValue = (data as JsonObject).track;
|
||||||
|
if (!Array.isArray(trackValue) || trackValue.length === 0) return data;
|
||||||
|
|
||||||
|
const properties = (sectionSchema as { properties?: Record<string, unknown> })
|
||||||
|
?.properties;
|
||||||
|
const filtersSchema = isJsonObject(properties)
|
||||||
|
? (properties.filters as { additionalProperties?: unknown } | undefined)
|
||||||
|
: undefined;
|
||||||
|
const filterEntrySchema = isJsonObject(filtersSchema?.additionalProperties)
|
||||||
|
? (filtersSchema.additionalProperties as RJSFSchema)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const existingFilters = isJsonObject((data as JsonObject).filters)
|
||||||
|
? ((data as JsonObject).filters as JsonObject)
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const newFilters: JsonObject = { ...existingFilters };
|
||||||
|
let added = false;
|
||||||
|
for (const label of trackValue) {
|
||||||
|
if (typeof label !== "string") continue;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(newFilters, label)) continue;
|
||||||
|
newFilters[label] = (
|
||||||
|
filterEntrySchema ? applySchemaDefaults(filterEntrySchema, {}) : {}
|
||||||
|
) as JsonValue;
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!added) return data;
|
||||||
|
return { ...(data as JsonObject), filters: newFilters };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitize overrides payloads for section-specific quirks.
|
* Sanitize overrides payloads for section-specific quirks.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user