StorageMaintainer now uses three-tier prioritized deletion when in
continuous_rollover mode instead of naive oldest-first:
1. Overwritable: continuous recordings with no event/review overlap
2. Event retention: recordings tied to active review segments
3. Protected: recordings tied to retain_indefinitely events (last resort)
This ensures event-associated footage is preserved longer than plain
continuous filler when disk space runs low. The default time-based
policy path is completely unchanged.
- Extract _finalize_deleted_recordings() helper to share cleanup logic
- Add camera-scoped review segment overlap checking
- Add 4 new tests covering prioritized deletion scenarios
- Add storage.breakdown.* i18n keys to en/views/system.json locale file
and remove inline fallback strings from t() calls
- Use lazy logging format (%s) instead of f-string in new debug log line
- Rewrite retain_policy tests to target RecordConfig directly, avoiding
Docker-only dependencies (labelmap, /config, detector runtimes)
- Add enum value and serialization roundtrip tests
Test RecordConfig directly instead of FrigateConfig to avoid dependencies
on Docker-only paths (/labelmap.txt, /config) and detector runtimes.
Added enum value and serialization roundtrip tests.
- C3: Make retain_policy global-only by hiding from per-camera settings
UI and removing from CameraConfig TypeScript type
- C4: Add missing units field to StorageBreakdown TypeScript type to
match API response
- I3: Add 24-hour grace period for deleted camera recordings in rollover
mode to prevent data loss if camera is temporarily removed from config
- I6: Deduplicate model_validator warning by using module-level flag to
prevent N+1 duplicate warnings during config propagation
- S5: Add debug log when skipping time-based expiry in rollover mode and
clean up redundant if/not-if to if/else
- C1: Rewrite breakdown endpoint with JOIN queries (eliminates N+1)
- C2: Use getUnitSize() for consistent MiB/GiB display
- C3: Add retain_policy to global FrigateConfig TypeScript type
- I1: Only fetch breakdown data when rollover mode is active
- I2: Handle NULL end_time for in-progress events/reviews
- I5: Add model_validator warning when rollover + days are both set
- S4: Hide breakdown when total is 0
New GET /recordings/storage/breakdown endpoint returns storage usage
broken into three categories: overwritable (continuous recordings),
event_retention (active alerts/detections), and protected (indefinite).
In rollover mode, RecordingCleanup no longer deletes recordings based
on continuous.days / motion.days. Instead, StorageMaintainer handles
overflow by deleting oldest recordings when disk fills up. Deleted
cameras have their recordings removed immediately rather than waiting
for time-based expiry. Review segment expiry still runs normally.
Introduce a new RetainPolicyEnum with 'time' (default, existing behavior)
and 'continuous_rollover' (fill disk, overwrite oldest) options. Add the
retain_policy field to RecordConfig and include unit tests for config
validation.