mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-07 05:55:27 +03:00
expose replay state on status endpoint and return 202 from start
This commit is contained in:
parent
e187446d04
commit
6f43ed41f2
@ -29,12 +29,16 @@ class DebugReplayStartResponse(BaseModel):
|
||||
|
||||
success: bool
|
||||
replay_camera: str
|
||||
state: str
|
||||
|
||||
|
||||
class DebugReplayStatusResponse(BaseModel):
|
||||
"""Response for debug replay status."""
|
||||
|
||||
active: bool
|
||||
state: str
|
||||
progress_percent: float | None = None
|
||||
error_message: str | None = None
|
||||
replay_camera: str | None = None
|
||||
source_camera: str | None = None
|
||||
start_time: float | None = None
|
||||
@ -53,10 +57,12 @@ class DebugReplayStopResponse(BaseModel):
|
||||
response_model=DebugReplayStartResponse,
|
||||
dependencies=[Depends(require_role(["admin"]))],
|
||||
summary="Start debug replay",
|
||||
description="Start a debug replay session from camera recordings.",
|
||||
description="Start a debug replay session from camera recordings. Returns "
|
||||
"immediately while clip generation runs asynchronously; poll "
|
||||
"/debug_replay/status to track progress.",
|
||||
)
|
||||
async def start_debug_replay(request: Request, body: DebugReplayStartBody):
|
||||
"""Start a debug replay session."""
|
||||
"""Start a debug replay session asynchronously."""
|
||||
replay_manager = request.app.replay_manager
|
||||
|
||||
if replay_manager.active:
|
||||
@ -77,28 +83,23 @@ async def start_debug_replay(request: Request, body: DebugReplayStartBody):
|
||||
frigate_config=request.app.frigate_config,
|
||||
config_publisher=request.app.config_publisher,
|
||||
)
|
||||
except ValueError:
|
||||
logger.exception("Invalid parameters for debug replay start request")
|
||||
except ValueError as exc:
|
||||
logger.info("Rejected debug replay start request: %s", exc)
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": False,
|
||||
"message": "Invalid debug replay request parameters",
|
||||
"message": str(exc),
|
||||
},
|
||||
status_code=400,
|
||||
)
|
||||
except RuntimeError:
|
||||
logger.exception("Error while starting debug replay session")
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": False,
|
||||
"message": "An internal error occurred while starting debug replay",
|
||||
},
|
||||
status_code=500,
|
||||
)
|
||||
|
||||
return DebugReplayStartResponse(
|
||||
success=True,
|
||||
replay_camera=replay_camera,
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": True,
|
||||
"replay_camera": replay_camera,
|
||||
"state": replay_manager.state.value,
|
||||
},
|
||||
status_code=202,
|
||||
)
|
||||
|
||||
|
||||
@ -132,6 +133,9 @@ def get_debug_replay_status(request: Request):
|
||||
|
||||
return DebugReplayStatusResponse(
|
||||
active=replay_manager.active,
|
||||
state=replay_manager.state.value,
|
||||
progress_percent=replay_manager.progress_percent,
|
||||
error_message=replay_manager.error_message,
|
||||
replay_camera=replay_camera,
|
||||
source_camera=replay_manager.source_camera,
|
||||
start_time=replay_manager.start_ts,
|
||||
|
||||
71
frigate/test/http_api/test_debug_replay_api.py
Normal file
71
frigate/test/http_api/test_debug_replay_api.py
Normal file
@ -0,0 +1,71 @@
|
||||
"""Tests for /debug_replay API endpoints."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from frigate.debug_replay import ReplayState
|
||||
from frigate.models import Event, Recordings, ReviewSegment
|
||||
from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp
|
||||
|
||||
|
||||
class TestDebugReplayAPI(BaseTestHttp):
|
||||
def setUp(self):
|
||||
super().setUp([Event, Recordings, ReviewSegment])
|
||||
self.app = self.create_app()
|
||||
|
||||
def test_start_returns_202_with_state_preparing_clip(self):
|
||||
with patch(
|
||||
"frigate.debug_replay.DebugReplayManager.start",
|
||||
return_value="_replay_front",
|
||||
):
|
||||
with patch.object(
|
||||
type(self.app.replay_manager),
|
||||
"state",
|
||||
new_callable=lambda: property(lambda s: ReplayState.preparing_clip),
|
||||
):
|
||||
with AuthTestClient(self.app) as client:
|
||||
resp = client.post(
|
||||
"/debug_replay/start",
|
||||
json={
|
||||
"camera": "front",
|
||||
"start_time": 100,
|
||||
"end_time": 200,
|
||||
},
|
||||
headers={"remote-user": "admin", "remote-role": "admin"},
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 202)
|
||||
body = resp.json()
|
||||
self.assertTrue(body["success"])
|
||||
self.assertEqual(body["replay_camera"], "_replay_front")
|
||||
self.assertEqual(body["state"], "preparing_clip")
|
||||
|
||||
def test_status_returns_state_and_error_message(self):
|
||||
manager = self.app.replay_manager
|
||||
manager._set_state(ReplayState.error, error_message="ffmpeg failed: boom")
|
||||
|
||||
with AuthTestClient(self.app) as client:
|
||||
resp = client.get(
|
||||
"/debug_replay/status",
|
||||
headers={"remote-user": "admin", "remote-role": "admin"},
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
body = resp.json()
|
||||
self.assertEqual(body["state"], "error")
|
||||
self.assertEqual(body["error_message"], "ffmpeg failed: boom")
|
||||
self.assertIsNone(body["progress_percent"])
|
||||
self.assertFalse(body["active"])
|
||||
|
||||
def test_status_returns_progress_percent_during_preparing_clip(self):
|
||||
manager = self.app.replay_manager
|
||||
manager._set_state(ReplayState.preparing_clip)
|
||||
manager.progress_percent = 37.5
|
||||
|
||||
with AuthTestClient(self.app) as client:
|
||||
resp = client.get(
|
||||
"/debug_replay/status",
|
||||
headers={"remote-user": "admin", "remote-role": "admin"},
|
||||
)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.json()["progress_percent"], 37.5)
|
||||
Loading…
Reference in New Issue
Block a user