node-red-contrib-hik-media-buffer 1.1.1 → 1.1.3

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.
Files changed (2) hide show
  1. package/hik-media-buffer.js +38 -27
  2. package/package.json +1 -1
@@ -10,7 +10,7 @@ module.exports = function(RED) {
10
10
  RED.nodes.createNode(this, config);
11
11
  const node = this;
12
12
 
13
- node.name = config.name;
13
+ node.name = config.name || "TEST";
14
14
  node.host = config.host;
15
15
  node.port = config.port || "80";
16
16
  node.protocol = config.protocol || "http";
@@ -19,23 +19,31 @@ module.exports = function(RED) {
19
19
  node.camPass = config.camPass || config.pass;
20
20
  node.cameras = config.cameras || [];
21
21
 
22
+ // --- DEFINIZIONE PERCORSI PROGETTO ---
23
+ // Puntiamo direttamente alla cartella del tuo progetto Docker
24
+ const baseStorage = `C:\\Users\\APerucca\\Documents\\progetto-docker\\storage\\${node.name}`;
25
+ const imgDir = path.join(baseStorage, "allarmi");
26
+ const vidDir = path.join(baseStorage, "video");
27
+
28
+ // Creazione cartelle se non esistono
29
+ [imgDir, vidDir].forEach(dir => {
30
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
31
+ });
32
+
22
33
  let streamRequest = null;
23
34
  let isClosing = false;
24
35
  let lastTriggerTime = 0;
25
36
  let nvrOnline = true;
26
37
  let statoCamera = {};
27
38
 
28
- const tempDir = os.tmpdir();
29
39
  const EventList = ["FieldDetection", "LineDetection"];
30
40
 
31
41
  node.status({fill:"grey", shape:"ring", text:"Inizializzazione..."});
32
42
 
33
43
  function toHikDate(d) { return d.toISOString().split('.')[0] + "Z"; }
34
44
 
35
- // --- HELPER AGGIORNATO: Solo Urllib con Digest ---
36
45
  async function hikRequest(options) {
37
46
  const { method, url, data, responseType, user, pass, headers = {} } = options;
38
-
39
47
  const res = await urllib.request(url, {
40
48
  method: method,
41
49
  digestAuth: `${user}:${pass}`,
@@ -45,8 +53,6 @@ module.exports = function(RED) {
45
53
  timeout: 15000,
46
54
  rejectUnauthorized: false
47
55
  });
48
-
49
- // Urllib restituisce i dati in .data
50
56
  return res;
51
57
  }
52
58
 
@@ -117,6 +123,7 @@ module.exports = function(RED) {
117
123
 
118
124
  const nomeCamera = await getCameraName(camera);
119
125
  const referenceTime = new Date();
126
+ const timestamp = Math.floor(referenceTime.getTime() / 1000);
120
127
  const startTime = toHikDate(new Date(referenceTime.getTime() - (10 * 1000)));
121
128
  const endTime = toHikDate(new Date(referenceTime.getTime() + (10 * 1000)));
122
129
 
@@ -124,28 +131,16 @@ module.exports = function(RED) {
124
131
  await new Promise(resolve => setTimeout(resolve, 6000));
125
132
 
126
133
  const baseUrl = `${node.protocol}://${camera.ip}:${node.port}/ISAPI/ContentMgmt`;
127
- let output = { ip: camera.ip, nomeCliente: node.name, nome_telecamera: nomeCamera, channel: channelID, event: evento, videoPath: null, imageBuffer: null };
134
+ let output = { ip: camera.ip, nomeCliente: node.name, nome_telecamera: nomeCamera, channel: channelID, event: evento, videoPath: null, imageBuffer: null, imagePath: null };
128
135
 
129
136
  try {
130
- // RIPRISTINATA LA TUA LOGICA DEI TRACKS
131
137
  const tracks = [{ name: "termicoV", id: "201" }, { name: "termico", id: "203" }];
132
138
  for (let t of tracks) {
133
- const searchXml = `<?xml version="1.0" encoding="utf-8"?>
134
- <CMSearchDescription>
135
- <searchID>LAST_EVENT</searchID>
136
- <trackIDList><trackID>${t.id}</trackID></trackIDList>
137
- <timeSpanList><timeSpan><startTime>${startTime}</startTime><endTime>${endTime}</endTime></timeSpan></timeSpanList>
138
- <maxResults>100</maxResults>
139
- <metadataList><metadataDescriptor>//recordType.meta.std-cgi.com/${evento}</metadataDescriptor></metadataList>
140
- </CMSearchDescription>`;
139
+ const searchXml = `<?xml version="1.0" encoding="utf-8"?><CMSearchDescription><searchID>LAST_EVENT</searchID><trackIDList><trackID>${t.id}</trackID></trackIDList><timeSpanList><timeSpan><startTime>${startTime}</startTime><endTime>${endTime}</endTime></timeSpan></timeSpanList><maxResults>100</maxResults><metadataList><metadataDescriptor>//recordType.meta.std-cgi.com/${evento}</metadataDescriptor></metadataList></CMSearchDescription>`;
141
140
 
142
141
  const resSearch = await hikRequest({
143
- method: 'POST',
144
- url: `${baseUrl}/search`,
145
- data: searchXml,
146
- headers: { "Content-Type": "application/xml" },
147
- user: node.user,
148
- pass: node.camPass
142
+ method: 'POST', url: `${baseUrl}/search`, data: searchXml,
143
+ headers: { "Content-Type": "application/xml" }, user: node.user, pass: node.camPass
149
144
  });
150
145
 
151
146
  let xml = resSearch.data.toString().replace(/<(\/?)\w+:/g, "<$1");
@@ -154,7 +149,7 @@ module.exports = function(RED) {
154
149
  if (uriMatch) {
155
150
  const rawUri = uriMatch[1].replace(/&amp;/g, '&');
156
151
  const resDown = await hikRequest({
157
- method: 'GET',
152
+ method: 'GET',
158
153
  url: `${baseUrl}/download`,
159
154
  data: `<?xml version="1.0" encoding="UTF-8"?><downloadRequest><playbackURI>${rawUri.replace(/&/g, '&amp;')}</playbackURI></downloadRequest>`,
160
155
  responseType: 'arraybuffer',
@@ -163,12 +158,24 @@ module.exports = function(RED) {
163
158
  });
164
159
 
165
160
  let buffer = Buffer.from(resDown.data);
161
+
166
162
  if (t.id === "203") {
163
+ // --- LOGICA FOTO (IDENTICA ALLA TUA) ---
167
164
  output.imageBuffer = buffer;
165
+
166
+ // AGGIUNTA: Salviamo fisicamente il file per il sito
167
+ const imgFileName = `img_${timestamp}.jpg`;
168
+ const fullImgPath = path.join(imgDir, imgFileName);
169
+ fs.writeFileSync(fullImgPath, buffer);
170
+ output.imagePath = fullImgPath;
171
+
168
172
  } else {
173
+ // --- LOGICA VIDEO (IDENTICA ALLA TUA) ---
169
174
  if (buffer.slice(0, 4).toString() === 'IMKH') buffer = buffer.slice(40);
170
- const rawPath = path.join(tempDir, `raw_${Date.now()}.mp4`);
171
- const fixedPath = path.join(tempDir, `hik_v_${channelID}_${Date.now()}.mp4`);
175
+
176
+ const rawPath = path.join(vidDir, `raw_${timestamp}.mp4`);
177
+ const fixedPath = path.join(vidDir, `hik_v_${channelID}_${timestamp}.mp4`);
178
+
172
179
  fs.writeFileSync(rawPath, buffer);
173
180
 
174
181
  await new Promise((resolve) => {
@@ -176,21 +183,25 @@ module.exports = function(RED) {
176
183
  if (!err) {
177
184
  output.videoPath = fixedPath;
178
185
  try { fs.unlinkSync(rawPath); } catch(e) {}
179
- } else { output.videoPath = rawPath; }
186
+ } else {
187
+ output.videoPath = rawPath;
188
+ }
180
189
  resolve();
181
190
  });
182
191
  });
192
+
183
193
  setTimeout(() => { if (output.videoPath && fs.existsSync(output.videoPath)) fs.unlinkSync(output.videoPath); }, 180000);
184
194
  }
185
195
  }
186
196
  }
187
- if (output.imageBuffer || output.videoPath) node.send({ payload: output });
197
+ if (output.imagePath || output.videoPath) node.send({ payload: output });
188
198
  } catch (e) {
189
199
  node.error(`Errore Download Cam ${channelID}: ${e.message}`);
190
200
  }
191
201
  updateNodeStatus();
192
202
  }
193
203
 
204
+ // --- GESTIONE NVR ALERT STREAM ---
194
205
  function startAlertStream() {
195
206
  if (isClosing) return;
196
207
  const url = `${node.protocol}://${node.host}:${node.port}/ISAPI/Event/notification/alertStream`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-hik-media-buffer",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Ottiene buffer video e immagine da camere Hikvision via ISAPI",
5
5
  "keywords": [
6
6
  "node-red",