diff --git a/frigate/api/export.py b/frigate/api/export.py index 786e046dbb..614ac78db6 100644 --- a/frigate/api/export.py +++ b/frigate/api/export.py @@ -31,7 +31,6 @@ from frigate.api.defs.tags import Tags from frigate.const import CLIPS_DIR, EXPORT_DIR from frigate.models import Export, Previews, Recordings from frigate.record.export import ( - ChaptersEnum, PlaybackFactorEnum, PlaybackSourceEnum, RecordingExporter, @@ -94,6 +93,14 @@ def export_recording( friendly_name = body.name existing_image = sanitize_filepath(body.image_path) if body.image_path else None + # a chapters value in the request body overrides the camera's export config + camera_config = request.app.frigate_config.cameras[camera_name] + chapters = ( + body.chapters + if body.chapters is not None + else camera_config.record.export.chapters + ) + # Ensure that existing_image is a valid path if existing_image and not existing_image.startswith(CLIPS_DIR): return JSONResponse( @@ -162,7 +169,7 @@ def export_recording( if playback_source in PlaybackSourceEnum.__members__.values() else PlaybackSourceEnum.recordings ), - chapters=ChaptersEnum(body.chapters) if body.chapters else None, + chapters=chapters, ) exporter.start() return JSONResponse( diff --git a/frigate/config/camera/record.py b/frigate/config/camera/record.py index 09a7a84d5b..4c8f873568 100644 --- a/frigate/config/camera/record.py +++ b/frigate/config/camera/record.py @@ -9,6 +9,7 @@ from frigate.review.types import SeverityEnum from ..base import FrigateBaseModel __all__ = [ + "ChaptersEnum", "RecordConfig", "RecordExportConfig", "RecordPreviewConfig", @@ -66,10 +67,19 @@ class RecordPreviewConfig(FrigateBaseModel): ) +class ChaptersEnum(str, Enum): + none = "none" + recording_segments = "recording_segments" + + class RecordExportConfig(FrigateBaseModel): timelapse_args: str = Field( default=DEFAULT_TIME_LAPSE_FFMPEG_ARGS, title="Timelapse Args" ) + chapters: ChaptersEnum = Field( + default=ChaptersEnum.none, + title="Chapter metadata to embed in exported recordings", + ) class RecordConfig(FrigateBaseModel): diff --git a/frigate/record/export.py b/frigate/record/export.py index d5d5ddb766..28a72d05ea 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -16,6 +16,7 @@ import pytz from peewee import DoesNotExist from frigate.config import FfmpegConfig, FrigateConfig +from frigate.config.camera.record import ChaptersEnum from frigate.const import ( CACHE_DIR, CLIPS_DIR, @@ -51,14 +52,6 @@ class PlaybackSourceEnum(str, Enum): preview = "preview" -class ChaptersEnum(str, Enum): - # One chapter per recording segment, titled with the segment's - # wallclock start time in strict ISO 8601 form. Lets viewers map - # output playback time back to wallclock without reading a timestamp - # overlay via OCR. - recording_segments = "recording_segments" - - class RecordingExporter(threading.Thread): """Exports a specific set of recordings for a camera to storage as a single file.""" @@ -358,6 +351,8 @@ class RecordingExporter(threading.Thread): f"title={title}", "-metadata", f"creation_time={creation_time}", + "-metadata", + f"comment=Camera: {self.camera}", ] ) @@ -435,7 +430,7 @@ class RecordingExporter(threading.Thread): if self.playback_factor == PlaybackFactorEnum.realtime: ffmpeg_cmd = ( - f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} {codec} -movflags +faststart {video_path}" + f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} {codec} -movflags +faststart" ).split(" ") elif self.playback_factor == PlaybackFactorEnum.timelapse_25x: ffmpeg_cmd = ( @@ -443,7 +438,7 @@ class RecordingExporter(threading.Thread): self.config.ffmpeg.ffmpeg_path, self.config.ffmpeg.hwaccel_args, f"{TIMELAPSE_DATA_INPUT_ARGS} {ffmpeg_input}", - f"{self.config.cameras[self.camera].record.export.timelapse_args} -movflags +faststart {video_path}", + f"{self.config.cameras[self.camera].record.export.timelapse_args} -movflags +faststart", EncodeTypeEnum.timelapse, ) ).split(" ") @@ -459,9 +454,13 @@ class RecordingExporter(threading.Thread): f"title={title}", "-metadata", f"creation_time={creation_time}", + "-metadata", + f"comment=Camera: {self.camera}", ] ) + ffmpeg_cmd.append(video_path) + return ffmpeg_cmd, playlist_lines def run(self) -> None: