From 51770e69224962980f8c0cbb55abfbcceffceb57 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:50:13 -0500 Subject: [PATCH] optimize recordings/unavailable gap detection and drop empty motion activity buckets --- frigate/api/record.py | 26 ++++++++++++++++++++------ frigate/api/review.py | 5 +++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/frigate/api/record.py b/frigate/api/record.py index f6366813b6..04b81cf38a 100644 --- a/frigate/api/record.py +++ b/frigate/api/record.py @@ -299,22 +299,36 @@ async def no_recordings( .iterator() ) - # Convert recordings to list of (start, end) tuples + # Convert recordings to list of (start, end) tuples, ordered by start_time recordings = [(r["start_time"], r["end_time"]) for r in data] + # Merge overlapping/adjacent recordings into covered intervals. The query + # orders by start_time, so a single pass merges them + covered: list[tuple[float, float]] = [] + for rec_start, rec_end in recordings: + if covered and rec_start <= covered[-1][1]: + covered[-1] = (covered[-1][0], max(covered[-1][1], rec_end)) + else: + covered.append((rec_start, rec_end)) + # Iterate through time segments and check if each has any recording no_recording_segments = [] current = after current_gap_start = None + idx = 0 + covered_count = len(covered) while current < before: segment_end = min(current + scale, before) - # Check if this segment overlaps with any recording - has_recording = any( - rec_start < segment_end and rec_end > current - for rec_start, rec_end in recordings - ) + # Advance past covered intervals that end before this segment begins; + # they cannot overlap this or any later segment. + while idx < covered_count and covered[idx][1] <= current: + idx += 1 + + # A covered interval overlaps the segment when it starts before the + # segment ends (its end is already known to be > current). + has_recording = idx < covered_count and covered[idx][0] < segment_end if not has_recording: # This segment has no recordings diff --git a/frigate/api/review.py b/frigate/api/review.py index cb114db2a0..bb4a9a70ee 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -658,6 +658,11 @@ def motion_activity( else: df.iloc[i : i + chunk, 0] = 0.0 + # Drop resample gap-fill buckets. The resample above emits a row for every + # {scale}s bucket spanning the range, and buckets with no recording get a + # motion of 0 (from fillna) and an empty camera (from joining an empty set). + df = df[df["camera"] != ""] + # change types for output df.index = df.index.astype(int) // (10**9) normalized = df.reset_index().to_dict("records")