Make Snapshots path configurable

This commit is contained in:
Judah Rand 2024-03-25 18:03:15 +00:00
parent 258cd5b6d7
commit 43edbac4d0
6 changed files with 34 additions and 23 deletions

View File

@ -18,9 +18,8 @@ from flask import (
from peewee import DoesNotExist, fn, operator
from playhouse.shortcuts import model_to_dict
from frigate.const import (
CLIPS_DIR,
)
from frigate.config import SnapshotsConfig
from frigate.const import CLIPS_DIR
from frigate.models import Event, Timeline
from frigate.object_processing import TrackedObject
from frigate.util.builtin import get_tz_modifiers
@ -350,8 +349,9 @@ def send_to_plus(id):
# load clean.png
try:
snapshot_config: SnapshotsConfig = current_app.frigate_config.cameras[event.camera]
filename = f"{event.camera}-{event.id}-clean.png"
image = cv2.imread(os.path.join(CLIPS_DIR, filename))
image = cv2.imread(os.path.join(snapshot_config.path, filename))
except Exception:
logger.error(f"Unable to load clean png for event: {event.id}")
return make_response(
@ -601,9 +601,10 @@ def delete_event(id):
media_name = f"{event.camera}-{event.id}"
if event.has_snapshot:
media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.jpg")
snapshot_config: SnapshotsConfig = current_app.frigate_config.cameras[event.camera].snapshots
media = Path(f"{os.path.join(snapshot_config.path, media_name)}.jpg")
media.unlink(missing_ok=True)
media = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png")
media = Path(f"{os.path.join(snapshot_config.path, media_name)}-clean.png")
media.unlink(missing_ok=True)
if event.has_clip:
media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4")

View File

@ -25,6 +25,7 @@ from peewee import DoesNotExist, fn
from tzlocal import get_localzone_name
from werkzeug.utils import secure_filename
from frigate.config import SnapshotsConfig
from frigate.const import (
CACHE_DIR,
CLIPS_DIR,
@ -978,8 +979,9 @@ def event_snapshot_clean(id):
)
if png_bytes is None:
try:
snapshot_config: SnapshotsConfig = current_app.frigate_config.cameras[event.camera].snapshots
clean_snapshot_path = os.path.join(
CLIPS_DIR, f"{event.camera}-{event.id}-clean.png"
snapshot_config.path, f"{event.camera}-{event.id}-clean.png"
)
if not os.path.exists(clean_snapshot_path):
return make_response(
@ -989,7 +991,7 @@ def event_snapshot_clean(id):
404,
)
with open(
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}-clean.png"), "rb"
os.path.join(snapshot_config.path, f"{event.camera}-{event.id}-clean.png"), "rb"
) as image_file:
png_bytes = image_file.read()
except Exception:
@ -1023,8 +1025,9 @@ def event_snapshot(id):
jsonify({"success": False, "message": "Snapshot not available"}), 404
)
# read snapshot from disk
snapshot_config: SnapshotsConfig = current_app.frigate_config.cameras[event.camera].snapshots
with open(
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}.jpg"), "rb"
os.path.join(snapshot_config.path, f"{event.camera}-{event.id}.jpg"), "rb"
) as image_file:
jpg_bytes = image_file.read()
except DoesNotExist:

View File

@ -26,6 +26,7 @@ from frigate.const import (
AUDIO_MIN_CONFIDENCE,
CACHE_DIR,
CACHE_SEGMENT_FORMAT,
CLIPS_DIR,
DEFAULT_DB_PATH,
MAX_PRE_CAPTURE,
REGEX_CAMERA_NAME,
@ -722,6 +723,7 @@ class CameraFfmpegConfig(FfmpegConfig):
class SnapshotsConfig(FrigateBaseModel):
enabled: bool = Field(default=False, title="Snapshots enabled.")
path: str = Field(default=CLIPS_DIR, title="Snapshots path.")
clean_copy: bool = Field(
default=True, title="Create a clean copy of the snapshot image."
)

View File

@ -8,7 +8,7 @@ from enum import Enum
from multiprocessing.synchronize import Event as MpEvent
from pathlib import Path
from frigate.config import FrigateConfig
from frigate.config import FrigateConfig, SnapshotsConfig
from frigate.const import CLIPS_DIR
from frigate.models import Event, Timeline
@ -64,10 +64,12 @@ class EventCleanup(threading.Thread):
def expire(self, media_type: EventCleanupType) -> list[str]:
## Expire events from unlisted cameras based on the global config
if media_type == EventCleanupType.clips:
base_dir = CLIPS_DIR
retain_config = self.config.record.events.retain
file_extension = None # mp4 clips are no longer stored in /clips
update_params = {"has_clip": False}
else:
base_dir = self.config.snapshots.path
retain_config = self.config.snapshots.retain
file_extension = "jpg"
update_params = {"has_snapshot": False}
@ -101,12 +103,12 @@ class EventCleanup(threading.Thread):
for expired in expired_events:
media_name = f"{expired.camera}-{expired.id}"
media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
f"{os.path.join(base_dir, media_name)}.{file_extension}"
)
media_path.unlink(missing_ok=True)
if file_extension == "jpg":
media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
f"{os.path.join(base_dir, media_name)}-clean.png"
)
media_path.unlink(missing_ok=True)
@ -124,8 +126,10 @@ class EventCleanup(threading.Thread):
## Expire events from cameras based on the camera config
for name, camera in self.config.cameras.items():
if media_type == EventCleanupType.clips:
base_dir = CLIPS_DIR
retain_config = camera.record.events.retain
else:
base_dir = camera.snapshots.path
retain_config = camera.snapshots.retain
# get distinct objects in database for this camera
@ -165,11 +169,11 @@ class EventCleanup(threading.Thread):
if media_type == EventCleanupType.snapshots:
media_name = f"{event.camera}-{event.id}"
media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
f"{os.path.join(base_dir, media_name)}.{file_extension}"
)
media_path.unlink(missing_ok=True)
media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
f"{os.path.join(base_dir, media_name)}-clean.png"
)
media_path.unlink(missing_ok=True)
@ -198,10 +202,11 @@ class EventCleanup(threading.Thread):
duplicate_events = Event.raw(duplicate_query)
for event in duplicate_events:
logger.debug(f"Removing duplicate: {event.id}")
snapshot_config: SnapshotsConfig = self.config.cameras[event.camera].snapshots
media_name = f"{event.camera}-{event.id}"
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.jpg")
media_path = Path(f"{os.path.join(snapshot_config.path, media_name)}.jpg")
media_path.unlink(missing_ok=True)
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png")
media_path = Path(f"{os.path.join(snapshot_config.path, media_name)}-clean.png")
media_path.unlink(missing_ok=True)
(

View File

@ -11,8 +11,7 @@ from typing import Optional
import cv2
from frigate.comms.events_updater import EventUpdatePublisher
from frigate.config import CameraConfig, FrigateConfig
from frigate.const import CLIPS_DIR
from frigate.config import CameraConfig, FrigateConfig, SnapshotsConfig
from frigate.events.types import EventStateEnum, EventTypeEnum
from frigate.util.image import draw_box_with_label
@ -94,6 +93,8 @@ class ExternalEventProcessor:
draw: dict[str, any],
img_frame: any,
) -> str:
snapshot_config: SnapshotsConfig = camera_config.snapshots
# write clean snapshot if enabled
if camera_config.snapshots.clean_copy:
ret, png = cv2.imencode(".png", img_frame)
@ -101,7 +102,7 @@ class ExternalEventProcessor:
if ret:
with open(
os.path.join(
CLIPS_DIR,
snapshot_config.path,
f"{camera_config.name}-{event_id}-clean.png",
),
"wb",
@ -130,7 +131,7 @@ class ExternalEventProcessor:
ret, jpg = cv2.imencode(".jpg", img_frame)
with open(
os.path.join(CLIPS_DIR, f"{camera_config.name}-{event_id}.jpg"),
os.path.join(snapshot_config.path, f"{camera_config.name}-{event_id}.jpg"),
"wb",
) as j:
j.write(jpg.tobytes())

View File

@ -23,7 +23,6 @@ from frigate.config import (
SnapshotsConfig,
ZoomingModeEnum,
)
from frigate.const import CLIPS_DIR
from frigate.events.types import EventStateEnum, EventTypeEnum
from frigate.ptz.autotrack import PtzAutoTrackerThread
from frigate.util.image import (
@ -897,7 +896,7 @@ class TrackedObjectProcessor(threading.Thread):
logger.warning(f"Unable to save snapshot for {obj.obj_data['id']}.")
else:
with open(
os.path.join(CLIPS_DIR, f"{camera}-{obj.obj_data['id']}.jpg"),
os.path.join(snapshot_config.path, f"{camera}-{obj.obj_data['id']}.jpg"),
"wb",
) as j:
j.write(jpg_bytes)
@ -912,7 +911,7 @@ class TrackedObjectProcessor(threading.Thread):
else:
with open(
os.path.join(
CLIPS_DIR,
snapshot_config.path,
f"{camera}-{obj.obj_data['id']}-clean.png",
),
"wb",