iobroker.agent-dvr 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "agent-dvr",
4
- "version": "0.2.2",
4
+ "version": "0.4.0",
5
5
  "news": {
6
+ "0.4.0": {
7
+ "en": "Optional MJPEG and snapshot stream proxy through ioBroker (browser needs only one connection)",
8
+ "de": "Optionaler MJPEG- und Snapshot-Stream-Proxy über ioBroker (Browser benötigt nur eine Verbindung)",
9
+ "ru": "Опциональный прокси потоков MJPEG и снимков через ioBroker",
10
+ "pt": "Proxy opcional de streams MJPEG e snapshots pelo ioBroker",
11
+ "nl": "Optionele MJPEG- en snapshot-proxy via ioBroker",
12
+ "fr": "Proxy optionnel pour les flux MJPEG et snapshots via ioBroker",
13
+ "it": "Proxy opzionale per stream MJPEG e snapshot tramite ioBroker",
14
+ "es": "Proxy opcional de flujos MJPEG e instantáneas a través de ioBroker",
15
+ "pl": "Opcjonalny proxy strumieni MJPEG i migawek przez ioBroker",
16
+ "zh-cn": "可选的 MJPEG 和快照流代理(通过 ioBroker)",
17
+ "uk": "Опціональний проксі потоків MJPEG і знімків через ioBroker"
18
+ },
19
+ "0.3.0": {
20
+ "en": "New control buttons (scheduleOn/Off, detectorOn/Off, sensitivityMin/Max/Gain), audio URL states for microphones, native browser fullscreen in dashboard, collapsible tag filter, auto-hide header",
21
+ "de": "Neue Steuertasten (scheduleOn/Off, detectorOn/Off, Empfindlichkeit), Audio-URL-Zustände für Mikrofone, nativer Browser-Vollbild im Dashboard, ausklappbarer Tag-Filter, Auto-Hide-Header",
22
+ "ru": "Новые кнопки управления (расписание, детектор, чувствительность), URL-состояния для микрофонов, нативный полноэкранный режим, сворачиваемый фильтр меток",
23
+ "pt": "Novos botões de controlo (schedule, detector, sensibilidade), URLs de áudio para microfones, tela cheia nativa, filtro de etiquetas recolhível",
24
+ "nl": "Nieuwe besturingsknoppen (schema, detector, gevoeligheid), audio-URL-staten voor microfoons, native browserfullscreen, inklapbaar labelfilter",
25
+ "fr": "Nouveaux boutons de commande (planification, détecteur, sensibilité), états URL audio pour microphones, plein écran natif, filtre d'étiquettes rétractable",
26
+ "it": "Nuovi pulsanti di controllo (pianificazione, rilevatore, sensibilità), stati URL audio per microfoni, schermo intero nativo, filtro etichette comprimibile",
27
+ "es": "Nuevos botones de control (programación, detector, sensibilidad), estados de URL de audio para micrófonos, pantalla completa nativa, filtro de etiquetas plegable",
28
+ "pl": "Nowe przyciski sterowania (harmonogram, detektor, czułość), stany URL audio dla mikrofonów, natywny pełny ekran, zwijany filtr etykiet",
29
+ "zh-cn": "新增控制按钮(计划、检测器、灵敏度),麦克风音频URL状态,本地浏览器全屏,可折叠标签过滤器",
30
+ "uk": "Нові кнопки керування (розклад, детектор, чутливість), стани URL аудіо для мікрофонів, нативний повноекранний режим, згортуваний фільтр міток"
31
+ },
6
32
  "0.2.2": {
7
33
  "en": "fix: translate remaining German strings in DashboardPanel to English via I18n.t()\nfix: add i18n keys loadingCamerasAndStreams, cfgCameraColumn, cfgStreamSourceColumn, reload in all 11 languages\nchore: replace POSIX mv with cross-platform node rename in src-admin build script",
8
34
  "de": "Behebung: Die verbleibenden deutschen Zeichenketten im DashboardPanel werden mithilfe von I18n.t() ins Englische übersetzt.\nBehebung: Hinzufügen der i18n-Schlüssel loadingCamerasAndStreams, cfgCameraColumn, cfgStreamSourceColumn und reload in allen 11 Sprachen\nAufgabe: Ersetzen Sie POSIX mv durch plattformübergreifendes Node-Rename im src-admin-Build-Skript.",
@@ -67,32 +93,6 @@
67
93
  "pl": "dodano dokumentację Live Dashboard i go2rtc WebRTC do README",
68
94
  "uk": "додано документацію Live Dashboard і go2rtc WebRTC до README",
69
95
  "zh-cn": "README 中新增 Live Dashboard 和 go2rtc WebRTC 文档"
70
- },
71
- "0.0.5": {
72
- "en": "go2rtc WebRTC integration (admin table mapping, ioBroker WS proxy); auto-delete stale cameras; fix recording prev/next order",
73
- "de": "go2rtc WebRTC-Integration (Admin-Tabellen-Mapping, ioBroker WS-Proxy); gelöschte Kameras werden automatisch entfernt; Vor/Zurück-Reihenfolge bei Aufnahmen korrigiert",
74
- "ru": "интеграция go2rtc WebRTC (таблица в admin, WS-прокси через ioBroker); автоудаление устаревших камер; исправлен порядок кнопок Пред/След для записей",
75
- "pt": "integração go2rtc WebRTC (tabela no admin, proxy WS via ioBroker); remoção automática de câmeras obsoletas; ordem anterior/próximo nas gravações corrigida",
76
- "nl": "go2rtc WebRTC-integratie (admintabel, ioBroker WS-proxy); automatisch verwijderen van verouderde camera's; volgorde vorige/volgende opnames gecorrigeerd",
77
- "fr": "intégration go2rtc WebRTC (table admin, proxy WS ioBroker); suppression automatique des caméras obsolètes; ordre précédent/suivant des enregistrements corrigé",
78
- "it": "integrazione go2rtc WebRTC (tabella admin, proxy WS ioBroker); eliminazione automatica telecamere obsolete; ordine precedente/successivo registrazioni corretto",
79
- "es": "integración go2rtc WebRTC (tabla en admin, proxy WS ioBroker); eliminación automática de cámaras obsoletas; orden anterior/siguiente en grabaciones corregido",
80
- "pl": "integracja go2rtc WebRTC (tabela w admin, proxy WS ioBroker); automatyczne usuwanie nieaktywnych kamer; poprawiona kolejność poprzedni/następny nagrań",
81
- "uk": "інтеграція go2rtc WebRTC (таблиця в admin, WS-проксі через ioBroker); автовидалення застарілих камер; виправлено порядок кнопок Попередній/Наступний для записів",
82
- "zh-cn": "go2rtc WebRTC 集成(管理表格映射,ioBroker WS 代理);自动删除过时摄像头;修正录像上一个/下一个顺序"
83
- },
84
- "0.0.4": {
85
- "en": "fix DP roles: snapshot_b64 role state, profile selector role level",
86
- "de": "DP-Rollen korrigiert: snapshot_b64 role state, Profil-Selektor role level",
87
- "ru": "исправлены роли DP: snapshot_b64 role state, селектор профиля role level",
88
- "pt": "corrigir roles DP: snapshot_b64 role state, seletor de perfil role level",
89
- "nl": "DP-rollen gecorrigeerd: snapshot_b64 role state, profielkiezer role level",
90
- "fr": "correction des rôles DP: snapshot_b64 role state, sélecteur de profil role level",
91
- "it": "corretto ruoli DP: snapshot_b64 role state, selettore profilo role level",
92
- "es": "corregir roles DP: snapshot_b64 role state, selector de perfil role level",
93
- "pl": "poprawiono role DP: snapshot_b64 role state, selektor profilu role level",
94
- "uk": "виправлено ролі DP: snapshot_b64 role state, вибір профілю role level",
95
- "zh-cn": "修正 DP 角色:snapshot_b64 角色 state,配置文件选择器角色 level"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/lib/web.js CHANGED
@@ -28,6 +28,11 @@ class AgentDvrWeb {
28
28
  const ns = instanceSettings ? instanceSettings._id.replace('system.adapter.', '') : 'agent-dvr.0';
29
29
  const native = (instanceSettings && instanceSettings.native) ? instanceSettings.native : {};
30
30
 
31
+ // Cache auth header for proxy endpoints
32
+ this._authHeader = (native.user || native.pass)
33
+ ? 'Basic ' + Buffer.from((native.user || '') + ':' + (native.pass || '')).toString('base64')
34
+ : null;
35
+
31
36
  // ── REST API ─────────────────────────────────────────────────────────
32
37
  // Must be registered before the static file handler (Express matches in order)
33
38
  app.get('/' + ROUTE + '/api/cameras', (req, res) => {
@@ -108,6 +113,16 @@ class AgentDvrWeb {
108
113
  });
109
114
  }
110
115
 
116
+ // Replace stream URLs with proxy URLs when enableStreamProxy is on
117
+ if (native.enableStreamProxy) {
118
+ Object.values(cameras).forEach(cam => {
119
+ if (cam.oid) {
120
+ cam.mjpegUrl = '/' + ROUTE + '/api/mjpeg?oid=' + cam.oid;
121
+ cam.snapshotUrl = '/' + ROUTE + '/api/snap?oid=' + cam.oid;
122
+ }
123
+ });
124
+ }
125
+
111
126
  // Build aspect from data.width / data.height states written by flattenWrite.
112
127
  // Reading from ioBroker states is reliable regardless of whether the web
113
128
  // extension runs in the same process as the main adapter.
@@ -159,6 +174,53 @@ class AgentDvrWeb {
159
174
  }));
160
175
  });
161
176
 
177
+ // ── MJPEG stream proxy (optional: enableStreamProxy) ─────────────────
178
+ app.get('/' + ROUTE + '/api/mjpeg', (req, res) => {
179
+ const base = (native.ip && native.port) ? 'http://' + native.ip + ':' + native.port : '';
180
+ if (!base) { res.status(503).end('AgentDVR not configured'); return; }
181
+ const oid = String(req.query.oid || '');
182
+ if (!oid || !/^\d+$/.test(oid)) { res.status(400).end('invalid oid'); return; }
183
+ const url = base + '/video.mjpg?oid=' + oid;
184
+ let target;
185
+ try { target = new URL(url); } catch (_) { res.status(500).end(); return; }
186
+ const mod = target.protocol === 'https:' ? https : http;
187
+ const headers = {};
188
+ if (this._authHeader) headers['Authorization'] = this._authHeader;
189
+ const upReq = mod.get({ hostname: target.hostname, port: target.port, path: target.pathname + target.search, headers }, upstream => {
190
+ res.setHeader('Content-Type', upstream.headers['content-type'] || 'multipart/x-mixed-replace');
191
+ res.setHeader('Cache-Control', 'no-cache');
192
+ res.setHeader('X-Accel-Buffering', 'no');
193
+ upstream.pipe(res);
194
+ upstream.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
195
+ });
196
+ upReq.setTimeout(0); // no timeout for infinite MJPEG stream
197
+ upReq.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
198
+ req.on('close', () => { try { upReq.destroy(); } catch (_) {} });
199
+ });
200
+
201
+ // ── Snapshot proxy (optional: enableStreamProxy) ──────────────────────
202
+ app.get('/' + ROUTE + '/api/snap', (req, res) => {
203
+ const base = (native.ip && native.port) ? 'http://' + native.ip + ':' + native.port : '';
204
+ if (!base) { res.status(503).end('AgentDVR not configured'); return; }
205
+ const oid = String(req.query.oid || '');
206
+ if (!oid || !/^\d+$/.test(oid)) { res.status(400).end('invalid oid'); return; }
207
+ const url = base + '/grab.jpg?oid=' + oid;
208
+ let target;
209
+ try { target = new URL(url); } catch (_) { res.status(500).end(); return; }
210
+ const mod = target.protocol === 'https:' ? https : http;
211
+ const headers = {};
212
+ if (this._authHeader) headers['Authorization'] = this._authHeader;
213
+ const upReq = mod.get({ hostname: target.hostname, port: target.port, path: target.pathname + target.search, headers }, upstream => {
214
+ res.setHeader('Content-Type', upstream.headers['content-type'] || 'image/jpeg');
215
+ res.setHeader('Cache-Control', 'no-cache');
216
+ upstream.pipe(res);
217
+ upstream.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
218
+ });
219
+ upReq.setTimeout(8000, () => upReq.destroy());
220
+ upReq.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
221
+ req.on('close', () => { try { upReq.destroy(); } catch (_) {} });
222
+ });
223
+
162
224
  // ── FLV stream proxy (avoids browser CORS block for flv.js XHR) ────────
163
225
  app.get('/' + ROUTE + '/api/stream', (req, res) => {
164
226
  const base = (native.ip && native.port) ? 'http://' + native.ip + ':' + native.port : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.agent-dvr",
3
- "version": "0.2.2",
3
+ "version": "0.4.0",
4
4
  "description": "Connects ioBroker to AgentDVR: auto-discovers cameras, mirrors all device values as data points, delivers real-time triggers on new recordings, and generates a responsive HTML gallery with optional search and tag filtering.",
5
5
  "author": {
6
6
  "name": "ipod86",