mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 17:12:16 +03:00
add global sections, camera config overrides, and reset button
This commit is contained in:
@@ -11,8 +11,17 @@ import { MotionSection } from "@/components/config-form/sections/MotionSection";
|
||||
import { ObjectsSection } from "@/components/config-form/sections/ObjectsSection";
|
||||
import { ReviewSection } from "@/components/config-form/sections/ReviewSection";
|
||||
import { AudioSection } from "@/components/config-form/sections/AudioSection";
|
||||
import { AudioTranscriptionSection } from "@/components/config-form/sections/AudioTranscriptionSection";
|
||||
import { BirdseyeSection } from "@/components/config-form/sections/BirdseyeSection";
|
||||
import { CameraMqttSection } from "@/components/config-form/sections/CameraMqttSection";
|
||||
import { CameraUiSection } from "@/components/config-form/sections/CameraUiSection";
|
||||
import { FaceRecognitionSection } from "@/components/config-form/sections/FaceRecognitionSection";
|
||||
import { FfmpegSection } from "@/components/config-form/sections/FfmpegSection";
|
||||
import { LprSection } from "@/components/config-form/sections/LprSection";
|
||||
import { NotificationsSection } from "@/components/config-form/sections/NotificationsSection";
|
||||
import { OnvifSection } from "@/components/config-form/sections/OnvifSection";
|
||||
import { LiveSection } from "@/components/config-form/sections/LiveSection";
|
||||
import { SemanticSearchSection } from "@/components/config-form/sections/SemanticSearchSection";
|
||||
import { TimestampSection } from "@/components/config-form/sections/TimestampSection";
|
||||
import { useAllCameraOverrides } from "@/hooks/use-config-override";
|
||||
import type { FrigateConfig } from "@/types/frigateConfig";
|
||||
@@ -179,8 +188,17 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
"config/objects",
|
||||
"config/review",
|
||||
"config/audio",
|
||||
"config/audio_transcription",
|
||||
"config/birdseye",
|
||||
"config/camera_mqtt",
|
||||
"config/camera_ui",
|
||||
"config/face_recognition",
|
||||
"config/ffmpeg",
|
||||
"config/lpr",
|
||||
"config/notifications",
|
||||
"config/onvif",
|
||||
"config/live",
|
||||
"config/semantic_search",
|
||||
"config/timestamp_style",
|
||||
"views/settings",
|
||||
"common",
|
||||
@@ -200,12 +218,23 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
);
|
||||
}
|
||||
|
||||
const sections = [
|
||||
const sections: Array<{
|
||||
key: string;
|
||||
i18nNamespace: string;
|
||||
component: typeof DetectSection;
|
||||
showOverrideIndicator?: boolean;
|
||||
}> = [
|
||||
{
|
||||
key: "detect",
|
||||
i18nNamespace: "config/detect",
|
||||
component: DetectSection,
|
||||
},
|
||||
{
|
||||
key: "ffmpeg",
|
||||
i18nNamespace: "config/ffmpeg",
|
||||
component: FfmpegSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "record",
|
||||
i18nNamespace: "config/record",
|
||||
@@ -232,12 +261,60 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
component: ReviewSection,
|
||||
},
|
||||
{ key: "audio", i18nNamespace: "config/audio", component: AudioSection },
|
||||
{
|
||||
key: "audio_transcription",
|
||||
i18nNamespace: "config/audio_transcription",
|
||||
component: AudioTranscriptionSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "notifications",
|
||||
i18nNamespace: "config/notifications",
|
||||
component: NotificationsSection,
|
||||
},
|
||||
{ key: "live", i18nNamespace: "config/live", component: LiveSection },
|
||||
{
|
||||
key: "birdseye",
|
||||
i18nNamespace: "config/birdseye",
|
||||
component: BirdseyeSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "face_recognition",
|
||||
i18nNamespace: "config/face_recognition",
|
||||
component: FaceRecognitionSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "lpr",
|
||||
i18nNamespace: "config/lpr",
|
||||
component: LprSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "semantic_search",
|
||||
i18nNamespace: "config/semantic_search",
|
||||
component: SemanticSearchSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "mqtt",
|
||||
i18nNamespace: "config/camera_mqtt",
|
||||
component: CameraMqttSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "onvif",
|
||||
i18nNamespace: "config/onvif",
|
||||
component: OnvifSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "ui",
|
||||
i18nNamespace: "config/camera_ui",
|
||||
component: CameraUiSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "timestamp_style",
|
||||
i18nNamespace: "config/timestamp_style",
|
||||
@@ -273,9 +350,9 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
<span>{sectionLabel}</span>
|
||||
{isOverridden && (
|
||||
<Badge variant="secondary" className="h-5 px-1.5 text-xs">
|
||||
{t("button.modified", {
|
||||
{t("button.overridden", {
|
||||
ns: "common",
|
||||
defaultValue: "Modified",
|
||||
defaultValue: "Overridden",
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
@@ -298,7 +375,7 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
<SectionComponent
|
||||
level="camera"
|
||||
cameraName={cameraName}
|
||||
showOverrideIndicator
|
||||
showOverrideIndicator={section.showOverrideIndicator !== false}
|
||||
onSave={onSave}
|
||||
showTitle={true}
|
||||
/>
|
||||
|
||||
@@ -79,6 +79,7 @@ const globalSectionConfigs: Record<
|
||||
"topic_prefix",
|
||||
"client_id",
|
||||
"stats_interval",
|
||||
"qos",
|
||||
"tls_ca_certs",
|
||||
"tls_client_cert",
|
||||
"tls_client_key",
|
||||
@@ -86,6 +87,7 @@ const globalSectionConfigs: Record<
|
||||
],
|
||||
advancedFields: [
|
||||
"stats_interval",
|
||||
"qos",
|
||||
"tls_ca_certs",
|
||||
"tls_client_cert",
|
||||
"tls_client_key",
|
||||
@@ -102,17 +104,71 @@ const globalSectionConfigs: Record<
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"reset_admin_password",
|
||||
"cookie_name",
|
||||
"cookie_secure",
|
||||
"session_length",
|
||||
"refresh_time",
|
||||
"native_oauth_url",
|
||||
"failed_login_rate_limit",
|
||||
"trusted_proxies",
|
||||
"hash_iterations",
|
||||
"roles",
|
||||
"admin_first_time_login",
|
||||
],
|
||||
advancedFields: [
|
||||
"cookie_name",
|
||||
"cookie_secure",
|
||||
"session_length",
|
||||
"refresh_time",
|
||||
"failed_login_rate_limit",
|
||||
"trusted_proxies",
|
||||
"hash_iterations",
|
||||
"roles",
|
||||
"admin_first_time_login",
|
||||
],
|
||||
advancedFields: ["failed_login_rate_limit", "trusted_proxies"],
|
||||
},
|
||||
tls: {
|
||||
i18nNamespace: "config/tls",
|
||||
fieldOrder: ["enabled", "cert", "key"],
|
||||
advancedFields: [],
|
||||
},
|
||||
networking: {
|
||||
i18nNamespace: "config/networking",
|
||||
fieldOrder: ["ipv6"],
|
||||
advancedFields: [],
|
||||
},
|
||||
proxy: {
|
||||
i18nNamespace: "config/proxy",
|
||||
fieldOrder: [
|
||||
"header_map",
|
||||
"logout_url",
|
||||
"auth_secret",
|
||||
"default_role",
|
||||
"separator",
|
||||
],
|
||||
advancedFields: ["header_map", "auth_secret", "separator"],
|
||||
},
|
||||
ui: {
|
||||
i18nNamespace: "config/ui",
|
||||
fieldOrder: [
|
||||
"timezone",
|
||||
"time_format",
|
||||
"date_style",
|
||||
"time_style",
|
||||
"unit_system",
|
||||
],
|
||||
advancedFields: [],
|
||||
},
|
||||
logger: {
|
||||
i18nNamespace: "config/logger",
|
||||
fieldOrder: ["default", "logs"],
|
||||
advancedFields: ["logs"],
|
||||
},
|
||||
environment_vars: {
|
||||
i18nNamespace: "config/environment_vars",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
telemetry: {
|
||||
i18nNamespace: "config/telemetry",
|
||||
fieldOrder: ["network_interfaces", "stats", "version_check"],
|
||||
@@ -129,39 +185,191 @@ const globalSectionConfigs: Record<
|
||||
"mode",
|
||||
"layout",
|
||||
"inactivity_threshold",
|
||||
"idle_heartbeat_fps",
|
||||
],
|
||||
advancedFields: ["width", "height", "quality", "inactivity_threshold"],
|
||||
},
|
||||
ffmpeg: {
|
||||
i18nNamespace: "config/ffmpeg",
|
||||
fieldOrder: [
|
||||
"path",
|
||||
"global_args",
|
||||
"hwaccel_args",
|
||||
"input_args",
|
||||
"output_args",
|
||||
"retry_interval",
|
||||
"apple_compatibility",
|
||||
"gpu",
|
||||
],
|
||||
advancedFields: [
|
||||
"global_args",
|
||||
"hwaccel_args",
|
||||
"input_args",
|
||||
"output_args",
|
||||
"retry_interval",
|
||||
"apple_compatibility",
|
||||
"gpu",
|
||||
],
|
||||
},
|
||||
detectors: {
|
||||
i18nNamespace: "config/detectors",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
model: {
|
||||
i18nNamespace: "config/model",
|
||||
fieldOrder: [
|
||||
"path",
|
||||
"labelmap_path",
|
||||
"width",
|
||||
"height",
|
||||
"input_pixel_format",
|
||||
"input_tensor",
|
||||
"input_dtype",
|
||||
"model_type",
|
||||
"labelmap",
|
||||
"attributes_map",
|
||||
],
|
||||
advancedFields: [
|
||||
"labelmap",
|
||||
"attributes_map",
|
||||
"input_pixel_format",
|
||||
"input_tensor",
|
||||
"input_dtype",
|
||||
"model_type",
|
||||
],
|
||||
},
|
||||
genai: {
|
||||
i18nNamespace: "config/genai",
|
||||
fieldOrder: [
|
||||
"provider",
|
||||
"api_key",
|
||||
"base_url",
|
||||
"model",
|
||||
"provider_options",
|
||||
"runtime_options",
|
||||
],
|
||||
advancedFields: ["base_url", "provider_options", "runtime_options"],
|
||||
},
|
||||
classification: {
|
||||
i18nNamespace: "config/classification",
|
||||
fieldOrder: ["bird", "custom"],
|
||||
advancedFields: [],
|
||||
},
|
||||
semantic_search: {
|
||||
i18nNamespace: "config/semantic_search",
|
||||
fieldOrder: ["enabled", "reindex", "model_size"],
|
||||
advancedFields: ["reindex"],
|
||||
fieldOrder: ["enabled", "reindex", "model", "model_size", "device"],
|
||||
advancedFields: ["reindex", "device"],
|
||||
},
|
||||
audio_transcription: {
|
||||
i18nNamespace: "config/audio_transcription",
|
||||
fieldOrder: ["enabled", "language", "device", "model_size", "live_enabled"],
|
||||
advancedFields: ["language", "device", "model_size"],
|
||||
},
|
||||
face_recognition: {
|
||||
i18nNamespace: "config/face_recognition",
|
||||
fieldOrder: ["enabled", "threshold", "min_area", "model_size"],
|
||||
advancedFields: ["threshold", "min_area"],
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"model_size",
|
||||
"unknown_score",
|
||||
"detection_threshold",
|
||||
"recognition_threshold",
|
||||
"min_area",
|
||||
"min_faces",
|
||||
"save_attempts",
|
||||
"blur_confidence_filter",
|
||||
"device",
|
||||
],
|
||||
advancedFields: [
|
||||
"unknown_score",
|
||||
"detection_threshold",
|
||||
"recognition_threshold",
|
||||
"min_area",
|
||||
"min_faces",
|
||||
"save_attempts",
|
||||
"blur_confidence_filter",
|
||||
"device",
|
||||
],
|
||||
},
|
||||
lpr: {
|
||||
i18nNamespace: "config/lpr",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"threshold",
|
||||
"min_area",
|
||||
"min_ratio",
|
||||
"max_ratio",
|
||||
"model_size",
|
||||
"detection_threshold",
|
||||
"min_area",
|
||||
"recognition_threshold",
|
||||
"min_plate_length",
|
||||
"format",
|
||||
"match_distance",
|
||||
"known_plates",
|
||||
"enhancement",
|
||||
"debug_save_plates",
|
||||
"device",
|
||||
"replace_rules",
|
||||
],
|
||||
advancedFields: ["threshold", "min_area", "min_ratio", "max_ratio"],
|
||||
advancedFields: [
|
||||
"detection_threshold",
|
||||
"recognition_threshold",
|
||||
"min_plate_length",
|
||||
"format",
|
||||
"match_distance",
|
||||
"known_plates",
|
||||
"enhancement",
|
||||
"debug_save_plates",
|
||||
"device",
|
||||
"replace_rules",
|
||||
],
|
||||
},
|
||||
go2rtc: {
|
||||
i18nNamespace: "config/go2rtc",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
camera_groups: {
|
||||
i18nNamespace: "config/camera_groups",
|
||||
fieldOrder: ["cameras", "icon", "order"],
|
||||
advancedFields: [],
|
||||
},
|
||||
safe_mode: {
|
||||
i18nNamespace: "config/safe_mode",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
version: {
|
||||
i18nNamespace: "config/version",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
};
|
||||
|
||||
// System sections (global only)
|
||||
const systemSections = ["database", "tls", "auth", "telemetry", "birdseye"];
|
||||
const systemSections = [
|
||||
"database",
|
||||
"tls",
|
||||
"auth",
|
||||
"networking",
|
||||
"proxy",
|
||||
"ui",
|
||||
"logger",
|
||||
"environment_vars",
|
||||
"telemetry",
|
||||
"birdseye",
|
||||
"ffmpeg",
|
||||
"detectors",
|
||||
"model",
|
||||
"classification",
|
||||
"go2rtc",
|
||||
"camera_groups",
|
||||
"safe_mode",
|
||||
"version",
|
||||
];
|
||||
|
||||
// Integration sections (global only)
|
||||
const integrationSections = [
|
||||
"mqtt",
|
||||
"audio_transcription",
|
||||
"genai",
|
||||
"semantic_search",
|
||||
"face_recognition",
|
||||
"lpr",
|
||||
@@ -186,18 +394,12 @@ const GlobalConfigSection = memo(function GlobalConfigSection({
|
||||
"views/settings",
|
||||
"common",
|
||||
]);
|
||||
const [pendingData, setPendingData] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
const [pendingData, setPendingData] = useState<unknown | null>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
const formData = useMemo((): Record<string, unknown> => {
|
||||
if (!config) return {} as Record<string, unknown>;
|
||||
const value = (config as unknown as Record<string, unknown>)[sectionKey];
|
||||
return (
|
||||
(value as Record<string, unknown>) || ({} as Record<string, unknown>)
|
||||
);
|
||||
const formData = useMemo((): unknown => {
|
||||
if (!config) return {};
|
||||
return (config as unknown as Record<string, unknown>)[sectionKey];
|
||||
}, [config, sectionKey]);
|
||||
|
||||
const hasChanges = useMemo(() => {
|
||||
@@ -205,7 +407,7 @@ const GlobalConfigSection = memo(function GlobalConfigSection({
|
||||
return !isEqual(formData, pendingData);
|
||||
}, [formData, pendingData]);
|
||||
|
||||
const handleChange = useCallback((data: Record<string, unknown>) => {
|
||||
const handleChange = useCallback((data: unknown) => {
|
||||
setPendingData(data);
|
||||
}, []);
|
||||
|
||||
@@ -300,14 +502,29 @@ export default function GlobalConfigView() {
|
||||
"config/live",
|
||||
"config/timestamp_style",
|
||||
"config/mqtt",
|
||||
"config/audio_transcription",
|
||||
"config/database",
|
||||
"config/auth",
|
||||
"config/tls",
|
||||
"config/networking",
|
||||
"config/proxy",
|
||||
"config/ui",
|
||||
"config/logger",
|
||||
"config/environment_vars",
|
||||
"config/telemetry",
|
||||
"config/birdseye",
|
||||
"config/ffmpeg",
|
||||
"config/detectors",
|
||||
"config/model",
|
||||
"config/genai",
|
||||
"config/classification",
|
||||
"config/semantic_search",
|
||||
"config/face_recognition",
|
||||
"config/lpr",
|
||||
"config/go2rtc",
|
||||
"config/camera_groups",
|
||||
"config/safe_mode",
|
||||
"config/version",
|
||||
"common",
|
||||
]);
|
||||
const [activeTab, setActiveTab] = useState("shared");
|
||||
|
||||
Reference in New Issue
Block a user