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.
- package/hik-media-buffer.js +28 -31
- 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,21 +149,23 @@ 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',
|
|
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, '&')}</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(
|
|
171
|
-
const fixedPath = path.join(
|
|
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.
|
|
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`;
|