node-red-contrib-hik-media-buffer 1.1.0 → 1.1.1
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/hik-media-buffer.js +33 -53
- package/package.json +1 -1
package/hik-media-buffer.js
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
const
|
|
1
|
+
const urllib = require('urllib');
|
|
2
2
|
const https = require('https');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const { exec } = require('child_process');
|
|
7
7
|
|
|
8
|
-
// --- STRATEGIA SWITCH: DEFINIZIONE MODALITÀ ---
|
|
9
|
-
// Valori possibili: 'digest', 'basic', 'bearer'
|
|
10
|
-
const AUTH_MODE = 'digest';
|
|
11
|
-
|
|
12
8
|
module.exports = function(RED) {
|
|
13
9
|
function HikMediaBufferNode(config) {
|
|
14
10
|
RED.nodes.createNode(this, config);
|
|
15
11
|
const node = this;
|
|
16
12
|
|
|
17
|
-
// --- ASSEGNAZIONE PROPRIETÀ ---
|
|
18
13
|
node.name = config.name;
|
|
19
14
|
node.host = config.host;
|
|
20
15
|
node.port = config.port || "80";
|
|
@@ -30,7 +25,6 @@ module.exports = function(RED) {
|
|
|
30
25
|
let nvrOnline = true;
|
|
31
26
|
let statoCamera = {};
|
|
32
27
|
|
|
33
|
-
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
34
28
|
const tempDir = os.tmpdir();
|
|
35
29
|
const EventList = ["FieldDetection", "LineDetection"];
|
|
36
30
|
|
|
@@ -38,7 +32,24 @@ module.exports = function(RED) {
|
|
|
38
32
|
|
|
39
33
|
function toHikDate(d) { return d.toISOString().split('.')[0] + "Z"; }
|
|
40
34
|
|
|
41
|
-
// ---
|
|
35
|
+
// --- HELPER AGGIORNATO: Solo Urllib con Digest ---
|
|
36
|
+
async function hikRequest(options) {
|
|
37
|
+
const { method, url, data, responseType, user, pass, headers = {} } = options;
|
|
38
|
+
|
|
39
|
+
const res = await urllib.request(url, {
|
|
40
|
+
method: method,
|
|
41
|
+
digestAuth: `${user}:${pass}`,
|
|
42
|
+
content: data,
|
|
43
|
+
headers: headers,
|
|
44
|
+
dataType: responseType === 'arraybuffer' ? 'buffer' : 'text',
|
|
45
|
+
timeout: 15000,
|
|
46
|
+
rejectUnauthorized: false
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Urllib restituisce i dati in .data
|
|
50
|
+
return res;
|
|
51
|
+
}
|
|
52
|
+
|
|
42
53
|
async function getCameraName(cam) {
|
|
43
54
|
try {
|
|
44
55
|
const res = await hikRequest({
|
|
@@ -47,40 +58,13 @@ module.exports = function(RED) {
|
|
|
47
58
|
user: node.user,
|
|
48
59
|
pass: node.camPass
|
|
49
60
|
});
|
|
50
|
-
const match = res.data.match(/<name>([^<]+)<\/name>/);
|
|
61
|
+
const match = res.data.toString().match(/<name>([^<]+)<\/name>/);
|
|
51
62
|
return match ? match[1] : `Cam_${cam.channel}`;
|
|
52
63
|
} catch (e) {
|
|
53
|
-
return `Camera_${cam.ip}`;
|
|
64
|
+
return `Camera_${cam.ip}`;
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
// --- HELPER UNICO PER LE CHIAMATE (STRATEGIA SWITCH) ---
|
|
58
|
-
async function hikRequest(options) {
|
|
59
|
-
const { method, url, data, responseType, user, pass, headers = {} } = options;
|
|
60
|
-
|
|
61
|
-
const requestConfig = {
|
|
62
|
-
method,
|
|
63
|
-
url,
|
|
64
|
-
data,
|
|
65
|
-
responseType: responseType || 'data',
|
|
66
|
-
httpsAgent: node.protocol === "https" ? httpsAgent : undefined,
|
|
67
|
-
timeout: 10000,
|
|
68
|
-
headers: { ...headers }
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// LOGICA DI AUTENTICAZIONE
|
|
72
|
-
if (AUTH_MODE === 'digest' || AUTH_MODE === 'basic') {
|
|
73
|
-
// Axios gestisce Basic/Digest internamente se configurato così:
|
|
74
|
-
requestConfig.auth = { username: user, password: pass };
|
|
75
|
-
} else if (AUTH_MODE === 'bearer') {
|
|
76
|
-
// Esempio per Token futuro
|
|
77
|
-
requestConfig.headers['Authorization'] = `Bearer ${node.context().get('myToken')}`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return axios(requestConfig);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// --- CONTROLLO ONLINE TELECAMERE ---
|
|
84
68
|
async function checkCameras() {
|
|
85
69
|
if (isClosing) return;
|
|
86
70
|
for (let cam of node.cameras) {
|
|
@@ -123,17 +107,15 @@ module.exports = function(RED) {
|
|
|
123
107
|
|
|
124
108
|
const heartbeatInterval = setInterval(checkCameras, 30000);
|
|
125
109
|
|
|
126
|
-
// --- FUNZIONE DOWNLOAD MEDIA ---
|
|
127
110
|
async function downloadMedia(evento, channelID) {
|
|
128
111
|
const camera = node.cameras.find(c => c.channel == channelID);
|
|
129
|
-
const nomeCamera = await getCameraName(camera);
|
|
130
|
-
|
|
131
112
|
if (!camera) return;
|
|
132
113
|
|
|
133
114
|
const nowTime = Date.now();
|
|
134
115
|
if (nowTime - lastTriggerTime < 10000) return;
|
|
135
116
|
lastTriggerTime = nowTime;
|
|
136
117
|
|
|
118
|
+
const nomeCamera = await getCameraName(camera);
|
|
137
119
|
const referenceTime = new Date();
|
|
138
120
|
const startTime = toHikDate(new Date(referenceTime.getTime() - (10 * 1000)));
|
|
139
121
|
const endTime = toHikDate(new Date(referenceTime.getTime() + (10 * 1000)));
|
|
@@ -145,6 +127,7 @@ module.exports = function(RED) {
|
|
|
145
127
|
let output = { ip: camera.ip, nomeCliente: node.name, nome_telecamera: nomeCamera, channel: channelID, event: evento, videoPath: null, imageBuffer: null };
|
|
146
128
|
|
|
147
129
|
try {
|
|
130
|
+
// RIPRISTINATA LA TUA LOGICA DEI TRACKS
|
|
148
131
|
const tracks = [{ name: "termicoV", id: "201" }, { name: "termico", id: "203" }];
|
|
149
132
|
for (let t of tracks) {
|
|
150
133
|
const searchXml = `<?xml version="1.0" encoding="utf-8"?>
|
|
@@ -165,7 +148,7 @@ module.exports = function(RED) {
|
|
|
165
148
|
pass: node.camPass
|
|
166
149
|
});
|
|
167
150
|
|
|
168
|
-
let xml = resSearch.data.replace(/<(\/?)\w+:/g, "<$1");
|
|
151
|
+
let xml = resSearch.data.toString().replace(/<(\/?)\w+:/g, "<$1");
|
|
169
152
|
const uriMatch = xml.match(/<playbackURI>([^<]+)</);
|
|
170
153
|
|
|
171
154
|
if (uriMatch) {
|
|
@@ -208,26 +191,24 @@ module.exports = function(RED) {
|
|
|
208
191
|
updateNodeStatus();
|
|
209
192
|
}
|
|
210
193
|
|
|
211
|
-
// --- GESTIONE NVR ALERT STREAM ---
|
|
212
194
|
function startAlertStream() {
|
|
213
195
|
if (isClosing) return;
|
|
214
196
|
const url = `${node.protocol}://${node.host}:${node.port}/ISAPI/Event/notification/alertStream`;
|
|
215
197
|
|
|
216
|
-
|
|
198
|
+
urllib.request(url, {
|
|
217
199
|
method: 'GET',
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}).then(
|
|
223
|
-
streamRequest = response;
|
|
200
|
+
digestAuth: `${node.user}:${node.pass}`,
|
|
201
|
+
streaming: true,
|
|
202
|
+
timeout: 60000,
|
|
203
|
+
rejectUnauthorized: false
|
|
204
|
+
}).then(res => {
|
|
224
205
|
if (!nvrOnline) {
|
|
225
206
|
node.send({ payload: { status: "online", ip: node.host, msg: "NVR Online" } });
|
|
226
207
|
nvrOnline = true;
|
|
227
208
|
}
|
|
228
209
|
updateNodeStatus();
|
|
229
210
|
|
|
230
|
-
|
|
211
|
+
res.res.on('data', (chunk) => {
|
|
231
212
|
const data = chunk.toString().toLowerCase();
|
|
232
213
|
if (data.includes("active")) {
|
|
233
214
|
const chMatch = data.match(/<channelid>(\d+)<\/channelid>/i);
|
|
@@ -239,8 +220,8 @@ module.exports = function(RED) {
|
|
|
239
220
|
}
|
|
240
221
|
});
|
|
241
222
|
|
|
242
|
-
|
|
243
|
-
|
|
223
|
+
res.res.on('error', () => handleNvrError());
|
|
224
|
+
res.res.on('end', () => !isClosing && setTimeout(startAlertStream, 5000));
|
|
244
225
|
}).catch(() => handleNvrError());
|
|
245
226
|
}
|
|
246
227
|
|
|
@@ -259,7 +240,6 @@ module.exports = function(RED) {
|
|
|
259
240
|
node.on('close', (done) => {
|
|
260
241
|
isClosing = true;
|
|
261
242
|
clearInterval(heartbeatInterval);
|
|
262
|
-
if (streamRequest && streamRequest.data) streamRequest.data.destroy();
|
|
263
243
|
done();
|
|
264
244
|
});
|
|
265
245
|
}
|