Store camera labels in dict and other optimizations

This commit is contained in:
Nick Mowen 2023-07-19 11:02:22 -06:00
parent f2ff55dba2
commit cfb1052675
2 changed files with 64 additions and 37 deletions

View File

@ -4,6 +4,7 @@ import datetime
import logging import logging
import os import os
import threading import threading
from enum import Enum
from multiprocessing.synchronize import Event as MpEvent from multiprocessing.synchronize import Event as MpEvent
from pathlib import Path from pathlib import Path
@ -14,6 +15,11 @@ from frigate.models import Event
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class EventCleanupType(str, Enum):
clips = "clips"
snapshots = "snapshots"
class EventCleanup(threading.Thread): class EventCleanup(threading.Thread):
def __init__(self, config: FrigateConfig, stop_event: MpEvent): def __init__(self, config: FrigateConfig, stop_event: MpEvent):
threading.Thread.__init__(self) threading.Thread.__init__(self)
@ -21,25 +27,45 @@ class EventCleanup(threading.Thread):
self.config = config self.config = config
self.stop_event = stop_event self.stop_event = stop_event
self.camera_keys = list(self.config.cameras.keys()) self.camera_keys = list(self.config.cameras.keys())
self.removed_camera_labels: list[str] = None
self.camera_labels: dict[str, list] = {}
def expire(self, media_type: str) -> None: def get_removed_camera_labels(self) -> list[Event]:
# TODO: Refactor media_type to enum if self.removed_camera_labels is None:
self.removed_camera_labels = list(
Event.select(Event.label)
.where(Event.camera.not_in(self.camera_keys))
.distinct()
.execute()
)
return self.removed_camera_labels
def get_camera_labels(self, camera: str) -> list[Event]:
if self.camera_labels.get(camera) is None:
self.camera_labels[camera] = list(
Event.select(Event.label)
.where(Event.camera == camera)
.distinct()
.execute()
)
return self.camera_labels[camera]
def expire(self, media_type: EventCleanupType) -> None:
## Expire events from unlisted cameras based on the global config ## Expire events from unlisted cameras based on the global config
if media_type == "clips": if media_type == EventCleanupType.clips:
retain_config = self.config.record.events.retain retain_config = self.config.record.events.retain
file_extension = "mp4" file_extension = None # mp4 clips are no longer stored in /clips
update_params = {"has_clip": False} update_params = {"has_clip": False}
else: else:
retain_config = self.config.snapshots.retain retain_config = self.config.snapshots.retain
file_extension = "jpg" file_extension = "jpg"
update_params = {"has_snapshot": False} update_params = {"has_snapshot": False}
distinct_labels = ( distinct_labels = self.get_removed_camera_labels()
Event.select(Event.label)
.where(Event.camera.not_in(self.camera_keys))
.distinct()
)
## Expire events from cameras no longer in the config
# loop over object types in db # loop over object types in db
for event in distinct_labels: for event in distinct_labels:
# get expiration time for this label # get expiration time for this label
@ -78,14 +104,13 @@ class EventCleanup(threading.Thread):
## Expire events from cameras based on the camera config ## Expire events from cameras based on the camera config
for name, camera in self.config.cameras.items(): for name, camera in self.config.cameras.items():
if media_type == "clips": if media_type == EventCleanupType.clips:
retain_config = camera.record.events.retain retain_config = camera.record.events.retain
else: else:
retain_config = camera.snapshots.retain retain_config = camera.snapshots.retain
# get distinct objects in database for this camera # get distinct objects in database for this camera
distinct_labels = ( distinct_labels = self.get_camera_labels(name)
Event.select(Event.label).where(Event.camera == name).distinct()
)
# loop over object types in db # loop over object types in db
for event in distinct_labels: for event in distinct_labels:
@ -103,18 +128,22 @@ class EventCleanup(threading.Thread):
Event.label == event.label, Event.label == event.label,
Event.retain_indefinitely == False, Event.retain_indefinitely == False,
) )
# delete the grabbed clips from disk # delete the grabbed clips from disk
# only snapshots are stored in /clips
# so no need to delete mp4 files
for event in expired_events: for event in expired_events:
if media_type == EventCleanupType.snapshots:
media_name = f"{event.camera}-{event.id}" media_name = f"{event.camera}-{event.id}"
media_path = Path( media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}" f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}"
) )
media_path.unlink(missing_ok=True) media_path.unlink(missing_ok=True)
if file_extension == "jpg":
media_path = Path( media_path = Path(
f"{os.path.join(CLIPS_DIR, media_name)}-clean.png" f"{os.path.join(CLIPS_DIR, media_name)}-clean.png"
) )
media_path.unlink(missing_ok=True) media_path.unlink(missing_ok=True)
# update the clips attribute for the db entry # update the clips attribute for the db entry
update_query = Event.update(update_params).where( update_query = Event.update(update_params).where(
Event.camera == name, Event.camera == name,
@ -149,8 +178,6 @@ class EventCleanup(threading.Thread):
media_path.unlink(missing_ok=True) 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(CLIPS_DIR, media_name)}-clean.png")
media_path.unlink(missing_ok=True) media_path.unlink(missing_ok=True)
media_path = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4")
media_path.unlink(missing_ok=True)
( (
Event.delete() Event.delete()
@ -161,8 +188,8 @@ class EventCleanup(threading.Thread):
def run(self) -> None: def run(self) -> None:
# only expire events every 5 minutes # only expire events every 5 minutes
while not self.stop_event.wait(300): while not self.stop_event.wait(300):
self.expire("clips") self.expire(EventCleanupType.clips)
self.expire("snapshots") self.expire(EventCleanupType.snapshots)
self.purge_duplicates() self.purge_duplicates()
# drop events from db where has_clip and has_snapshot are false # drop events from db where has_clip and has_snapshot are false