mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-29 18:04:42 +03:00
fixes
This commit is contained in:
@@ -202,6 +202,19 @@ class TestLiveTranscodeConfig(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(config.cameras["front"].live.transcode.source, "front_sub")
|
self.assertEqual(config.cameras["front"].live.transcode.source, "front_sub")
|
||||||
|
|
||||||
|
def test_startup_and_validation_agree_on_inherited_source(self):
|
||||||
|
config = camera_config({"transcode": {"enabled": True}})
|
||||||
|
config["live"] = {"streams": {"Sub": "front_sub"}}
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
raw_transcode_streams(config),
|
||||||
|
generated_transcode_streams(FrigateConfig(**config)),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
raw_transcode_streams(config)["front_transcode_360p"],
|
||||||
|
transcode_stream_source("front_sub", 360, 250),
|
||||||
|
)
|
||||||
|
|
||||||
def test_placed_entries_keep_position_and_label(self):
|
def test_placed_entries_keep_position_and_label(self):
|
||||||
streams = self.streams(
|
streams = self.streams(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ def raw_transcode_streams(config: dict[str, Any]) -> dict[str, str]:
|
|||||||
resolves defaults the same way the validator does.
|
resolves defaults the same way the validator does.
|
||||||
"""
|
"""
|
||||||
streams: dict[str, str] = {}
|
streams: dict[str, str] = {}
|
||||||
|
global_streams = (config.get("live") or {}).get("streams") or {}
|
||||||
|
|
||||||
for camera, camera_config in (config.get("cameras") or {}).items():
|
for camera, camera_config in (config.get("cameras") or {}).items():
|
||||||
live = (camera_config or {}).get("live") or {}
|
live = (camera_config or {}).get("live") or {}
|
||||||
@@ -89,8 +90,15 @@ def raw_transcode_streams(config: dict[str, Any]) -> dict[str, str]:
|
|||||||
if not transcode.get("enabled"):
|
if not transcode.get("enabled"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# cameras inherit global live streams after their own, like deep_merge
|
||||||
|
camera_streams = live.get("streams") or {}
|
||||||
|
live_streams = camera_streams | {
|
||||||
|
label: name
|
||||||
|
for label, name in global_streams.items()
|
||||||
|
if label not in camera_streams
|
||||||
|
}
|
||||||
source = transcode.get("source") or default_transcode_source(
|
source = transcode.get("source") or default_transcode_source(
|
||||||
camera, live.get("streams") or {camera: camera}
|
camera, live_streams or {camera: camera}
|
||||||
)
|
)
|
||||||
|
|
||||||
if source is None:
|
if source is None:
|
||||||
|
|||||||
@@ -150,6 +150,37 @@ test.describe("live stream order and transcoded streams @medium", () => {
|
|||||||
).toEqual(["Main", "Sub"]);
|
).toEqual(["Main", "Sub"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("save all keeps a reorder made on another page", async ({
|
||||||
|
frigateApp,
|
||||||
|
}) => {
|
||||||
|
const capture = await installRoutes(frigateApp.page);
|
||||||
|
await frigateApp.goto(SETTINGS_URL);
|
||||||
|
|
||||||
|
await expect
|
||||||
|
.poll(() => streamNames(frigateApp.page))
|
||||||
|
.toEqual(["Sub", "Main"]);
|
||||||
|
|
||||||
|
await dragRow(frigateApp.page, 1, 0);
|
||||||
|
await expect
|
||||||
|
.poll(() => streamNames(frigateApp.page))
|
||||||
|
.toEqual(["Main", "Sub"]);
|
||||||
|
|
||||||
|
await frigateApp.page.getByText("Snapshots", { exact: true }).click();
|
||||||
|
await frigateApp.page.getByRole("button", { name: "Save All" }).click();
|
||||||
|
|
||||||
|
await expect.poll(() => capture.saved()).not.toBeNull();
|
||||||
|
const saved = capture.saved() as {
|
||||||
|
replace_paths: string[];
|
||||||
|
config_data: {
|
||||||
|
cameras: { front_door: { live: { streams: Record<string, string> } } };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
expect(saved.replace_paths).toEqual(["cameras.front_door.live.streams"]);
|
||||||
|
expect(
|
||||||
|
Object.keys(saved.config_data.cameras.front_door.live.streams),
|
||||||
|
).toEqual(["Main", "Sub"]);
|
||||||
|
});
|
||||||
|
|
||||||
test("enabling transcoding saves the shown source", async ({
|
test("enabling transcoding saves the shown source", async ({
|
||||||
frigateApp,
|
frigateApp,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -484,10 +484,13 @@ export function LiveStreamsField(props: FieldProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Auto order commits after measuring, so edits made meanwhile must survive
|
// Auto order commits after measuring, so edits made meanwhile must survive
|
||||||
|
// and a camera switched to meanwhile must not receive the result
|
||||||
const dataRef = useRef(data);
|
const dataRef = useRef(data);
|
||||||
|
const cameraRef = useRef(camera);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dataRef.current = data;
|
dataRef.current = data;
|
||||||
}, [data]);
|
cameraRef.current = camera;
|
||||||
|
}, [data, camera]);
|
||||||
|
|
||||||
const [measurement, setMeasurement] = useState<{
|
const [measurement, setMeasurement] = useState<{
|
||||||
data: LiveStreamsData;
|
data: LiveStreamsData;
|
||||||
@@ -527,6 +530,11 @@ export function LiveStreamsField(props: FieldProps) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (cameraRef.current !== camera) {
|
||||||
|
setMeasuring(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const next = Object.fromEntries(measured);
|
const next = Object.fromEntries(measured);
|
||||||
const latest = dataRef.current;
|
const latest = dataRef.current;
|
||||||
setMeasurement({ data: latest, transcode, rates: next });
|
setMeasurement({ data: latest, transcode, rates: next });
|
||||||
|
|||||||
@@ -875,6 +875,9 @@ export default function Settings() {
|
|||||||
requires_restart: payload.needsRestart ? 1 : 0,
|
requires_restart: payload.needsRestart ? 1 : 0,
|
||||||
update_topic: payload.updateTopic,
|
update_topic: payload.updateTopic,
|
||||||
config_data: configData,
|
config_data: configData,
|
||||||
|
...(payload.replacePaths?.length
|
||||||
|
? { replace_paths: payload.replacePaths }
|
||||||
|
: {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (payload.needsRestart) {
|
if (payload.needsRestart) {
|
||||||
|
|||||||
@@ -556,6 +556,7 @@ export interface SectionSavePayload {
|
|||||||
updateTopic: string | undefined;
|
updateTopic: string | undefined;
|
||||||
needsRestart: boolean;
|
needsRestart: boolean;
|
||||||
pendingDataKey: string;
|
pendingDataKey: string;
|
||||||
|
replacePaths?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -765,7 +766,13 @@ export function prepareSectionSavePayload(opts: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Build overrides
|
// Build overrides
|
||||||
const overrides = buildOverrides(pendingData, rawData, effectiveDefaults);
|
const orderedMaps = sectionConfig.orderedMaps ?? [];
|
||||||
|
const overrides = applyOrderedMaps(
|
||||||
|
buildOverrides(pendingData, rawData, effectiveDefaults),
|
||||||
|
pendingData,
|
||||||
|
rawData,
|
||||||
|
orderedMaps,
|
||||||
|
);
|
||||||
const sanitizedOverrides = sanitizeOverridesForSection(
|
const sanitizedOverrides = sanitizeOverridesForSection(
|
||||||
schemaSection,
|
schemaSection,
|
||||||
level,
|
level,
|
||||||
@@ -815,6 +822,9 @@ export function prepareSectionSavePayload(opts: {
|
|||||||
updateTopic,
|
updateTopic,
|
||||||
needsRestart,
|
needsRestart,
|
||||||
pendingDataKey,
|
pendingDataKey,
|
||||||
|
replacePaths: changedOrderedMapPaths(pendingData, rawData, orderedMaps).map(
|
||||||
|
(path) => `${basePath}.${path}`,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user