iobroker.agent-dvr 0.0.6 → 0.2.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/README.de.md +289 -175
- package/README.md +231 -96
- package/admin/assets/index-ulvdM37k.js +685 -0
- package/admin/i18n/de.json +131 -135
- package/admin/i18n/en.json +131 -135
- package/admin/i18n/es.json +131 -135
- package/admin/i18n/fr.json +131 -135
- package/admin/i18n/it.json +131 -135
- package/admin/i18n/nl.json +131 -135
- package/admin/i18n/pl.json +131 -135
- package/admin/i18n/pt.json +131 -135
- package/admin/i18n/ru.json +131 -135
- package/admin/i18n/uk.json +131 -135
- package/admin/i18n/zh-cn.json +131 -135
- package/admin/index_m.html +27 -0
- package/build/main.js +152 -103
- package/build/main.js.map +2 -2
- package/io-package.json +253 -241
- package/lib/web.js +52 -5
- package/package.json +1 -1
- package/www/flv.min.js +7534 -0
- package/www/index.html +737 -120
- package/admin/custom/go2rtcMapping.js +0 -169
- package/admin/jsonConfig.json +0 -818
package/lib/web.js
CHANGED
|
@@ -42,13 +42,15 @@ class AgentDvrWeb {
|
|
|
42
42
|
adapter.getForeignStatesAsync(ns + '.cam_*.status.recording'),
|
|
43
43
|
adapter.getForeignStatesAsync(ns + '.cam_*.status.detected'),
|
|
44
44
|
adapter.getForeignStatesAsync(ns + '.cam_*.status.alerted'),
|
|
45
|
-
|
|
45
|
+
adapter.getForeignStatesAsync(ns + '.cam_*.data.width'),
|
|
46
|
+
adapter.getForeignStatesAsync(ns + '.cam_*.data.height'),
|
|
47
|
+
]).then(([nameStates, mjpegStates, snapStates, onlineStates, ptzStates, recStates, detStates, alertStates, widthStates, heightStates]) => {
|
|
46
48
|
const cameras = {};
|
|
47
49
|
|
|
48
50
|
Object.entries(nameStates || {}).forEach(([id, state]) => {
|
|
49
51
|
const rel = id.slice(ns.length + 1);
|
|
50
52
|
const camKey = rel.split('.')[0];
|
|
51
|
-
const m = camKey.match(/^cam_(\d+)
|
|
53
|
+
const m = camKey.match(/^cam_(\d+)/);
|
|
52
54
|
cameras[camKey] = {
|
|
53
55
|
name: (state && state.val != null) ? String(state.val) : camKey,
|
|
54
56
|
oid: m ? m[1] : null,
|
|
@@ -106,6 +108,23 @@ class AgentDvrWeb {
|
|
|
106
108
|
});
|
|
107
109
|
}
|
|
108
110
|
|
|
111
|
+
// Build aspect from data.width / data.height states written by flattenWrite.
|
|
112
|
+
// Reading from ioBroker states is reliable regardless of whether the web
|
|
113
|
+
// extension runs in the same process as the main adapter.
|
|
114
|
+
const wMap = {}, hMap = {};
|
|
115
|
+
Object.entries(widthStates || {}).forEach(([id, s]) => {
|
|
116
|
+
const k = id.slice(ns.length + 1).split('.')[0];
|
|
117
|
+
if (s && typeof s.val === 'number') wMap[k] = s.val;
|
|
118
|
+
});
|
|
119
|
+
Object.entries(heightStates || {}).forEach(([id, s]) => {
|
|
120
|
+
const k = id.slice(ns.length + 1).split('.')[0];
|
|
121
|
+
if (s && typeof s.val === 'number') hMap[k] = s.val;
|
|
122
|
+
});
|
|
123
|
+
Object.entries(cameras).forEach(([k, cam]) => {
|
|
124
|
+
const w = wMap[k], h = hMap[k];
|
|
125
|
+
cam.aspect = (w >= 100 && h >= 100 && w / h >= 0.3 && w / h <= 4) ? w + '/' + h : '';
|
|
126
|
+
});
|
|
127
|
+
|
|
109
128
|
res.setHeader('Content-Type', 'application/json');
|
|
110
129
|
res.end(JSON.stringify({ instance: ns, base, cameras }));
|
|
111
130
|
}).catch(err => {
|
|
@@ -132,15 +151,41 @@ class AgentDvrWeb {
|
|
|
132
151
|
dashColorOnline: native.dashColorOnline || '#22c55e',
|
|
133
152
|
dashColorOffline: native.dashColorOffline || '#ef4444',
|
|
134
153
|
dashTagPosition: native.dashTagPosition || 'top-right',
|
|
135
|
-
|
|
154
|
+
dashStreamType: native.dashStreamType || 'mjpeg',
|
|
136
155
|
go2rtcUrl: native.go2rtcUrl || '',
|
|
137
156
|
go2rtcMapping: Array.isArray(native.go2rtcMapping) ? native.go2rtcMapping : [],
|
|
157
|
+
cameraStreams: (native.cameraStreams && typeof native.cameraStreams === 'object') ? native.cameraStreams : {},
|
|
158
|
+
dashLiveSource: native.dashLiveSource || 'agentdvr',
|
|
138
159
|
}));
|
|
139
160
|
});
|
|
140
161
|
|
|
162
|
+
// ── FLV stream proxy (avoids browser CORS block for flv.js XHR) ────────
|
|
163
|
+
app.get('/' + ROUTE + '/api/stream', (req, res) => {
|
|
164
|
+
const base = (native.ip && native.port) ? 'http://' + native.ip + ':' + native.port : '';
|
|
165
|
+
if (!base) { res.status(503).end('AgentDVR not configured'); return; }
|
|
166
|
+
const oid = String(req.query.oid || '');
|
|
167
|
+
if (!oid || !/^\d+$/.test(oid)) { res.status(400).end('invalid oid'); return; }
|
|
168
|
+
const size = String(req.query.size || '');
|
|
169
|
+
const sizeStr = /^\d{1,5}x\d{1,5}$/.test(size) ? '&size=' + size : '';
|
|
170
|
+
const url = base + '/video.mp4?oid=' + oid + sizeStr;
|
|
171
|
+
let target;
|
|
172
|
+
try { target = new URL(url); } catch (_) { res.status(500).end(); return; }
|
|
173
|
+
const mod = target.protocol === 'https:' ? https : http;
|
|
174
|
+
const upReq = mod.get(url, upstream => {
|
|
175
|
+
res.setHeader('Content-Type', 'video/x-flv');
|
|
176
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
177
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
178
|
+
upstream.pipe(res);
|
|
179
|
+
upstream.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
|
|
180
|
+
});
|
|
181
|
+
upReq.setTimeout(8000, () => upReq.destroy());
|
|
182
|
+
upReq.on('error', () => { if (!res.headersSent) res.status(502).end(); else res.end(); });
|
|
183
|
+
req.on('close', () => { try { upReq.destroy(); } catch (_) {} });
|
|
184
|
+
});
|
|
185
|
+
|
|
141
186
|
// ── go2rtc streams proxy ─────────────────────────────────────────────
|
|
142
187
|
app.get('/' + ROUTE + '/api/go2rtc/streams', (req, res) => {
|
|
143
|
-
if (!native.
|
|
188
|
+
if (!native.go2rtcUrl) {
|
|
144
189
|
res.setHeader('Content-Type', 'application/json');
|
|
145
190
|
res.end('{"streams":[]}');
|
|
146
191
|
return;
|
|
@@ -235,6 +280,8 @@ class AgentDvrWeb {
|
|
|
235
280
|
if (err) { res.status(404).end('Not found'); return; }
|
|
236
281
|
const ext = path.extname(file).toLowerCase();
|
|
237
282
|
res.setHeader('Content-Type', MIME[ext] || 'application/octet-stream');
|
|
283
|
+
// HTML must never be cached so updates are visible immediately
|
|
284
|
+
if (ext === '.html') res.setHeader('Cache-Control', 'no-store');
|
|
238
285
|
res.end(data);
|
|
239
286
|
});
|
|
240
287
|
});
|
|
@@ -244,7 +291,7 @@ class AgentDvrWeb {
|
|
|
244
291
|
// ── WebSocket proxy for go2rtc (bypasses browser cross-origin block) ─
|
|
245
292
|
server.on('upgrade', (req, socket, head) => {
|
|
246
293
|
if (!(req.url || '').startsWith('/' + ROUTE + '/api/ws')) return;
|
|
247
|
-
if (!native.
|
|
294
|
+
if (!native.go2rtcUrl) {
|
|
248
295
|
socket.write('HTTP/1.1 503 Service Unavailable\r\n\r\n');
|
|
249
296
|
socket.destroy();
|
|
250
297
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.agent-dvr",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.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",
|