@camstack/addon-pipeline 1.2.370 → 1.2.371
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/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +3 -3
- package/dist/{custom-model-catalog-CVbK0xKc.mjs → custom-model-catalog-C6C2cSwd.mjs} +2 -2
- package/dist/{custom-model-catalog-BFosahFu.js → custom-model-catalog-n9v8MUL6.js} +2 -2
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-CbFPOFAD.mjs → dist-8AEiKOFO.mjs} +291 -21
- package/dist/{dist-jkwxbcbK.js → dist-DmuX7IFJ.js} +308 -20
- package/dist/{model-catalogs-DJmwfLfC.mjs → model-catalogs-BSWAHZJh.mjs} +1 -1
- package/dist/{model-catalogs-Ds2hC2Br.js → model-catalogs-DO4aA1qo.js} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-C-2fB9kH.mjs → node-Bd4va7c-.mjs} +1 -1
- package/dist/{node-C_1U1uzn.js → node-DqA6nD1z.js} +1 -1
- package/dist/pipeline-runner/index.js +3 -3
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-Cfh1BQYg.js → process-memory-2OQ2oxrz.js} +1 -1
- package/dist/{process-memory-B3I-NvnX.mjs → process-memory-BvS2Z88w.mjs} +1 -1
- package/dist/recorder/index.js +184 -54
- package/dist/recorder/index.mjs +184 -54
- package/dist/{segment-demux-js-uxBcJy15.js → segment-demux-js-BkGK1rhu.js} +1 -1
- package/dist/{segment-demux-js-D3JBJeN6.mjs → segment-demux-js-By9MLn_g.mjs} +1 -1
- package/dist/stream-broker/_stub.js +1188 -861
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DwwDKaZJ.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DjoAY42S.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C3ssSNKh.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-nua-KuDg.mjs +26 -0
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-DVwYY7oo.mjs → hostInit-DPiGNc5u.mjs} +3 -3
- package/dist/stream-broker/index.js +3 -3
- package/dist/stream-broker/index.mjs +3 -3
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/i18n/en.json +8 -0
- package/i18n/it.json +8 -0
- package/package.json +6 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bna0L8uC.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAvXcDKy.mjs +0 -26
package/dist/recorder/index.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
6
|
+
const require_dist = require("../dist-DmuX7IFJ.js");
|
|
7
|
+
const require_node = require("../node-DqA6nD1z.js");
|
|
8
8
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
9
9
|
const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
|
|
10
10
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
|
@@ -4996,9 +4996,10 @@ function archiveUsageComplete(ledger, locationIds) {
|
|
|
4996
4996
|
/**
|
|
4997
4997
|
* Per-device recording-config persistence for the recorder addon.
|
|
4998
4998
|
*
|
|
4999
|
-
*
|
|
5000
|
-
*
|
|
5001
|
-
* saving its config writes that
|
|
4999
|
+
* A camera's config is its PER-DEVICE SETTINGS (D865), key `recordingConfig`,
|
|
5000
|
+
* through `ctx.settings.*DeviceStore`. Asking what camera 617 records is one
|
|
5001
|
+
* scope read; saving its config writes that key and touches no other camera.
|
|
5002
|
+
* Deleting the camera clears the scope centrally.
|
|
5002
5003
|
*
|
|
5003
5004
|
* ## What this replaced, and why
|
|
5004
5005
|
*
|
|
@@ -5019,54 +5020,79 @@ function archiveUsageComplete(ledger, locationIds) {
|
|
|
5019
5020
|
* empty fallback (= the whole fleet silently unconfigured).
|
|
5020
5021
|
*/
|
|
5021
5022
|
/**
|
|
5022
|
-
*
|
|
5023
|
-
*
|
|
5024
|
-
*
|
|
5025
|
-
*
|
|
5026
|
-
*
|
|
5027
|
-
*
|
|
5023
|
+
* READ is forgiving where the wire contract is strict — see the header.
|
|
5024
|
+
* `.strip()` keeps a row written before a key was retired readable.
|
|
5025
|
+
*
|
|
5026
|
+
* The preprocess is the D371→D380 migration, applied on EVERY read so a row
|
|
5027
|
+
* behaves correctly from the first read of the boot, before
|
|
5028
|
+
* {@link migrateLegacyDeviceSignalRows} has persisted anything. Pure and
|
|
5029
|
+
* idempotent — a row with no legacy key passes through by reference.
|
|
5028
5030
|
*/
|
|
5029
|
-
var
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5031
|
+
var StoredRecordingConfigSchema = require_dist.preprocess((raw) => require_dist.migrateLegacyDeviceSignalConfig(raw).value, require_dist.RecordingConfigSchema.strip()).transform((config) => require_dist.stripRetiredBandPreBufferSec(config).config);
|
|
5032
|
+
/** The per-device settings key a camera's recording config lives under. */
|
|
5033
|
+
var RECORDING_CONFIG_KEY = "recordingConfig";
|
|
5034
|
+
var DeviceSettingsConfigsState = class {
|
|
5035
|
+
deps;
|
|
5036
|
+
constructor(deps) {
|
|
5037
|
+
this.deps = deps;
|
|
5038
|
+
}
|
|
5039
|
+
async declare() {}
|
|
5040
|
+
parse(deviceId, raw) {
|
|
5041
|
+
const parsed = StoredRecordingConfigSchema.safeParse(raw);
|
|
5042
|
+
if (parsed.success) return parsed.data;
|
|
5043
|
+
this.deps.logger.warn("recording config row skipped — it does not parse", {
|
|
5044
|
+
tags: { deviceId },
|
|
5045
|
+
meta: { error: parsed.error.message }
|
|
5046
|
+
});
|
|
5047
|
+
return null;
|
|
5048
|
+
}
|
|
5049
|
+
async read(key) {
|
|
5050
|
+
const deviceId = Number(key);
|
|
5051
|
+
if (!Number.isInteger(deviceId)) return null;
|
|
5052
|
+
const held = (await this.deps.settings.readDeviceStore(deviceId))[RECORDING_CONFIG_KEY];
|
|
5053
|
+
return held === void 0 ? null : this.parse(deviceId, held);
|
|
5054
|
+
}
|
|
5055
|
+
async readAllRaw() {
|
|
5056
|
+
const out = /* @__PURE__ */ new Map();
|
|
5057
|
+
const list = this.deps.settings.listDeviceStoreIds;
|
|
5058
|
+
if (list === void 0) throw new Error("the per-device settings cannot list their devices on this view");
|
|
5059
|
+
const ids = await list.call(this.deps.settings);
|
|
5060
|
+
const batch = this.deps.settings.readDeviceStoreBatch;
|
|
5061
|
+
const blobs = batch === void 0 ? new Map(await Promise.all(ids.map(async (id) => [id, await this.deps.settings.readDeviceStore(id)]))) : await batch.call(this.deps.settings, ids);
|
|
5062
|
+
for (const [id, blob] of blobs) {
|
|
5063
|
+
const held = blob[RECORDING_CONFIG_KEY];
|
|
5064
|
+
if (held !== void 0) out.set(String(id), held);
|
|
5047
5065
|
}
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5066
|
+
return out;
|
|
5067
|
+
}
|
|
5068
|
+
async readAll() {
|
|
5069
|
+
const out = /* @__PURE__ */ new Map();
|
|
5070
|
+
for (const [key, raw] of await this.readAllRaw()) {
|
|
5071
|
+
const parsed = this.parse(Number(key), raw);
|
|
5072
|
+
if (parsed !== null) out.set(key, parsed);
|
|
5073
|
+
}
|
|
5074
|
+
return out;
|
|
5075
|
+
}
|
|
5076
|
+
async write(key, value) {
|
|
5077
|
+
const deviceId = Number(key);
|
|
5078
|
+
if (!Number.isInteger(deviceId)) throw new Error(`recording config: bad device id "${key}"`);
|
|
5079
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: value });
|
|
5080
|
+
}
|
|
5081
|
+
async remove(key) {
|
|
5082
|
+
const deviceId = Number(key);
|
|
5083
|
+
if (!Number.isInteger(deviceId)) return;
|
|
5084
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: void 0 });
|
|
5085
|
+
}
|
|
5056
5086
|
};
|
|
5087
|
+
/** Build the handle over the standard per-device settings. */
|
|
5088
|
+
function recordingConfigsState(deps) {
|
|
5089
|
+
return new DeviceSettingsConfigsState(deps);
|
|
5090
|
+
}
|
|
5057
5091
|
/** Cold-start default for a camera that has never been configured: disabled, no bands. */
|
|
5058
5092
|
var DEFAULT_DEVICE_CONFIG = {
|
|
5059
5093
|
enabled: false,
|
|
5060
5094
|
bands: []
|
|
5061
5095
|
};
|
|
5062
|
-
/** Build the handle. `declare()` MUST run once at boot before any read/write. */
|
|
5063
|
-
function recordingConfigsState(store, logger) {
|
|
5064
|
-
return new require_retire_root_keys.RowMapStore({
|
|
5065
|
-
spec: RECORDING_CONFIGS_SPEC,
|
|
5066
|
-
store,
|
|
5067
|
-
logger
|
|
5068
|
-
});
|
|
5069
|
-
}
|
|
5070
5096
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
5071
5097
|
async function readDeviceConfigs(state) {
|
|
5072
5098
|
const rows = await state.readAll();
|
|
@@ -6194,6 +6220,7 @@ Object.freeze({
|
|
|
6194
6220
|
"device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
|
|
6195
6221
|
"device-extension": ["listSlots", "listSources"],
|
|
6196
6222
|
"device-provider": ["discoverDevices", "getDevices"],
|
|
6223
|
+
"device-removal-hook": ["listDeviceData"],
|
|
6197
6224
|
"failure-contribution": ["list"],
|
|
6198
6225
|
"llm": [
|
|
6199
6226
|
"getDefaults",
|
|
@@ -11106,7 +11133,7 @@ function buildRecordingProvider(deps) {
|
|
|
11106
11133
|
limit
|
|
11107
11134
|
}) ?? [],
|
|
11108
11135
|
renderClip: async (input) => {
|
|
11109
|
-
if (!deps.clipRender) throw
|
|
11136
|
+
if (!deps.clipRender) throw require_dist.userError("recording.clip-render-unavailable", void 0, "clip rendering unavailable on this node");
|
|
11110
11137
|
return deps.clipRender(input);
|
|
11111
11138
|
},
|
|
11112
11139
|
renderGif: async (input) => {
|
|
@@ -20079,7 +20106,7 @@ async function hasFootage(index, deviceId, profile, fromMs, toMs) {
|
|
|
20079
20106
|
/** Load a record or throw a clear NOT_FOUND-style error. */
|
|
20080
20107
|
async function requireRecord(state, exportId) {
|
|
20081
20108
|
const rec = await getExportRecord(state, exportId);
|
|
20082
|
-
if (!rec) throw
|
|
20109
|
+
if (!rec) throw require_dist.userError("recording.export-unknown", { id: exportId }, `recording-export: unknown export "${exportId}"`);
|
|
20083
20110
|
return rec;
|
|
20084
20111
|
}
|
|
20085
20112
|
/** Build the `recordingExport` provider over the injected deps. */
|
|
@@ -20192,11 +20219,14 @@ function buildRecordingExportProvider(deps) {
|
|
|
20192
20219
|
if (subject.kind === "clip") return await createClipExport(subject, input.options);
|
|
20193
20220
|
const { deviceId, profiles, fromMs, toMs } = subject;
|
|
20194
20221
|
const { options } = input;
|
|
20195
|
-
if (!(fromMs < toMs)) throw
|
|
20222
|
+
if (!(fromMs < toMs)) throw require_dist.userError("recording.export-invalid-range", {
|
|
20223
|
+
fromMs,
|
|
20224
|
+
toMs
|
|
20225
|
+
}, `recording-export: invalid range ${fromMs}..${toMs}`);
|
|
20196
20226
|
for (const profile of profiles) if (!await hasFootage(deps.index, deviceId, profile, fromMs, toMs)) throw new Error(`recording-export: no footage for ${deviceId}/${profile} in ${fromMs}..${toMs}`);
|
|
20197
20227
|
const now = deps.now();
|
|
20198
20228
|
const [head, ...rest] = profiles;
|
|
20199
|
-
if (head === void 0) throw
|
|
20229
|
+
if (head === void 0) throw require_dist.userError("recording.export-no-profiles", void 0, "recording-export: profiles must contain at least one profile");
|
|
20200
20230
|
const first = await queueOne({
|
|
20201
20231
|
deviceId,
|
|
20202
20232
|
profile: head,
|
|
@@ -20247,7 +20277,10 @@ function buildRecordingExportProvider(deps) {
|
|
|
20247
20277
|
state: rec.state
|
|
20248
20278
|
}
|
|
20249
20279
|
});
|
|
20250
|
-
throw
|
|
20280
|
+
throw require_dist.userError("recording.export-not-ready", {
|
|
20281
|
+
id: exportId,
|
|
20282
|
+
state: rec.state
|
|
20283
|
+
}, `recording-export: export "${exportId}" is not ready (${rec.state})`);
|
|
20251
20284
|
}
|
|
20252
20285
|
if (deps.now() >= rec.expiresAt) {
|
|
20253
20286
|
deps.logger.warn("export byte read refused: export has expired", {
|
|
@@ -20257,7 +20290,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20257
20290
|
expiresAt: rec.expiresAt
|
|
20258
20291
|
}
|
|
20259
20292
|
});
|
|
20260
|
-
throw
|
|
20293
|
+
throw require_dist.userError("recording.export-expired", { id: exportId }, `recording-export: export "${exportId}" has expired`);
|
|
20261
20294
|
}
|
|
20262
20295
|
const file = deps.outPath(rec);
|
|
20263
20296
|
let size;
|
|
@@ -20271,7 +20304,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20271
20304
|
error: String(err)
|
|
20272
20305
|
}
|
|
20273
20306
|
});
|
|
20274
|
-
throw
|
|
20307
|
+
throw Object.assign(require_dist.userError("recording.export-no-file", { id: exportId }, `recording-export: export "${exportId}" has no file on disk`), { cause: err });
|
|
20275
20308
|
}
|
|
20276
20309
|
if (size > 52428800) {
|
|
20277
20310
|
const shrink = deps.shrinkToBound;
|
|
@@ -20352,6 +20385,75 @@ function buildRecordingExportProvider(deps) {
|
|
|
20352
20385
|
* the host extracted from `CAMSTACK_HUB_URL`).
|
|
20353
20386
|
*/
|
|
20354
20387
|
var resolveRecordingHubHostname = require_hub_hostname.resolveHubHostname;
|
|
20388
|
+
async function nextVictims(deps, deviceId) {
|
|
20389
|
+
const archive = deps.archive?.() ?? null;
|
|
20390
|
+
if (archive === null) return null;
|
|
20391
|
+
const victims = [];
|
|
20392
|
+
for await (const page of archive.streamSegments({ deviceId })) {
|
|
20393
|
+
victims.push(...page);
|
|
20394
|
+
if (victims.length >= 2e4) break;
|
|
20395
|
+
}
|
|
20396
|
+
return victims;
|
|
20397
|
+
}
|
|
20398
|
+
/** Delete every segment the archive holds for `deviceId`, across every location of this node. */
|
|
20399
|
+
async function purgeDeviceFootage(deps, deviceId) {
|
|
20400
|
+
let deletedBuckets = 0;
|
|
20401
|
+
let reclaimedBytes = 0;
|
|
20402
|
+
for (let pass = 0; pass < 200; pass++) {
|
|
20403
|
+
const victims = await nextVictims(deps, deviceId);
|
|
20404
|
+
if (victims === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20405
|
+
if (victims.length === 0) break;
|
|
20406
|
+
const freed = await evictVictims(deps, deviceId, victims);
|
|
20407
|
+
deletedBuckets += freed.deletedBuckets;
|
|
20408
|
+
reclaimedBytes += freed.reclaimedBytes;
|
|
20409
|
+
if (freed.deletedBuckets === 0) break;
|
|
20410
|
+
}
|
|
20411
|
+
const census = await deps.archive?.()?.census({ deviceId });
|
|
20412
|
+
if (census === null || census === void 0 || census.segments > 0) throw new Error(`footage of device ${deviceId} is not fully removed (${census?.segments ?? "unknown"} segments left)`);
|
|
20413
|
+
if (deletedBuckets > 0) await deps.opsLog?.append({
|
|
20414
|
+
op: "prune",
|
|
20415
|
+
reason: "orphaned-device",
|
|
20416
|
+
deviceId,
|
|
20417
|
+
itemsAffected: deletedBuckets,
|
|
20418
|
+
bytesReclaimed: reclaimedBytes,
|
|
20419
|
+
detail: "the device was deleted — its footage was removed with it"
|
|
20420
|
+
});
|
|
20421
|
+
return {
|
|
20422
|
+
deletedBuckets,
|
|
20423
|
+
reclaimedBytes
|
|
20424
|
+
};
|
|
20425
|
+
}
|
|
20426
|
+
function createRecorderRemovalHook(deps) {
|
|
20427
|
+
const removeDevice = async (input) => {
|
|
20428
|
+
const { deviceId } = input;
|
|
20429
|
+
const footage = await purgeDeviceFootage(deps, deviceId);
|
|
20430
|
+
const counts = { footage: footage.deletedBuckets };
|
|
20431
|
+
if (footage.deletedBuckets > 0) deps.logger.info("recorder: removed the data of a deleted device", {
|
|
20432
|
+
tags: { deviceId },
|
|
20433
|
+
meta: {
|
|
20434
|
+
counts,
|
|
20435
|
+
reclaimedBytes: footage.reclaimedBytes,
|
|
20436
|
+
deletedAt: input.deletedAt
|
|
20437
|
+
}
|
|
20438
|
+
});
|
|
20439
|
+
return { counts };
|
|
20440
|
+
};
|
|
20441
|
+
return {
|
|
20442
|
+
onDeviceRemoved: removeDevice,
|
|
20443
|
+
listDeviceData: async () => {
|
|
20444
|
+
const out = [];
|
|
20445
|
+
const locationIds = new Set(deps.locations().map((l) => l.id));
|
|
20446
|
+
const usage = deps.archiveDeviceUsage?.(locationIds) ?? null;
|
|
20447
|
+
if (usage === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20448
|
+
for (const [deviceId, u] of usage) if (u.segments > 0) out.push({
|
|
20449
|
+
store: "footage",
|
|
20450
|
+
deviceId,
|
|
20451
|
+
count: u.segments
|
|
20452
|
+
});
|
|
20453
|
+
return out;
|
|
20454
|
+
}
|
|
20455
|
+
};
|
|
20456
|
+
}
|
|
20355
20457
|
//#endregion
|
|
20356
20458
|
//#region src/recorder/addon/recording-source-provider.ts
|
|
20357
20459
|
/** What the picker calls us. */
|
|
@@ -20858,7 +20960,7 @@ var RECORDER_RETIRED_ROOT_KEYS = [
|
|
|
20858
20960
|
},
|
|
20859
20961
|
{
|
|
20860
20962
|
key: "recordingConfigs",
|
|
20861
|
-
successor:
|
|
20963
|
+
successor: "the recorder per-device settings (key recordingConfig)",
|
|
20862
20964
|
reason: "one row per camera since the flatten; as a blob, asking what camera 617 records parsed every camera in the installation"
|
|
20863
20965
|
}
|
|
20864
20966
|
];
|
|
@@ -23213,6 +23315,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23213
23315
|
* Already ALIAS-GUARDED: a writable same-type duplicate of another
|
|
23214
23316
|
* location's physical root arrives here demoted to read-only. */
|
|
23215
23317
|
resolvedLocations = [];
|
|
23318
|
+
/** The `device-removal-hook` provider (D865); its reconcile is armed in `onHubReachable`. */
|
|
23319
|
+
removalHook = null;
|
|
23216
23320
|
/** Attach-boundary placement (assignment + mount probe + audit). Null on
|
|
23217
23321
|
* non-recording nodes / before `onInitialize`. */
|
|
23218
23322
|
placement = null;
|
|
@@ -23916,6 +24020,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23916
24020
|
migrateLegacyDeviceSignalRows(this.configStore(), this.ctx.logger).catch((err) => {
|
|
23917
24021
|
this.ctx.logger.warn("recorder: device-signal config migration failed", { meta: { error: require_dist.errMsg(err) } });
|
|
23918
24022
|
}).then(() => warnLostBands(this.configStore(), this.ctx.logger));
|
|
24023
|
+
const removalHook = createRecorderRemovalHook(recordingDeps);
|
|
24024
|
+
this.removalHook = removalHook;
|
|
23919
24025
|
const providers = [
|
|
23920
24026
|
{
|
|
23921
24027
|
capability: require_dist.recordingArchiveCapability,
|
|
@@ -23947,6 +24053,10 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23947
24053
|
{
|
|
23948
24054
|
capability: require_dist.loadContributionCapability,
|
|
23949
24055
|
provider: { list: () => this.costRegistry.contributions() }
|
|
24056
|
+
},
|
|
24057
|
+
{
|
|
24058
|
+
capability: require_dist.deviceRemovalHookCapability,
|
|
24059
|
+
provider: removalHook
|
|
23950
24060
|
}
|
|
23951
24061
|
];
|
|
23952
24062
|
this.footageDiscard = createFootageDiscardJob({
|
|
@@ -24078,6 +24188,12 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
24078
24188
|
async onHubReachable() {
|
|
24079
24189
|
if (!this.isRecordingNode) return;
|
|
24080
24190
|
await this.refreshLocations();
|
|
24191
|
+
const removalHook = this.removalHook;
|
|
24192
|
+
if (removalHook !== null) this.ctx.addDisposer(require_dist.armRemovalReconcile({
|
|
24193
|
+
api: this.ctx.api,
|
|
24194
|
+
logger: this.ctx.logger,
|
|
24195
|
+
remove: (input) => removalHook.onDeviceRemoved(input)
|
|
24196
|
+
}));
|
|
24081
24197
|
try {
|
|
24082
24198
|
this.exportsRoot = await this.ctx.api.storage.resolve.query({
|
|
24083
24199
|
location: ARTIFACTS_MEDIA_LOCATION,
|
|
@@ -25059,19 +25175,25 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
25059
25175
|
return this.schema({ sections: [{
|
|
25060
25176
|
id: "cluster-recording",
|
|
25061
25177
|
title: "Cluster recording",
|
|
25178
|
+
titleKey: "pipeline:globalSettings.s_cluster-recording.title",
|
|
25062
25179
|
description: "Which node runs recording (continuous + events write path). A SINGLE node — no multi-node balancing; every other node stays fully inert. Default: hub.",
|
|
25180
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.description",
|
|
25063
25181
|
columns: 1,
|
|
25064
25182
|
fields: [{
|
|
25065
25183
|
type: "text",
|
|
25066
25184
|
key: "recordingNodeId",
|
|
25067
25185
|
label: "Recording node",
|
|
25186
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.label",
|
|
25068
25187
|
description: "Node id that records + serves playback (e.g. \"hub\"). Leave \"hub\" unless you intentionally move recording to an agent. Takes effect on addon restart.",
|
|
25188
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.description",
|
|
25069
25189
|
default: RECORDING_NODE_DEFAULT
|
|
25070
25190
|
}, {
|
|
25071
25191
|
type: "text",
|
|
25072
25192
|
key: "recordingHubHostname",
|
|
25073
25193
|
label: "Hub host (cross-node)",
|
|
25194
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.label",
|
|
25074
25195
|
description: "Advanced: only used when the recording node is an AGENT. The hub's host the agent dials to pull broker streams (the RTSP restream port must be reachable there). Leave empty to auto-derive from the agent's hub connection.",
|
|
25196
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.description",
|
|
25075
25197
|
default: ""
|
|
25076
25198
|
}]
|
|
25077
25199
|
}] });
|
|
@@ -25145,8 +25267,16 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
25145
25267
|
this.censusedSettingsStoreClient ??= censusStoreClient(this.ctx.api.settingsStore, this.readCensus);
|
|
25146
25268
|
return this.censusedSettingsStoreClient;
|
|
25147
25269
|
}
|
|
25270
|
+
configsState = null;
|
|
25271
|
+
/** A camera's recording config — its per-device settings (D865), migrated on read. */
|
|
25148
25272
|
configStore() {
|
|
25149
|
-
|
|
25273
|
+
const settings = this.ctx.settings;
|
|
25274
|
+
if (settings === void 0) throw new Error("recorder: device settings are not available");
|
|
25275
|
+
this.configsState ??= recordingConfigsState({
|
|
25276
|
+
settings,
|
|
25277
|
+
logger: this.ctx.logger
|
|
25278
|
+
});
|
|
25279
|
+
return this.configsState;
|
|
25150
25280
|
}
|
|
25151
25281
|
/** Durable handle over the exportId→ExportRecord blob (same `BaseAddon.state()`
|
|
25152
25282
|
* wrapper as the config store). */
|