mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-09 04:35:25 +03:00
Make Snapshots path configurable
This commit is contained in:
parent
258cd5b6d7
commit
43edbac4d0
@ -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")
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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."
|
||||
)
|
||||
|
||||
@ -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)
|
||||
|
||||
(
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user