node-red-contrib-hik-media-buffer 1.1.1 → 1.1.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.
Files changed (2) hide show
  1. package/hik-media-buffer.js +28 -31
  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,21 +149,23 @@ 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',
158
- url: `${baseUrl}/download`,
152
+ method: 'GET', url: `${baseUrl}/download`,
159
153
  data: `<?xml version="1.0" encoding="UTF-8"?><downloadRequest><playbackURI>${rawUri.replace(/&/g, '&amp;')}</playbackURI></downloadRequest>`,
160
- responseType: 'arraybuffer',
161
- user: node.user,
162
- pass: node.camPass
154
+ responseType: 'arraybuffer', user: node.user, pass: node.camPass
163
155
  });
164
156
 
165
157
  let buffer = Buffer.from(resDown.data);
166
158
  if (t.id === "203") {
159
+ // SALVATAGGIO IMMAGINE FISICO
160
+ const imgFileName = `img_${timestamp}.jpg`;
161
+ const fullImgPath = path.join(imgDir, imgFileName);
162
+ fs.writeFileSync(fullImgPath, buffer);
167
163
  output.imageBuffer = buffer;
164
+ output.imagePath = fullImgPath; // Passiamo il path fisico
168
165
  } else {
169
166
  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`);
167
+ const rawPath = path.join(vidDir, `raw_${timestamp}.mp4`);
168
+ const fixedPath = path.join(vidDir, `hik_v_${channelID}_${timestamp}.mp4`);
172
169
  fs.writeFileSync(rawPath, buffer);
173
170
 
174
171
  await new Promise((resolve) => {
@@ -180,17 +177,17 @@ module.exports = function(RED) {
180
177
  resolve();
181
178
  });
182
179
  });
183
- setTimeout(() => { if (output.videoPath && fs.existsSync(output.videoPath)) fs.unlinkSync(output.videoPath); }, 180000);
184
180
  }
185
181
  }
186
182
  }
187
- if (output.imageBuffer || output.videoPath) node.send({ payload: output });
183
+ if (output.imagePath || output.videoPath) node.send({ payload: output });
188
184
  } catch (e) {
189
185
  node.error(`Errore Download Cam ${channelID}: ${e.message}`);
190
186
  }
191
187
  updateNodeStatus();
192
188
  }
193
189
 
190
+ // --- GESTIONE NVR ALERT STREAM ---
194
191
  function startAlertStream() {
195
192
  if (isClosing) return;
196
193
  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.2",
4
4
  "description": "Ottiene buffer video e immagine da camere Hikvision via ISAPI",
5
5
  "keywords": [
6
6
  "node-red",