@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.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,79 @@ 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
|
+
if (list === void 0) throw new Error("the per-device settings cannot list their devices on this view");
|
|
5052
|
+
const ids = await list.call(this.deps.settings);
|
|
5053
|
+
const batch = this.deps.settings.readDeviceStoreBatch;
|
|
5054
|
+
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);
|
|
5055
|
+
for (const [id, blob] of blobs) {
|
|
5056
|
+
const held = blob[RECORDING_CONFIG_KEY];
|
|
5057
|
+
if (held !== void 0) out.set(String(id), held);
|
|
5040
5058
|
}
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5059
|
+
return out;
|
|
5060
|
+
}
|
|
5061
|
+
async readAll() {
|
|
5062
|
+
const out = /* @__PURE__ */ new Map();
|
|
5063
|
+
for (const [key, raw] of await this.readAllRaw()) {
|
|
5064
|
+
const parsed = this.parse(Number(key), raw);
|
|
5065
|
+
if (parsed !== null) out.set(key, parsed);
|
|
5066
|
+
}
|
|
5067
|
+
return out;
|
|
5068
|
+
}
|
|
5069
|
+
async write(key, value) {
|
|
5070
|
+
const deviceId = Number(key);
|
|
5071
|
+
if (!Number.isInteger(deviceId)) throw new Error(`recording config: bad device id "${key}"`);
|
|
5072
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: value });
|
|
5073
|
+
}
|
|
5074
|
+
async remove(key) {
|
|
5075
|
+
const deviceId = Number(key);
|
|
5076
|
+
if (!Number.isInteger(deviceId)) return;
|
|
5077
|
+
await this.deps.settings.writeDeviceStore(deviceId, { [RECORDING_CONFIG_KEY]: void 0 });
|
|
5078
|
+
}
|
|
5049
5079
|
};
|
|
5080
|
+
/** Build the handle over the standard per-device settings. */
|
|
5081
|
+
function recordingConfigsState(deps) {
|
|
5082
|
+
return new DeviceSettingsConfigsState(deps);
|
|
5083
|
+
}
|
|
5050
5084
|
/** Cold-start default for a camera that has never been configured: disabled, no bands. */
|
|
5051
5085
|
var DEFAULT_DEVICE_CONFIG = {
|
|
5052
5086
|
enabled: false,
|
|
5053
5087
|
bands: []
|
|
5054
5088
|
};
|
|
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
5089
|
/** Read every persisted device config, keyed by numeric deviceId. */
|
|
5064
5090
|
async function readDeviceConfigs(state) {
|
|
5065
5091
|
const rows = await state.readAll();
|
|
@@ -6186,6 +6212,7 @@ Object.freeze({
|
|
|
6186
6212
|
"device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
|
|
6187
6213
|
"device-extension": ["listSlots", "listSources"],
|
|
6188
6214
|
"device-provider": ["discoverDevices", "getDevices"],
|
|
6215
|
+
"device-removal-hook": ["listDeviceData"],
|
|
6189
6216
|
"failure-contribution": ["list"],
|
|
6190
6217
|
"llm": [
|
|
6191
6218
|
"getDefaults",
|
|
@@ -11098,7 +11125,7 @@ function buildRecordingProvider(deps) {
|
|
|
11098
11125
|
limit
|
|
11099
11126
|
}) ?? [],
|
|
11100
11127
|
renderClip: async (input) => {
|
|
11101
|
-
if (!deps.clipRender) throw
|
|
11128
|
+
if (!deps.clipRender) throw userError("recording.clip-render-unavailable", void 0, "clip rendering unavailable on this node");
|
|
11102
11129
|
return deps.clipRender(input);
|
|
11103
11130
|
},
|
|
11104
11131
|
renderGif: async (input) => {
|
|
@@ -20071,7 +20098,7 @@ async function hasFootage(index, deviceId, profile, fromMs, toMs) {
|
|
|
20071
20098
|
/** Load a record or throw a clear NOT_FOUND-style error. */
|
|
20072
20099
|
async function requireRecord(state, exportId) {
|
|
20073
20100
|
const rec = await getExportRecord(state, exportId);
|
|
20074
|
-
if (!rec) throw
|
|
20101
|
+
if (!rec) throw userError("recording.export-unknown", { id: exportId }, `recording-export: unknown export "${exportId}"`);
|
|
20075
20102
|
return rec;
|
|
20076
20103
|
}
|
|
20077
20104
|
/** Build the `recordingExport` provider over the injected deps. */
|
|
@@ -20184,11 +20211,14 @@ function buildRecordingExportProvider(deps) {
|
|
|
20184
20211
|
if (subject.kind === "clip") return await createClipExport(subject, input.options);
|
|
20185
20212
|
const { deviceId, profiles, fromMs, toMs } = subject;
|
|
20186
20213
|
const { options } = input;
|
|
20187
|
-
if (!(fromMs < toMs)) throw
|
|
20214
|
+
if (!(fromMs < toMs)) throw userError("recording.export-invalid-range", {
|
|
20215
|
+
fromMs,
|
|
20216
|
+
toMs
|
|
20217
|
+
}, `recording-export: invalid range ${fromMs}..${toMs}`);
|
|
20188
20218
|
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
20219
|
const now = deps.now();
|
|
20190
20220
|
const [head, ...rest] = profiles;
|
|
20191
|
-
if (head === void 0) throw
|
|
20221
|
+
if (head === void 0) throw userError("recording.export-no-profiles", void 0, "recording-export: profiles must contain at least one profile");
|
|
20192
20222
|
const first = await queueOne({
|
|
20193
20223
|
deviceId,
|
|
20194
20224
|
profile: head,
|
|
@@ -20239,7 +20269,10 @@ function buildRecordingExportProvider(deps) {
|
|
|
20239
20269
|
state: rec.state
|
|
20240
20270
|
}
|
|
20241
20271
|
});
|
|
20242
|
-
throw
|
|
20272
|
+
throw userError("recording.export-not-ready", {
|
|
20273
|
+
id: exportId,
|
|
20274
|
+
state: rec.state
|
|
20275
|
+
}, `recording-export: export "${exportId}" is not ready (${rec.state})`);
|
|
20243
20276
|
}
|
|
20244
20277
|
if (deps.now() >= rec.expiresAt) {
|
|
20245
20278
|
deps.logger.warn("export byte read refused: export has expired", {
|
|
@@ -20249,7 +20282,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20249
20282
|
expiresAt: rec.expiresAt
|
|
20250
20283
|
}
|
|
20251
20284
|
});
|
|
20252
|
-
throw
|
|
20285
|
+
throw userError("recording.export-expired", { id: exportId }, `recording-export: export "${exportId}" has expired`);
|
|
20253
20286
|
}
|
|
20254
20287
|
const file = deps.outPath(rec);
|
|
20255
20288
|
let size;
|
|
@@ -20263,7 +20296,7 @@ function buildRecordingExportProvider(deps) {
|
|
|
20263
20296
|
error: String(err)
|
|
20264
20297
|
}
|
|
20265
20298
|
});
|
|
20266
|
-
throw
|
|
20299
|
+
throw Object.assign(userError("recording.export-no-file", { id: exportId }, `recording-export: export "${exportId}" has no file on disk`), { cause: err });
|
|
20267
20300
|
}
|
|
20268
20301
|
if (size > 52428800) {
|
|
20269
20302
|
const shrink = deps.shrinkToBound;
|
|
@@ -20344,6 +20377,75 @@ function buildRecordingExportProvider(deps) {
|
|
|
20344
20377
|
* the host extracted from `CAMSTACK_HUB_URL`).
|
|
20345
20378
|
*/
|
|
20346
20379
|
var resolveRecordingHubHostname = resolveHubHostname;
|
|
20380
|
+
async function nextVictims(deps, deviceId) {
|
|
20381
|
+
const archive = deps.archive?.() ?? null;
|
|
20382
|
+
if (archive === null) return null;
|
|
20383
|
+
const victims = [];
|
|
20384
|
+
for await (const page of archive.streamSegments({ deviceId })) {
|
|
20385
|
+
victims.push(...page);
|
|
20386
|
+
if (victims.length >= 2e4) break;
|
|
20387
|
+
}
|
|
20388
|
+
return victims;
|
|
20389
|
+
}
|
|
20390
|
+
/** Delete every segment the archive holds for `deviceId`, across every location of this node. */
|
|
20391
|
+
async function purgeDeviceFootage(deps, deviceId) {
|
|
20392
|
+
let deletedBuckets = 0;
|
|
20393
|
+
let reclaimedBytes = 0;
|
|
20394
|
+
for (let pass = 0; pass < 200; pass++) {
|
|
20395
|
+
const victims = await nextVictims(deps, deviceId);
|
|
20396
|
+
if (victims === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20397
|
+
if (victims.length === 0) break;
|
|
20398
|
+
const freed = await evictVictims(deps, deviceId, victims);
|
|
20399
|
+
deletedBuckets += freed.deletedBuckets;
|
|
20400
|
+
reclaimedBytes += freed.reclaimedBytes;
|
|
20401
|
+
if (freed.deletedBuckets === 0) break;
|
|
20402
|
+
}
|
|
20403
|
+
const census = await deps.archive?.()?.census({ deviceId });
|
|
20404
|
+
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)`);
|
|
20405
|
+
if (deletedBuckets > 0) await deps.opsLog?.append({
|
|
20406
|
+
op: "prune",
|
|
20407
|
+
reason: "orphaned-device",
|
|
20408
|
+
deviceId,
|
|
20409
|
+
itemsAffected: deletedBuckets,
|
|
20410
|
+
bytesReclaimed: reclaimedBytes,
|
|
20411
|
+
detail: "the device was deleted — its footage was removed with it"
|
|
20412
|
+
});
|
|
20413
|
+
return {
|
|
20414
|
+
deletedBuckets,
|
|
20415
|
+
reclaimedBytes
|
|
20416
|
+
};
|
|
20417
|
+
}
|
|
20418
|
+
function createRecorderRemovalHook(deps) {
|
|
20419
|
+
const removeDevice = async (input) => {
|
|
20420
|
+
const { deviceId } = input;
|
|
20421
|
+
const footage = await purgeDeviceFootage(deps, deviceId);
|
|
20422
|
+
const counts = { footage: footage.deletedBuckets };
|
|
20423
|
+
if (footage.deletedBuckets > 0) deps.logger.info("recorder: removed the data of a deleted device", {
|
|
20424
|
+
tags: { deviceId },
|
|
20425
|
+
meta: {
|
|
20426
|
+
counts,
|
|
20427
|
+
reclaimedBytes: footage.reclaimedBytes,
|
|
20428
|
+
deletedAt: input.deletedAt
|
|
20429
|
+
}
|
|
20430
|
+
});
|
|
20431
|
+
return { counts };
|
|
20432
|
+
};
|
|
20433
|
+
return {
|
|
20434
|
+
onDeviceRemoved: removeDevice,
|
|
20435
|
+
listDeviceData: async () => {
|
|
20436
|
+
const out = [];
|
|
20437
|
+
const locationIds = new Set(deps.locations().map((l) => l.id));
|
|
20438
|
+
const usage = deps.archiveDeviceUsage?.(locationIds) ?? null;
|
|
20439
|
+
if (usage === null) throw new Error("no durable hour ledger on this node — footage cannot be enumerated");
|
|
20440
|
+
for (const [deviceId, u] of usage) if (u.segments > 0) out.push({
|
|
20441
|
+
store: "footage",
|
|
20442
|
+
deviceId,
|
|
20443
|
+
count: u.segments
|
|
20444
|
+
});
|
|
20445
|
+
return out;
|
|
20446
|
+
}
|
|
20447
|
+
};
|
|
20448
|
+
}
|
|
20347
20449
|
//#endregion
|
|
20348
20450
|
//#region src/recorder/addon/recording-source-provider.ts
|
|
20349
20451
|
/** What the picker calls us. */
|
|
@@ -20850,7 +20952,7 @@ var RECORDER_RETIRED_ROOT_KEYS = [
|
|
|
20850
20952
|
},
|
|
20851
20953
|
{
|
|
20852
20954
|
key: "recordingConfigs",
|
|
20853
|
-
successor:
|
|
20955
|
+
successor: "the recorder per-device settings (key recordingConfig)",
|
|
20854
20956
|
reason: "one row per camera since the flatten; as a blob, asking what camera 617 records parsed every camera in the installation"
|
|
20855
20957
|
}
|
|
20856
20958
|
];
|
|
@@ -23205,6 +23307,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23205
23307
|
* Already ALIAS-GUARDED: a writable same-type duplicate of another
|
|
23206
23308
|
* location's physical root arrives here demoted to read-only. */
|
|
23207
23309
|
resolvedLocations = [];
|
|
23310
|
+
/** The `device-removal-hook` provider (D865); its reconcile is armed in `onHubReachable`. */
|
|
23311
|
+
removalHook = null;
|
|
23208
23312
|
/** Attach-boundary placement (assignment + mount probe + audit). Null on
|
|
23209
23313
|
* non-recording nodes / before `onInitialize`. */
|
|
23210
23314
|
placement = null;
|
|
@@ -23908,6 +24012,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23908
24012
|
migrateLegacyDeviceSignalRows(this.configStore(), this.ctx.logger).catch((err) => {
|
|
23909
24013
|
this.ctx.logger.warn("recorder: device-signal config migration failed", { meta: { error: errMsg$1(err) } });
|
|
23910
24014
|
}).then(() => warnLostBands(this.configStore(), this.ctx.logger));
|
|
24015
|
+
const removalHook = createRecorderRemovalHook(recordingDeps);
|
|
24016
|
+
this.removalHook = removalHook;
|
|
23911
24017
|
const providers = [
|
|
23912
24018
|
{
|
|
23913
24019
|
capability: recordingArchiveCapability,
|
|
@@ -23939,6 +24045,10 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
23939
24045
|
{
|
|
23940
24046
|
capability: loadContributionCapability,
|
|
23941
24047
|
provider: { list: () => this.costRegistry.contributions() }
|
|
24048
|
+
},
|
|
24049
|
+
{
|
|
24050
|
+
capability: deviceRemovalHookCapability,
|
|
24051
|
+
provider: removalHook
|
|
23942
24052
|
}
|
|
23943
24053
|
];
|
|
23944
24054
|
this.footageDiscard = createFootageDiscardJob({
|
|
@@ -24070,6 +24180,12 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
24070
24180
|
async onHubReachable() {
|
|
24071
24181
|
if (!this.isRecordingNode) return;
|
|
24072
24182
|
await this.refreshLocations();
|
|
24183
|
+
const removalHook = this.removalHook;
|
|
24184
|
+
if (removalHook !== null) this.ctx.addDisposer(armRemovalReconcile({
|
|
24185
|
+
api: this.ctx.api,
|
|
24186
|
+
logger: this.ctx.logger,
|
|
24187
|
+
remove: (input) => removalHook.onDeviceRemoved(input)
|
|
24188
|
+
}));
|
|
24073
24189
|
try {
|
|
24074
24190
|
this.exportsRoot = await this.ctx.api.storage.resolve.query({
|
|
24075
24191
|
location: ARTIFACTS_MEDIA_LOCATION,
|
|
@@ -25051,19 +25167,25 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
25051
25167
|
return this.schema({ sections: [{
|
|
25052
25168
|
id: "cluster-recording",
|
|
25053
25169
|
title: "Cluster recording",
|
|
25170
|
+
titleKey: "pipeline:globalSettings.s_cluster-recording.title",
|
|
25054
25171
|
description: "Which node runs recording (continuous + events write path). A SINGLE node — no multi-node balancing; every other node stays fully inert. Default: hub.",
|
|
25172
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.description",
|
|
25055
25173
|
columns: 1,
|
|
25056
25174
|
fields: [{
|
|
25057
25175
|
type: "text",
|
|
25058
25176
|
key: "recordingNodeId",
|
|
25059
25177
|
label: "Recording node",
|
|
25178
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.label",
|
|
25060
25179
|
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.",
|
|
25180
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingNodeId.description",
|
|
25061
25181
|
default: RECORDING_NODE_DEFAULT
|
|
25062
25182
|
}, {
|
|
25063
25183
|
type: "text",
|
|
25064
25184
|
key: "recordingHubHostname",
|
|
25065
25185
|
label: "Hub host (cross-node)",
|
|
25186
|
+
labelKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.label",
|
|
25066
25187
|
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.",
|
|
25188
|
+
descriptionKey: "pipeline:globalSettings.s_cluster-recording.recordingHubHostname.description",
|
|
25067
25189
|
default: ""
|
|
25068
25190
|
}]
|
|
25069
25191
|
}] });
|
|
@@ -25137,8 +25259,16 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
25137
25259
|
this.censusedSettingsStoreClient ??= censusStoreClient(this.ctx.api.settingsStore, this.readCensus);
|
|
25138
25260
|
return this.censusedSettingsStoreClient;
|
|
25139
25261
|
}
|
|
25262
|
+
configsState = null;
|
|
25263
|
+
/** A camera's recording config — its per-device settings (D865), migrated on read. */
|
|
25140
25264
|
configStore() {
|
|
25141
|
-
|
|
25265
|
+
const settings = this.ctx.settings;
|
|
25266
|
+
if (settings === void 0) throw new Error("recorder: device settings are not available");
|
|
25267
|
+
this.configsState ??= recordingConfigsState({
|
|
25268
|
+
settings,
|
|
25269
|
+
logger: this.ctx.logger
|
|
25270
|
+
});
|
|
25271
|
+
return this.configsState;
|
|
25142
25272
|
}
|
|
25143
25273
|
/** Durable handle over the exportId→ExportRecord blob (same `BaseAddon.state()`
|
|
25144
25274
|
* 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
|
/**
|