@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
|
@@ -4,6 +4,7 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
const require_dist = require("../dist-dm3t4BOt.js");
|
|
6
6
|
const require_remote_restream = require("../remote-restream-BYbAsgUf.js");
|
|
7
|
+
const require_retire_root_keys = require("../retire-root-keys-KE6D6Xh_.js");
|
|
7
8
|
let node_crypto = require("node:crypto");
|
|
8
9
|
node_crypto = require_dist.__toESM(node_crypto, 1);
|
|
9
10
|
let node_child_process = require("node:child_process");
|
|
@@ -117,8 +118,9 @@ function createClientLogIngest(onEntries) {
|
|
|
117
118
|
*/
|
|
118
119
|
/**
|
|
119
120
|
* Last-observed runtime probe (present only after the stream was pulled once).
|
|
120
|
-
* Zod-backed because it is persisted durably (
|
|
121
|
-
*
|
|
121
|
+
* Zod-backed because it is persisted durably (ONE ROW per broker in
|
|
122
|
+
* `stream-broker:probe-snapshots`) so the field stays populated across a hub
|
|
123
|
+
* restart — the schema is the single source of truth, applied per row.
|
|
122
124
|
*/
|
|
123
125
|
var ProbedSnapshotSchema = require_dist.object({
|
|
124
126
|
/** Detected video codec — preferred over the declared one when present. */
|
|
@@ -134,8 +136,7 @@ var ProbedSnapshotSchema = require_dist.object({
|
|
|
134
136
|
/** Epoch ms the snapshot was captured. */
|
|
135
137
|
at: require_dist.number()
|
|
136
138
|
});
|
|
137
|
-
|
|
138
|
-
var ProbedSnapshotMapSchema = require_dist.record(require_dist.string(), ProbedSnapshotSchema);
|
|
139
|
+
require_dist.record(require_dist.string(), ProbedSnapshotSchema);
|
|
139
140
|
var SEP = " · ";
|
|
140
141
|
function formatCodec(codec) {
|
|
141
142
|
const lower = codec.toLowerCase();
|
|
@@ -184,6 +185,208 @@ function formatProbedSummary(declared, snapshot, opts) {
|
|
|
184
185
|
return parts.join(SEP);
|
|
185
186
|
}
|
|
186
187
|
//#endregion
|
|
188
|
+
//#region src/stream-broker/broker-row-stores.ts
|
|
189
|
+
/**
|
|
190
|
+
* The stream-broker's flattened settings maps — one row per entry.
|
|
191
|
+
*
|
|
192
|
+
* ## What this replaced
|
|
193
|
+
*
|
|
194
|
+
* `stream-broker:addon-settings` / `root` was 33 155 bytes on the reference
|
|
195
|
+
* hub, and three keys were 32 735 of it: `probeSnapshots` (17 282 B, 109
|
|
196
|
+
* streams), `rtspTokens` (13 147 B, 243 streams) and `profileMap` (2 306 B, 29
|
|
197
|
+
* cameras — a per-DEVICE map, so it grows with the fleet). Every read of that
|
|
198
|
+
* row parsed all of it, including `BaseAddon.resolveConfig`, and every write
|
|
199
|
+
* rewrote all of it — a probe flush every few seconds while streams are live.
|
|
200
|
+
*
|
|
201
|
+
* Two keys deliberately STAY blob keys on that row, and the reasons are not
|
|
202
|
+
* "they are small":
|
|
203
|
+
*
|
|
204
|
+
* - `rtspEnabled` — brokerId → boolean, EMPTY on the reference hub. Its
|
|
205
|
+
* persister replaces the whole map from the set of registered restreamers,
|
|
206
|
+
* and reproducing that on rows means an authoritative prune. A prune driven
|
|
207
|
+
* by a map that shrinks whenever a broker unregisters is exactly the shape
|
|
208
|
+
* that turns an unbind into a wipe (D130, D49), and the twin key
|
|
209
|
+
* `rtspTokens` carries a comment about precisely that loss. Nothing is
|
|
210
|
+
* bought by moving 0 bytes at that risk.
|
|
211
|
+
* - `deviceOverrides` — 354 B, two cameras, same replace-the-whole-map
|
|
212
|
+
* persister, written only when an operator edits an override.
|
|
213
|
+
*
|
|
214
|
+
* The row survives either way: it is also where `BaseAddon.resolveConfig` reads
|
|
215
|
+
* the operator's config fields from. The problem was a 33 KB row, not a blob.
|
|
216
|
+
*/
|
|
217
|
+
/**
|
|
218
|
+
* The addon id — the settings-store namespace whose `root` row the flattened
|
|
219
|
+
* keys are being retired from. Matches `packages/addon-pipeline/package.json`
|
|
220
|
+
* (`camstack.addons[].id`).
|
|
221
|
+
*/
|
|
222
|
+
var STREAM_BROKER_ADDON_ID = "stream-broker";
|
|
223
|
+
/**
|
|
224
|
+
* Every one of these collections is keyed by the BROKER ID
|
|
225
|
+
* (`<deviceId>/<camStreamId>`) or by the numeric deviceId. `brokerDeviceId`
|
|
226
|
+
* recovers the numeric device from either, for the projected column that makes
|
|
227
|
+
* a per-camera question an indexed query — and for `tags: { deviceId }` on
|
|
228
|
+
* every line these stores log.
|
|
229
|
+
*
|
|
230
|
+
* Returns `null` rather than guessing: a key whose head is not a number is a
|
|
231
|
+
* key this addon did not write, and inventing a device for it would put a row
|
|
232
|
+
* on a camera at random.
|
|
233
|
+
*/
|
|
234
|
+
function brokerDeviceId(key) {
|
|
235
|
+
const head = key.includes("/") ? key.slice(0, key.indexOf("/")) : key;
|
|
236
|
+
const id = Number(head);
|
|
237
|
+
return Number.isInteger(id) && id > 0 ? id : null;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Bound for a whole-map read. `settings-store.query` silently caps an unbounded
|
|
241
|
+
* read at 2 000 rows and answers as if that were everything; at 974 devices ×
|
|
242
|
+
* up to 5 streams the default is HALF the margin, so every read here names its
|
|
243
|
+
* own ceiling and a truncation is reported rather than shrunk.
|
|
244
|
+
*/
|
|
245
|
+
var BROKER_ROWS_LIMIT = 2e4;
|
|
246
|
+
/**
|
|
247
|
+
* @durable class=mirror owner=stream-broker
|
|
248
|
+
* write="one row per broker (`<deviceId>/<camStreamId>`), replaced wholesale from the
|
|
249
|
+
* manager's live probe map by the throttled metrics flush and once on shutdown"
|
|
250
|
+
* retention="the flush is authoritative: a broker the manager no longer holds loses its
|
|
251
|
+
* row. Losing the whole collection costs the settings 'Probed' field showing 'not yet
|
|
252
|
+
* probed' until the stream is pulled again — it is re-derived from the next probe."
|
|
253
|
+
*/
|
|
254
|
+
var BROKER_PROBE_SNAPSHOTS_COLLECTION = "stream-broker:probe-snapshots";
|
|
255
|
+
var BROKER_PROBE_SNAPSHOTS_SPEC = {
|
|
256
|
+
collection: BROKER_PROBE_SNAPSHOTS_COLLECTION,
|
|
257
|
+
schema: ProbedSnapshotSchema,
|
|
258
|
+
columns: [{
|
|
259
|
+
name: "deviceId",
|
|
260
|
+
type: "INTEGER"
|
|
261
|
+
}, {
|
|
262
|
+
name: "at",
|
|
263
|
+
type: "INTEGER",
|
|
264
|
+
notNull: true
|
|
265
|
+
}],
|
|
266
|
+
indexes: [{
|
|
267
|
+
name: "idx_stream_broker_probe_device",
|
|
268
|
+
columns: ["deviceId"]
|
|
269
|
+
}],
|
|
270
|
+
project: (key, value) => ({
|
|
271
|
+
deviceId: brokerDeviceId(key),
|
|
272
|
+
at: value.at
|
|
273
|
+
}),
|
|
274
|
+
deviceIdColumn: "deviceId",
|
|
275
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
276
|
+
};
|
|
277
|
+
function probeSnapshotsStore(store, logger) {
|
|
278
|
+
return new require_retire_root_keys.RowMapStore({
|
|
279
|
+
spec: BROKER_PROBE_SNAPSHOTS_SPEC,
|
|
280
|
+
store,
|
|
281
|
+
logger
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* The token, wrapped in an object rather than stored bare.
|
|
286
|
+
*
|
|
287
|
+
* A bare string in a `JSON` column round-trips only because the engine's
|
|
288
|
+
* decoder JSON-parses a stored string ONLY when it starts with `{` or `[`. That
|
|
289
|
+
* is a heuristic, not a contract; a token is opaque hex today and a future
|
|
290
|
+
* format that starts with a brace would come back as an object. One field costs
|
|
291
|
+
* nothing and makes the row self-describing.
|
|
292
|
+
*/
|
|
293
|
+
var RtspTokenRowSchema = require_dist.object({ token: require_dist.string().min(1) });
|
|
294
|
+
/**
|
|
295
|
+
* @durable class=ledger owner=stream-broker
|
|
296
|
+
* write="one row per broker (`<deviceId>/<camStreamId>`), upserted when the restream
|
|
297
|
+
* server mints a token. MERGE only — a token is stable forever per stream unless it
|
|
298
|
+
* is explicitly regenerated"
|
|
299
|
+
* retention="nothing deletes a row. The persister receives only the CURRENTLY registered
|
|
300
|
+
* restreamers, so a replace would erase the token of an unregistered broker and turn
|
|
301
|
+
* an external NVR URL into a dead 404 at the next idle or reboot."
|
|
302
|
+
*/
|
|
303
|
+
var BROKER_RTSP_TOKENS_COLLECTION = "stream-broker:rtsp-tokens";
|
|
304
|
+
var BROKER_RTSP_TOKENS_SPEC = {
|
|
305
|
+
collection: BROKER_RTSP_TOKENS_COLLECTION,
|
|
306
|
+
schema: RtspTokenRowSchema,
|
|
307
|
+
columns: [{
|
|
308
|
+
name: "deviceId",
|
|
309
|
+
type: "INTEGER"
|
|
310
|
+
}],
|
|
311
|
+
indexes: [{
|
|
312
|
+
name: "idx_stream_broker_rtsp_tokens_device",
|
|
313
|
+
columns: ["deviceId"]
|
|
314
|
+
}],
|
|
315
|
+
project: (key) => ({ deviceId: brokerDeviceId(key) }),
|
|
316
|
+
deviceIdColumn: "deviceId",
|
|
317
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
318
|
+
};
|
|
319
|
+
function rtspTokensStore(store, logger) {
|
|
320
|
+
return new require_retire_root_keys.RowMapStore({
|
|
321
|
+
spec: BROKER_RTSP_TOKENS_SPEC,
|
|
322
|
+
store,
|
|
323
|
+
logger
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/** One camera's profile → camStreamId assignment, plus whether it is automatic. */
|
|
327
|
+
var ProfileAssignmentSchema = require_dist.object({
|
|
328
|
+
map: require_dist.record(require_dist.string(), require_dist.string()),
|
|
329
|
+
auto: require_dist.boolean()
|
|
330
|
+
});
|
|
331
|
+
/**
|
|
332
|
+
* @durable class=config owner=stream-broker
|
|
333
|
+
* write="one row per camera, replaced wholesale from the manager's assignment map on
|
|
334
|
+
* every profile mutation — the map is authoritative and its boot pass PRUNES cameras
|
|
335
|
+
* the device registry no longer knows, which is why the write is a replace and not
|
|
336
|
+
* an upsert"
|
|
337
|
+
* retention="the replace is the retention: a camera dropped from the manager's map loses
|
|
338
|
+
* its row. Losing a row costs one automatic re-assignment on the next catalog pull."
|
|
339
|
+
*/
|
|
340
|
+
var BROKER_PROFILE_MAP_COLLECTION = "stream-broker:profile-map";
|
|
341
|
+
var BROKER_PROFILE_MAP_SPEC = {
|
|
342
|
+
collection: BROKER_PROFILE_MAP_COLLECTION,
|
|
343
|
+
schema: ProfileAssignmentSchema,
|
|
344
|
+
columns: [{
|
|
345
|
+
name: "deviceId",
|
|
346
|
+
type: "INTEGER",
|
|
347
|
+
notNull: true
|
|
348
|
+
}, {
|
|
349
|
+
name: "auto",
|
|
350
|
+
type: "BOOLEAN",
|
|
351
|
+
notNull: true
|
|
352
|
+
}],
|
|
353
|
+
project: (key, value) => ({
|
|
354
|
+
deviceId: Number(key),
|
|
355
|
+
auto: value.auto
|
|
356
|
+
}),
|
|
357
|
+
deviceIdColumn: "deviceId",
|
|
358
|
+
loadLimit: BROKER_ROWS_LIMIT
|
|
359
|
+
};
|
|
360
|
+
function profileMapStore(store, logger) {
|
|
361
|
+
return new require_retire_root_keys.RowMapStore({
|
|
362
|
+
spec: BROKER_PROFILE_MAP_SPEC,
|
|
363
|
+
store,
|
|
364
|
+
logger
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* The dead `addon-settings` / `root` keys. A CLAIM that each key is dead,
|
|
369
|
+
* enforced by nothing else — the migration script names the same three, keep
|
|
370
|
+
* the two in lockstep.
|
|
371
|
+
*/
|
|
372
|
+
var STREAM_BROKER_RETIRED_ROOT_KEYS = [
|
|
373
|
+
{
|
|
374
|
+
key: "probeSnapshots",
|
|
375
|
+
successor: BROKER_PROBE_SNAPSHOTS_COLLECTION,
|
|
376
|
+
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"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
key: "rtspTokens",
|
|
380
|
+
successor: BROKER_RTSP_TOKENS_COLLECTION,
|
|
381
|
+
reason: "one row per broker since the flatten; as a blob a single minted token rewrote 33 KB"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
key: "profileMap",
|
|
385
|
+
successor: BROKER_PROFILE_MAP_COLLECTION,
|
|
386
|
+
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"
|
|
387
|
+
}
|
|
388
|
+
];
|
|
389
|
+
//#endregion
|
|
187
390
|
//#region src/stream-broker/battery-wake-on-play.ts
|
|
188
391
|
/**
|
|
189
392
|
* Wake-on-play — pressing play on a sleeping battery camera wakes it, and the
|
|
@@ -14195,15 +14398,18 @@ function readDeclaredAudioEnabled(v) {
|
|
|
14195
14398
|
/** Whole-blob schema for the persisted `deviceOverrides` store key:
|
|
14196
14399
|
* numeric-string deviceId → DeviceOverride (includes `derivedStreams`). */
|
|
14197
14400
|
var DeviceOverridesMapSchema = require_dist.record(require_dist.string(), DeviceOverrideSchema);
|
|
14198
|
-
/**
|
|
14199
|
-
*
|
|
14200
|
-
*
|
|
14201
|
-
|
|
14401
|
+
/**
|
|
14402
|
+
* The one persisted store KEY the broker still keeps as a blob: `rtspEnabled`,
|
|
14403
|
+
* brokerId → enabled.
|
|
14404
|
+
*
|
|
14405
|
+
* `rtspTokens` and `profileMap` moved to their own collections in the
|
|
14406
|
+
* 2026-08-19 flatten and their whole-blob schemas are gone with them — the row
|
|
14407
|
+
* shapes live in `broker-row-stores.ts`. `rtspEnabled` stayed, and why is
|
|
14408
|
+
* argued in that file's header: its persister replaces the map from the set of
|
|
14409
|
+
* CURRENTLY registered restreamers, and reproducing that on rows would mean a
|
|
14410
|
+
* prune driven by a map that shrinks on every unregister.
|
|
14411
|
+
*/
|
|
14202
14412
|
var RtspEnabledSchema = require_dist.record(require_dist.string(), require_dist.boolean());
|
|
14203
|
-
var ProfileMapSchema = require_dist.record(require_dist.string(), require_dist.object({
|
|
14204
|
-
map: require_dist.record(require_dist.string(), require_dist.string()),
|
|
14205
|
-
auto: require_dist.boolean()
|
|
14206
|
-
}));
|
|
14207
14413
|
var PUSH_KINDS = new Set(["push-annexb"]);
|
|
14208
14414
|
/** Walk a profile map and return the slot that points at `camStreamId`, or null. */
|
|
14209
14415
|
function findProfileForStream(map, camStreamId) {
|
|
@@ -34952,14 +35158,16 @@ var EventLoopStallMonitor = class {
|
|
|
34952
35158
|
//#endregion
|
|
34953
35159
|
//#region src/stream-broker/addon.ts
|
|
34954
35160
|
/**
|
|
34955
|
-
* Hydrate the persisted RTSP restream state
|
|
34956
|
-
*
|
|
34957
|
-
*
|
|
34958
|
-
*
|
|
34959
|
-
*
|
|
35161
|
+
* Hydrate the persisted RTSP restream state. Tokens come from their own
|
|
35162
|
+
* collection (one row per broker); `rtspEnabled` is still a key of the settings
|
|
35163
|
+
* row. An unparseable token row is skipped by the store — one dead token, not
|
|
35164
|
+
* every token — and an empty set keeps first-boot silence (no loader call, no
|
|
35165
|
+
* log).
|
|
34960
35166
|
*/
|
|
34961
35167
|
async function hydratePersistedRtspState(deps) {
|
|
34962
|
-
const
|
|
35168
|
+
const rows = await deps.tokensStore.readAll();
|
|
35169
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
35170
|
+
for (const [brokerId, row] of rows) tokens.set(brokerId, row.token);
|
|
34963
35171
|
if (tokens.size > 0) {
|
|
34964
35172
|
deps.loadTokens(tokens);
|
|
34965
35173
|
deps.logger.info("Loaded persisted RTSP tokens", { meta: { tokenCount: tokens.size } });
|
|
@@ -35061,7 +35269,7 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35061
35269
|
/** Durable per-stream probe map (powers the settings "Probed" field across
|
|
35062
35270
|
* restarts). Loaded on activate, flushed throttled from the metrics loop and
|
|
35063
35271
|
* once on shutdown. */
|
|
35064
|
-
|
|
35272
|
+
probeSnapshots = null;
|
|
35065
35273
|
lastProbePersistAt = 0;
|
|
35066
35274
|
constructor() {
|
|
35067
35275
|
super({
|
|
@@ -35137,13 +35345,15 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35137
35345
|
const localNodeId = localNode ? localNode.includes("/") ? localNode.split("/")[0] : localNode : void 0;
|
|
35138
35346
|
this.brokerManager.setApiAccess(this.ctx.api, localNodeId, this.ctx.kernel.hwaccel);
|
|
35139
35347
|
await this.loadFfmpegConfig();
|
|
35348
|
+
await this.declareRowCollections();
|
|
35349
|
+
await this.purgeFlattenedSettingsKeys();
|
|
35140
35350
|
const rtspProvider = this.brokerManager.getRtspRestreamProvider();
|
|
35141
35351
|
const brokerManager = this.brokerManager;
|
|
35142
|
-
const
|
|
35352
|
+
const rtspTokens = rtspTokensStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35143
35353
|
const rtspEnabledState = this.state("rtspEnabled", RtspEnabledSchema, {});
|
|
35144
35354
|
try {
|
|
35145
35355
|
await hydratePersistedRtspState({
|
|
35146
|
-
|
|
35356
|
+
tokensStore: rtspTokens,
|
|
35147
35357
|
enabledState: rtspEnabledState,
|
|
35148
35358
|
loadTokens: (tokens) => brokerManager.loadPersistedTokens(tokens),
|
|
35149
35359
|
loadEnabled: (states) => rtspProvider.loadPersistedEnabled(states),
|
|
@@ -35152,11 +35362,7 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35152
35362
|
} catch {}
|
|
35153
35363
|
rtspProvider.setTokenPersister(async (tokens) => {
|
|
35154
35364
|
try {
|
|
35155
|
-
await
|
|
35156
|
-
const merged = { ...prev };
|
|
35157
|
-
for (const [k, v] of tokens) merged[k] = v;
|
|
35158
|
-
return merged;
|
|
35159
|
-
});
|
|
35365
|
+
for (const [brokerId, token] of tokens) await rtspTokens.write(brokerId, { token });
|
|
35160
35366
|
this.ctx.logger.info("Persisted RTSP tokens", { meta: { current: tokens.size } });
|
|
35161
35367
|
} catch (err) {
|
|
35162
35368
|
this.ctx.logger.warn("Failed to persist RTSP tokens", { meta: { error: require_dist.errMsg(err) } });
|
|
@@ -35189,22 +35395,22 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35189
35395
|
this.ctx.logger.warn("Failed to persist device overrides", { meta: { error: require_dist.errMsg(err) } });
|
|
35190
35396
|
});
|
|
35191
35397
|
});
|
|
35192
|
-
const
|
|
35398
|
+
const profileMap = profileMapStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35193
35399
|
const profileMapPersister = (assignments) => {
|
|
35194
|
-
const
|
|
35195
|
-
for (const [deviceId, assignment] of assignments)
|
|
35400
|
+
const next = /* @__PURE__ */ new Map();
|
|
35401
|
+
for (const [deviceId, assignment] of assignments) next.set(`${deviceId}`, {
|
|
35196
35402
|
map: { ...assignment.map },
|
|
35197
35403
|
auto: assignment.auto
|
|
35198
|
-
};
|
|
35199
|
-
|
|
35404
|
+
});
|
|
35405
|
+
profileMap.replaceAll(next).catch((err) => {
|
|
35200
35406
|
this.ctx.logger.warn("Failed to persist profile map", { meta: { error: require_dist.errMsg(err) } });
|
|
35201
35407
|
});
|
|
35202
35408
|
};
|
|
35203
35409
|
this.brokerManager.setProfileMapPersister(profileMapPersister);
|
|
35204
35410
|
try {
|
|
35205
|
-
const
|
|
35411
|
+
const rows = await profileMap.readAll();
|
|
35206
35412
|
const loaded = /* @__PURE__ */ new Map();
|
|
35207
|
-
for (const [rawKey, val] of
|
|
35413
|
+
for (const [rawKey, val] of rows) {
|
|
35208
35414
|
const deviceId = Number(rawKey);
|
|
35209
35415
|
if (!Number.isInteger(deviceId)) continue;
|
|
35210
35416
|
const map = {};
|
|
@@ -35222,12 +35428,13 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35222
35428
|
} catch (err) {
|
|
35223
35429
|
this.ctx.logger.warn("Failed to load profile map", { meta: { error: require_dist.errMsg(err) } });
|
|
35224
35430
|
}
|
|
35225
|
-
const
|
|
35226
|
-
this.
|
|
35431
|
+
const probeSnapshots = probeSnapshotsStore(this.ctx.api.settingsStore, this.ctx.logger);
|
|
35432
|
+
this.probeSnapshots = probeSnapshots;
|
|
35227
35433
|
try {
|
|
35228
|
-
const
|
|
35434
|
+
const rows = await probeSnapshots.readAll();
|
|
35435
|
+
const blob = Object.fromEntries(rows);
|
|
35229
35436
|
this.brokerManager.loadProbeSnapshots(blob);
|
|
35230
|
-
this.ctx.logger.info("Loaded persisted probe snapshots", { meta: { streamCount:
|
|
35437
|
+
this.ctx.logger.info("Loaded persisted probe snapshots", { meta: { streamCount: rows.size } });
|
|
35231
35438
|
} catch (err) {
|
|
35232
35439
|
this.ctx.logger.warn("Failed to load probe snapshots", { meta: { error: require_dist.errMsg(err) } });
|
|
35233
35440
|
}
|
|
@@ -35584,17 +35791,54 @@ var StreamBrokerAddon = class extends require_dist.BaseAddon {
|
|
|
35584
35791
|
this.flushProbeSnapshots(timestamp, false);
|
|
35585
35792
|
}
|
|
35586
35793
|
/**
|
|
35794
|
+
* Declare the three row collections that replaced the `probeSnapshots`,
|
|
35795
|
+
* `rtspTokens` and `profileMap` blob keys. Idempotent — a re-declaration of
|
|
35796
|
+
* the same shape is a no-op in the engine.
|
|
35797
|
+
*
|
|
35798
|
+
* Not swallowed: a broker whose `stream-broker:rtsp-tokens` is undeclared
|
|
35799
|
+
* mints a new token for every stream and every external NVR URL in the
|
|
35800
|
+
* installation goes dead. Crashing loudly is bounded by `CrashSupervisor`
|
|
35801
|
+
* (D6); silently reissuing tokens is not.
|
|
35802
|
+
*/
|
|
35803
|
+
async declareRowCollections() {
|
|
35804
|
+
const store = this.ctx.api.settingsStore;
|
|
35805
|
+
const logger = this.ctx.logger;
|
|
35806
|
+
await probeSnapshotsStore(store, logger).declare();
|
|
35807
|
+
await rtspTokensStore(store, logger).declare();
|
|
35808
|
+
await profileMapStore(store, logger).declare();
|
|
35809
|
+
}
|
|
35810
|
+
/**
|
|
35811
|
+
* Drop the flattened keys from `stream-broker:addon-settings` / `root`, each
|
|
35812
|
+
* gated on its own successor collection being non-empty. `rtspEnabled` and
|
|
35813
|
+
* `deviceOverrides` stay — see `broker-row-stores.ts` for why.
|
|
35814
|
+
*
|
|
35815
|
+
* Best-effort: a purge that throws must not take the broker down. The keys
|
|
35816
|
+
* are dead weight, not a fault.
|
|
35817
|
+
*/
|
|
35818
|
+
async purgeFlattenedSettingsKeys() {
|
|
35819
|
+
try {
|
|
35820
|
+
await require_retire_root_keys.retireRootKeys({
|
|
35821
|
+
store: this.ctx.api.settingsStore,
|
|
35822
|
+
addonId: STREAM_BROKER_ADDON_ID,
|
|
35823
|
+
logger: this.ctx.logger,
|
|
35824
|
+
specs: STREAM_BROKER_RETIRED_ROOT_KEYS
|
|
35825
|
+
});
|
|
35826
|
+
} catch (err) {
|
|
35827
|
+
this.ctx.logger.warn("stream-broker: retiring the flattened settings keys failed", { meta: { error: require_dist.errMsg(err) } });
|
|
35828
|
+
}
|
|
35829
|
+
}
|
|
35830
|
+
/**
|
|
35587
35831
|
* Persist the manager's probe map to the durable handle. Throttled to
|
|
35588
35832
|
* PROBE_SNAPSHOTS_PERSIST_INTERVAL_MS (or `force` on shutdown) so a busy
|
|
35589
35833
|
* streaming fleet doesn't drive a write per metrics tick.
|
|
35590
35834
|
*/
|
|
35591
35835
|
flushProbeSnapshots(now, force) {
|
|
35592
|
-
const
|
|
35836
|
+
const store = this.probeSnapshots;
|
|
35593
35837
|
const manager = this.brokerManager;
|
|
35594
|
-
if (!
|
|
35838
|
+
if (!store || !manager) return;
|
|
35595
35839
|
if (!force && now - this.lastProbePersistAt < PROBE_SNAPSHOTS_PERSIST_INTERVAL_MS) return;
|
|
35596
35840
|
this.lastProbePersistAt = now;
|
|
35597
|
-
|
|
35841
|
+
store.replaceAll(new Map(Object.entries(manager.getProbeSnapshots()))).catch((err) => {
|
|
35598
35842
|
this.ctx.logger.warn("Failed to persist probe snapshots", { meta: { error: require_dist.errMsg(err) } });
|
|
35599
35843
|
});
|
|
35600
35844
|
}
|