@camstack/addon-pipeline 1.2.214 → 1.2.217
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 +2 -2
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/{default-detection-model-C2vYSX3O.mjs → default-detection-model-BwNEV8Wl.mjs} +1 -1
- package/dist/{default-detection-model-f0PImZnh.js → default-detection-model-DzzuINe-.js} +1 -1
- package/dist/detection-pipeline/index.js +6 -7
- package/dist/detection-pipeline/index.mjs +5 -6
- package/dist/{dist-DoHcimdu.js → dist-D7hxiBnC.js} +237 -16
- package/dist/{dist-DttqKvsQ.mjs → dist-DgeXZfwn.mjs} +237 -16
- package/dist/{lazy-sharp-BeeQGCUu.js → lazy-sharp-BSkbiuz5.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-NtM7__8q.mjs → node-C9GcBrwk.mjs} +1 -1
- package/dist/{node-6uF72wri.js → node-DDhmPcOx.js} +1 -1
- package/dist/pipeline-runner/index.js +541 -26
- package/dist/pipeline-runner/index.mjs +539 -24
- package/dist/{process-memory-CddpCDWL.mjs → process-memory-Dw3CGS3D.mjs} +1 -1
- package/dist/{process-memory-BRdyl5fE.js → process-memory-ENqmiiis.js} +1 -1
- package/dist/recorder/index.js +158 -47
- package/dist/recorder/index.mjs +158 -47
- package/dist/{segment-demux-js-CsFtPVzH.mjs → segment-demux-js-BGbKlotF.mjs} +1 -1
- package/dist/{segment-demux-js-CkJPdyDH.js → segment-demux-js-sLs8mbZQ.js} +1 -1
- package/dist/session-decode/decode-worker-child.js +61 -180
- package/dist/session-decode/decode-worker-child.mjs +60 -179
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbBa4iV-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BULotDCb.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-yJ2CyixO.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-Uxkwkgy5.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DICVQkNi.mjs} +1 -1
- 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-CagSUJH7.mjs → hostInit-_RQxF72h.mjs} +3 -3
- package/dist/stream-broker/index.js +20 -21
- package/dist/stream-broker/index.mjs +20 -21
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BWO2WNjw.js → worker-protocol-DGql9R4g.js} +1 -1
- package/dist/{worker-protocol-BKwkLZWT.mjs → worker-protocol-SzmaLNnP.mjs} +1 -1
- package/package.json +1 -1
- package/dist/event-loop-stall-monitor-DXLNBMkY.mjs +0 -138
- package/dist/event-loop-stall-monitor-DaUBcb13.js +0 -149
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-0MF-KL1v.mjs +0 -26
package/dist/recorder/index.js
CHANGED
|
@@ -2,11 +2,10 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
6
|
-
const require_node = require("../node-
|
|
5
|
+
const require_dist = require("../dist-D7hxiBnC.js");
|
|
6
|
+
const require_node = require("../node-DDhmPcOx.js");
|
|
7
7
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
8
8
|
const require_restream_intent = require("../restream-intent-B1Difcnq.js");
|
|
9
|
-
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
|
|
10
9
|
const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
|
|
11
10
|
let node_crypto = require("node:crypto");
|
|
12
11
|
let node_fs = require("node:fs");
|
|
@@ -1334,27 +1333,6 @@ var RecordingIndex = class {
|
|
|
1334
1333
|
return [...out.values()];
|
|
1335
1334
|
}
|
|
1336
1335
|
/**
|
|
1337
|
-
* WHICH devices hold footage on any of `locationIds` — the census, without
|
|
1338
|
-
* the archive.
|
|
1339
|
-
*
|
|
1340
|
-
* `getStorageUsage` used to answer this with `segmentsOnLocation(id)` per
|
|
1341
|
-
* location: a full multi-device COPY of every row on the disk plus a sort of
|
|
1342
|
-
* it (294 k rows on the live hub), to read one number off each row and throw
|
|
1343
|
-
* the array away. This visits each device only until its first matching row.
|
|
1344
|
-
* Same membership rule as before — a device is listed when at least one of
|
|
1345
|
-
* its rows sits on one of the given locations, so footage stranded on a
|
|
1346
|
-
* location id that no longer resolves stays invisible here exactly as it was.
|
|
1347
|
-
* @residency archive-shaped
|
|
1348
|
-
*/
|
|
1349
|
-
deviceIdsOnLocations(locationIds) {
|
|
1350
|
-
const out = /* @__PURE__ */ new Set();
|
|
1351
|
-
for (const [deviceId, m] of this.byDevice) for (const s of m.values()) if (locationIds.has(s.locationId)) {
|
|
1352
|
-
out.add(deviceId);
|
|
1353
|
-
break;
|
|
1354
|
-
}
|
|
1355
|
-
return out;
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
1336
|
* Every RESIDENT segment on a storage location, across devices, oldest-first.
|
|
1359
1337
|
*
|
|
1360
1338
|
* ## The one that bit (D295)
|
|
@@ -6867,6 +6845,81 @@ function buildRecordingDeviceSchema() {
|
|
|
6867
6845
|
}]
|
|
6868
6846
|
}] };
|
|
6869
6847
|
}
|
|
6848
|
+
//#endregion
|
|
6849
|
+
//#region src/recorder/addon/device-storage-usage.ts
|
|
6850
|
+
/**
|
|
6851
|
+
* Gap between the per-camera sum and the per-volume total that is worth
|
|
6852
|
+
* saying out loud, as a share of the volume total.
|
|
6853
|
+
*
|
|
6854
|
+
* The two are aggregations of the SAME hour rows over the same location set —
|
|
6855
|
+
* one folded per camera, one summed in SQL — so in a healthy recorder they
|
|
6856
|
+
* agree to the byte. A few tenths of a percent is a write landing between the
|
|
6857
|
+
* two reads; 97% (60.5 GB against 1.9 TB) is the defect this file closes,
|
|
6858
|
+
* rebuilt somewhere new.
|
|
6859
|
+
*/
|
|
6860
|
+
var USAGE_MISMATCH_SHARE = .02;
|
|
6861
|
+
/** Floor under {@link USAGE_MISMATCH_SHARE} so a nearly empty disk does not
|
|
6862
|
+
* warn on a single in-flight segment. */
|
|
6863
|
+
var USAGE_MISMATCH_FLOOR_BYTES = 1e9;
|
|
6864
|
+
/**
|
|
6865
|
+
* One row per camera the ARCHIVE holds footage for on `locationIds`, biggest
|
|
6866
|
+
* first (deviceId breaks a tie, so two consecutive calls compare).
|
|
6867
|
+
*
|
|
6868
|
+
* A node with no ledger REFUSES (D295 rule 3): empty rows, `read:
|
|
6869
|
+
* 'unavailable'`, and a line saying so. Falling back to the index would answer
|
|
6870
|
+
* the same question with the wrong quantity and look exactly like a correct
|
|
6871
|
+
* answer, which is how this shipped in the first place.
|
|
6872
|
+
*/
|
|
6873
|
+
function buildDeviceUsage(input) {
|
|
6874
|
+
if (input.archive === null) {
|
|
6875
|
+
input.logger.warn("recorder: no hour ledger on this node — the per-camera storage rows are UNANSWERED, not empty", { meta: { locations: [...input.locationIds].toSorted().join(",") } });
|
|
6876
|
+
return {
|
|
6877
|
+
devices: [],
|
|
6878
|
+
totalUsedBytes: 0,
|
|
6879
|
+
read: "unavailable"
|
|
6880
|
+
};
|
|
6881
|
+
}
|
|
6882
|
+
const devices = [];
|
|
6883
|
+
let totalUsedBytes = 0;
|
|
6884
|
+
for (const [deviceId, archived] of input.archive) {
|
|
6885
|
+
const resident = input.resident(deviceId);
|
|
6886
|
+
totalUsedBytes += archived.bytes;
|
|
6887
|
+
devices.push({
|
|
6888
|
+
deviceId,
|
|
6889
|
+
usedBytes: archived.bytes,
|
|
6890
|
+
oldestMs: archived.oldestMs,
|
|
6891
|
+
segments: archived.segments,
|
|
6892
|
+
residentBytes: resident.bytes,
|
|
6893
|
+
residentOldestMs: resident.oldestMs
|
|
6894
|
+
});
|
|
6895
|
+
}
|
|
6896
|
+
return {
|
|
6897
|
+
devices: devices.toSorted((a, b) => b.usedBytes - a.usedBytes || a.deviceId - b.deviceId),
|
|
6898
|
+
totalUsedBytes,
|
|
6899
|
+
read: "archive"
|
|
6900
|
+
};
|
|
6901
|
+
}
|
|
6902
|
+
/**
|
|
6903
|
+
* Say it when the per-camera sum and the per-volume total disagree.
|
|
6904
|
+
*
|
|
6905
|
+
* The "I saw N of M" rule of D295, applied to a REPORT: these two numbers are
|
|
6906
|
+
* the same rows counted two ways, so a gap means one of the two reads saw a
|
|
6907
|
+
* fraction of the archive — and the whole point of D418 is that a page which
|
|
6908
|
+
* shows both without comparing them lets a 97% gap read as a fact about the
|
|
6909
|
+
* disks.
|
|
6910
|
+
*/
|
|
6911
|
+
function warnOnUsageMismatch(logger, deviceBytes, volumeBytes, read) {
|
|
6912
|
+
if (read !== "archive") return;
|
|
6913
|
+
if (volumeBytes === 0) return;
|
|
6914
|
+
const gap = Math.abs(volumeBytes - deviceBytes);
|
|
6915
|
+
if (gap <= USAGE_MISMATCH_FLOOR_BYTES) return;
|
|
6916
|
+
if (gap <= volumeBytes * USAGE_MISMATCH_SHARE) return;
|
|
6917
|
+
logger.warn("recorder: the per-camera storage sum disagrees with the per-volume total — one of the two reads saw a fraction of the archive", { meta: {
|
|
6918
|
+
deviceBytes,
|
|
6919
|
+
volumeBytes,
|
|
6920
|
+
gap
|
|
6921
|
+
} });
|
|
6922
|
+
}
|
|
6870
6923
|
/** The placement-store key for one (camera, profile). Mirrors `assignmentKey`
|
|
6871
6924
|
* in `placement-store.ts`; kept local so this module stays dependency-free. */
|
|
6872
6925
|
function sourceKey(source) {
|
|
@@ -7454,18 +7507,15 @@ function buildRecordingProvider(deps) {
|
|
|
7454
7507
|
getPlaybackManifest: ({ deviceId, fromMs, toMs }) => buildPlaybackManifest(deps, codecCache, deviceId, fromMs, toMs),
|
|
7455
7508
|
getStorageUsage: async () => {
|
|
7456
7509
|
const locations = deps.locations();
|
|
7457
|
-
const
|
|
7458
|
-
const
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
oldestMs: accounting.oldestMs
|
|
7467
|
-
});
|
|
7468
|
-
}
|
|
7510
|
+
const locationIds = new Set(locations.map((l) => l.id));
|
|
7511
|
+
const deviceUsage = buildDeviceUsage({
|
|
7512
|
+
locationIds,
|
|
7513
|
+
archive: deps.archiveDeviceUsage?.(locationIds) ?? null,
|
|
7514
|
+
resident: (deviceId) => deps.index.accounting(deviceId),
|
|
7515
|
+
logger: deps.logger
|
|
7516
|
+
});
|
|
7517
|
+
const devices = deviceUsage.devices;
|
|
7518
|
+
const totalUsedBytes = deviceUsage.totalUsedBytes;
|
|
7469
7519
|
const byPhysical = /* @__PURE__ */ new Map();
|
|
7470
7520
|
for (const loc of locations) {
|
|
7471
7521
|
const key = physicalRootKey(loc.root);
|
|
@@ -7494,11 +7544,12 @@ function buildRecordingProvider(deps) {
|
|
|
7494
7544
|
totalBytes: cap.totalBytes
|
|
7495
7545
|
};
|
|
7496
7546
|
}));
|
|
7497
|
-
|
|
7547
|
+
warnOnUsageMismatch(deps.logger, totalUsedBytes, locationUsages.reduce((sum, l) => sum + l.usedBytes, 0), deviceUsage.read);
|
|
7498
7548
|
return {
|
|
7499
7549
|
nodeId: deps.nodeId,
|
|
7500
7550
|
totalUsedBytes,
|
|
7501
|
-
devices,
|
|
7551
|
+
devices: [...devices],
|
|
7552
|
+
devicesRead: deviceUsage.read,
|
|
7502
7553
|
locations: locationUsages
|
|
7503
7554
|
};
|
|
7504
7555
|
},
|
|
@@ -8392,6 +8443,14 @@ var DurableLedger = class DurableLedger {
|
|
|
8392
8443
|
//#endregion
|
|
8393
8444
|
//#region src/recorder/addon/segment-hour-ledger.ts
|
|
8394
8445
|
var HOUR_MS$3 = 36e5;
|
|
8446
|
+
/** `Math.min` where a missing left operand means "nothing yet", not `NaN`. */
|
|
8447
|
+
function minOf(a, b) {
|
|
8448
|
+
return a === null ? b : Math.min(a, b);
|
|
8449
|
+
}
|
|
8450
|
+
/** `Math.max` with the same rule as {@link minOf}. */
|
|
8451
|
+
function maxOf(a, b) {
|
|
8452
|
+
return a === null ? b : Math.max(a, b);
|
|
8453
|
+
}
|
|
8395
8454
|
var macrotask$1 = () => new Promise((resolve) => {
|
|
8396
8455
|
setImmediate(resolve);
|
|
8397
8456
|
});
|
|
@@ -9042,7 +9101,7 @@ var SegmentHourLedger = class SegmentHourLedger {
|
|
|
9042
9101
|
*
|
|
9043
9102
|
* The reader repairs the durable index it just reconstructed, so the next
|
|
9044
9103
|
* cold read of this hour is a point read instead of another `readdir`
|
|
9045
|
-
* (`docs/design/2026-08-29-
|
|
9104
|
+
* (`docs/design/2026-08-29-reference-nvr-recording-model.md` §4.2). Two
|
|
9046
9105
|
* constraints, both enforced by the caller and re-stated here because
|
|
9047
9106
|
* getting either wrong turns a READ into durable damage: the hour must be
|
|
9048
9107
|
* CLOSED (a live hour's directory races the writer, so a walk of it is a
|
|
@@ -9275,6 +9334,53 @@ var SegmentHourLedger = class SegmentHourLedger {
|
|
|
9275
9334
|
for (const row of this.ledger.snapshot()) if (locationIds.has(row.locationId)) out.add(row.deviceId);
|
|
9276
9335
|
return out;
|
|
9277
9336
|
}
|
|
9337
|
+
/**
|
|
9338
|
+
* Bytes / segments / oldest / newest per CAMERA on these locations — the
|
|
9339
|
+
* per-camera half of the storage page, answered by the ARCHIVE (D418).
|
|
9340
|
+
*
|
|
9341
|
+
* ## Why the T1 fold and not `census({ deviceId })` per camera
|
|
9342
|
+
*
|
|
9343
|
+
* `census` is the right authority for a SCOPE, and it cannot express this
|
|
9344
|
+
* scope: {@link SegmentHourLedger.predicate} takes ONE `locationId`, so
|
|
9345
|
+
* "camera 615 across the three resolvable volumes" is three aggregates, and
|
|
9346
|
+
* `census({ deviceId })` alone would silently count footage stranded on a
|
|
9347
|
+
* location id that no longer resolves — bytes no volume row on the same page
|
|
9348
|
+
* accounts for. Per camera per volume that is `cameras × volumes` SQL
|
|
9349
|
+
* aggregates for one operator click, and it grows with the fleet.
|
|
9350
|
+
*
|
|
9351
|
+
* The T1 hour summary answers all of them in ONE pass with no I/O, exactly
|
|
9352
|
+
* as {@link devicesWithFootage} and {@link profilesWithFootage} already do,
|
|
9353
|
+
* and it is the same rows `accountingForLocations` sums in SQL — the mirror
|
|
9354
|
+
* is write-behind, so it is never BEHIND the store. The cost is one pass
|
|
9355
|
+
* over the hour rows the mirror holds — O(writers × profiles × hours), ~19 k
|
|
9356
|
+
* on the live hub (see {@link REPAIR_MAX_ROWS}) against 98 445 segments,
|
|
9357
|
+
* i.e. it does NOT scale with the segment count — and it is capped for good
|
|
9358
|
+
* by {@link SEGMENT_HOUR_SUMMARY_LIMIT}, the mirror's own ceiling.
|
|
9359
|
+
*
|
|
9360
|
+
* ## What an empty answer means
|
|
9361
|
+
*
|
|
9362
|
+
* The same thing it means for {@link devicesWithFootage}: "the mirror knows
|
|
9363
|
+
* no hour here" — which is also what a ledger whose load failed reports. The
|
|
9364
|
+
* caller must not render that as "no camera has footage"; `getStorageUsage`
|
|
9365
|
+
* cross-checks the per-camera sum against the volume aggregate and SAYS so
|
|
9366
|
+
* when the two disagree.
|
|
9367
|
+
*/
|
|
9368
|
+
usageByDevice(locationIds) {
|
|
9369
|
+
const out = /* @__PURE__ */ new Map();
|
|
9370
|
+
if (locationIds.size === 0) return out;
|
|
9371
|
+
for (const row of this.ledger.snapshot()) {
|
|
9372
|
+
if (!locationIds.has(row.locationId)) continue;
|
|
9373
|
+
const empty = row.segments === 0;
|
|
9374
|
+
const prev = out.get(row.deviceId);
|
|
9375
|
+
out.set(row.deviceId, {
|
|
9376
|
+
bytes: (prev?.bytes ?? 0) + row.bytes,
|
|
9377
|
+
segments: (prev?.segments ?? 0) + row.segments,
|
|
9378
|
+
oldestMs: empty ? prev?.oldestMs ?? null : minOf(prev?.oldestMs ?? null, row.minStartMs),
|
|
9379
|
+
newestMs: empty ? prev?.newestMs ?? null : maxOf(prev?.newestMs ?? null, row.maxStartMs)
|
|
9380
|
+
});
|
|
9381
|
+
}
|
|
9382
|
+
return out;
|
|
9383
|
+
}
|
|
9278
9384
|
async oldestSegmentsScoped(locationIds, deviceIds, targetBytes) {
|
|
9279
9385
|
const ids = [...locationIds];
|
|
9280
9386
|
if (ids.length === 0 || targetBytes <= 0) return {
|
|
@@ -14534,7 +14640,7 @@ var RecordingController = class {
|
|
|
14534
14640
|
/**
|
|
14535
14641
|
* The ADAPTIVE attach bound. A transient `spawn ffmpeg` failure (EAGAIN) is
|
|
14536
14642
|
* the host saying it cannot fork right now, and the answer is to attach
|
|
14537
|
-
* FEWER cameras at once — not to serialise them, which
|
|
14643
|
+
* FEWER cameras at once — not to serialise them, which removed
|
|
14538
14644
|
* for good reason. Every bounded-parallel attach in this file reads it.
|
|
14539
14645
|
*/
|
|
14540
14646
|
spawnPressure;
|
|
@@ -16039,7 +16145,7 @@ function selectRedundantSegments(segs) {
|
|
|
16039
16145
|
* The pruner that used to remove it, `runRedundancyJanitor`, was reachable ONLY
|
|
16040
16146
|
* from `runFullArchiveWalk`, which was reachable only from
|
|
16041
16147
|
* `scheduleDeferredFullWalk`, which had no caller at all — so on 2026-08-27
|
|
16042
|
-
*
|
|
16148
|
+
* all three were deleted together as dead code. That was
|
|
16043
16149
|
* correct about the walk and left the system with **no duplicate pruner
|
|
16044
16150
|
* whatsoever**. The precedent for letting this class of waste run unattended is
|
|
16045
16151
|
* the 179 GB of stranded staging segments in `staging-reconcile.ts`.
|
|
@@ -16983,11 +17089,15 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
16983
17089
|
/**
|
|
16984
17090
|
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
16985
17091
|
*
|
|
16986
|
-
* REUSED,
|
|
16987
|
-
*
|
|
16988
|
-
*
|
|
16989
|
-
* that
|
|
16990
|
-
*
|
|
17092
|
+
* REUSED, never re-implemented. It used to live next door in this bundle;
|
|
17093
|
+
* on 2026-09-09 it moved to `@camstack/system/addon-utils` (the subpath, never
|
|
17094
|
+
* the barrel — that one drags the kernel into the runner) because two more
|
|
17095
|
+
* processes that do NOT share this bundle needed it — `pipeline-analytics` and
|
|
17096
|
+
* hub-main — and a per-bundle copy is how two diagnostics end up disagreeing
|
|
17097
|
+
* about the same freeze. The cost of the move is a slower ship chain: this
|
|
17098
|
+
* import is host-resolved, so a `camstack deploy` of this addon onto a hub
|
|
17099
|
+
* whose server closure predates the export fails to load. Deploy order is
|
|
17100
|
+
* closure first, addons second. See D417.
|
|
16991
17101
|
*/
|
|
16992
17102
|
stopStallMonitor = null;
|
|
16993
17103
|
/**
|
|
@@ -17188,7 +17298,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
17188
17298
|
} });
|
|
17189
17299
|
}
|
|
17190
17300
|
async onInitialize() {
|
|
17191
|
-
this.stopStallMonitor =
|
|
17301
|
+
this.stopStallMonitor = (0, _camstack_system_addon_utils.startEventLoopStallMonitor)(this.ctx.logger);
|
|
17192
17302
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
17193
17303
|
this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
|
|
17194
17304
|
const configuredNode = this.config.recordingNodeId;
|
|
@@ -17446,6 +17556,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
17446
17556
|
refreshLocations: () => this.refreshLocations(),
|
|
17447
17557
|
capacity: (root) => this.locationCapacity(root),
|
|
17448
17558
|
archiveAccounting: (locationIds) => this.segmentHours?.accountingForLocations(locationIds) ?? Promise.resolve(EMPTY_ARCHIVE_ACCOUNTING),
|
|
17559
|
+
archiveDeviceUsage: (locationIds) => this.segmentHours?.usageByDevice(locationIds) ?? null,
|
|
17449
17560
|
archive: () => this.segmentHours,
|
|
17450
17561
|
ledgerWalk: this.ledgerWalkReconciler(),
|
|
17451
17562
|
hydrateDevice: (deviceId, locations) => hydrateDeviceFromStorage(this.ctx.api, this.index, deviceId, locations, this.ctx.logger),
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { At as storageEvictableCapability, Ct as recordingSignalCapability, D as RecordingSignalStatusSchema, E as RecordingConfigSchema, Et as resolveLocationMode, Gt as DeviceFeature, H as deriveBatteryPresence, It as errMsg, Jt as hydrateSchema, Kt as DeviceType, Nt as stripRetiredBandPreBufferSec, Ot as resolveRecordingProfiles, P as batteryCapability, Q as evictionPolicyOfLocation, Qt as nodePin, R as customAction, St as recordingExportCapability, T as RECORDING_EXPORT_MAX_READ_BYTES, Ut as BaseAddon, V as defineCustomActions, W as deriveRecordingMode, Y as evaluateSensorEdge, Z as evictionPolicyForMode, an as boolean, ct as mayReadLocation, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, dn as preprocess, dt as modeMayWrite, et as gbToBytes, fn as record, g as ExportRecordSchema, hn as EventCategory, i as BatteryStatusSchema, in as array, it as loadContributionCapability, jt as storageOccupancyCapability, ln as number, lt as mayWriteToLocation, nn as sleep$1, pn as string, rn as _enum, rt as isFirstLevelMacroClass, tn as selectAssignedProfileSlots, un as object, ut as migrateLegacyDeviceSignalConfig, x as OpsLogEntrySchema, xt as recordingCapability } from "../dist-
|
|
2
|
-
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
1
|
+
import { At as storageEvictableCapability, Ct as recordingSignalCapability, D as RecordingSignalStatusSchema, E as RecordingConfigSchema, Et as resolveLocationMode, Gt as DeviceFeature, H as deriveBatteryPresence, It as errMsg, Jt as hydrateSchema, Kt as DeviceType, Nt as stripRetiredBandPreBufferSec, Ot as resolveRecordingProfiles, P as batteryCapability, Q as evictionPolicyOfLocation, Qt as nodePin, R as customAction, St as recordingExportCapability, T as RECORDING_EXPORT_MAX_READ_BYTES, Ut as BaseAddon, V as defineCustomActions, W as deriveRecordingMode, Y as evaluateSensorEdge, Z as evictionPolicyForMode, an as boolean, ct as mayReadLocation, d as DEFAULT_EVENTS_BAND_BUFFER_SEC, dn as preprocess, dt as modeMayWrite, et as gbToBytes, fn as record, g as ExportRecordSchema, hn as EventCategory, i as BatteryStatusSchema, in as array, it as loadContributionCapability, jt as storageOccupancyCapability, ln as number, lt as mayWriteToLocation, nn as sleep$1, pn as string, rn as _enum, rt as isFirstLevelMacroClass, tn as selectAssignedProfileSlots, un as object, ut as migrateLegacyDeviceSignalConfig, x as OpsLogEntrySchema, xt as recordingCapability } from "../dist-DgeXZfwn.mjs";
|
|
2
|
+
import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-C9GcBrwk.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { c as PREBUFFER_MAX_MS, r as RECORDING_PRE_ROLL_REQUEST_MS, s as withRecordingIntent } from "../restream-intent-DWe2kInY.mjs";
|
|
5
|
-
import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
|
|
6
5
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
|
|
7
6
|
import { randomUUID } from "node:crypto";
|
|
8
7
|
import { createReadStream, promises, realpathSync } from "node:fs";
|
|
9
8
|
import path from "node:path";
|
|
10
9
|
import { spawn } from "node:child_process";
|
|
11
10
|
import { open } from "node:fs/promises";
|
|
12
|
-
import { contentTypeFor, createFileDataPlaneHandler, parseRangeHeader } from "@camstack/system/addon-utils";
|
|
11
|
+
import { contentTypeFor, createFileDataPlaneHandler, parseRangeHeader, startEventLoopStallMonitor } from "@camstack/system/addon-utils";
|
|
13
12
|
//#region src/recorder/event-capture.ts
|
|
14
13
|
/**
|
|
15
14
|
* La sottoscrizione motion del recorder: bus → `onTrigger(deviceId, atMs)`.
|
|
@@ -1329,27 +1328,6 @@ var RecordingIndex = class {
|
|
|
1329
1328
|
return [...out.values()];
|
|
1330
1329
|
}
|
|
1331
1330
|
/**
|
|
1332
|
-
* WHICH devices hold footage on any of `locationIds` — the census, without
|
|
1333
|
-
* the archive.
|
|
1334
|
-
*
|
|
1335
|
-
* `getStorageUsage` used to answer this with `segmentsOnLocation(id)` per
|
|
1336
|
-
* location: a full multi-device COPY of every row on the disk plus a sort of
|
|
1337
|
-
* it (294 k rows on the live hub), to read one number off each row and throw
|
|
1338
|
-
* the array away. This visits each device only until its first matching row.
|
|
1339
|
-
* Same membership rule as before — a device is listed when at least one of
|
|
1340
|
-
* its rows sits on one of the given locations, so footage stranded on a
|
|
1341
|
-
* location id that no longer resolves stays invisible here exactly as it was.
|
|
1342
|
-
* @residency archive-shaped
|
|
1343
|
-
*/
|
|
1344
|
-
deviceIdsOnLocations(locationIds) {
|
|
1345
|
-
const out = /* @__PURE__ */ new Set();
|
|
1346
|
-
for (const [deviceId, m] of this.byDevice) for (const s of m.values()) if (locationIds.has(s.locationId)) {
|
|
1347
|
-
out.add(deviceId);
|
|
1348
|
-
break;
|
|
1349
|
-
}
|
|
1350
|
-
return out;
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
1331
|
* Every RESIDENT segment on a storage location, across devices, oldest-first.
|
|
1354
1332
|
*
|
|
1355
1333
|
* ## The one that bit (D295)
|
|
@@ -6861,6 +6839,81 @@ function buildRecordingDeviceSchema() {
|
|
|
6861
6839
|
}]
|
|
6862
6840
|
}] };
|
|
6863
6841
|
}
|
|
6842
|
+
//#endregion
|
|
6843
|
+
//#region src/recorder/addon/device-storage-usage.ts
|
|
6844
|
+
/**
|
|
6845
|
+
* Gap between the per-camera sum and the per-volume total that is worth
|
|
6846
|
+
* saying out loud, as a share of the volume total.
|
|
6847
|
+
*
|
|
6848
|
+
* The two are aggregations of the SAME hour rows over the same location set —
|
|
6849
|
+
* one folded per camera, one summed in SQL — so in a healthy recorder they
|
|
6850
|
+
* agree to the byte. A few tenths of a percent is a write landing between the
|
|
6851
|
+
* two reads; 97% (60.5 GB against 1.9 TB) is the defect this file closes,
|
|
6852
|
+
* rebuilt somewhere new.
|
|
6853
|
+
*/
|
|
6854
|
+
var USAGE_MISMATCH_SHARE = .02;
|
|
6855
|
+
/** Floor under {@link USAGE_MISMATCH_SHARE} so a nearly empty disk does not
|
|
6856
|
+
* warn on a single in-flight segment. */
|
|
6857
|
+
var USAGE_MISMATCH_FLOOR_BYTES = 1e9;
|
|
6858
|
+
/**
|
|
6859
|
+
* One row per camera the ARCHIVE holds footage for on `locationIds`, biggest
|
|
6860
|
+
* first (deviceId breaks a tie, so two consecutive calls compare).
|
|
6861
|
+
*
|
|
6862
|
+
* A node with no ledger REFUSES (D295 rule 3): empty rows, `read:
|
|
6863
|
+
* 'unavailable'`, and a line saying so. Falling back to the index would answer
|
|
6864
|
+
* the same question with the wrong quantity and look exactly like a correct
|
|
6865
|
+
* answer, which is how this shipped in the first place.
|
|
6866
|
+
*/
|
|
6867
|
+
function buildDeviceUsage(input) {
|
|
6868
|
+
if (input.archive === null) {
|
|
6869
|
+
input.logger.warn("recorder: no hour ledger on this node — the per-camera storage rows are UNANSWERED, not empty", { meta: { locations: [...input.locationIds].toSorted().join(",") } });
|
|
6870
|
+
return {
|
|
6871
|
+
devices: [],
|
|
6872
|
+
totalUsedBytes: 0,
|
|
6873
|
+
read: "unavailable"
|
|
6874
|
+
};
|
|
6875
|
+
}
|
|
6876
|
+
const devices = [];
|
|
6877
|
+
let totalUsedBytes = 0;
|
|
6878
|
+
for (const [deviceId, archived] of input.archive) {
|
|
6879
|
+
const resident = input.resident(deviceId);
|
|
6880
|
+
totalUsedBytes += archived.bytes;
|
|
6881
|
+
devices.push({
|
|
6882
|
+
deviceId,
|
|
6883
|
+
usedBytes: archived.bytes,
|
|
6884
|
+
oldestMs: archived.oldestMs,
|
|
6885
|
+
segments: archived.segments,
|
|
6886
|
+
residentBytes: resident.bytes,
|
|
6887
|
+
residentOldestMs: resident.oldestMs
|
|
6888
|
+
});
|
|
6889
|
+
}
|
|
6890
|
+
return {
|
|
6891
|
+
devices: devices.toSorted((a, b) => b.usedBytes - a.usedBytes || a.deviceId - b.deviceId),
|
|
6892
|
+
totalUsedBytes,
|
|
6893
|
+
read: "archive"
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
/**
|
|
6897
|
+
* Say it when the per-camera sum and the per-volume total disagree.
|
|
6898
|
+
*
|
|
6899
|
+
* The "I saw N of M" rule of D295, applied to a REPORT: these two numbers are
|
|
6900
|
+
* the same rows counted two ways, so a gap means one of the two reads saw a
|
|
6901
|
+
* fraction of the archive — and the whole point of D418 is that a page which
|
|
6902
|
+
* shows both without comparing them lets a 97% gap read as a fact about the
|
|
6903
|
+
* disks.
|
|
6904
|
+
*/
|
|
6905
|
+
function warnOnUsageMismatch(logger, deviceBytes, volumeBytes, read) {
|
|
6906
|
+
if (read !== "archive") return;
|
|
6907
|
+
if (volumeBytes === 0) return;
|
|
6908
|
+
const gap = Math.abs(volumeBytes - deviceBytes);
|
|
6909
|
+
if (gap <= USAGE_MISMATCH_FLOOR_BYTES) return;
|
|
6910
|
+
if (gap <= volumeBytes * USAGE_MISMATCH_SHARE) return;
|
|
6911
|
+
logger.warn("recorder: the per-camera storage sum disagrees with the per-volume total — one of the two reads saw a fraction of the archive", { meta: {
|
|
6912
|
+
deviceBytes,
|
|
6913
|
+
volumeBytes,
|
|
6914
|
+
gap
|
|
6915
|
+
} });
|
|
6916
|
+
}
|
|
6864
6917
|
/** The placement-store key for one (camera, profile). Mirrors `assignmentKey`
|
|
6865
6918
|
* in `placement-store.ts`; kept local so this module stays dependency-free. */
|
|
6866
6919
|
function sourceKey(source) {
|
|
@@ -7448,18 +7501,15 @@ function buildRecordingProvider(deps) {
|
|
|
7448
7501
|
getPlaybackManifest: ({ deviceId, fromMs, toMs }) => buildPlaybackManifest(deps, codecCache, deviceId, fromMs, toMs),
|
|
7449
7502
|
getStorageUsage: async () => {
|
|
7450
7503
|
const locations = deps.locations();
|
|
7451
|
-
const
|
|
7452
|
-
const
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
oldestMs: accounting.oldestMs
|
|
7461
|
-
});
|
|
7462
|
-
}
|
|
7504
|
+
const locationIds = new Set(locations.map((l) => l.id));
|
|
7505
|
+
const deviceUsage = buildDeviceUsage({
|
|
7506
|
+
locationIds,
|
|
7507
|
+
archive: deps.archiveDeviceUsage?.(locationIds) ?? null,
|
|
7508
|
+
resident: (deviceId) => deps.index.accounting(deviceId),
|
|
7509
|
+
logger: deps.logger
|
|
7510
|
+
});
|
|
7511
|
+
const devices = deviceUsage.devices;
|
|
7512
|
+
const totalUsedBytes = deviceUsage.totalUsedBytes;
|
|
7463
7513
|
const byPhysical = /* @__PURE__ */ new Map();
|
|
7464
7514
|
for (const loc of locations) {
|
|
7465
7515
|
const key = physicalRootKey(loc.root);
|
|
@@ -7488,11 +7538,12 @@ function buildRecordingProvider(deps) {
|
|
|
7488
7538
|
totalBytes: cap.totalBytes
|
|
7489
7539
|
};
|
|
7490
7540
|
}));
|
|
7491
|
-
|
|
7541
|
+
warnOnUsageMismatch(deps.logger, totalUsedBytes, locationUsages.reduce((sum, l) => sum + l.usedBytes, 0), deviceUsage.read);
|
|
7492
7542
|
return {
|
|
7493
7543
|
nodeId: deps.nodeId,
|
|
7494
7544
|
totalUsedBytes,
|
|
7495
|
-
devices,
|
|
7545
|
+
devices: [...devices],
|
|
7546
|
+
devicesRead: deviceUsage.read,
|
|
7496
7547
|
locations: locationUsages
|
|
7497
7548
|
};
|
|
7498
7549
|
},
|
|
@@ -8386,6 +8437,14 @@ var DurableLedger = class DurableLedger {
|
|
|
8386
8437
|
//#endregion
|
|
8387
8438
|
//#region src/recorder/addon/segment-hour-ledger.ts
|
|
8388
8439
|
var HOUR_MS$3 = 36e5;
|
|
8440
|
+
/** `Math.min` where a missing left operand means "nothing yet", not `NaN`. */
|
|
8441
|
+
function minOf(a, b) {
|
|
8442
|
+
return a === null ? b : Math.min(a, b);
|
|
8443
|
+
}
|
|
8444
|
+
/** `Math.max` with the same rule as {@link minOf}. */
|
|
8445
|
+
function maxOf(a, b) {
|
|
8446
|
+
return a === null ? b : Math.max(a, b);
|
|
8447
|
+
}
|
|
8389
8448
|
var macrotask$1 = () => new Promise((resolve) => {
|
|
8390
8449
|
setImmediate(resolve);
|
|
8391
8450
|
});
|
|
@@ -9036,7 +9095,7 @@ var SegmentHourLedger = class SegmentHourLedger {
|
|
|
9036
9095
|
*
|
|
9037
9096
|
* The reader repairs the durable index it just reconstructed, so the next
|
|
9038
9097
|
* cold read of this hour is a point read instead of another `readdir`
|
|
9039
|
-
* (`docs/design/2026-08-29-
|
|
9098
|
+
* (`docs/design/2026-08-29-reference-nvr-recording-model.md` §4.2). Two
|
|
9040
9099
|
* constraints, both enforced by the caller and re-stated here because
|
|
9041
9100
|
* getting either wrong turns a READ into durable damage: the hour must be
|
|
9042
9101
|
* CLOSED (a live hour's directory races the writer, so a walk of it is a
|
|
@@ -9269,6 +9328,53 @@ var SegmentHourLedger = class SegmentHourLedger {
|
|
|
9269
9328
|
for (const row of this.ledger.snapshot()) if (locationIds.has(row.locationId)) out.add(row.deviceId);
|
|
9270
9329
|
return out;
|
|
9271
9330
|
}
|
|
9331
|
+
/**
|
|
9332
|
+
* Bytes / segments / oldest / newest per CAMERA on these locations — the
|
|
9333
|
+
* per-camera half of the storage page, answered by the ARCHIVE (D418).
|
|
9334
|
+
*
|
|
9335
|
+
* ## Why the T1 fold and not `census({ deviceId })` per camera
|
|
9336
|
+
*
|
|
9337
|
+
* `census` is the right authority for a SCOPE, and it cannot express this
|
|
9338
|
+
* scope: {@link SegmentHourLedger.predicate} takes ONE `locationId`, so
|
|
9339
|
+
* "camera 615 across the three resolvable volumes" is three aggregates, and
|
|
9340
|
+
* `census({ deviceId })` alone would silently count footage stranded on a
|
|
9341
|
+
* location id that no longer resolves — bytes no volume row on the same page
|
|
9342
|
+
* accounts for. Per camera per volume that is `cameras × volumes` SQL
|
|
9343
|
+
* aggregates for one operator click, and it grows with the fleet.
|
|
9344
|
+
*
|
|
9345
|
+
* The T1 hour summary answers all of them in ONE pass with no I/O, exactly
|
|
9346
|
+
* as {@link devicesWithFootage} and {@link profilesWithFootage} already do,
|
|
9347
|
+
* and it is the same rows `accountingForLocations` sums in SQL — the mirror
|
|
9348
|
+
* is write-behind, so it is never BEHIND the store. The cost is one pass
|
|
9349
|
+
* over the hour rows the mirror holds — O(writers × profiles × hours), ~19 k
|
|
9350
|
+
* on the live hub (see {@link REPAIR_MAX_ROWS}) against 98 445 segments,
|
|
9351
|
+
* i.e. it does NOT scale with the segment count — and it is capped for good
|
|
9352
|
+
* by {@link SEGMENT_HOUR_SUMMARY_LIMIT}, the mirror's own ceiling.
|
|
9353
|
+
*
|
|
9354
|
+
* ## What an empty answer means
|
|
9355
|
+
*
|
|
9356
|
+
* The same thing it means for {@link devicesWithFootage}: "the mirror knows
|
|
9357
|
+
* no hour here" — which is also what a ledger whose load failed reports. The
|
|
9358
|
+
* caller must not render that as "no camera has footage"; `getStorageUsage`
|
|
9359
|
+
* cross-checks the per-camera sum against the volume aggregate and SAYS so
|
|
9360
|
+
* when the two disagree.
|
|
9361
|
+
*/
|
|
9362
|
+
usageByDevice(locationIds) {
|
|
9363
|
+
const out = /* @__PURE__ */ new Map();
|
|
9364
|
+
if (locationIds.size === 0) return out;
|
|
9365
|
+
for (const row of this.ledger.snapshot()) {
|
|
9366
|
+
if (!locationIds.has(row.locationId)) continue;
|
|
9367
|
+
const empty = row.segments === 0;
|
|
9368
|
+
const prev = out.get(row.deviceId);
|
|
9369
|
+
out.set(row.deviceId, {
|
|
9370
|
+
bytes: (prev?.bytes ?? 0) + row.bytes,
|
|
9371
|
+
segments: (prev?.segments ?? 0) + row.segments,
|
|
9372
|
+
oldestMs: empty ? prev?.oldestMs ?? null : minOf(prev?.oldestMs ?? null, row.minStartMs),
|
|
9373
|
+
newestMs: empty ? prev?.newestMs ?? null : maxOf(prev?.newestMs ?? null, row.maxStartMs)
|
|
9374
|
+
});
|
|
9375
|
+
}
|
|
9376
|
+
return out;
|
|
9377
|
+
}
|
|
9272
9378
|
async oldestSegmentsScoped(locationIds, deviceIds, targetBytes) {
|
|
9273
9379
|
const ids = [...locationIds];
|
|
9274
9380
|
if (ids.length === 0 || targetBytes <= 0) return {
|
|
@@ -14528,7 +14634,7 @@ var RecordingController = class {
|
|
|
14528
14634
|
/**
|
|
14529
14635
|
* The ADAPTIVE attach bound. A transient `spawn ffmpeg` failure (EAGAIN) is
|
|
14530
14636
|
* the host saying it cannot fork right now, and the answer is to attach
|
|
14531
|
-
* FEWER cameras at once — not to serialise them, which
|
|
14637
|
+
* FEWER cameras at once — not to serialise them, which removed
|
|
14532
14638
|
* for good reason. Every bounded-parallel attach in this file reads it.
|
|
14533
14639
|
*/
|
|
14534
14640
|
spawnPressure;
|
|
@@ -16033,7 +16139,7 @@ function selectRedundantSegments(segs) {
|
|
|
16033
16139
|
* The pruner that used to remove it, `runRedundancyJanitor`, was reachable ONLY
|
|
16034
16140
|
* from `runFullArchiveWalk`, which was reachable only from
|
|
16035
16141
|
* `scheduleDeferredFullWalk`, which had no caller at all — so on 2026-08-27
|
|
16036
|
-
*
|
|
16142
|
+
* all three were deleted together as dead code. That was
|
|
16037
16143
|
* correct about the walk and left the system with **no duplicate pruner
|
|
16038
16144
|
* whatsoever**. The precedent for letting this class of waste run unattended is
|
|
16039
16145
|
* the 179 GB of stranded staging segments in `staging-reconcile.ts`.
|
|
@@ -16977,11 +17083,15 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
16977
17083
|
/**
|
|
16978
17084
|
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
16979
17085
|
*
|
|
16980
|
-
* REUSED,
|
|
16981
|
-
*
|
|
16982
|
-
*
|
|
16983
|
-
* that
|
|
16984
|
-
*
|
|
17086
|
+
* REUSED, never re-implemented. It used to live next door in this bundle;
|
|
17087
|
+
* on 2026-09-09 it moved to `@camstack/system/addon-utils` (the subpath, never
|
|
17088
|
+
* the barrel — that one drags the kernel into the runner) because two more
|
|
17089
|
+
* processes that do NOT share this bundle needed it — `pipeline-analytics` and
|
|
17090
|
+
* hub-main — and a per-bundle copy is how two diagnostics end up disagreeing
|
|
17091
|
+
* about the same freeze. The cost of the move is a slower ship chain: this
|
|
17092
|
+
* import is host-resolved, so a `camstack deploy` of this addon onto a hub
|
|
17093
|
+
* whose server closure predates the export fails to load. Deploy order is
|
|
17094
|
+
* closure first, addons second. See D417.
|
|
16985
17095
|
*/
|
|
16986
17096
|
stopStallMonitor = null;
|
|
16987
17097
|
/**
|
|
@@ -17440,6 +17550,7 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
17440
17550
|
refreshLocations: () => this.refreshLocations(),
|
|
17441
17551
|
capacity: (root) => this.locationCapacity(root),
|
|
17442
17552
|
archiveAccounting: (locationIds) => this.segmentHours?.accountingForLocations(locationIds) ?? Promise.resolve(EMPTY_ARCHIVE_ACCOUNTING),
|
|
17553
|
+
archiveDeviceUsage: (locationIds) => this.segmentHours?.usageByDevice(locationIds) ?? null,
|
|
17443
17554
|
archive: () => this.segmentHours,
|
|
17444
17555
|
ledgerWalk: this.ledgerWalkReconciler(),
|
|
17445
17556
|
hydrateDevice: (deviceId, locations) => hydrateDeviceFromStorage(this.ctx.api, this.index, deviceId, locations, this.ctx.logger),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { It as errMsg } from "./dist-
|
|
2
|
+
import { It as errMsg } from "./dist-DgeXZfwn.mjs";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
//#region ../../node_modules/mp4box/dist/mp4box.all.js
|
|
5
5
|
var require_mp4box_all = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-D7hxiBnC.js");
|
|
2
2
|
let node_child_process = require("node:child_process");
|
|
3
3
|
//#region ../../node_modules/mp4box/dist/mp4box.all.js
|
|
4
4
|
var require_mp4box_all = /* @__PURE__ */ require_dist.__commonJSMin(((exports) => {
|