mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-18 14:18:21 +03:00
Fix recordings storage roots to ignore pseudo stat paths
This commit is contained in:
parent
ced95052ee
commit
51b66857a0
@ -37,8 +37,40 @@ router = APIRouter(tags=[Tags.recordings])
|
||||
def get_recordings_storage_usage(request: Request):
|
||||
storage_stats = request.app.stats_emitter.get_latest_stats()["service"]["storage"]
|
||||
|
||||
configured_recording_paths = request.app.frigate_config.get_recordings_paths()
|
||||
recording_stats = [storage_stats.get(path, {}) for path in configured_recording_paths]
|
||||
def normalize_storage_path(path: str) -> str:
|
||||
return str(Path(path)).rstrip("/") or "/"
|
||||
|
||||
def resolve_root_storage_stats(root_path: str) -> dict:
|
||||
normalized_root = normalize_storage_path(root_path)
|
||||
|
||||
direct_stats = storage_stats.get(normalized_root, {})
|
||||
if direct_stats:
|
||||
return direct_stats
|
||||
|
||||
# Some stats payloads include date/hour pseudo-paths under a configured
|
||||
# recordings root. In that case, use a descendant stat for the owning root
|
||||
# without exposing the pseudo-path as a separate root entry.
|
||||
descendant_candidates = [
|
||||
(normalize_storage_path(path), stats)
|
||||
for path, stats in storage_stats.items()
|
||||
if normalize_storage_path(path).startswith(f"{normalized_root}/") and stats
|
||||
]
|
||||
|
||||
if not descendant_candidates:
|
||||
return {}
|
||||
|
||||
descendant_candidates.sort(key=lambda item: len(item[0]))
|
||||
return descendant_candidates[0][1]
|
||||
|
||||
configured_recording_paths = sorted(
|
||||
{
|
||||
normalize_storage_path(path)
|
||||
for path in request.app.frigate_config.get_recordings_paths()
|
||||
}
|
||||
)
|
||||
recording_stats = [
|
||||
resolve_root_storage_stats(path) for path in configured_recording_paths
|
||||
]
|
||||
total_mb = sum(stat.get("total", 0) for stat in recording_stats)
|
||||
|
||||
if total_mb == 0:
|
||||
@ -57,11 +89,13 @@ def get_recordings_storage_usage(request: Request):
|
||||
|
||||
recording_roots = []
|
||||
all_recording_roots = sorted(
|
||||
set(configured_recording_paths).union(root_camera_usages.keys())
|
||||
set(configured_recording_paths).union(
|
||||
normalize_storage_path(path) for path in root_camera_usages.keys()
|
||||
)
|
||||
)
|
||||
|
||||
for root_path in all_recording_roots:
|
||||
root_stats = storage_stats.get(root_path, {})
|
||||
root_stats = resolve_root_storage_stats(root_path)
|
||||
total = root_stats.get("total", 0)
|
||||
used = root_stats.get("used", 0)
|
||||
free = root_stats.get("free", 0)
|
||||
|
||||
@ -307,3 +307,39 @@ class TestHttpRecordingsStorage(BaseTestHttp):
|
||||
assert "/video1/2026-03-07/08" not in roots
|
||||
assert "/video1/2026-03-07/09" not in roots
|
||||
|
||||
def test_recordings_storage_ignores_pseudo_root_storage_stat_entries(self):
|
||||
self.minimal_config["cameras"]["back_yard"] = {
|
||||
"ffmpeg": {
|
||||
"inputs": [{"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]}]
|
||||
},
|
||||
"detect": {"height": 1080, "width": 1920, "fps": 5},
|
||||
"path": "/video1",
|
||||
}
|
||||
|
||||
self.test_stats["service"]["storage"]["/video1"] = {
|
||||
"free": 600,
|
||||
"mount_type": "ext4",
|
||||
"total": 1000,
|
||||
"used": 400,
|
||||
}
|
||||
self.test_stats["service"]["storage"]["/media/frigate/recordings/2026-03-07/10"] = {
|
||||
"free": 700,
|
||||
"mount_type": "ext4",
|
||||
"total": 1000,
|
||||
"used": 300,
|
||||
}
|
||||
self.test_stats["service"]["storage"]["/video1/2026-03-07/10"] = {
|
||||
"free": 500,
|
||||
"mount_type": "ext4",
|
||||
"total": 1000,
|
||||
"used": 500,
|
||||
}
|
||||
|
||||
app = self._build_app()
|
||||
|
||||
with AuthTestClient(app) as client:
|
||||
payload = client.get("/recordings/storage").json()
|
||||
|
||||
root_paths = {root["path"] for root in payload["recording_roots"]}
|
||||
|
||||
assert root_paths == {"/media/frigate/recordings", "/video1"}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user