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.
- package/hik-media-buffer.js +38 -27
- package/package.json +1 -1
package/hik-media-buffer.js
CHANGED
|
@@ -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
|
-
|
|
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(/&/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, '&')}</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
|
-
|
|
171
|
-
const
|
|
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 {
|
|
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.
|
|
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`;
|