@camstack/addon-pipeline 1.2.370 → 1.2.372
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 +189 -54
- package/dist/recorder/index.mjs +189 -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,80 @@ 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
|
+
const fallback = this.deps.listDeviceIds;
|
|
5059
|
+
if (list === void 0 && fallback === void 0) throw new Error("the per-device settings cannot list their devices on this view");
|
|
5060
|
+
const ids = list !== void 0 ? await list.call(this.deps.settings) : fallback !== void 0 ? await fallback() : [];
|
|
5061
|
+
const batch = this.deps.settings.readDeviceStoreBatch;
|
|
5062
|
+
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);
|
|
5063
|
+
for (const [id, blob] of blobs) {
|
|
5064
|
+
const held = blob[RECORDING_CONFIG_KEY];
|
|
5065
|
+
if (held !== void 0) out.set(String(id), held);
|
|
5047
5066
|
}
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5067
|
+
return out;
|
|
5068
|
+
}
|
|
5069
|
+
async readAll() {
|
|
5070
|
+
const out = /* @__PURE__ */ new Map();
|
|
5071
|
+
for (const [key, raw] of await this.readAllRaw()) {
|
|
5072
|
+
const parsed = this.parse(Number(key), raw);
|
|
5073
|
+
if (parsed !== null) out.set(key, parsed);
|
|
5074
|
+
}
|
|
5075
|
+
return out;
|
|
5076
|
+
}
|
|
5077
|
+
async write(key, value) {
|
|
5078
|
+
const deviceId = Number(key);
|
|
5079
|
+
if (!Number.isInteger(deviceId)) throw new Error(`recording config: bad device id "${key}"`);
|
|
5080
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: value });
|
|
5081
|
+
}
|
|
5082
|
+
async remove(key) {
|
|
5083
|
+
const deviceId = Number(key);
|
|
5084
|
+
if (!Number.isInteger(deviceId)) return;
|
|
5085
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: void 0 });
|
|
5086
|
+
}
|
|
5056
5087
|
};
|
|
5088
|
+
/** Build the handle over the standard per-device settings. */
|
|
5089
|
+
function recordingConfigsState(deps) {
|
|
5090
|
+
return new DeviceSettingsConfigsState(deps);
|
|
5091
|
+
}
|
|
5057
5092
|
/** Cold-start default for a camera that has never been configured: disabled, no bands. */
|
|
5058
5093
|
var DEFAULT_DEVICE_CONFIG = {
|
|
5059
5094
|
enabled: false,
|
|
5060
5095
|
bands: []
|
|
5061
5096
|
};
|
|
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
5097
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
5071
5098
|
async function readDeviceConfigs(state) {
|
|
5072
5099
|
const rows = await state.readAll();
|
|
@@ -6194,6 +6221,7 @@ Object.freeze({
|
|
|
6194
6221
|
"device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
|
|
6195
6222
|
"device-extension": ["listSlots", "listSources"],
|
|
6196
6223
|
"device-provider": ["discoverDevices", "getDevices"],
|
|
6224
|
+
"device-removal-hook": ["listDeviceData"],
|
|
6197
6225
|
"failure-contribution": ["list"],
|
|
6198
6226
|
"llm": [
|
|
6199
6227
|
"getDefaults",
|
|
@@ -11106,7 +11134,7 @@ function buildRecordingProvider(deps) {
|
|
|
11106
11134
|
limit
|
|
11107
11135
|
}) ?? [],
|
|
11108
11136
|
renderClip: async (input) => {
|
|
11109
|
-
if (!deps.clipRender) throw
|
|
11137
|
+
if (!deps.clipRender) throw require_dist.userError("recording.clip-render-unavailable", void 0, "clip rendering unavailable on this node");
|
|
11110
11138
|
return deps.clipRender(input);
|
|
11111
11139
|
},
|
|
11112
11140
|
renderGif: async (input) => {
|
|
@@ -20079,7 +20107,7 @@ async function hasFootage(index, deviceId, profile, fromMs, toMs) {
|
|
|
20079
20107
|
/** Load a record or throw a clear NOT_FOUND-style error. */
|
|
20080
20108
|
async function requireRecord(state, exportId) {
|
|
20081
20109
|
const rec = await getExportRecord(state, exportId);
|
|
20082
|
-
if (!rec) throw
|
|
20110
|
+
if (!rec) throw require_dist.userError("recording.export-unknown", { id: exportId }, `recording-export: unknown export "${exportId}"`);
|
|
20083
20111
|
return rec;
|
|
20084
20112
|
}
|
|
20085
20113
|
/** Build the `recordingExport` provider over the injected deps. */
|
|
@@ -20192,11 +20220,14 @@ function buildRecordingExportProvider(deps) {
|
|
|
20192
20220
|
if (subject.kind === "clip") return await createClipExport(subject, input.options);
|
|
20193
20221
|
const { deviceId, profiles, fromMs, toMs } = subject;
|
|
20194
20222
|
const { options } = input;
|
|
20195
|
-
if (!(fromMs < toMs)) throw
|
|
20223
|
+
if (!(fromMs < toMs)) throw require_dist.userError("recording.export-invalid-range", {
|
|
20224
|
+
fromMs,
|
|
20225
|
+
toMs
|
|
20226
|
+
}, `recording-export: invalid range ${fromMs}..${toMs}`);
|
|
20196
20227
|
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
20228
|
const now = deps.now();
|
|
20198
20229
|
const [head, ...rest] = profiles;
|
|
20199
|
-
if (head === void 0) throw
|
|
20230
|
+
if (head === void 0) throw require_dist.userError("recording.export-no-profiles", void 0, "recording-export: profiles must contain at least one profile");
|
|
20200
20231
|
const first = await queueOne({
|
|
20201
20232
|
deviceId,
|
|
20202
20233
|
profile: head,
|
|
@@ -20247,7 +20278,10 @@ function buildRecordingExportProvider(deps) {
|
|
|
20247
20278
|
state: rec.state
|
|
20248
20279
|
}
|
|
20249
20280
|
});
|
|
20250
|
-
throw
|
|
20281
|
+
throw require_dist.userError("recording.export-not-ready", {
|
|
20282
|
+
id: exportId,
|
|
20283
|
+
state: rec.state
|
|
20284
|
+
}, `recording-export: export "${exportId}" is not ready (${rec.state})`);
|
|
20251
20285
|
}
|
|
20252
20286
|
if (deps.now() >= rec.expiresAt) {
|
|
20253
20287
|
deps.logger.warn("export byte read refused: export has expired", {
|
|
@@ -20257,7 +20291,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20257
20291
|
expiresAt: rec.expiresAt
|
|
20258
20292
|
}
|
|
20259
20293
|
});
|
|
20260
|
-
throw
|
|
20294
|
+
throw require_dist.userError("recording.export-expired", { id: exportId }, `recording-export: export "${exportId}" has expired`);
|
|
20261
20295
|
}
|
|
20262
20296
|
const file = deps.outPath(rec);
|
|
20263
20297
|
let size;
|
|
@@ -20271,7 +20305,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20271
20305
|
error: String(err)
|
|
20272
20306
|
}
|
|
20273
20307
|
});
|
|
20274
|
-
throw
|
|
20308
|
+
throw Object.assign(require_dist.userError("recording.export-no-file", { id: exportId }, `recording-export: export "${exportId}" has no file on disk`), { cause: err });
|
|
20275
20309
|
}
|
|
20276
20310
|
if (size > 52428800) {
|
|
20277
20311
|
const shrink = deps.shrinkToBound;
|
|
@@ -20352,6 +20386,75 @@ function buildRecordingExportProvider(deps) {
|
|
|
20352
20386
|
* the host extracted from `CAMSTACK_HUB_URL`).
|
|
20353
20387
|
*/
|
|
20354
20388
|
var resolveRecordingHubHostname = require_hub_hostname.resolveHubHostname;
|
|
20389
|
+
async function nextVictims(deps, deviceId) {
|
|
20390
|
+
const archive = deps.archive?.() ?? null;
|
|
20391
|
+
if (archive === null) return null;
|
|
20392
|
+
const victims = [];
|
|
20393
|
+
for await (const page of archive.streamSegments({ deviceId })) {
|
|
20394
|
+
victims.push(...page);
|
|
20395
|
+
if (victims.length >= 2e4) break;
|
|
20396
|
+
}
|
|
20397
|
+
return victims;
|
|
20398
|
+
}
|
|
20399
|
+
/** Delete every segment the archive holds for `deviceId`, across every location of this node. */
|
|
20400
|
+
async function purgeDeviceFootage(deps, deviceId) {
|
|
20401
|
+
let deletedBuckets = 0;
|
|
20402
|
+
let reclaimedBytes = 0;
|
|
20403
|
+
for (let pass = 0; pass < 200; pass++) {
|
|
20404
|
+
const victims = await nextVictims(deps, deviceId);
|
|
20405
|
+
if (victims === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20406
|
+
if (victims.length === 0) break;
|
|
20407
|
+
const freed = await evictVictims(deps, deviceId, victims);
|
|
20408
|
+
deletedBuckets += freed.deletedBuckets;
|
|
20409
|
+
reclaimedBytes += freed.reclaimedBytes;
|
|
20410
|
+
if (freed.deletedBuckets === 0) break;
|
|
20411
|
+
}
|
|
20412
|
+
const census = await deps.archive?.()?.census({ deviceId });
|
|
20413
|
+
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)`);
|
|
20414
|
+
if (deletedBuckets > 0) await deps.opsLog?.append({
|
|
20415
|
+
op: "prune",
|
|
20416
|
+
reason: "orphaned-device",
|
|
20417
|
+
deviceId,
|
|
20418
|
+
itemsAffected: deletedBuckets,
|
|
20419
|
+
bytesReclaimed: reclaimedBytes,
|
|
20420
|
+
detail: "the device was deleted — its footage was removed with it"
|
|
20421
|
+
});
|
|
20422
|
+
return {
|
|
20423
|
+
deletedBuckets,
|
|
20424
|
+
reclaimedBytes
|
|
20425
|
+
};
|
|
20426
|
+
}
|
|
20427
|
+
function createRecorderRemovalHook(deps) {
|
|
20428
|
+
const removeDevice = async (input) => {
|
|
20429
|
+
const { deviceId } = input;
|
|
20430
|
+
const footage = await purgeDeviceFootage(deps, deviceId);
|
|
20431
|
+
const counts = { footage: footage.deletedBuckets };
|
|
20432
|
+
if (footage.deletedBuckets > 0) deps.logger.info("recorder: removed the data of a deleted device", {
|
|
20433
|
+
tags: { deviceId },
|
|
20434
|
+
meta: {
|
|
20435
|
+
counts,
|
|
20436
|
+
reclaimedBytes: footage.reclaimedBytes,
|
|
20437
|
+
deletedAt: input.deletedAt
|
|
20438
|
+
}
|
|
20439
|
+
});
|
|
20440
|
+
return { counts };
|
|
20441
|
+
};
|
|
20442
|
+
return {
|
|
20443
|
+
onDeviceRemoved: removeDevice,
|
|
20444
|
+
listDeviceData: async () => {
|
|
20445
|
+
const out = [];
|
|
20446
|
+
const locationIds = new Set(deps.locations().map((l) => l.id));
|
|
20447
|
+
const usage = deps.archiveDeviceUsage?.(locationIds) ?? null;
|
|
20448
|
+
if (usage === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20449
|
+
for (const [deviceId, u] of usage) if (u.segments > 0) out.push({
|
|
20450
|
+
store: "footage",
|
|
20451
|
+
deviceId,
|
|
20452
|
+
count: u.segments
|
|
20453
|
+
});
|
|
20454
|
+
return out;
|
|
20455
|
+
}
|
|
20456
|
+
};
|
|
20457
|
+
}
|
|
20355
20458
|
//#endregion
|
|
20356
20459
|
//#region src/recorder/addon/recording-source-provider.ts
|
|
20357
20460
|
/** What the picker calls us. */
|
|
@@ -20858,7 +20961,7 @@ var RECORDER_RETIRED_ROOT_KEYS = [
|
|
|
20858
20961
|
},
|
|
20859
20962
|
{
|
|
20860
20963
|
key: "recordingConfigs",
|
|
20861
|
-
successor:
|
|
20964
|
+
successor: "the recorder per-device settings (key recordingConfig)",
|
|
20862
20965
|
reason: "one row per camera since the flatten; as a blob, asking what camera 617 records parsed every camera in the installation"
|
|
20863
20966
|
}
|
|
20864
20967
|
];
|
|
@@ -23213,6 +23316,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23213
23316
|
* Already ALIAS-GUARDED: a writable same-type duplicate of another
|
|
23214
23317
|
* location's physical root arrives here demoted to read-only. */
|
|
23215
23318
|
resolvedLocations = [];
|
|
23319
|
+
/** The `device-removal-hook` provider (D865); its reconcile is armed in `onHubReachable`. */
|
|
23320
|
+
removalHook = null;
|
|
23216
23321
|
/** Attach-boundary placement (assignment + mount probe + audit). Null on
|
|
23217
23322
|
* non-recording nodes / before `onInitialize`. */
|
|
23218
23323
|
placement = null;
|
|
@@ -23916,6 +24021,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23916
24021
|
migrateLegacyDeviceSignalRows(this.configStore(), this.ctx.logger).catch((err) => {
|
|
23917
24022
|
this.ctx.logger.warn("recorder: device-signal config migration failed", { meta: { error: require_dist.errMsg(err) } });
|
|
23918
24023
|
}).then(() => warnLostBands(this.configStore(), this.ctx.logger));
|
|
24024
|
+
const removalHook = createRecorderRemovalHook(recordingDeps);
|
|
24025
|
+
this.removalHook = removalHook;
|
|
23919
24026
|
const providers = [
|
|
23920
24027
|
{
|
|
23921
24028
|
capability: require_dist.recordingArchiveCapability,
|
|
@@ -23947,6 +24054,10 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
23947
24054
|
{
|
|
23948
24055
|
capability: require_dist.loadContributionCapability,
|
|
23949
24056
|
provider: { list: () => this.costRegistry.contributions() }
|
|
24057
|
+
},
|
|
24058
|
+
{
|
|
24059
|
+
capability: require_dist.deviceRemovalHookCapability,
|
|
24060
|
+
provider: removalHook
|
|
23950
24061
|
}
|
|
23951
24062
|
];
|
|
23952
24063
|
this.footageDiscard = createFootageDiscardJob({
|
|
@@ -24078,6 +24189,12 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
24078
24189
|
async onHubReachable() {
|
|
24079
24190
|
if (!this.isRecordingNode) return;
|
|
24080
24191
|
await this.refreshLocations();
|
|
24192
|
+
const removalHook = this.removalHook;
|
|
24193
|
+
if (removalHook !== null) this.ctx.addDisposer(require_dist.armRemovalReconcile({
|
|
24194
|
+
api: this.ctx.api,
|
|
24195
|
+
logger: this.ctx.logger,
|
|
24196
|
+
remove: (input) => removalHook.onDeviceRemoved(input)
|
|
24197
|
+
}));
|
|
24081
24198
|
try {
|
|
24082
24199
|
this.exportsRoot = await this.ctx.api.storage.resolve.query({
|
|
24083
24200
|
location: ARTIFACTS_MEDIA_LOCATION,
|
|
@@ -25059,19 +25176,25 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
25059
25176
|
return this.schema({ sections: [{
|
|
25060
25177
|
id: "cluster-recording",
|
|
25061
25178
|
title: "Cluster recording",
|
|
25179
|
+
titleKey: "pipeline:globalSettings.s_cluster-recording.title",
|
|
25062
25180
|
description: "Which node runs recording (continuous + events write path). A SINGLE node — no multi-node balancing; every other node stays fully inert. Default: hub.",
|
|
25181
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.description",
|
|
25063
25182
|
columns: 1,
|
|
25064
25183
|
fields: [{
|
|
25065
25184
|
type: "text",
|
|
25066
25185
|
key: "recordingNodeId",
|
|
25067
25186
|
label: "Recording node",
|
|
25187
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.label",
|
|
25068
25188
|
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.",
|
|
25189
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.description",
|
|
25069
25190
|
default: RECORDING_NODE_DEFAULT
|
|
25070
25191
|
}, {
|
|
25071
25192
|
type: "text",
|
|
25072
25193
|
key: "recordingHubHostname",
|
|
25073
25194
|
label: "Hub host (cross-node)",
|
|
25195
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.label",
|
|
25074
25196
|
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.",
|
|
25197
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.description",
|
|
25075
25198
|
default: ""
|
|
25076
25199
|
}]
|
|
25077
25200
|
}] });
|
|
@@ -25145,8 +25268,20 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
25145
25268
|
this.censusedSettingsStoreClient ??= censusStoreClient(this.ctx.api.settingsStore, this.readCensus);
|
|
25146
25269
|
return this.censusedSettingsStoreClient;
|
|
25147
25270
|
}
|
|
25271
|
+
configsState = null;
|
|
25272
|
+
/** A camera's recording config — its per-device settings (D865), migrated on read. */
|
|
25148
25273
|
configStore() {
|
|
25149
|
-
|
|
25274
|
+
const settings = this.ctx.settings;
|
|
25275
|
+
if (settings === void 0) throw new Error("recorder: device settings are not available");
|
|
25276
|
+
this.configsState ??= recordingConfigsState({
|
|
25277
|
+
settings,
|
|
25278
|
+
logger: this.ctx.logger,
|
|
25279
|
+
listDeviceIds: async () => (await this.ctx.api.deviceManager.listAll.query({
|
|
25280
|
+
isCamera: true,
|
|
25281
|
+
projection: "slim"
|
|
25282
|
+
})).map((device) => device.id)
|
|
25283
|
+
});
|
|
25284
|
+
return this.configsState;
|
|
25150
25285
|
}
|
|
25151
25286
|
/** Durable handle over the exportId→ExportRecord blob (same `BaseAddon.state()`
|
|
25152
25287
|
* wrapper as the config store). */
|