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 CHANGED
@@ -5,7 +5,7 @@
5
5
  defaults: {
6
6
  name: { value: "" },
7
7
  host: { value: "" },
8
- channels: { value: "1" },
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="text" id="node-input-startTime" placeholder="Formato: AAAA-MM-GG ORE:MIN:SEC (Es: 2026-07-14 10:00:00)">
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="text" id="node-input-endTime" placeholder="Formato: AAAA-MM-GG ORE:MIN:SEC (Es: 2026-07-14 10:05:00)">
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
- // 🌟 1. LOGICA DI PULIZIA DELLE DATE PER HIKVISION (es. "2026-07-14 10:00:00" -> "20260714T100000")
65
+ // Pulizia date per l'NVR
67
66
  const pulisciDataNVR = (dataStr) => {
68
- return dataStr
69
- .replace(/[-:\s]/g, '') // Rimuove trattini, due punti e spazi
70
- .split('.')[0] // Rimuove eventuali millisecondi
71
- .replace('Z', ''); // Rimuove la Z se presente
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
- // 🌟 2. CREAZIONE CARTELLA DI DESTINAZIONE (C:\download\YYYY-MM-DD)
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
- const playbackURI = `rtsp://${host}/Streaming/tracks/${trackId}/?starttime=${startTimeNVR}&endtime=${endTimeNVR}`;
107
- const downloadUrl = `${protocol}://${host}:${port}/ISAPI/ContentMgmt/download?playbackURI=${encodeURIComponent(playbackURI)}`;
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, '&amp;');
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: downloadUrl,
113
- responseType: 'arraybuffer',
114
- httpsAgent: protocol === 'https' ? httpsAgent : undefined,
115
- timeout: 120000 // 2 minuti di timeout per i video corposi
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
- // Scrittura fisica del file su disco C:\download\data\file.mp4
128
- fs.writeFileSync(finalFilePath, videoBuffer);
129
- node.log(`File salvato con successo in: ${finalFilePath}`);
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; // Spedisce il percorso del file salvato come testo
151
+ outMsg.payload = finalFilePath;
134
152
  outMsg.channel = ch;
135
153
  outMsg.filename = finalFileName;
136
154
  node.send(outMsg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-hik-media-buffer",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "Ottiene buffer video e immagine da camere Hikvision via ISAPI",
5
5
  "keywords": [
6
6
  "node-red",