probe metadata and snapshot separately

This commit is contained in:
Josh Hawkins 2025-10-23 07:19:38 -05:00
parent 7e3be76c9a
commit f388b4c45a
2 changed files with 31 additions and 19 deletions

View File

@ -16,7 +16,6 @@
"ui": "UI",
"enrichments": "Enrichments",
"cameraManagement": "Management",
"cameraReview": "Review",
"masksAndZones": "Masks / Zones",
"motionTuner": "Motion Tuner",
"triggers": "Triggers",
@ -188,6 +187,10 @@
"testSuccess": "Connection test successful!",
"testFailed": "Connection test failed. Please check your input and try again.",
"streamDetails": "Stream Details",
"testing": {
"probingMetadata": "Probing camera metadata...",
"fetchingSnapshot": "Fetching camera snapshot..."
},
"warnings": {
"noSnapshot": "Unable to fetch a snapshot from the configured stream."
},
@ -197,8 +200,9 @@
"nameLength": "Camera name must be 64 characters or less",
"invalidCharacters": "Camera name contains invalid characters",
"nameExists": "Camera name already exists",
"customUrlRtspRequired": "Custom URLs must begin with \"rtsp://\". Manual configuration is required for non-RTSP camera streams.",
"brands": {
"reolink-rtsp": "Reolink RTSP is not recommended. It is recommended to enable http in the camera settings and restart the camera wizard."
"reolink-rtsp": "Reolink RTSP is not recommended. Enable HTTP in the camera's firmware settings and restart the wizard."
}
},
"docs": {

View File

@ -65,6 +65,7 @@ export default function Step1NameCamera({
const { data: config } = useSWR<FrigateConfig>("config");
const [showPassword, setShowPassword] = useState(false);
const [isTesting, setIsTesting] = useState(false);
const [testStatus, setTestStatus] = useState<string>("");
const [testResult, setTestResult] = useState<TestResult | null>(null);
const existingCameraNames = useMemo(() => {
@ -88,7 +89,13 @@ export default function Step1NameCamera({
username: z.string().optional(),
password: z.string().optional(),
brandTemplate: z.enum(CAMERA_BRAND_VALUES).optional(),
customUrl: z.string().optional(),
customUrl: z
.string()
.optional()
.refine(
(val) => !val || val.startsWith("rtsp://"),
t("cameraWizard.step1.errors.customUrlRtspRequired"),
),
})
.refine(
(data) => {
@ -204,24 +211,17 @@ export default function Step1NameCamera({
}
setIsTesting(true);
setTestStatus("");
setTestResult(null);
// First get probe data for metadata
const probePromise = axios.get("ffprobe", {
params: { paths: streamUrl, detailed: true },
timeout: 10000,
});
// Then get snapshot for preview
const snapshotPromise = axios.get("ffprobe/snapshot", {
params: { url: streamUrl },
responseType: "blob",
timeout: 10000,
});
try {
// First get probe data for metadata
const probeResponse = await probePromise;
setTestStatus(t("cameraWizard.step1.testing.probingMetadata"));
const probeResponse = await axios.get("ffprobe", {
params: { paths: streamUrl, detailed: true },
timeout: 10000,
});
let probeData = null;
if (
probeResponse.data &&
@ -234,8 +234,13 @@ export default function Step1NameCamera({
// Then get snapshot for preview (only if probe succeeded)
let snapshotBlob = null;
if (probeData) {
setTestStatus(t("cameraWizard.step1.testing.fetchingSnapshot"));
try {
const snapshotResponse = await snapshotPromise;
const snapshotResponse = await axios.get("ffprobe/snapshot", {
params: { url: streamUrl },
responseType: "blob",
timeout: 10000,
});
snapshotBlob = snapshotResponse.data;
} catch (snapshotError) {
// Snapshot is optional, don't fail if it doesn't work
@ -321,6 +326,7 @@ export default function Step1NameCamera({
);
} finally {
setIsTesting(false);
setTestStatus("");
}
}, [form, generateStreamUrl, t]);
@ -610,7 +616,9 @@ export default function Step1NameCamera({
className="flex items-center justify-center gap-2 sm:flex-1"
>
{isTesting && <ActivityIndicator className="size-4" />}
{t("cameraWizard.step1.testConnection")}
{isTesting && testStatus
? testStatus
: t("cameraWizard.step1.testConnection")}
</Button>
)}
</div>