mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 10:33:11 +03:00
clean up camera inputs fields
This commit is contained in:
parent
afeb03a32f
commit
ef665a8c3d
@ -1252,7 +1252,7 @@
|
||||
"manualPlaceholder": "Enter FFmpeg arguments"
|
||||
},
|
||||
"cameraInputs": {
|
||||
"itemTitle": "Stream {{index}}: {{path}}"
|
||||
"itemTitle": "Stream {{index}}"
|
||||
},
|
||||
"restartRequiredField": "Restart required",
|
||||
"restartRequiredFooter": "Configuration changed - Restart required",
|
||||
|
||||
@ -30,6 +30,7 @@ const ffmpeg: SectionConfigOverrides = {
|
||||
output_args: "/configuration/ffmpeg_presets#output-args-presets",
|
||||
"inputs.output_args": "/configuration/ffmpeg_presets#output-args-presets",
|
||||
"output_args.record": "/configuration/ffmpeg_presets#output-args-presets",
|
||||
"inputs.roles": "/configuration/cameras/#setting-up-camera-inputs",
|
||||
},
|
||||
restartRequired: [],
|
||||
fieldOrder: [
|
||||
@ -85,6 +86,9 @@ const ffmpeg: SectionConfigOverrides = {
|
||||
},
|
||||
roles: {
|
||||
"ui:widget": "inputRoles",
|
||||
"ui:options": {
|
||||
showArrayItemDescription: true,
|
||||
},
|
||||
},
|
||||
global_args: {
|
||||
"ui:widget": "hidden",
|
||||
@ -92,10 +96,13 @@ const ffmpeg: SectionConfigOverrides = {
|
||||
hwaccel_args: ffmpegArgsWidget("hwaccel_args", {
|
||||
allowInherit: true,
|
||||
hideDescription: true,
|
||||
forceSplitLayout: true,
|
||||
showArrayItemDescription: true,
|
||||
}),
|
||||
input_args: ffmpegArgsWidget("input_args", {
|
||||
allowInherit: true,
|
||||
hideDescription: true,
|
||||
forceSplitLayout: true,
|
||||
showArrayItemDescription: true,
|
||||
}),
|
||||
output_args: {
|
||||
|
||||
@ -87,7 +87,7 @@ const normalizeNonDetectHwaccel = (inputs: FfmpegInput[]): FfmpegInput[] =>
|
||||
|
||||
return {
|
||||
...input,
|
||||
hwaccel_args: null,
|
||||
hwaccel_args: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
@ -311,8 +311,9 @@ export function CameraInputsField(props: FieldProps) {
|
||||
const itemTitle = t("configForm.cameraInputs.itemTitle", {
|
||||
ns: "views/settings",
|
||||
index: index + 1,
|
||||
path: typeof input.path === "string" ? input.path.trim() : "",
|
||||
});
|
||||
const itemPath =
|
||||
typeof input.path === "string" ? input.path.trim() : "";
|
||||
|
||||
return (
|
||||
<Card key={`${baseId}-${index}`} className="w-full">
|
||||
@ -328,7 +329,14 @@ export function CameraInputsField(props: FieldProps) {
|
||||
<CollapsibleTrigger asChild>
|
||||
<CardHeader className="cursor-pointer p-4 transition-colors hover:bg-muted/50">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<CardTitle className="text-sm">{itemTitle}</CardTitle>
|
||||
<CardTitle className="text-sm">
|
||||
<span>{itemTitle}</span>
|
||||
{itemPath ? (
|
||||
<span className="mt-1 block text-xs font-normal text-muted-foreground">
|
||||
{itemPath}
|
||||
</span>
|
||||
) : null}
|
||||
</CardTitle>
|
||||
{open ? (
|
||||
<LuChevronDown className="h-4 w-4" />
|
||||
) : (
|
||||
@ -352,11 +360,7 @@ export function CameraInputsField(props: FieldProps) {
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
{renderField(index, "roles", {
|
||||
showSchemaDescription: true,
|
||||
})}
|
||||
</div>
|
||||
<div className="w-full">{renderField(index, "roles")}</div>
|
||||
|
||||
{renderField(index, "input_args")}
|
||||
|
||||
|
||||
@ -135,9 +135,10 @@ export function FieldTemplate(props: FieldTemplateProps) {
|
||||
!isMultiSchemaWrapper &&
|
||||
!isObjectField &&
|
||||
!isAdditionalProperty;
|
||||
const forceSplitLayout = uiOptionsFromSchema.forceSplitLayout === true;
|
||||
const useSplitLayout =
|
||||
uiOptionsFromSchema.splitLayout !== false &&
|
||||
isScalarValueField &&
|
||||
(isScalarValueField || forceSplitLayout) &&
|
||||
!isBoolean &&
|
||||
!isMultiSchemaWrapper &&
|
||||
!isObjectField &&
|
||||
|
||||
@ -64,6 +64,10 @@ const resolveMode = (
|
||||
return "inherit";
|
||||
}
|
||||
|
||||
if (allowInherit && Array.isArray(value) && value.length === 0) {
|
||||
return "inherit";
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return "manual";
|
||||
}
|
||||
@ -116,6 +120,7 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
const presetField = options?.ffmpegPresetField as PresetField | undefined;
|
||||
const allowInherit = options?.allowInherit === true;
|
||||
const hideDescription = options?.hideDescription === true;
|
||||
const useSplitLayout = options?.splitLayout !== false;
|
||||
|
||||
const { data } = useSWR<FfmpegPresetResponse>("ffmpeg/presets");
|
||||
|
||||
@ -148,7 +153,7 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
setMode(nextMode);
|
||||
|
||||
if (nextMode === "inherit") {
|
||||
onChange(null);
|
||||
onChange(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -164,10 +169,15 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "preset") {
|
||||
onChange("");
|
||||
return;
|
||||
}
|
||||
|
||||
const manualText = normalizeManualText(value);
|
||||
onChange(manualText);
|
||||
},
|
||||
[onChange, presetOptions, value],
|
||||
[mode, onChange, presetOptions, value],
|
||||
);
|
||||
|
||||
const handlePresetChange = useCallback(
|
||||
@ -237,6 +247,23 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
onValueChange={(next) => handleModeChange(next as FfmpegArgsMode)}
|
||||
className="gap-3"
|
||||
>
|
||||
{allowInherit ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="inherit"
|
||||
id={`${id}-inherit`}
|
||||
disabled={disabled || readonly}
|
||||
className={
|
||||
mode === "inherit"
|
||||
? "bg-selected from-selected/50 to-selected/90 text-selected"
|
||||
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
|
||||
}
|
||||
/>
|
||||
<label htmlFor={`${id}-inherit`} className="cursor-pointer text-sm">
|
||||
{t("configForm.ffmpegArgs.inherit", { ns: "views/settings" })}
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="preset"
|
||||
@ -267,23 +294,6 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
{t("configForm.ffmpegArgs.manual", { ns: "views/settings" })}
|
||||
</label>
|
||||
</div>
|
||||
{allowInherit ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem
|
||||
value="inherit"
|
||||
id={`${id}-inherit`}
|
||||
disabled={disabled || readonly}
|
||||
className={
|
||||
mode === "inherit"
|
||||
? "bg-selected from-selected/50 to-selected/90 text-selected"
|
||||
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
|
||||
}
|
||||
/>
|
||||
<label htmlFor={`${id}-inherit`} className="cursor-pointer text-sm">
|
||||
{t("configForm.ffmpegArgs.inherit", { ns: "views/settings" })}
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
</RadioGroup>
|
||||
|
||||
{mode === "inherit" ? null : mode === "preset" && canUsePresets ? (
|
||||
@ -292,7 +302,7 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
onValueChange={handlePresetChange}
|
||||
disabled={disabled || readonly}
|
||||
>
|
||||
<SelectTrigger id={id} className="w-full">
|
||||
<SelectTrigger id={id} className="w-full md:max-w-md">
|
||||
<SelectValue
|
||||
placeholder={
|
||||
placeholder ||
|
||||
@ -326,7 +336,7 @@ export function FfmpegArgsWidget(props: WidgetProps) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{!hideDescription && fieldDescription ? (
|
||||
{!hideDescription && !useSplitLayout && fieldDescription ? (
|
||||
<p className="text-xs text-muted-foreground">{fieldDescription}</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@ -35,31 +35,33 @@ export function InputRolesWidget(props: WidgetProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2 rounded-lg bg-secondary p-2 pr-0 md:max-w-md">
|
||||
{INPUT_ROLES.map((role) => {
|
||||
const checked = selectedRoles.includes(role);
|
||||
const label = t(`configForm.inputRoles.options.${role}`, {
|
||||
ns: "views/settings",
|
||||
defaultValue: role,
|
||||
});
|
||||
<div className="rounded-lg border border-secondary-highlight bg-background_alt p-2 pr-0 md:max-w-md">
|
||||
<div className="grid gap-2">
|
||||
{INPUT_ROLES.map((role) => {
|
||||
const checked = selectedRoles.includes(role);
|
||||
const label = t(`configForm.inputRoles.options.${role}`, {
|
||||
ns: "views/settings",
|
||||
defaultValue: role,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
key={role}
|
||||
className="flex items-center justify-between rounded-md px-3 py-0"
|
||||
>
|
||||
<label htmlFor={`${id}-${role}`} className="text-sm">
|
||||
{label}
|
||||
</label>
|
||||
<Switch
|
||||
id={`${id}-${role}`}
|
||||
checked={checked}
|
||||
disabled={disabled || readonly}
|
||||
onCheckedChange={(enabled) => toggleRole(role, !!enabled)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<div
|
||||
key={role}
|
||||
className="flex items-center justify-between rounded-md px-3 py-0"
|
||||
>
|
||||
<label htmlFor={`${id}-${role}`} className="text-sm">
|
||||
{label}
|
||||
</label>
|
||||
<Switch
|
||||
id={`${id}-${role}`}
|
||||
checked={checked}
|
||||
disabled={disabled || readonly}
|
||||
onCheckedChange={(enabled) => toggleRole(role, !!enabled)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user