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:
@@ -39,6 +39,7 @@ def _camera_from_body(body: dict, cam_id: str) -> Camera:
|
||||
password=password,
|
||||
main_path=(body.get("main_path") or DEFAULT_MAIN).strip(),
|
||||
sub_path=sub,
|
||||
storage_id=(body.get("storage_id") or "").strip(),
|
||||
)
|
||||
|
||||
|
||||
@@ -57,6 +58,7 @@ def _camera_payload(cam_id: str) -> dict:
|
||||
"password": cam.password,
|
||||
"main_path": cam.main_path,
|
||||
"sub_path": cam.sub_path,
|
||||
"storage_id": cam.storage_id,
|
||||
"has_sub": has_sub,
|
||||
"status": status.to_dict() if status else None,
|
||||
# live-источники go2rtc: raw (как есть) и h264 (перекодирование по опции)
|
||||
@@ -324,8 +326,10 @@ async def toggle_camera(cam_id: str, request: Request) -> dict:
|
||||
kw["enabled"] = bool(body["enabled"])
|
||||
if "record" in body:
|
||||
kw["record"] = bool(body["record"])
|
||||
if "storage_id" in body: # смена хранилища записи камеры
|
||||
kw["storage_id"] = (body["storage_id"] or "").strip()
|
||||
if not kw:
|
||||
raise HTTPException(400, "ожидается enabled и/или record")
|
||||
raise HTTPException(400, "ожидается enabled, record и/или storage_id")
|
||||
enabled_changed = "enabled" in kw and kw["enabled"] != cam.enabled
|
||||
new = replace(cam, **kw)
|
||||
idx = next(i for i, c in enumerate(runtime.config.cameras) if c.id == cam_id)
|
||||
@@ -336,5 +340,5 @@ async def toggle_camera(cam_id: str, request: Request) -> dict:
|
||||
await go2rtc.add_camera_streams(runtime.config, new)
|
||||
else:
|
||||
await go2rtc.remove_camera_streams(runtime.config, cam_id)
|
||||
await runtime.supervisor.replace_camera(new) # рекордер запускается при enabled AND record
|
||||
return {"ok": True, "enabled": new.enabled, "record": new.record}
|
||||
await runtime.supervisor.replace_camera(new) # рекордер: репойнт на новое хранилище / enabled AND record
|
||||
return {"ok": True, "enabled": new.enabled, "record": new.record, "storage_id": new.storage_id}
|
||||
|
||||
Reference in New Issue
Block a user