"Изменить" now renders all 64 slots as editable rows — filled ones for editing,
empty ones with blank fields (user prefilled "admin", channel 1) for adding.
Saving PUTs changed cameras and POSTs filled empty slots (auto id camN, lowest
free). Removed the per-row "+ Добавить" buttons; empty slots in normal view just
show "пусто". Bulk edit can now be entered with zero cameras.
VERSION 0.0.134.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cameras (esp. an NVR at one IP with many channels) are configured by a channel
number now: channel N → main /Streaming/Channels/{N*100+1}, sub {N*100+2}
(101/102 = ch1, 1101/1102 = ch11, 3201/3202 = ch32). The add/edit form and the
bulk-edit row use a 1-32 dropdown; the camera list shows "канал N". Frontend
derives main_path/sub_path from the channel, so the backend is unchanged.
VERSION 0.0.132.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Изменить" previously replaced each camera row with a vertical .cam-edit-grid
form inside one colspan cell — effectively a different table. Now bulk edit
keeps the same columns and turns the cells into compact inline inputs:
Камера→name, IP→ip, Потоки→main/sub, Вкл/Запись toggles, Хранилище select,
and the Статус column becomes Логин/Пароль (header swaps via data-bulk-label).
Live per-row onchange is suppressed while bulk is active (changes collected
until Сохранить). Inputs are compact (22px) so row height stays dense.
VERSION 0.0.131.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-row "Хранилище" <select> inherited the base select padding (6px 8px,
~32px tall) and inflated every camera row. Scope a compact rule to .stor-sel
(22px height, matching the toggles) so row height stays dense. Edit/bulk-form
selects are unaffected.
VERSION 0.0.130.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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>
Replace the single fixed /records path with a list of storages and an
active selection (where new recordings go). Quota is now per-storage.
- config: StorageItem(id,name,type,path,quota_gb,nas-meta) + Storage keeps
global retention_days/segment_seconds; active + items. Migration from the
old single {path,quota_gb} schema → one local item.
- recorder: write to the active storage; restart_all() repoints on switch;
NAS-not-mounted guard (never write into the container overlay).
- indexer: scan ALL storage roots so recordings survive an active switch.
- retention: enforce quota PER storage via db.total_size_under /
oldest_recordings_under (path-prefix LIKE).
- api/storages: GET/POST/PUT/DELETE + activate + policy. NAS uses host-mount
(backend stores SMB params, generates the mount/fstab command, reports
ismount status). Retire old POST /api/storage.
- frontend: new "Хранилища" settings tab — table with inline per-row quota,
add form (local folder / NAS SMB), global policy (segment + retention).
- compose: add shared ./storages:/storages root for extra local folders and
host-mounted NAS shares.
VERSION 0.0.127.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a separate `record` field so a camera can be registered but fully
off, or on but not recording:
- enabled (Вкл): camera on/off. Disabled = no go2rtc stream (no live),
no recording; entry is just stored. build_streams/sync_all/reconcile
skip disabled cameras; live grid shows "камера выключена".
- record (REC): write segments to disk. Recorder runs only when
enabled AND record.
Unified POST /api/cameras/{id}/toggle {enabled?, record?} (replaces the
/record endpoint); go2rtc is touched only when `enabled` changes, so
flipping REC doesn't blip live. Camera table gets Вкл + Запись toggle
columns; add/edit forms get both switches.
load_config migrates the old schema (no `record` key): old `enabled`
meant "record" with live always on -> enabled=True, record=old enabled.
VERSION -> 0.0.126.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`enabled` already gated recording only (go2rtc/live work regardless), but
it was a buried "Вкл" checkbox. Make it an explicit REC control:
- New "Запись" column with an inline REC switch in each camera row.
- Relabel the add/edit form toggle "Вкл" -> "REC" + tooltip.
- New POST /api/cameras/{id}/record {enabled} that flips recording through
the supervisor only (no go2rtc re-sync, so live view doesn't blip).
Off = view-only (live via go2rtc); on = record segments to disk.
VERSION -> 0.0.124.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The params row was a ragged flex-wrap and "Готовый профиль" was floated
with margin-left:auto, so controls didn't line up and the preset menu
dangled at an odd height. Split the row into a left grid (auto-fit
columns, controls bottom-aligned, full-width) and a separate right-side
"Готовый профиль" zone. Reorder fields logically: codec, H.264 profile,
preset, rate control, bitrate, QP, keyframe interval. Drop the now-dead
SOFT/MEDIUM/HARD button CSS.
VERSION -> 0.0.123.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SOFT/MEDIUM/HARD selector lost its color coding when it became a
dropdown. Re-apply colors (SOFT red, MEDIUM yellow, HARD white) to the
options and to the select's shown value, and right-align the "Готовый
профиль" label (margin-left:auto) so it clearly sits on the right.
VERSION -> 0.0.122.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Double-clicking a grid cell now maximizes that camera ("solo") via a CSS
class on the grid instead of putting the tile into the browser Fullscreen
API. In fullscreen the soloed camera fills the screen; double-clicking
again returns to the grid (staying fullscreen). From windowed mode the
gesture also enters fullscreen so the camera fills the screen. ESC exits
fullscreen natively; on exit the solo state is cleared.
VERSION -> 0.0.121.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Style QP <input type=number> like the <select>s (it rendered with the
default white background -> stood out); add a disabled (greyed) style.
- QP vs bitrate are now mutually DISABLED instead of hidden: QP mode
greys the bitrate field, VBR/CBR greys the QP field. Hiding never
worked because .tr-row label{display:flex} overrode [hidden].
- Replace SOFT/MEDIUM/HARD preset buttons with a "Готовый профиль"
dropdown, placed last (rightmost) in the params row.
VERSION -> 0.0.120.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Player/transcoding settings gain encoder controls:
- Rate-control mode VBR / CBR / QP (constant quantizer). x264: VBR uses
-b:v/-maxrate/-bufsize, CBR adds -x264-params nal-hrd=cbr (min=max=b:v),
QP uses -qp N (no bitrate). VP9 maps QP -> -crf N -b:v 0, CBR -> min=max=b:v.
- Preset list expanded to ultrafast/superfast/veryfast/faster/fast/medium
(was only ultrafast/superfast); for VP9 the preset maps to cpu-used.
- New QP field in the UI, shown when rate control = QP, otherwise the
bitrate field is shown.
Both /play.mp4 (archive) and /live.mp4 accept rc + qp query params.
All three modes verified producing valid output on the live restream.
VERSION -> 0.0.119.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Система" tab was a catch-all (Recording, Player & transcoding,
Storage, System info, Account). Split out two dedicated tabs:
- "Плеер": per-browser playback/transcode profiles. Kept at operator+
(the transcode option spawns server-side ffmpeg, i.e. CPU load).
- "Учётная запись": current user + logout. Ungated, so any signed-in
user (incl. viewers) can log out -- previously it was buried inside
the operator-only "Система" tab.
.tabs gets flex-wrap so the now-7 tabs wrap instead of overflowing.
VERSION -> 0.0.117.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The built-in WASM player (frontend/static/vendor/hevcjs/hevc-core.js)
rendered washed-out, green/red-shifted video. Two causes, both fixed:
- VideoFrame path (Chrome): the I420 VideoFrame was created without a
colorSpace, so the browser defaulted to BT.601 limited -> hue shift,
and guessed the range -> washout. Set explicit BT.709, fullRange:false.
- WebGL fallback shader: used BT.709 FULL-range coefficients on
limited-range camera output -> washout/desaturation. Switched to
BT.709 limited-range conversion ((Y-16)*255/219, chroma centered 128).
VERSION -> 0.0.116.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>