diff --git a/frigate/__main__.py b/frigate/__main__.py index 208af9e23..ea888a948 100644 --- a/frigate/__main__.py +++ b/frigate/__main__.py @@ -1,10 +1,16 @@ +import argparse import faulthandler import logging +import signal +import sys import threading from flask import cli +from pydantic import ValidationError from frigate.app import FrigateApp +from frigate.config import FrigateConfig +from frigate.log import log_thread def main() -> None: @@ -20,8 +26,50 @@ def main() -> None: threading.current_thread().name = "frigate" cli.show_server_banner = lambda *x: None + # Make sure we exit cleanly on SIGTERM. + signal.signal(signal.SIGTERM, lambda sig, frame: sys.exit()) + + run() + + +@log_thread() +def run() -> None: + # Parse the cli arguments. + parser = argparse.ArgumentParser( + prog="Frigate", + description="An NVR with realtime local object detection for IP cameras.", + ) + parser.add_argument("--validate-config", action="store_true") + args = parser.parse_args() + + # Load the configuration. + try: + config = FrigateConfig.load() + except ValidationError as e: + print("*************************************************************") + print("*************************************************************") + print("*** Your config file is not valid! ***") + print("*** Please check the docs at ***") + print("*** https://docs.frigate.video/configuration/ ***") + print("*************************************************************") + print("*************************************************************") + print("*** Config Validation Errors ***") + print("*************************************************************") + for error in e.errors(): + location = ".".join(str(item) for item in error["loc"]) + print(f"{location}: {error['msg']}") + print("*************************************************************") + print("*** End Config Validation Errors ***") + print("*************************************************************") + sys.exit(1) + if args.validate_config: + print("*************************************************************") + print("*** Your config file is valid. ***") + print("*************************************************************") + sys.exit(0) + # Run the main application. - FrigateApp().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 f6479d9ad..fe53c6a7d 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -1,23 +1,17 @@ -import argparse import datetime import logging import multiprocessing as mp import os import secrets import shutil -import signal -import sys -import traceback from multiprocessing import Queue from multiprocessing.synchronize import Event as MpEvent -from types import FrameType -from typing import Optional +from typing import Any import psutil from peewee_migrate import Router from playhouse.sqlite_ext import SqliteExtDatabase from playhouse.sqliteq import SqliteQueueDatabase -from pydantic import ValidationError from frigate.api.app import create_app from frigate.api.auth import hash_password @@ -28,7 +22,6 @@ from frigate.comms.mqtt import MqttClient from frigate.comms.webpush import WebPushClient from frigate.comms.ws import WebSocketClient from frigate.comms.zmq_proxy import ZmqProxy -from frigate.config import FrigateConfig from frigate.const import ( CACHE_DIR, CLIPS_DIR, @@ -43,7 +36,6 @@ from frigate.events.audio import listen_to_audio from frigate.events.cleanup import EventCleanup from frigate.events.external import ExternalEventProcessor from frigate.events.maintainer import EventProcessor -from frigate.log import log_thread from frigate.models import ( Event, Export, @@ -58,7 +50,6 @@ from frigate.models import ( from frigate.object_detection import ObjectDetectProcess from frigate.object_processing import TrackedObjectProcessor from frigate.output.output import output_frames -from frigate.plus import PlusApi from frigate.ptz.autotrack import PtzAutoTrackerThread from frigate.ptz.onvif import OnvifController from frigate.record.cleanup import RecordingCleanup @@ -70,8 +61,7 @@ from frigate.stats.util import stats_init from frigate.storage import StorageMaintainer from frigate.timeline import TimelineProcessor from frigate.types import CameraMetricsTypes, PTZMetricsTypes -from frigate.util.builtin import empty_and_close_queue, save_default_config -from frigate.util.config import migrate_frigate_config +from frigate.util.builtin import empty_and_close_queue from frigate.util.object import get_camera_regions_grid from frigate.version import VERSION from frigate.video import capture_camera, track_camera @@ -81,22 +71,19 @@ logger = logging.getLogger(__name__) class FrigateApp: - def __init__(self) -> 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 = PlusApi() self.camera_metrics: dict[str, CameraMetricsTypes] = {} self.ptz_metrics: dict[str, PTZMetricsTypes] = {} self.processes: dict[str, int] = {} self.region_grids: dict[str, list[list[dict[str, int]]]] = {} - - def set_environment_vars(self) -> None: - for key, value in self.config.environment_vars.items(): - os.environ[key] = value + self.config = config def ensure_dirs(self) -> None: for d in [ @@ -113,24 +100,7 @@ class FrigateApp: else: logger.debug(f"Skipping directory: {d}") - def init_config(self) -> None: - config_file = os.environ.get("CONFIG_FILE", "/config/config.yml") - - # Check if we can use .yaml instead of .yml - config_file_yaml = config_file.replace(".yml", ".yaml") - if os.path.isfile(config_file_yaml): - config_file = config_file_yaml - - if not os.path.isfile(config_file): - print("No config file found, saving default config") - config_file = config_file_yaml - save_default_config(config_file) - - # check if the config file needs to be migrated - migrate_frigate_config(config_file) - - self.config = FrigateConfig.parse_file(config_file, plus_api=self.plus_api) - + def init_camera_metrics(self) -> None: for camera_name in self.config.cameras.keys(): # create camera_metrics self.camera_metrics[camera_name] = { @@ -190,17 +160,6 @@ class FrigateApp: } self.ptz_metrics[camera_name]["ptz_motor_stopped"].set() - def set_log_levels(self) -> None: - logging.getLogger().setLevel(self.config.logger.default.value.upper()) - for log, level in self.config.logger.logs.items(): - logging.getLogger(log).setLevel(level.value.upper()) - - if "werkzeug" not in self.config.logger.logs: - logging.getLogger("werkzeug").setLevel("ERROR") - - if "ws4py" not in self.config.logger.logs: - logging.getLogger("ws4py").setLevel("ERROR") - def init_queues(self) -> None: # Queue for cameras to push tracked objects to self.detected_frames_queue: Queue = mp.Queue( @@ -374,19 +333,6 @@ class FrigateApp: self.inter_config_updater = ConfigPublisher() self.inter_zmq_proxy = ZmqProxy() - def init_web_server(self) -> None: - self.flask_app = create_app( - self.config, - self.db, - self.embeddings, - self.detected_frames_processor, - self.storage_maintainer, - self.onvif_controller, - self.external_event_processor, - self.plus_api, - self.stats_emitter, - ) - def init_onvif(self) -> None: self.onvif_controller = OnvifController(self.config, self.ptz_metrics) @@ -527,7 +473,7 @@ class FrigateApp: capture_process = mp.Process( target=capture_camera, name=f"camera_capture:{name}", - args=(name, config, self.shm_frame_count, self.camera_metrics[name]), + args=(name, config, self.shm_frame_count(), self.camera_metrics[name]), ) capture_process.daemon = True self.camera_metrics[name]["capture_process"] = capture_process @@ -590,7 +536,7 @@ class FrigateApp: self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event) self.frigate_watchdog.start() - def check_shm(self) -> None: + def shm_frame_count(self) -> int: total_shm = round(shutil.disk_usage("/dev/shm").total / pow(2, 20), 1) # required for log files + nginx cache @@ -610,17 +556,19 @@ class FrigateApp: 1, ) - self.shm_frame_count = min(50, int(available_shm / (cam_total_frame_size))) + shm_frame_count = min(50, int(available_shm / (cam_total_frame_size))) logger.debug( - f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {self.shm_frame_count} frames for each camera in SHM" + f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM" ) - if self.shm_frame_count < 10: + if shm_frame_count < 10: logger.warning( f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size)}MB." ) + return shm_frame_count + def init_auth(self) -> None: if self.config.auth.enabled: if User.select().count() == 0: @@ -657,97 +605,58 @@ class FrigateApp: logger.info("********************************************************") logger.info("********************************************************") - @log_thread() def start(self) -> None: - parser = argparse.ArgumentParser( - prog="Frigate", - description="An NVR with realtime local object detection for IP cameras.", - ) - parser.add_argument("--validate-config", action="store_true") - args = parser.parse_args() - logger.info(f"Starting Frigate ({VERSION})") - try: - self.ensure_dirs() - try: - self.init_config() - except Exception as e: - print("*************************************************************") - print("*************************************************************") - print("*** Your config file is not valid! ***") - print("*** Please check the docs at ***") - print("*** https://docs.frigate.video/configuration/index ***") - print("*************************************************************") - print("*************************************************************") - print("*** Config Validation Errors ***") - print("*************************************************************") - if isinstance(e, ValidationError): - for error in e.errors(): - location = ".".join(str(item) for item in error["loc"]) - print(f"{location}: {error['msg']}") - else: - print(e) - print(traceback.format_exc()) - print("*************************************************************") - print("*** End Config Validation Errors ***") - print("*************************************************************") - sys.exit(1) - if args.validate_config: - print("*************************************************************") - print("*** Your config file is valid. ***") - print("*************************************************************") - sys.exit(0) - self.set_environment_vars() - self.set_log_levels() - self.init_queues() - self.init_database() - self.init_onvif() - self.init_recording_manager() - self.init_review_segment_manager() - self.init_embeddings_manager() - self.init_go2rtc() - self.bind_database() - self.check_db_data_migrations() - self.init_inter_process_communicator() - self.init_dispatcher() - except Exception as e: - print(e) - sys.exit(1) + # Ensure global state. + self.ensure_dirs() + self.config.install() + + # Start frigate services. + self.init_camera_metrics() + self.init_queues() + self.init_database() + self.init_onvif() + self.init_recording_manager() + self.init_review_segment_manager() + self.init_embeddings_manager() + self.init_go2rtc() + self.bind_database() + self.check_db_data_migrations() + self.init_inter_process_communicator() + self.init_dispatcher() self.start_detectors() self.start_video_output_processor() self.start_ptz_autotracker() self.init_historical_regions() self.start_detected_frames_processor() self.start_camera_processors() - self.check_shm() self.start_camera_capture_processes() self.start_audio_processors() self.start_storage_maintainer() self.init_external_event_processor() self.start_stats_emitter() - self.init_web_server() self.start_timeline_processor() self.start_event_processor() self.start_event_cleanup() self.start_record_cleanup() self.start_watchdog() + self.init_auth() - # Flask only listens for SIGINT, so we need to catch SIGTERM and send SIGINT - def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None: - os.kill(os.getpid(), signal.SIGINT) - - signal.signal(signal.SIGTERM, receiveSignal) - try: - self.flask_app.run(host="127.0.0.1", port=5001, debug=False, threaded=True) - except KeyboardInterrupt: - pass - - logger.info("Flask has exited...") - - self.stop() + create_app( + self.config, + self.db, + self.embeddings, + self.detected_frames_processor, + self.storage_maintainer, + self.onvif_controller, + self.external_event_processor, + self.stats_emitter, + ).run(host="127.0.0.1", port=5001, debug=False, threaded=True) + finally: + self.stop() def stop(self) -> None: logger.info("Stopping...") diff --git a/frigate/config.py b/frigate/config.py index e9ffde35a..ffd5afb02 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -45,13 +45,18 @@ 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, ) -from frigate.util.config import StreamInfoRetriever, get_relative_coordinates +from frigate.util.config import ( + StreamInfoRetriever, + get_relative_coordinates, + migrate_frigate_config, +) from frigate.util.image import create_mask from frigate.util.services import auto_detect_hwaccel @@ -59,6 +64,25 @@ logger = logging.getLogger(__name__) yaml = YAML() +DEFAULT_CONFIG_FILES = ["/config/config.yaml", "/config/config.yml"] +DEFAULT_CONFIG = """ +mqtt: + enabled: False + +cameras: + name_of_your_camera: # <------ Name the camera + enabled: True + ffmpeg: + inputs: + - path: rtsp://10.0.10.10:554/rtsp # <----- The stream you want to use for detection + roles: + - detect + detect: + enabled: False # <---- disable detection until you have a working camera feed + width: 1280 + height: 720 +""" + # TODO: Identify what the default format to display timestamps is DEFAULT_TIME_FORMAT = "%m/%d/%Y %H:%M:%S" # German Style: @@ -1272,6 +1296,19 @@ class LoggerConfig(FrigateBaseModel): default_factory=dict, title="Log level for specified processes." ) + def install(self): + """Install global logging state.""" + logging.getLogger().setLevel(self.default.value.upper()) + + log_levels = { + "werkzeug": LogLevelEnum.error, + "ws4py": LogLevelEnum.error, + **self.logs, + } + + for log, level in log_levels.items(): + logging.getLogger(log).setLevel(level.value.upper()) + class CameraGroupConfig(FrigateBaseModel): """Represents a group of cameras.""" @@ -1492,11 +1529,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 @@ -1691,7 +1739,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) @@ -1726,7 +1774,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 @@ -1743,8 +1791,38 @@ class FrigateConfig(FrigateBaseModel): return v @classmethod - def parse_file(cls, config_path, **kwargs): - with open(config_path) as f: + def load(cls, **kwargs): + config_path = os.environ.get("CONFIG_FILE") + + # No explicit configuration file, try to find one in the default paths. + if config_path is None: + for path in DEFAULT_CONFIG_FILES: + if os.path.isfile(path): + config_path = path + break + + # No configuration file found, create one. + new_config = False + if config_path is None: + logger.info("No config file found, saving default config") + config_path = DEFAULT_CONFIG_FILES[-1] + new_config = True + else: + # Check if the config file needs to be migrated. + migrate_frigate_config(config_path) + + # Finally, load the resulting configuration file. + with open(config_path, "a+") as f: + # Only write the default config if the opened file is non-empty. This can happen as + # a race condition. It's extremely unlikely, but eh. Might as well check it. + if new_config and f.tell() == 0: + f.write(DEFAULT_CONFIG) + logger.info( + "Created default config file, see the getting started docs \ + for configuration https://docs.frigate.video/guides/getting_started" + ) + + f.seek(0) return FrigateConfig.parse(f, **kwargs) @classmethod @@ -1775,10 +1853,17 @@ 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}) + + def install(self): + """Install global state from the config.""" + self.logger.install() + + for key, value in self.environment_vars.items(): + os.environ[key] = value diff --git a/frigate/events/audio.py b/frigate/events/audio.py index 662eb5189..5875dca84 100644 --- a/frigate/events/audio.py +++ b/frigate/events/audio.py @@ -76,16 +76,8 @@ def listen_to_audio( stop_event = mp.Event() audio_threads: list[threading.Thread] = [] - def exit_process() -> None: - for thread in audio_threads: - thread.join() - - logger.info("Exiting audio detector...") - def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None: - logger.debug(f"Audio process received signal {signalNumber}") stop_event.set() - exit_process() signal.signal(signal.SIGTERM, receiveSignal) signal.signal(signal.SIGINT, receiveSignal) @@ -104,6 +96,11 @@ def listen_to_audio( audio_threads.append(audio) audio.start() + for thread in audio_threads: + thread.join() + + logger.info("Exiting audio detector...") + class AudioTfl: def __init__(self, stop_event: mp.Event, num_threads=2): diff --git a/frigate/log.py b/frigate/log.py index 475be50d4..ec60b1b71 100644 --- a/frigate/log.py +++ b/frigate/log.py @@ -2,6 +2,7 @@ import atexit import logging import multiprocessing as mp import os +import sys import threading from collections import deque from contextlib import AbstractContextManager, ContextDecorator @@ -68,6 +69,19 @@ class log_thread(AbstractContextManager, ContextDecorator): self._stop_thread() +# When a multiprocessing.Process exits, python tries to flush stdout and stderr. However, if the +# process is created after a thread (for example a logging thread) is created and the process fork +# happens while an internal lock is held, the stdout/err flush can cause a deadlock. +# +# https://github.com/python/cpython/issues/91776 +def reopen_std_streams() -> None: + sys.stdout = os.fdopen(1, "w") + sys.stderr = os.fdopen(2, "w") + + +os.register_at_fork(after_in_child=reopen_std_streams) + + # based on https://codereview.stackexchange.com/a/17959 class LogPipe(threading.Thread): def __init__(self, log_name: str): diff --git a/frigate/object_detection.py b/frigate/object_detection.py index de383dffa..d5b8b0cfe 100644 --- a/frigate/object_detection.py +++ b/frigate/object_detection.py @@ -92,7 +92,6 @@ def run_detector( stop_event = mp.Event() def receiveSignal(signalNumber, frame): - logger.info("Signal to exit detection process...") stop_event.set() signal.signal(signal.SIGTERM, receiveSignal) diff --git a/frigate/output/output.py b/frigate/output/output.py index c1be7154c..7d5b6d39a 100644 --- a/frigate/output/output.py +++ b/frigate/output/output.py @@ -38,7 +38,6 @@ def output_frames( stop_event = mp.Event() def receiveSignal(signalNumber, frame): - logger.debug(f"Output frames process received signal {signalNumber}") stop_event.set() signal.signal(signal.SIGTERM, receiveSignal) diff --git a/frigate/record/record.py b/frigate/record/record.py index 00634f157..252b80545 100644 --- a/frigate/record/record.py +++ b/frigate/record/record.py @@ -22,7 +22,6 @@ def manage_recordings(config: FrigateConfig) -> None: stop_event = mp.Event() def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None: - logger.debug(f"Recording manager process received signal {signalNumber}") stop_event.set() signal.signal(signal.SIGTERM, receiveSignal) diff --git a/frigate/review/review.py b/frigate/review/review.py index d0e3a163f..dafa6c802 100644 --- a/frigate/review/review.py +++ b/frigate/review/review.py @@ -20,7 +20,6 @@ def manage_review_segments(config: FrigateConfig) -> None: stop_event = mp.Event() def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None: - logger.debug(f"Manage review segments process received signal {signalNumber}") stop_event.set() signal.signal(signal.SIGTERM, receiveSignal) 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, ) diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index e50882e10..164c34091 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -258,37 +258,6 @@ def find_by_key(dictionary, target_key): return None -def save_default_config(location: str) -> None: - try: - with open(location, "w") as f: - f.write( - """ -mqtt: - enabled: False - -cameras: - name_of_your_camera: # <------ Name the camera - enabled: True - ffmpeg: - inputs: - - path: rtsp://10.0.10.10:554/rtsp # <----- The stream you want to use for detection - roles: - - detect - detect: - enabled: False # <---- disable detection until you have a working camera feed - width: 1280 - height: 720 - """ - ) - except PermissionError: - logger.error("Unable to write default config to /config") - return - - logger.info( - "Created default config file, see the getting started docs for configuration https://docs.frigate.video/guides/getting_started" - ) - - def get_tomorrow_at_time(hour: int) -> datetime.datetime: """Returns the datetime of the following day at 2am.""" try: diff --git a/frigate/video.py b/frigate/video.py index f31b1d267..5c3c8a054 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -390,7 +390,6 @@ def capture_camera(name, config: CameraConfig, shm_frame_count: int, process_inf stop_event = mp.Event() def receiveSignal(signalNumber, frame): - logger.debug(f"Capture camera received signal {signalNumber}") stop_event.set() signal.signal(signal.SIGTERM, receiveSignal)