diff --git a/frigate/__main__.py b/frigate/__main__.py index 6b49394c9..6574181d3 100644 --- a/frigate/__main__.py +++ b/frigate/__main__.py @@ -9,7 +9,6 @@ from pydantic import ValidationError from frigate.app import FrigateApp from frigate.config import FrigateConfig -from frigate.plus import PlusApi def main() -> None: @@ -33,11 +32,9 @@ def main() -> None: parser.add_argument("--validate-config", action="store_true") args = parser.parse_args() - plus_api = PlusApi() - # Load the configuration. try: - config = FrigateConfig.load(plus_api=plus_api) + config = FrigateConfig.load() except ValidationError as e: print("*************************************************************") print("*************************************************************") @@ -62,7 +59,7 @@ def main() -> None: sys.exit(0) # Run the main application. - FrigateApp(config, plus_api).start() + FrigateApp(config).start() if __name__ == "__main__": diff --git a/frigate/api/app.py b/frigate/api/app.py index 889bbea74..8f9b5109e 100644 --- a/frigate/api/app.py +++ b/frigate/api/app.py @@ -28,7 +28,6 @@ from frigate.const import CONFIG_DIR from frigate.embeddings import EmbeddingsContext from frigate.events.external import ExternalEventProcessor from frigate.models import Event, Timeline -from frigate.plus import PlusApi from frigate.ptz.onvif import OnvifController from frigate.stats.emitter import StatsEmitter from frigate.storage import StorageMaintainer @@ -61,7 +60,6 @@ def create_app( storage_maintainer: StorageMaintainer, onvif: OnvifController, external_processor: ExternalEventProcessor, - plus_api: PlusApi, stats_emitter: StatsEmitter, ): app = Flask(__name__) @@ -89,7 +87,6 @@ def create_app( app.storage_maintainer = storage_maintainer app.onvif = onvif app.external_processor = external_processor - app.plus_api = plus_api app.camera_error_image = None app.stats_emitter = stats_emitter app.jwt_token = get_jwt_secret() if frigate_config.auth.enabled else None @@ -199,7 +196,7 @@ def config(): for zone_name, zone in config_obj.cameras[camera_name].zones.items(): camera_dict["zones"][zone_name]["color"] = zone.color - config["plus"] = {"enabled": current_app.plus_api.is_active()} + config["plus"] = {"enabled": current_app.frigate_config.plus_api.is_active()} config["model"]["colormap"] = config_obj.model.colormap for detector_config in config["detectors"].values(): @@ -362,7 +359,7 @@ def config_set(): if json.get("requires_restart", 1) == 0: current_app.frigate_config = FrigateConfig.parse_object( - config_obj, plus_api=current_app.plus_api + config_obj, plus_api=current_app.frigate_config.plus_api ) return make_response( diff --git a/frigate/api/event.py b/frigate/api/event.py index a49b8942d..bd6e68157 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -612,7 +612,7 @@ def set_retain(id): @EventBp.route("/events//plus", methods=("POST",)) def send_to_plus(id): - if not current_app.plus_api.is_active(): + if not current_app.frigate_config.plus_api.is_active(): message = "PLUS_API_KEY environment variable is not set" logger.error(message) return make_response( @@ -680,7 +680,7 @@ def send_to_plus(id): ) try: - plus_id = current_app.plus_api.upload_image(image, event.camera) + plus_id = current_app.frigate_config.plus_api.upload_image(image, event.camera) except Exception as ex: logger.exception(ex) return make_response( @@ -696,7 +696,7 @@ def send_to_plus(id): box = event.data["box"] try: - current_app.plus_api.add_annotation( + current_app.frigate_config.plus_api.add_annotation( event.plus_id, box, event.label, @@ -720,7 +720,7 @@ def send_to_plus(id): @EventBp.route("/events//false_positive", methods=("PUT",)) def false_positive(id): - if not current_app.plus_api.is_active(): + if not current_app.frigate_config.plus_api.is_active(): message = "PLUS_API_KEY environment variable is not set" logger.error(message) return make_response( @@ -769,7 +769,7 @@ def false_positive(id): ) try: - current_app.plus_api.add_false_positive( + current_app.frigate_config.plus_api.add_false_positive( event.plus_id, region, box, diff --git a/frigate/api/media.py b/frigate/api/media.py index e146fa195..484871326 100644 --- a/frigate/api/media.py +++ b/frigate/api/media.py @@ -294,7 +294,7 @@ def submit_recording_snapshot_to_plus(camera_name: str, frame_time: str): ) nd = cv2.imdecode(np.frombuffer(image_data, dtype=np.int8), cv2.IMREAD_COLOR) - current_app.plus_api.upload_image(nd, camera_name) + current_app.frigate_config.plus_api.upload_image(nd, camera_name) return make_response( jsonify( diff --git a/frigate/app.py b/frigate/app.py index ef94c7745..65ff79198 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -9,7 +9,7 @@ import sys from multiprocessing import Queue from multiprocessing.synchronize import Event as MpEvent from types import FrameType -from typing import Optional +from typing import Any, Optional import psutil from peewee_migrate import Router @@ -75,14 +75,14 @@ logger = logging.getLogger(__name__) class FrigateApp: - def __init__(self, config, plus_api) -> None: + # TODO: Fix FrigateConfig usage, so we can properly annotate it here without mypy erroring out. + def __init__(self, config: Any) -> None: self.stop_event: MpEvent = mp.Event() self.detection_queue: Queue = mp.Queue() self.detectors: dict[str, ObjectDetectProcess] = {} self.detection_out_events: dict[str, MpEvent] = {} self.detection_shms: list[mp.shared_memory.SharedMemory] = [] self.log_queue: Queue = mp.Queue() - self.plus_api = plus_api self.camera_metrics: dict[str, CameraMetricsTypes] = {} self.ptz_metrics: dict[str, PTZMetricsTypes] = {} self.processes: dict[str, int] = {} @@ -361,7 +361,6 @@ class FrigateApp: self.storage_maintainer, self.onvif_controller, self.external_event_processor, - self.plus_api, self.stats_emitter, ) diff --git a/frigate/config.py b/frigate/config.py index 8b663f5b7..fee63a6dd 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -45,6 +45,7 @@ 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, @@ -1515,11 +1516,22 @@ class FrigateConfig(FrigateBaseModel): ) version: Optional[str] = Field(default=None, title="Current config version.") + _plus_api: PlusApi + + @property + def plus_api(self) -> PlusApi: + return self._plus_api + @model_validator(mode="after") def post_validation(self, info: ValidationInfo) -> Self: - plus_api = None + # Load plus api from context, if possible. + self._plus_api = None if isinstance(info.context, dict): - plus_api = info.context.get("plus_api") + self._plus_api = info.context.get("plus_api") + + # Ensure self._plus_api is set, if no explicit value is provided. + if self._plus_api is None: + self._plus_api = PlusApi() # set notifications state self.notifications.enabled_in_config = self.notifications.enabled @@ -1714,7 +1726,7 @@ class FrigateConfig(FrigateBaseModel): enabled_labels.update(camera.objects.track) self.model.create_colormap(sorted(enabled_labels)) - self.model.check_and_load_plus_model(plus_api) + self.model.check_and_load_plus_model(self.plus_api) for key, detector in self.detectors.items(): adapter = TypeAdapter(DetectorConfig) @@ -1749,7 +1761,7 @@ class FrigateConfig(FrigateBaseModel): detector_config.model = ModelConfig.model_validate(merged_model) detector_config.model.check_and_load_plus_model( - plus_api, detector_config.type + self.plus_api, detector_config.type ) detector_config.model.compute_model_hash() self.detectors[key] = detector_config @@ -1828,10 +1840,10 @@ class FrigateConfig(FrigateBaseModel): # 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) + + @classmethod + def parse_object(cls, obj: Any, *, plus_api: Optional[PlusApi] = None): + return cls.model_validate(obj, context={"plus_api": plus_api}) diff --git a/frigate/test/test_http.py b/frigate/test/test_http.py index 127a82b72..ec820e268 100644 --- a/frigate/test/test_http.py +++ b/frigate/test/test_http.py @@ -13,7 +13,6 @@ from playhouse.sqliteq import SqliteQueueDatabase from frigate.api.app import create_app from frigate.config import FrigateConfig from frigate.models import Event, Recordings -from frigate.plus import PlusApi from frigate.stats.emitter import StatsEmitter from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS @@ -121,7 +120,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -158,7 +156,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -180,7 +177,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -201,7 +197,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -224,7 +219,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -251,7 +245,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) morning_id = "123456.random" @@ -290,7 +283,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -326,7 +318,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -352,7 +343,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) @@ -370,7 +360,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), None, ) id = "123456.random" @@ -392,7 +381,6 @@ class TestHttp(unittest.TestCase): None, None, None, - PlusApi(), stats, )