@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.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $
|
|
2
|
-
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
1
|
+
import { $n as _enum, $t as recordingArchiveCapability, An as errMsg$1, B as RECORDING_SOURCE_CAMSTACK_ADDON, Bn as defineCustomActions, Bt as memoizePinnedFfmpeg, C as EVENT_PAD_MS, Ct as evictionPolicyForMode, Ft as loadContributionCapability, G as VIDEOCLIPS_MAX_OFFER_BYTES, H as RecordingConfigSchema, Ht as modeMayWrite, Jn as nodePin, K as VISIT_MERGE_GAP_MS, Mn as BaseAddon, Mt as isFirstLevelMacroClass, N as OpsLogEntrySchema, On as DeviceFeature, Ot as gbToBytes, Qn as userError, R as RECORDING_EXPORT_MAX_READ_BYTES, Rt as mayReadLocation, S as EVENT_OWNER_TYPES, T as ExportRecordSchema, Tn as evaluateSensorEdge, Tt as exportSubjectOf, U as RecordingSignalStatusSchema, Un as hydrateSchema, Vt as migrateLegacyDeviceSignalConfig, Xn as selectAssignedProfileSlots, Z as armRemovalReconcile, Zn as sleep$1, _n as stripRetiredBandPreBufferSec, ar as number, at as clipExportFailure, cr as record, dr as EventCategory, en as recordingCapability, er as array, hn as storageOccupancyCapability, ht as deriveRecordingMode, in as resolveCreateExportSubject, kn as DeviceType, ln as resolveRecordingProfiles, lr as string, mn as storageEvictableCapability, nn as recordingSignalCapability, o as BatteryStatusSchema, on as resolveLocationMode, or as object, ot as clipExportFailureOf, pt as deriveBatteryPresence, s as CLIP_BROKER_PACED_RATES, sr as preprocess, tn as recordingExportCapability, tr as boolean, tt as batteryCapability, v as DEFAULT_EVENTS_BAND_BUFFER_SEC, vt as deviceRemovalHookCapability, wt as evictionPolicyOfLocation, yn as videoclipsCapability, z as RECORDING_SOURCE_CAMSTACK, zn as customAction, zt as mayWriteToLocation } from "../dist-8AEiKOFO.mjs";
|
|
2
|
+
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Bd4va7c-.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
|
|
5
5
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
@@ -4989,9 +4989,10 @@ function archiveUsageComplete(ledger, locationIds) {
|
|
|
4989
4989
|
/**
|
|
4990
4990
|
* Per-device recording-config persistence for the recorder addon.
|
|
4991
4991
|
*
|
|
4992
|
-
*
|
|
4993
|
-
*
|
|
4994
|
-
* saving its config writes that
|
|
4992
|
+
* A camera's config is its PER-DEVICE SETTINGS (D865), key `recordingConfig`,
|
|
4993
|
+
* through `ctx.settings.*DeviceStore`. Asking what camera 617 records is one
|
|
4994
|
+
* scope read; saving its config writes that key and touches no other camera.
|
|
4995
|
+
* Deleting the camera clears the scope centrally.
|
|
4995
4996
|
*
|
|
4996
4997
|
* ## What this replaced, and why
|
|
4997
4998
|
*
|
|
@@ -5012,54 +5013,80 @@ function archiveUsageComplete(ledger, locationIds) {
|
|
|
5012
5013
|
* empty fallback (= the whole fleet silently unconfigured).
|
|
5013
5014
|
*/
|
|
5014
5015
|
/**
|
|
5015
|
-
*
|
|
5016
|
-
*
|
|
5017
|
-
*
|
|
5018
|
-
*
|
|
5019
|
-
*
|
|
5020
|
-
*
|
|
5016
|
+
* READ is forgiving where the wire contract is strict — see the header.
|
|
5017
|
+
* `.strip()` keeps a row written before a key was retired readable.
|
|
5018
|
+
*
|
|
5019
|
+
* The preprocess is the D371→D380 migration, applied on EVERY read so a row
|
|
5020
|
+
* behaves correctly from the first read of the boot, before
|
|
5021
|
+
* {@link migrateLegacyDeviceSignalRows} has persisted anything. Pure and
|
|
5022
|
+
* idempotent — a row with no legacy key passes through by reference.
|
|
5021
5023
|
*/
|
|
5022
|
-
var
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5024
|
+
var StoredRecordingConfigSchema = preprocess((raw) => migrateLegacyDeviceSignalConfig(raw).value, RecordingConfigSchema.strip()).transform((config) => stripRetiredBandPreBufferSec(config).config);
|
|
5025
|
+
/** The per-device settings key a camera's recording config lives under. */
|
|
5026
|
+
var RECORDING_CONFIG_KEY = "recordingConfig";
|
|
5027
|
+
var DeviceSettingsConfigsState = class {
|
|
5028
|
+
deps;
|
|
5029
|
+
constructor(deps) {
|
|
5030
|
+
this.deps = deps;
|
|
5031
|
+
}
|
|
5032
|
+
async declare() {}
|
|
5033
|
+
parse(deviceId, raw) {
|
|
5034
|
+
const parsed = StoredRecordingConfigSchema.safeParse(raw);
|
|
5035
|
+
if (parsed.success) return parsed.data;
|
|
5036
|
+
this.deps.logger.warn("recording config row skipped — it does not parse", {
|
|
5037
|
+
tags: { deviceId },
|
|
5038
|
+
meta: { error: parsed.error.message }
|
|
5039
|
+
});
|
|
5040
|
+
return null;
|
|
5041
|
+
}
|
|
5042
|
+
async read(key) {
|
|
5043
|
+
const deviceId = Number(key);
|
|
5044
|
+
if (!Number.isInteger(deviceId)) return null;
|
|
5045
|
+
const held = (await this.deps.settings.readDeviceStore(deviceId))[RECORDING_CONFIG_KEY];
|
|
5046
|
+
return held === void 0 ? null : this.parse(deviceId, held);
|
|
5047
|
+
}
|
|
5048
|
+
async readAllRaw() {
|
|
5049
|
+
const out = /* @__PURE__ */ new Map();
|
|
5050
|
+
const list = this.deps.settings.listDeviceStoreIds;
|
|
5051
|
+
const fallback = this.deps.listDeviceIds;
|
|
5052
|
+
if (list === void 0 && fallback === void 0) throw new Error("the per-device settings cannot list their devices on this view");
|
|
5053
|
+
const ids = list !== void 0 ? await list.call(this.deps.settings) : fallback !== void 0 ? await fallback() : [];
|
|
5054
|
+
const batch = this.deps.settings.readDeviceStoreBatch;
|
|
5055
|
+
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);
|
|
5056
|
+
for (const [id, blob] of blobs) {
|
|
5057
|
+
const held = blob[RECORDING_CONFIG_KEY];
|
|
5058
|
+
if (held !== void 0) out.set(String(id), held);
|
|
5040
5059
|
}
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5060
|
+
return out;
|
|
5061
|
+
}
|
|
5062
|
+
async readAll() {
|
|
5063
|
+
const out = /* @__PURE__ */ new Map();
|
|
5064
|
+
for (const [key, raw] of await this.readAllRaw()) {
|
|
5065
|
+
const parsed = this.parse(Number(key), raw);
|
|
5066
|
+
if (parsed !== null) out.set(key, parsed);
|
|
5067
|
+
}
|
|
5068
|
+
return out;
|
|
5069
|
+
}
|
|
5070
|
+
async write(key, value) {
|
|
5071
|
+
const deviceId = Number(key);
|
|
5072
|
+
if (!Number.isInteger(deviceId)) throw new Error(`recording config: bad device id "${key}"`);
|
|
5073
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: value });
|
|
5074
|
+
}
|
|
5075
|
+
async remove(key) {
|
|
5076
|
+
const deviceId = Number(key);
|
|
5077
|
+
if (!Number.isInteger(deviceId)) return;
|
|
5078
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: void 0 });
|
|
5079
|
+
}
|
|
5049
5080
|
};
|
|
5081
|
+
/** Build the handle over the standard per-device settings. */
|
|
5082
|
+
function recordingConfigsState(deps) {
|
|
5083
|
+
return new DeviceSettingsConfigsState(deps);
|
|
5084
|
+
}
|
|
5050
5085
|
/** Cold-start default for a camera that has never been configured: disabled, no bands. */
|
|
5051
5086
|
var DEFAULT_DEVICE_CONFIG = {
|
|
5052
5087
|
enabled: false,
|
|
5053
5088
|
bands: []
|
|
5054
5089
|
};
|
|
5055
|
-
/** Build the handle. `declare()` MUST run once at boot before any read/write. */
|
|
5056
|
-
function recordingConfigsState(store, logger) {
|
|
5057
|
-
return new RowMapStore({
|
|
5058
|
-
spec: RECORDING_CONFIGS_SPEC,
|
|
5059
|
-
store,
|
|
5060
|
-
logger
|
|
5061
|
-
});
|
|
5062
|
-
}
|
|
5063
5090
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
5064
5091
|
async function readDeviceConfigs(state) {
|
|
5065
5092
|
const rows = await state.readAll();
|
|
@@ -6186,6 +6213,7 @@ Object.freeze({
|
|
|
6186
6213
|
"device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
|
|
6187
6214
|
"device-extension": ["listSlots", "listSources"],
|
|
6188
6215
|
"device-provider": ["discoverDevices", "getDevices"],
|
|
6216
|
+
"device-removal-hook": ["listDeviceData"],
|
|
6189
6217
|
"failure-contribution": ["list"],
|
|
6190
6218
|
"llm": [
|
|
6191
6219
|
"getDefaults",
|
|
@@ -11098,7 +11126,7 @@ function buildRecordingProvider(deps) {
|
|
|
11098
11126
|
limit
|
|
11099
11127
|
}) ?? [],
|
|
11100
11128
|
renderClip: async (input) => {
|
|
11101
|
-
if (!deps.clipRender) throw
|
|
11129
|
+
if (!deps.clipRender) throw userError("recording.clip-render-unavailable", void 0, "clip rendering unavailable on this node");
|
|
11102
11130
|
return deps.clipRender(input);
|
|
11103
11131
|
},
|
|
11104
11132
|
renderGif: async (input) => {
|
|
@@ -20071,7 +20099,7 @@ async function hasFootage(index, deviceId, profile, fromMs, toMs) {
|
|
|
20071
20099
|
/** Load a record or throw a clear NOT_FOUND-style error. */
|
|
20072
20100
|
async function requireRecord(state, exportId) {
|
|
20073
20101
|
const rec = await getExportRecord(state, exportId);
|
|
20074
|
-
if (!rec) throw
|
|
20102
|
+
if (!rec) throw userError("recording.export-unknown", { id: exportId }, `recording-export: unknown export "${exportId}"`);
|
|
20075
20103
|
return rec;
|
|
20076
20104
|
}
|
|
20077
20105
|
/** Build the `recordingExport` provider over the injected deps. */
|
|
@@ -20184,11 +20212,14 @@ function buildRecordingExportProvider(deps) {
|
|
|
20184
20212
|
if (subject.kind === "clip") return await createClipExport(subject, input.options);
|
|
20185
20213
|
const { deviceId, profiles, fromMs, toMs } = subject;
|
|
20186
20214
|
const { options } = input;
|
|
20187
|
-
if (!(fromMs < toMs)) throw
|
|
20215
|
+
if (!(fromMs < toMs)) throw userError("recording.export-invalid-range", {
|
|
20216
|
+
fromMs,
|
|
20217
|
+
toMs
|
|
20218
|
+
}, `recording-export: invalid range ${fromMs}..${toMs}`);
|
|
20188
20219
|
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}`);
|
|
20189
20220
|
const now = deps.now();
|
|
20190
20221
|
const [head, ...rest] = profiles;
|
|
20191
|
-
if (head === void 0) throw
|
|
20222
|
+
if (head === void 0) throw userError("recording.export-no-profiles", void 0, "recording-export: profiles must contain at least one profile");
|
|
20192
20223
|
const first = await queueOne({
|
|
20193
20224
|
deviceId,
|
|
20194
20225
|
profile: head,
|
|
@@ -20239,7 +20270,10 @@ function buildRecordingExportProvider(deps) {
|
|
|
20239
20270
|
state: rec.state
|
|
20240
20271
|
}
|
|
20241
20272
|
});
|
|
20242
|
-
throw
|
|
20273
|
+
throw userError("recording.export-not-ready", {
|
|
20274
|
+
id: exportId,
|
|
20275
|
+
state: rec.state
|
|
20276
|
+
}, `recording-export: export "${exportId}" is not ready (${rec.state})`);
|
|
20243
20277
|
}
|
|
20244
20278
|
if (deps.now() >= rec.expiresAt) {
|
|
20245
20279
|
deps.logger.warn("export byte read refused: export has expired", {
|
|
@@ -20249,7 +20283,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20249
20283
|
expiresAt: rec.expiresAt
|
|
20250
20284
|
}
|
|
20251
20285
|
});
|
|
20252
|
-
throw
|
|
20286
|
+
throw userError("recording.export-expired", { id: exportId }, `recording-export: export "${exportId}" has expired`);
|
|
20253
20287
|
}
|
|
20254
20288
|
const file = deps.outPath(rec);
|
|
20255
20289
|
let size;
|
|
@@ -20263,7 +20297,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20263
20297
|
error: String(err)
|
|
20264
20298
|
}
|
|
20265
20299
|
});
|
|
20266
|
-
throw
|
|
20300
|
+
throw Object.assign(userError("recording.export-no-file", { id: exportId }, `recording-export: export "${exportId}" has no file on disk`), { cause: err });
|
|
20267
20301
|
}
|
|
20268
20302
|
if (size > 52428800) {
|
|
20269
20303
|
const shrink = deps.shrinkToBound;
|
|
@@ -20344,6 +20378,75 @@ function buildRecordingExportProvider(deps) {
|
|
|
20344
20378
|
* the host extracted from `CAMSTACK_HUB_URL`).
|
|
20345
20379
|
*/
|
|
20346
20380
|
var resolveRecordingHubHostname = resolveHubHostname;
|
|
20381
|
+
async function nextVictims(deps, deviceId) {
|
|
20382
|
+
const archive = deps.archive?.() ?? null;
|
|
20383
|
+
if (archive === null) return null;
|
|
20384
|
+
const victims = [];
|
|
20385
|
+
for await (const page of archive.streamSegments({ deviceId })) {
|
|
20386
|
+
victims.push(...page);
|
|
20387
|
+
if (victims.length >= 2e4) break;
|
|
20388
|
+
}
|
|
20389
|
+
return victims;
|
|
20390
|
+
}
|
|
20391
|
+
/** Delete every segment the archive holds for `deviceId`, across every location of this node. */
|
|
20392
|
+
async function purgeDeviceFootage(deps, deviceId) {
|
|
20393
|
+
let deletedBuckets = 0;
|
|
20394
|
+
let reclaimedBytes = 0;
|
|
20395
|
+
for (let pass = 0; pass < 200; pass++) {
|
|
20396
|
+
const victims = await nextVictims(deps, deviceId);
|
|
20397
|
+
if (victims === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20398
|
+
if (victims.length === 0) break;
|
|
20399
|
+
const freed = await evictVictims(deps, deviceId, victims);
|
|
20400
|
+
deletedBuckets += freed.deletedBuckets;
|
|
20401
|
+
reclaimedBytes += freed.reclaimedBytes;
|
|
20402
|
+
if (freed.deletedBuckets === 0) break;
|
|
20403
|
+
}
|
|
20404
|
+
const census = await deps.archive?.()?.census({ deviceId });
|
|
20405
|
+
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)`);
|
|
20406
|
+
if (deletedBuckets > 0) await deps.opsLog?.append({
|
|
20407
|
+
op: "prune",
|
|
20408
|
+
reason: "orphaned-device",
|
|
20409
|
+
deviceId,
|
|
20410
|
+
itemsAffected: deletedBuckets,
|
|
20411
|
+
bytesReclaimed: reclaimedBytes,
|
|
20412
|
+
detail: "the device was deleted — its footage was removed with it"
|
|
20413
|
+
});
|
|
20414
|
+
return {
|
|
20415
|
+
deletedBuckets,
|
|
20416
|
+
reclaimedBytes
|
|
20417
|
+
};
|
|
20418
|
+
}
|
|
20419
|
+
function createRecorderRemovalHook(deps) {
|
|
20420
|
+
const removeDevice = async (input) => {
|
|
20421
|
+
const { deviceId } = input;
|
|
20422
|
+
const footage = await purgeDeviceFootage(deps, deviceId);
|
|
20423
|
+
const counts = { footage: footage.deletedBuckets };
|
|
20424
|
+
if (footage.deletedBuckets > 0) deps.logger.info("recorder: removed the data of a deleted device", {
|
|
20425
|
+
tags: { deviceId },
|
|
20426
|
+
meta: {
|
|
20427
|
+
counts,
|
|
20428
|
+
reclaimedBytes: footage.reclaimedBytes,
|
|
20429
|
+
deletedAt: input.deletedAt
|
|
20430
|
+
}
|
|
20431
|
+
});
|
|
20432
|
+
return { counts };
|
|
20433
|
+
};
|
|
20434
|
+
return {
|
|
20435
|
+
onDeviceRemoved: removeDevice,
|
|
20436
|
+
listDeviceData: async () => {
|
|
20437
|
+
const out = [];
|
|
20438
|
+
const locationIds = new Set(deps.locations().map((l) => l.id));
|
|
20439
|
+
const usage = deps.archiveDeviceUsage?.(locationIds) ?? null;
|
|
20440
|
+
if (usage === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20441
|
+
for (const [deviceId, u] of usage) if (u.segments > 0) out.push({
|
|
20442
|
+
store: "footage",
|
|
20443
|
+
deviceId,
|
|
20444
|
+
count: u.segments
|
|
20445
|
+
});
|
|
20446
|
+
return out;
|
|
20447
|
+
}
|
|
20448
|
+
};
|
|
20449
|
+
}
|
|
20347
20450
|
//#endregion
|
|
20348
20451
|
//#region src/recorder/addon/recording-source-provider.ts
|
|
20349
20452
|
/** What the picker calls us. */
|
|
@@ -20850,7 +20953,7 @@ var RECORDER_RETIRED_ROOT_KEYS = [
|
|
|
20850
20953
|
},
|
|
20851
20954
|
{
|
|
20852
20955
|
key: "recordingConfigs",
|
|
20853
|
-
successor:
|
|
20956
|
+
successor: "the recorder per-device settings (key recordingConfig)",
|
|
20854
20957
|
reason: "one row per camera since the flatten; as a blob, asking what camera 617 records parsed every camera in the installation"
|
|
20855
20958
|
}
|
|
20856
20959
|
];
|
|
@@ -23205,6 +23308,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23205
23308
|
* Already ALIAS-GUARDED: a writable same-type duplicate of another
|
|
23206
23309
|
* location's physical root arrives here demoted to read-only. */
|
|
23207
23310
|
resolvedLocations = [];
|
|
23311
|
+
/** The `device-removal-hook` provider (D865); its reconcile is armed in `onHubReachable`. */
|
|
23312
|
+
removalHook = null;
|
|
23208
23313
|
/** Attach-boundary placement (assignment + mount probe + audit). Null on
|
|
23209
23314
|
* non-recording nodes / before `onInitialize`. */
|
|
23210
23315
|
placement = null;
|
|
@@ -23908,6 +24013,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23908
24013
|
migrateLegacyDeviceSignalRows(this.configStore(), this.ctx.logger).catch((err) => {
|
|
23909
24014
|
this.ctx.logger.warn("recorder: device-signal config migration failed", { meta: { error: errMsg$1(err) } });
|
|
23910
24015
|
}).then(() => warnLostBands(this.configStore(), this.ctx.logger));
|
|
24016
|
+
const removalHook = createRecorderRemovalHook(recordingDeps);
|
|
24017
|
+
this.removalHook = removalHook;
|
|
23911
24018
|
const providers = [
|
|
23912
24019
|
{
|
|
23913
24020
|
capability: recordingArchiveCapability,
|
|
@@ -23939,6 +24046,10 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23939
24046
|
{
|
|
23940
24047
|
capability: loadContributionCapability,
|
|
23941
24048
|
provider: { list: () => this.costRegistry.contributions() }
|
|
24049
|
+
},
|
|
24050
|
+
{
|
|
24051
|
+
capability: deviceRemovalHookCapability,
|
|
24052
|
+
provider: removalHook
|
|
23942
24053
|
}
|
|
23943
24054
|
];
|
|
23944
24055
|
this.footageDiscard = createFootageDiscardJob({
|
|
@@ -24070,6 +24181,12 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
24070
24181
|
async onHubReachable() {
|
|
24071
24182
|
if (!this.isRecordingNode) return;
|
|
24072
24183
|
await this.refreshLocations();
|
|
24184
|
+
const removalHook = this.removalHook;
|
|
24185
|
+
if (removalHook !== null) this.ctx.addDisposer(armRemovalReconcile({
|
|
24186
|
+
api: this.ctx.api,
|
|
24187
|
+
logger: this.ctx.logger,
|
|
24188
|
+
remove: (input) => removalHook.onDeviceRemoved(input)
|
|
24189
|
+
}));
|
|
24073
24190
|
try {
|
|
24074
24191
|
this.exportsRoot = await this.ctx.api.storage.resolve.query({
|
|
24075
24192
|
location: ARTIFACTS_MEDIA_LOCATION,
|
|
@@ -25051,19 +25168,25 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
25051
25168
|
return this.schema({ sections: [{
|
|
25052
25169
|
id: "cluster-recording",
|
|
25053
25170
|
title: "Cluster recording",
|
|
25171
|
+
titleKey: "pipeline:globalSettings.s_cluster-recording.title",
|
|
25054
25172
|
description: "Which node runs recording (continuous + events write path). A SINGLE node — no multi-node balancing; every other node stays fully inert. Default: hub.",
|
|
25173
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.description",
|
|
25055
25174
|
columns: 1,
|
|
25056
25175
|
fields: [{
|
|
25057
25176
|
type: "text",
|
|
25058
25177
|
key: "recordingNodeId",
|
|
25059
25178
|
label: "Recording node",
|
|
25179
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.label",
|
|
25060
25180
|
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.",
|
|
25181
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.description",
|
|
25061
25182
|
default: RECORDING_NODE_DEFAULT
|
|
25062
25183
|
}, {
|
|
25063
25184
|
type: "text",
|
|
25064
25185
|
key: "recordingHubHostname",
|
|
25065
25186
|
label: "Hub host (cross-node)",
|
|
25187
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.label",
|
|
25066
25188
|
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.",
|
|
25189
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.description",
|
|
25067
25190
|
default: ""
|
|
25068
25191
|
}]
|
|
25069
25192
|
}] });
|
|
@@ -25137,8 +25260,20 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
25137
25260
|
this.censusedSettingsStoreClient ??= censusStoreClient(this.ctx.api.settingsStore, this.readCensus);
|
|
25138
25261
|
return this.censusedSettingsStoreClient;
|
|
25139
25262
|
}
|
|
25263
|
+
configsState = null;
|
|
25264
|
+
/** A camera's recording config — its per-device settings (D865), migrated on read. */
|
|
25140
25265
|
configStore() {
|
|
25141
|
-
|
|
25266
|
+
const settings = this.ctx.settings;
|
|
25267
|
+
if (settings === void 0) throw new Error("recorder: device settings are not available");
|
|
25268
|
+
this.configsState ??= recordingConfigsState({
|
|
25269
|
+
settings,
|
|
25270
|
+
logger: this.ctx.logger,
|
|
25271
|
+
listDeviceIds: async () => (await this.ctx.api.deviceManager.listAll.query({
|
|
25272
|
+
isCamera: true,
|
|
25273
|
+
projection: "slim"
|
|
25274
|
+
})).map((device) => device.id)
|
|
25275
|
+
});
|
|
25276
|
+
return this.configsState;
|
|
25142
25277
|
}
|
|
25143
25278
|
/** Durable handle over the exportId→ExportRecord blob (same `BaseAddon.state()`
|
|
25144
25279
|
* wrapper as the config store). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-emK7D4bc.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-DmuX7IFJ.js");
|
|
3
3
|
let node_child_process = require("node:child_process");
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { An as errMsg } from "./dist-8AEiKOFO.mjs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region src/stream-broker/recorded/avcc-annexb.ts
|
|
5
5
|
/**
|