diff --git a/frigate/storage.py b/frigate/storage.py index bd028ea8c..da5783263 100644 --- a/frigate/storage.py +++ b/frigate/storage.py @@ -174,11 +174,6 @@ class StorageMaintainer(threading.Thread): return root_usages def _get_recordings_root_from_path(self, recording_path: str, camera: str) -> str: - camera_segment = f"/{camera}/" - - if camera_segment in recording_path: - return recording_path.split(camera_segment, 1)[0].rstrip("/") or "/" - # Prefer configured recording roots when available. for configured_root in sorted( self.config.get_recordings_paths(), key=len, reverse=True @@ -195,6 +190,10 @@ class StorageMaintainer(threading.Thread): if date_hour_match: return date_hour_match.group("root").rstrip("/") or "/" + camera_segment = f"/{camera}/" + if camera_segment in recording_path: + return recording_path.split(camera_segment, 1)[0].rstrip("/") or "/" + # Fallback for unexpected path layouts; expected format is root/camera/date/file path = Path(recording_path) if len(path.parents) >= 3: diff --git a/frigate/test/test_storage.py b/frigate/test/test_storage.py index 1a6c9d070..f9370c2e4 100644 --- a/frigate/test/test_storage.py +++ b/frigate/test/test_storage.py @@ -261,6 +261,49 @@ class TestHttp(unittest.TestCase): assert Recordings.get(Recordings.id == rec_k3_id) + def test_get_recordings_root_from_date_hour_layout(self): + """Ensure date/hour recording paths resolve to configured roots.""" + config = FrigateConfig( + **{ + "mqtt": {"host": "mqtt"}, + "record": {"enabled": True, "path": "/media/frigate/recordings"}, + "cameras": { + "cam2_sub": { + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]} + ] + }, + "detect": {"height": 1080, "width": 1920, "fps": 5}, + }, + "cam1_sub": { + "path": "/video1", + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} + ] + }, + "detect": {"height": 1080, "width": 1920, "fps": 5}, + }, + }, + } + ) + storage = StorageMaintainer(config, MagicMock()) + + assert ( + storage._get_recordings_root_from_path( + "/media/frigate/recordings/2026-03-07/11/cam2_sub/20.59.mp4", + "cam2_sub", + ) + == "/media/frigate/recordings" + ) + assert ( + storage._get_recordings_root_from_path( + "/video1/2026-03-07/11/cam1_sub/13.07.mp4", + "cam1_sub", + ) + == "/video1" + ) def test_storage_cleanup_only_for_overflowed_root(self): """Ensure cleanup removes recordings only from the targeted recordings root."""