diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 425a282d4..63adae73d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -52,7 +52,8 @@ "csstools.postcss", "blanu.vscode-styled-jsx", "bradlc.vscode-tailwindcss", - "charliermarsh.ruff" + "charliermarsh.ruff", + "eamodio.gitlens" ], "settings": { "remote.autoForwardPorts": false, diff --git a/.gitignore b/.gitignore index 195708e2d..8456d9be0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store -*.pyc +__pycache__ +.mypy_cache *.swp debug .vscode/* diff --git a/frigate/api/app.py b/frigate/api/app.py index b1b3b8449..889bbea74 100644 --- a/frigate/api/app.py +++ b/frigate/api/app.py @@ -248,7 +248,7 @@ def config_save(): # Validate the config schema try: - FrigateConfig.parse_raw(new_config) + FrigateConfig.parse_yaml(new_config) except Exception: return make_response( jsonify( @@ -336,7 +336,7 @@ def config_set(): f.close() # Validate the config schema try: - config_obj = FrigateConfig.parse_raw(new_raw_config) + config_obj = FrigateConfig.parse_yaml(new_raw_config) except Exception: with open(config_file, "w") as f: f.write(old_raw_config) @@ -361,8 +361,8 @@ def config_set(): json = request.get_json(silent=True) or {} if json.get("requires_restart", 1) == 0: - current_app.frigate_config = FrigateConfig.runtime_config( - config_obj, current_app.plus_api + current_app.frigate_config = FrigateConfig.parse_object( + config_obj, plus_api=current_app.plus_api ) return make_response( diff --git a/frigate/app.py b/frigate/app.py index 0059cbfe2..f6479d9ad 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -129,8 +129,7 @@ class FrigateApp: # check if the config file needs to be migrated migrate_frigate_config(config_file) - user_config = FrigateConfig.parse_file(config_file) - self.config = user_config.runtime_config(self.plus_api) + self.config = FrigateConfig.parse_file(config_file, plus_api=self.plus_api) for camera_name in self.config.cameras.keys(): # create camera_metrics diff --git a/frigate/config.py b/frigate/config.py index b1af9c51b..aec525076 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -6,10 +6,11 @@ import os import shutil from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Annotated, Any, Dict, List, Optional, Tuple, Union import numpy as np from pydantic import ( + AfterValidator, BaseModel, ConfigDict, Field, @@ -17,8 +18,11 @@ from pydantic import ( ValidationInfo, field_serializer, field_validator, + model_validator, ) from pydantic.fields import PrivateAttr +from ruamel.yaml import YAML +from typing_extensions import Self from frigate.const import ( ALL_ATTRIBUTE_LABELS, @@ -31,7 +35,7 @@ from frigate.const import ( INCLUDED_FFMPEG_VERSIONS, MAX_PRE_CAPTURE, REGEX_CAMERA_NAME, - YAML_EXT, + REGEX_JSON, ) from frigate.detectors import DetectorConfig, ModelConfig from frigate.detectors.detector_config import BaseDetectorConfig @@ -41,13 +45,11 @@ from frigate.ffmpeg_presets import ( parse_preset_input, parse_preset_output_record, ) -from frigate.plus import PlusApi from frigate.util.builtin import ( deep_merge, escape_special_characters, generate_color_palette, get_ffmpeg_arg_list, - load_config_with_no_duplicates, ) from frigate.util.config import StreamInfoRetriever, get_relative_coordinates from frigate.util.image import create_mask @@ -55,6 +57,8 @@ from frigate.util.services import auto_detect_hwaccel logger = logging.getLogger(__name__) +yaml = YAML() + # TODO: Identify what the default format to display timestamps is DEFAULT_TIME_FORMAT = "%m/%d/%Y %H:%M:%S" # German Style: @@ -103,6 +107,13 @@ class DateTimeStyleEnum(str, Enum): short = "short" +def validate_env_string(v: str) -> str: + return v.format(**FRIGATE_ENV_VARS) + + +EnvString = Annotated[str, AfterValidator(validate_env_string)] + + class UIConfig(FrigateBaseModel): timezone: Optional[str] = Field(default=None, title="Override UI timezone.") time_format: TimeFormatEnum = Field( @@ -137,7 +148,7 @@ class ProxyConfig(FrigateBaseModel): logout_url: Optional[str] = Field( default=None, title="Redirect url for logging out with proxy." ) - auth_secret: Optional[str] = Field( + auth_secret: Optional[EnvString] = Field( default=None, title="Secret value for proxy authentication.", ) @@ -208,8 +219,10 @@ class MqttConfig(FrigateBaseModel): stats_interval: int = Field( default=60, ge=FREQUENCY_STATS_POINTS, title="MQTT Camera Stats Interval" ) - user: Optional[str] = Field(None, title="MQTT Username") - password: Optional[str] = Field(None, title="MQTT Password", validate_default=True) + user: Optional[EnvString] = Field(None, title="MQTT Username") + password: Optional[EnvString] = Field( + None, title="MQTT Password", validate_default=True + ) tls_ca_certs: Optional[str] = Field(None, title="MQTT TLS CA Certificates") tls_client_cert: Optional[str] = Field(None, title="MQTT TLS Client Certificate") tls_client_key: Optional[str] = Field(None, title="MQTT TLS Client Key") @@ -284,8 +297,8 @@ class PtzAutotrackConfig(FrigateBaseModel): class OnvifConfig(FrigateBaseModel): host: str = Field(default="", title="Onvif Host") port: int = Field(default=8000, title="Onvif Port") - user: Optional[str] = Field(None, title="Onvif Username") - password: Optional[str] = Field(None, title="Onvif Password") + user: Optional[EnvString] = Field(None, title="Onvif Username") + password: Optional[EnvString] = Field(None, title="Onvif Password") autotracking: PtzAutotrackConfig = Field( default_factory=PtzAutotrackConfig, title="PTZ auto tracking config.", @@ -756,7 +769,7 @@ class GenAIConfig(FrigateBaseModel): default=GenAIProviderEnum.openai, title="GenAI provider." ) base_url: Optional[str] = Field(None, title="Provider base url.") - api_key: Optional[str] = Field(None, title="Provider API key.") + api_key: Optional[EnvString] = Field(None, title="Provider API key.") model: str = Field(default="gpt-4o", title="GenAI model.") prompt: str = Field( default="Describe the {label} in the sequence of images with as much detail as possible. Do not describe the background.", @@ -926,7 +939,7 @@ class CameraRoleEnum(str, Enum): class CameraInput(FrigateBaseModel): - path: str = Field(title="Camera input path.") + path: EnvString = Field(title="Camera input path.") roles: List[CameraRoleEnum] = Field(title="Roles assigned to this input.") global_args: Union[str, List[str]] = Field( default_factory=list, title="FFmpeg global arguments." @@ -1346,17 +1359,15 @@ def verify_recording_segments_setup_with_reasonable_time( if record_args[0].startswith("preset"): return - seg_arg_index = record_args.index("-segment_time") - - if seg_arg_index < 0: - raise ValueError( - f"Camera {camera_config.name} has no segment_time in recording output args, segment args are required for record." - ) + try: + seg_arg_index = record_args.index("-segment_time") + except ValueError: + raise ValueError(f"Camera {camera_config.name} has no segment_time in \ + recording output args, segment args are required for record.") if int(record_args[seg_arg_index + 1]) > 60: - raise ValueError( - f"Camera {camera_config.name} has invalid segment_time output arg, segment_time must be 60 or less." - ) + raise ValueError(f"Camera {camera_config.name} has invalid segment_time output arg, \ + segment_time must be 60 or less.") def verify_zone_objects_are_tracked(camera_config: CameraConfig) -> None: @@ -1481,41 +1492,28 @@ class FrigateConfig(FrigateBaseModel): ) version: Optional[str] = Field(default=None, title="Current config version.") - def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig: - """Merge camera config with globals.""" - config = self.model_copy(deep=True) - - # Proxy secret substitution - if config.proxy.auth_secret: - config.proxy.auth_secret = config.proxy.auth_secret.format( - **FRIGATE_ENV_VARS - ) - - # MQTT user/password substitutions - if config.mqtt.user or config.mqtt.password: - config.mqtt.user = config.mqtt.user.format(**FRIGATE_ENV_VARS) - config.mqtt.password = config.mqtt.password.format(**FRIGATE_ENV_VARS) + @model_validator(mode="after") + def post_validation(self, info: ValidationInfo) -> Self: + plus_api = None + if isinstance(info.context, dict): + plus_api = info.context.get("plus_api") # set notifications state - config.notifications.enabled_in_config = config.notifications.enabled - - # GenAI substitution - if config.genai.api_key: - config.genai.api_key = config.genai.api_key.format(**FRIGATE_ENV_VARS) + self.notifications.enabled_in_config = self.notifications.enabled # set default min_score for object attributes for attribute in ALL_ATTRIBUTE_LABELS: - if not config.objects.filters.get(attribute): - config.objects.filters[attribute] = FilterConfig(min_score=0.7) - elif config.objects.filters[attribute].min_score == 0.5: - config.objects.filters[attribute].min_score = 0.7 + if not self.objects.filters.get(attribute): + self.objects.filters[attribute] = FilterConfig(min_score=0.7) + elif self.objects.filters[attribute].min_score == 0.5: + self.objects.filters[attribute].min_score = 0.7 # auto detect hwaccel args - if config.ffmpeg.hwaccel_args == "auto": - config.ffmpeg.hwaccel_args = auto_detect_hwaccel() + if self.ffmpeg.hwaccel_args == "auto": + self.ffmpeg.hwaccel_args = auto_detect_hwaccel() # Global config to propagate down to camera level - global_config = config.model_dump( + global_config = self.model_dump( include={ "audio": ..., "birdseye": ..., @@ -1533,7 +1531,7 @@ class FrigateConfig(FrigateBaseModel): exclude_unset=True, ) - for name, camera in config.cameras.items(): + for name, camera in self.cameras.items(): merged_config = deep_merge( camera.model_dump(exclude_unset=True), global_config ) @@ -1542,7 +1540,7 @@ class FrigateConfig(FrigateBaseModel): ) if camera_config.ffmpeg.hwaccel_args == "auto": - camera_config.ffmpeg.hwaccel_args = config.ffmpeg.hwaccel_args + camera_config.ffmpeg.hwaccel_args = self.ffmpeg.hwaccel_args for input in camera_config.ffmpeg.inputs: need_record_fourcc = False and "record" in input.roles @@ -1555,7 +1553,7 @@ class FrigateConfig(FrigateBaseModel): stream_info = {"width": 0, "height": 0, "fourcc": None} try: stream_info = stream_info_retriever.get_stream_info( - config.ffmpeg, input.path + self.ffmpeg, input.path ) except Exception: logger.warn( @@ -1607,18 +1605,6 @@ class FrigateConfig(FrigateBaseModel): if camera_config.detect.stationary.interval is None: camera_config.detect.stationary.interval = stationary_threshold - # FFMPEG input substitution - for input in camera_config.ffmpeg.inputs: - input.path = input.path.format(**FRIGATE_ENV_VARS) - - # ONVIF substitution - if camera_config.onvif.user or camera_config.onvif.password: - camera_config.onvif.user = camera_config.onvif.user.format( - **FRIGATE_ENV_VARS - ) - camera_config.onvif.password = camera_config.onvif.password.format( - **FRIGATE_ENV_VARS - ) # set config pre-value camera_config.audio.enabled_in_config = camera_config.audio.enabled camera_config.record.enabled_in_config = camera_config.record.enabled @@ -1685,8 +1671,12 @@ class FrigateConfig(FrigateBaseModel): if not camera_config.live.stream_name: camera_config.live.stream_name = name + # generate the ffmpeg commands + camera_config.create_ffmpeg_cmds() + self.cameras[name] = camera_config + verify_config_roles(camera_config) - verify_valid_live_stream_name(config, camera_config) + verify_valid_live_stream_name(self, camera_config) verify_recording_retention(camera_config) verify_recording_segments_setup_with_reasonable_time(camera_config) verify_zone_objects_are_tracked(camera_config) @@ -1694,20 +1684,16 @@ class FrigateConfig(FrigateBaseModel): verify_autotrack_zones(camera_config) verify_motion_and_detect(camera_config) - # generate the ffmpeg commands - camera_config.create_ffmpeg_cmds() - config.cameras[name] = camera_config - # get list of unique enabled labels for tracking - enabled_labels = set(config.objects.track) + enabled_labels = set(self.objects.track) - for _, camera in config.cameras.items(): + for camera in self.cameras.values(): enabled_labels.update(camera.objects.track) - config.model.create_colormap(sorted(enabled_labels)) - config.model.check_and_load_plus_model(plus_api) + self.model.create_colormap(sorted(enabled_labels)) + self.model.check_and_load_plus_model(plus_api) - for key, detector in config.detectors.items(): + for key, detector in self.detectors.items(): adapter = TypeAdapter(DetectorConfig) model_dict = ( detector @@ -1716,10 +1702,10 @@ class FrigateConfig(FrigateBaseModel): ) detector_config: DetectorConfig = adapter.validate_python(model_dict) if detector_config.model is None: - detector_config.model = config.model.model_copy() + detector_config.model = self.model.model_copy() else: path = detector_config.model.path - detector_config.model = config.model.model_copy() + detector_config.model = self.model.model_copy() detector_config.model.path = path if "path" not in model_dict or len(model_dict.keys()) > 1: @@ -1729,7 +1715,7 @@ class FrigateConfig(FrigateBaseModel): merged_model = deep_merge( detector_config.model.model_dump(exclude_unset=True, warnings="none"), - config.model.model_dump(exclude_unset=True, warnings="none"), + self.model.model_dump(exclude_unset=True, warnings="none"), ) if "path" not in merged_model: @@ -1743,9 +1729,9 @@ class FrigateConfig(FrigateBaseModel): plus_api, detector_config.type ) detector_config.model.compute_model_hash() - config.detectors[key] = detector_config + self.detectors[key] = detector_config - return config + return self @field_validator("cameras") @classmethod @@ -1757,18 +1743,42 @@ class FrigateConfig(FrigateBaseModel): return v @classmethod - def parse_file(cls, config_file): - with open(config_file) as f: - raw_config = f.read() - - if config_file.endswith(YAML_EXT): - config = load_config_with_no_duplicates(raw_config) - elif config_file.endswith(".json"): - config = json.loads(raw_config) - - return cls.model_validate(config) + def parse_file(cls, config_path, **kwargs): + with open(config_path) as f: + return FrigateConfig.parse(f, **kwargs) @classmethod - def parse_raw(cls, raw_config): - config = load_config_with_no_duplicates(raw_config) - return cls.model_validate(config) + def parse(cls, config, *, is_json=None, **context): + # If config is a file, read its contents. + if hasattr(config, "read"): + fname = getattr(config, "name", None) + config = config.read() + + # Try to guess the value of is_json from the file extension. + if is_json is None and fname: + _, ext = os.path.splitext(fname) + if ext in (".yaml", ".yml"): + is_json = False + elif ext == ".json": + is_json = True + + # At this point, ry to sniff the config string, to guess if it is json or not. + if is_json is None: + is_json = REGEX_JSON.match(config) is not None + + # Parse the config into a dictionary. + if is_json: + config = json.load(config) + else: + config = yaml.load(config) + + # Validate and return the config dict. + return cls.parse_object(config, **context) + + @classmethod + def parse_object(cls, obj: Any, **context): + return cls.model_validate(obj, context=context) + + @classmethod + def parse_yaml(cls, config_yaml, **context): + return cls.parse(config_yaml, is_json=False, **context) diff --git a/frigate/const.py b/frigate/const.py index a0066774b..890fbb3ca 100644 --- a/frigate/const.py +++ b/frigate/const.py @@ -1,3 +1,5 @@ +import re + CONFIG_DIR = "/config" DEFAULT_DB_PATH = f"{CONFIG_DIR}/frigate.db" MODEL_CACHE_DIR = f"{CONFIG_DIR}/model_cache" @@ -7,7 +9,6 @@ RECORD_DIR = f"{BASE_DIR}/recordings" EXPORT_DIR = f"{BASE_DIR}/exports" BIRDSEYE_PIPE = "/tmp/cache/birdseye" CACHE_DIR = "/tmp/cache" -YAML_EXT = (".yaml", ".yml") FRIGATE_LOCALHOST = "http://127.0.0.1:5000" PLUS_ENV_VAR = "PLUS_API_KEY" PLUS_API_HOST = "https://api.frigate.video" @@ -56,6 +57,7 @@ FFMPEG_HWACCEL_VULKAN = "preset-vulkan" REGEX_CAMERA_NAME = r"^[a-zA-Z0-9_-]+$" REGEX_RTSP_CAMERA_USER_PASS = r":\/\/[a-zA-Z0-9_-]+:[\S]+@" REGEX_HTTP_CAMERA_USER_PASS = r"user=[a-zA-Z0-9_-]+&password=[\S]+" +REGEX_JSON = re.compile(r"^\s*\{") # Known Driver Names diff --git a/frigate/test/test_config.py b/frigate/test/test_config.py index c703de893..143609386 100644 --- a/frigate/test/test_config.py +++ b/frigate/test/test_config.py @@ -5,12 +5,12 @@ from unittest.mock import patch import numpy as np from pydantic import ValidationError +from ruamel.yaml.constructor import DuplicateKeyError from frigate.config import BirdseyeModeEnum, FrigateConfig from frigate.const import MODEL_CACHE_DIR from frigate.detectors import DetectorTypeEnum -from frigate.plus import PlusApi -from frigate.util.builtin import deep_merge, load_config_with_no_duplicates +from frigate.util.builtin import deep_merge class TestConfig(unittest.TestCase): @@ -64,12 +64,9 @@ class TestConfig(unittest.TestCase): def test_config_class(self): frigate_config = FrigateConfig(**self.minimal) - assert self.minimal == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert "cpu" in runtime_config.detectors.keys() - assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu - assert runtime_config.detectors["cpu"].model.width == 320 + assert "cpu" in frigate_config.detectors.keys() + assert frigate_config.detectors["cpu"].type == DetectorTypeEnum.cpu + assert frigate_config.detectors["cpu"].model.width == 320 @patch("frigate.detectors.detector_config.load_labels") def test_detector_custom_model_path(self, mock_labels): @@ -93,24 +90,23 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**(deep_merge(config, self.minimal))) - runtime_config = frigate_config.runtime_config() - assert "cpu" in runtime_config.detectors.keys() - assert "edgetpu" in runtime_config.detectors.keys() - assert "openvino" in runtime_config.detectors.keys() + assert "cpu" in frigate_config.detectors.keys() + assert "edgetpu" in frigate_config.detectors.keys() + assert "openvino" in frigate_config.detectors.keys() - assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu - assert runtime_config.detectors["edgetpu"].type == DetectorTypeEnum.edgetpu - assert runtime_config.detectors["openvino"].type == DetectorTypeEnum.openvino + assert frigate_config.detectors["cpu"].type == DetectorTypeEnum.cpu + assert frigate_config.detectors["edgetpu"].type == DetectorTypeEnum.edgetpu + assert frigate_config.detectors["openvino"].type == DetectorTypeEnum.openvino - assert runtime_config.detectors["cpu"].num_threads == 3 - assert runtime_config.detectors["edgetpu"].device is None - assert runtime_config.detectors["openvino"].device is None + assert frigate_config.detectors["cpu"].num_threads == 3 + assert frigate_config.detectors["edgetpu"].device is None + assert frigate_config.detectors["openvino"].device is None - assert runtime_config.model.path == "/etc/hosts" - assert runtime_config.detectors["cpu"].model.path == "/cpu_model.tflite" - assert runtime_config.detectors["edgetpu"].model.path == "/edgetpu_model.tflite" - assert runtime_config.detectors["openvino"].model.path == "/etc/hosts" + assert frigate_config.model.path == "/etc/hosts" + assert frigate_config.detectors["cpu"].model.path == "/cpu_model.tflite" + assert frigate_config.detectors["edgetpu"].model.path == "/edgetpu_model.tflite" + assert frigate_config.detectors["openvino"].model.path == "/etc/hosts" def test_invalid_mqtt_config(self): config = { @@ -151,11 +147,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "dog" in runtime_config.cameras["back"].objects.track + frigate_config = FrigateConfig(**config) + assert "dog" in frigate_config.cameras["back"].objects.track def test_override_birdseye(self): config = { @@ -177,12 +171,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert not runtime_config.cameras["back"].birdseye.enabled - assert runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.motion + frigate_config = FrigateConfig(**config) + assert not frigate_config.cameras["back"].birdseye.enabled + assert frigate_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.motion def test_override_birdseye_non_inheritable(self): config = { @@ -203,11 +195,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].birdseye.enabled + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].birdseye.enabled def test_inherit_birdseye(self): config = { @@ -228,13 +218,11 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].birdseye.enabled + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].birdseye.enabled assert ( - runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.continuous + frigate_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.continuous ) def test_override_tracked_objects(self): @@ -257,11 +245,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "cat" in runtime_config.cameras["back"].objects.track + frigate_config = FrigateConfig(**config) + assert "cat" in frigate_config.cameras["back"].objects.track def test_default_object_filters(self): config = { @@ -282,11 +268,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "dog" in runtime_config.cameras["back"].objects.filters + frigate_config = FrigateConfig(**config) + assert "dog" in frigate_config.cameras["back"].objects.filters def test_inherit_object_filters(self): config = { @@ -310,12 +294,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "dog" in runtime_config.cameras["back"].objects.filters - assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7 + frigate_config = FrigateConfig(**config) + assert "dog" in frigate_config.cameras["back"].objects.filters + assert frigate_config.cameras["back"].objects.filters["dog"].threshold == 0.7 def test_override_object_filters(self): config = { @@ -339,12 +321,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "dog" in runtime_config.cameras["back"].objects.filters - assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7 + frigate_config = FrigateConfig(**config) + assert "dog" in frigate_config.cameras["back"].objects.filters + assert frigate_config.cameras["back"].objects.filters["dog"].threshold == 0.7 def test_global_object_mask(self): config = { @@ -369,11 +349,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - back_camera = runtime_config.cameras["back"] + frigate_config = FrigateConfig(**config) + back_camera = frigate_config.cameras["back"] assert "dog" in back_camera.objects.filters assert len(back_camera.objects.filters["dog"].raw_mask) == 2 assert len(back_camera.objects.filters["person"].raw_mask) == 1 @@ -419,7 +397,8 @@ class TestConfig(unittest.TestCase): }, }, } - frigate_config = FrigateConfig(**config).runtime_config() + + frigate_config = FrigateConfig(**config) assert np.array_equal( frigate_config.cameras["explicit"].motion.mask, frigate_config.cameras["relative"].motion.mask, @@ -448,10 +427,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert "-rtsp_transport" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + assert "-rtsp_transport" in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] def test_ffmpeg_params_global(self): config = { @@ -476,11 +452,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + frigate_config = FrigateConfig(**config) + assert "-re" in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] def test_ffmpeg_params_camera(self): config = { @@ -506,12 +480,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] - assert "test" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + frigate_config = FrigateConfig(**config) + assert "-re" in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + assert "test" not in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] def test_ffmpeg_params_input(self): config = { @@ -541,14 +513,12 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] - assert "test" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] - assert "test2" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] - assert "test3" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + frigate_config = FrigateConfig(**config) + assert "-re" in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + assert "test" in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + assert "test2" not in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] + assert "test3" not in frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] def test_inherit_clips_retention(self): config = { @@ -569,11 +539,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].record.alerts.retain.days == 20 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].record.alerts.retain.days == 20 def test_roles_listed_twice_throws_error(self): config = { @@ -657,14 +625,12 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() + frigate_config = FrigateConfig(**config) assert isinstance( - runtime_config.cameras["back"].zones["test"].contour, np.ndarray + frigate_config.cameras["back"].zones["test"].contour, np.ndarray ) - assert runtime_config.cameras["back"].zones["test"].color != (0, 0, 0) + assert frigate_config.cameras["back"].zones["test"].color != (0, 0, 0) def test_zone_relative_matches_explicit(self): config = { @@ -699,7 +665,8 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config).runtime_config() + + frigate_config = FrigateConfig(**config) assert np.array_equal( frigate_config.cameras["back"].zones["explicit"].contour, frigate_config.cameras["back"].zones["relative"].contour, @@ -729,10 +696,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - ffmpeg_cmds = runtime_config.cameras["back"].ffmpeg_cmds + ffmpeg_cmds = frigate_config.cameras["back"].ffmpeg_cmds assert len(ffmpeg_cmds) == 1 assert "clips" not in ffmpeg_cmds[0]["roles"] @@ -760,10 +724,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].detect.max_disappeared == 5 * 5 + assert frigate_config.cameras["back"].detect.max_disappeared == 5 * 5 def test_motion_frame_height_wont_go_below_120(self): config = { @@ -788,10 +749,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].motion.frame_height == 100 + assert frigate_config.cameras["back"].motion.frame_height == 100 def test_motion_contour_area_dynamic(self): config = { @@ -816,10 +774,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert round(runtime_config.cameras["back"].motion.contour_area) == 10 + assert round(frigate_config.cameras["back"].motion.contour_area) == 10 def test_merge_labelmap(self): config = { @@ -845,10 +800,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert runtime_config.model.merged_labelmap[7] == "truck" + assert frigate_config.model.merged_labelmap[7] == "truck" def test_default_labelmap_empty(self): config = { @@ -873,10 +825,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert runtime_config.model.merged_labelmap[0] == "person" + assert frigate_config.model.merged_labelmap[0] == "person" def test_default_labelmap(self): config = { @@ -902,10 +851,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config() - assert runtime_config.model.merged_labelmap[0] == "person" + assert frigate_config.model.merged_labelmap[0] == "person" def test_plus_labelmap(self): with open("/config/model_cache/test", "w") as f: @@ -936,10 +882,7 @@ class TestConfig(unittest.TestCase): } frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - - runtime_config = frigate_config.runtime_config(PlusApi()) - assert runtime_config.model.merged_labelmap[0] == "amazon" + assert frigate_config.model.merged_labelmap[0] == "amazon" def test_fails_on_invalid_role(self): config = { @@ -996,8 +939,7 @@ class TestConfig(unittest.TestCase): }, } - frigate_config = FrigateConfig(**config) - self.assertRaises(ValueError, lambda: frigate_config.runtime_config()) + self.assertRaises(ValueError, lambda: FrigateConfig(**config)) def test_works_on_missing_role_multiple_cams(self): config = { @@ -1044,8 +986,7 @@ class TestConfig(unittest.TestCase): }, } - frigate_config = FrigateConfig(**config) - frigate_config.runtime_config() + FrigateConfig(**config) def test_global_detect(self): config = { @@ -1069,12 +1010,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].detect.max_disappeared == 1 - assert runtime_config.cameras["back"].detect.height == 1080 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].detect.max_disappeared == 1 + assert frigate_config.cameras["back"].detect.height == 1080 def test_default_detect(self): config = { @@ -1097,12 +1036,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].detect.max_disappeared == 25 - assert runtime_config.cameras["back"].detect.height == 720 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].detect.max_disappeared == 25 + assert frigate_config.cameras["back"].detect.height == 720 def test_global_detect_merge(self): config = { @@ -1126,13 +1063,11 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].detect.max_disappeared == 1 - assert runtime_config.cameras["back"].detect.height == 1080 - assert runtime_config.cameras["back"].detect.width == 1920 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].detect.max_disappeared == 1 + assert frigate_config.cameras["back"].detect.height == 1080 + assert frigate_config.cameras["back"].detect.width == 1920 def test_global_snapshots(self): config = { @@ -1159,12 +1094,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].snapshots.enabled - assert runtime_config.cameras["back"].snapshots.height == 100 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].snapshots.enabled + assert frigate_config.cameras["back"].snapshots.height == 100 def test_default_snapshots(self): config = { @@ -1187,12 +1120,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].snapshots.bounding_box - assert runtime_config.cameras["back"].snapshots.quality == 70 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].snapshots.bounding_box + assert frigate_config.cameras["back"].snapshots.quality == 70 def test_global_snapshots_merge(self): config = { @@ -1220,13 +1151,11 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].snapshots.bounding_box is False - assert runtime_config.cameras["back"].snapshots.height == 150 - assert runtime_config.cameras["back"].snapshots.enabled + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].snapshots.bounding_box is False + assert frigate_config.cameras["back"].snapshots.height == 150 + assert frigate_config.cameras["back"].snapshots.enabled def test_global_jsmpeg(self): config = { @@ -1250,11 +1179,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].live.quality == 4 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].live.quality == 4 def test_default_live(self): config = { @@ -1277,11 +1204,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].live.quality == 8 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].live.quality == 8 def test_global_live_merge(self): config = { @@ -1308,12 +1233,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].live.quality == 7 - assert runtime_config.cameras["back"].live.height == 480 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].live.quality == 7 + assert frigate_config.cameras["back"].live.height == 480 def test_global_timestamp_style(self): config = { @@ -1337,11 +1260,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].timestamp_style.position == "bl" + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].timestamp_style.position == "bl" def test_default_timestamp_style(self): config = { @@ -1364,11 +1285,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].timestamp_style.position == "tl" + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].timestamp_style.position == "tl" def test_global_timestamp_style_merge(self): config = { @@ -1393,12 +1312,10 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].timestamp_style.position == "bl" - assert runtime_config.cameras["back"].timestamp_style.thickness == 4 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].timestamp_style.position == "bl" + assert frigate_config.cameras["back"].timestamp_style.thickness == 4 def test_allow_retain_to_be_a_decimal(self): config = { @@ -1422,11 +1339,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].snapshots.retain.default == 1.5 + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].snapshots.retain.default == 1.5 def test_fails_on_bad_camera_name(self): config = { @@ -1451,11 +1366,7 @@ class TestConfig(unittest.TestCase): }, } - frigate_config = FrigateConfig(**config) - - self.assertRaises( - ValidationError, lambda: frigate_config.runtime_config().cameras - ) + self.assertRaises(ValidationError, lambda: FrigateConfig(**config).cameras) def test_fails_on_bad_segment_time(self): config = { @@ -1483,11 +1394,9 @@ class TestConfig(unittest.TestCase): }, } - frigate_config = FrigateConfig(**config) - self.assertRaises( ValueError, - lambda: frigate_config.runtime_config().ffmpeg.output_args.record, + lambda: FrigateConfig(**config).ffmpeg.output_args.record, ) def test_fails_zone_defines_untracked_object(self): @@ -1519,9 +1428,7 @@ class TestConfig(unittest.TestCase): }, } - frigate_config = FrigateConfig(**config) - - self.assertRaises(ValueError, lambda: frigate_config.runtime_config().cameras) + self.assertRaises(ValueError, lambda: FrigateConfig(**config).cameras) def test_fails_duplicate_keys(self): raw_config = """ @@ -1537,7 +1444,7 @@ class TestConfig(unittest.TestCase): """ self.assertRaises( - ValueError, lambda: load_config_with_no_duplicates(raw_config) + DuplicateKeyError, lambda: FrigateConfig.parse_yaml(raw_config) ) def test_object_filter_ratios_work(self): @@ -1562,13 +1469,11 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - assert config == frigate_config.model_dump(exclude_unset=True) - runtime_config = frigate_config.runtime_config() - assert "dog" in runtime_config.cameras["back"].objects.filters - assert runtime_config.cameras["back"].objects.filters["dog"].min_ratio == 0.2 - assert runtime_config.cameras["back"].objects.filters["dog"].max_ratio == 10.1 + frigate_config = FrigateConfig(**config) + assert "dog" in frigate_config.cameras["back"].objects.filters + assert frigate_config.cameras["back"].objects.filters["dog"].min_ratio == 0.2 + assert frigate_config.cameras["back"].objects.filters["dog"].max_ratio == 10.1 def test_valid_movement_weights(self): config = { @@ -1591,10 +1496,9 @@ class TestConfig(unittest.TestCase): } }, } - frigate_config = FrigateConfig(**config) - runtime_config = frigate_config.runtime_config() - assert runtime_config.cameras["back"].onvif.autotracking.movement_weights == [ + frigate_config = FrigateConfig(**config) + assert frigate_config.cameras["back"].onvif.autotracking.movement_weights == [ "0.0", "1.0", "1.23", diff --git a/frigate/test/test_ffmpeg_presets.py b/frigate/test/test_ffmpeg_presets.py index 0275469cd..f28d77de4 100644 --- a/frigate/test/test_ffmpeg_presets.py +++ b/frigate/test/test_ffmpeg_presets.py @@ -36,16 +36,13 @@ class TestFfmpegPresets(unittest.TestCase): } def test_default_ffmpeg(self): - frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() - assert self.default_ffmpeg == frigate_config.dict(exclude_unset=True) + FrigateConfig(**self.default_ffmpeg) def test_ffmpeg_hwaccel_preset(self): self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["hwaccel_args"] = ( "preset-rpi-64-h264" ) frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "preset-rpi-64-h264" not in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -58,7 +55,6 @@ class TestFfmpegPresets(unittest.TestCase): "-other-hwaccel args" ) frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "-other-hwaccel args" in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -73,7 +69,6 @@ class TestFfmpegPresets(unittest.TestCase): "fps": 10, } frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "preset-nvidia-h264" not in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -89,8 +84,6 @@ class TestFfmpegPresets(unittest.TestCase): "preset-rtsp-generic" ) frigate_preset_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() - frigate_preset_config.cameras["back"].create_ffmpeg_cmds() assert ( # Ignore global and user_agent args in comparison frigate_preset_config.cameras["back"].ffmpeg_cmds[0]["cmd"] @@ -102,7 +95,6 @@ class TestFfmpegPresets(unittest.TestCase): "preset-rtmp-generic" ) frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "preset-rtmp-generic" not in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -117,7 +109,6 @@ class TestFfmpegPresets(unittest.TestCase): argsList = defaultArgsList + ["-some", "arg with space"] self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["input_args"] = argsString frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert set(argsList).issubset( frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"] ) @@ -125,7 +116,6 @@ class TestFfmpegPresets(unittest.TestCase): def test_ffmpeg_input_not_preset(self): self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["input_args"] = "-some inputs" frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "-some inputs" in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -135,7 +125,6 @@ class TestFfmpegPresets(unittest.TestCase): "preset-record-generic-audio-aac" ) frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "preset-record-generic-audio-aac" not in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) @@ -145,10 +134,9 @@ class TestFfmpegPresets(unittest.TestCase): def test_ffmpeg_output_record_not_preset(self): self.default_ffmpeg["cameras"]["back"]["ffmpeg"]["output_args"]["record"] = ( - "-some output" + "-some output -segment_time 10" ) frigate_config = FrigateConfig(**self.default_ffmpeg) - frigate_config.cameras["back"].create_ffmpeg_cmds() assert "-some output" in ( " ".join(frigate_config.cameras["back"].ffmpeg_cmds[0]["cmd"]) ) diff --git a/frigate/test/test_http.py b/frigate/test/test_http.py index 936dc80e5..127a82b72 100644 --- a/frigate/test/test_http.py +++ b/frigate/test/test_http.py @@ -345,7 +345,7 @@ class TestHttp(unittest.TestCase): def test_config(self): app = create_app( - FrigateConfig(**self.minimal_config).runtime_config(), + FrigateConfig(**self.minimal_config), self.db, None, None, @@ -363,7 +363,7 @@ class TestHttp(unittest.TestCase): def test_recordings(self): app = create_app( - FrigateConfig(**self.minimal_config).runtime_config(), + FrigateConfig(**self.minimal_config), self.db, None, None, @@ -385,7 +385,7 @@ class TestHttp(unittest.TestCase): stats = Mock(spec=StatsEmitter) stats.get_latest_stats.return_value = self.test_stats app = create_app( - FrigateConfig(**self.minimal_config).runtime_config(), + FrigateConfig(**self.minimal_config), self.db, None, None, diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index 2c3051fc0..e50882e10 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -9,14 +9,12 @@ import queue import re import shlex import urllib.parse -from collections import Counter from collections.abc import Mapping from pathlib import Path from typing import Any, Optional, Tuple import numpy as np import pytz -import yaml from ruamel.yaml import YAML from tzlocal import get_localzone from zoneinfo import ZoneInfoNotFoundError @@ -89,34 +87,6 @@ def deep_merge(dct1: dict, dct2: dict, override=False, merge_lists=False) -> dic return merged -def load_config_with_no_duplicates(raw_config) -> dict: - """Get config ensuring duplicate keys are not allowed.""" - - # https://stackoverflow.com/a/71751051 - # important to use SafeLoader here to avoid RCE - class PreserveDuplicatesLoader(yaml.loader.SafeLoader): - pass - - def map_constructor(loader, node, deep=False): - keys = [loader.construct_object(node, deep=deep) for node, _ in node.value] - vals = [loader.construct_object(node, deep=deep) for _, node in node.value] - key_count = Counter(keys) - data = {} - for key, val in zip(keys, vals): - if key_count[key] > 1: - raise ValueError( - f"Config input {key} is defined multiple times for the same field, this is not allowed." - ) - else: - data[key] = val - return data - - PreserveDuplicatesLoader.add_constructor( - yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, map_constructor - ) - return yaml.load(raw_config, PreserveDuplicatesLoader) - - def clean_camera_user_pass(line: str) -> str: """Removes user and password from line.""" rtsp_cleaned = re.sub(REGEX_RTSP_CAMERA_USER_PASS, "://*:*@", line) diff --git a/process_clip.py b/process_clip.py index 77cfdba2c..54bbf0c1e 100644 --- a/process_clip.py +++ b/process_clip.py @@ -280,10 +280,7 @@ def process(path, label, output, debug_path): json_config["cameras"]["camera"]["ffmpeg"]["inputs"][0]["path"] = c frigate_config = FrigateConfig(**json_config) - runtime_config = frigate_config.runtime_config() - runtime_config.cameras["camera"].create_ffmpeg_cmds() - - process_clip = ProcessClip(c, frame_shape, runtime_config) + process_clip = ProcessClip(c, frame_shape, frigate_config) process_clip.load_frames() process_clip.process_frames(object_detector, objects_to_track=[label])