From 9c20cd5f7b7d6bab0cea359cc7704fafa1ec599b Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 11 Nov 2024 08:30:55 -0700 Subject: [PATCH] Handle in progress previews export and fix time check bug (#14930) * Handle in progress previews and fix time check bug * Formatting --- frigate/api/export.py | 3 ++- frigate/output/output.py | 3 +++ frigate/record/export.py | 29 ++++++++++++++++++++++++++++- frigate/util/builtin.py | 11 +++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/frigate/api/export.py b/frigate/api/export.py index 2b8f0778c..71f612371 100644 --- a/frigate/api/export.py +++ b/frigate/api/export.py @@ -19,6 +19,7 @@ from frigate.record.export import ( PlaybackSourceEnum, RecordingExporter, ) +from frigate.util.builtin import is_current_hour logger = logging.getLogger(__name__) @@ -86,7 +87,7 @@ def export_recording( .count() ) - if previews_count <= 0: + if not is_current_hour(start_time) and previews_count <= 0: return JSONResponse( content=( {"success": False, "message": "No previews found for time range"} diff --git a/frigate/output/output.py b/frigate/output/output.py index 1859ebd69..5d564b936 100644 --- a/frigate/output/output.py +++ b/frigate/output/output.py @@ -144,6 +144,9 @@ def output_frames( # check for any cameras that are currently offline # and need to generate a preview if generated_preview: + logger.debug( + "Checking for offline cameras because another camera generated a preview." + ) for camera, time in preview_write_times.copy().items(): if time != 0 and frame_time - time > 10: preview_recorders[camera].flag_offline(frame_time) diff --git a/frigate/record/export.py b/frigate/record/export.py index 325f08419..a4b9ee521 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -27,6 +27,7 @@ from frigate.ffmpeg_presets import ( parse_preset_hardware_acceleration_encode, ) from frigate.models import Export, Previews, Recordings +from frigate.util.builtin import is_current_hour logger = logging.getLogger(__name__) @@ -235,6 +236,32 @@ class RecordingExporter(threading.Thread): def get_preview_export_command(self, video_path: str) -> list[str]: playlist_lines = [] + codec = "-c copy" + + if is_current_hour(self.start_time): + # get list of current preview frames + preview_dir = os.path.join(CACHE_DIR, "preview_frames") + file_start = f"preview_{self.camera}" + start_file = f"{file_start}-{self.start_time}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}-{self.end_time}.{PREVIEW_FRAME_TYPE}" + + for file in sorted(os.listdir(preview_dir)): + if not file.startswith(file_start): + continue + + if file < start_file: + continue + + if file > end_file: + break + + playlist_lines.append(f"file '{os.path.join(preview_dir, file)}'") + playlist_lines.append("duration 0.12") + + if playlist_lines: + last_file = playlist_lines[-2] + playlist_lines.append(last_file) + codec = "-c:v libx264" # get full set of previews export_previews = ( @@ -277,7 +304,7 @@ class RecordingExporter(threading.Thread): if self.playback_factor == PlaybackFactorEnum.realtime: ffmpeg_cmd = ( - f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} -c copy -movflags +faststart {video_path}" + f"{self.config.ffmpeg.ffmpeg_path} -hide_banner {ffmpeg_input} {codec} -movflags +faststart {video_path}" ).split(" ") elif self.playback_factor == PlaybackFactorEnum.timelapse_25x: ffmpeg_cmd = ( diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index 6dab16206..af8ababe9 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -282,6 +282,17 @@ def get_tomorrow_at_time(hour: int) -> datetime.datetime: ) +def is_current_hour(timestamp: int) -> bool: + """Returns if timestamp is in the current UTC hour.""" + start_of_next_hour = ( + datetime.datetime.now(datetime.timezone.utc).replace( + minute=0, second=0, microsecond=0 + ) + + datetime.timedelta(hours=1) + ).timestamp() + return timestamp < start_of_next_hour + + def clear_and_unlink(file: Path, missing_ok: bool = True) -> None: """clear file then unlink to avoid space retained by file descriptors.""" if not missing_ok and not file.exists():