@camstack/addon-pipeline 1.1.59 → 1.1.60
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 +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +41 -3
- package/dist/detection-pipeline/index.mjs +41 -3
- package/dist/{dist-DKeNH_5z.mjs → dist-CcqqznPf.mjs} +68 -2
- package/dist/{dist-B1u-QN05.js → dist-DynD1EC-.js} +73 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +4 -4
- package/dist/pipeline-runner/index.mjs +4 -4
- package/dist/recorder/index.js +48 -20
- package/dist/recorder/index.mjs +48 -20
- package/dist/{sheet-geometry-BN80zpCa.js → sheet-geometry-BR-ip0CP.js} +2 -2
- package/dist/{sheet-geometry-Bk3fR4s7.mjs → sheet-geometry-DcpuGTCm.mjs} +2 -2
- package/dist/{step-definitions-DUB27V8Y.mjs → step-definitions-CvwB_Otk.mjs} +1 -1
- package/dist/{step-definitions-CPch37vh.js → step-definitions-DhhwklAL.js} +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DdkfKDgw.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B07QYb7Q.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DYQ6RHcb.mjs +26 -0
- package/dist/stream-broker/{hostInit-BLn2Pv8Q.mjs → hostInit-C02YjaoP.mjs} +3 -3
- package/dist/stream-broker/index.js +1 -1
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-B0_nx15B.js → MaskShapeCanvas-DI4BY7W2-ColkZGA1.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-B8uM-VeO.js → MotionZonesSettings-NcxxQN8r-BIzNJmu0.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DMOs4Hj9.js → PrivacyMaskSettings-APgPLF7p-BHuYOg7Q.js} +1 -1
- package/embed-dist/assets/{index-W9-H5LTj.js → index-n1_ZSmy0.js} +10 -10
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D3h_W9uu.mjs +0 -26
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-DynD1EC-.js");
|
|
7
7
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
8
8
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Cp_GTKTw.js");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { C as mapAudioLabelToMacro,
|
|
2
|
+
import { C as mapAudioLabelToMacro, L as errMsg, R as BaseAddon, U as hydrateSchema, h as audioAnalyzerCapability, l as HF_BASE_URL, m as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG } from "../dist-CcqqznPf.mjs";
|
|
3
3
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
4
4
|
import { r as downloadFile } from "../model-download-service-Cp9f4dk6-0wh7OK3s.mjs";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_step_definitions = require("../step-definitions-
|
|
6
|
+
const require_dist = require("../dist-DynD1EC-.js");
|
|
7
|
+
const require_step_definitions = require("../step-definitions-DhhwklAL.js");
|
|
8
8
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
9
9
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Cp_GTKTw.js");
|
|
10
10
|
let node_child_process = require("node:child_process");
|
|
@@ -3690,6 +3690,15 @@ var ONNX_FLOOR = {
|
|
|
3690
3690
|
*/
|
|
3691
3691
|
var PROBE_READY_TIMEOUT_MS = 12e4;
|
|
3692
3692
|
/**
|
|
3693
|
+
* Bound for the direct `platformProbe.getCapabilities` reconcile RPC issued
|
|
3694
|
+
* when the readiness gate times out (D8: readiness events are telemetry —
|
|
3695
|
+
* lossy — so a timed-out registry wait is reconciled against the provider
|
|
3696
|
+
* itself before conceding to the conservative floor). `getCapabilities`
|
|
3697
|
+
* resolves only once the probe is complete, so an answer IS readiness; the
|
|
3698
|
+
* race bound keeps a wedged transport from wedging `setApi`.
|
|
3699
|
+
*/
|
|
3700
|
+
var PROBE_DIRECT_RPC_TIMEOUT_MS = 6e4;
|
|
3701
|
+
/**
|
|
3693
3702
|
* Build the onnx-cpu floor pick using `pickBestRuntime` with a null hardware
|
|
3694
3703
|
* env. Used wherever the old `detectBestEngine()` sync probe fell back — the
|
|
3695
3704
|
* result is identical (onnx / cpu) but is now derived through the shared rules
|
|
@@ -3922,7 +3931,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3922
3931
|
nodeId
|
|
3923
3932
|
}, { timeoutMs: PROBE_READY_TIMEOUT_MS });
|
|
3924
3933
|
} catch (err) {
|
|
3925
|
-
this.log.
|
|
3934
|
+
if (await this.probePlatformReadinessDirect()) this.log.info("platform-probe readiness reconciled via direct probe RPC (ready event lost)", { meta: {
|
|
3935
|
+
nodeId,
|
|
3936
|
+
timeoutMs: PROBE_READY_TIMEOUT_MS
|
|
3937
|
+
} });
|
|
3938
|
+
else this.log.warn("platform-probe readiness gate timed out — conservative engine pick (floor is not persisted)", { meta: {
|
|
3926
3939
|
nodeId,
|
|
3927
3940
|
timeoutMs: PROBE_READY_TIMEOUT_MS,
|
|
3928
3941
|
error: require_dist.errMsg(err)
|
|
@@ -3933,6 +3946,31 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3933
3946
|
this.startProvisioningForCurrentEngine();
|
|
3934
3947
|
}
|
|
3935
3948
|
/**
|
|
3949
|
+
* Authoritative platform-probe reconcile: query the probe provider
|
|
3950
|
+
* DIRECTLY over the cap plane. The RPC resolves only once the async
|
|
3951
|
+
* probe has completed (`getCapabilities` awaits the probe promise), so
|
|
3952
|
+
* a successful answer proves readiness independent of the (lossy)
|
|
3953
|
+
* `system.ready-state` event stream. Mirrors the local-node branch of
|
|
3954
|
+
* `resolveProbeHardware` — an unpinned `getCapabilities` routes to this
|
|
3955
|
+
* node's own in-process provider (agent parent / hub). Bounded by
|
|
3956
|
+
* {@link PROBE_DIRECT_RPC_TIMEOUT_MS}; `false` on any failure.
|
|
3957
|
+
*/
|
|
3958
|
+
async probePlatformReadinessDirect() {
|
|
3959
|
+
const api = this.addonCtx?.api;
|
|
3960
|
+
if (!api) return false;
|
|
3961
|
+
let timer;
|
|
3962
|
+
try {
|
|
3963
|
+
const bound = new Promise((resolve) => {
|
|
3964
|
+
timer = setTimeout(() => resolve(null), PROBE_DIRECT_RPC_TIMEOUT_MS);
|
|
3965
|
+
});
|
|
3966
|
+
return await Promise.race([api.platformProbe.getCapabilities.query(), bound]) !== null;
|
|
3967
|
+
} catch {
|
|
3968
|
+
return false;
|
|
3969
|
+
} finally {
|
|
3970
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3936
3974
|
* Resolve the `'auto'` engine LIVE from this node's own kernel inference
|
|
3937
3975
|
* resolver (`ctx.kernel.inferenceEngine`) — the runtime mirror of
|
|
3938
3976
|
* `ctx.kernel.hwaccel`. Each node (hub + forked worker + remote agent)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { D as nodePin,
|
|
3
|
-
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-
|
|
2
|
+
import { D as nodePin, F as supportedRuntimes$1, H as createEvent, L as errMsg, M as runtimeDevices$1, O as pipelineExecutorCapability, R as BaseAddon, U as hydrateSchema, X as sleep, b as detectionPipelineCapability, ct as EventCategory, f as YAMNET_TO_MACRO, q as parseJsonUnknown, t as APPLE_SA_TO_MACRO, v as defaultDeviceFor$1, x as evaluateZoneRules } from "../dist-CcqqznPf.mjs";
|
|
3
|
+
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-CvwB_Otk.mjs";
|
|
4
4
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
5
5
|
import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-0wh7OK3s.mjs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -3682,6 +3682,15 @@ var ONNX_FLOOR = {
|
|
|
3682
3682
|
*/
|
|
3683
3683
|
var PROBE_READY_TIMEOUT_MS = 12e4;
|
|
3684
3684
|
/**
|
|
3685
|
+
* Bound for the direct `platformProbe.getCapabilities` reconcile RPC issued
|
|
3686
|
+
* when the readiness gate times out (D8: readiness events are telemetry —
|
|
3687
|
+
* lossy — so a timed-out registry wait is reconciled against the provider
|
|
3688
|
+
* itself before conceding to the conservative floor). `getCapabilities`
|
|
3689
|
+
* resolves only once the probe is complete, so an answer IS readiness; the
|
|
3690
|
+
* race bound keeps a wedged transport from wedging `setApi`.
|
|
3691
|
+
*/
|
|
3692
|
+
var PROBE_DIRECT_RPC_TIMEOUT_MS = 6e4;
|
|
3693
|
+
/**
|
|
3685
3694
|
* Build the onnx-cpu floor pick using `pickBestRuntime` with a null hardware
|
|
3686
3695
|
* env. Used wherever the old `detectBestEngine()` sync probe fell back — the
|
|
3687
3696
|
* result is identical (onnx / cpu) but is now derived through the shared rules
|
|
@@ -3914,7 +3923,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3914
3923
|
nodeId
|
|
3915
3924
|
}, { timeoutMs: PROBE_READY_TIMEOUT_MS });
|
|
3916
3925
|
} catch (err) {
|
|
3917
|
-
this.log.
|
|
3926
|
+
if (await this.probePlatformReadinessDirect()) this.log.info("platform-probe readiness reconciled via direct probe RPC (ready event lost)", { meta: {
|
|
3927
|
+
nodeId,
|
|
3928
|
+
timeoutMs: PROBE_READY_TIMEOUT_MS
|
|
3929
|
+
} });
|
|
3930
|
+
else this.log.warn("platform-probe readiness gate timed out — conservative engine pick (floor is not persisted)", { meta: {
|
|
3918
3931
|
nodeId,
|
|
3919
3932
|
timeoutMs: PROBE_READY_TIMEOUT_MS,
|
|
3920
3933
|
error: errMsg(err)
|
|
@@ -3925,6 +3938,31 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3925
3938
|
this.startProvisioningForCurrentEngine();
|
|
3926
3939
|
}
|
|
3927
3940
|
/**
|
|
3941
|
+
* Authoritative platform-probe reconcile: query the probe provider
|
|
3942
|
+
* DIRECTLY over the cap plane. The RPC resolves only once the async
|
|
3943
|
+
* probe has completed (`getCapabilities` awaits the probe promise), so
|
|
3944
|
+
* a successful answer proves readiness independent of the (lossy)
|
|
3945
|
+
* `system.ready-state` event stream. Mirrors the local-node branch of
|
|
3946
|
+
* `resolveProbeHardware` — an unpinned `getCapabilities` routes to this
|
|
3947
|
+
* node's own in-process provider (agent parent / hub). Bounded by
|
|
3948
|
+
* {@link PROBE_DIRECT_RPC_TIMEOUT_MS}; `false` on any failure.
|
|
3949
|
+
*/
|
|
3950
|
+
async probePlatformReadinessDirect() {
|
|
3951
|
+
const api = this.addonCtx?.api;
|
|
3952
|
+
if (!api) return false;
|
|
3953
|
+
let timer;
|
|
3954
|
+
try {
|
|
3955
|
+
const bound = new Promise((resolve) => {
|
|
3956
|
+
timer = setTimeout(() => resolve(null), PROBE_DIRECT_RPC_TIMEOUT_MS);
|
|
3957
|
+
});
|
|
3958
|
+
return await Promise.race([api.platformProbe.getCapabilities.query(), bound]) !== null;
|
|
3959
|
+
} catch {
|
|
3960
|
+
return false;
|
|
3961
|
+
} finally {
|
|
3962
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3928
3966
|
* Resolve the `'auto'` engine LIVE from this node's own kernel inference
|
|
3929
3967
|
* resolver (`ctx.kernel.inferenceEngine`) — the runtime mirror of
|
|
3930
3968
|
* `ctx.kernel.hwaccel`. Each node (hub + forked worker + remote agent)
|
|
@@ -7379,6 +7379,65 @@ var RecordingRetentionSchema = object({
|
|
|
7379
7379
|
maxSizeGb: number().min(0).optional()
|
|
7380
7380
|
});
|
|
7381
7381
|
/**
|
|
7382
|
+
* Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
|
|
7383
|
+
* sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
|
|
7384
|
+
* previews at. Five graduated steps; absent on a config = `standard` (the
|
|
7385
|
+
* shipped default, matching `sheet-geometry`/`sheet-composer`).
|
|
7386
|
+
*
|
|
7387
|
+
* Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
|
|
7388
|
+
* Each window's index sidecar carries its own tile dims, so a camera whose
|
|
7389
|
+
* preset changed over time renders every historical window at the dims it was
|
|
7390
|
+
* written with.
|
|
7391
|
+
*/
|
|
7392
|
+
var ScrubThumbnailPresetSchema = _enum([
|
|
7393
|
+
"minimal",
|
|
7394
|
+
"low",
|
|
7395
|
+
"standard",
|
|
7396
|
+
"high",
|
|
7397
|
+
"max"
|
|
7398
|
+
]);
|
|
7399
|
+
/**
|
|
7400
|
+
* Preset → tile geometry + JPEG quality. Every step is 16:9; the graduated
|
|
7401
|
+
* ladder trades sheet size (and remote fetch cost) for scrub crispness.
|
|
7402
|
+
* `standard` matches the shipped `sheet-geometry`/`sheet-composer` defaults
|
|
7403
|
+
* (480×270 q72), so an unset config is byte-identical to today. The map is
|
|
7404
|
+
* strictly monotone in resolution AND quality across the ordered steps.
|
|
7405
|
+
*/
|
|
7406
|
+
var SCRUB_THUMBNAIL_PRESETS = {
|
|
7407
|
+
minimal: {
|
|
7408
|
+
tileWidth: 240,
|
|
7409
|
+
tileHeight: 135,
|
|
7410
|
+
quality: 55
|
|
7411
|
+
},
|
|
7412
|
+
low: {
|
|
7413
|
+
tileWidth: 320,
|
|
7414
|
+
tileHeight: 180,
|
|
7415
|
+
quality: 60
|
|
7416
|
+
},
|
|
7417
|
+
standard: {
|
|
7418
|
+
tileWidth: 480,
|
|
7419
|
+
tileHeight: 270,
|
|
7420
|
+
quality: 72
|
|
7421
|
+
},
|
|
7422
|
+
high: {
|
|
7423
|
+
tileWidth: 640,
|
|
7424
|
+
tileHeight: 360,
|
|
7425
|
+
quality: 80
|
|
7426
|
+
},
|
|
7427
|
+
max: {
|
|
7428
|
+
tileWidth: 960,
|
|
7429
|
+
tileHeight: 540,
|
|
7430
|
+
quality: 85
|
|
7431
|
+
}
|
|
7432
|
+
};
|
|
7433
|
+
/**
|
|
7434
|
+
* Resolve the geometry for a recording config's scrub-thumbnail preset,
|
|
7435
|
+
* defaulting to `standard` when unset. Pure — no I/O.
|
|
7436
|
+
*/
|
|
7437
|
+
function resolveScrubThumbnailGeometry(preset) {
|
|
7438
|
+
return SCRUB_THUMBNAIL_PRESETS[preset ?? "standard"];
|
|
7439
|
+
}
|
|
7440
|
+
/**
|
|
7382
7441
|
* The full per-camera recording intent — the wire shape of a RecordingTarget.
|
|
7383
7442
|
*
|
|
7384
7443
|
* `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
|
|
@@ -7415,7 +7474,14 @@ var RecordingConfigSchema = object({
|
|
|
7415
7474
|
* derived into bands once via `migrateConfigToBands`.
|
|
7416
7475
|
*/
|
|
7417
7476
|
bands: array(RecordingBandSchema).optional(),
|
|
7418
|
-
retention: RecordingRetentionSchema.optional()
|
|
7477
|
+
retention: RecordingRetentionSchema.optional(),
|
|
7478
|
+
/**
|
|
7479
|
+
* Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
|
|
7480
|
+
* timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
|
|
7481
|
+
* windows only — existing sheets are immutable, and each window's index
|
|
7482
|
+
* carries its own tile dims so mixed-preset history renders correctly.
|
|
7483
|
+
*/
|
|
7484
|
+
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
7419
7485
|
});
|
|
7420
7486
|
/** All seven weekdays (0 = Sunday … 6 = Saturday), used when a schedule omits a
|
|
7421
7487
|
* weekday filter. */
|
|
@@ -26156,4 +26222,4 @@ function defaultDeviceFor(id) {
|
|
|
26156
26222
|
return def(id).defaultDevice;
|
|
26157
26223
|
}
|
|
26158
26224
|
//#endregion
|
|
26159
|
-
export {
|
|
26225
|
+
export { boolean as $, recordingCapability as A, DeviceFeature as B, mapAudioLabelToMacro as C, nodePin as D, motionDetectionCapability as E, supportedRuntimes as F, makeProfileBrokerId as G, createEvent as H, webrtcSessionCapability as I, parseProfileBrokerId as J, makeSourceBrokerId as K, errMsg as L, runtimeDevices as M, storageEvictableCapability as N, pipelineExecutorCapability as O, streamBrokerCapability as P, array as Q, BaseAddon as R, hfModelUrl as S, migrateConfigToBands as T, hydrateSchema as U, DeviceType as V, isEvent as W, sleep as X, selectAssignedProfileSlots as Y, _enum as Z, customAction as _, COCO_TO_MACRO as a, record as at, detectionPipelineCapability as b, EncodeProfileSchema as c, EventCategory as ct, RingBuffer as d, discriminatedUnion as et, YAMNET_TO_MACRO as f, cameraStreamsCapability as g, audioAnalyzerCapability as h, COCO_80_LABELS as i, object as it, resolveScrubThumbnailGeometry as j, pipelineRunnerCapability as k, HF_BASE_URL as l, audioAnalysisCapability as m, AUDIO_BACKEND_CHOICES as n, literal as nt, DEFAULT_AUDIO_ANALYZER_CONFIG as o, string as ot, addonWidgetsSourceCapability as p, parseJsonUnknown as q, AUDIO_MACRO_LABELS as r, number as rt, EVENT_PAD_MS as s, union as st, APPLE_SA_TO_MACRO as t, lazy as tt, RecordingConfigSchema as u, defaultDeviceFor as v, maskUrlCredentials as w, evaluateZoneRules as x, defineCustomActions as y, CAM_PROFILE_ORDER as z };
|
|
@@ -7379,6 +7379,65 @@ var RecordingRetentionSchema = object({
|
|
|
7379
7379
|
maxSizeGb: number().min(0).optional()
|
|
7380
7380
|
});
|
|
7381
7381
|
/**
|
|
7382
|
+
* Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
|
|
7383
|
+
* sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
|
|
7384
|
+
* previews at. Five graduated steps; absent on a config = `standard` (the
|
|
7385
|
+
* shipped default, matching `sheet-geometry`/`sheet-composer`).
|
|
7386
|
+
*
|
|
7387
|
+
* Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
|
|
7388
|
+
* Each window's index sidecar carries its own tile dims, so a camera whose
|
|
7389
|
+
* preset changed over time renders every historical window at the dims it was
|
|
7390
|
+
* written with.
|
|
7391
|
+
*/
|
|
7392
|
+
var ScrubThumbnailPresetSchema = _enum([
|
|
7393
|
+
"minimal",
|
|
7394
|
+
"low",
|
|
7395
|
+
"standard",
|
|
7396
|
+
"high",
|
|
7397
|
+
"max"
|
|
7398
|
+
]);
|
|
7399
|
+
/**
|
|
7400
|
+
* Preset → tile geometry + JPEG quality. Every step is 16:9; the graduated
|
|
7401
|
+
* ladder trades sheet size (and remote fetch cost) for scrub crispness.
|
|
7402
|
+
* `standard` matches the shipped `sheet-geometry`/`sheet-composer` defaults
|
|
7403
|
+
* (480×270 q72), so an unset config is byte-identical to today. The map is
|
|
7404
|
+
* strictly monotone in resolution AND quality across the ordered steps.
|
|
7405
|
+
*/
|
|
7406
|
+
var SCRUB_THUMBNAIL_PRESETS = {
|
|
7407
|
+
minimal: {
|
|
7408
|
+
tileWidth: 240,
|
|
7409
|
+
tileHeight: 135,
|
|
7410
|
+
quality: 55
|
|
7411
|
+
},
|
|
7412
|
+
low: {
|
|
7413
|
+
tileWidth: 320,
|
|
7414
|
+
tileHeight: 180,
|
|
7415
|
+
quality: 60
|
|
7416
|
+
},
|
|
7417
|
+
standard: {
|
|
7418
|
+
tileWidth: 480,
|
|
7419
|
+
tileHeight: 270,
|
|
7420
|
+
quality: 72
|
|
7421
|
+
},
|
|
7422
|
+
high: {
|
|
7423
|
+
tileWidth: 640,
|
|
7424
|
+
tileHeight: 360,
|
|
7425
|
+
quality: 80
|
|
7426
|
+
},
|
|
7427
|
+
max: {
|
|
7428
|
+
tileWidth: 960,
|
|
7429
|
+
tileHeight: 540,
|
|
7430
|
+
quality: 85
|
|
7431
|
+
}
|
|
7432
|
+
};
|
|
7433
|
+
/**
|
|
7434
|
+
* Resolve the geometry for a recording config's scrub-thumbnail preset,
|
|
7435
|
+
* defaulting to `standard` when unset. Pure — no I/O.
|
|
7436
|
+
*/
|
|
7437
|
+
function resolveScrubThumbnailGeometry(preset) {
|
|
7438
|
+
return SCRUB_THUMBNAIL_PRESETS[preset ?? "standard"];
|
|
7439
|
+
}
|
|
7440
|
+
/**
|
|
7382
7441
|
* The full per-camera recording intent — the wire shape of a RecordingTarget.
|
|
7383
7442
|
*
|
|
7384
7443
|
* `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
|
|
@@ -7415,7 +7474,14 @@ var RecordingConfigSchema = object({
|
|
|
7415
7474
|
* derived into bands once via `migrateConfigToBands`.
|
|
7416
7475
|
*/
|
|
7417
7476
|
bands: array(RecordingBandSchema).optional(),
|
|
7418
|
-
retention: RecordingRetentionSchema.optional()
|
|
7477
|
+
retention: RecordingRetentionSchema.optional(),
|
|
7478
|
+
/**
|
|
7479
|
+
* Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
|
|
7480
|
+
* timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
|
|
7481
|
+
* windows only — existing sheets are immutable, and each window's index
|
|
7482
|
+
* carries its own tile dims so mixed-preset history renders correctly.
|
|
7483
|
+
*/
|
|
7484
|
+
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
7419
7485
|
});
|
|
7420
7486
|
/** All seven weekdays (0 = Sunday … 6 = Saturday), used when a schedule omits a
|
|
7421
7487
|
* weekday filter. */
|
|
@@ -26468,6 +26534,12 @@ Object.defineProperty(exports, "recordingCapability", {
|
|
|
26468
26534
|
return recordingCapability;
|
|
26469
26535
|
}
|
|
26470
26536
|
});
|
|
26537
|
+
Object.defineProperty(exports, "resolveScrubThumbnailGeometry", {
|
|
26538
|
+
enumerable: true,
|
|
26539
|
+
get: function() {
|
|
26540
|
+
return resolveScrubThumbnailGeometry;
|
|
26541
|
+
}
|
|
26542
|
+
});
|
|
26471
26543
|
Object.defineProperty(exports, "runtimeDevices", {
|
|
26472
26544
|
enumerable: true,
|
|
26473
26545
|
get: function() {
|
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-DynD1EC-.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as motionDetectionCapability, R as BaseAddon, U as hydrateSchema, V as DeviceType, x as evaluateZoneRules } from "../dist-CcqqznPf.mjs";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region src/motion-wasm/wasm-motion-detector.ts
|
|
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
6
|
+
const require_dist = require("../dist-DynD1EC-.js");
|
|
7
7
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
8
|
-
const require_sheet_geometry = require("../sheet-geometry-
|
|
8
|
+
const require_sheet_geometry = require("../sheet-geometry-BR-ip0CP.js");
|
|
9
9
|
const require_worker_protocol = require("../worker-protocol-PP4jKHHJ.js");
|
|
10
|
-
const require_step_definitions = require("../step-definitions-
|
|
10
|
+
const require_step_definitions = require("../step-definitions-DhhwklAL.js");
|
|
11
11
|
let node_child_process = require("node:child_process");
|
|
12
12
|
let node_url = require("node:url");
|
|
13
13
|
let sharp = require("sharp");
|
|
@@ -3087,7 +3087,7 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
|
|
|
3087
3087
|
const data = frame.data;
|
|
3088
3088
|
(async () => {
|
|
3089
3089
|
try {
|
|
3090
|
-
const targetW = Math.min(
|
|
3090
|
+
const targetW = Math.min(480, width);
|
|
3091
3091
|
const image = (0, sharp.default)(data, { raw: {
|
|
3092
3092
|
width,
|
|
3093
3093
|
height,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as boolean, D as nodePin, H as createEvent, L as errMsg, Q as array, R as BaseAddon, Z as _enum, _ as customAction, ct as EventCategory, it as object, k as pipelineRunnerCapability, ot as string, rt as number, tt as lazy, y as defineCustomActions } from "../dist-CcqqznPf.mjs";
|
|
2
2
|
import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
|
|
3
|
-
import { r as resolveHubHostname } from "../sheet-geometry-
|
|
3
|
+
import { r as resolveHubHostname } from "../sheet-geometry-DcpuGTCm.mjs";
|
|
4
4
|
import { t as isWorkerReply } from "../worker-protocol-CyVJTZEO.mjs";
|
|
5
|
-
import { a as getStepDefinition } from "../step-definitions-
|
|
5
|
+
import { a as getStepDefinition } from "../step-definitions-CvwB_Otk.mjs";
|
|
6
6
|
import { fork } from "node:child_process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import sharp from "sharp";
|
|
@@ -3081,7 +3081,7 @@ var PipelineRunnerAddon = class extends BaseAddon {
|
|
|
3081
3081
|
const data = frame.data;
|
|
3082
3082
|
(async () => {
|
|
3083
3083
|
try {
|
|
3084
|
-
const targetW = Math.min(
|
|
3084
|
+
const targetW = Math.min(480, width);
|
|
3085
3085
|
const image = sharp(data, { raw: {
|
|
3086
3086
|
width,
|
|
3087
3087
|
height,
|
package/dist/recorder/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
2
|
-
const require_dist = require("../dist-
|
|
3
|
-
const require_sheet_geometry = require("../sheet-geometry-
|
|
2
|
+
const require_dist = require("../dist-DynD1EC-.js");
|
|
3
|
+
const require_sheet_geometry = require("../sheet-geometry-BR-ip0CP.js");
|
|
4
4
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Cp_GTKTw.js");
|
|
5
5
|
let node_child_process = require("node:child_process");
|
|
6
6
|
let sharp = require("sharp");
|
|
@@ -2075,8 +2075,8 @@ var resolveRecordingHubHostname = require_sheet_geometry.resolveHubHostname;
|
|
|
2075
2075
|
*/
|
|
2076
2076
|
function buildThumbnailIndex(input) {
|
|
2077
2077
|
if (input.samples.length === 0) throw new Error("buildThumbnailIndex: no samples");
|
|
2078
|
-
const tileWidth = input.tileWidth ??
|
|
2079
|
-
const tileHeight = input.tileHeight ??
|
|
2078
|
+
const tileWidth = input.tileWidth ?? 480;
|
|
2079
|
+
const tileHeight = input.tileHeight ?? 270;
|
|
2080
2080
|
const maxCols = input.maxCols ?? 10;
|
|
2081
2081
|
const ordered = [...input.samples].sort((a, b) => a.t - b.t);
|
|
2082
2082
|
const geometry = require_sheet_geometry.computeSheetGeometry(ordered.length, {
|
|
@@ -2229,6 +2229,7 @@ var ThumbnailSink = class {
|
|
|
2229
2229
|
}
|
|
2230
2230
|
async doFlush(deviceId, win) {
|
|
2231
2231
|
if (win.samples.length === 0) return;
|
|
2232
|
+
const preset = await this.deps.resolvePreset(deviceId);
|
|
2232
2233
|
const ordered = [...win.samples].sort((a, b) => a.capturedAt - b.capturedAt);
|
|
2233
2234
|
const built = buildThumbnailIndex({
|
|
2234
2235
|
deviceId,
|
|
@@ -2236,8 +2237,8 @@ var ThumbnailSink = class {
|
|
|
2236
2237
|
windowMs: this.deps.windowMs,
|
|
2237
2238
|
intervalMs: this.deps.intervalMs,
|
|
2238
2239
|
source: this.deps.source,
|
|
2239
|
-
tileWidth:
|
|
2240
|
-
tileHeight:
|
|
2240
|
+
tileWidth: preset.tileWidth,
|
|
2241
|
+
tileHeight: preset.tileHeight,
|
|
2241
2242
|
maxCols: this.deps.maxCols,
|
|
2242
2243
|
samples: ordered.map((s) => ({ t: s.capturedAt }))
|
|
2243
2244
|
});
|
|
@@ -2245,7 +2246,8 @@ var ThumbnailSink = class {
|
|
|
2245
2246
|
const sheetBytes = await this.deps.compose({
|
|
2246
2247
|
tiles: ordered.map((s) => s.jpeg),
|
|
2247
2248
|
placements,
|
|
2248
|
-
geometry: built.geometry
|
|
2249
|
+
geometry: built.geometry,
|
|
2250
|
+
quality: preset.quality
|
|
2249
2251
|
});
|
|
2250
2252
|
await this.deps.persist({
|
|
2251
2253
|
deviceId,
|
|
@@ -2272,7 +2274,7 @@ async function composeSheet(sharp, input) {
|
|
|
2272
2274
|
const entries = (await Promise.all(tiles.map((t) => sharp(t).resize(geometry.tileWidth, geometry.tileHeight, {
|
|
2273
2275
|
fit: "cover",
|
|
2274
2276
|
position: "centre"
|
|
2275
|
-
}).jpeg({ quality: input.quality ??
|
|
2277
|
+
}).jpeg({ quality: input.quality ?? 72 }).toBuffer()))).map((buf, i) => {
|
|
2276
2278
|
const p = placements[i];
|
|
2277
2279
|
return {
|
|
2278
2280
|
input: buf,
|
|
@@ -2289,7 +2291,7 @@ async function composeSheet(sharp, input) {
|
|
|
2289
2291
|
g: 0,
|
|
2290
2292
|
b: 0
|
|
2291
2293
|
}
|
|
2292
|
-
} }).composite(entries).jpeg({ quality: input.quality ??
|
|
2294
|
+
} }).composite(entries).jpeg({ quality: input.quality ?? 72 }).toBuffer();
|
|
2293
2295
|
}
|
|
2294
2296
|
//#endregion
|
|
2295
2297
|
//#region src/thumbnails/thumbnail-persister.ts
|
|
@@ -2483,6 +2485,22 @@ function planBackfill(input) {
|
|
|
2483
2485
|
//#endregion
|
|
2484
2486
|
//#region src/thumbnails/backfill-ffmpeg.ts
|
|
2485
2487
|
/**
|
|
2488
|
+
* Map a JPEG quality (higher = better; the scrub presets span ~55..85) onto
|
|
2489
|
+
* ffmpeg's INVERSE `-q:v` scale (2 = best .. 31 = worst). Inverse-linear over
|
|
2490
|
+
* the full JPEG 1..100 domain, monotonic-decreasing (a higher JPEG quality
|
|
2491
|
+
* yields a lower/better ffmpeg q), clamped to [2, 31] for any input.
|
|
2492
|
+
*
|
|
2493
|
+
* These extracted keyframes are INTERMEDIATE — `composeSheet` re-encodes them
|
|
2494
|
+
* into the sheet at the preset's final JPEG quality — so this only governs the
|
|
2495
|
+
* extract's crispness. Anchoring the extract to the same preset keeps a
|
|
2496
|
+
* high-preset camera's backfill from being the bottleneck while a low-preset
|
|
2497
|
+
* camera spends fewer bytes on throwaway intermediates.
|
|
2498
|
+
*/
|
|
2499
|
+
function jpegQualityToFfmpegQ(jpegQuality) {
|
|
2500
|
+
const q = Math.round(31 - (Math.min(100, Math.max(1, jpegQuality)) - 1) / 99 * 29);
|
|
2501
|
+
return Math.min(31, Math.max(2, q));
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2486
2504
|
* ffmpeg args to extract keyframes from one segment as scaled JPEGs. `-skip_frame
|
|
2487
2505
|
* nokey` + `-vsync 0` (a.k.a. passthrough) keeps only I-frames without
|
|
2488
2506
|
* duplicating; `-an` drops audio. The caller resolves timestamps from the
|
|
@@ -2579,16 +2597,16 @@ var ThumbnailService = class {
|
|
|
2579
2597
|
this.sink = new ThumbnailSink({
|
|
2580
2598
|
windowMs: deps.config.windowMs,
|
|
2581
2599
|
intervalMs: deps.config.intervalMs,
|
|
2582
|
-
tileWidth: 320,
|
|
2583
|
-
tileHeight: 180,
|
|
2584
2600
|
maxCols: 10,
|
|
2601
|
+
resolvePreset: (deviceId) => deps.resolvePreset(deviceId),
|
|
2585
2602
|
maxTilesPerWindow: MAX_TILES_PER_WINDOW,
|
|
2586
2603
|
idleGraceMs: IDLE_GRACE_MS,
|
|
2587
2604
|
source: "sampled",
|
|
2588
2605
|
compose: (req) => composeSheet(SHARP, {
|
|
2589
2606
|
tiles: [...req.tiles],
|
|
2590
2607
|
placements: req.placements,
|
|
2591
|
-
geometry: req.geometry
|
|
2608
|
+
geometry: req.geometry,
|
|
2609
|
+
quality: req.quality
|
|
2592
2610
|
}),
|
|
2593
2611
|
persist: (req) => this.persist(req),
|
|
2594
2612
|
logger: deps.logger.child("thumbnail-sink")
|
|
@@ -2854,13 +2872,14 @@ var ThumbnailService = class {
|
|
|
2854
2872
|
/** Fill ONE window: extract keyframes from its covering low segments, compose
|
|
2855
2873
|
* a sheet, persist with `source:'backfill'`. */
|
|
2856
2874
|
async backfillWindow(deviceId, windowStartMs, segments, root) {
|
|
2875
|
+
const preset = await this.deps.resolvePreset(deviceId);
|
|
2857
2876
|
const workDir = await node_fs.promises.mkdtemp(node_path.default.join((0, node_os.tmpdir)(), `csthumb-${deviceId}-`));
|
|
2858
2877
|
try {
|
|
2859
2878
|
const samples = [];
|
|
2860
2879
|
for (const seg of segments) {
|
|
2861
2880
|
const absSeg = this.resolveSegmentAbs(deviceId, seg.path);
|
|
2862
2881
|
if (!absSeg) continue;
|
|
2863
|
-
const jpegs = await this.extractKeyframes(absSeg, workDir);
|
|
2882
|
+
const jpegs = await this.extractKeyframes(absSeg, workDir, preset);
|
|
2864
2883
|
const n = jpegs.length;
|
|
2865
2884
|
for (let i = 0; i < n; i += 1) {
|
|
2866
2885
|
const t = seg.startMs + Math.round(seg.durMs * i / Math.max(1, n));
|
|
@@ -2879,21 +2898,22 @@ var ThumbnailService = class {
|
|
|
2879
2898
|
windowMs: this.deps.config.windowMs,
|
|
2880
2899
|
intervalMs: this.deps.config.intervalMs,
|
|
2881
2900
|
source: "backfill",
|
|
2882
|
-
tileWidth:
|
|
2883
|
-
tileHeight:
|
|
2901
|
+
tileWidth: preset.tileWidth,
|
|
2902
|
+
tileHeight: preset.tileHeight,
|
|
2884
2903
|
maxCols: 10,
|
|
2885
2904
|
samples: ordered.map((s) => ({ t: s.t }))
|
|
2886
2905
|
});
|
|
2887
2906
|
const geometry = require_sheet_geometry.computeSheetGeometry(ordered.length, {
|
|
2888
|
-
tileWidth:
|
|
2889
|
-
tileHeight:
|
|
2907
|
+
tileWidth: preset.tileWidth,
|
|
2908
|
+
tileHeight: preset.tileHeight,
|
|
2890
2909
|
maxCols: 10
|
|
2891
2910
|
});
|
|
2892
2911
|
const placements = require_sheet_geometry.allTilePlacements(ordered.length, geometry);
|
|
2893
2912
|
const sheetBytes = await composeSheet(SHARP, {
|
|
2894
2913
|
tiles: ordered.map((s) => s.jpeg),
|
|
2895
2914
|
placements,
|
|
2896
|
-
geometry
|
|
2915
|
+
geometry,
|
|
2916
|
+
quality: preset.quality
|
|
2897
2917
|
});
|
|
2898
2918
|
await persistThumbnailWindow(root, {
|
|
2899
2919
|
deviceId,
|
|
@@ -2921,11 +2941,12 @@ var ThumbnailService = class {
|
|
|
2921
2941
|
}
|
|
2922
2942
|
/** Run ONE keyframe-only ffmpeg pass over a segment; return the extracted
|
|
2923
2943
|
* JPEG buffers in order. */
|
|
2924
|
-
async extractKeyframes(absSeg, workDir) {
|
|
2944
|
+
async extractKeyframes(absSeg, workDir, preset) {
|
|
2925
2945
|
const args = buildKeyframeExtractArgs({
|
|
2926
2946
|
inputPath: absSeg,
|
|
2927
2947
|
outputPattern: node_path.default.join(workDir, "kf-%04d.jpg"),
|
|
2928
|
-
tileWidth:
|
|
2948
|
+
tileWidth: preset.tileWidth,
|
|
2949
|
+
quality: jpegQualityToFfmpegQ(preset.quality)
|
|
2929
2950
|
});
|
|
2930
2951
|
await this.spawnFfmpeg(args);
|
|
2931
2952
|
const files = (await node_fs.promises.readdir(workDir)).filter((f) => /^kf-\d+\.jpg$/.test(f)).sort();
|
|
@@ -3271,6 +3292,13 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
3271
3292
|
locations: () => this.resolvedLocations,
|
|
3272
3293
|
lowSegments: (deviceId) => this.index.segments(deviceId, "low"),
|
|
3273
3294
|
recordingDeviceIds: async () => [...(await readDeviceConfigs(this.configStore())).keys()],
|
|
3295
|
+
resolvePreset: async (deviceId) => {
|
|
3296
|
+
try {
|
|
3297
|
+
return require_dist.resolveScrubThumbnailGeometry((await loadDeviceConfig(this.configStore(), deviceId)).scrubThumbnails);
|
|
3298
|
+
} catch {
|
|
3299
|
+
return require_dist.resolveScrubThumbnailGeometry(void 0);
|
|
3300
|
+
}
|
|
3301
|
+
},
|
|
3274
3302
|
config: {
|
|
3275
3303
|
enabled: this.config.thumbnailsEnabled,
|
|
3276
3304
|
backfillEnabled: this.config.thumbnailBackfillEnabled,
|