node-red-contrib-hik-media-buffer 1.2.0 → 1.2.2
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 +4 -10
- package/hik-snapshot.js +27 -14
- package/package.json +1 -1
package/hik-media-buffer.js
CHANGED
|
@@ -126,12 +126,13 @@ module.exports = function(RED) {
|
|
|
126
126
|
if (nowTime - lastTriggerTime[channelID] < 5000) return;
|
|
127
127
|
lastTriggerTime[channelID] = nowTime;
|
|
128
128
|
|
|
129
|
-
node.status({fill:"yellow", shape:"dot", text:`Attesa (10s)...`});
|
|
130
|
-
await new Promise(resolve => setTimeout(resolve, 13000));
|
|
131
|
-
|
|
132
129
|
const nvrAuth = new AxiosDigestAuth({ username: node.user, password: node.pass });
|
|
133
130
|
const referenceTime = new Date();
|
|
134
131
|
const timestamp = Math.floor(referenceTime.getTime() / 1000);
|
|
132
|
+
|
|
133
|
+
node.status({fill:"yellow", shape:"dot", text:`Attesa (15s)...`});
|
|
134
|
+
await new Promise(resolve => setTimeout(resolve, 15000));
|
|
135
|
+
|
|
135
136
|
|
|
136
137
|
const videoTrackID = (parseInt(channelID) * 100) + 1;
|
|
137
138
|
const nomeCamera = await getCameraName(channelID);
|
|
@@ -211,7 +212,6 @@ module.exports = function(RED) {
|
|
|
211
212
|
const endClip = pulisciDataHik(targetEvento.endTime);
|
|
212
213
|
|
|
213
214
|
playbackURIGrezzo = `rtsp://${node.host}:${node.port}/Streaming/tracks/${videoTrackID}/?starttime=${startClip}&endtime=${endClip}`;
|
|
214
|
-
node.warn(`[HIK SCORCIATOIA] URI Generato: ${playbackURIGrezzo}`);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -224,9 +224,6 @@ module.exports = function(RED) {
|
|
|
224
224
|
<playbackURI>${playbackURIXml}</playbackURI>
|
|
225
225
|
</downloadRequest>`;
|
|
226
226
|
|
|
227
|
-
node.warn(`[HIK DOWNLOAD] Invio Body XML (Stream Mode):\n${payloadDownload}`);
|
|
228
|
-
|
|
229
|
-
// 🌟 CAMBIAMENTO CHIAVE: Chiediamo un 'stream' invece di 'arraybuffer'
|
|
230
227
|
const resDownVideo = await nvrAuth.request({
|
|
231
228
|
method: 'GET',
|
|
232
229
|
url: `${node.protocol}://${node.host}:${node.port}/ISAPI/ContentMgmt/download`,
|
|
@@ -270,9 +267,6 @@ module.exports = function(RED) {
|
|
|
270
267
|
fs.writeFileSync(rawPath, videoBuffer);
|
|
271
268
|
}
|
|
272
269
|
|
|
273
|
-
const stats = fs.statSync(rawPath);
|
|
274
|
-
node.warn(`[HIK DOWNLOAD OK] File scaricato. Dimensione: ${(stats.size / 1024).toFixed(1)} KB`);
|
|
275
|
-
|
|
276
270
|
await new Promise((resolve) => {
|
|
277
271
|
exec(`ffmpeg -y -i "${rawPath}" -c copy -movflags +faststart "${fixedPath}"`, (err) => {
|
|
278
272
|
if (!err && fs.existsSync(fixedPath)) {
|
package/hik-snapshot.js
CHANGED
|
@@ -8,9 +8,8 @@ module.exports = function(RED) {
|
|
|
8
8
|
RED.nodes.createNode(this, config);
|
|
9
9
|
const node = this;
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// Parametri di default salvati dalla configurazione grafica del nodo
|
|
12
12
|
node.nodeName = config.name || `NVR_${config.host}`;
|
|
13
|
-
|
|
14
13
|
node.protocol = config.protocol || "http";
|
|
15
14
|
node.host = config.host;
|
|
16
15
|
node.port = config.port || "80";
|
|
@@ -21,13 +20,25 @@ module.exports = function(RED) {
|
|
|
21
20
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
22
21
|
|
|
23
22
|
node.on('input', async function(msg) {
|
|
24
|
-
|
|
23
|
+
// 🌟 1. LOGICA DI ACCETTAZIONE FLESSIBILE
|
|
24
|
+
// Il nodo parte se il payload è 'true' OPPURE se stiamo passando una configurazione dinamica
|
|
25
|
+
if (msg.payload !== true && typeof msg.payload !== 'object') return;
|
|
25
26
|
|
|
26
27
|
node.status({fill: "blue", shape: "dot", text: "Verifica canali..."});
|
|
27
28
|
|
|
29
|
+
// 🌟 2. VALUTAZIONE PARAMETRI DINAMICI VS PARAMETRI STATICI
|
|
30
|
+
// Se le proprietà sono presenti nel msg, usiamo quelle, altrimenti facciamo il fallback su quelle del nodo
|
|
31
|
+
const NVR_HOST = msg.nvr_host || node.host;
|
|
32
|
+
const NVR_PORT = msg.nvr_port || node.port;
|
|
33
|
+
const NVR_USER = msg.nvr_user || node.user;
|
|
34
|
+
const NVR_PASS = msg.nvr_pass || node.pass;
|
|
35
|
+
const NVR_NAME = msg.nvr_name || node.nodeName;
|
|
36
|
+
const MAX_CHANNELS = parseInt(msg.nvr_channels) || node.maxChannels;
|
|
37
|
+
const PROTOCOL = msg.nvr_protocol || node.protocol;
|
|
38
|
+
|
|
28
39
|
const digest = new DigestAuthClass({
|
|
29
|
-
username:
|
|
30
|
-
password:
|
|
40
|
+
username: NVR_USER,
|
|
41
|
+
password: NVR_PASS
|
|
31
42
|
});
|
|
32
43
|
|
|
33
44
|
const data = new Date();
|
|
@@ -37,15 +48,16 @@ module.exports = function(RED) {
|
|
|
37
48
|
|
|
38
49
|
let snapshotResults = [];
|
|
39
50
|
|
|
40
|
-
|
|
51
|
+
// Il ciclo ora scala dinamicamente in base al numero di canali calcolato
|
|
52
|
+
for (let i = 1; i <= MAX_CHANNELS; i++) {
|
|
41
53
|
const chanId = i + "01";
|
|
42
|
-
const snapUrl = `${
|
|
43
|
-
const recordUrl = `${
|
|
54
|
+
const snapUrl = `${PROTOCOL}://${NVR_HOST}:${NVR_PORT}/ISAPI/Streaming/channels/${chanId}/picture`;
|
|
55
|
+
const recordUrl = `${PROTOCOL}://${NVR_HOST}:${NVR_PORT}/ISAPI/ContentMgmt/record/tracks/${chanId}/dailyDistribution`;
|
|
44
56
|
|
|
45
57
|
const recordXml = `<?xml version="1.0" encoding="utf-8"?><trackDailyParam><year>${year}</year><monthOfYear>${month}</monthOfYear><dayOfMonth>${day}</dayOfMonth></trackDailyParam>`;
|
|
46
58
|
|
|
47
59
|
let resCanale = {
|
|
48
|
-
name:
|
|
60
|
+
name: NVR_NAME, // Dinamico o statico a seconda della modalità
|
|
49
61
|
channel: i,
|
|
50
62
|
photo: null,
|
|
51
63
|
snapOk: false,
|
|
@@ -58,7 +70,7 @@ module.exports = function(RED) {
|
|
|
58
70
|
method: 'GET',
|
|
59
71
|
url: snapUrl,
|
|
60
72
|
responseType: 'arraybuffer',
|
|
61
|
-
httpsAgent:
|
|
73
|
+
httpsAgent: PROTOCOL === 'https' ? httpsAgent : undefined,
|
|
62
74
|
timeout: 5000
|
|
63
75
|
});
|
|
64
76
|
resCanale.photo = responseSnap.data;
|
|
@@ -74,7 +86,7 @@ module.exports = function(RED) {
|
|
|
74
86
|
url: recordUrl,
|
|
75
87
|
data: recordXml,
|
|
76
88
|
headers: { 'Content-Type': 'application/xml' },
|
|
77
|
-
httpsAgent:
|
|
89
|
+
httpsAgent: PROTOCOL === 'https' ? httpsAgent : undefined,
|
|
78
90
|
timeout: 5000
|
|
79
91
|
});
|
|
80
92
|
const xmlOutput = responseRec.data.toString();
|
|
@@ -88,17 +100,18 @@ module.exports = function(RED) {
|
|
|
88
100
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
89
101
|
}
|
|
90
102
|
|
|
103
|
+
// Restituiamo i risultati sovrascrivendo il payload ma lasciando inalterato il resto del msg (es. msg.chatId)
|
|
91
104
|
msg.payload = snapshotResults;
|
|
92
105
|
node.send(msg);
|
|
93
106
|
|
|
94
|
-
// Conteggi per lo stato del nodo
|
|
107
|
+
// Conteggi per lo stato visivo sul quadratino del nodo
|
|
95
108
|
const snapCount = snapshotResults.filter(v => v.snapOk).length;
|
|
96
109
|
const recCount = snapshotResults.filter(v => v.isRecording).length;
|
|
97
110
|
|
|
98
111
|
node.status({
|
|
99
|
-
fill: (snapCount ===
|
|
112
|
+
fill: (snapCount === MAX_CHANNELS && recCount === MAX_CHANNELS) ? "green" : "yellow",
|
|
100
113
|
shape: "dot",
|
|
101
|
-
text: `Snap: ${snapCount}/${
|
|
114
|
+
text: `Snap: ${snapCount}/${MAX_CHANNELS} | Rec: ${recCount}/${MAX_CHANNELS}`
|
|
102
115
|
});
|
|
103
116
|
});
|
|
104
117
|
}
|