@camstack/addon-provider-rtsp 0.1.14 → 0.1.15

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/dist/addon.js CHANGED
@@ -1,40 +1,14 @@
1
1
  "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/addon.ts
21
- var addon_exports = {};
22
- __export(addon_exports, {
23
- RtspProviderAddon: () => RtspProviderAddon
24
- });
25
- module.exports = __toCommonJS(addon_exports);
26
- var import_types2 = require("@camstack/types");
27
-
28
- // src/rtsp-camera.ts
29
- var import_zod = require("zod");
30
- var import_types = require("@camstack/types");
31
- var streamEntrySchema = import_zod.z.object({
32
- id: import_zod.z.string().regex(/^stream-\d+$/).describe("Stable slot id (stream-1/2/3)"),
33
- url: import_zod.z.string().describe("RTSP stream URL"),
34
- profileHint: import_zod.z.enum(["high", "mid", "low"]).optional().describe("Provider-suggested profile \u2014 advisory only"),
35
- resolution: import_zod.z.object({ width: import_zod.z.number().int().positive(), height: import_zod.z.number().int().positive() }).optional().describe("Probed resolution, if known")
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const types = require("@camstack/types");
4
+ const zod = require("zod");
5
+ const streamEntrySchema = zod.z.object({
6
+ id: zod.z.string().regex(/^stream-\d+$/).describe("Stable slot id (stream-1/2/3)"),
7
+ url: zod.z.string().describe("RTSP stream URL"),
8
+ profileHint: zod.z.enum(["high", "mid", "low"]).optional().describe("Provider-suggested profile advisory only"),
9
+ resolution: zod.z.object({ width: zod.z.number().int().positive(), height: zod.z.number().int().positive() }).optional().describe("Probed resolution, if known")
36
10
  });
37
- var streamsField = import_zod.z.preprocess(
11
+ const streamsField = zod.z.preprocess(
38
12
  (raw) => {
39
13
  if (!Array.isArray(raw)) return raw;
40
14
  return raw.map((entry, i) => {
@@ -51,30 +25,30 @@ var streamsField = import_zod.z.preprocess(
51
25
  return entry;
52
26
  });
53
27
  },
54
- import_zod.z.array(streamEntrySchema).min(1)
28
+ zod.z.array(streamEntrySchema).min(1)
55
29
  );
56
- var rtspCameraSchema = import_zod.z.object({
30
+ const rtspCameraSchema = zod.z.object({
57
31
  // `name` is now base meta (DB column on `device-meta`, not in this
58
32
  // hardware-config schema). Read via `this.name` (resolved by
59
33
  // `BaseDevice` from `ctx.deviceMeta.name`); mutated via
60
34
  // `kernel.devices.setName(id, name)`.
61
35
  streams: streamsField.describe("Published RTSP streams"),
62
- snapshotUrl: import_zod.z.string().default("").describe("Snapshot URL (optional)"),
63
- username: import_zod.z.string().default("").describe("Username"),
64
- password: import_zod.z.string().default("").describe("Password")
36
+ snapshotUrl: zod.z.string().default("").describe("Snapshot URL (optional)"),
37
+ username: zod.z.string().default("").describe("Username"),
38
+ password: zod.z.string().default("").describe("Password")
65
39
  });
66
- var legacyStreamSchema = import_zod.z.object({
67
- label: import_zod.z.enum(["high", "mid", "low"]),
68
- url: import_zod.z.string()
40
+ const legacyStreamSchema = zod.z.object({
41
+ label: zod.z.enum(["high", "mid", "low"]),
42
+ url: zod.z.string()
69
43
  });
70
- var RtspCamera = class extends import_types.BaseDevice {
71
- type = import_types.DeviceType.Camera;
44
+ class RtspCamera extends types.BaseDevice {
45
+ type = types.DeviceType.Camera;
72
46
  features = [];
73
47
  constructor(ctx) {
74
48
  super(
75
49
  ctx,
76
50
  rtspCameraSchema,
77
- { type: import_types.DeviceType.Camera }
51
+ { type: types.DeviceType.Camera }
78
52
  );
79
53
  this.migrateLegacyStreamsIfNeeded(ctx.persistedConfig ?? {});
80
54
  this.registerSnapshotProvider();
@@ -135,7 +109,7 @@ var RtspCamera = class extends import_types.BaseDevice {
135
109
  */
136
110
  async onActivate() {
137
111
  await this.publishToBroker().catch((err) => {
138
- this.ctx.logger.warn("publishToBroker on activate failed \u2014 will retry on broker ready", {
112
+ this.ctx.logger.warn("publishToBroker on activate failed will retry on broker ready", {
139
113
  meta: { error: err instanceof Error ? err.message : String(err) }
140
114
  });
141
115
  });
@@ -210,7 +184,7 @@ var RtspCamera = class extends import_types.BaseDevice {
210
184
  {
211
185
  id: "streams",
212
186
  title: "RTSP Streams",
213
- description: "Provide 1\u20133 RTSP URLs for this camera. After save, each URL is probed and the stream-broker assigns the (high / mid / low) profiles by resolution (highest pixel count \u2192 high). Input slot order in this form does not affect the broker profile mapping.",
187
+ description: "Provide 1–3 RTSP URLs for this camera. After save, each URL is probed and the stream-broker assigns the (high / mid / low) profiles by resolution (highest pixel count high). Input slot order in this form does not affect the broker profile mapping.",
214
188
  columns: 1,
215
189
  fields: [
216
190
  {
@@ -250,7 +224,7 @@ var RtspCamera = class extends import_types.BaseDevice {
250
224
  }
251
225
  ]
252
226
  };
253
- return (0, import_types.hydrateSchema)(schema, this.collectFormValues());
227
+ return types.hydrateSchema(schema, this.collectFormValues());
254
228
  }
255
229
  /**
256
230
  * Project stored config into the flat UI keys referenced by the schema.
@@ -299,7 +273,7 @@ var RtspCamera = class extends import_types.BaseDevice {
299
273
  const streamProbe = this.ctx.streamProbe;
300
274
  if (!streamProbe) {
301
275
  this.ctx.logger.warn(
302
- "[rtsp-edit] ctx.streamProbe unavailable \u2014 falling back to input-order profile assignment",
276
+ "[rtsp-edit] ctx.streamProbe unavailable falling back to input-order profile assignment",
303
277
  { meta: { streamCount: urls.length } }
304
278
  );
305
279
  const sequence = urls.length === 1 ? ["high"] : urls.length === 2 ? ["high", "low"] : ["high", "mid", "low"];
@@ -316,15 +290,15 @@ var RtspCamera = class extends import_types.BaseDevice {
316
290
  return { url, metadata };
317
291
  } catch (err) {
318
292
  this.ctx.logger.warn(
319
- "[rtsp-edit] probe failed \u2014 landing in the lowest tier via classifyStreams",
320
- { meta: { url: (0, import_types.maskUrlCredentials)(url), error: err instanceof Error ? err.message : String(err) } }
293
+ "[rtsp-edit] probe failed landing in the lowest tier via classifyStreams",
294
+ { meta: { url: types.maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } }
321
295
  );
322
296
  const emptyMetadata = {};
323
297
  return { url, metadata: emptyMetadata };
324
298
  }
325
299
  })
326
300
  );
327
- const classified = (0, import_types.classifyStreams)(probed);
301
+ const classified = types.classifyStreams(probed);
328
302
  const metadataByUrl = new Map(probed.map((p) => [p.url, p.metadata]));
329
303
  const classifiedByUrl = new Map(classified.map((c) => [c.url, c]));
330
304
  return urls.map((url, i) => {
@@ -352,7 +326,7 @@ var RtspCamera = class extends import_types.BaseDevice {
352
326
  invalidateCache: async () => {
353
327
  }
354
328
  };
355
- this.ctx.registerNativeCap(import_types.snapshotCapability, provider);
329
+ this.ctx.registerNativeCap(types.snapshotCapability, provider);
356
330
  }
357
331
  async fetchHttpSnapshot(url) {
358
332
  const username = (this.config.get("username") ?? "").trim();
@@ -367,9 +341,7 @@ var RtspCamera = class extends import_types.BaseDevice {
367
341
  const ab = await res.arrayBuffer();
368
342
  return Buffer.from(ab);
369
343
  }
370
- };
371
-
372
- // src/addon.ts
344
+ }
373
345
  function getString(obj, key) {
374
346
  const v = obj[key];
375
347
  return typeof v === "string" ? v : "";
@@ -393,7 +365,7 @@ function buildCreationFormSchema() {
393
365
  {
394
366
  id: "streams",
395
367
  title: "RTSP Streams",
396
- description: "Provide 1\u20133 RTSP URLs for this camera. Each URL is probed and published to the stream-broker; the broker assigns the (high / mid / low) profiles by resolution \u2014 you do not need to label the streams manually.",
368
+ description: "Provide 1–3 RTSP URLs for this camera. Each URL is probed and published to the stream-broker; the broker assigns the (high / mid / low) profiles by resolution you do not need to label the streams manually.",
397
369
  columns: 1,
398
370
  fields: [
399
371
  {
@@ -439,11 +411,11 @@ function isStreamBrokerReady(event) {
439
411
  const data = event.data;
440
412
  return data["capName"] === "stream-broker" && data["state"] === "ready";
441
413
  }
442
- var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
414
+ class RtspProviderAddon extends types.BaseDeviceProvider {
443
415
  addonId = "provider-rtsp";
444
416
  providerName = "RTSP";
445
417
  deviceClasses = {
446
- [import_types2.DeviceType.Camera]: RtspCamera
418
+ [types.DeviceType.Camera]: RtspCamera
447
419
  };
448
420
  constructor() {
449
421
  super({});
@@ -451,7 +423,7 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
451
423
  async onInitialize() {
452
424
  const regs = await super.onInitialize();
453
425
  this.subscribe(
454
- { category: import_types2.EventCategory.DeviceStateChanged },
426
+ { category: types.EventCategory.DeviceStateChanged },
455
427
  (event) => {
456
428
  const data = event.data;
457
429
  if (data.capName !== "camera-streams") return;
@@ -467,7 +439,7 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
467
439
  }
468
440
  );
469
441
  this.subscribe(
470
- { category: import_types2.EventCategory.SystemReadyState },
442
+ { category: types.EventCategory.SystemReadyState },
471
443
  (event) => {
472
444
  if (!isStreamBrokerReady(event)) return;
473
445
  void this.republishAll().catch((err) => {
@@ -481,11 +453,11 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
481
453
  }
482
454
  // ── Creation ─────────────────────────────────────────────────────────
483
455
  async onGetCreationSchema(type) {
484
- if (type !== import_types2.DeviceType.Camera) return null;
456
+ if (type !== types.DeviceType.Camera) return null;
485
457
  return buildCreationFormSchema();
486
458
  }
487
459
  async onCreateDevice(type, config) {
488
- if (type !== import_types2.DeviceType.Camera) {
460
+ if (type !== types.DeviceType.Camera) {
489
461
  throw new Error(`RTSP provider does not support device type: ${type}`);
490
462
  }
491
463
  const name = getString(config, "name").trim();
@@ -494,7 +466,7 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
494
466
  if (rawStreams.length === 0) throw new Error("At least one RTSP stream URL is required");
495
467
  if (rawStreams.length > 3) throw new Error("At most 3 RTSP stream URLs are supported");
496
468
  const streamProbe = this.ctx.kernel.streamProbe;
497
- if (!streamProbe) throw new Error("ctx.kernel.streamProbe unavailable \u2014 cannot probe streams");
469
+ if (!streamProbe) throw new Error("ctx.kernel.streamProbe unavailable cannot probe streams");
498
470
  const probed = await Promise.all(
499
471
  rawStreams.map(async (url) => {
500
472
  try {
@@ -502,14 +474,14 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
502
474
  return { url, metadata, ok: true };
503
475
  } catch (err) {
504
476
  this.ctx.logger.warn(
505
- "[rtsp-create] probe failed \u2014 publishing without probed metadata",
506
- { meta: { url: (0, import_types2.maskUrlCredentials)(url), error: err instanceof Error ? err.message : String(err) } }
477
+ "[rtsp-create] probe failed publishing without probed metadata",
478
+ { meta: { url: types.maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } }
507
479
  );
508
480
  return { url, metadata: void 0, ok: false };
509
481
  }
510
482
  })
511
483
  );
512
- const classified = (0, import_types2.classifyStreams)(
484
+ const classified = types.classifyStreams(
513
485
  probed.map((p) => ({ url: p.url, metadata: p.metadata ?? {} }))
514
486
  );
515
487
  const hintByUrl = new Map(classified.map((c) => [c.url, c.quality]));
@@ -529,18 +501,18 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
529
501
  password: getString(config, "password")
530
502
  });
531
503
  return {
532
- meta: { type: import_types2.DeviceType.Camera, name },
504
+ meta: { type: types.DeviceType.Camera, name },
533
505
  config: parsed
534
506
  };
535
507
  }
536
508
  // ── Field probing ────────────────────────────────────────────────────
537
509
  async testCreationField(input) {
538
- if (input.type !== import_types2.DeviceType.Camera) {
510
+ if (input.type !== types.DeviceType.Camera) {
539
511
  return { status: "error", error: `Unsupported device type: ${input.type}` };
540
512
  }
541
513
  const streamProbe = this.ctx.kernel.streamProbe;
542
514
  if (!streamProbe) {
543
- return { status: "error", error: "ctx.kernel.streamProbe unavailable \u2014 cannot reach kernel probe" };
515
+ return { status: "error", error: "ctx.kernel.streamProbe unavailable cannot reach kernel probe" };
544
516
  }
545
517
  return streamProbe.probeField(input.key, input.value);
546
518
  }
@@ -574,9 +546,8 @@ var RtspProviderAddon = class extends import_types2.BaseDeviceProvider {
574
546
  });
575
547
  }
576
548
  }
577
- };
578
- // Annotate the CommonJS export names for ESM import in node:
579
- 0 && (module.exports = {
580
- RtspProviderAddon
581
- });
582
- //# sourceMappingURL=addon.js.map
549
+ }
550
+ exports.RtspCamera = RtspCamera;
551
+ exports.RtspProviderAddon = RtspProviderAddon;
552
+ exports.rtspCameraSchema = rtspCameraSchema;
553
+ //# sourceMappingURL=addon.js.map
package/dist/addon.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/addon.ts","../src/rtsp-camera.ts"],"sourcesContent":["import type {\n ConfigUISchema,\n CreateDeviceSpec,\n DeviceConstructor,\n FieldProbeResult,\n IDevice,\n ProviderRegistration,\n StreamQuality,\n SystemEvent,\n} from '@camstack/types'\nimport { BaseDeviceProvider, DeviceType, classifyStreams, EventCategory, maskUrlCredentials } from '@camstack/types'\nimport { RtspCamera, rtspCameraSchema, type RtspStreamEntry } from './rtsp-camera.js'\n\nfunction getString(obj: Record<string, unknown>, key: string): string {\n const v = obj[key]\n return typeof v === 'string' ? v : ''\n}\n\nfunction getStringArray(obj: Record<string, unknown>, key: string): readonly string[] {\n const v = obj[key]\n if (!Array.isArray(v)) return []\n return v\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n}\n\n/**\n * Build the creation form schema. Hand-written ConfigUISchema instead of\n * converting from Zod — this lets us expose probe-typed fields (live\n * ffprobe \"Test\" button) that the automatic converter doesn't emit.\n *\n * `streams` is a single probe field rendered as a `multiple` array —\n * 1..3 instances; the backend probes each via the kernel's\n * `StreamProbeService` and publishes them to the stream-broker, which\n * decides the (high / mid / low) profile mapping by resolution.\n */\nfunction buildCreationFormSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'identity',\n title: 'Camera',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Name', required: true, placeholder: 'Living room' },\n ],\n },\n {\n id: 'streams',\n title: 'RTSP Streams',\n description: 'Provide 1–3 RTSP URLs for this camera. Each URL is probed and published to the stream-broker; the broker assigns the (high / mid / low) profiles by resolution — you do not need to label the streams manually.',\n columns: 1,\n fields: [\n {\n type: 'probe',\n key: 'streams',\n label: 'RTSP stream URL',\n required: true,\n placeholder: 'rtsp://user:pass@host:554/Streaming/Channels/101',\n inputType: 'url',\n multiple: {\n min: 1,\n max: 3,\n addLabel: 'Add stream',\n itemLabel: 'Stream ${n}',\n itemDefault: '',\n },\n },\n ],\n },\n {\n id: 'snapshot',\n title: 'Snapshot',\n columns: 1,\n fields: [\n { type: 'probe', key: 'snapshotUrl', label: 'Snapshot URL', description: 'Optional HTTP(S) endpoint that returns a JPEG still.', placeholder: 'http://host/ISAPI/Streaming/channels/101/picture', inputType: 'url' },\n ],\n },\n {\n id: 'credentials',\n title: 'Credentials',\n description: 'Optional. If the RTSP URL already contains user:pass@ you can leave these empty.',\n columns: 2,\n fields: [\n { type: 'text', key: 'username', label: 'Username' },\n { type: 'password', key: 'password', label: 'Password', showToggle: true },\n ],\n },\n ],\n }\n}\n\n/**\n * Re-publish every RtspCamera's streams to the broker whenever the\n * broker emits `system.ready-state` state=ready. Handles boot-order\n * races (broker starts after provider) and broker restart recovery.\n * At normal runtime, `onCreateDevice` publishes eagerly so new cameras\n * don't have to wait for the next ready fire.\n */\nfunction isStreamBrokerReady(event: SystemEvent): boolean {\n if (event.category !== 'system.ready-state') return false\n const data = event.data as Record<string, unknown>\n return data['capName'] === 'stream-broker' && data['state'] === 'ready'\n}\n\nexport class RtspProviderAddon extends BaseDeviceProvider {\n protected readonly addonId = 'provider-rtsp'\n protected readonly providerName = 'RTSP'\n protected readonly deviceClasses: Partial<Record<DeviceType, DeviceConstructor<IDevice>>> = {\n [DeviceType.Camera]: RtspCamera,\n }\n\n constructor() { super({}) }\n\n protected override async onInitialize(): Promise<ProviderRegistration[]> {\n const regs = await super.onInitialize()\n // Autonomous online tracking — mirror `state.cameraStreams.online`\n // (written by the stream-broker on every health flip) into\n // `device.online` for every RTSP-owned device. RTSP has no firmware\n // liveness signal, so the broker's view is the canonical source.\n // Reolink, which has its own firmware push events, doesn't subscribe.\n this.subscribe(\n { category: EventCategory.DeviceStateChanged },\n (event) => {\n const data = event.data as { deviceId?: number; capName?: string; slice?: { online?: boolean } }\n if (data.capName !== 'camera-streams') return\n const deviceId = data.deviceId\n if (typeof deviceId !== 'number') return\n const registry = this.ctx.kernel.deviceRegistry\n if (!registry) return\n if (registry.getAddonId(deviceId) !== this.addonId) return\n const device = registry.getById(deviceId)\n if (!device) return\n const online = data.slice?.online === true\n if (device.online !== online) device.online = online\n },\n )\n this.subscribe(\n { category: EventCategory.SystemReadyState },\n (event) => {\n if (!isStreamBrokerReady(event)) return\n void this.republishAll().catch((err: unknown) => {\n this.ctx.logger.warn('Failed to re-publish RTSP streams after broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n },\n )\n return regs\n }\n\n // ── Creation ─────────────────────────────────────────────────────────\n\n protected async onGetCreationSchema(type: DeviceType): Promise<ConfigUISchema | null> {\n if (type !== DeviceType.Camera) return null\n return buildCreationFormSchema()\n }\n\n protected async onCreateDevice(type: DeviceType, config: Record<string, unknown>): Promise<CreateDeviceSpec> {\n if (type !== DeviceType.Camera) {\n throw new Error(`RTSP provider does not support device type: ${type}`)\n }\n\n const name = getString(config, 'name').trim()\n if (!name) throw new Error('Camera name is required')\n\n const rawStreams = getStringArray(config, 'streams')\n if (rawStreams.length === 0) throw new Error('At least one RTSP stream URL is required')\n if (rawStreams.length > 3) throw new Error('At most 3 RTSP stream URLs are supported')\n\n const streamProbe = this.ctx.kernel.streamProbe\n if (!streamProbe) throw new Error('ctx.kernel.streamProbe unavailable — cannot probe streams')\n const probed = await Promise.all(\n rawStreams.map(async (url) => {\n try {\n const metadata = await streamProbe.probe(url, { force: false })\n return { url, metadata, ok: true as const }\n } catch (err) {\n this.ctx.logger.warn(\n '[rtsp-create] probe failed — publishing without probed metadata',\n { meta: { url: maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } },\n )\n return { url, metadata: undefined, ok: false as const }\n }\n }),\n )\n\n const classified = classifyStreams(\n probed.map((p) => ({ url: p.url, metadata: p.metadata ?? {} })),\n )\n const hintByUrl = new Map<string, StreamQuality>(classified.map((c) => [c.url, c.quality]))\n\n const streams: RtspStreamEntry[] = probed.map((p, i): RtspStreamEntry => {\n const entry: RtspStreamEntry = { id: `stream-${i + 1}`, url: p.url }\n const hint = hintByUrl.get(p.url)\n if (hint) entry.profileHint = hint\n if (p.metadata?.width && p.metadata?.height) {\n entry.resolution = { width: p.metadata.width, height: p.metadata.height }\n }\n return entry\n })\n\n const parsed = rtspCameraSchema.parse({\n streams,\n snapshotUrl: getString(config, 'snapshotUrl'),\n username: getString(config, 'username'),\n password: getString(config, 'password'),\n })\n\n return {\n meta: { type: DeviceType.Camera, name },\n config: parsed,\n }\n }\n\n // ── Field probing ────────────────────────────────────────────────────\n\n override async testCreationField(input: {\n type: DeviceType\n key: string\n value: unknown\n }): Promise<FieldProbeResult> {\n if (input.type !== DeviceType.Camera) {\n return { status: 'error', error: `Unsupported device type: ${input.type}` }\n }\n const streamProbe = this.ctx.kernel.streamProbe\n if (!streamProbe) {\n return { status: 'error', error: 'ctx.kernel.streamProbe unavailable — cannot reach kernel probe' }\n }\n return streamProbe.probeField(input.key, input.value)\n }\n\n // ── Restore ──────────────────────────────────────────────────────────\n //\n // `BaseDeviceProvider.onRestoreDevices` default impl iterates\n // `savedDevices`, looks up the right class via `deviceClasses`,\n // and calls `kernel.devices.create()`. Each created device fires\n // its `onCreated` lifecycle hook (see `RtspCamera.onCreated()`)\n // which publishes streams to the broker — no per-provider override\n // needed here.\n\n // ── Internal — re-publish every RtspCamera to the broker ────────────\n\n private async republishAll(): Promise<void> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n let published = 0\n for (const dev of all as readonly IDevice[]) {\n if (!(dev instanceof RtspCamera)) continue\n try {\n await dev.publishToBroker()\n published++\n } catch (err) {\n this.ctx.logger.debug('publishToBroker threw during republish', {\n tags: { deviceId: dev.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (published > 0) {\n this.ctx.logger.info('Re-published RTSP streams to stream-broker', {\n meta: { published, total: all.length },\n })\n }\n }\n}\n","import { z } from \"zod\";\nimport { BaseDevice, DeviceType, DeviceFeature, classifyStreams, hydrateSchema, snapshotCapability, maskUrlCredentials } from \"@camstack/types\";\nimport type {\n ConfigUISchema,\n ConfigUISchemaWithValues,\n DeviceContext,\n ICameraDevice,\n InferNativeProvider,\n StreamMetadata,\n StreamQuality,\n StreamSourceEntry,\n} from \"@camstack/types\";\n\n/**\n * Stable, provider-assigned ids within a (deviceId) scope.\n *\n * `stream-1/2/3` map 1:1 with the operator-visible input slots in the\n * creation form. Once assigned at create time (or via legacy migration)\n * the id is permanent — editing a URL keeps its slot. Separating the id\n * from the quality lets the stream-broker decide the (high/mid/low)\n * mapping from probed metadata without the driver having to re-label.\n */\nconst streamEntrySchema = z.object({\n id: z.string().regex(/^stream-\\d+$/).describe(\"Stable slot id (stream-1/2/3)\"),\n url: z.string().describe(\"RTSP stream URL\"),\n profileHint: z.enum(['high', 'mid', 'low']).optional().describe(\"Provider-suggested profile — advisory only\"),\n resolution: z\n .object({ width: z.number().int().positive(), height: z.number().int().positive() })\n .optional()\n .describe(\"Probed resolution, if known\"),\n});\n\nexport type RtspStreamEntry = z.infer<typeof streamEntrySchema>\n\n/**\n * Accepts both the new `{id, url, profileHint?, resolution?}` shape AND\n * the legacy `{label, url}` shape. Legacy entries get rewritten to the\n * new shape at parse time so the rest of the driver can treat storage\n * uniformly. The constructor then persists the new shape back to disk\n * in `migrateLegacyStreamsIfNeeded` so subsequent parses are already\n * on the new shape.\n */\nconst streamsField = z.preprocess(\n (raw: unknown): unknown => {\n if (!Array.isArray(raw)) return raw\n return (raw as readonly unknown[]).map((entry, i): unknown => {\n if (!entry || typeof entry !== 'object') return entry\n const o = entry as Record<string, unknown>\n if (typeof o['id'] === 'string') return entry\n if (typeof o['label'] === 'string' && typeof o['url'] === 'string') {\n return {\n id: `stream-${i + 1}`,\n url: o['url'],\n profileHint: o['label'],\n }\n }\n return entry\n })\n },\n z.array(streamEntrySchema).min(1),\n)\n\nexport const rtspCameraSchema = z.object({\n // `name` is now base meta (DB column on `device-meta`, not in this\n // hardware-config schema). Read via `this.name` (resolved by\n // `BaseDevice` from `ctx.deviceMeta.name`); mutated via\n // `kernel.devices.setName(id, name)`.\n streams: streamsField.describe(\"Published RTSP streams\"),\n snapshotUrl: z.string().default(\"\").describe(\"Snapshot URL (optional)\"),\n username: z.string().default(\"\").describe(\"Username\"),\n password: z.string().default(\"\").describe(\"Password\"),\n});\n\n/**\n * Legacy storage shape: `[{ label: 'high'|'mid'|'low', url }]`.\n * Migrated to the new `[{id, url, profileHint}]` shape on construct\n * via `config.setAll`. The conversion is permanent — after one boot\n * every camera row is on the new shape.\n */\nconst legacyStreamSchema = z.object({\n label: z.enum(['high', 'mid', 'low']),\n url: z.string(),\n})\n\nexport class RtspCamera\n extends BaseDevice<typeof rtspCameraSchema>\n implements ICameraDevice\n{\n readonly type = DeviceType.Camera as const;\n features = [] as const satisfies readonly DeviceFeature[];\n\n constructor(ctx: DeviceContext) {\n super(\n ctx,\n rtspCameraSchema,\n { type: DeviceType.Camera },\n );\n // Online state is now firmware/broker-event driven only — BaseDevice\n // seeds `device-status` slice with online=false, and the provider's\n // stream-health aggregator (`BaseDeviceProvider.subscribeStreamHealth`)\n // flips it true on the first `stream.online` event.\n this.migrateLegacyStreamsIfNeeded(ctx.persistedConfig ?? {})\n this.registerSnapshotProvider();\n }\n\n /**\n * Detect legacy `{label, url}` stream shapes and reshape to\n * `{id, url, profileHint}`. Persists via `config.setAll` once, so\n * subsequent boots find the new shape and this branch is inert.\n *\n * Runs fire-and-forget — the constructor can't await. The reshape is\n * additive (ids are newly minted) so a failed persist is recoverable\n * on next boot.\n */\n private migrateLegacyStreamsIfNeeded(initialData: Record<string, unknown>): void {\n const raw = initialData['streams']\n if (!Array.isArray(raw)) return\n // All entries already have `id` → new shape; nothing to do.\n if (raw.every((s) => s && typeof s === 'object' && typeof (s as Record<string, unknown>)['id'] === 'string')) {\n return\n }\n const migrated: RtspStreamEntry[] = []\n let nextIdx = 1\n for (const entry of raw) {\n const parsed = legacyStreamSchema.safeParse(entry)\n if (!parsed.success) continue\n migrated.push({\n id: `stream-${nextIdx++}`,\n url: parsed.data.url,\n profileHint: parsed.data.label,\n })\n }\n if (migrated.length === 0) return\n const snapshotUrl = this.config.get('snapshotUrl') ?? ''\n const username = this.config.get('username') ?? ''\n const password = this.config.get('password') ?? ''\n this.ctx.logger.info('Migrating legacy {label,url} streams to {id,url,profileHint}', {\n tags: { stableId: this.stableId },\n meta: { streamCount: migrated.length },\n })\n void this.config\n .setAll({ streams: migrated, snapshotUrl, username, password })\n .catch((err: unknown) => {\n this.ctx.logger.warn('Legacy stream migration failed', {\n tags: { stableId: this.stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n /**\n * Publish every configured stream to the system stream-broker as a\n * `pull-rtsp` cam stream. Idempotent — the broker's publishCameraStream\n * entry is keyed by `(deviceId, camStreamId)` so callers may invoke\n * this at will (e.g. on stream-broker restart). Fire-and-forget safe.\n */\n /**\n * Lifecycle hook fired by the kernel after registration. Publishes\n * the camera's streams to the broker — the kernel doesn't know\n * about the broker, but the camera does. Best-effort: if the\n * broker isn't ready, the provider's `system.ready-state`\n * subscription re-publishes on the next ready fire.\n */\n override async onActivate(): Promise<void> {\n await this.publishToBroker().catch((err: unknown) => {\n this.ctx.logger.warn('publishToBroker on activate failed — will retry on broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n async publishToBroker(): Promise<void> {\n const streams = this.config.get('streams')\n for (const [i, s] of streams.entries()) {\n try {\n await this.ctx.api.streamBroker.publishCameraStream.mutate({\n deviceId: this.id,\n camStreamId: s.id,\n kind: 'pull-rtsp',\n url: s.url,\n ...(s.resolution ? { resolution: s.resolution } : {}),\n label: `Stream ${i + 1}`,\n })\n } catch (err: unknown) {\n this.ctx.logger.warn('publishCameraStream failed', {\n tags: { deviceId: this.id, camStreamId: s.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n\n /**\n * Thin compat shim: reflects the stored `{id, url, profileHint?}`\n * streams into the legacy `StreamSourceEntry` shape so\n * `ICameraDevice`-aware consumers (device-manager legacy paths,\n * device-cap-proxy fallback) still find data. The broker itself does\n * NOT read this anymore — it consumes `publishCameraStream`. This\n * shim goes away when every consumer migrates (C5/C7).\n */\n async getStreamSources(): Promise<readonly StreamSourceEntry[]> {\n const streams = this.config.get('streams')\n return streams.map((s): StreamSourceEntry => ({\n id: s.id,\n label: s.profileHint ?? s.id,\n protocol: 'rtsp' as const,\n url: s.url,\n ...(s.profileHint ? { profileHint: s.profileHint } : {}),\n ...(s.resolution ? { resolution: s.resolution } : {}),\n }))\n }\n\n override async removeDevice(): Promise<void> {\n this.ctx.logger.info('Removing RTSP camera', { meta: { stableId: this.stableId } });\n const streams = this.config.get('streams')\n await Promise.all(\n streams.map((s) =>\n this.ctx.api.streamBroker.retractCameraStream\n .mutate({ deviceId: this.id, camStreamId: s.id })\n .catch((err: unknown) => {\n this.ctx.logger.warn('retractCameraStream failed', {\n tags: { deviceId: this.id, camStreamId: s.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }),\n ),\n )\n }\n\n // ── Driver-authored settings UI ────────────────────────────────────────\n //\n // Streams are stored as `Array<{id, url, profileHint?, resolution?}>`\n // but the settings UI exposes them as a `multiple` probe field of raw\n // URLs — the form only cares about the ordered list of URLs, the ids\n // and classification live in storage. Values are projected from\n // storage into the flat form keys referenced by the schema.\n\n override getSettingsUISchema(): ConfigUISchemaWithValues {\n const schema: ConfigUISchema = {\n sections: [\n // The `name` field is now base meta — rendered by the\n // device-manager's own settings contribution (with the\n // location field), NOT by the driver's hardware-config\n // schema. Driver UIs only carry technical knobs.\n {\n id: 'streams',\n title: 'RTSP Streams',\n description: 'Provide 1–3 RTSP URLs for this camera. After save, each URL is probed and the stream-broker assigns the (high / mid / low) profiles by resolution (highest pixel count → high). Input slot order in this form does not affect the broker profile mapping.',\n columns: 1,\n fields: [\n {\n type: 'probe',\n key: 'streams',\n label: 'RTSP stream URL',\n required: true,\n placeholder: 'rtsp://user:pass@host:554/Streaming/Channels/101',\n inputType: 'url',\n multiple: {\n min: 1,\n max: 3,\n addLabel: 'Add stream',\n itemLabel: 'Stream ${n}',\n itemDefault: '',\n },\n },\n ],\n },\n {\n id: 'snapshot',\n title: 'Snapshot',\n columns: 1,\n fields: [\n { type: 'probe', key: 'snapshotUrl', label: 'Snapshot URL', description: 'Optional HTTP(S) endpoint that returns a JPEG still.', placeholder: 'http://host/ISAPI/Streaming/channels/101/picture', inputType: 'url' },\n ],\n },\n {\n id: 'credentials',\n title: 'Credentials',\n description: 'Optional. If the RTSP URL already contains user:pass@ you can leave these empty.',\n columns: 2,\n fields: [\n { type: 'text', key: 'username', label: 'Username' },\n { type: 'password', key: 'password', label: 'Password', showToggle: true },\n ],\n },\n ],\n }\n return hydrateSchema(schema, this.collectFormValues())\n }\n\n /**\n * Project stored config into the flat UI keys referenced by the schema.\n * Form only sees URLs — the internal id/profileHint/resolution stay\n * invisible to the operator (the broker decides profiles from probed\n * metadata at save time).\n */\n private collectFormValues(): Record<string, unknown> {\n const stored = this.config.get('streams')\n // Preserve the slot order from storage (`stream-1`, `stream-2`, …).\n const sorted = [...stored].sort((a, b) => a.id.localeCompare(b.id, 'en', { numeric: true }))\n const urls = sorted.map((s) => s.url).filter((u) => u.length > 0)\n return {\n streams: urls,\n snapshotUrl: this.config.get('snapshotUrl'),\n username: this.config.get('username'),\n password: this.config.get('password'),\n }\n }\n\n override async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n const current = this.collectFormValues()\n const merged: Record<string, unknown> = { ...current, ...patch }\n const pickStr = (k: string): string => (typeof merged[k] === 'string' ? (merged[k] as string).trim() : '')\n\n const next: Record<string, unknown> = {}\n\n const rawStreams = merged['streams']\n const incomingUrls: string[] = Array.isArray(rawStreams)\n ? rawStreams\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n : []\n\n if (incomingUrls.length === 0) {\n // UI cleared every slot — keep current streams so zod's min(1)\n // invariant holds and the camera keeps streaming until the next\n // save with real URLs.\n next.streams = this.config.get('streams')\n } else {\n const probed = await this.probeAndClassify(incomingUrls.slice(0, 3))\n next.streams = probed\n }\n\n next.snapshotUrl = pickStr('snapshotUrl')\n next.username = pickStr('username')\n next.password = typeof merged['password'] === 'string' ? merged['password'] : this.config.get('password')\n\n await this.config.setAll(next)\n\n // Storage now has the new stream list — re-publish so the broker\n // sees any URL/resolution changes. `publishCameraStream` is\n // idempotent for the same camStreamId so slots that didn't change\n // are free no-ops.\n await this.publishToBroker()\n }\n\n /**\n * Probe a list of RTSP URLs through the kernel stream-probe and return\n * the new `{id, url, profileHint?, resolution?}` entries. Ids are\n * assigned `stream-1/2/3` in input order so the UI's slot ordering is\n * preserved across edits. The broker is in charge of the actual\n * (high/mid/low) profile assignment via `computeInitialAssignment`.\n */\n private async probeAndClassify(urls: readonly string[]): Promise<RtspStreamEntry[]> {\n const streamProbe = this.ctx.streamProbe\n if (!streamProbe) {\n this.ctx.logger.warn(\n '[rtsp-edit] ctx.streamProbe unavailable — falling back to input-order profile assignment',\n { meta: { streamCount: urls.length } },\n )\n const sequence: ReadonlyArray<StreamQuality> =\n urls.length === 1 ? ['high']\n : urls.length === 2 ? ['high', 'low']\n : ['high', 'mid', 'low']\n return urls.map((url, i): RtspStreamEntry => ({\n id: `stream-${i + 1}`,\n url,\n profileHint: sequence[i]!,\n }))\n }\n\n const probed = await Promise.all(\n urls.map(async (url) => {\n try {\n const metadata = await streamProbe.probe(url, { force: false })\n return { url, metadata }\n } catch (err) {\n this.ctx.logger.warn(\n '[rtsp-edit] probe failed — landing in the lowest tier via classifyStreams',\n { meta: { url: maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } },\n )\n const emptyMetadata: StreamMetadata = {}\n return { url, metadata: emptyMetadata }\n }\n }),\n )\n const classified = classifyStreams(probed)\n // Each classified entry carries a quality tag. We preserve the\n // input order for the slot id and use the tag as `profileHint`;\n // resolution comes from the probed metadata (matched by url).\n const metadataByUrl = new Map(probed.map((p) => [p.url, p.metadata]))\n const classifiedByUrl = new Map(classified.map((c) => [c.url, c]))\n return urls.map((url, i): RtspStreamEntry => {\n const tag = classifiedByUrl.get(url)\n const meta = metadataByUrl.get(url)\n const entry: RtspStreamEntry = { id: `stream-${i + 1}`, url }\n if (tag) entry.profileHint = tag.quality\n if (meta?.width && meta?.height) entry.resolution = { width: meta.width, height: meta.height }\n return entry\n })\n }\n\n // ── Native capabilities ────────────────────────────────────────────────\n\n private registerSnapshotProvider(): void {\n const snapshotUrl = (this.config.get(\"snapshotUrl\") ?? \"\").trim();\n if (!snapshotUrl) return;\n\n const provider: InferNativeProvider<typeof snapshotCapability> = {\n getSnapshot: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`RtspCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);\n }\n const buf = await this.fetchHttpSnapshot(snapshotUrl);\n if (buf.length === 0) return null;\n return { base64: buf.toString(\"base64\"), contentType: \"image/jpeg\" };\n },\n invalidateCache: async () => {\n /* no-op — caching is the snapshot orchestrator's concern */\n },\n };\n this.ctx.registerNativeCap(snapshotCapability, provider);\n }\n\n private async fetchHttpSnapshot(url: string): Promise<Buffer> {\n const username = (this.config.get(\"username\") ?? \"\").trim();\n const password = (this.config.get(\"password\") ?? \"\").trim();\n const headers: Record<string, string> = {};\n if (username || password) {\n const creds = Buffer.from(`${username}:${password}`).toString(\"base64\");\n headers[\"authorization\"] = `Basic ${creds}`;\n }\n const res = await fetch(url, { headers });\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n }\n}\n"],"mappings":"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","names":["import_types"]}
1
+ {"version":3,"file":"addon.js","sources":["../src/rtsp-camera.ts","../src/addon.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { BaseDevice, DeviceType, DeviceFeature, classifyStreams, hydrateSchema, snapshotCapability, maskUrlCredentials } from \"@camstack/types\";\nimport type {\n ConfigUISchema,\n ConfigUISchemaWithValues,\n DeviceContext,\n ICameraDevice,\n InferNativeProvider,\n StreamMetadata,\n StreamQuality,\n StreamSourceEntry,\n} from \"@camstack/types\";\n\n/**\n * Stable, provider-assigned ids within a (deviceId) scope.\n *\n * `stream-1/2/3` map 1:1 with the operator-visible input slots in the\n * creation form. Once assigned at create time (or via legacy migration)\n * the id is permanent — editing a URL keeps its slot. Separating the id\n * from the quality lets the stream-broker decide the (high/mid/low)\n * mapping from probed metadata without the driver having to re-label.\n */\nconst streamEntrySchema = z.object({\n id: z.string().regex(/^stream-\\d+$/).describe(\"Stable slot id (stream-1/2/3)\"),\n url: z.string().describe(\"RTSP stream URL\"),\n profileHint: z.enum(['high', 'mid', 'low']).optional().describe(\"Provider-suggested profile — advisory only\"),\n resolution: z\n .object({ width: z.number().int().positive(), height: z.number().int().positive() })\n .optional()\n .describe(\"Probed resolution, if known\"),\n});\n\nexport type RtspStreamEntry = z.infer<typeof streamEntrySchema>\n\n/**\n * Accepts both the new `{id, url, profileHint?, resolution?}` shape AND\n * the legacy `{label, url}` shape. Legacy entries get rewritten to the\n * new shape at parse time so the rest of the driver can treat storage\n * uniformly. The constructor then persists the new shape back to disk\n * in `migrateLegacyStreamsIfNeeded` so subsequent parses are already\n * on the new shape.\n */\nconst streamsField = z.preprocess(\n (raw: unknown): unknown => {\n if (!Array.isArray(raw)) return raw\n return (raw as readonly unknown[]).map((entry, i): unknown => {\n if (!entry || typeof entry !== 'object') return entry\n const o = entry as Record<string, unknown>\n if (typeof o['id'] === 'string') return entry\n if (typeof o['label'] === 'string' && typeof o['url'] === 'string') {\n return {\n id: `stream-${i + 1}`,\n url: o['url'],\n profileHint: o['label'],\n }\n }\n return entry\n })\n },\n z.array(streamEntrySchema).min(1),\n)\n\nexport const rtspCameraSchema = z.object({\n // `name` is now base meta (DB column on `device-meta`, not in this\n // hardware-config schema). Read via `this.name` (resolved by\n // `BaseDevice` from `ctx.deviceMeta.name`); mutated via\n // `kernel.devices.setName(id, name)`.\n streams: streamsField.describe(\"Published RTSP streams\"),\n snapshotUrl: z.string().default(\"\").describe(\"Snapshot URL (optional)\"),\n username: z.string().default(\"\").describe(\"Username\"),\n password: z.string().default(\"\").describe(\"Password\"),\n});\n\n/**\n * Legacy storage shape: `[{ label: 'high'|'mid'|'low', url }]`.\n * Migrated to the new `[{id, url, profileHint}]` shape on construct\n * via `config.setAll`. The conversion is permanent — after one boot\n * every camera row is on the new shape.\n */\nconst legacyStreamSchema = z.object({\n label: z.enum(['high', 'mid', 'low']),\n url: z.string(),\n})\n\nexport class RtspCamera\n extends BaseDevice<typeof rtspCameraSchema>\n implements ICameraDevice\n{\n readonly type = DeviceType.Camera as const;\n features = [] as const satisfies readonly DeviceFeature[];\n\n constructor(ctx: DeviceContext) {\n super(\n ctx,\n rtspCameraSchema,\n { type: DeviceType.Camera },\n );\n // Online state is now firmware/broker-event driven only — BaseDevice\n // seeds `device-status` slice with online=false, and the provider's\n // stream-health aggregator (`BaseDeviceProvider.subscribeStreamHealth`)\n // flips it true on the first `stream.online` event.\n this.migrateLegacyStreamsIfNeeded(ctx.persistedConfig ?? {})\n this.registerSnapshotProvider();\n }\n\n /**\n * Detect legacy `{label, url}` stream shapes and reshape to\n * `{id, url, profileHint}`. Persists via `config.setAll` once, so\n * subsequent boots find the new shape and this branch is inert.\n *\n * Runs fire-and-forget — the constructor can't await. The reshape is\n * additive (ids are newly minted) so a failed persist is recoverable\n * on next boot.\n */\n private migrateLegacyStreamsIfNeeded(initialData: Record<string, unknown>): void {\n const raw = initialData['streams']\n if (!Array.isArray(raw)) return\n // All entries already have `id` → new shape; nothing to do.\n if (raw.every((s) => s && typeof s === 'object' && typeof (s as Record<string, unknown>)['id'] === 'string')) {\n return\n }\n const migrated: RtspStreamEntry[] = []\n let nextIdx = 1\n for (const entry of raw) {\n const parsed = legacyStreamSchema.safeParse(entry)\n if (!parsed.success) continue\n migrated.push({\n id: `stream-${nextIdx++}`,\n url: parsed.data.url,\n profileHint: parsed.data.label,\n })\n }\n if (migrated.length === 0) return\n const snapshotUrl = this.config.get('snapshotUrl') ?? ''\n const username = this.config.get('username') ?? ''\n const password = this.config.get('password') ?? ''\n this.ctx.logger.info('Migrating legacy {label,url} streams to {id,url,profileHint}', {\n tags: { stableId: this.stableId },\n meta: { streamCount: migrated.length },\n })\n void this.config\n .setAll({ streams: migrated, snapshotUrl, username, password })\n .catch((err: unknown) => {\n this.ctx.logger.warn('Legacy stream migration failed', {\n tags: { stableId: this.stableId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n /**\n * Publish every configured stream to the system stream-broker as a\n * `pull-rtsp` cam stream. Idempotent — the broker's publishCameraStream\n * entry is keyed by `(deviceId, camStreamId)` so callers may invoke\n * this at will (e.g. on stream-broker restart). Fire-and-forget safe.\n */\n /**\n * Lifecycle hook fired by the kernel after registration. Publishes\n * the camera's streams to the broker — the kernel doesn't know\n * about the broker, but the camera does. Best-effort: if the\n * broker isn't ready, the provider's `system.ready-state`\n * subscription re-publishes on the next ready fire.\n */\n override async onActivate(): Promise<void> {\n await this.publishToBroker().catch((err: unknown) => {\n this.ctx.logger.warn('publishToBroker on activate failed — will retry on broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n async publishToBroker(): Promise<void> {\n const streams = this.config.get('streams')\n for (const [i, s] of streams.entries()) {\n try {\n await this.ctx.api.streamBroker.publishCameraStream.mutate({\n deviceId: this.id,\n camStreamId: s.id,\n kind: 'pull-rtsp',\n url: s.url,\n ...(s.resolution ? { resolution: s.resolution } : {}),\n label: `Stream ${i + 1}`,\n })\n } catch (err: unknown) {\n this.ctx.logger.warn('publishCameraStream failed', {\n tags: { deviceId: this.id, camStreamId: s.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n }\n\n /**\n * Thin compat shim: reflects the stored `{id, url, profileHint?}`\n * streams into the legacy `StreamSourceEntry` shape so\n * `ICameraDevice`-aware consumers (device-manager legacy paths,\n * device-cap-proxy fallback) still find data. The broker itself does\n * NOT read this anymore — it consumes `publishCameraStream`. This\n * shim goes away when every consumer migrates (C5/C7).\n */\n async getStreamSources(): Promise<readonly StreamSourceEntry[]> {\n const streams = this.config.get('streams')\n return streams.map((s): StreamSourceEntry => ({\n id: s.id,\n label: s.profileHint ?? s.id,\n protocol: 'rtsp' as const,\n url: s.url,\n ...(s.profileHint ? { profileHint: s.profileHint } : {}),\n ...(s.resolution ? { resolution: s.resolution } : {}),\n }))\n }\n\n override async removeDevice(): Promise<void> {\n this.ctx.logger.info('Removing RTSP camera', { meta: { stableId: this.stableId } });\n const streams = this.config.get('streams')\n await Promise.all(\n streams.map((s) =>\n this.ctx.api.streamBroker.retractCameraStream\n .mutate({ deviceId: this.id, camStreamId: s.id })\n .catch((err: unknown) => {\n this.ctx.logger.warn('retractCameraStream failed', {\n tags: { deviceId: this.id, camStreamId: s.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }),\n ),\n )\n }\n\n // ── Driver-authored settings UI ────────────────────────────────────────\n //\n // Streams are stored as `Array<{id, url, profileHint?, resolution?}>`\n // but the settings UI exposes them as a `multiple` probe field of raw\n // URLs — the form only cares about the ordered list of URLs, the ids\n // and classification live in storage. Values are projected from\n // storage into the flat form keys referenced by the schema.\n\n override getSettingsUISchema(): ConfigUISchemaWithValues {\n const schema: ConfigUISchema = {\n sections: [\n // The `name` field is now base meta — rendered by the\n // device-manager's own settings contribution (with the\n // location field), NOT by the driver's hardware-config\n // schema. Driver UIs only carry technical knobs.\n {\n id: 'streams',\n title: 'RTSP Streams',\n description: 'Provide 1–3 RTSP URLs for this camera. After save, each URL is probed and the stream-broker assigns the (high / mid / low) profiles by resolution (highest pixel count → high). Input slot order in this form does not affect the broker profile mapping.',\n columns: 1,\n fields: [\n {\n type: 'probe',\n key: 'streams',\n label: 'RTSP stream URL',\n required: true,\n placeholder: 'rtsp://user:pass@host:554/Streaming/Channels/101',\n inputType: 'url',\n multiple: {\n min: 1,\n max: 3,\n addLabel: 'Add stream',\n itemLabel: 'Stream ${n}',\n itemDefault: '',\n },\n },\n ],\n },\n {\n id: 'snapshot',\n title: 'Snapshot',\n columns: 1,\n fields: [\n { type: 'probe', key: 'snapshotUrl', label: 'Snapshot URL', description: 'Optional HTTP(S) endpoint that returns a JPEG still.', placeholder: 'http://host/ISAPI/Streaming/channels/101/picture', inputType: 'url' },\n ],\n },\n {\n id: 'credentials',\n title: 'Credentials',\n description: 'Optional. If the RTSP URL already contains user:pass@ you can leave these empty.',\n columns: 2,\n fields: [\n { type: 'text', key: 'username', label: 'Username' },\n { type: 'password', key: 'password', label: 'Password', showToggle: true },\n ],\n },\n ],\n }\n return hydrateSchema(schema, this.collectFormValues())\n }\n\n /**\n * Project stored config into the flat UI keys referenced by the schema.\n * Form only sees URLs — the internal id/profileHint/resolution stay\n * invisible to the operator (the broker decides profiles from probed\n * metadata at save time).\n */\n private collectFormValues(): Record<string, unknown> {\n const stored = this.config.get('streams')\n // Preserve the slot order from storage (`stream-1`, `stream-2`, …).\n const sorted = [...stored].sort((a, b) => a.id.localeCompare(b.id, 'en', { numeric: true }))\n const urls = sorted.map((s) => s.url).filter((u) => u.length > 0)\n return {\n streams: urls,\n snapshotUrl: this.config.get('snapshotUrl'),\n username: this.config.get('username'),\n password: this.config.get('password'),\n }\n }\n\n override async applySettingsPatch(patch: Record<string, unknown>): Promise<void> {\n const current = this.collectFormValues()\n const merged: Record<string, unknown> = { ...current, ...patch }\n const pickStr = (k: string): string => (typeof merged[k] === 'string' ? (merged[k] as string).trim() : '')\n\n const next: Record<string, unknown> = {}\n\n const rawStreams = merged['streams']\n const incomingUrls: string[] = Array.isArray(rawStreams)\n ? rawStreams\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n : []\n\n if (incomingUrls.length === 0) {\n // UI cleared every slot — keep current streams so zod's min(1)\n // invariant holds and the camera keeps streaming until the next\n // save with real URLs.\n next.streams = this.config.get('streams')\n } else {\n const probed = await this.probeAndClassify(incomingUrls.slice(0, 3))\n next.streams = probed\n }\n\n next.snapshotUrl = pickStr('snapshotUrl')\n next.username = pickStr('username')\n next.password = typeof merged['password'] === 'string' ? merged['password'] : this.config.get('password')\n\n await this.config.setAll(next)\n\n // Storage now has the new stream list — re-publish so the broker\n // sees any URL/resolution changes. `publishCameraStream` is\n // idempotent for the same camStreamId so slots that didn't change\n // are free no-ops.\n await this.publishToBroker()\n }\n\n /**\n * Probe a list of RTSP URLs through the kernel stream-probe and return\n * the new `{id, url, profileHint?, resolution?}` entries. Ids are\n * assigned `stream-1/2/3` in input order so the UI's slot ordering is\n * preserved across edits. The broker is in charge of the actual\n * (high/mid/low) profile assignment via `computeInitialAssignment`.\n */\n private async probeAndClassify(urls: readonly string[]): Promise<RtspStreamEntry[]> {\n const streamProbe = this.ctx.streamProbe\n if (!streamProbe) {\n this.ctx.logger.warn(\n '[rtsp-edit] ctx.streamProbe unavailable — falling back to input-order profile assignment',\n { meta: { streamCount: urls.length } },\n )\n const sequence: ReadonlyArray<StreamQuality> =\n urls.length === 1 ? ['high']\n : urls.length === 2 ? ['high', 'low']\n : ['high', 'mid', 'low']\n return urls.map((url, i): RtspStreamEntry => ({\n id: `stream-${i + 1}`,\n url,\n profileHint: sequence[i]!,\n }))\n }\n\n const probed = await Promise.all(\n urls.map(async (url) => {\n try {\n const metadata = await streamProbe.probe(url, { force: false })\n return { url, metadata }\n } catch (err) {\n this.ctx.logger.warn(\n '[rtsp-edit] probe failed — landing in the lowest tier via classifyStreams',\n { meta: { url: maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } },\n )\n const emptyMetadata: StreamMetadata = {}\n return { url, metadata: emptyMetadata }\n }\n }),\n )\n const classified = classifyStreams(probed)\n // Each classified entry carries a quality tag. We preserve the\n // input order for the slot id and use the tag as `profileHint`;\n // resolution comes from the probed metadata (matched by url).\n const metadataByUrl = new Map(probed.map((p) => [p.url, p.metadata]))\n const classifiedByUrl = new Map(classified.map((c) => [c.url, c]))\n return urls.map((url, i): RtspStreamEntry => {\n const tag = classifiedByUrl.get(url)\n const meta = metadataByUrl.get(url)\n const entry: RtspStreamEntry = { id: `stream-${i + 1}`, url }\n if (tag) entry.profileHint = tag.quality\n if (meta?.width && meta?.height) entry.resolution = { width: meta.width, height: meta.height }\n return entry\n })\n }\n\n // ── Native capabilities ────────────────────────────────────────────────\n\n private registerSnapshotProvider(): void {\n const snapshotUrl = (this.config.get(\"snapshotUrl\") ?? \"\").trim();\n if (!snapshotUrl) return;\n\n const provider: InferNativeProvider<typeof snapshotCapability> = {\n getSnapshot: async ({ deviceId }) => {\n if (deviceId !== this.id) {\n throw new Error(`RtspCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);\n }\n const buf = await this.fetchHttpSnapshot(snapshotUrl);\n if (buf.length === 0) return null;\n return { base64: buf.toString(\"base64\"), contentType: \"image/jpeg\" };\n },\n invalidateCache: async () => {\n /* no-op — caching is the snapshot orchestrator's concern */\n },\n };\n this.ctx.registerNativeCap(snapshotCapability, provider);\n }\n\n private async fetchHttpSnapshot(url: string): Promise<Buffer> {\n const username = (this.config.get(\"username\") ?? \"\").trim();\n const password = (this.config.get(\"password\") ?? \"\").trim();\n const headers: Record<string, string> = {};\n if (username || password) {\n const creds = Buffer.from(`${username}:${password}`).toString(\"base64\");\n headers[\"authorization\"] = `Basic ${creds}`;\n }\n const res = await fetch(url, { headers });\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n const ab = await res.arrayBuffer();\n return Buffer.from(ab);\n }\n}\n","import type {\n ConfigUISchema,\n CreateDeviceSpec,\n DeviceConstructor,\n FieldProbeResult,\n IDevice,\n ProviderRegistration,\n StreamQuality,\n SystemEvent,\n} from '@camstack/types'\nimport { BaseDeviceProvider, DeviceType, classifyStreams, EventCategory, maskUrlCredentials } from '@camstack/types'\nimport { RtspCamera, rtspCameraSchema, type RtspStreamEntry } from './rtsp-camera.js'\n\nfunction getString(obj: Record<string, unknown>, key: string): string {\n const v = obj[key]\n return typeof v === 'string' ? v : ''\n}\n\nfunction getStringArray(obj: Record<string, unknown>, key: string): readonly string[] {\n const v = obj[key]\n if (!Array.isArray(v)) return []\n return v\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n}\n\n/**\n * Build the creation form schema. Hand-written ConfigUISchema instead of\n * converting from Zod — this lets us expose probe-typed fields (live\n * ffprobe \"Test\" button) that the automatic converter doesn't emit.\n *\n * `streams` is a single probe field rendered as a `multiple` array —\n * 1..3 instances; the backend probes each via the kernel's\n * `StreamProbeService` and publishes them to the stream-broker, which\n * decides the (high / mid / low) profile mapping by resolution.\n */\nfunction buildCreationFormSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'identity',\n title: 'Camera',\n columns: 1,\n fields: [\n { type: 'text', key: 'name', label: 'Name', required: true, placeholder: 'Living room' },\n ],\n },\n {\n id: 'streams',\n title: 'RTSP Streams',\n description: 'Provide 1–3 RTSP URLs for this camera. Each URL is probed and published to the stream-broker; the broker assigns the (high / mid / low) profiles by resolution — you do not need to label the streams manually.',\n columns: 1,\n fields: [\n {\n type: 'probe',\n key: 'streams',\n label: 'RTSP stream URL',\n required: true,\n placeholder: 'rtsp://user:pass@host:554/Streaming/Channels/101',\n inputType: 'url',\n multiple: {\n min: 1,\n max: 3,\n addLabel: 'Add stream',\n itemLabel: 'Stream ${n}',\n itemDefault: '',\n },\n },\n ],\n },\n {\n id: 'snapshot',\n title: 'Snapshot',\n columns: 1,\n fields: [\n { type: 'probe', key: 'snapshotUrl', label: 'Snapshot URL', description: 'Optional HTTP(S) endpoint that returns a JPEG still.', placeholder: 'http://host/ISAPI/Streaming/channels/101/picture', inputType: 'url' },\n ],\n },\n {\n id: 'credentials',\n title: 'Credentials',\n description: 'Optional. If the RTSP URL already contains user:pass@ you can leave these empty.',\n columns: 2,\n fields: [\n { type: 'text', key: 'username', label: 'Username' },\n { type: 'password', key: 'password', label: 'Password', showToggle: true },\n ],\n },\n ],\n }\n}\n\n/**\n * Re-publish every RtspCamera's streams to the broker whenever the\n * broker emits `system.ready-state` state=ready. Handles boot-order\n * races (broker starts after provider) and broker restart recovery.\n * At normal runtime, `onCreateDevice` publishes eagerly so new cameras\n * don't have to wait for the next ready fire.\n */\nfunction isStreamBrokerReady(event: SystemEvent): boolean {\n if (event.category !== 'system.ready-state') return false\n const data = event.data as Record<string, unknown>\n return data['capName'] === 'stream-broker' && data['state'] === 'ready'\n}\n\nexport class RtspProviderAddon extends BaseDeviceProvider {\n protected readonly addonId = 'provider-rtsp'\n protected readonly providerName = 'RTSP'\n protected readonly deviceClasses: Partial<Record<DeviceType, DeviceConstructor<IDevice>>> = {\n [DeviceType.Camera]: RtspCamera,\n }\n\n constructor() { super({}) }\n\n protected override async onInitialize(): Promise<ProviderRegistration[]> {\n const regs = await super.onInitialize()\n // Autonomous online tracking — mirror `state.cameraStreams.online`\n // (written by the stream-broker on every health flip) into\n // `device.online` for every RTSP-owned device. RTSP has no firmware\n // liveness signal, so the broker's view is the canonical source.\n // Reolink, which has its own firmware push events, doesn't subscribe.\n this.subscribe(\n { category: EventCategory.DeviceStateChanged },\n (event) => {\n const data = event.data as { deviceId?: number; capName?: string; slice?: { online?: boolean } }\n if (data.capName !== 'camera-streams') return\n const deviceId = data.deviceId\n if (typeof deviceId !== 'number') return\n const registry = this.ctx.kernel.deviceRegistry\n if (!registry) return\n if (registry.getAddonId(deviceId) !== this.addonId) return\n const device = registry.getById(deviceId)\n if (!device) return\n const online = data.slice?.online === true\n if (device.online !== online) device.online = online\n },\n )\n this.subscribe(\n { category: EventCategory.SystemReadyState },\n (event) => {\n if (!isStreamBrokerReady(event)) return\n void this.republishAll().catch((err: unknown) => {\n this.ctx.logger.warn('Failed to re-publish RTSP streams after broker ready', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n },\n )\n return regs\n }\n\n // ── Creation ─────────────────────────────────────────────────────────\n\n protected async onGetCreationSchema(type: DeviceType): Promise<ConfigUISchema | null> {\n if (type !== DeviceType.Camera) return null\n return buildCreationFormSchema()\n }\n\n protected async onCreateDevice(type: DeviceType, config: Record<string, unknown>): Promise<CreateDeviceSpec> {\n if (type !== DeviceType.Camera) {\n throw new Error(`RTSP provider does not support device type: ${type}`)\n }\n\n const name = getString(config, 'name').trim()\n if (!name) throw new Error('Camera name is required')\n\n const rawStreams = getStringArray(config, 'streams')\n if (rawStreams.length === 0) throw new Error('At least one RTSP stream URL is required')\n if (rawStreams.length > 3) throw new Error('At most 3 RTSP stream URLs are supported')\n\n const streamProbe = this.ctx.kernel.streamProbe\n if (!streamProbe) throw new Error('ctx.kernel.streamProbe unavailable — cannot probe streams')\n const probed = await Promise.all(\n rawStreams.map(async (url) => {\n try {\n const metadata = await streamProbe.probe(url, { force: false })\n return { url, metadata, ok: true as const }\n } catch (err) {\n this.ctx.logger.warn(\n '[rtsp-create] probe failed — publishing without probed metadata',\n { meta: { url: maskUrlCredentials(url), error: err instanceof Error ? err.message : String(err) } },\n )\n return { url, metadata: undefined, ok: false as const }\n }\n }),\n )\n\n const classified = classifyStreams(\n probed.map((p) => ({ url: p.url, metadata: p.metadata ?? {} })),\n )\n const hintByUrl = new Map<string, StreamQuality>(classified.map((c) => [c.url, c.quality]))\n\n const streams: RtspStreamEntry[] = probed.map((p, i): RtspStreamEntry => {\n const entry: RtspStreamEntry = { id: `stream-${i + 1}`, url: p.url }\n const hint = hintByUrl.get(p.url)\n if (hint) entry.profileHint = hint\n if (p.metadata?.width && p.metadata?.height) {\n entry.resolution = { width: p.metadata.width, height: p.metadata.height }\n }\n return entry\n })\n\n const parsed = rtspCameraSchema.parse({\n streams,\n snapshotUrl: getString(config, 'snapshotUrl'),\n username: getString(config, 'username'),\n password: getString(config, 'password'),\n })\n\n return {\n meta: { type: DeviceType.Camera, name },\n config: parsed,\n }\n }\n\n // ── Field probing ────────────────────────────────────────────────────\n\n override async testCreationField(input: {\n type: DeviceType\n key: string\n value: unknown\n }): Promise<FieldProbeResult> {\n if (input.type !== DeviceType.Camera) {\n return { status: 'error', error: `Unsupported device type: ${input.type}` }\n }\n const streamProbe = this.ctx.kernel.streamProbe\n if (!streamProbe) {\n return { status: 'error', error: 'ctx.kernel.streamProbe unavailable — cannot reach kernel probe' }\n }\n return streamProbe.probeField(input.key, input.value)\n }\n\n // ── Restore ──────────────────────────────────────────────────────────\n //\n // `BaseDeviceProvider.onRestoreDevices` default impl iterates\n // `savedDevices`, looks up the right class via `deviceClasses`,\n // and calls `kernel.devices.create()`. Each created device fires\n // its `onCreated` lifecycle hook (see `RtspCamera.onCreated()`)\n // which publishes streams to the broker — no per-provider override\n // needed here.\n\n // ── Internal — re-publish every RtspCamera to the broker ────────────\n\n private async republishAll(): Promise<void> {\n const all = (await this.ctx.kernel.devices?.getAll()) ?? []\n let published = 0\n for (const dev of all as readonly IDevice[]) {\n if (!(dev instanceof RtspCamera)) continue\n try {\n await dev.publishToBroker()\n published++\n } catch (err) {\n this.ctx.logger.debug('publishToBroker threw during republish', {\n tags: { deviceId: dev.id },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n }\n if (published > 0) {\n this.ctx.logger.info('Re-published RTSP streams to stream-broker', {\n meta: { published, total: all.length },\n })\n }\n }\n}\n"],"names":["z","BaseDevice","DeviceType","hydrateSchema","maskUrlCredentials","classifyStreams","snapshotCapability","BaseDeviceProvider","EventCategory"],"mappings":"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"}