This commit is contained in:
leccelecce 2026-03-10 22:57:53 +00:00 committed by leccelecce
parent 8294d2f611
commit 1f31dba7b6
2 changed files with 55 additions and 31 deletions

View File

@ -45,6 +45,7 @@ from frigate.models import Event, Previews, Recordings, Regions, ReviewSegment
from frigate.output.preview import get_most_recent_preview_frame from frigate.output.preview import get_most_recent_preview_frame
from frigate.track.object_processing import TrackedObjectProcessor from frigate.track.object_processing import TrackedObjectProcessor
from frigate.util.file import ( from frigate.util.file import (
get_event_snapshot_path,
get_event_snapshot_bytes, get_event_snapshot_bytes,
get_event_thumbnail_bytes, get_event_thumbnail_bytes,
load_event_snapshot_image, load_event_snapshot_image,
@ -1055,8 +1056,10 @@ def clear_region_grid(request: Request, camera_name: str):
) )
def event_snapshot_clean(request: Request, event_id: str, download: bool = False): def event_snapshot_clean(request: Request, event_id: str, download: bool = False):
webp_bytes = None webp_bytes = None
event_complete = False
try: try:
event = Event.get(Event.id == event_id) event = Event.get(Event.id == event_id)
event_complete = event.end_time is not None
snapshot_config = request.app.frigate_config.cameras[event.camera].snapshots snapshot_config = request.app.frigate_config.cameras[event.camera].snapshots
if not (snapshot_config.enabled and event.has_snapshot): if not (snapshot_config.enabled and event.has_snapshot):
return JSONResponse( return JSONResponse(
@ -1094,8 +1097,10 @@ def event_snapshot_clean(request: Request, event_id: str, download: bool = False
) )
if webp_bytes is None: if webp_bytes is None:
try: try:
image, is_clean_snapshot = load_event_snapshot_image(event, clean_only=True) image_path, is_clean_snapshot = get_event_snapshot_path(
if not is_clean_snapshot or image is None: event, clean_only=True
)
if not is_clean_snapshot or image_path is None:
return JSONResponse( return JSONResponse(
content={ content={
"success": False, "success": False,
@ -1104,19 +1109,33 @@ def event_snapshot_clean(request: Request, event_id: str, download: bool = False
status_code=404, status_code=404,
) )
ret, webp_data = cv2.imencode( if image_path.endswith(".webp"):
".webp", image, get_image_quality_params("webp", None) with open(image_path, "rb") as image_file:
) webp_bytes = image_file.read()
if not ret: else:
return JSONResponse( image = load_event_snapshot_image(event, clean_only=True)[0]
content={ if image is None:
"success": False, return JSONResponse(
"message": "Unable to convert snapshot to webp", content={
}, "success": False,
status_code=400, "message": "Unable to load clean snapshot for event",
) },
status_code=400,
)
webp_bytes = webp_data.tobytes() ret, webp_data = cv2.imencode(
".webp", image, get_image_quality_params("webp", None)
)
if not ret:
return JSONResponse(
content={
"success": False,
"message": "Unable to convert snapshot to webp",
},
status_code=400,
)
webp_bytes = webp_data.tobytes()
except Exception: except Exception:
logger.error(f"Unable to load clean snapshot for event: {event.id}") logger.error(f"Unable to load clean snapshot for event: {event.id}")
return JSONResponse( return JSONResponse(
@ -1129,7 +1148,7 @@ def event_snapshot_clean(request: Request, event_id: str, download: bool = False
headers = { headers = {
"Content-Type": "image/webp", "Content-Type": "image/webp",
"Cache-Control": "private, max-age=31536000", "Cache-Control": "private, max-age=31536000" if event_complete else "no-cache",
} }
if download: if download:

View File

@ -55,38 +55,43 @@ def _event_snapshot_is_clean(event: Event) -> bool:
return bool(event.data and event.data.get("snapshot_clean")) return bool(event.data and event.data.get("snapshot_clean"))
def load_event_snapshot_image( def get_event_snapshot_path(
event: Event, *, clean_only: bool = False event: Event, *, clean_only: bool = False
) -> tuple[ndarray | None, bool]: ) -> tuple[str | None, bool]:
clean_snapshot_paths = [ clean_snapshot_paths = [
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}-clean.webp"), os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}-clean.webp"),
os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}-clean.png"), os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}-clean.png"),
] ]
for image_path in clean_snapshot_paths: for image_path in clean_snapshot_paths:
if not os.path.exists(image_path): if os.path.exists(image_path):
continue return image_path, True
image = _load_snapshot_image(image_path)
if image is None:
logger.warning("Unable to load clean snapshot from %s", image_path)
continue
return image, True
snapshot_path = os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}.jpg") snapshot_path = os.path.join(CLIPS_DIR, f"{event.camera}-{event.id}.jpg")
if not os.path.exists(snapshot_path): if not os.path.exists(snapshot_path):
return None, False return None, False
image = _load_snapshot_image(snapshot_path)
if image is None:
logger.warning("Unable to load snapshot from %s", snapshot_path)
return None, False
is_clean_snapshot = _event_snapshot_is_clean(event) is_clean_snapshot = _event_snapshot_is_clean(event)
if clean_only and not is_clean_snapshot: if clean_only and not is_clean_snapshot:
return None, False return None, False
return snapshot_path, is_clean_snapshot
def load_event_snapshot_image(
event: Event, *, clean_only: bool = False
) -> tuple[ndarray | None, bool]:
image_path, is_clean_snapshot = get_event_snapshot_path(
event, clean_only=clean_only
)
if image_path is None:
return None, False
image = _load_snapshot_image(image_path)
if image is None:
logger.warning("Unable to load snapshot from %s", image_path)
return None, False
return image, is_clean_snapshot return image, is_clean_snapshot