@camstack/addon-pipeline 1.2.309 → 1.2.311
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/pipeline-runner/index.js +43 -2
- package/dist/pipeline-runner/index.mjs +43 -2
- package/dist/recorder/index.js +70 -3
- package/dist/recorder/index.mjs +70 -3
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-7H0Oxf23.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DZh9jQi-.mjs} +1 -1
- package/dist/stream-broker/{hostInit-DEY-Dqqo.mjs → hostInit-BJYQvQ3e.mjs} +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -3885,6 +3885,24 @@ function isParkedDetailFresh(parked, nowMs) {
|
|
|
3885
3885
|
function buildDetailNotAttachedError(deviceId) {
|
|
3886
3886
|
return /* @__PURE__ */ new Error(`${DETAIL_NOT_ATTACHED_MARKER} — camera ${deviceId} is not attached and no detail state is parked within the grace window`);
|
|
3887
3887
|
}
|
|
3888
|
+
/**
|
|
3889
|
+
* The wire marker for the typed handle-expired refusal (D688): the frame handle
|
|
3890
|
+
* a detail dispatch named is past the native lease age bound, so every later
|
|
3891
|
+
* ask for it is refused too. Twin constant in
|
|
3892
|
+
* `addon-post-analysis/src/pipeline-analytics/detail-refusal.ts` — keep in sync.
|
|
3893
|
+
* An error rather than a result field for the same reason as
|
|
3894
|
+
* {@link DETAIL_NOT_ATTACHED_MARKER}: the cap output schema strips unknown keys.
|
|
3895
|
+
*/
|
|
3896
|
+
var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
|
|
3897
|
+
/**
|
|
3898
|
+
* The error the cap method throws when the dispatch's only pixel source was a
|
|
3899
|
+
* frame handle the lease registry refused as expired. The dispatcher recognises
|
|
3900
|
+
* the marker, retires the handle for that track and falls back to its parked
|
|
3901
|
+
* frame — it never names the same handle again.
|
|
3902
|
+
*/
|
|
3903
|
+
function buildDetailHandleExpiredError(deviceId, handleAgeMs) {
|
|
3904
|
+
return /* @__PURE__ */ new Error(`${DETAIL_HANDLE_EXPIRED_MARKER} — camera ${deviceId}: the frame handle is past the native lease age bound (age ${handleAgeMs ?? "unknown"} ms)`);
|
|
3905
|
+
}
|
|
3888
3906
|
//#endregion
|
|
3889
3907
|
//#region src/pipeline-runner/face-parent-geometry.ts
|
|
3890
3908
|
/** Is the centre of `face` inside `parent` (edges inclusive)? */
|
|
@@ -3970,7 +3988,11 @@ var FaceOutsideParentLog = class {
|
|
|
3970
3988
|
//#region src/pipeline-runner/detail-subtree.ts
|
|
3971
3989
|
/** Did the subtree run refuse because the camera is not attached (park lapsed)? */
|
|
3972
3990
|
function isDetailNotAttachedRefusal(outcome) {
|
|
3973
|
-
return "refusal" in outcome;
|
|
3991
|
+
return "refusal" in outcome && outcome.refusal === "not-attached";
|
|
3992
|
+
}
|
|
3993
|
+
/** Did the subtree run refuse because the named frame handle is past its lease age bound? */
|
|
3994
|
+
function isDetailHandleExpiredRefusal(outcome) {
|
|
3995
|
+
return "refusal" in outcome && outcome.refusal === "handle-expired";
|
|
3974
3996
|
}
|
|
3975
3997
|
/** Did the native-lease fetch report a miss (rather than return pixels)? */
|
|
3976
3998
|
function isNativeCropMiss(fetch) {
|
|
@@ -4272,6 +4294,10 @@ async function runDetailSubtree(deps, input) {
|
|
|
4272
4294
|
nativeWorkerReason: nativeMiss?.workerReason ?? null
|
|
4273
4295
|
}
|
|
4274
4296
|
});
|
|
4297
|
+
if (nativeMiss?.miss === "handle-expired") return {
|
|
4298
|
+
refusal: "handle-expired",
|
|
4299
|
+
handleAgeMs: nativeMiss.handleAgeMs
|
|
4300
|
+
};
|
|
4275
4301
|
return null;
|
|
4276
4302
|
}
|
|
4277
4303
|
const nativeCropRef = input.frameHandle !== void 0 && crop.source === "native" ? {
|
|
@@ -5298,6 +5324,19 @@ async function resolveMotionFrameView(registry, ref) {
|
|
|
5298
5324
|
/** The step id (`PipelineStepInput.addonId`) the governor gates. */
|
|
5299
5325
|
var PACKAGE_DETECTION_STEP_ID = "package-detection";
|
|
5300
5326
|
/**
|
|
5327
|
+
* The executor's frame, stamped `packageLooked` when the `package-detection`
|
|
5328
|
+
* root was among the steps actually DISPATCHED for it (the governor let it
|
|
5329
|
+
* through) and the frame was not shed. Post-analysis reads it as "an empty
|
|
5330
|
+
* doormat on this frame is an ABSENCE, not silence" (D687). Never mutates.
|
|
5331
|
+
*/
|
|
5332
|
+
function withPackageLook(result, dispatched) {
|
|
5333
|
+
if (result.shed === true) return result;
|
|
5334
|
+
return dispatched.some((s) => s.addonId === "package-detection" && s.enabled !== false) ? {
|
|
5335
|
+
...result,
|
|
5336
|
+
packageLooked: true
|
|
5337
|
+
} : result;
|
|
5338
|
+
}
|
|
5339
|
+
/**
|
|
5301
5340
|
* Burst: 5 runs, 5s apart — 0s, 5s, 10s, 15s, 20s after the settle edge.
|
|
5302
5341
|
*
|
|
5303
5342
|
* Sized as a SAFETY NET when the operator raises `packageDropDwellSec` above
|
|
@@ -10058,6 +10097,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
10058
10097
|
recordFaceOutsideParent: (drop) => this.faceOutsideParentLog.record(drop)
|
|
10059
10098
|
}, input);
|
|
10060
10099
|
if (outcome !== null && isDetailNotAttachedRefusal(outcome)) throw buildDetailNotAttachedError(input.deviceId);
|
|
10100
|
+
if (outcome !== null && isDetailHandleExpiredRefusal(outcome)) throw buildDetailHandleExpiredError(input.deviceId, outcome.handleAgeMs);
|
|
10061
10101
|
return outcome;
|
|
10062
10102
|
}
|
|
10063
10103
|
/**
|
|
@@ -11385,7 +11425,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
11385
11425
|
return null;
|
|
11386
11426
|
}
|
|
11387
11427
|
if (result === null) return null;
|
|
11388
|
-
|
|
11428
|
+
const looked = withPackageLook(result, dispatchSteps);
|
|
11429
|
+
return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
|
|
11389
11430
|
}
|
|
11390
11431
|
/**
|
|
11391
11432
|
* The second detector pass at native resolution — `small-subject-recovery.ts`
|
|
@@ -3877,6 +3877,24 @@ function isParkedDetailFresh(parked, nowMs) {
|
|
|
3877
3877
|
function buildDetailNotAttachedError(deviceId) {
|
|
3878
3878
|
return /* @__PURE__ */ new Error(`${DETAIL_NOT_ATTACHED_MARKER} — camera ${deviceId} is not attached and no detail state is parked within the grace window`);
|
|
3879
3879
|
}
|
|
3880
|
+
/**
|
|
3881
|
+
* The wire marker for the typed handle-expired refusal (D688): the frame handle
|
|
3882
|
+
* a detail dispatch named is past the native lease age bound, so every later
|
|
3883
|
+
* ask for it is refused too. Twin constant in
|
|
3884
|
+
* `addon-post-analysis/src/pipeline-analytics/detail-refusal.ts` — keep in sync.
|
|
3885
|
+
* An error rather than a result field for the same reason as
|
|
3886
|
+
* {@link DETAIL_NOT_ATTACHED_MARKER}: the cap output schema strips unknown keys.
|
|
3887
|
+
*/
|
|
3888
|
+
var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
|
|
3889
|
+
/**
|
|
3890
|
+
* The error the cap method throws when the dispatch's only pixel source was a
|
|
3891
|
+
* frame handle the lease registry refused as expired. The dispatcher recognises
|
|
3892
|
+
* the marker, retires the handle for that track and falls back to its parked
|
|
3893
|
+
* frame — it never names the same handle again.
|
|
3894
|
+
*/
|
|
3895
|
+
function buildDetailHandleExpiredError(deviceId, handleAgeMs) {
|
|
3896
|
+
return /* @__PURE__ */ new Error(`${DETAIL_HANDLE_EXPIRED_MARKER} — camera ${deviceId}: the frame handle is past the native lease age bound (age ${handleAgeMs ?? "unknown"} ms)`);
|
|
3897
|
+
}
|
|
3880
3898
|
//#endregion
|
|
3881
3899
|
//#region src/pipeline-runner/face-parent-geometry.ts
|
|
3882
3900
|
/** Is the centre of `face` inside `parent` (edges inclusive)? */
|
|
@@ -3962,7 +3980,11 @@ var FaceOutsideParentLog = class {
|
|
|
3962
3980
|
//#region src/pipeline-runner/detail-subtree.ts
|
|
3963
3981
|
/** Did the subtree run refuse because the camera is not attached (park lapsed)? */
|
|
3964
3982
|
function isDetailNotAttachedRefusal(outcome) {
|
|
3965
|
-
return "refusal" in outcome;
|
|
3983
|
+
return "refusal" in outcome && outcome.refusal === "not-attached";
|
|
3984
|
+
}
|
|
3985
|
+
/** Did the subtree run refuse because the named frame handle is past its lease age bound? */
|
|
3986
|
+
function isDetailHandleExpiredRefusal(outcome) {
|
|
3987
|
+
return "refusal" in outcome && outcome.refusal === "handle-expired";
|
|
3966
3988
|
}
|
|
3967
3989
|
/** Did the native-lease fetch report a miss (rather than return pixels)? */
|
|
3968
3990
|
function isNativeCropMiss(fetch) {
|
|
@@ -4264,6 +4286,10 @@ async function runDetailSubtree(deps, input) {
|
|
|
4264
4286
|
nativeWorkerReason: nativeMiss?.workerReason ?? null
|
|
4265
4287
|
}
|
|
4266
4288
|
});
|
|
4289
|
+
if (nativeMiss?.miss === "handle-expired") return {
|
|
4290
|
+
refusal: "handle-expired",
|
|
4291
|
+
handleAgeMs: nativeMiss.handleAgeMs
|
|
4292
|
+
};
|
|
4267
4293
|
return null;
|
|
4268
4294
|
}
|
|
4269
4295
|
const nativeCropRef = input.frameHandle !== void 0 && crop.source === "native" ? {
|
|
@@ -5290,6 +5316,19 @@ async function resolveMotionFrameView(registry, ref) {
|
|
|
5290
5316
|
/** The step id (`PipelineStepInput.addonId`) the governor gates. */
|
|
5291
5317
|
var PACKAGE_DETECTION_STEP_ID = "package-detection";
|
|
5292
5318
|
/**
|
|
5319
|
+
* The executor's frame, stamped `packageLooked` when the `package-detection`
|
|
5320
|
+
* root was among the steps actually DISPATCHED for it (the governor let it
|
|
5321
|
+
* through) and the frame was not shed. Post-analysis reads it as "an empty
|
|
5322
|
+
* doormat on this frame is an ABSENCE, not silence" (D687). Never mutates.
|
|
5323
|
+
*/
|
|
5324
|
+
function withPackageLook(result, dispatched) {
|
|
5325
|
+
if (result.shed === true) return result;
|
|
5326
|
+
return dispatched.some((s) => s.addonId === "package-detection" && s.enabled !== false) ? {
|
|
5327
|
+
...result,
|
|
5328
|
+
packageLooked: true
|
|
5329
|
+
} : result;
|
|
5330
|
+
}
|
|
5331
|
+
/**
|
|
5293
5332
|
* Burst: 5 runs, 5s apart — 0s, 5s, 10s, 15s, 20s after the settle edge.
|
|
5294
5333
|
*
|
|
5295
5334
|
* Sized as a SAFETY NET when the operator raises `packageDropDwellSec` above
|
|
@@ -10050,6 +10089,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
10050
10089
|
recordFaceOutsideParent: (drop) => this.faceOutsideParentLog.record(drop)
|
|
10051
10090
|
}, input);
|
|
10052
10091
|
if (outcome !== null && isDetailNotAttachedRefusal(outcome)) throw buildDetailNotAttachedError(input.deviceId);
|
|
10092
|
+
if (outcome !== null && isDetailHandleExpiredRefusal(outcome)) throw buildDetailHandleExpiredError(input.deviceId, outcome.handleAgeMs);
|
|
10053
10093
|
return outcome;
|
|
10054
10094
|
}
|
|
10055
10095
|
/**
|
|
@@ -11377,7 +11417,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
11377
11417
|
return null;
|
|
11378
11418
|
}
|
|
11379
11419
|
if (result === null) return null;
|
|
11380
|
-
|
|
11420
|
+
const looked = withPackageLook(result, dispatchSteps);
|
|
11421
|
+
return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
|
|
11381
11422
|
}
|
|
11382
11423
|
/**
|
|
11383
11424
|
* The second detector pass at native resolution — `small-subject-recovery.ts`
|
package/dist/recorder/index.js
CHANGED
|
@@ -6777,6 +6777,30 @@ async function patchExport(state, id, patch) {
|
|
|
6777
6777
|
return merged;
|
|
6778
6778
|
});
|
|
6779
6779
|
}
|
|
6780
|
+
/**
|
|
6781
|
+
* Patch a row UNLESS its current state says not to — the check and the write
|
|
6782
|
+
* are ONE serialized step, so a delete that lands between a separate read and
|
|
6783
|
+
* a separate patch can no longer be overwritten (review R5).
|
|
6784
|
+
*/
|
|
6785
|
+
async function patchExportUnless(state, id, patch, refuse) {
|
|
6786
|
+
return serialize(state, id, async () => {
|
|
6787
|
+
const existing = await state.read(id);
|
|
6788
|
+
if (existing === null) return { kind: "missing" };
|
|
6789
|
+
if (refuse(existing)) return {
|
|
6790
|
+
kind: "refused",
|
|
6791
|
+
record: existing
|
|
6792
|
+
};
|
|
6793
|
+
const merged = require_dist.ExportRecordSchema.parse({
|
|
6794
|
+
...existing,
|
|
6795
|
+
...patch
|
|
6796
|
+
});
|
|
6797
|
+
await state.write(id, merged);
|
|
6798
|
+
return {
|
|
6799
|
+
kind: "patched",
|
|
6800
|
+
record: merged
|
|
6801
|
+
};
|
|
6802
|
+
});
|
|
6803
|
+
}
|
|
6780
6804
|
/** Read one export row by id — ONE keyed row read. */
|
|
6781
6805
|
async function getExportRecord(state, id) {
|
|
6782
6806
|
return state.read(id);
|
|
@@ -6996,6 +7020,13 @@ function expectedOutSeconds(rec) {
|
|
|
6996
7020
|
}
|
|
6997
7021
|
return rangeSec / (rec.options.speed ?? 1);
|
|
6998
7022
|
}
|
|
7023
|
+
/**
|
|
7024
|
+
* A row nobody will read: its caller deleted it, it expired, or it already
|
|
7025
|
+
* failed (a cancel finalises as `failed`). The engine renders none of these.
|
|
7026
|
+
*/
|
|
7027
|
+
function isAbandonedExportState(state) {
|
|
7028
|
+
return state === "deleted" || state === "expired" || state === "failed";
|
|
7029
|
+
}
|
|
6999
7030
|
var ExportEngine = class {
|
|
7000
7031
|
deps;
|
|
7001
7032
|
queue = [];
|
|
@@ -7056,16 +7087,37 @@ var ExportEngine = class {
|
|
|
7056
7087
|
async render(id) {
|
|
7057
7088
|
const existing = await getExportRecord(this.deps.state, id);
|
|
7058
7089
|
if (!existing) return;
|
|
7090
|
+
if (isAbandonedExportState(existing.state)) {
|
|
7091
|
+
this.deps.logger.info("export render skipped: the export was abandoned before it ran", {
|
|
7092
|
+
tags: { deviceId: existing.deviceId },
|
|
7093
|
+
meta: {
|
|
7094
|
+
exportId: id,
|
|
7095
|
+
state: existing.state
|
|
7096
|
+
}
|
|
7097
|
+
});
|
|
7098
|
+
return;
|
|
7099
|
+
}
|
|
7059
7100
|
const subject = require_dist.exportSubjectOf(existing);
|
|
7060
7101
|
if (subject.kind === "clip" && subject.source !== ANALYTICS_CLIP_SOURCE) {
|
|
7061
7102
|
await this.fetchClip(id, existing, subject);
|
|
7062
7103
|
return;
|
|
7063
7104
|
}
|
|
7064
|
-
const
|
|
7105
|
+
const claimed = await patchExportUnless(this.deps.state, id, {
|
|
7065
7106
|
state: "rendering",
|
|
7066
7107
|
progressPct: 0
|
|
7067
|
-
});
|
|
7068
|
-
if (
|
|
7108
|
+
}, (current) => isAbandonedExportState(current.state));
|
|
7109
|
+
if (claimed.kind === "missing") return;
|
|
7110
|
+
if (claimed.kind === "refused") {
|
|
7111
|
+
this.deps.logger.info("export render skipped: the export was abandoned before it ran", {
|
|
7112
|
+
tags: { deviceId: claimed.record.deviceId },
|
|
7113
|
+
meta: {
|
|
7114
|
+
exportId: id,
|
|
7115
|
+
state: claimed.record.state
|
|
7116
|
+
}
|
|
7117
|
+
});
|
|
7118
|
+
return;
|
|
7119
|
+
}
|
|
7120
|
+
const rec = claimed.record;
|
|
7069
7121
|
const playlist = await this.deps.resolvePlaylist(rec);
|
|
7070
7122
|
if (!playlist) {
|
|
7071
7123
|
await this.fail(id, "no footage for range");
|
|
@@ -7699,6 +7751,19 @@ var ExportEngine = class {
|
|
|
7699
7751
|
await this.fail(id, `render produced no video frames (${bytes} bytes, < ${EXPORT_MIN_PLAUSIBLE_BYTES})`);
|
|
7700
7752
|
return;
|
|
7701
7753
|
}
|
|
7754
|
+
const current = await getExportRecord(this.deps.state, id);
|
|
7755
|
+
if (current !== null && isAbandonedExportState(current.state)) {
|
|
7756
|
+
await this.deps.removeFile(outPath);
|
|
7757
|
+
this.deps.logger.info("export render discarded: the export was abandoned while it ran", {
|
|
7758
|
+
tags: { deviceId: current.deviceId },
|
|
7759
|
+
meta: {
|
|
7760
|
+
exportId: id,
|
|
7761
|
+
state: current.state,
|
|
7762
|
+
bytes
|
|
7763
|
+
}
|
|
7764
|
+
});
|
|
7765
|
+
return;
|
|
7766
|
+
}
|
|
7702
7767
|
await patchExport(this.deps.state, id, {
|
|
7703
7768
|
state: "ready",
|
|
7704
7769
|
progressPct: 100,
|
|
@@ -7726,6 +7791,8 @@ var ExportEngine = class {
|
|
|
7726
7791
|
exportId: id,
|
|
7727
7792
|
error
|
|
7728
7793
|
} });
|
|
7794
|
+
const current = await getExportRecord(this.deps.state, id);
|
|
7795
|
+
if (current !== null && current.state === "deleted") return;
|
|
7729
7796
|
await patchExport(this.deps.state, id, {
|
|
7730
7797
|
state: "failed",
|
|
7731
7798
|
progressPct: null,
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -6769,6 +6769,30 @@ async function patchExport(state, id, patch) {
|
|
|
6769
6769
|
return merged;
|
|
6770
6770
|
});
|
|
6771
6771
|
}
|
|
6772
|
+
/**
|
|
6773
|
+
* Patch a row UNLESS its current state says not to — the check and the write
|
|
6774
|
+
* are ONE serialized step, so a delete that lands between a separate read and
|
|
6775
|
+
* a separate patch can no longer be overwritten (review R5).
|
|
6776
|
+
*/
|
|
6777
|
+
async function patchExportUnless(state, id, patch, refuse) {
|
|
6778
|
+
return serialize(state, id, async () => {
|
|
6779
|
+
const existing = await state.read(id);
|
|
6780
|
+
if (existing === null) return { kind: "missing" };
|
|
6781
|
+
if (refuse(existing)) return {
|
|
6782
|
+
kind: "refused",
|
|
6783
|
+
record: existing
|
|
6784
|
+
};
|
|
6785
|
+
const merged = ExportRecordSchema.parse({
|
|
6786
|
+
...existing,
|
|
6787
|
+
...patch
|
|
6788
|
+
});
|
|
6789
|
+
await state.write(id, merged);
|
|
6790
|
+
return {
|
|
6791
|
+
kind: "patched",
|
|
6792
|
+
record: merged
|
|
6793
|
+
};
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
6772
6796
|
/** Read one export row by id — ONE keyed row read. */
|
|
6773
6797
|
async function getExportRecord(state, id) {
|
|
6774
6798
|
return state.read(id);
|
|
@@ -6988,6 +7012,13 @@ function expectedOutSeconds(rec) {
|
|
|
6988
7012
|
}
|
|
6989
7013
|
return rangeSec / (rec.options.speed ?? 1);
|
|
6990
7014
|
}
|
|
7015
|
+
/**
|
|
7016
|
+
* A row nobody will read: its caller deleted it, it expired, or it already
|
|
7017
|
+
* failed (a cancel finalises as `failed`). The engine renders none of these.
|
|
7018
|
+
*/
|
|
7019
|
+
function isAbandonedExportState(state) {
|
|
7020
|
+
return state === "deleted" || state === "expired" || state === "failed";
|
|
7021
|
+
}
|
|
6991
7022
|
var ExportEngine = class {
|
|
6992
7023
|
deps;
|
|
6993
7024
|
queue = [];
|
|
@@ -7048,16 +7079,37 @@ var ExportEngine = class {
|
|
|
7048
7079
|
async render(id) {
|
|
7049
7080
|
const existing = await getExportRecord(this.deps.state, id);
|
|
7050
7081
|
if (!existing) return;
|
|
7082
|
+
if (isAbandonedExportState(existing.state)) {
|
|
7083
|
+
this.deps.logger.info("export render skipped: the export was abandoned before it ran", {
|
|
7084
|
+
tags: { deviceId: existing.deviceId },
|
|
7085
|
+
meta: {
|
|
7086
|
+
exportId: id,
|
|
7087
|
+
state: existing.state
|
|
7088
|
+
}
|
|
7089
|
+
});
|
|
7090
|
+
return;
|
|
7091
|
+
}
|
|
7051
7092
|
const subject = exportSubjectOf(existing);
|
|
7052
7093
|
if (subject.kind === "clip" && subject.source !== ANALYTICS_CLIP_SOURCE) {
|
|
7053
7094
|
await this.fetchClip(id, existing, subject);
|
|
7054
7095
|
return;
|
|
7055
7096
|
}
|
|
7056
|
-
const
|
|
7097
|
+
const claimed = await patchExportUnless(this.deps.state, id, {
|
|
7057
7098
|
state: "rendering",
|
|
7058
7099
|
progressPct: 0
|
|
7059
|
-
});
|
|
7060
|
-
if (
|
|
7100
|
+
}, (current) => isAbandonedExportState(current.state));
|
|
7101
|
+
if (claimed.kind === "missing") return;
|
|
7102
|
+
if (claimed.kind === "refused") {
|
|
7103
|
+
this.deps.logger.info("export render skipped: the export was abandoned before it ran", {
|
|
7104
|
+
tags: { deviceId: claimed.record.deviceId },
|
|
7105
|
+
meta: {
|
|
7106
|
+
exportId: id,
|
|
7107
|
+
state: claimed.record.state
|
|
7108
|
+
}
|
|
7109
|
+
});
|
|
7110
|
+
return;
|
|
7111
|
+
}
|
|
7112
|
+
const rec = claimed.record;
|
|
7061
7113
|
const playlist = await this.deps.resolvePlaylist(rec);
|
|
7062
7114
|
if (!playlist) {
|
|
7063
7115
|
await this.fail(id, "no footage for range");
|
|
@@ -7691,6 +7743,19 @@ var ExportEngine = class {
|
|
|
7691
7743
|
await this.fail(id, `render produced no video frames (${bytes} bytes, < ${EXPORT_MIN_PLAUSIBLE_BYTES})`);
|
|
7692
7744
|
return;
|
|
7693
7745
|
}
|
|
7746
|
+
const current = await getExportRecord(this.deps.state, id);
|
|
7747
|
+
if (current !== null && isAbandonedExportState(current.state)) {
|
|
7748
|
+
await this.deps.removeFile(outPath);
|
|
7749
|
+
this.deps.logger.info("export render discarded: the export was abandoned while it ran", {
|
|
7750
|
+
tags: { deviceId: current.deviceId },
|
|
7751
|
+
meta: {
|
|
7752
|
+
exportId: id,
|
|
7753
|
+
state: current.state,
|
|
7754
|
+
bytes
|
|
7755
|
+
}
|
|
7756
|
+
});
|
|
7757
|
+
return;
|
|
7758
|
+
}
|
|
7694
7759
|
await patchExport(this.deps.state, id, {
|
|
7695
7760
|
state: "ready",
|
|
7696
7761
|
progressPct: 100,
|
|
@@ -7718,6 +7783,8 @@ var ExportEngine = class {
|
|
|
7718
7783
|
exportId: id,
|
|
7719
7784
|
error
|
|
7720
7785
|
} });
|
|
7786
|
+
const current = await getExportRecord(this.deps.state, id);
|
|
7787
|
+
if (current !== null && current.state === "deleted") return;
|
|
7721
7788
|
await patchExport(this.deps.state, id, {
|
|
7722
7789
|
state: "failed",
|
|
7723
7790
|
progressPct: null,
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DZh9jQi-.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.311",
|
|
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",
|