node-red-contrib-hik-media-buffer 1.1.117 → 1.1.119
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 +28 -30
- package/package.json +1 -1
package/hik-media-buffer.js
CHANGED
|
@@ -27,28 +27,29 @@ module.exports = function(RED) {
|
|
|
27
27
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
28
28
|
const EventList = ["FieldDetection", "LineDetection"];
|
|
29
29
|
|
|
30
|
-
// CARTELLA TEMPORANEA
|
|
31
30
|
const baseStorage = path.join(os.tmpdir(), "hik_temp_media");
|
|
32
31
|
if (!fs.existsSync(baseStorage)) fs.mkdirSync(baseStorage, { recursive: true });
|
|
33
32
|
|
|
34
33
|
node.status({fill:"grey", shape:"ring", text:"Inizializzazione..."});
|
|
35
34
|
|
|
36
|
-
function
|
|
35
|
+
function toHikSearchDate(d) {
|
|
36
|
+
return d.toISOString().split('.')[0] + "Z";
|
|
37
|
+
}
|
|
37
38
|
|
|
38
|
-
// --- PRENDE IL NOME REALE DELLA TELECAMERA
|
|
39
|
+
// --- 1. PRENDE IL NOME REALE DELLA TELECAMERA DAL DIGITAL PROXY ---
|
|
39
40
|
async function getCameraNameFromNVR(channelID) {
|
|
40
41
|
const nvrAuth = new AxiosDigestAuth({ username: node.user, password: node.pass });
|
|
41
42
|
try {
|
|
43
|
+
// In accordo con la pag. 58 della documentazione, interroghiamo l'InputProxy del canale digitale
|
|
42
44
|
const res = await nvrAuth.request({
|
|
43
45
|
method: 'GET',
|
|
44
|
-
url: `${node.protocol}://${node.host}:${node.port}/ISAPI/
|
|
46
|
+
url: `${node.protocol}://${node.host}:${node.port}/ISAPI/ContentMgmt/InputProxy/channels/${channelID}`,
|
|
45
47
|
timeout: 5000,
|
|
46
48
|
httpsAgent: node.protocol === "https" ? httpsAgent : undefined
|
|
47
49
|
});
|
|
48
|
-
|
|
49
50
|
const data = res.data.toString();
|
|
50
|
-
//
|
|
51
|
-
const match = data.match(/<
|
|
51
|
+
// Estraiamo il tag <name> nativo del canale digitale
|
|
52
|
+
const match = data.match(/<name>([^<]+)<\/name>/i);
|
|
52
53
|
if (match && match[1]) return match[1].trim();
|
|
53
54
|
return `Canale ${channelID}`;
|
|
54
55
|
} catch (e) {
|
|
@@ -56,45 +57,47 @@ module.exports = function(RED) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
// --- CONTROLLO STATUS CAMERE
|
|
60
|
+
// --- 2. CONTROLLO STATUS CAMERE AUTOMATICO ---
|
|
60
61
|
async function checkCameras() {
|
|
61
62
|
if (isClosing || !nvrOnline) return;
|
|
62
63
|
const nvrAuth = new AxiosDigestAuth({ username: node.user, password: node.pass });
|
|
63
64
|
try {
|
|
65
|
+
// Interroghiamo l'elenco proxy centralizzato dell'NVR
|
|
64
66
|
const res = await nvrAuth.request({
|
|
65
67
|
method: 'GET',
|
|
66
|
-
url: `${node.protocol}://${node.host}:${node.port}/ISAPI/
|
|
67
|
-
timeout:
|
|
68
|
+
url: `${node.protocol}://${node.host}:${node.port}/ISAPI/ContentMgmt/InputProxy/channels`,
|
|
69
|
+
timeout: 6000,
|
|
68
70
|
httpsAgent: node.protocol === "https" ? httpsAgent : undefined
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
const xml = res.data.toString();
|
|
72
|
-
|
|
74
|
+
// Estraiamo i singoli blocchi di canali digitali
|
|
75
|
+
const channelBlocks = xml.match(/<InputProxyChannel>[\s\S]*?<\/InputProxyChannel>/g) || [];
|
|
73
76
|
|
|
74
77
|
for (let block of channelBlocks) {
|
|
75
78
|
const idMatch = block.match(/<id>(\d+)<\/id>/i);
|
|
76
|
-
//
|
|
77
|
-
const onlineMatch = block.match(/<
|
|
79
|
+
// Questa regex cerca la parola "true" o "false" dentro il tag online, sia esso <online>, <onLine> o <ONLINE>
|
|
80
|
+
const onlineMatch = block.match(/<onLine[^>]*>(true|false)<\/onLine>/i) || block.match(/<online[^>]*>(true|false)<\/online>/i);
|
|
78
81
|
|
|
79
82
|
if (idMatch) {
|
|
80
83
|
const ch = idMatch[1];
|
|
81
|
-
|
|
82
|
-
const isOnline = onlineMatch ? onlineMatch[1].trim().toLowerCase() === "true" : true;
|
|
84
|
+
const isOnline = onlineMatch ? onlineMatch[1].toLowerCase() === "true" : false;
|
|
83
85
|
|
|
84
86
|
if (isOnline && statoCamera[ch] === false) {
|
|
85
87
|
const nomeOnline = await getCameraNameFromNVR(ch);
|
|
86
88
|
node.send({ payload: { tipo_messaggio: "status", stato_telecamera: "online", nome_cliente: node.name, nome_telecamera: nomeOnline, ip_telecamera: node.host, channel: ch, msg: "Camera ripristinata" } });
|
|
87
89
|
statoCamera[ch] = true;
|
|
88
|
-
} else if (!isOnline && statoCamera[ch] !== false) {
|
|
90
|
+
} else if (!isOnline && statoCamera[ch] !== false && statoCamera[ch] !== undefined) {
|
|
89
91
|
node.send({ payload: { tipo_messaggio: "status", stato_telecamera: "offline", nome_cliente: node.name, nome_telecamera: `Camera ${ch}`, ip_telecamera: node.host, channel: ch, msg: "Camera non raggiungibile" } });
|
|
90
92
|
statoCamera[ch] = false;
|
|
91
93
|
} else if (statoCamera[ch] === undefined) {
|
|
94
|
+
// Configurazione iniziale dello stato al boot
|
|
92
95
|
statoCamera[ch] = isOnline;
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
} catch (e) {
|
|
97
|
-
node.error(`Errore
|
|
100
|
+
node.error(`Errore nel check diagnostico: ${e.message}`);
|
|
98
101
|
}
|
|
99
102
|
updateNodeStatus();
|
|
100
103
|
}
|
|
@@ -110,20 +113,18 @@ module.exports = function(RED) {
|
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
const heartbeatInterval = setInterval(checkCameras,
|
|
116
|
+
const heartbeatInterval = setInterval(checkCameras, 15000);
|
|
114
117
|
|
|
115
|
-
// ---
|
|
116
|
-
function toHikSearchDate(d) {
|
|
117
|
-
return d.toISOString().split('.')[0] + "Z";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// --- DOWNLOAD CON ORARIO DINAMICO STRETTO SULL'ULTIMO EVENTO ---
|
|
118
|
+
// --- 3. DOWNLOAD MEDIA CON DELAY E FORMATO STRINGA CORRETTO ---
|
|
121
119
|
async function downloadMedia(evento, channelID) {
|
|
122
120
|
const nowTime = Date.now();
|
|
123
121
|
if (!lastTriggerTime[channelID]) lastTriggerTime[channelID] = 0;
|
|
124
122
|
if (nowTime - lastTriggerTime[channelID] < 5000) return;
|
|
125
123
|
lastTriggerTime[channelID] = nowTime;
|
|
126
124
|
|
|
125
|
+
node.status({fill:"yellow", shape:"dot", text:`Attesa scrittura NVR (6s)...`});
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, 6000));
|
|
127
|
+
|
|
127
128
|
const nvrAuth = new AxiosDigestAuth({ username: node.user, password: node.pass });
|
|
128
129
|
const referenceTime = new Date();
|
|
129
130
|
const timestamp = Math.floor(referenceTime.getTime() / 1000);
|
|
@@ -132,7 +133,6 @@ module.exports = function(RED) {
|
|
|
132
133
|
const nomeCamera = await getCameraNameFromNVR(channelID);
|
|
133
134
|
node.status({fill:"yellow", shape:"dot", text:`Download Cam ${channelID}...`});
|
|
134
135
|
|
|
135
|
-
// FINESTRA TEMPORALE REALE: Cerchiamo l'evento da 2 minuti fa a 1 minuto nel futuro
|
|
136
136
|
const inizioFinestra = new Date(referenceTime.getTime() - (2 * 60 * 1000));
|
|
137
137
|
const fineFinestra = new Date(referenceTime.getTime() + (1 * 60 * 1000));
|
|
138
138
|
|
|
@@ -155,12 +155,12 @@ module.exports = function(RED) {
|
|
|
155
155
|
let fileDaCancellare = [];
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
|
-
// 1. 📸
|
|
158
|
+
// 1. 📸 SCARICAMENTO IMMAGINE
|
|
159
159
|
const payloadFoto = {
|
|
160
160
|
"EventSearchDescription": {
|
|
161
161
|
"searchID": "C5AFEE35-B1E0-4C01-83F8-47FD77892E4A",
|
|
162
162
|
"searchResultPosition": 0,
|
|
163
|
-
"maxResults": 1,
|
|
163
|
+
"maxResults": 1,
|
|
164
164
|
"timeSpanList": [{ "startTime": startSearchStr, "endTime": endSearchStr }],
|
|
165
165
|
"type": "all",
|
|
166
166
|
"channels": [parseInt(channelID)],
|
|
@@ -193,7 +193,7 @@ module.exports = function(RED) {
|
|
|
193
193
|
fileDaCancellare.push(fullImgPath);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
// 2. 🎥
|
|
196
|
+
// 2. 🎥 SCARICAMENTO VIDEO
|
|
197
197
|
const payloadVideoSearch = `<?xml version="1.0" encoding="utf-8"?>
|
|
198
198
|
<CMSearchDescription>
|
|
199
199
|
<searchID>CEC13F0F-1AEA-4474-A2DA-CFFF332C7C5B</searchID>
|
|
@@ -221,10 +221,8 @@ module.exports = function(RED) {
|
|
|
221
221
|
const uriMatch = xmlString.match(/<playbackURI>([\s\S]*?)<\/playbackURI>/i);
|
|
222
222
|
|
|
223
223
|
if (uriMatch && uriMatch[1]) {
|
|
224
|
-
// Estrae il playbackURI grezzo che contiene GIÀ la data compressa senza trattini generata dall'NVR
|
|
225
224
|
const playbackURIGrezzo = uriMatch[1].trim();
|
|
226
225
|
|
|
227
|
-
// Inviamo il downloadRequest passando l'URI nativo fornito dall'NVR (che rispetta il formato compresso)
|
|
228
226
|
const payloadDownload = `<?xml version="1.0" encoding="UTF-8"?>
|
|
229
227
|
<downloadRequest>
|
|
230
228
|
<playbackURI>${playbackURIGrezzo}</playbackURI>
|