@apocaliss92/nodelink-js 0.4.26 → 0.4.28
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/README.md +4 -2
- package/dist/{chunk-F3XCYKYT.js → chunk-NQ7D5TLR.js} +145 -26
- package/dist/chunk-NQ7D5TLR.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +105 -25
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +473 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -6
- package/dist/index.d.ts +221 -5
- package/dist/index.js +290 -5
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-F3XCYKYT.js.map +0 -1
package/dist/cli/rtsp-server.cjs
CHANGED
|
@@ -8114,6 +8114,21 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
8114
8114
|
let audioSampleRate = null;
|
|
8115
8115
|
let streamStarted = false;
|
|
8116
8116
|
let closed = false;
|
|
8117
|
+
const signal = options?.signal;
|
|
8118
|
+
let sleepResolve = null;
|
|
8119
|
+
let sleepTimer = null;
|
|
8120
|
+
const clearSleepTimer = () => {
|
|
8121
|
+
if (sleepTimer) {
|
|
8122
|
+
clearTimeout(sleepTimer);
|
|
8123
|
+
sleepTimer = null;
|
|
8124
|
+
}
|
|
8125
|
+
};
|
|
8126
|
+
const handleAbort = () => {
|
|
8127
|
+
clearSleepTimer();
|
|
8128
|
+
const r = sleepResolve;
|
|
8129
|
+
sleepResolve = null;
|
|
8130
|
+
r?.();
|
|
8131
|
+
};
|
|
8117
8132
|
const onError = (_error) => {
|
|
8118
8133
|
closed = true;
|
|
8119
8134
|
api.logger?.warn?.(
|
|
@@ -8211,7 +8226,13 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
8211
8226
|
}
|
|
8212
8227
|
});
|
|
8213
8228
|
streamStarted = true;
|
|
8214
|
-
|
|
8229
|
+
if (signal) {
|
|
8230
|
+
if (signal.aborted) {
|
|
8231
|
+
closed = true;
|
|
8232
|
+
} else {
|
|
8233
|
+
signal.addEventListener("abort", handleAbort);
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8215
8236
|
while (!closed && !signal?.aborted) {
|
|
8216
8237
|
if (frameQueue.length > 0) {
|
|
8217
8238
|
const frame = frameQueue.shift();
|
|
@@ -8219,31 +8240,30 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
8219
8240
|
} else {
|
|
8220
8241
|
await new Promise((resolve) => {
|
|
8221
8242
|
frameResolve = resolve;
|
|
8222
|
-
|
|
8243
|
+
sleepResolve = resolve;
|
|
8244
|
+
sleepTimer = setTimeout(() => {
|
|
8245
|
+
sleepTimer = null;
|
|
8246
|
+
if (sleepResolve === resolve) sleepResolve = null;
|
|
8223
8247
|
if (frameResolve === resolve) {
|
|
8224
8248
|
frameResolve = null;
|
|
8225
8249
|
resolve();
|
|
8226
8250
|
}
|
|
8227
8251
|
}, 1e3);
|
|
8228
|
-
if (signal) {
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
};
|
|
8234
|
-
if (signal.aborted) {
|
|
8235
|
-
clearTimeout(timer);
|
|
8236
|
-
frameResolve = null;
|
|
8237
|
-
resolve();
|
|
8238
|
-
} else {
|
|
8239
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
8240
|
-
}
|
|
8252
|
+
if (signal?.aborted) {
|
|
8253
|
+
clearSleepTimer();
|
|
8254
|
+
sleepResolve = null;
|
|
8255
|
+
frameResolve = null;
|
|
8256
|
+
resolve();
|
|
8241
8257
|
}
|
|
8242
8258
|
});
|
|
8259
|
+
sleepResolve = null;
|
|
8260
|
+
clearSleepTimer();
|
|
8243
8261
|
}
|
|
8244
8262
|
}
|
|
8245
8263
|
} finally {
|
|
8246
8264
|
closed = true;
|
|
8265
|
+
if (signal) signal.removeEventListener("abort", handleAbort);
|
|
8266
|
+
clearSleepTimer();
|
|
8247
8267
|
try {
|
|
8248
8268
|
await videoStream.stop();
|
|
8249
8269
|
} catch {
|
|
@@ -10277,12 +10297,13 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
10277
10297
|
if (frame.videoType === "H264" || frame.videoType === "H265") {
|
|
10278
10298
|
this.setFlowVideoType(frame.videoType, "native stream");
|
|
10279
10299
|
}
|
|
10280
|
-
this.flow.
|
|
10281
|
-
|
|
10282
|
-
|
|
10300
|
+
if (!this.flow.getFmtp().hasParamSets) {
|
|
10301
|
+
this.flow.extractParameterSets(frame.data);
|
|
10302
|
+
}
|
|
10303
|
+
if (this.flow.getFmtp().hasParamSets) {
|
|
10283
10304
|
this.markFirstFrameReceived();
|
|
10284
10305
|
}
|
|
10285
|
-
const isKeyframe = this.isRawFrameKeyframe(frame);
|
|
10306
|
+
const isKeyframe = typeof frame.isKeyframe === "boolean" ? frame.isKeyframe : this.isRawFrameKeyframe(frame);
|
|
10286
10307
|
this.prebuffer.push({
|
|
10287
10308
|
frame: { ...frame, data: Buffer.from(frame.data) },
|
|
10288
10309
|
time: Date.now(),
|
|
@@ -19033,6 +19054,29 @@ var parseSirenStatusListPushXml = (xml) => {
|
|
|
19033
19054
|
return out;
|
|
19034
19055
|
};
|
|
19035
19056
|
|
|
19057
|
+
// src/emailPush/bus.ts
|
|
19058
|
+
var import_node_events5 = require("events");
|
|
19059
|
+
var emitter = new import_node_events5.EventEmitter();
|
|
19060
|
+
function onEmailPushEvent(handler) {
|
|
19061
|
+
emitter.on("event", handler);
|
|
19062
|
+
return () => emitter.off("event", handler);
|
|
19063
|
+
}
|
|
19064
|
+
function mapEmailPushInferredType(inferred) {
|
|
19065
|
+
switch (inferred) {
|
|
19066
|
+
case "motion":
|
|
19067
|
+
case "doorbell":
|
|
19068
|
+
case "people":
|
|
19069
|
+
case "vehicle":
|
|
19070
|
+
case "animal":
|
|
19071
|
+
case "face":
|
|
19072
|
+
case "package":
|
|
19073
|
+
return inferred;
|
|
19074
|
+
case "other":
|
|
19075
|
+
default:
|
|
19076
|
+
return "motion";
|
|
19077
|
+
}
|
|
19078
|
+
}
|
|
19079
|
+
|
|
19036
19080
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
19037
19081
|
var DUAL_LENS_DUAL_MOTION_MODELS = /* @__PURE__ */ new Set([
|
|
19038
19082
|
"Reolink Duo PoE",
|
|
@@ -19178,7 +19222,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19178
19222
|
* general socket is created, logged in, and all event/push/guard listeners
|
|
19179
19223
|
* are re-attached automatically.
|
|
19180
19224
|
*
|
|
19181
|
-
* This is a **no-op** when the API is already
|
|
19225
|
+
* This is a **no-op** when the API is already ready (see `isReadyState()`).
|
|
19182
19226
|
*
|
|
19183
19227
|
* @throws If `close()` was called — the API is permanently closed and a new
|
|
19184
19228
|
* instance must be created.
|
|
@@ -19259,7 +19303,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19259
19303
|
/**
|
|
19260
19304
|
* Attach event, push, channelInfo, and guard listeners to the current
|
|
19261
19305
|
* "general" client. Called from the constructor and from
|
|
19262
|
-
*
|
|
19306
|
+
* `reconnectGeneralSocket()`.
|
|
19263
19307
|
*/
|
|
19264
19308
|
setupGeneralClientListeners() {
|
|
19265
19309
|
const client = this.client;
|
|
@@ -20848,6 +20892,40 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
20848
20892
|
this.econnresetStormRebootInFlight = void 0;
|
|
20849
20893
|
});
|
|
20850
20894
|
}
|
|
20895
|
+
/**
|
|
20896
|
+
* Bind this API instance to the global email-push bus so that incoming
|
|
20897
|
+
* SMTP-delivered motion / AI events for the matching camera surface on
|
|
20898
|
+
* this instance's standard `onSimpleEvent` channel. The consumer keeps
|
|
20899
|
+
* a single subscription (`onSimpleEvent`) and gets both the native
|
|
20900
|
+
* Baichuan push and the email-push transport on the same stream.
|
|
20901
|
+
*
|
|
20902
|
+
* - `cameraId` shorthand: match events with `event.cameraId === cameraId`.
|
|
20903
|
+
* - `match`: arbitrary predicate (e.g. when the consumer uses a
|
|
20904
|
+
* nickname-based mapping or wants to handle multiple recipients).
|
|
20905
|
+
*
|
|
20906
|
+
* Returns an `off()` handle. Safe to call repeatedly — each call
|
|
20907
|
+
* registers its own listener.
|
|
20908
|
+
*/
|
|
20909
|
+
subscribeEmailPushEvents(params) {
|
|
20910
|
+
const channel = params.channel ?? 0;
|
|
20911
|
+
const matches = "match" in params ? params.match : (event) => event.cameraId === params.cameraId;
|
|
20912
|
+
const off = onEmailPushEvent((event) => {
|
|
20913
|
+
if (!matches(event)) return;
|
|
20914
|
+
this.dispatchSimpleEvent({
|
|
20915
|
+
type: mapEmailPushInferredType(event.inferredType),
|
|
20916
|
+
channel,
|
|
20917
|
+
timestamp: event.receivedAtMs
|
|
20918
|
+
});
|
|
20919
|
+
if (event.inferredType !== "motion" && event.inferredType !== "doorbell" && event.inferredType !== "other") {
|
|
20920
|
+
this.dispatchSimpleEvent({
|
|
20921
|
+
type: "motion",
|
|
20922
|
+
channel,
|
|
20923
|
+
timestamp: event.receivedAtMs
|
|
20924
|
+
});
|
|
20925
|
+
}
|
|
20926
|
+
});
|
|
20927
|
+
return off;
|
|
20928
|
+
}
|
|
20851
20929
|
/**
|
|
20852
20930
|
* Subscribe to minimal high-level events.
|
|
20853
20931
|
* The API manages Baichuan subscribe/unsubscribe automatically.
|
|
@@ -20906,7 +20984,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
20906
20984
|
* Subscribe to per-frame detection events sourced from the BcMedia
|
|
20907
20985
|
* `additionalHeader` block on active video streams.
|
|
20908
20986
|
*
|
|
20909
|
-
* Mirrors
|
|
20987
|
+
* Mirrors `onSimpleEvent()` but is fed by the streaming side-channel:
|
|
20910
20988
|
* one event fires for every I-frame / P-frame that carries an overlay block.
|
|
20911
20989
|
* Coordinates are reported in normalized [0, 1] fractions of the source
|
|
20912
20990
|
* frame, so the same box renders correctly on mainStream, subStream, and
|
|
@@ -20933,7 +21011,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
20933
21011
|
* Subscribe to AI object detections (people / vehicle / animal / face boxes
|
|
20934
21012
|
* with class label and confidence) without managing a video stream yourself.
|
|
20935
21013
|
*
|
|
20936
|
-
* Mirrors
|
|
21014
|
+
* Mirrors `onSimpleEvent()` end-to-end: on the first listener for a given
|
|
20937
21015
|
* `(channel, profile)` tuple the API ensures the corresponding video stream
|
|
20938
21016
|
* is running (the pool socket may already be shared with a regular consumer),
|
|
20939
21017
|
* forwards every box-bearing `additionalHeader` to your callback, and tears
|
|
@@ -27777,7 +27855,7 @@ ${xml}`
|
|
|
27777
27855
|
* Field meaning per stream:
|
|
27778
27856
|
* - `audio` — 0/1 toggle
|
|
27779
27857
|
* - `width`/`height` — resolution in pixels. Must be one of the
|
|
27780
|
-
* resolutions returned by
|
|
27858
|
+
* resolutions returned by `getStreamInfoList()`.
|
|
27781
27859
|
* - `bitRate` — kbps. Must match the table from `getStreamInfoList`.
|
|
27782
27860
|
* - `frameRate` — fps. Must match the table from `getStreamInfoList`.
|
|
27783
27861
|
* - `videoEncType` — `"h264"` or `"h265"`
|
|
@@ -29166,10 +29244,12 @@ ${xml}`
|
|
|
29166
29244
|
const triggers = params.triggerTypes ?? ["MD", "people", "vehicle"];
|
|
29167
29245
|
const attachmentType = params.attachmentType ?? "picture";
|
|
29168
29246
|
const interval = params.interval ?? 30;
|
|
29247
|
+
const rawUser = params.authUsername ?? recipient;
|
|
29248
|
+
const wireUser = rawUser.includes("@") ? rawUser : `${rawUser}@${domain}`;
|
|
29169
29249
|
const emailPatch = {
|
|
29170
29250
|
smtpServer: params.managerHost,
|
|
29171
29251
|
smtpPort: port,
|
|
29172
|
-
userName:
|
|
29252
|
+
userName: wireUser,
|
|
29173
29253
|
password: params.authPassword ?? "",
|
|
29174
29254
|
address1: recipient,
|
|
29175
29255
|
address2: "",
|