@camstack/addon-pipeline 1.2.99 → 1.2.100
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/recorder/index.js +39 -7
- package/dist/recorder/index.mjs +39 -7
- package/package.json +1 -1
package/dist/recorder/index.js
CHANGED
|
@@ -1374,6 +1374,17 @@ function encodeDirectory(payload) {
|
|
|
1374
1374
|
}
|
|
1375
1375
|
//#endregion
|
|
1376
1376
|
//#region src/recorder/segment-store.ts
|
|
1377
|
+
/**
|
|
1378
|
+
* Below this a file cannot be a media segment: one fMP4 `moof` + `mdat`
|
|
1379
|
+
* carrying a single sample already exceeds this by an order of magnitude.
|
|
1380
|
+
* What it actually catches is the structural stub — ffmpeg opens the next
|
|
1381
|
+
* segment the instant it closes the previous one, so a killed writer always
|
|
1382
|
+
* leaves one just-opened file (observed: 28-byte bare `styp` boxes). The boot
|
|
1383
|
+
* recovery plans by NAME and would otherwise index it with a nominal duration,
|
|
1384
|
+
* painting the timeline over footage that decodes to nothing — the same
|
|
1385
|
+
* failure `MAX_GAP_FACTOR` in staging-reconcile exists to prevent via gaps.
|
|
1386
|
+
*/
|
|
1387
|
+
var MIN_INDEXABLE_SEGMENT_BYTES = 64;
|
|
1377
1388
|
var SegmentStore = class SegmentStore {
|
|
1378
1389
|
deps;
|
|
1379
1390
|
constructor(deps) {
|
|
@@ -1397,7 +1408,16 @@ var SegmentStore = class SegmentStore {
|
|
|
1397
1408
|
});
|
|
1398
1409
|
return null;
|
|
1399
1410
|
}
|
|
1400
|
-
if (bytes
|
|
1411
|
+
if (bytes < MIN_INDEXABLE_SEGMENT_BYTES) {
|
|
1412
|
+
this.deps.logger.warn("segment too small to index — left in staging", {
|
|
1413
|
+
tags: { deviceId: input.deviceId },
|
|
1414
|
+
meta: {
|
|
1415
|
+
bytes,
|
|
1416
|
+
path: input.flatAbsPath
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1401
1421
|
const relPath = segmentRelPath(input.deviceId, input.profile, input.startMs, input.durMs, bytes);
|
|
1402
1422
|
const toAbs = `${input.locationRoot}/${relPath}`;
|
|
1403
1423
|
try {
|
|
@@ -7946,12 +7966,22 @@ var RecordingController = class {
|
|
|
7946
7966
|
}
|
|
7947
7967
|
/** Stop one profile's writer + watcher and release its broker lease. */
|
|
7948
7968
|
async teardownProfile(deviceId, r) {
|
|
7969
|
+
const teardownStartMs = Date.now();
|
|
7949
7970
|
try {
|
|
7950
7971
|
await r.writer.stopAndWait();
|
|
7951
7972
|
} catch {}
|
|
7973
|
+
const writerStopMs = Date.now() - teardownStartMs;
|
|
7952
7974
|
try {
|
|
7953
7975
|
await r.watcher.flush?.();
|
|
7954
7976
|
} catch {}
|
|
7977
|
+
this.deps.logger.info("recorder: profile teardown drained", {
|
|
7978
|
+
tags: { deviceId },
|
|
7979
|
+
meta: {
|
|
7980
|
+
profile: r.profile,
|
|
7981
|
+
writerStopMs,
|
|
7982
|
+
flushMs: Date.now() - teardownStartMs - writerStopMs
|
|
7983
|
+
}
|
|
7984
|
+
});
|
|
7955
7985
|
try {
|
|
7956
7986
|
r.watcher.stop();
|
|
7957
7987
|
} catch {}
|
|
@@ -9306,6 +9336,7 @@ async function recoverAllStagedOrphans(deps) {
|
|
|
9306
9336
|
return entries;
|
|
9307
9337
|
};
|
|
9308
9338
|
const walkStartedMs = Date.now();
|
|
9339
|
+
const segmentSecondsMap = await deps.segmentSecondsByDevice();
|
|
9309
9340
|
const work = [];
|
|
9310
9341
|
for (const location of locations) {
|
|
9311
9342
|
const stagingRoot = `${location.root}/${STAGING_DIR_NAME}`;
|
|
@@ -9316,7 +9347,7 @@ async function recoverAllStagedOrphans(deps) {
|
|
|
9316
9347
|
if (!Number.isInteger(deviceId) || deviceId <= 0) continue;
|
|
9317
9348
|
const profileDirs = await listPaced(`${stagingRoot}/${deviceDir}`);
|
|
9318
9349
|
if (profileDirs === null) continue;
|
|
9319
|
-
const segmentSeconds =
|
|
9350
|
+
const segmentSeconds = segmentSecondsMap.get(deviceId) ?? deps.defaultSegmentSeconds;
|
|
9320
9351
|
for (const profile of profileDirs) {
|
|
9321
9352
|
if (!KNOWN_PROFILES.has(profile)) continue;
|
|
9322
9353
|
work.push({
|
|
@@ -10359,13 +10390,14 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
10359
10390
|
},
|
|
10360
10391
|
logger: this.ctx.logger,
|
|
10361
10392
|
bootMs,
|
|
10362
|
-
|
|
10393
|
+
segmentSecondsByDevice: async () => {
|
|
10394
|
+
const out = /* @__PURE__ */ new Map();
|
|
10363
10395
|
try {
|
|
10364
|
-
|
|
10365
|
-
} catch {
|
|
10366
|
-
|
|
10367
|
-
}
|
|
10396
|
+
for (const [id, config] of await readDeviceConfigs(this.configStore())) if (config.segmentSeconds !== void 0) out.set(id, config.segmentSeconds);
|
|
10397
|
+
} catch {}
|
|
10398
|
+
return out;
|
|
10368
10399
|
},
|
|
10400
|
+
defaultSegmentSeconds: this.config.segmentSeconds,
|
|
10369
10401
|
shouldStop: () => this.segmentStore === null
|
|
10370
10402
|
});
|
|
10371
10403
|
} catch (err) {
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -1373,6 +1373,17 @@ function encodeDirectory(payload) {
|
|
|
1373
1373
|
}
|
|
1374
1374
|
//#endregion
|
|
1375
1375
|
//#region src/recorder/segment-store.ts
|
|
1376
|
+
/**
|
|
1377
|
+
* Below this a file cannot be a media segment: one fMP4 `moof` + `mdat`
|
|
1378
|
+
* carrying a single sample already exceeds this by an order of magnitude.
|
|
1379
|
+
* What it actually catches is the structural stub — ffmpeg opens the next
|
|
1380
|
+
* segment the instant it closes the previous one, so a killed writer always
|
|
1381
|
+
* leaves one just-opened file (observed: 28-byte bare `styp` boxes). The boot
|
|
1382
|
+
* recovery plans by NAME and would otherwise index it with a nominal duration,
|
|
1383
|
+
* painting the timeline over footage that decodes to nothing — the same
|
|
1384
|
+
* failure `MAX_GAP_FACTOR` in staging-reconcile exists to prevent via gaps.
|
|
1385
|
+
*/
|
|
1386
|
+
var MIN_INDEXABLE_SEGMENT_BYTES = 64;
|
|
1376
1387
|
var SegmentStore = class SegmentStore {
|
|
1377
1388
|
deps;
|
|
1378
1389
|
constructor(deps) {
|
|
@@ -1396,7 +1407,16 @@ var SegmentStore = class SegmentStore {
|
|
|
1396
1407
|
});
|
|
1397
1408
|
return null;
|
|
1398
1409
|
}
|
|
1399
|
-
if (bytes
|
|
1410
|
+
if (bytes < MIN_INDEXABLE_SEGMENT_BYTES) {
|
|
1411
|
+
this.deps.logger.warn("segment too small to index — left in staging", {
|
|
1412
|
+
tags: { deviceId: input.deviceId },
|
|
1413
|
+
meta: {
|
|
1414
|
+
bytes,
|
|
1415
|
+
path: input.flatAbsPath
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1400
1420
|
const relPath = segmentRelPath(input.deviceId, input.profile, input.startMs, input.durMs, bytes);
|
|
1401
1421
|
const toAbs = `${input.locationRoot}/${relPath}`;
|
|
1402
1422
|
try {
|
|
@@ -7945,12 +7965,22 @@ var RecordingController = class {
|
|
|
7945
7965
|
}
|
|
7946
7966
|
/** Stop one profile's writer + watcher and release its broker lease. */
|
|
7947
7967
|
async teardownProfile(deviceId, r) {
|
|
7968
|
+
const teardownStartMs = Date.now();
|
|
7948
7969
|
try {
|
|
7949
7970
|
await r.writer.stopAndWait();
|
|
7950
7971
|
} catch {}
|
|
7972
|
+
const writerStopMs = Date.now() - teardownStartMs;
|
|
7951
7973
|
try {
|
|
7952
7974
|
await r.watcher.flush?.();
|
|
7953
7975
|
} catch {}
|
|
7976
|
+
this.deps.logger.info("recorder: profile teardown drained", {
|
|
7977
|
+
tags: { deviceId },
|
|
7978
|
+
meta: {
|
|
7979
|
+
profile: r.profile,
|
|
7980
|
+
writerStopMs,
|
|
7981
|
+
flushMs: Date.now() - teardownStartMs - writerStopMs
|
|
7982
|
+
}
|
|
7983
|
+
});
|
|
7954
7984
|
try {
|
|
7955
7985
|
r.watcher.stop();
|
|
7956
7986
|
} catch {}
|
|
@@ -9305,6 +9335,7 @@ async function recoverAllStagedOrphans(deps) {
|
|
|
9305
9335
|
return entries;
|
|
9306
9336
|
};
|
|
9307
9337
|
const walkStartedMs = Date.now();
|
|
9338
|
+
const segmentSecondsMap = await deps.segmentSecondsByDevice();
|
|
9308
9339
|
const work = [];
|
|
9309
9340
|
for (const location of locations) {
|
|
9310
9341
|
const stagingRoot = `${location.root}/${STAGING_DIR_NAME}`;
|
|
@@ -9315,7 +9346,7 @@ async function recoverAllStagedOrphans(deps) {
|
|
|
9315
9346
|
if (!Number.isInteger(deviceId) || deviceId <= 0) continue;
|
|
9316
9347
|
const profileDirs = await listPaced(`${stagingRoot}/${deviceDir}`);
|
|
9317
9348
|
if (profileDirs === null) continue;
|
|
9318
|
-
const segmentSeconds =
|
|
9349
|
+
const segmentSeconds = segmentSecondsMap.get(deviceId) ?? deps.defaultSegmentSeconds;
|
|
9319
9350
|
for (const profile of profileDirs) {
|
|
9320
9351
|
if (!KNOWN_PROFILES.has(profile)) continue;
|
|
9321
9352
|
work.push({
|
|
@@ -10358,13 +10389,14 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
10358
10389
|
},
|
|
10359
10390
|
logger: this.ctx.logger,
|
|
10360
10391
|
bootMs,
|
|
10361
|
-
|
|
10392
|
+
segmentSecondsByDevice: async () => {
|
|
10393
|
+
const out = /* @__PURE__ */ new Map();
|
|
10362
10394
|
try {
|
|
10363
|
-
|
|
10364
|
-
} catch {
|
|
10365
|
-
|
|
10366
|
-
}
|
|
10395
|
+
for (const [id, config] of await readDeviceConfigs(this.configStore())) if (config.segmentSeconds !== void 0) out.set(id, config.segmentSeconds);
|
|
10396
|
+
} catch {}
|
|
10397
|
+
return out;
|
|
10367
10398
|
},
|
|
10399
|
+
defaultSegmentSeconds: this.config.segmentSeconds,
|
|
10368
10400
|
shouldStop: () => this.segmentStore === null
|
|
10369
10401
|
});
|
|
10370
10402
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.100",
|
|
4
4
|
"description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|