From 5272b83959711f23bb96dc9cd31c637efb073200 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:31:06 -0600 Subject: [PATCH] clean up camera edit form --- .../components/settings/CameraEditForm.tsx | 87 +++++++++++++++++-- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/web/src/components/settings/CameraEditForm.tsx b/web/src/components/settings/CameraEditForm.tsx index 3b910c176..c18b5ffd3 100644 --- a/web/src/components/settings/CameraEditForm.tsx +++ b/web/src/components/settings/CameraEditForm.tsx @@ -18,7 +18,7 @@ import { z } from "zod"; import axios from "axios"; import { toast, Toaster } from "sonner"; import { useTranslation } from "react-i18next"; -import { useState, useMemo } from "react"; +import { useState, useMemo, useEffect } from "react"; import { LuTrash2, LuPlus } from "react-icons/lu"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { FrigateConfig } from "@/types/frigateConfig"; @@ -42,7 +42,15 @@ export default function CameraEditForm({ onCancel, }: CameraEditFormProps) { const { t } = useTranslation(["views/settings"]); - const { data: config } = useSWR("config"); + const { data: config, mutate: mutateConfig } = + useSWR("config"); + const { data: rawPaths, mutate: mutateRawPaths } = useSWR<{ + cameras: Record< + string, + { ffmpeg: { inputs: { path: string; roles: string[] }[] } } + >; + go2rtc: { streams: Record }; + }>(cameraName ? "config/raw_paths" : null); const [isLoading, setIsLoading] = useState(false); const formSchema = useMemo( @@ -145,14 +153,23 @@ export default function CameraEditForm({ if (cameraName && config?.cameras[cameraName]) { const camera = config.cameras[cameraName]; defaultValues.enabled = camera.enabled ?? true; - defaultValues.ffmpeg.inputs = camera.ffmpeg?.inputs?.length - ? camera.ffmpeg.inputs.map((input) => ({ + + // Use raw paths from the admin endpoint if available, otherwise fall back to masked paths + const rawCameraData = rawPaths?.cameras?.[cameraName]; + defaultValues.ffmpeg.inputs = rawCameraData?.ffmpeg?.inputs?.length + ? rawCameraData.ffmpeg.inputs.map((input) => ({ path: input.path, roles: input.roles as Role[], })) - : defaultValues.ffmpeg.inputs; + : camera.ffmpeg?.inputs?.length + ? camera.ffmpeg.inputs.map((input) => ({ + path: input.path, + roles: input.roles as Role[], + })) + : defaultValues.ffmpeg.inputs; - const go2rtcStreams = config.go2rtc?.streams || {}; + const go2rtcStreams = + rawPaths?.go2rtc?.streams || config.go2rtc?.streams || {}; const cameraStreams: Record = {}; // get candidate stream names for this camera. could be the camera's own name, @@ -196,6 +213,60 @@ export default function CameraEditForm({ mode: "onChange", }); + // Update form values when rawPaths loads + useEffect(() => { + if ( + cameraName && + config?.cameras[cameraName] && + rawPaths?.cameras?.[cameraName] + ) { + const camera = config.cameras[cameraName]; + const rawCameraData = rawPaths.cameras[cameraName]; + + // Update ffmpeg inputs with raw paths + if (rawCameraData.ffmpeg?.inputs?.length) { + form.setValue( + "ffmpeg.inputs", + rawCameraData.ffmpeg.inputs.map((input) => ({ + path: input.path, + roles: input.roles as Role[], + })), + ); + } + + // Update go2rtc streams with raw URLs + if (rawPaths.go2rtc?.streams) { + const validNames = new Set(); + validNames.add(cameraName); + + camera.ffmpeg?.inputs?.forEach((input) => { + const restreamMatch = input.path.match( + /^rtsp:\/\/127\.0\.0\.1:8554\/([^?#/]+)(?:[?#].*)?$/, + ); + if (restreamMatch) { + validNames.add(restreamMatch[1]); + } + }); + + const liveStreams = camera?.live?.streams; + if (liveStreams) { + Object.keys(liveStreams).forEach((key) => validNames.add(key)); + } + + const cameraStreams: Record = {}; + Object.entries(rawPaths.go2rtc.streams).forEach(([name, urls]) => { + if (validNames.has(name)) { + cameraStreams[name] = Array.isArray(urls) ? urls : [urls]; + } + }); + + if (Object.keys(cameraStreams).length > 0) { + form.setValue("go2rtcStreams", cameraStreams); + } + } + } + }, [cameraName, config, rawPaths, form]); + const { fields, append, remove } = useFieldArray({ control: form.control, name: "ffmpeg.inputs", @@ -268,6 +339,8 @@ export default function CameraEditForm({ }), { position: "top-center" }, ); + mutateConfig(); + mutateRawPaths(); if (onSave) onSave(); }); } else { @@ -277,6 +350,8 @@ export default function CameraEditForm({ }), { position: "top-center" }, ); + mutateConfig(); + mutateRawPaths(); if (onSave) onSave(); } } else {