diff --git a/VERSION b/VERSION
index 1ad6b81..9ab8fdd 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.0.131
+0.0.132
diff --git a/frontend/static/app.js b/frontend/static/app.js
index ae55709..d7a9bf1 100644
--- a/frontend/static/app.js
+++ b/frontend/static/app.js
@@ -106,6 +106,21 @@ function fmtUptime(s) {
const STATE_RU = { online: "запись", retrying: "переподключение", offline: "выключена", starting: "запуск" };
function dotClass(state) { return "dot " + (state || "offline"); }
+// Канал Hikvision (1-32) ↔ пути RTSP: канал N → осн. /Streaming/Channels/{N*100+1},
+// суб /Streaming/Channels/{N*100+2} (101/102 = канал 1, 1101/1102 = канал 11, 3201/3202 = 32).
+function channelFromPath(p) {
+ const m = /(\d+)\s*$/.exec(p || "");
+ const ch = m ? Math.floor(parseInt(m[1], 10) / 100) : 1;
+ return ch >= 1 ? ch : 1;
+}
+function mainPathForChannel(ch) { return `/Streaming/Channels/${ch * 100 + 1}`; }
+function subPathForChannel(ch) { return `/Streaming/Channels/${ch * 100 + 2}`; }
+function channelOptions(sel) {
+ let h = "";
+ for (let i = 1; i <= 32; i++) h += ``;
+ return h;
+}
+
// ── шапка: часы + статистика + выход ────────────────────────────
function startClock() {
const el = document.getElementById("clock");
@@ -1204,10 +1219,7 @@ const CamSettings = {
`
${slotNo} | ` +
` | ` +
` | ` +
- `` +
- `` +
- `` +
- ` | ` +
+ ` | ` +
` | ` +
` | ` +
` | ` +
@@ -1239,8 +1251,8 @@ const CamSettings = {
ip: get("ip").trim(),
user: get("user").trim(),
password: get("password"),
- main_path: get("main_path").trim(),
- sub_path: get("sub_path").trim(),
+ main_path: mainPathForChannel(+get("channel") || 1),
+ sub_path: subPathForChannel(+get("channel") || 1),
storage_id: get("storage_id"),
enabled: tr.querySelector('[data-f="enabled"]').checked,
record: tr.querySelector('[data-f="record"]').checked,
@@ -1281,8 +1293,7 @@ const CamSettings = {
`` +
`` +
`` +
- `` +
- `` +
+ `` +
`` +
`` +
`` +
@@ -1325,8 +1336,8 @@ const CamSettings = {
ip: val("e-ip").trim(),
user: val("e-user").trim(),
password: val("e-pass"),
- main_path: val("e-main").trim(),
- sub_path: val("e-sub").trim(),
+ main_path: mainPathForChannel(+val("e-channel") || 1),
+ sub_path: subPathForChannel(+val("e-channel") || 1),
storage_id: val("e-storage"),
enabled: document.getElementById("e-enabled").checked,
record: document.getElementById("e-record").checked,
@@ -1380,7 +1391,7 @@ const CamSettings = {
const tr = document.createElement("tr");
if (c) {
const st = (c.status && c.status.state) || "offline";
- const streams = "осн. 101" + (c.has_sub ? " + суб. 102" : "");
+ const streams = "канал " + channelFromPath(c.main_path);
const statusCell = c.enabled
? `${STATE_RU[st] || st}`
: `выключена`;
diff --git a/frontend/static/style.css b/frontend/static/style.css
index 28730ec..26a8d43 100644
--- a/frontend/static/style.css
+++ b/frontend/static/style.css
@@ -437,8 +437,8 @@ button.primary { background: var(--accent); border-color: #c0392b; color: #fff;
button.danger { border-color: #c0392b; color: #e74c3c; }
button.danger:hover { background: #c0392b; border-color: #c0392b; color: #fff; }
table.cams td.actions button { padding: 2px 8px; font-size: 12px; }
-/* инлайн-выбор хранилища в строке — компактный, не раздувает высоту строки (как тумблеры) */
-table.cams td select.stor-sel {
+/* инлайн-селекты в строке (хранилище, канал) — компактные, не раздувают высоту строки */
+table.cams td select.stor-sel, table.cams td select.cell-sel {
height: 22px; padding: 1px 4px; font-size: 12px; line-height: 1;
border-radius: 5px; width: 100%; max-width: 150px; box-sizing: border-box;
vertical-align: middle;