@camstack/addon-pipeline 1.2.99 → 1.2.101
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/recorder/index.js +370 -130
- package/dist/recorder/index.mjs +371 -131
- package/dist/retire-root-keys-DMolfhsP.mjs +308 -0
- package/dist/retire-root-keys-KE6D6Xh_.js +319 -0
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B7ERJocD.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C4fRkYcj.mjs} +1 -1
- package/dist/stream-broker/{hostInit-Cm2rl8-e.mjs → hostInit-CS6xpCdb.mjs} +1 -1
- package/dist/stream-broker/index.js +284 -40
- package/dist/stream-broker/index.mjs +284 -40
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "../chunk-DnnnRqeS.mjs";
|
|
2
2
|
import { A as cameraStreamsCapability, At as boolean, B as egressTransportFromRequest, Ct as nodePin, E as addonWidgetsSourceCapability, F as deriveBatteryPresence, Ft as object, G as maskUrlCredentials, It as record, Lt as string, Nt as literal, Ot as _enum, Pt as number, Rt as union, St as makeSourceBrokerId, Tt as parseProfileBrokerId, _t as DeviceType, at as streamBrokerCapability, b as RATE_CONTROL_RELAXED, bt as isEvent, ct as errMsg, dt as buildFfmpegArgs$1, et as resolveEgressDecodeHwAccel, ft as invocationFromEncodeProfile, gt as DeviceFeature, ht as CAM_PROFILE_ORDER, i as BatteryStatusSchema, j as createHwAccelCache, jt as discriminatedUnion, k as batteryCapability, kt as array, lt as AUDIO_PRESETS, mt as BaseAddon, p as EncodeProfileSchema, pt as isSoftwareDecode, st as webrtcSessionCapability, ut as Fmp4BoxSplitter, vt as createEvent, w as RingBuffer, x as RATE_CONTROL_TIGHT, xt as makeProfileBrokerId, z as egressTranscodeSharingKey, zt as EventCategory } from "../dist-gXdWP96z.mjs";
|
|
3
3
|
import { n as profileForStreamId, r as parseRestreamPath } from "../remote-restream-Ci7RXNGb.mjs";
|
|
4
|
+
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DMolfhsP.mjs";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import * as crypto$1 from "node:crypto";
|
|
6
7
|
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
@@ -112,8 +113,9 @@ function createClientLogIngest(onEntries) {
|
|
|
112
113
|
*/
|
|
113
114
|
/**
|
|
114
115
|
* Last-observed runtime probe (present only after the stream was pulled once).
|
|
115
|
-
* Zod-backed because it is persisted durably (
|
|
116
|
-
*
|
|
116
|
+
* Zod-backed because it is persisted durably (ONE ROW per broker in
|
|
117
|
+
* `stream-broker:probe-snapshots`) so the field stays populated across a hub
|
|
118
|
+
* restart — the schema is the single source of truth, applied per row.
|
|
117
119
|
*/
|
|
118
120
|
var ProbedSnapshotSchema = object({
|
|
119
121
|
/** Detected video codec — preferred over the declared one when present. */
|
|
@@ -129,8 +131,7 @@ var ProbedSnapshotSchema = object({
|
|
|
129
131
|
/** Epoch ms the snapshot was captured. */
|
|
130
132
|
at: number()
|
|
131
133
|
});
|
|
132
|
-
|
|
133
|
-
var ProbedSnapshotMapSchema = record(string(), ProbedSnapshotSchema);
|
|
134
|
+
record(string(), ProbedSnapshotSchema);
|
|
134
135
|
var SEP = " · ";
|
|
135
136
|
function formatCodec(codec) {
|
|
136
137
|
const lower = codec.toLowerCase();
|
|
@@ -179,6 +180,208 @@ function formatProbedSummary(declared, snapshot, opts) {
|
|
|
179
180
|
return parts.join(SEP);
|
|
180
181
|
}
|
|
181
182
|
//#endregion
|
|
183
|
+
//#region src/stream-broker/broker-row-stores.ts
|
|
184
|
+
/**
|
|
185
|
+
* The stream-broker's flattened settings maps — one row per entry.
|
|
186
|
+
*
|
|
187
|
+
* ## What this replaced
|
|
188
|
+
*
|
|
189
|
+
* `stream-broker:addon-settings` / `root` was 33 155 bytes on the reference
|
|
190
|
+
* hub, and three keys were 32 735 of it: `probeSnapshots` (17 282 B, 109
|
|
191
|
+
* streams), `rtspTokens` (13 147 B, 243 streams) and `profileMap` (2 306 B, 29
|
|
192
|
+
* cameras — a per-DEVICE map, so it grows with the fleet). Every read of that
|
|
193
|
+
* row parsed all of it, including `BaseAddon.resolveConfig`, and every write
|
|
194
|
+
* rewrote all of it — a probe flush every few seconds while streams are live.
|
|
195
|
+
*
|
|
196
|
+
* Two keys deliberately STAY blob keys on that row, and the reasons are not
|
|
197
|
+
* "they are small":
|
|
198
|
+
*
|
|
199
|
+
* - `rtspEnabled` — brokerId → boolean, EMPTY on the reference hub. Its
|
|
200
|
+
* persister replaces the whole map from the set of registered restreamers,
|
|
201
|
+
* and reproducing that on rows means an authoritative prune. A prune driven
|
|
202
|
+
* by a map that shrinks whenever a broker unregisters is exactly the shape
|
|
203
|
+
* that turns an unbind into a wipe (D130, D49), and the twin key
|
|
204
|
+
* `rtspTokens` carries a comment about precisely that loss. Nothing is
|
|
205
|
+
* bought by moving 0 bytes at that risk.
|
|
206
|
+
* - `deviceOverrides` — 354 B, two cameras, same replace-the-whole-map
|
|
207
|
+
* persister, written only when an operator edits an override.
|
|
208
|
+
*
|
|
209
|
+
* The row survives either way: it is also where `BaseAddon.resolveConfig` reads
|
|
210
|
+
* the operator's config fields from. The problem was a 33 KB row, not a blob.
|
|
211
|
+
*/
|
|
212
|
+
/**
|
|
213
|
+
* The addon id — the settings-store namespace whose `root` row the flattened
|
|
214
|
+
* keys are being retired from. Matches `packages/addon-pipeline/package.json`
|
|
215
|
+
* (`camstack.addons[].id`).
|
|
216
|
+
*/
|
|
217
|
+
var STREAM_BROKER_ADDON_ID = "stream-broker";
|
|
218
|
+
/**
|
|
219
|
+
* Every one of these collections is keyed by the BROKER ID
|
|
220
|
+
* (`<deviceId>/<camStreamId>`) or by the numeric deviceId. `brokerDeviceId`
|
|
221
|
+
* recovers the numeric device from either, for the projected column that makes
|
|
222
|
+
* a per-camera question an indexed query — and for `tags: { deviceId }` on
|
|
223
|
+
* every line these stores log.
|
|
224
|
+
*
|
|
225
|
+
* Returns `null` rather than guessing: a key whose head is not a number is a
|
|
226
|
+
* key this addon did not write, and inventing a device for it would put a row
|
|
227
|
+
* on a camera at random.
|
|
228
|
+
*/
|
|
229
|
+
function brokerDeviceId(key) {
|
|
230
|
+
const head = key.includes("/") ? key.slice(0, key.indexOf("/")) : key;
|
|
231
|
+
const id = Number(head);
|
|
232
|
+
return Number.isInteger(id) && id > 0 ? id : null;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Bound for a whole-map read. `settings-store.query` silently caps an unbounded
|
|
236
|
+
* read at 2 000 rows and answers as if that were everything; at 974 devices ×
|
|
237
|
+
* up to 5 streams the default is HALF the margin, so every read here names its
|
|
238
|
+
* own ceiling and a truncation is reported rather than shrunk.
|
|
239
|
+
*/
|
|
240
|
+
var BROKER_ROWS_LIMIT = 2e4;
|
|
241
|
+
/**
|
|
242
|
+
* @durable class=mirror owner=stream-broker
|
|
243
|
+
* write="one row per broker (`<deviceId>/<camStreamId>`), replaced wholesale from the
|
|
244
|
+
* manager's live probe map by the throttled metrics flush and once on shutdown"
|
|
245
|
+
* retention="the flush is authoritative: a broker the manager no longer holds loses its
|
|
246
|
+
* row. Losing the whole collection costs the settings 'Probed' field showing 'not yet
|
|
247
|
+
* probed' until the stream is pulled again — it is re-derived from the next probe."
|
|
248
|
+
*/
|
|
249
|
+
var BROKER_PROBE_SNAPSHOTS_COLLECTION = "stream-broker:probe-snapshots";
|
|
250
|
+
var BROKER_PROBE_SNAPSHOTS_SPEC = {
|
|
251
|
+
collection: BROKER_PROBE_SNAPSHOTS_COLLECTION,
|
|
252
|
+
schema: ProbedSnapshotSchema,
|
|
253
|
+
columns: [{
|
|
254
|
+
name: "deviceId",
|
|
255
|
+
type: "INTEGER"
|
|
256
|
+
}, {
|
|
257
|
+
name: "at",
|
|
258
|
+
type: "INTEGER",
|
|
259
|
+
notNull: true
|
|
260
|
+
}],
|
|
261
|
+
indexes: [{
|
|
262
|
+
name: "idx_stream_broker_probe_device",
|
|
263
|
+
columns: ["deviceId"]
|
|
264
|
+
}],
|
|
265
|
+
project: (key, value) => ({
|
|
266
|
+
deviceId: brokerDeviceId(key),
|
|
267
|
+
at: value.at
|
|
268
|
+
}),
|
|
269
|
+
deviceIdColumn: "deviceId",
|
|
270
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
271
|
+
};
|
|
272
|
+
function probeSnapshotsStore(store, logger) {
|
|
273
|
+
return new RowMapStore({
|
|
274
|
+
spec: BROKER_PROBE_SNAPSHOTS_SPEC,
|
|
275
|
+
store,
|
|
276
|
+
logger
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The token, wrapped in an object rather than stored bare.
|
|
281
|
+
*
|
|
282
|
+
* A bare string in a `JSON` column round-trips only because the engine's
|
|
283
|
+
* decoder JSON-parses a stored string ONLY when it starts with `{` or `[`. That
|
|
284
|
+
* is a heuristic, not a contract; a token is opaque hex today and a future
|
|
285
|
+
* format that starts with a brace would come back as an object. One field costs
|
|
286
|
+
* nothing and makes the row self-describing.
|
|
287
|
+
*/
|
|
288
|
+
var RtspTokenRowSchema = object({ token: string().min(1) });
|
|
289
|
+
/**
|
|
290
|
+
* @durable class=ledger owner=stream-broker
|
|
291
|
+
* write="one row per broker (`<deviceId>/<camStreamId>`), upserted when the restream
|
|
292
|
+
* server mints a token. MERGE only — a token is stable forever per stream unless it
|
|
293
|
+
* is explicitly regenerated"
|
|
294
|
+
* retention="nothing deletes a row. The persister receives only the CURRENTLY registered
|
|
295
|
+
* restreamers, so a replace would erase the token of an unregistered broker and turn
|
|
296
|
+
* an external NVR URL into a dead 404 at the next idle or reboot."
|
|
297
|
+
*/
|
|
298
|
+
var BROKER_RTSP_TOKENS_COLLECTION = "stream-broker:rtsp-tokens";
|
|
299
|
+
var BROKER_RTSP_TOKENS_SPEC = {
|
|
300
|
+
collection: BROKER_RTSP_TOKENS_COLLECTION,
|
|
301
|
+
schema: RtspTokenRowSchema,
|
|
302
|
+
columns: [{
|
|
303
|
+
name: "deviceId",
|
|
304
|
+
type: "INTEGER"
|
|
305
|
+
}],
|
|
306
|
+
indexes: [{
|
|
307
|
+
name: "idx_stream_broker_rtsp_tokens_device",
|
|
308
|
+
columns: ["deviceId"]
|
|
309
|
+
}],
|
|
310
|
+
project: (key) => ({ deviceId: brokerDeviceId(key) }),
|
|
311
|
+
deviceIdColumn: "deviceId",
|
|
312
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
313
|
+
};
|
|
314
|
+
function rtspTokensStore(store, logger) {
|
|
315
|
+
return new RowMapStore({
|
|
316
|
+
spec: BROKER_RTSP_TOKENS_SPEC,
|
|
317
|
+
store,
|
|
318
|
+
logger
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/** One camera's profile → camStreamId assignment, plus whether it is automatic. */
|
|
322
|
+
var ProfileAssignmentSchema = object({
|
|
323
|
+
map: record(string(), string()),
|
|
324
|
+
auto: boolean()
|
|
325
|
+
});
|
|
326
|
+
/**
|
|
327
|
+
* @durable class=config owner=stream-broker
|
|
328
|
+
* write="one row per camera, replaced wholesale from the manager's assignment map on
|
|
329
|
+
* every profile mutation — the map is authoritative and its boot pass PRUNES cameras
|
|
330
|
+
* the device registry no longer knows, which is why the write is a replace and not
|
|
331
|
+
* an upsert"
|
|
332
|
+
* retention="the replace is the retention: a camera dropped from the manager's map loses
|
|
333
|
+
* its row. Losing a row costs one automatic re-assignment on the next catalog pull."
|
|
334
|
+
*/
|
|
335
|
+
var BROKER_PROFILE_MAP_COLLECTION = "stream-broker:profile-map";
|
|
336
|
+
var BROKER_PROFILE_MAP_SPEC = {
|
|
337
|
+
collection: BROKER_PROFILE_MAP_COLLECTION,
|
|
338
|
+
schema: ProfileAssignmentSchema,
|
|
339
|
+
columns: [{
|
|
340
|
+
name: "deviceId",
|
|
341
|
+
type: "INTEGER",
|
|
342
|
+
notNull: true
|
|
343
|
+
}, {
|
|
344
|
+
name: "auto",
|
|
345
|
+
type: "BOOLEAN",
|
|
346
|
+
notNull: true
|
|
347
|
+
}],
|
|
348
|
+
project: (key, value) => ({
|
|
349
|
+
deviceId: Number(key),
|
|
350
|
+
auto: value.auto
|
|
351
|
+
}),
|
|
352
|
+
deviceIdColumn: "deviceId",
|
|
353
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
354
|
+
};
|
|
355
|
+
function profileMapStore(store, logger) {
|
|
356
|
+
return new RowMapStore({
|
|
357
|
+
spec: BROKER_PROFILE_MAP_SPEC,
|
|
358
|
+
store,
|
|
359
|
+
logger
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* The dead `addon-settings` / `root` keys. A CLAIM that each key is dead,
|
|
364
|
+
* enforced by nothing else — the migration script names the same three, keep
|
|
365
|
+
* the two in lockstep.
|
|
366
|
+
*/
|
|
367
|
+
var STREAM_BROKER_RETIRED_ROOT_KEYS = [
|
|
368
|
+
{
|
|
369
|
+
key: "probeSnapshots",
|
|
370
|
+
successor: BROKER_PROBE_SNAPSHOTS_COLLECTION,
|
|
371
|
+
reason: "one row per broker since the flatten; as a blob it was 17 KB of the 33 KB root row, rewritten whole by the throttled metrics flush"
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
key: "rtspTokens",
|
|
375
|
+
successor: BROKER_RTSP_TOKENS_COLLECTION,
|
|
376
|
+
reason: "one row per broker since the flatten; as a blob a single minted token rewrote 33 KB"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
key: "profileMap",
|
|
380
|
+
successor: BROKER_PROFILE_MAP_COLLECTION,
|
|
381
|
+
reason: "one row per camera since the flatten; a per-DEVICE map, so as a blob it grew with the fleet — 2.3 KB at 29 cameras, ~77 KB at 974"
|
|
382
|
+
}
|
|
383
|
+
];
|
|
384
|
+
//#endregion
|
|
182
385
|
//#region src/stream-broker/battery-wake-on-play.ts
|
|
183
386
|
/**
|
|
184
387
|
* Wake-on-play — pressing play on a sleeping battery camera wakes it, and the
|
|
@@ -14190,15 +14393,18 @@ function readDeclaredAudioEnabled(v) {
|
|
|
14190
14393
|
/** Whole-blob schema for the persisted `deviceOverrides` store key:
|
|
14191
14394
|
* numeric-string deviceId → DeviceOverride (includes `derivedStreams`). */
|
|
14192
14395
|
var DeviceOverridesMapSchema = record(string(), DeviceOverrideSchema);
|
|
14193
|
-
/**
|
|
14194
|
-
*
|
|
14195
|
-
*
|
|
14196
|
-
|
|
14396
|
+
/**
|
|
14397
|
+
* The one persisted store KEY the broker still keeps as a blob: `rtspEnabled`,
|
|
14398
|
+
* brokerId → enabled.
|
|
14399
|
+
*
|
|
14400
|
+
* `rtspTokens` and `profileMap` moved to their own collections in the
|
|
14401
|
+
* 2026-08-19 flatten and their whole-blob schemas are gone with them — the row
|
|
14402
|
+
* shapes live in `broker-row-stores.ts`. `rtspEnabled` stayed, and why is
|
|
14403
|
+
* argued in that file's header: its persister replaces the map from the set of
|
|
14404
|
+
* CURRENTLY registered restreamers, and reproducing that on rows would mean a
|
|
14405
|
+
* prune driven by a map that shrinks on every unregister.
|
|
14406
|
+
*/
|
|
14197
14407
|
var RtspEnabledSchema = record(string(), boolean());
|
|
14198
|
-
var ProfileMapSchema = record(string(), object({
|
|
14199
|
-
map: record(string(), string()),
|
|
14200
|
-
auto: boolean()
|
|
14201
|
-
}));
|
|
14202
14408
|
var PUSH_KINDS = new Set(["push-annexb"]);
|
|
14203
14409
|
/** Walk a profile map and return the slot that points at `camStreamId`, or null. */
|
|
14204
14410
|
function findProfileForStream(map, camStreamId) {
|
|
@@ -34944,14 +35150,16 @@ var EventLoopStallMonitor = class {
|
|
|
34944
35150
|
//#endregion
|
|
34945
35151
|
//#region src/stream-broker/addon.ts
|
|
34946
35152
|
/**
|
|
34947
|
-
* Hydrate the persisted RTSP restream state
|
|
34948
|
-
*
|
|
34949
|
-
*
|
|
34950
|
-
*
|
|
34951
|
-
*
|
|
35153
|
+
* Hydrate the persisted RTSP restream state. Tokens come from their own
|
|
35154
|
+
* collection (one row per broker); `rtspEnabled` is still a key of the settings
|
|
35155
|
+
* row. An unparseable token row is skipped by the store — one dead token, not
|
|
35156
|
+
* every token — and an empty set keeps first-boot silence (no loader call, no
|
|
35157
|
+
* log).
|
|
34952
35158
|
*/
|
|
34953
35159
|
async function hydratePersistedRtspState(deps) {
|
|
34954
|
-
const
|
|
35160
|
+
const rows = await deps.tokensStore.readAll();
|
|
35161
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
35162
|
+
for (const [brokerId, row] of rows) tokens.set(brokerId, row.token);
|
|
34955
35163
|
if (tokens.size > 0) {
|
|
34956
35164
|
deps.loadTokens(tokens);
|
|
34957
35165
|
deps.logger.info("Loaded persisted RTSP tokens", { meta: { tokenCount: tokens.size } });
|
|
@@ -35053,7 +35261,7 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35053
35261
|
/** Durable per-stream probe map (powers the settings "Probed" field across
|
|
35054
35262
|
* restarts). Loaded on activate, flushed throttled from the metrics loop and
|
|
35055
35263
|
* once on shutdown. */
|
|
35056
|
-
|
|
35264
|
+
probeSnapshots = null;
|
|
35057
35265
|
lastProbePersistAt = 0;
|
|
35058
35266
|
constructor() {
|
|
35059
35267
|
super({
|
|
@@ -35129,13 +35337,15 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35129
35337
|
const localNodeId = localNode ? localNode.includes("/") ? localNode.split("/")[0] : localNode : void 0;
|
|
35130
35338
|
this.brokerManager.setApiAccess(this.ctx.api, localNodeId, this.ctx.kernel.hwaccel);
|
|
35131
35339
|
await this.loadFfmpegConfig();
|
|
35340
|
+
await this.declareRowCollections();
|
|
35341
|
+
await this.purgeFlattenedSettingsKeys();
|
|
35132
35342
|
const rtspProvider = this.brokerManager.getRtspRestreamProvider();
|
|
35133
35343
|
const brokerManager = this.brokerManager;
|
|
35134
|
-
const
|
|
35344
|
+
const rtspTokens = rtspTokensStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35135
35345
|
const rtspEnabledState = this.state("rtspEnabled", RtspEnabledSchema, {});
|
|
35136
35346
|
try {
|
|
35137
35347
|
await hydratePersistedRtspState({
|
|
35138
|
-
|
|
35348
|
+
tokensStore: rtspTokens,
|
|
35139
35349
|
enabledState: rtspEnabledState,
|
|
35140
35350
|
loadTokens: (tokens) => brokerManager.loadPersistedTokens(tokens),
|
|
35141
35351
|
loadEnabled: (states) => rtspProvider.loadPersistedEnabled(states),
|
|
@@ -35144,11 +35354,7 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35144
35354
|
} catch {}
|
|
35145
35355
|
rtspProvider.setTokenPersister(async (tokens) => {
|
|
35146
35356
|
try {
|
|
35147
|
-
await
|
|
35148
|
-
const merged = { ...prev };
|
|
35149
|
-
for (const [k, v] of tokens) merged[k] = v;
|
|
35150
|
-
return merged;
|
|
35151
|
-
});
|
|
35357
|
+
for (const [brokerId, token] of tokens) await rtspTokens.write(brokerId, { token });
|
|
35152
35358
|
this.ctx.logger.info("Persisted RTSP tokens", { meta: { current: tokens.size } });
|
|
35153
35359
|
} catch (err) {
|
|
35154
35360
|
this.ctx.logger.warn("Failed to persist RTSP tokens", { meta: { error: errMsg(err) } });
|
|
@@ -35181,22 +35387,22 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35181
35387
|
this.ctx.logger.warn("Failed to persist device overrides", { meta: { error: errMsg(err) } });
|
|
35182
35388
|
});
|
|
35183
35389
|
});
|
|
35184
|
-
const
|
|
35390
|
+
const profileMap = profileMapStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35185
35391
|
const profileMapPersister = (assignments) => {
|
|
35186
|
-
const
|
|
35187
|
-
for (const [deviceId, assignment] of assignments)
|
|
35392
|
+
const next = /* @__PURE__ */ new Map();
|
|
35393
|
+
for (const [deviceId, assignment] of assignments) next.set(`${deviceId}`, {
|
|
35188
35394
|
map: { ...assignment.map },
|
|
35189
35395
|
auto: assignment.auto
|
|
35190
|
-
};
|
|
35191
|
-
|
|
35396
|
+
});
|
|
35397
|
+
profileMap.replaceAll(next).catch((err) => {
|
|
35192
35398
|
this.ctx.logger.warn("Failed to persist profile map", { meta: { error: errMsg(err) } });
|
|
35193
35399
|
});
|
|
35194
35400
|
};
|
|
35195
35401
|
this.brokerManager.setProfileMapPersister(profileMapPersister);
|
|
35196
35402
|
try {
|
|
35197
|
-
const
|
|
35403
|
+
const rows = await profileMap.readAll();
|
|
35198
35404
|
const loaded = /* @__PURE__ */ new Map();
|
|
35199
|
-
for (const [rawKey, val] of
|
|
35405
|
+
for (const [rawKey, val] of rows) {
|
|
35200
35406
|
const deviceId = Number(rawKey);
|
|
35201
35407
|
if (!Number.isInteger(deviceId)) continue;
|
|
35202
35408
|
const map = {};
|
|
@@ -35214,12 +35420,13 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35214
35420
|
} catch (err) {
|
|
35215
35421
|
this.ctx.logger.warn("Failed to load profile map", { meta: { error: errMsg(err) } });
|
|
35216
35422
|
}
|
|
35217
|
-
const
|
|
35218
|
-
this.
|
|
35423
|
+
const probeSnapshots = probeSnapshotsStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35424
|
+
this.probeSnapshots = probeSnapshots;
|
|
35219
35425
|
try {
|
|
35220
|
-
const
|
|
35426
|
+
const rows = await probeSnapshots.readAll();
|
|
35427
|
+
const blob = Object.fromEntries(rows);
|
|
35221
35428
|
this.brokerManager.loadProbeSnapshots(blob);
|
|
35222
|
-
this.ctx.logger.info("Loaded persisted probe snapshots", { meta: { streamCount:
|
|
35429
|
+
this.ctx.logger.info("Loaded persisted probe snapshots", { meta: { streamCount: rows.size } });
|
|
35223
35430
|
} catch (err) {
|
|
35224
35431
|
this.ctx.logger.warn("Failed to load probe snapshots", { meta: { error: errMsg(err) } });
|
|
35225
35432
|
}
|
|
@@ -35576,17 +35783,54 @@ var StreamBrokerAddon = class extends BaseAddon {
|
|
|
35576
35783
|
this.flushProbeSnapshots(timestamp, false);
|
|
35577
35784
|
}
|
|
35578
35785
|
/**
|
|
35786
|
+
* Declare the three row collections that replaced the `probeSnapshots`,
|
|
35787
|
+
* `rtspTokens` and `profileMap` blob keys. Idempotent — a re-declaration of
|
|
35788
|
+
* the same shape is a no-op in the engine.
|
|
35789
|
+
*
|
|
35790
|
+
* Not swallowed: a broker whose `stream-broker:rtsp-tokens` is undeclared
|
|
35791
|
+
* mints a new token for every stream and every external NVR URL in the
|
|
35792
|
+
* installation goes dead. Crashing loudly is bounded by `CrashSupervisor`
|
|
35793
|
+
* (D6); silently reissuing tokens is not.
|
|
35794
|
+
*/
|
|
35795
|
+
async declareRowCollections() {
|
|
35796
|
+
const store = this.ctx.api.settingsStore;
|
|
35797
|
+
const logger = this.ctx.logger;
|
|
35798
|
+
await probeSnapshotsStore(store, logger).declare();
|
|
35799
|
+
await rtspTokensStore(store, logger).declare();
|
|
35800
|
+
await profileMapStore(store, logger).declare();
|
|
35801
|
+
}
|
|
35802
|
+
/**
|
|
35803
|
+
* Drop the flattened keys from `stream-broker:addon-settings` / `root`, each
|
|
35804
|
+
* gated on its own successor collection being non-empty. `rtspEnabled` and
|
|
35805
|
+
* `deviceOverrides` stay — see `broker-row-stores.ts` for why.
|
|
35806
|
+
*
|
|
35807
|
+
* Best-effort: a purge that throws must not take the broker down. The keys
|
|
35808
|
+
* are dead weight, not a fault.
|
|
35809
|
+
*/
|
|
35810
|
+
async purgeFlattenedSettingsKeys() {
|
|
35811
|
+
try {
|
|
35812
|
+
await retireRootKeys({
|
|
35813
|
+
store: this.ctx.api.settingsStore,
|
|
35814
|
+
addonId: STREAM_BROKER_ADDON_ID,
|
|
35815
|
+
logger: this.ctx.logger,
|
|
35816
|
+
specs: STREAM_BROKER_RETIRED_ROOT_KEYS
|
|
35817
|
+
});
|
|
35818
|
+
} catch (err) {
|
|
35819
|
+
this.ctx.logger.warn("stream-broker: retiring the flattened settings keys failed", { meta: { error: errMsg(err) } });
|
|
35820
|
+
}
|
|
35821
|
+
}
|
|
35822
|
+
/**
|
|
35579
35823
|
* Persist the manager's probe map to the durable handle. Throttled to
|
|
35580
35824
|
* PROBE_SNAPSHOTS_PERSIST_INTERVAL_MS (or `force` on shutdown) so a busy
|
|
35581
35825
|
* streaming fleet doesn't drive a write per metrics tick.
|
|
35582
35826
|
*/
|
|
35583
35827
|
flushProbeSnapshots(now, force) {
|
|
35584
|
-
const
|
|
35828
|
+
const store = this.probeSnapshots;
|
|
35585
35829
|
const manager = this.brokerManager;
|
|
35586
|
-
if (!
|
|
35830
|
+
if (!store || !manager) return;
|
|
35587
35831
|
if (!force && now - this.lastProbePersistAt < PROBE_SNAPSHOTS_PERSIST_INTERVAL_MS) return;
|
|
35588
35832
|
this.lastProbePersistAt = now;
|
|
35589
|
-
|
|
35833
|
+
store.replaceAll(new Map(Object.entries(manager.getProbeSnapshots()))).catch((err) => {
|
|
35590
35834
|
this.ctx.logger.warn("Failed to persist probe snapshots", { meta: { error: errMsg(err) } });
|
|
35591
35835
|
});
|
|
35592
35836
|
}
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C4fRkYcj.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.101",
|
|
4
4
|
"description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|