@camstack/addon-pipeline 1.1.19 → 1.1.21

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 (34) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/audio-codec-ffmpeg/index.js +1125 -0
  4. package/dist/audio-codec-ffmpeg/index.mjs +1107 -0
  5. package/dist/decoder-ffmpeg/index.js +543 -288
  6. package/dist/decoder-ffmpeg/index.mjs +542 -287
  7. package/dist/detection-pipeline/index.js +1 -1
  8. package/dist/detection-pipeline/index.mjs +1 -1
  9. package/dist/{dist-BgoBMCez.js → dist-BiP1gPeY.js} +20 -1
  10. package/dist/{dist-7Yx2dmuV.mjs → dist-tzhTTRq4.mjs} +20 -1
  11. package/dist/{ffmpeg-args-common-c3p-nWtU.mjs → frame-dropper-CwkBTPGV.mjs} +22 -22
  12. package/dist/motion-wasm/index.js +1 -1
  13. package/dist/motion-wasm/index.mjs +1 -1
  14. package/dist/pipeline-runner/index.js +1 -1
  15. package/dist/pipeline-runner/index.mjs +1 -1
  16. package/dist/recorder/index.js +24 -4
  17. package/dist/recorder/index.mjs +24 -4
  18. package/dist/stream-broker/_stub.js +35 -35
  19. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-w2pP0MYO.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D08qJTw_.mjs} +3 -3
  20. package/dist/stream-broker/{hostInit-BUEbBinp.mjs → hostInit-CGnMNtPl.mjs} +3 -3
  21. package/dist/stream-broker/index.js +487 -458
  22. package/dist/stream-broker/index.mjs +477 -448
  23. package/dist/stream-broker/remoteEntry.js +1 -1
  24. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-CYmHXacX.js → MaskShapeCanvas-DI4BY7W2-CW4Qu4V6.js} +1 -1
  25. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-B-BkPQvX.js → MotionZonesSettings-NcxxQN8r-CHJ39tfi.js} +1 -1
  26. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DC3Jw85p.js → PrivacyMaskSettings-APgPLF7p-dInJ7kcz.js} +1 -1
  27. package/embed-dist/assets/{index-B0IQgci0.js → index-CKpVWKSG.js} +4 -4
  28. package/embed-dist/index.html +1 -1
  29. package/package.json +10 -10
  30. package/dist/audio-codec-nodeav/index.js +0 -310
  31. package/dist/audio-codec-nodeav/index.mjs +0 -305
  32. package/dist/codec-runtime-BOk-13PN.js +0 -202
  33. package/dist/codec-runtime-BsqlEjPi.mjs +0 -197
  34. package/dist/{ffmpeg-args-common-CASoNt42.js → frame-dropper-7RTo_YyG.js} +21 -21
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
6
- const require_dist = require("../dist-BgoBMCez.js");
6
+ const require_dist = require("../dist-BiP1gPeY.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,5 +1,5 @@
1
1
  import { t as __require } from "../chunk-BdkLduGY.mjs";
2
- import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-7Yx2dmuV.mjs";
2
+ import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-tzhTTRq4.mjs";
3
3
  import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
@@ -7214,7 +7214,21 @@ var StorageLocationDeclarationSchema = object({
7214
7214
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
7215
7215
  * configure the primary location.
7216
7216
  */
7217
- defaultsTo: string().optional()
7217
+ defaultsTo: string().optional(),
7218
+ /**
7219
+ * Which node root the seeded `<id>:default` instance is placed under on a
7220
+ * FRESH install:
7221
+ * - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
7222
+ * the appData volume. Right for small/durable data (backups, logs, models).
7223
+ * - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
7224
+ * env is set, else falls back to the data root. Right for bulky, hot media
7225
+ * (recordings, event media) that should stay off the appData disk.
7226
+ *
7227
+ * Only affects the seeded default's `basePath`; operators can repoint any
7228
+ * location afterwards, and a `defaultsTo` slot inherits its parent's root
7229
+ * regardless of this field. Absent (the common case) is treated as `'data'`.
7230
+ */
7231
+ defaultRoot: _enum(["data", "media"]).optional()
7218
7232
  });
7219
7233
  var DecoderStatsSchema = object({
7220
7234
  inputFps: number(),
@@ -8572,6 +8586,10 @@ var RtspRestreamEntrySchema = object({
8572
8586
  var BrokerRtspClientSchema = object({
8573
8587
  sessionId: string(),
8574
8588
  remoteAddr: string(),
8589
+ /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
8590
+ * null/absent when the client sent none. Lets the UI label a consumer by
8591
+ * purpose. Optional so a client built against an older schema stays valid. */
8592
+ userAgent: string().nullish(),
8575
8593
  playing: boolean(),
8576
8594
  muted: boolean(),
8577
8595
  connectedAt: number(),
@@ -12844,6 +12862,7 @@ var audioCodecCapability = {
12844
12862
  name: "audio-codec",
12845
12863
  scope: "system",
12846
12864
  mode: "singleton",
12865
+ preferredProvider: "audio-codec-ffmpeg",
12847
12866
  methods: {
12848
12867
  /** Probe the local runtime and return the supported codec matrix. */
12849
12868
  listSupportedCodecs: method(_void(), array(AudioCodecInfoSchema).readonly()),
@@ -7214,7 +7214,21 @@ var StorageLocationDeclarationSchema = object({
7214
7214
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
7215
7215
  * configure the primary location.
7216
7216
  */
7217
- defaultsTo: string().optional()
7217
+ defaultsTo: string().optional(),
7218
+ /**
7219
+ * Which node root the seeded `<id>:default` instance is placed under on a
7220
+ * FRESH install:
7221
+ * - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
7222
+ * the appData volume. Right for small/durable data (backups, logs, models).
7223
+ * - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
7224
+ * env is set, else falls back to the data root. Right for bulky, hot media
7225
+ * (recordings, event media) that should stay off the appData disk.
7226
+ *
7227
+ * Only affects the seeded default's `basePath`; operators can repoint any
7228
+ * location afterwards, and a `defaultsTo` slot inherits its parent's root
7229
+ * regardless of this field. Absent (the common case) is treated as `'data'`.
7230
+ */
7231
+ defaultRoot: _enum(["data", "media"]).optional()
7218
7232
  });
7219
7233
  var DecoderStatsSchema = object({
7220
7234
  inputFps: number(),
@@ -8572,6 +8586,10 @@ var RtspRestreamEntrySchema = object({
8572
8586
  var BrokerRtspClientSchema = object({
8573
8587
  sessionId: string(),
8574
8588
  remoteAddr: string(),
8589
+ /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
8590
+ * null/absent when the client sent none. Lets the UI label a consumer by
8591
+ * purpose. Optional so a client built against an older schema stays valid. */
8592
+ userAgent: string().nullish(),
8575
8593
  playing: boolean(),
8576
8594
  muted: boolean(),
8577
8595
  connectedAt: number(),
@@ -12844,6 +12862,7 @@ var audioCodecCapability = {
12844
12862
  name: "audio-codec",
12845
12863
  scope: "system",
12846
12864
  mode: "singleton",
12865
+ preferredProvider: "audio-codec-ffmpeg",
12847
12866
  methods: {
12848
12867
  /** Probe the local runtime and return the supported codec matrix. */
12849
12868
  listSupportedCodecs: method(_void(), array(AudioCodecInfoSchema).readonly()),
@@ -1,24 +1,3 @@
1
- //#region src/stream-broker/stream-broker/frame-dropper.ts
2
- var FrameDropper = class {
3
- intervalMs;
4
- lastPassedAt = -Infinity;
5
- constructor(maxFps) {
6
- this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
7
- }
8
- shouldKeep() {
9
- if (this.intervalMs === 0) return true;
10
- const now = Date.now();
11
- if (now - this.lastPassedAt >= this.intervalMs) {
12
- this.lastPassedAt = now;
13
- return true;
14
- }
15
- return false;
16
- }
17
- setMaxFps(maxFps) {
18
- this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
19
- }
20
- };
21
- //#endregion
22
1
  //#region src/stream-broker/stream-broker/ffmpeg-args-common.ts
23
2
  var AUDIO_ENCODER_BY_CODEC = {
24
3
  opus: "libopus",
@@ -48,4 +27,25 @@ function logBannerArgs(level) {
48
27
  ];
49
28
  }
50
29
  //#endregion
51
- export { logBannerArgs as n, FrameDropper as r, audioEncoderArgs as t };
30
+ //#region src/stream-broker/stream-broker/frame-dropper.ts
31
+ var FrameDropper = class {
32
+ intervalMs;
33
+ lastPassedAt = -Infinity;
34
+ constructor(maxFps) {
35
+ this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
36
+ }
37
+ shouldKeep() {
38
+ if (this.intervalMs === 0) return true;
39
+ const now = Date.now();
40
+ if (now - this.lastPassedAt >= this.intervalMs) {
41
+ this.lastPassedAt = now;
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+ setMaxFps(maxFps) {
47
+ this.intervalMs = maxFps > 0 ? 1e3 / maxFps : 0;
48
+ }
49
+ };
50
+ //#endregion
51
+ export { audioEncoderArgs as n, logBannerArgs as r, FrameDropper as t };
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BgoBMCez.js");
5
+ const require_dist = require("../dist-BiP1gPeY.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_path = require("node:path");
8
8
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -1,4 +1,4 @@
1
- import { B as BaseAddon, J as hydrateSchema, U as DeviceType, k as motionDetectionCapability, w as evaluateZoneRules } from "../dist-7Yx2dmuV.mjs";
1
+ import { B as BaseAddon, J as hydrateSchema, U as DeviceType, k as motionDetectionCapability, w as evaluateZoneRules } from "../dist-tzhTTRq4.mjs";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  //#region src/motion-wasm/wasm-motion-detector.ts
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BgoBMCez.js");
5
+ const require_dist = require("../dist-BiP1gPeY.js");
6
6
  let _camstack_shm_ring = require("@camstack/shm-ring");
7
7
  //#region src/pipeline-runner/frame-queue.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { A as nodePin, B as BaseAddon, M as pipelineRunnerCapability, S as defineCustomActions, W as EventCategory, X as makeSourceBrokerId, at as lazy, ct as object, nt as array, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, y as customAction, z as errMsg } from "../dist-7Yx2dmuV.mjs";
1
+ import { A as nodePin, B as BaseAddon, M as pipelineRunnerCapability, S as defineCustomActions, W as EventCategory, X as makeSourceBrokerId, at as lazy, ct as object, nt as array, q as createEvent, rt as boolean, st as number, tt as _enum, ut as string, y as customAction, z as errMsg } from "../dist-tzhTTRq4.mjs";
2
2
  import { FrameRingReaderCache } from "@camstack/shm-ring";
3
3
  //#region src/pipeline-runner/frame-queue.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
2
- const require_dist = require("../dist-BgoBMCez.js");
2
+ const require_dist = require("../dist-BiP1gPeY.js");
3
3
  let node_fs = require("node:fs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_model_download_service_C_IHWnXx.__toESM(node_path);
@@ -965,6 +965,8 @@ function buildPassthroughArgs(a) {
965
965
  return [
966
966
  "-rtsp_transport",
967
967
  "tcp",
968
+ "-user_agent",
969
+ "recorder",
968
970
  "-i",
969
971
  a.rtspUrl,
970
972
  "-map",
@@ -1004,6 +1006,8 @@ var RESTART_MAX_MS = 1e4;
1004
1006
  /** A process that ran at least this long is "stable" → reset the restart count,
1005
1007
  * so sporadic blips over a long lifetime never accumulate into a give-up. */
1006
1008
  var STABLE_RUN_MS = 3e4;
1009
+ /** Grace between the SIGTERM in stop() and the escalated SIGKILL. */
1010
+ var KILL_GRACE_MS = 500;
1007
1011
  /** Supervises one passthrough ffmpeg for a single (camera, profile). */
1008
1012
  var SegmentWriter = class {
1009
1013
  cfg;
@@ -1068,8 +1072,17 @@ var SegmentWriter = class {
1068
1072
  clearTimeout(this.restartTimer);
1069
1073
  this.restartTimer = null;
1070
1074
  }
1071
- this.proc?.kill();
1075
+ const proc = this.proc;
1072
1076
  this.proc = null;
1077
+ if (!proc) return;
1078
+ try {
1079
+ proc.kill("SIGTERM");
1080
+ } catch {}
1081
+ setTimeout(() => {
1082
+ if (proc.exitCode === null && proc.signalCode === null) try {
1083
+ proc.kill("SIGKILL");
1084
+ } catch {}
1085
+ }, KILL_GRACE_MS).unref?.();
1073
1086
  }
1074
1087
  };
1075
1088
  //#endregion
@@ -1893,12 +1906,19 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
1893
1906
  statBytes: async (absPath) => (await node_fs.promises.stat(absPath)).size,
1894
1907
  moveFile: async (fromAbs, toAbs) => {
1895
1908
  await node_fs.promises.mkdir(node_path.default.dirname(toAbs), { recursive: true });
1896
- await node_fs.promises.rename(fromAbs, toAbs);
1909
+ try {
1910
+ await node_fs.promises.rename(fromAbs, toAbs);
1911
+ } catch (err) {
1912
+ if (err.code !== "EXDEV") throw err;
1913
+ await node_fs.promises.copyFile(fromAbs, toAbs);
1914
+ await node_fs.promises.unlink(fromAbs);
1915
+ }
1897
1916
  },
1898
1917
  logger: { warn: (message, extras) => this.ctx.logger.warn(message, { meta: { extras } }) }
1899
1918
  };
1900
1919
  this.segmentStore = new SegmentStore(segmentStoreDeps);
1901
- const tmpRoot = node_path.default.join(this.ctx.dataDir, "rec-tmp");
1920
+ const mediaRoot = process.env.CAMSTACK_MEDIA_ROOT?.trim();
1921
+ const tmpRoot = node_path.default.join(mediaRoot ?? this.ctx.dataDir, "rec-tmp");
1902
1922
  const brokerHandle = this.ctx.useCapability("stream-broker", brokerScope(BROKER_HOST_NODE_ID));
1903
1923
  this.controller = new RecordingController({
1904
1924
  api: this.ctx.api,
@@ -1,4 +1,4 @@
1
- import { $ as selectAssignedProfileSlots, B as BaseAddon, F as storageEvictableCapability, J as hydrateSchema, K as createDurableState, N as recordingCapability, O as migrateConfigToBands, U as DeviceType, W as EventCategory, d as RecordingConfigSchema, lt as record, ut as string, z as errMsg } from "../dist-7Yx2dmuV.mjs";
1
+ import { $ as selectAssignedProfileSlots, B as BaseAddon, F as storageEvictableCapability, J as hydrateSchema, K as createDurableState, N as recordingCapability, O as migrateConfigToBands, U as DeviceType, W as EventCategory, d as RecordingConfigSchema, lt as record, ut as string, z as errMsg } from "../dist-tzhTTRq4.mjs";
2
2
  import { t as createFileDataPlaneHandler } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
3
3
  import { promises } from "node:fs";
4
4
  import path from "node:path";
@@ -964,6 +964,8 @@ function buildPassthroughArgs(a) {
964
964
  return [
965
965
  "-rtsp_transport",
966
966
  "tcp",
967
+ "-user_agent",
968
+ "recorder",
967
969
  "-i",
968
970
  a.rtspUrl,
969
971
  "-map",
@@ -1003,6 +1005,8 @@ var RESTART_MAX_MS = 1e4;
1003
1005
  /** A process that ran at least this long is "stable" → reset the restart count,
1004
1006
  * so sporadic blips over a long lifetime never accumulate into a give-up. */
1005
1007
  var STABLE_RUN_MS = 3e4;
1008
+ /** Grace between the SIGTERM in stop() and the escalated SIGKILL. */
1009
+ var KILL_GRACE_MS = 500;
1006
1010
  /** Supervises one passthrough ffmpeg for a single (camera, profile). */
1007
1011
  var SegmentWriter = class {
1008
1012
  cfg;
@@ -1067,8 +1071,17 @@ var SegmentWriter = class {
1067
1071
  clearTimeout(this.restartTimer);
1068
1072
  this.restartTimer = null;
1069
1073
  }
1070
- this.proc?.kill();
1074
+ const proc = this.proc;
1071
1075
  this.proc = null;
1076
+ if (!proc) return;
1077
+ try {
1078
+ proc.kill("SIGTERM");
1079
+ } catch {}
1080
+ setTimeout(() => {
1081
+ if (proc.exitCode === null && proc.signalCode === null) try {
1082
+ proc.kill("SIGKILL");
1083
+ } catch {}
1084
+ }, KILL_GRACE_MS).unref?.();
1072
1085
  }
1073
1086
  };
1074
1087
  //#endregion
@@ -1892,12 +1905,19 @@ var RecorderV2Addon = class extends BaseAddon {
1892
1905
  statBytes: async (absPath) => (await promises.stat(absPath)).size,
1893
1906
  moveFile: async (fromAbs, toAbs) => {
1894
1907
  await promises.mkdir(path.dirname(toAbs), { recursive: true });
1895
- await promises.rename(fromAbs, toAbs);
1908
+ try {
1909
+ await promises.rename(fromAbs, toAbs);
1910
+ } catch (err) {
1911
+ if (err.code !== "EXDEV") throw err;
1912
+ await promises.copyFile(fromAbs, toAbs);
1913
+ await promises.unlink(fromAbs);
1914
+ }
1896
1915
  },
1897
1916
  logger: { warn: (message, extras) => this.ctx.logger.warn(message, { meta: { extras } }) }
1898
1917
  };
1899
1918
  this.segmentStore = new SegmentStore(segmentStoreDeps);
1900
- const tmpRoot = path.join(this.ctx.dataDir, "rec-tmp");
1919
+ const mediaRoot = process.env.CAMSTACK_MEDIA_ROOT?.trim();
1920
+ const tmpRoot = path.join(mediaRoot ?? this.ctx.dataDir, "rec-tmp");
1901
1921
  const brokerHandle = this.ctx.useCapability("stream-broker", brokerScope(BROKER_HOST_NODE_ID));
1902
1922
  this.controller = new RecordingController({
1903
1923
  api: this.ctx.api,
@@ -307,7 +307,7 @@ function ae({ outputArgs: e, onChange: t }) {
307
307
  }
308
308
  //#endregion
309
309
  //#region src/stream-broker/widgets/FfmpegParamsField.tsx
310
- var N = [
310
+ var oe = [
311
311
  {
312
312
  group: "Demuxer",
313
313
  label: "-thread_queue_size 1024",
@@ -350,7 +350,7 @@ var N = [
350
350
  args: ["-stimeout", "5000000"],
351
351
  description: "Socket I/O timeout (microseconds, 5s)"
352
352
  }
353
- ], oe = [
353
+ ], N = [
354
354
  {
355
355
  group: "Muxer",
356
356
  label: "-flush_packets 1",
@@ -733,7 +733,7 @@ function ce({ config: t, value: n, onSave: i }) {
733
733
  }), /* @__PURE__ */ g(B, {
734
734
  id: `${t.fieldKey}-input-picker`,
735
735
  disabled: !1,
736
- catalogue: N,
736
+ catalogue: oe,
737
737
  onPick: (e) => D("input", e)
738
738
  })]
739
739
  }), /* @__PURE__ */ g("textarea", {
@@ -765,7 +765,7 @@ function ce({ config: t, value: n, onSave: i }) {
765
765
  }), /* @__PURE__ */ g(B, {
766
766
  id: `${t.fieldKey}-output-picker`,
767
767
  disabled: !1,
768
- catalogue: oe,
768
+ catalogue: N,
769
769
  onPick: (e) => D("output", e)
770
770
  })]
771
771
  }), /* @__PURE__ */ g("textarea", {
@@ -954,16 +954,16 @@ function B({ id: e, disabled: n, catalogue: i, onPick: a }) {
954
954
  //#endregion
955
955
  //#region src/stream-broker/widgets/format-ua.ts
956
956
  var V = 24;
957
- function H(e) {
957
+ function de(e) {
958
958
  return /\bEdg\b|\bEdgA\b|\bEdgiOS\b|Edge\//.test(e) ? "Edge" : /\bOPR\b|Opera/.test(e) ? "Opera" : /Firefox\/|\bFxiOS\b/.test(e) ? "Firefox" : /Chrome\/|\bCriOS\b/.test(e) ? "Chrome" : /Safari\//.test(e) ? "Safari" : null;
959
959
  }
960
- function de(e) {
960
+ function fe(e) {
961
961
  return /iPhone|iPad|iPod|\biOS\b|CriOS|FxiOS/.test(e) ? "iOS" : /Mac OS X|Macintosh/.test(e) ? "macOS" : /Windows/.test(e) ? "Windows" : /Android/.test(e) ? "Android" : /Linux/.test(e) ? "Linux" : null;
962
962
  }
963
- function fe(e) {
963
+ function H(e) {
964
964
  let t = e.trim();
965
965
  if (t.length === 0) return "";
966
- let n = H(t), r = de(t);
966
+ let n = de(t), r = fe(t);
967
967
  return n && r ? `${n}/${r}` : n || r || (t.length > V ? `${t.slice(0, V)}…` : t);
968
968
  }
969
969
  //#endregion
@@ -982,7 +982,7 @@ var U = {
982
982
  "high",
983
983
  "mid",
984
984
  "low"
985
- ], me = {
985
+ ], G = {
986
986
  alexa: "bg-blue-500/15 text-blue-400",
987
987
  homekit: "bg-purple-500/15 text-purple-400",
988
988
  "webrtc-browser": "bg-emerald-500/15 text-emerald-400",
@@ -995,7 +995,7 @@ var U = {
995
995
  snapshot: "bg-cyan-500/15 text-cyan-400",
996
996
  warmup: "bg-foreground-subtle/10 text-foreground-subtle",
997
997
  unknown: "bg-foreground-subtle/10 text-foreground-subtle"
998
- }, G = {
998
+ }, K = {
999
999
  alexa: "ALEXA",
1000
1000
  homekit: "HOMEKIT",
1001
1001
  "webrtc-browser": "BROWSER",
@@ -1009,25 +1009,25 @@ var U = {
1009
1009
  warmup: "WARMUP",
1010
1010
  unknown: "—"
1011
1011
  };
1012
+ function me(e) {
1013
+ return K[e] ?? e.toUpperCase().slice(0, 8);
1014
+ }
1012
1015
  function he(e) {
1013
- return G[e] ?? e.toUpperCase().slice(0, 8);
1016
+ return K[e] ?? e;
1014
1017
  }
1015
1018
  function ge(e) {
1016
- return G[e] ?? e;
1017
- }
1018
- function _e(e) {
1019
1019
  let t = e.config ?? {};
1020
1020
  return e.deviceId === void 0 ? /* @__PURE__ */ g("div", {
1021
1021
  className: "rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",
1022
1022
  children: "StreamBrokerPanel requires a deviceId"
1023
- }) : /* @__PURE__ */ g(ve, {
1023
+ }) : /* @__PURE__ */ g(_e, {
1024
1024
  deviceId: e.deviceId,
1025
1025
  title: t.title,
1026
1026
  variant: t.variant,
1027
1027
  hideEmpty: t.hideEmpty
1028
1028
  });
1029
1029
  }
1030
- function ve({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hideEmpty: r = !1 }) {
1030
+ function _e({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hideEmpty: r = !1 }) {
1031
1031
  f(["streamBroker", "listAllProfileSlots"], [
1032
1032
  "device.streams-registered",
1033
1033
  "device.streams-unregistered",
@@ -1048,7 +1048,7 @@ function ve({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hid
1048
1048
  let d = new Set(a.map((e) => e.sourceCamStreamId).filter((e) => e !== null)), p = (c ?? []).filter((e) => !d.has(e.camStreamId)), m = a.length > 0, v = p.length > 0;
1049
1049
  return r && !m && !v ? null : n === "compact" ? m ? /* @__PURE__ */ g("div", {
1050
1050
  className: "divide-y divide-border/30",
1051
- children: a.map((e) => /* @__PURE__ */ g(q, {
1051
+ children: a.map((e) => /* @__PURE__ */ g(J, {
1052
1052
  slot: e,
1053
1053
  camStreams: l,
1054
1054
  compact: !0
@@ -1071,7 +1071,7 @@ function ve({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hid
1071
1071
  }),
1072
1072
  m ? /* @__PURE__ */ g("div", {
1073
1073
  className: "divide-y divide-border/30",
1074
- children: a.map((e) => /* @__PURE__ */ g(q, {
1074
+ children: a.map((e) => /* @__PURE__ */ g(J, {
1075
1075
  slot: e,
1076
1076
  camStreams: l,
1077
1077
  compact: !1
@@ -1083,7 +1083,7 @@ function ve({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hid
1083
1083
  children: "No profile slots assigned for this device"
1084
1084
  })]
1085
1085
  }),
1086
- v && /* @__PURE__ */ g(ye, {
1086
+ v && /* @__PURE__ */ g(ve, {
1087
1087
  deviceId: e,
1088
1088
  camStreamIds: p.map((e) => e.camStreamId),
1089
1089
  camStreams: l
@@ -1091,7 +1091,7 @@ function ve({ deviceId: e, title: t = "Stream Brokers", variant: n = "full", hid
1091
1091
  ]
1092
1092
  });
1093
1093
  }
1094
- function ye({ deviceId: e, camStreamIds: t, camStreams: n }) {
1094
+ function ve({ deviceId: e, camStreamIds: t, camStreams: n }) {
1095
1095
  let [i, a] = r(!1);
1096
1096
  return /* @__PURE__ */ _("div", {
1097
1097
  className: "border-t border-border/50",
@@ -1108,7 +1108,7 @@ function ye({ deviceId: e, camStreamIds: t, camStreams: n }) {
1108
1108
  })]
1109
1109
  }), i && /* @__PURE__ */ g("div", {
1110
1110
  className: "divide-y divide-border/30",
1111
- children: t.map((t) => /* @__PURE__ */ g(be, {
1111
+ children: t.map((t) => /* @__PURE__ */ g(ye, {
1112
1112
  deviceId: e,
1113
1113
  camStreamId: t,
1114
1114
  camStreams: n
@@ -1116,7 +1116,7 @@ function ye({ deviceId: e, camStreamIds: t, camStreams: n }) {
1116
1116
  })]
1117
1117
  });
1118
1118
  }
1119
- function be({ deviceId: e, camStreamId: t, camStreams: n }) {
1119
+ function ye({ deviceId: e, camStreamId: t, camStreams: n }) {
1120
1120
  let r = x(e, t), i = c("stream-broker.metrics-snapshot", (e) => e.brokerId === r)?.stats, a = n.get(t), o = i?.status === "streaming";
1121
1121
  return /* @__PURE__ */ _("div", {
1122
1122
  className: "px-4 py-2",
@@ -1191,7 +1191,7 @@ function be({ deviceId: e, camStreamId: t, camStreams: n }) {
1191
1191
  })]
1192
1192
  });
1193
1193
  }
1194
- function K(e, t) {
1194
+ function q(e, t) {
1195
1195
  let n = (e) => {
1196
1196
  if (!e || typeof e != "object") return;
1197
1197
  let r = e;
@@ -1212,13 +1212,13 @@ function K(e, t) {
1212
1212
  if (t !== void 0) return t;
1213
1213
  }
1214
1214
  }
1215
- function q({ slot: e, camStreams: t, compact: n }) {
1215
+ function J({ slot: e, camStreams: t, compact: n }) {
1216
1216
  let r = y(), a = c("stream-broker.metrics-snapshot", (t) => t.brokerId === e.brokerId)?.stats;
1217
1217
  f(["streamBroker", "listClients"], ["stream-broker.metrics-snapshot"]);
1218
1218
  let { data: s } = l({ brokerId: e.brokerId }, {
1219
1219
  enabled: a?.status === "streaming",
1220
1220
  staleTime: 2e3
1221
- }), { data: u } = d({ deviceId: e.deviceId }, { staleTime: 15e3 }), m = e.sourceCamStreamId ?? e.profile, h = !!(K(u, `preBufferEnabled:${m}`) ?? !0), b = K(u, `preBufferSec:${m}`), x = o({ onSuccess: () => {
1221
+ }), { data: u } = d({ deviceId: e.deviceId }, { staleTime: 15e3 }), m = e.sourceCamStreamId ?? e.profile, h = !!(q(u, `preBufferEnabled:${m}`) ?? !0), b = q(u, `preBufferSec:${m}`), x = o({ onSuccess: () => {
1222
1222
  r.invalidateQueries({ queryKey: [["streamBroker", "getDeviceSettingsContribution"]] });
1223
1223
  } }), S = (t) => {
1224
1224
  x.mutate({
@@ -1506,12 +1506,12 @@ function q({ slot: e, camStreams: t, compact: n }) {
1506
1506
  ]
1507
1507
  });
1508
1508
  }
1509
- var xe = 280, J = 120;
1509
+ var be = 280, xe = 120;
1510
1510
  function Y({ trigger: t, content: n, widthClass: a }) {
1511
1511
  let o = e(null), s = e(null), [c, l] = r(!1), [u, d] = r(!1), f = i(() => {
1512
1512
  s.current &&= (clearTimeout(s.current), null);
1513
1513
  }, []), p = i(() => {
1514
- f(), s.current = setTimeout(() => l(!1), J);
1514
+ f(), s.current = setTimeout(() => l(!1), xe);
1515
1515
  }, [f]);
1516
1516
  return /* @__PURE__ */ _("span", {
1517
1517
  ref: o,
@@ -1521,7 +1521,7 @@ function Y({ trigger: t, content: n, widthClass: a }) {
1521
1521
  let e = o.current;
1522
1522
  if (e) {
1523
1523
  let t = e.getBoundingClientRect(), n = window.innerHeight - t.bottom, r = t.top;
1524
- d(n < xe && r > n);
1524
+ d(n < be && r > n);
1525
1525
  }
1526
1526
  l(!0);
1527
1527
  }, [f]),
@@ -1600,7 +1600,7 @@ function Se({ clients: e, encodedCount: t, onKill: n }) {
1600
1600
  return /* @__PURE__ */ g(Q, {
1601
1601
  kind: "RTSP",
1602
1602
  kindClass: "bg-warning/10 text-warning",
1603
- tag: e.remoteAddr,
1603
+ tag: e.userAgent ? H(e.userAgent) : e.remoteAddr,
1604
1604
  state: {
1605
1605
  text: i + (e.muted ? " · muted" : ""),
1606
1606
  className: a
@@ -1614,11 +1614,11 @@ function Se({ clients: e, encodedCount: t, onKill: n }) {
1614
1614
  }, `r-${e.sessionId}`);
1615
1615
  }),
1616
1616
  s.map((e) => {
1617
- let t = e.attribution, n = me[t.kind] ?? "bg-foreground-subtle/10 text-foreground-subtle", r = t.sessionId ? t.sessionId.slice(0, 8) : null, i = [];
1618
- t.label && i.push(t.label), r && i.push(`sess ${r}`), t.userId && i.push(`user ${t.userId.slice(0, 8)}`), t.remoteAddr && i.push(`@${t.remoteAddr}`), t.userAgent && i.push(fe(t.userAgent));
1619
- let a = i.length > 0 ? i.join(" · ") : ge(t.kind), o = [];
1617
+ let t = e.attribution, n = G[t.kind] ?? "bg-foreground-subtle/10 text-foreground-subtle", r = t.sessionId ? t.sessionId.slice(0, 8) : null, i = [];
1618
+ t.label && i.push(t.label), r && i.push(`sess ${r}`), t.userId && i.push(`user ${t.userId.slice(0, 8)}`), t.remoteAddr && i.push(`@${t.remoteAddr}`), t.userAgent && i.push(H(t.userAgent));
1619
+ let a = i.length > 0 ? i.join(" · ") : he(t.kind), o = [];
1620
1620
  return t.media && o.push({ text: t.media }), t.targetCodec && o.push(t.targetCodec), t.transport && o.push(t.transport), o.push(`${e.packetsDelivered} pkts`), /* @__PURE__ */ g(Q, {
1621
- kind: he(t.kind),
1621
+ kind: me(t.kind),
1622
1622
  kindClass: n,
1623
1623
  tag: a,
1624
1624
  metrics: o,
@@ -1794,7 +1794,7 @@ function Te({ count: e, clients: t, onKill: n }) {
1794
1794
  className: "flex items-center justify-between gap-2",
1795
1795
  children: [/* @__PURE__ */ g("span", {
1796
1796
  className: "text-foreground font-mono",
1797
- children: e.remoteAddr
1797
+ children: e.userAgent ? H(e.userAgent) : e.remoteAddr
1798
1798
  }), /* @__PURE__ */ _("span", {
1799
1799
  className: "flex items-center gap-1 shrink-0",
1800
1800
  children: [/* @__PURE__ */ _("span", {
@@ -1829,7 +1829,7 @@ function Te({ count: e, clients: t, onKill: n }) {
1829
1829
  //#endregion
1830
1830
  //#region src/stream-broker/widgets/index.tsx
1831
1831
  var Ee = {
1832
- "stream-broker-panel": _e,
1832
+ "stream-broker-panel": ge,
1833
1833
  "ffmpeg-params": ue
1834
1834
  };
1835
1835
  //#endregion
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.1.12",
6
+ version: "1.1.13",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_stream_broker_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.1.15",
21
+ version: "1.1.16",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.1.14",
36
+ version: "1.1.15",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.1.15",
39
+ version: "1.1.16",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.1.12",
48
+ version: "1.1.13",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.1.14",
84
+ version: "1.1.15",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,