Miscellaneous fixes (0.18 beta) (#23790)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s

* recreate review thumbnail directory before writing and log write failures

cleanup's remove_empty_directories() can rmdir an empty clips/review, after which thumbnail writes silently fail. Ensure the directory exists before both cv2.imwrite calls and check their return value

* add docs for add camera wizard

* Handle indefinite events when a segment needs to forcibly be ended for a ceamera

* update keyframe interval article link

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
Josh Hawkins
2026-07-24 10:08:21 -06:00
committed by GitHub
co-authored by Nicolas Mowen
parent e3fa701893
commit 9f918362e9
6 changed files with 66 additions and 9 deletions
+18 -4
View File
@@ -115,9 +115,11 @@ class PendingReviewSegment:
if self._frame is not None:
self.thumb_time = datetime.datetime.now().timestamp()
self.has_frame = True
cv2.imwrite(
Path(self.frame_path).parent.mkdir(parents=True, exist_ok=True)
if not cv2.imwrite(
self.frame_path, self._frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60]
)
):
logger.error("Failed to write review thumbnail to %s", self.frame_path)
def save_full_frame(self, camera_config: CameraConfig, frame: np.ndarray) -> None:
color_frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420)
@@ -128,9 +130,11 @@ class PendingReviewSegment:
if self._frame is not None:
self.has_frame = True
cv2.imwrite(
Path(self.frame_path).parent.mkdir(parents=True, exist_ok=True)
if not cv2.imwrite(
self.frame_path, self._frame, [int(cv2.IMWRITE_WEBP_QUALITY), 60]
)
):
logger.error("Failed to write review thumbnail to %s", self.frame_path)
def get_data(self, ended: bool) -> dict:
end_time = None
@@ -374,6 +378,16 @@ class ReviewSegmentMaintainer(threading.Thread):
"""Forcibly end the pending segment for a camera."""
segment = self.active_review_segments.get(camera)
if segment:
if self.indefinite_events.get(camera):
self.indefinite_events[camera] = {}
now = datetime.datetime.now().timestamp()
if segment.last_alert_time == sys.maxsize:
segment.last_alert_time = now
if segment.last_detection_time == sys.maxsize:
segment.last_detection_time = now
prev_data = segment.get_data(False)
return self._publish_segment_end(segment, prev_data)
return None