mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-08 04:05:26 +03:00
Refactor path handling (#9611)
- Use pathlib for path joining sugar using slashes - Rename BASE_DIR to MEDIA_DIR, which is more meaningful - Introduce INSTALL_DIR for the installation base dir - Replace glob.glob usage with no wildcard to file existance check
This commit is contained in:
parent
92a0705b03
commit
26041e66c0
@ -109,7 +109,7 @@ class FrigateApp:
|
|||||||
root_configurer(self.log_queue)
|
root_configurer(self.log_queue)
|
||||||
|
|
||||||
def init_config(self) -> None:
|
def init_config(self) -> None:
|
||||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
config_file = os.environ.get("CONFIG_FILE", str(CONFIG_DIR / "config.yml"))
|
||||||
|
|
||||||
# Check if we can use .yaml instead of .yml
|
# Check if we can use .yaml instead of .yml
|
||||||
config_file_yaml = config_file.replace(".yml", ".yaml")
|
config_file_yaml = config_file.replace(".yml", ".yaml")
|
||||||
|
|||||||
@ -910,7 +910,7 @@ class CameraConfig(FrigateBaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class DatabaseConfig(FrigateBaseModel):
|
class DatabaseConfig(FrigateBaseModel):
|
||||||
path: str = Field(default=DEFAULT_DB_PATH, title="Database path.")
|
path: str = Field(default=str(DEFAULT_DB_PATH), title="Database path.")
|
||||||
|
|
||||||
|
|
||||||
class LogLevelEnum(str, Enum):
|
class LogLevelEnum(str, Enum):
|
||||||
|
|||||||
@ -1,14 +1,23 @@
|
|||||||
CONFIG_DIR = "/config"
|
from pathlib import Path
|
||||||
DEFAULT_DB_PATH = f"{CONFIG_DIR}/frigate.db"
|
|
||||||
MODEL_CACHE_DIR = f"{CONFIG_DIR}/model_cache"
|
CACHE_DIR = Path("/tmp/cache")
|
||||||
BASE_DIR = "/media/frigate"
|
BIRDSEYE_PIPE = CACHE_DIR / "birdseye"
|
||||||
CLIPS_DIR = f"{BASE_DIR}/clips"
|
|
||||||
RECORD_DIR = f"{BASE_DIR}/recordings"
|
CONFIG_DIR = Path("/config")
|
||||||
EXPORT_DIR = f"{BASE_DIR}/exports"
|
DEFAULT_DB_PATH = CONFIG_DIR / "frigate.db"
|
||||||
BIRDSEYE_PIPE = "/tmp/cache/birdseye"
|
MODEL_CACHE_DIR = CONFIG_DIR / "model_cache"
|
||||||
CACHE_DIR = "/tmp/cache"
|
|
||||||
|
INSTALL_DIR = Path("/opt/frigate")
|
||||||
|
|
||||||
|
MEDIA_DIR = Path("/media/frigate")
|
||||||
|
CLIPS_DIR = MEDIA_DIR / "clips"
|
||||||
|
RECORD_DIR = MEDIA_DIR / "recordings"
|
||||||
|
EXPORT_DIR = MEDIA_DIR / "exports"
|
||||||
|
|
||||||
YAML_EXT = (".yaml", ".yml")
|
YAML_EXT = (".yaml", ".yml")
|
||||||
|
|
||||||
FRIGATE_LOCALHOST = "http://127.0.0.1:5000"
|
FRIGATE_LOCALHOST = "http://127.0.0.1:5000"
|
||||||
|
|
||||||
PLUS_ENV_VAR = "PLUS_API_KEY"
|
PLUS_ENV_VAR = "PLUS_API_KEY"
|
||||||
PLUS_API_HOST = "https://api.frigate.video"
|
PLUS_API_HOST = "https://api.frigate.video"
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import requests
|
|||||||
from pydantic import BaseModel, Extra, Field
|
from pydantic import BaseModel, Extra, Field
|
||||||
from pydantic.fields import PrivateAttr
|
from pydantic.fields import PrivateAttr
|
||||||
|
|
||||||
|
from frigate.const import MODEL_CACHE_DIR
|
||||||
from frigate.plus import PlusApi
|
from frigate.plus import PlusApi
|
||||||
from frigate.util.builtin import load_labels
|
from frigate.util.builtin import load_labels
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ class ModelConfig(BaseModel):
|
|||||||
return
|
return
|
||||||
|
|
||||||
model_id = self.path[7:]
|
model_id = self.path[7:]
|
||||||
self.path = f"/config/model_cache/{model_id}"
|
self.path = str(MODEL_CACHE_DIR / model_id)
|
||||||
model_info_path = f"{self.path}.json"
|
model_info_path = f"{self.path}.json"
|
||||||
|
|
||||||
# download the model if it doesn't exist
|
# download the model if it doesn't exist
|
||||||
|
|||||||
@ -15,6 +15,7 @@ except: # noqa: E722
|
|||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
|
from frigate.const import MODEL_CACHE_DIR
|
||||||
from frigate.detectors.detection_api import DetectionApi
|
from frigate.detectors.detection_api import DetectionApi
|
||||||
from frigate.detectors.detector_config import BaseDetectorConfig
|
from frigate.detectors.detector_config import BaseDetectorConfig
|
||||||
|
|
||||||
@ -86,12 +87,10 @@ class Rknn(DetectionApi):
|
|||||||
else:
|
else:
|
||||||
model_suffix = yolov8_suffix[self.model_path]
|
model_suffix = yolov8_suffix[self.model_path]
|
||||||
self.model_path = (
|
self.model_path = (
|
||||||
"/config/model_cache/rknn/yolov8{suffix}-320x320-{soc}.rknn".format(
|
MODEL_CACHE_DIR / f"rknn/yolov8{model_suffix}-320x320-{soc}.rknn"
|
||||||
suffix=model_suffix, soc=soc
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
os.makedirs("/config/model_cache/rknn", exist_ok=True)
|
os.makedirs(MODEL_CACHE_DIR / "rknn", exist_ok=True)
|
||||||
if not os.path.isfile(self.model_path):
|
if not os.path.isfile(self.model_path):
|
||||||
logger.info(
|
logger.info(
|
||||||
"Downloading yolov8{suffix} model.".format(suffix=model_suffix)
|
"Downloading yolov8{suffix} model.".format(suffix=model_suffix)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import base64
|
import base64
|
||||||
import copy
|
import copy
|
||||||
import glob
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -41,7 +40,9 @@ from frigate.const import (
|
|||||||
CLIPS_DIR,
|
CLIPS_DIR,
|
||||||
CONFIG_DIR,
|
CONFIG_DIR,
|
||||||
EXPORT_DIR,
|
EXPORT_DIR,
|
||||||
|
INSTALL_DIR,
|
||||||
MAX_SEGMENT_DURATION,
|
MAX_SEGMENT_DURATION,
|
||||||
|
MEDIA_DIR,
|
||||||
RECORD_DIR,
|
RECORD_DIR,
|
||||||
)
|
)
|
||||||
from frigate.events.external import ExternalEventProcessor
|
from frigate.events.external import ExternalEventProcessor
|
||||||
@ -1384,7 +1385,7 @@ def config():
|
|||||||
|
|
||||||
@bp.route("/config/raw")
|
@bp.route("/config/raw")
|
||||||
def config_raw():
|
def config_raw():
|
||||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
config_file = os.environ.get("CONFIG_FILE", CONFIG_DIR / "config.yml")
|
||||||
|
|
||||||
# Check if we can use .yaml instead of .yml
|
# Check if we can use .yaml instead of .yml
|
||||||
config_file_yaml = config_file.replace(".yml", ".yaml")
|
config_file_yaml = config_file.replace(".yml", ".yaml")
|
||||||
@ -1434,7 +1435,7 @@ def config_save():
|
|||||||
|
|
||||||
# Save the config to file
|
# Save the config to file
|
||||||
try:
|
try:
|
||||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
config_file = os.environ.get("CONFIG_FILE", CONFIG_DIR / "config.yml")
|
||||||
|
|
||||||
# Check if we can use .yaml instead of .yml
|
# Check if we can use .yaml instead of .yml
|
||||||
config_file_yaml = config_file.replace(".yml", ".yaml")
|
config_file_yaml = config_file.replace(".yml", ".yaml")
|
||||||
@ -1647,11 +1648,11 @@ def latest_frame(camera_name):
|
|||||||
+ retry_interval
|
+ retry_interval
|
||||||
):
|
):
|
||||||
if current_app.camera_error_image is None:
|
if current_app.camera_error_image is None:
|
||||||
error_image = glob.glob("/opt/frigate/frigate/images/camera-error.jpg")
|
error_image = INSTALL_DIR / "frigate/images/camera-error.jpg"
|
||||||
|
|
||||||
if len(error_image) > 0:
|
if error_image.is_file():
|
||||||
current_app.camera_error_image = cv2.imread(
|
current_app.camera_error_image = cv2.imread(
|
||||||
error_image[0], cv2.IMREAD_UNCHANGED
|
str(error_image), cv2.IMREAD_UNCHANGED
|
||||||
)
|
)
|
||||||
|
|
||||||
frame = current_app.camera_error_image
|
frame = current_app.camera_error_image
|
||||||
@ -2097,7 +2098,7 @@ def preview_ts(camera_name, start_ts, end_ts):
|
|||||||
clips.append(
|
clips.append(
|
||||||
{
|
{
|
||||||
"camera": preview["camera"],
|
"camera": preview["camera"],
|
||||||
"src": preview["path"].replace("/media/frigate", ""),
|
"src": preview["path"].replace(MEDIA_DIR, ""),
|
||||||
"type": "video/mp4",
|
"type": "video/mp4",
|
||||||
"start": preview["start_time"],
|
"start": preview["start_time"],
|
||||||
"end": preview["end_time"],
|
"end": preview["end_time"],
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
"""Handle outputting birdseye frames via jsmpeg and go2rtc."""
|
"""Handle outputting birdseye frames via jsmpeg and go2rtc."""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import glob
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
@ -15,7 +14,7 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
from frigate.config import BirdseyeModeEnum, FrigateConfig
|
||||||
from frigate.const import BASE_DIR, BIRDSEYE_PIPE
|
from frigate.const import BIRDSEYE_PIPE, INSTALL_DIR, MEDIA_DIR
|
||||||
from frigate.types import CameraMetricsTypes
|
from frigate.types import CameraMetricsTypes
|
||||||
from frigate.util.image import (
|
from frigate.util.image import (
|
||||||
SharedMemoryFrameManager,
|
SharedMemoryFrameManager,
|
||||||
@ -280,16 +279,16 @@ class BirdsEyeFrameManager:
|
|||||||
# find and copy the logo on the blank frame
|
# find and copy the logo on the blank frame
|
||||||
birdseye_logo = None
|
birdseye_logo = None
|
||||||
|
|
||||||
custom_logo_files = glob.glob(f"{BASE_DIR}/custom.png")
|
custom_logo_file = MEDIA_DIR / "custom.png"
|
||||||
|
|
||||||
if len(custom_logo_files) > 0:
|
if custom_logo_file.is_file():
|
||||||
birdseye_logo = cv2.imread(custom_logo_files[0], cv2.IMREAD_UNCHANGED)
|
birdseye_logo = cv2.imread(str(custom_logo_file), cv2.IMREAD_UNCHANGED)
|
||||||
|
|
||||||
if birdseye_logo is None:
|
if birdseye_logo is None:
|
||||||
logo_files = glob.glob("/opt/frigate/frigate/images/birdseye.png")
|
logo_file = INSTALL_DIR / "frigate/images/birdseye.png"
|
||||||
|
|
||||||
if len(logo_files) > 0:
|
if logo_file.is_file():
|
||||||
birdseye_logo = cv2.imread(logo_files[0], cv2.IMREAD_UNCHANGED)
|
birdseye_logo = cv2.imread(str(logo_file), cv2.IMREAD_UNCHANGED)
|
||||||
|
|
||||||
if birdseye_logo is not None:
|
if birdseye_logo is not None:
|
||||||
transparent_layer = birdseye_logo[:, :, 3]
|
transparent_layer = birdseye_logo[:, :, 3]
|
||||||
|
|||||||
@ -300,7 +300,7 @@ def stats_snapshot(
|
|||||||
|
|
||||||
for path in [RECORD_DIR, CLIPS_DIR, CACHE_DIR, "/dev/shm"]:
|
for path in [RECORD_DIR, CLIPS_DIR, CACHE_DIR, "/dev/shm"]:
|
||||||
try:
|
try:
|
||||||
storage_stats = shutil.disk_usage(path)
|
storage_stats = shutil.disk_usage(str(path))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
stats["service"]["storage"][path] = {}
|
stats["service"]["storage"][path] = {}
|
||||||
continue
|
continue
|
||||||
@ -309,7 +309,7 @@ def stats_snapshot(
|
|||||||
"total": round(storage_stats.total / pow(2, 20), 1),
|
"total": round(storage_stats.total / pow(2, 20), 1),
|
||||||
"used": round(storage_stats.used / pow(2, 20), 1),
|
"used": round(storage_stats.used / pow(2, 20), 1),
|
||||||
"free": round(storage_stats.free / pow(2, 20), 1),
|
"free": round(storage_stats.free / pow(2, 20), 1),
|
||||||
"mount_type": get_fs_type(path),
|
"mount_type": get_fs_type(str(path)),
|
||||||
}
|
}
|
||||||
|
|
||||||
stats["processes"] = {}
|
stats["processes"] = {}
|
||||||
|
|||||||
@ -847,9 +847,9 @@ class TestConfig(unittest.TestCase):
|
|||||||
assert runtime_config.model.merged_labelmap[0] == "person"
|
assert runtime_config.model.merged_labelmap[0] == "person"
|
||||||
|
|
||||||
def test_plus_labelmap(self):
|
def test_plus_labelmap(self):
|
||||||
with open("/config/model_cache/test", "w") as f:
|
with open(MODEL_CACHE_DIR / "test", "w") as f:
|
||||||
json.dump(self.plus_model_info, f)
|
json.dump(self.plus_model_info, f)
|
||||||
with open("/config/model_cache/test.json", "w") as f:
|
with open(MODEL_CACHE_DIR / "test.json", "w") as f:
|
||||||
json.dump(self.plus_model_info, f)
|
json.dump(self.plus_model_info, f)
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from playhouse.sqlite_ext import SqliteExtDatabase
|
|||||||
from playhouse.sqliteq import SqliteQueueDatabase
|
from playhouse.sqliteq import SqliteQueueDatabase
|
||||||
|
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
|
from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
|
||||||
from frigate.http import create_app
|
from frigate.http import create_app
|
||||||
from frigate.models import Event, Recordings
|
from frigate.models import Event, Recordings
|
||||||
from frigate.plus import PlusApi
|
from frigate.plus import PlusApi
|
||||||
@ -76,19 +77,19 @@ class TestHttp(unittest.TestCase):
|
|||||||
"total": 67.1,
|
"total": 67.1,
|
||||||
"used": 16.6,
|
"used": 16.6,
|
||||||
},
|
},
|
||||||
"/media/frigate/clips": {
|
str(CLIPS_DIR): {
|
||||||
"free": 42429.9,
|
"free": 42429.9,
|
||||||
"mount_type": "ext4",
|
"mount_type": "ext4",
|
||||||
"total": 244529.7,
|
"total": 244529.7,
|
||||||
"used": 189607.0,
|
"used": 189607.0,
|
||||||
},
|
},
|
||||||
"/media/frigate/recordings": {
|
str(RECORD_DIR): {
|
||||||
"free": 0.2,
|
"free": 0.2,
|
||||||
"mount_type": "ext4",
|
"mount_type": "ext4",
|
||||||
"total": 8.0,
|
"total": 8.0,
|
||||||
"used": 7.8,
|
"used": 7.8,
|
||||||
},
|
},
|
||||||
"/tmp/cache": {
|
str(CACHE_DIR): {
|
||||||
"free": 976.8,
|
"free": 976.8,
|
||||||
"mount_type": "tmpfs",
|
"mount_type": "tmpfs",
|
||||||
"total": 1000.0,
|
"total": 1000.0,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user