mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-26 05:08:58 +03:00
fix frozen time bounds on the recordings API (#24121)
`after` and `before` defaulted to `datetime.now()` in the function signature, so they were evaluated once at import. Requests that omitted them got a window ending at process start. They now resolve in the handler.
This commit is contained in:
committed by
Nicolas Mowen
parent
e2da7aae99
commit
9416e74aeb
Vendored
+6
-2
@@ -7197,13 +7197,17 @@ paths:
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: number
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: 'null'
|
||||
title: After
|
||||
- name: before
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: number
|
||||
anyOf:
|
||||
- type: number
|
||||
- type: 'null'
|
||||
title: Before
|
||||
responses:
|
||||
'200':
|
||||
|
||||
@@ -358,10 +358,13 @@ async def recordings_coverage(
|
||||
@router.get("/{camera_name}/recordings", dependencies=[Depends(require_camera_access)])
|
||||
async def recordings(
|
||||
camera_name: str,
|
||||
after: float = (datetime.now() - timedelta(hours=1)).timestamp(),
|
||||
before: float = datetime.now().timestamp(),
|
||||
after: float | None = None,
|
||||
before: float | None = None,
|
||||
):
|
||||
"""Return specific camera recordings between the given 'after'/'end' times. If not provided the last hour will be used"""
|
||||
now = datetime.now()
|
||||
after = after if after is not None else (now - timedelta(hours=1)).timestamp()
|
||||
before = before if before is not None else now.timestamp()
|
||||
recordings = (
|
||||
Recordings.select(
|
||||
Recordings.id,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Unit tests for recordings/media API endpoints."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytz
|
||||
from fastapi import Request
|
||||
@@ -1838,6 +1839,27 @@ class TestHttpMedia(BaseTestHttp):
|
||||
recording["id"] for recording in response.json()
|
||||
] == expected_ids
|
||||
|
||||
def test_recordings_default_range_follows_the_request_clock(self):
|
||||
"""Omitted bounds resolve at request time, not at module import time."""
|
||||
uptime = timedelta(hours=2)
|
||||
recorded_at = (datetime.now() + uptime).timestamp()
|
||||
|
||||
class UptimeDatetime(datetime):
|
||||
"""Stands in for a process that booted `uptime` ago."""
|
||||
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return datetime.now(tz) + uptime
|
||||
|
||||
with AuthTestClient(self.app) as client:
|
||||
self._insert_recording("after_boot", recorded_at - 20, recorded_at - 10)
|
||||
|
||||
with patch("frigate.api.record.datetime", UptimeDatetime):
|
||||
response = client.get("/front_door/recordings")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert [recording["id"] for recording in response.json()] == ["after_boot"]
|
||||
|
||||
def test_vod_handles_all_range_relations(self):
|
||||
"""VOD clips every interval relation with positive playback duration."""
|
||||
with AuthTestClient(self.app) as client:
|
||||
|
||||
@@ -410,11 +410,10 @@ def enrich(spec: dict, access_map: dict) -> tuple[dict, list, list]:
|
||||
|
||||
|
||||
# Numeric defaults at or above this magnitude are treated as live Unix
|
||||
# timestamps baked into the schema at import time (e.g. the /{camera_name}
|
||||
# /recordings after/before params default to datetime.now()). They make the
|
||||
# export non-deterministic and document a meaningless frozen epoch, so they are
|
||||
# stripped. The proper fix is to default those route params to None and resolve
|
||||
# "now" inside the handler.
|
||||
# timestamps baked into the schema at import time. They make the export
|
||||
# non-deterministic and document a meaningless frozen epoch, so they are
|
||||
# stripped. No route defaults this way today, and route params that need "now"
|
||||
# resolve it inside the handler. Kept as a guard against the pattern returning.
|
||||
VOLATILE_DEFAULT_THRESHOLD = 1_000_000_000
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user