mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-03 12:07:40 +03:00
Implement tiered recording
This commit is contained in:
parent
20e0addae1
commit
55c598fc5a
@ -22,27 +22,31 @@ __all__ = [
|
|||||||
DEFAULT_TIME_LAPSE_FFMPEG_ARGS = "-vf setpts=0.04*PTS -r 30"
|
DEFAULT_TIME_LAPSE_FFMPEG_ARGS = "-vf setpts=0.04*PTS -r 30"
|
||||||
|
|
||||||
|
|
||||||
|
class RecordRetainConfig(FrigateBaseModel):
|
||||||
|
days: float = Field(default=0, ge=0, title="Default retention period.")
|
||||||
|
|
||||||
|
|
||||||
class RetainModeEnum(str, Enum):
|
class RetainModeEnum(str, Enum):
|
||||||
all = "all"
|
all = "all"
|
||||||
motion = "motion"
|
motion = "motion"
|
||||||
active_objects = "active_objects"
|
active_objects = "active_objects"
|
||||||
|
|
||||||
|
|
||||||
class RecordRetainConfig(FrigateBaseModel):
|
|
||||||
days: float = Field(default=0, title="Default retention period.")
|
|
||||||
mode: RetainModeEnum = Field(default=RetainModeEnum.all, title="Retain mode.")
|
|
||||||
|
|
||||||
|
|
||||||
class ReviewRetainConfig(FrigateBaseModel):
|
class ReviewRetainConfig(FrigateBaseModel):
|
||||||
days: float = Field(default=10, title="Default retention period.")
|
days: float = Field(default=10, ge=10, title="Default retention period.")
|
||||||
mode: RetainModeEnum = Field(default=RetainModeEnum.motion, title="Retain mode.")
|
mode: RetainModeEnum = Field(default=RetainModeEnum.motion, title="Retain mode.")
|
||||||
|
|
||||||
|
|
||||||
class EventsConfig(FrigateBaseModel):
|
class EventsConfig(FrigateBaseModel):
|
||||||
pre_capture: int = Field(
|
pre_capture: int = Field(
|
||||||
default=5, title="Seconds to retain before event starts.", le=MAX_PRE_CAPTURE
|
default=5,
|
||||||
|
title="Seconds to retain before event starts.",
|
||||||
|
le=MAX_PRE_CAPTURE,
|
||||||
|
ge=0,
|
||||||
|
)
|
||||||
|
post_capture: int = Field(
|
||||||
|
default=5, ge=0, title="Seconds to retain after event ends."
|
||||||
)
|
)
|
||||||
post_capture: int = Field(default=5, title="Seconds to retain after event ends.")
|
|
||||||
retain: ReviewRetainConfig = Field(
|
retain: ReviewRetainConfig = Field(
|
||||||
default_factory=ReviewRetainConfig, title="Event retention settings."
|
default_factory=ReviewRetainConfig, title="Event retention settings."
|
||||||
)
|
)
|
||||||
@ -77,8 +81,12 @@ class RecordConfig(FrigateBaseModel):
|
|||||||
default=60,
|
default=60,
|
||||||
title="Number of minutes to wait between cleanup runs.",
|
title="Number of minutes to wait between cleanup runs.",
|
||||||
)
|
)
|
||||||
retain: RecordRetainConfig = Field(
|
continuous: RecordRetainConfig = Field(
|
||||||
default_factory=RecordRetainConfig, title="Record retention settings."
|
default_factory=RecordRetainConfig,
|
||||||
|
title="Continuous recording retention settings.",
|
||||||
|
)
|
||||||
|
motion: RecordRetainConfig = Field(
|
||||||
|
default_factory=RecordRetainConfig, title="Motion recording retention settings."
|
||||||
)
|
)
|
||||||
detections: EventsConfig = Field(
|
detections: EventsConfig = Field(
|
||||||
default_factory=EventsConfig, title="Detection specific retention settings."
|
default_factory=EventsConfig, title="Detection specific retention settings."
|
||||||
|
|||||||
@ -100,7 +100,11 @@ class RecordingCleanup(threading.Thread):
|
|||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
def expire_existing_camera_recordings(
|
def expire_existing_camera_recordings(
|
||||||
self, expire_date: float, config: CameraConfig, reviews: ReviewSegment
|
self,
|
||||||
|
continuous_expire_date: float,
|
||||||
|
motion_expire_date: float,
|
||||||
|
config: CameraConfig,
|
||||||
|
reviews: ReviewSegment,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Delete recordings for existing camera based on retention config."""
|
"""Delete recordings for existing camera based on retention config."""
|
||||||
# Get the timestamp for cutoff of retained days
|
# Get the timestamp for cutoff of retained days
|
||||||
@ -116,8 +120,14 @@ class RecordingCleanup(threading.Thread):
|
|||||||
Recordings.motion,
|
Recordings.motion,
|
||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
Recordings.camera == config.name,
|
(Recordings.camera == config.name)
|
||||||
Recordings.end_time < expire_date,
|
& (
|
||||||
|
(
|
||||||
|
(Recordings.end_time < continuous_expire_date)
|
||||||
|
& (Recordings.motion == 0)
|
||||||
|
)
|
||||||
|
| (Recordings.end_time < motion_expire_date)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.order_by(Recordings.start_time)
|
.order_by(Recordings.start_time)
|
||||||
.namedtuples()
|
.namedtuples()
|
||||||
@ -188,7 +198,7 @@ class RecordingCleanup(threading.Thread):
|
|||||||
Recordings.id << deleted_recordings_list[i : i + max_deletes]
|
Recordings.id << deleted_recordings_list[i : i + max_deletes]
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
previews: Previews = (
|
previews: list[Previews] = (
|
||||||
Previews.select(
|
Previews.select(
|
||||||
Previews.id,
|
Previews.id,
|
||||||
Previews.start_time,
|
Previews.start_time,
|
||||||
@ -196,8 +206,14 @@ class RecordingCleanup(threading.Thread):
|
|||||||
Previews.path,
|
Previews.path,
|
||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
Previews.camera == config.name,
|
(Recordings.camera == config.name)
|
||||||
Previews.end_time < expire_date,
|
& (
|
||||||
|
(
|
||||||
|
(Recordings.end_time < continuous_expire_date)
|
||||||
|
& (Recordings.motion == 0)
|
||||||
|
)
|
||||||
|
| (Recordings.end_time < motion_expire_date)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.order_by(Previews.start_time)
|
.order_by(Previews.start_time)
|
||||||
.namedtuples()
|
.namedtuples()
|
||||||
@ -291,9 +307,12 @@ class RecordingCleanup(threading.Thread):
|
|||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
self.expire_review_segments(config, now)
|
self.expire_review_segments(config, now)
|
||||||
|
continuous_expire_date = (
|
||||||
expire_days = config.record.retain.days
|
now - datetime.timedelta(days=config.record.continuous.days)
|
||||||
expire_date = (now - datetime.timedelta(days=expire_days)).timestamp()
|
).timestamp()
|
||||||
|
motion_expire_date = (
|
||||||
|
now - datetime.timedelta(days=config.record.motion.days)
|
||||||
|
).timestamp()
|
||||||
|
|
||||||
# Get all the reviews to check against
|
# Get all the reviews to check against
|
||||||
reviews: ReviewSegment = (
|
reviews: ReviewSegment = (
|
||||||
@ -306,13 +325,15 @@ class RecordingCleanup(threading.Thread):
|
|||||||
ReviewSegment.camera == camera,
|
ReviewSegment.camera == camera,
|
||||||
# need to ensure segments for all reviews starting
|
# need to ensure segments for all reviews starting
|
||||||
# before the expire date are included
|
# before the expire date are included
|
||||||
ReviewSegment.start_time < expire_date,
|
ReviewSegment.start_time < motion_expire_date,
|
||||||
)
|
)
|
||||||
.order_by(ReviewSegment.start_time)
|
.order_by(ReviewSegment.start_time)
|
||||||
.namedtuples()
|
.namedtuples()
|
||||||
)
|
)
|
||||||
|
|
||||||
self.expire_existing_camera_recordings(expire_date, config, reviews)
|
self.expire_existing_camera_recordings(
|
||||||
|
continuous_expire_date, motion_expire_date, config, reviews
|
||||||
|
)
|
||||||
logger.debug(f"End camera: {camera}.")
|
logger.debug(f"End camera: {camera}.")
|
||||||
|
|
||||||
logger.debug("End all cameras.")
|
logger.debug("End all cameras.")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user