node-red-contrib-hik-media-buffer 1.2.5 → 1.2.6
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-download.html +3 -3
- package/hik-download.js +41 -23
- package/package.json +1 -1
package/hik-download.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
host: { value: "" },
|
|
8
|
-
channels: { value: "
|
|
8
|
+
channels: { value: "" },
|
|
9
9
|
protocol: { value: "http" },
|
|
10
10
|
port: { value: "80" },
|
|
11
11
|
user: { value: "admin" },
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
|
|
51
51
|
<div class="form-row">
|
|
52
52
|
<label for="node-input-startTime"><i class="fa fa-calendar"></i> Data Inizio</label>
|
|
53
|
-
<input type="
|
|
53
|
+
<input type="datetime-local" id="node-input-startTime" step="1" style="width: 70%">
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
56
|
<div class="form-row">
|
|
57
57
|
<label for="node-input-endTime"><i class="fa fa-calendar"></i> Data Fine</label>
|
|
58
|
-
<input type="
|
|
58
|
+
<input type="datetime-local" id="node-input-endTime" step="1" style="width: 70%">
|
|
59
59
|
</div>
|
|
60
60
|
|
|
61
61
|
<div class="form-row">
|
package/hik-download.js
CHANGED
|
@@ -21,7 +21,6 @@ module.exports = function(RED) {
|
|
|
21
21
|
|
|
22
22
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
23
23
|
|
|
24
|
-
// Helper per decifrare i canali (es: "1,3" o "1-3")
|
|
25
24
|
function parseChannels(channelsStr) {
|
|
26
25
|
let channels = [];
|
|
27
26
|
if (!channelsStr) return [1];
|
|
@@ -63,19 +62,23 @@ module.exports = function(RED) {
|
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
//
|
|
65
|
+
// Pulizia date per l'NVR
|
|
67
66
|
const pulisciDataNVR = (dataStr) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
let d = dataStr.replace(' ', 'T');
|
|
68
|
+
d = d.replace(/[-:]/g, '')
|
|
69
|
+
.split('.')[0]
|
|
70
|
+
.replace('Z', '');
|
|
71
|
+
|
|
72
|
+
if (d.includes('T') && d.split('T')[1].length === 4) {
|
|
73
|
+
d += '00';
|
|
74
|
+
}
|
|
75
|
+
return d;
|
|
72
76
|
};
|
|
73
77
|
|
|
74
78
|
const startTimeNVR = pulisciDataNVR(startTimeRaw);
|
|
75
79
|
const endTimeNVR = pulisciDataNVR(endTimeRaw);
|
|
76
80
|
|
|
77
|
-
//
|
|
78
|
-
// Estraiamo la data del giorno dallo startTime (i primi 10 caratteri, es: "2026-07-14")
|
|
81
|
+
// Creazione cartella C:\download\YYYY-MM-DD
|
|
79
82
|
const dataGiorno = startTimeRaw.substring(0, 10).replace(/\//g, '-');
|
|
80
83
|
const baseDir = "C:\\download";
|
|
81
84
|
const targetDir = path.join(baseDir, dataGiorno);
|
|
@@ -92,7 +95,6 @@ module.exports = function(RED) {
|
|
|
92
95
|
|
|
93
96
|
const digest = new DigestAuthClass({ username: user, password: pass });
|
|
94
97
|
|
|
95
|
-
// 🌟 3. CICLO DI DOWNLOAD E SALVATAGGIO SU DISCO
|
|
96
98
|
for (let index = 0; index < targetChannels.length; index++) {
|
|
97
99
|
const ch = targetChannels[index];
|
|
98
100
|
const trackId = ch + "01";
|
|
@@ -103,34 +105,50 @@ module.exports = function(RED) {
|
|
|
103
105
|
text: `Download Ch ${ch} (${index + 1}/${targetChannels.length})...`
|
|
104
106
|
});
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
+
// 🌟 1. GENERIAMO L'URI DI PLAYBACK ESATTAMENTE COME RICHIESTO
|
|
109
|
+
const playbackURIGrezzo = `rtsp://${host}:${port}/Streaming/tracks/${trackId}/?starttime=${startTimeNVR}&endtime=${endTimeNVR}`;
|
|
110
|
+
const playbackURIXml = playbackURIGrezzo.replace(/&/g, '&');
|
|
111
|
+
|
|
112
|
+
const payloadDownload = `<?xml version="1.0" encoding="UTF-8"?>
|
|
113
|
+
<downloadRequest>
|
|
114
|
+
<playbackURI>${playbackURIXml}</playbackURI>
|
|
115
|
+
</downloadRequest>`;
|
|
108
116
|
|
|
109
117
|
try {
|
|
118
|
+
// 🌟 2. GET DI DOWNLOAD CON PAYLOAD XML E RESPONSE 'STREAM'
|
|
110
119
|
const response = await digest.request({
|
|
111
120
|
method: 'GET',
|
|
112
|
-
url:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
url: `${protocol}://${host}:${port}/ISAPI/ContentMgmt/download`,
|
|
122
|
+
data: payloadDownload,
|
|
123
|
+
headers: { "Content-Type": "application/xml" },
|
|
124
|
+
responseType: 'stream',
|
|
125
|
+
insecureHTTPParser: true,
|
|
126
|
+
httpsAgent: protocol === "https" ? httpsAgent : undefined,
|
|
127
|
+
timeout: 120000
|
|
116
128
|
});
|
|
117
129
|
|
|
118
130
|
if (response.status === 200 || response.status === 206) {
|
|
119
|
-
const videoBuffer = Buffer.from(response.data);
|
|
120
|
-
|
|
121
|
-
// Prepariamo il nome del file ripulito (es: NVR_Cam1_100000.mp4)
|
|
122
131
|
const nomeCondominio = (msg.nvr_name || node.name || 'NVR').replace(/[^a-zA-Z0-9]/g, '_');
|
|
123
132
|
const oraInizio = startTimeNVR.split('T')[1] || startTimeNVR;
|
|
124
133
|
const finalFileName = `${nomeCondominio}_Cam${ch}_${oraInizio}.mp4`;
|
|
125
134
|
const finalFilePath = path.join(targetDir, finalFileName);
|
|
126
135
|
|
|
127
|
-
//
|
|
128
|
-
fs.
|
|
129
|
-
|
|
136
|
+
// 🌟 3. SALVATAGGIO TRAMITE STREAM (Ottimizzato per file grandi)
|
|
137
|
+
const writer = fs.createWriteStream(finalFilePath);
|
|
138
|
+
response.data.pipe(writer);
|
|
139
|
+
|
|
140
|
+
await new Promise((resolve, reject) => {
|
|
141
|
+
writer.on('finish', resolve);
|
|
142
|
+
writer.on('error', (err) => {
|
|
143
|
+
writer.close();
|
|
144
|
+
reject(err);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
node.log(`File salvato in: ${finalFilePath}`);
|
|
130
149
|
|
|
131
|
-
// Inviamo comunque il messaggio avanti nel flusso nel caso servisse ad altri nodi
|
|
132
150
|
let outMsg = RED.util.cloneMessage(msg);
|
|
133
|
-
outMsg.payload = finalFilePath;
|
|
151
|
+
outMsg.payload = finalFilePath;
|
|
134
152
|
outMsg.channel = ch;
|
|
135
153
|
outMsg.filename = finalFileName;
|
|
136
154
|
node.send(outMsg);
|