From 07cdec07186c4555647b01d10d67769ecfb84983 Mon Sep 17 00:00:00 2001 From: Sergey Krashevich Date: Tue, 23 May 2023 22:34:42 +0300 Subject: [PATCH] Add option to archive expired records to S3 instead of deleting them --- frigate/config.py | 1 + frigate/record/cleanup.py | 4 ++-- frigate/record/maintainer.py | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/frigate/config.py b/frigate/config.py index 88d1d6554..273773ca2 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -523,6 +523,7 @@ class SnapshotsConfig(FrigateBaseModel): class StorageS3Config(FrigateBaseModel): enabled: bool = Field(default=False, title="S3 enabled.") + archive: bool = Field(default=False, title="Archive expired records to S3 instead of delete") access_key_id: str = Field(default="", title="AWS_ACCESS_KEY_ID") secret_access_key: str = Field(default="", title="AWS_SECRET_ACCESS_KEY") bucket_name: str = Field(default="", title="Bucket name") diff --git a/frigate/record/cleanup.py b/frigate/record/cleanup.py index 78e5faba5..3211385a1 100644 --- a/frigate/record/cleanup.py +++ b/frigate/record/cleanup.py @@ -29,7 +29,7 @@ class RecordingCleanup(threading.Thread): self.config = config self.stop_event = stop_event - if self.config.storage.s3.enabled: + if self.config.storage.s3.enabled or self.config.storage.s3.archive: self.s3 = StorageS3(config) def clean_tmp_clips(self) -> None: @@ -144,7 +144,7 @@ class RecordingCleanup(threading.Thread): and recording.objects == 0 ) ): - if self.config.storage.s3.enabled: + if self.config.storage.s3.archive: s3path = self.s3.upload_file_to_s3(recording.path) if s3path != "": moved_recordings.add({"id": recording.id, "path": s3path}) diff --git a/frigate/record/maintainer.py b/frigate/record/maintainer.py index 651bd1214..0a074b87a 100644 --- a/frigate/record/maintainer.py +++ b/frigate/record/maintainer.py @@ -21,6 +21,7 @@ from frigate.const import CACHE_DIR, MAX_SEGMENT_DURATION, RECORD_DIR from frigate.models import Event, Recordings from frigate.types import RecordMetricsTypes from frigate.util import area +from frigate.storage import StorageS3 logger = logging.getLogger(__name__) @@ -42,6 +43,9 @@ class RecordingMaintainer(threading.Thread): self.recordings_info: dict[str, Any] = defaultdict(list) self.end_time_cache: dict[str, Tuple[datetime.datetime, float]] = {} + if self.config.storage.s3.enabled: + self.s3 = StorageS3(config) + def move_files(self) -> None: cache_files = sorted( [ @@ -335,6 +339,17 @@ class RecordingMaintainer(threading.Thread): rand_id = "".join( random.choices(string.ascii_lowercase + string.digits, k=6) ) + storage = "local" + if self.config.storage.s3.enabled: + s3path = self.s3.upload_file_to_s3(file_path) + if s3path != "": + Path(file_path).unlink(missing_ok=True) + file_path = s3path + storage = "s3" + else: + logger.error(f"Unable to upload recording segment {file_path} to s3, fallback to local") + logger.error(e) + Recordings.create( id=f"{start_time.timestamp()}-{rand_id}", camera=camera, @@ -346,6 +361,7 @@ class RecordingMaintainer(threading.Thread): # TODO: update this to store list of active objects at some point objects=active_count, segment_size=segment_size, + storage=storage, ) except Exception as e: logger.error(f"Unable to store recording segment {cache_path}")