@apocaliss92/nodelink-js 0.5.1-beta.1 → 0.5.1-beta.11

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.
@@ -12,7 +12,7 @@ import {
12
12
  sampleStreams,
13
13
  sanitizeFixtureData,
14
14
  testChannelStreams
15
- } from "./chunk-AHY4L7JI.js";
15
+ } from "./chunk-VOPEOB4H.js";
16
16
  import "./chunk-GVWJGQPT.js";
17
17
  export {
18
18
  captureModelFixtures,
@@ -29,4 +29,4 @@ export {
29
29
  sanitizeFixtureData,
30
30
  testChannelStreams
31
31
  };
32
- //# sourceMappingURL=DiagnosticsTools-ILDDJZL7.js.map
32
+ //# sourceMappingURL=DiagnosticsTools-7BIWJDZS.js.map
@@ -30,7 +30,7 @@ import {
30
30
  runAllDiagnosticsConsecutively,
31
31
  runMultifocalDiagnosticsConsecutively,
32
32
  xmlEscape
33
- } from "./chunk-AHY4L7JI.js";
33
+ } from "./chunk-VOPEOB4H.js";
34
34
  import {
35
35
  BC_CLASS_FILE_DOWNLOAD,
36
36
  BC_CLASS_LEGACY,
@@ -656,6 +656,10 @@ function sleep(ms) {
656
656
  return new Promise((r) => setTimeout(r, ms));
657
657
  }
658
658
  var P2P_RELAY_HOSTNAMES = [
659
+ // Master anycast routers — try first.
660
+ "p2pm-abr.reolink.com",
661
+ "p2pm-ali.reolink.com",
662
+ // Numbered regional relays.
659
663
  "p2p.reolink.com",
660
664
  "p2p1.reolink.com",
661
665
  "p2p2.reolink.com",
@@ -667,8 +671,29 @@ var P2P_RELAY_HOSTNAMES = [
667
671
  "p2p8.reolink.com",
668
672
  "p2p9.reolink.com",
669
673
  "p2p10.reolink.com",
670
- "p2p11.reolink.com"
674
+ "p2p11.reolink.com",
675
+ // China-region fallbacks (intentionally last).
676
+ "p2p.reolink.com.cn",
677
+ "p2p1.reolink.com.cn",
678
+ "p2p2.reolink.com.cn",
679
+ "p2p3.reolink.com.cn",
680
+ "p2p4.reolink.com.cn",
681
+ "p2p5.reolink.com.cn",
682
+ "p2p6.reolink.com.cn",
683
+ "p2p7.reolink.com.cn",
684
+ "p2p8.reolink.com.cn",
685
+ "p2p9.reolink.com.cn"
671
686
  ];
687
+ function isUnroutableForP2P(ip) {
688
+ if (!ip) return true;
689
+ if (ip === "0.0.0.0") return true;
690
+ if (ip.startsWith("127.")) return true;
691
+ if (ip.startsWith("10.")) return true;
692
+ if (ip.startsWith("192.168.")) return true;
693
+ if (/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(ip)) return true;
694
+ if (/^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(ip)) return true;
695
+ return false;
696
+ }
672
697
  var P2P_LOOKUP_PORT = 9999;
673
698
  var P2P_MAX_WAIT_MS = 15e3;
674
699
  var P2P_RESEND_WAIT_MS = 500;
@@ -835,19 +860,30 @@ var BcUdpStream = class extends EventEmitter {
835
860
  }
836
861
  async p2pUidLookup(sock, uid) {
837
862
  const resolved = [];
863
+ const sinkholed = [];
838
864
  for (const host of P2P_RELAY_HOSTNAMES) {
839
865
  try {
840
866
  const answers = await dns.lookup(host, { family: 4, all: true });
841
867
  for (const a of answers) {
842
- if (a.address && !resolved.includes(a.address))
843
- resolved.push(a.address);
868
+ if (!a.address) continue;
869
+ if (isUnroutableForP2P(a.address)) {
870
+ sinkholed.push({ host, ip: a.address });
871
+ continue;
872
+ }
873
+ if (!resolved.includes(a.address)) resolved.push(a.address);
844
874
  }
845
875
  } catch {
846
876
  }
847
877
  }
848
878
  if (resolved.length === 0) {
879
+ if (sinkholed.length > 0) {
880
+ const samples = sinkholed.slice(0, 3).map((s) => `${s.host} \u2192 ${s.ip}`).join(", ");
881
+ throw new Error(
882
+ `P2P UID lookup failed: DNS resolves Reolink P2P hostnames to non-routable IPs (${samples}). This is almost certainly an /etc/hosts rewrite or a DNS filter (Pi-hole, AdGuard, NextDNS) blocking *.reolink.com. Battery cameras cannot connect without P2P \u2014 whitelist *.reolink.com (at least p2p*.reolink.com on UDP/9999) or remove the override. Verify with \`getent hosts p2p.reolink.com\` inside your container \u2014 if it differs from \`nslookup p2p.reolink.com\`, the offender is /etc/hosts.`
883
+ );
884
+ }
849
885
  throw new Error(
850
- "P2P UID lookup failed: no p2p.reolink.com addresses resolved"
886
+ "P2P UID lookup failed: no p2p.reolink.com addresses resolved (DNS failure)"
851
887
  );
852
888
  }
853
889
  const start = Date.now();
@@ -19771,6 +19807,37 @@ ${xml}`
19771
19807
  }
19772
19808
  );
19773
19809
  }
19810
+ if (!isMultiFocal) {
19811
+ try {
19812
+ const caps = await this.getDeviceCapabilities(channel);
19813
+ const channelNumRaw = caps.support?.channelNum;
19814
+ const channelNum = typeof channelNumRaw === "string" ? Number.parseInt(channelNumRaw, 10) : channelNumRaw;
19815
+ if (Number.isFinite(channelNum) && channelNum >= 2) {
19816
+ isMultiFocal = true;
19817
+ }
19818
+ if (!isMultiFocal) {
19819
+ const items = caps.support?.items;
19820
+ if (Array.isArray(items)) {
19821
+ for (const it of items) {
19822
+ const raw = it.binoCfg;
19823
+ const v = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : 0;
19824
+ if (Number.isFinite(v) && v > 0) {
19825
+ isMultiFocal = true;
19826
+ break;
19827
+ }
19828
+ }
19829
+ }
19830
+ }
19831
+ } catch (e) {
19832
+ logDebug(
19833
+ "[ReolinkBaichuanApi] buildVideoStreamOptions: SupportInfo dual-lens probe failed",
19834
+ {
19835
+ host: this.host,
19836
+ err: e instanceof Error ? e.message : String(e)
19837
+ }
19838
+ );
19839
+ }
19840
+ }
19774
19841
  logDebug("[ReolinkBaichuanApi] buildVideoStreamOptions: inputs", {
19775
19842
  host: this.host,
19776
19843
  onNvr,
@@ -20165,7 +20232,7 @@ ${xml}`
20165
20232
  * @returns Test results for all stream types and profiles
20166
20233
  */
20167
20234
  async testChannelStreams(channel, logger) {
20168
- const { testChannelStreams } = await import("./DiagnosticsTools-ILDDJZL7.js");
20235
+ const { testChannelStreams } = await import("./DiagnosticsTools-7BIWJDZS.js");
20169
20236
  return await testChannelStreams({
20170
20237
  api: this,
20171
20238
  channel: this.normalizeChannel(channel),
@@ -20181,7 +20248,7 @@ ${xml}`
20181
20248
  * @returns Complete diagnostics for all channels and streams
20182
20249
  */
20183
20250
  async collectMultifocalDiagnostics(logger) {
20184
- const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-ILDDJZL7.js");
20251
+ const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-7BIWJDZS.js");
20185
20252
  return await collectMultifocalDiagnostics({
20186
20253
  api: this,
20187
20254
  logger
@@ -24231,19 +24298,95 @@ function isTcpFailureThatShouldFallbackToUdp(e) {
24231
24298
  return message.includes("ECONNREFUSED") || message.includes("ETIMEDOUT") || message.includes("EHOSTUNREACH") || message.includes("ENETUNREACH") || message.includes("socket hang up") || message.includes("TCP connection timeout") || message.includes("Baichuan socket closed") || message.includes("timeout waiting for nonce") || message.includes("expected encryption info") || message.includes("ECONNRESET") || message.includes("EPIPE");
24232
24299
  }
24233
24300
  async function pingHost(host, timeoutMs = 3e3) {
24234
- const { exec } = await import("child_process");
24301
+ if (!host || typeof host !== "string") return false;
24235
24302
  const platform2 = process.platform;
24236
- const pingCmd = platform2 === "win32" ? `ping -n 1 -w ${timeoutMs} ${host}` : platform2 === "darwin" ? (
24237
- // macOS: -W is in milliseconds (Linux: seconds)
24238
- `ping -c 1 -W ${timeoutMs} ${host}`
24239
- ) : (
24240
- // Linux/BSD-ish: -W is in seconds on most distros
24241
- `ping -c 1 -W ${Math.max(1, Math.floor(timeoutMs / 1e3))} ${host}`
24242
- );
24303
+ const pingCandidates = platform2 === "win32" ? ["ping"] : platform2 === "darwin" ? ["/sbin/ping", "/usr/sbin/ping", "ping"] : ["/bin/ping", "/usr/bin/ping", "ping"];
24304
+ const pingArgs = (bin) => {
24305
+ void bin;
24306
+ if (platform2 === "win32") {
24307
+ return ["-n", "1", "-w", String(timeoutMs), host];
24308
+ }
24309
+ if (platform2 === "darwin") {
24310
+ return ["-c", "1", "-W", String(timeoutMs), host];
24311
+ }
24312
+ return ["-c", "1", "-W", String(Math.max(1, Math.floor(timeoutMs / 1e3))), host];
24313
+ };
24314
+ const { spawn: spawn3 } = await import("child_process");
24315
+ for (const bin of pingCandidates) {
24316
+ const ranOk = await new Promise((resolve) => {
24317
+ let settled = false;
24318
+ let child;
24319
+ try {
24320
+ child = spawn3(bin, pingArgs(bin), { stdio: "ignore" });
24321
+ } catch {
24322
+ resolve("spawn-failed");
24323
+ return;
24324
+ }
24325
+ const timer = setTimeout(() => {
24326
+ if (settled) return;
24327
+ settled = true;
24328
+ try {
24329
+ child?.kill("SIGKILL");
24330
+ } catch {
24331
+ }
24332
+ resolve(false);
24333
+ }, timeoutMs + 500);
24334
+ child.on("error", () => {
24335
+ if (settled) return;
24336
+ settled = true;
24337
+ clearTimeout(timer);
24338
+ resolve("spawn-failed");
24339
+ });
24340
+ child.on("exit", (code) => {
24341
+ if (settled) return;
24342
+ settled = true;
24343
+ clearTimeout(timer);
24344
+ resolve(code === 0);
24345
+ });
24346
+ });
24347
+ if (ranOk === true) return true;
24348
+ if (ranOk === "spawn-failed") continue;
24349
+ break;
24350
+ }
24351
+ for (const port of [9e3, 443, 80]) {
24352
+ if (await tcpReachabilityProbe(host, port, 800)) return true;
24353
+ }
24354
+ return false;
24355
+ }
24356
+ async function tcpReachabilityProbe(host, port, timeoutMs) {
24357
+ const net4 = await import("net");
24243
24358
  return new Promise((resolve) => {
24244
- exec(pingCmd, (error) => {
24245
- resolve(!error);
24359
+ let settled = false;
24360
+ const socket = new net4.Socket();
24361
+ const timer = setTimeout(() => {
24362
+ if (settled) return;
24363
+ settled = true;
24364
+ try {
24365
+ socket.destroy();
24366
+ } catch {
24367
+ }
24368
+ resolve(false);
24369
+ }, timeoutMs);
24370
+ const finish = (reachable) => {
24371
+ if (settled) return;
24372
+ settled = true;
24373
+ clearTimeout(timer);
24374
+ try {
24375
+ socket.destroy();
24376
+ } catch {
24377
+ }
24378
+ resolve(reachable);
24379
+ };
24380
+ socket.once("connect", () => finish(true));
24381
+ socket.once("error", (err) => {
24382
+ if (err?.code === "ECONNREFUSED") finish(true);
24383
+ else finish(false);
24246
24384
  });
24385
+ try {
24386
+ socket.connect(port, host);
24387
+ } catch {
24388
+ finish(false);
24389
+ }
24247
24390
  });
24248
24391
  }
24249
24392
  function createBaichuanApi(inputs, transport) {
@@ -24759,6 +24902,7 @@ export {
24759
24902
  encodeHeader,
24760
24903
  decodeHeader,
24761
24904
  BaichuanFrameParser,
24905
+ isUnroutableForP2P,
24762
24906
  BcUdpStream,
24763
24907
  asLogger,
24764
24908
  createNullLogger,
@@ -24823,6 +24967,7 @@ export {
24823
24967
  normalizeUid,
24824
24968
  maskUid,
24825
24969
  isTcpFailureThatShouldFallbackToUdp,
24970
+ tcpReachabilityProbe,
24826
24971
  autoDetectDeviceType
24827
24972
  };
24828
- //# sourceMappingURL=chunk-6ILAHQF5.js.map
24973
+ //# sourceMappingURL=chunk-COUH6ZJT.js.map