storage: per-camera target + date subfolders; move active-storage widget

- Camera.storage_id: each camera chooses its storage ("" = active/default).
  supervisor resolves per-camera (storage_by_id → active fallback); cameras
  API (body/payload) + /toggle accept storage_id and repoint the recorder.
- Recordings now go to <root>/<cam>/<YYYY-MM-DD>/<file>.mp4. This ffmpeg build
  lacks -strftime_mkdir, so recorder_task pre-creates today+tomorrow dirs at
  start and the indexer re-ensures them each tick (survives midnight).
- indexer: scan date subdirs + legacy flat files; sort by basename so the
  in-progress segment is detected correctly when both layouts coexist.
- frontend: per-camera "Хранилище" dropdown in the camera table (inline) and
  in the add/edit + bulk forms. Move the active-storage summary widget from
  the Система tab into the Хранилища tab.

VERSION 0.0.129.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 19:47:44 +05:00
parent a64749ccc8
commit 8e4844111b
9 changed files with 114 additions and 40 deletions
+6 -6
View File
@@ -56,7 +56,7 @@
</div>
<table class="cams">
<thead>
<tr><th style="width:48px">ID</th><th>Камера</th><th>IP</th><th>Потоки</th><th style="width:56px">Вкл</th><th style="width:64px">Запись</th><th>Статус</th><th></th></tr>
<tr><th style="width:48px">ID</th><th>Камера</th><th>IP</th><th>Потоки</th><th style="width:56px">Вкл</th><th style="width:64px">Запись</th><th style="width:150px">Хранилище</th><th>Статус</th><th></th></tr>
</thead>
<tbody id="cam-rows"></tbody>
</table>
@@ -140,16 +140,16 @@
<div style="margin-top:12px"><button id="pol-save">Сохранить</button></div>
<div class="muted" id="pol-saved" style="margin-top:8px;font-size:12px"></div>
</div>
<div class="card">
<h2>Активное хранилище <span class="muted" style="font-weight:400;font-size:12px">— сводка</span></h2>
<div class="kv" id="storage-kv"><div class="muted">загрузка…</div></div>
</div>
</div>
<!-- ВКЛАДКА 2: Система -->
<div class="tab-panel" data-panel="general" data-min-role="operator" hidden>
<div class="wrap">
<div class="card">
<h2>Хранилище <span class="muted" style="font-weight:400;font-size:12px">— активное (управление во вкладке «Хранилища»)</span></h2>
<div class="kv" id="storage-kv"><div class="muted">загрузка…</div></div>
</div>
<div class="card">
<h2>Система</h2>
<div class="kv" id="system-kv"><div class="muted">загрузка…</div></div>