@camstack/addon-benchmark 1.2.33 → 1.2.35

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.
Files changed (20) hide show
  1. package/dist/{MotionZonesSettings-FXWWaTYb.mjs → MotionZonesSettings-byh-hz4h.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-BpMptp4x.mjs → PrivacyMaskSettings-BTJXzpVs.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-Cf3XyH9c.mjs → SceneMonitorEditor-ByMg9jNA.mjs} +3 -3
  4. package/dist/_stub.js +323 -323
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-DepVjAhb.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-BlLbFUGp.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CVGfn-Jl.mjs +26 -0
  7. package/dist/{hostInit-xoO6xcwl.mjs → hostInit-CUsURldd.mjs} +3 -3
  8. package/dist/index.js +94 -15
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +94 -15
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/{player-overlays-D5oVSKal.mjs → player-overlays-BJCbexS5.mjs} +1 -1
  13. package/dist/remoteEntry.js +1 -1
  14. package/dist/{responsive-Cf5Bnr8W.mjs → responsive-DD0uvbma.mjs} +1 -1
  15. package/dist/{square-D6nXF0kO.mjs → square-DDT1ScwA.mjs} +1 -1
  16. package/dist/{trash-2-C-_6tBsE.mjs → trash-2-Cp-MYXa5.mjs} +1 -1
  17. package/dist/{use-device-snapshot-BexLgV9y.mjs → use-device-snapshot-DGJssDxu.mjs} +1 -1
  18. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-CSmRZSn7.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-BjvSVZ3k.mjs} +1 -1
  19. package/package.json +1 -1
  20. package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-__ozgT-Z.mjs +0 -26
package/dist/index.mjs CHANGED
@@ -15661,10 +15661,18 @@ async function orchestrateReplayRun(deps, input) {
15661
15661
  meta: { maxFrames: input.maxFrames, planned: framePlan.instants.length }
15662
15662
  });
15663
15663
  }
15664
- const sourceBytes = await deps.fetchSourceBytes(input.source);
15664
+ const sourceFetch = await deps.fetchSourceBytes(input.source);
15665
+ if (sourceFetch.kind === "refused") {
15666
+ deps.logger.warn("replay: run refused \u2014 could not fetch source bytes", {
15667
+ tags,
15668
+ meta: { reason: sourceFetch.reason }
15669
+ });
15670
+ return { kind: "refused", reason: sourceFetch.reason };
15671
+ }
15672
+ const sourceBytes = sourceFetch.bytes;
15665
15673
  const first = framePlan.instants[0] ?? 0;
15666
15674
  const last = framePlan.instants[framePlan.instants.length - 1] ?? first;
15667
- const startSec = input.source.kind === "recording" ? (first - input.source.fromMs) / 1e3 : first / 1e3;
15675
+ const startSec = (first - sourceFetch.startMs) / 1e3;
15668
15676
  const durationSec = Math.max(1 / input.fps, (last - first) / 1e3 + 1 / input.fps);
15669
15677
  const args = buildReplaySequenceArgs({
15670
15678
  startSec,
@@ -16172,27 +16180,98 @@ var BenchmarkAddon = class _BenchmarkAddon extends BaseAddon {
16172
16180
  });
16173
16181
  return result;
16174
16182
  }
16183
+ /**
16184
+ * The profile a replay window is read from. `high` — same choice
16185
+ * `RECORDED_STILL_PROFILE` makes in `addon-post-analysis` — because the
16186
+ * whole point of `recording` over `upload` is NATIVE pixels (`-c:v copy`
16187
+ * means the recorded frame IS the live frame); a window cut from a
16188
+ * lower-rate rendition would be a worse version of the thing this source
16189
+ * exists to avoid.
16190
+ */
16191
+ static REPLAY_RECORDING_PROFILE = "high";
16175
16192
  /**
16176
16193
  * Byte source for the replay decode step.
16177
16194
  *
16178
- * `upload` is fully wired: `storage.read` against the `replayClips`
16179
- * location. `recording` is NOT — it needs a recorder-side read port
16180
- * spanning a time WINDOW (locate + multi-GOP read, the plan's own
16181
- * comparison is `RecordedStillPorts`, which reads exactly ONE instant).
16182
- * Whether device 615's footage even covers 2026-08-26 18:12 was never
16183
- * verified against the live hub — the plan's Unknown #2. Reporting that
16184
- * clearly here beats guessing at a port shape nobody has confirmed.
16195
+ * `upload` is `storage.read` against the `replayClips` location. `recording`
16196
+ * is `recording.getDeviceConfig` `recording.locateSegment`
16197
+ * `recording.readWindowBytes` the same "cheap question first" ordering
16198
+ * `RecordedStillService` uses (a camera that is not recording costs a
16199
+ * settings read, never a disk read), through `ctx.api` because addons never
16200
+ * import each other.
16201
+ *
16202
+ * Every expected failure — not recording, no footage covering `fromMs`, a
16203
+ * window that runs past its covering segment — returns a NAMED `refused`,
16204
+ * never a thrown error: "the window has no segments" is a RESULT this
16205
+ * whole feature exists to make legible (see the plan's stated unknown about
16206
+ * whether device 615's footage covers 2026-08-26 18:12), not a failure to
16207
+ * hide behind a generic exception.
16185
16208
  */
16186
16209
  async fetchReplaySourceBytes(source) {
16187
16210
  if (source.kind === "upload") {
16188
- return this.api.storage.read.query({
16189
- location: _BenchmarkAddon.REPLAY_CLIPS_LOCATION,
16190
- relativePath: `${source.clipId}.bin`
16211
+ try {
16212
+ const bytes = await this.api.storage.read.query({
16213
+ location: _BenchmarkAddon.REPLAY_CLIPS_LOCATION,
16214
+ relativePath: `${source.clipId}.bin`
16215
+ });
16216
+ return { kind: "ok", bytes, startMs: 0 };
16217
+ } catch (err) {
16218
+ return { kind: "refused", reason: `clip-not-found: ${errMsg(err)}` };
16219
+ }
16220
+ }
16221
+ const profile = _BenchmarkAddon.REPLAY_RECORDING_PROFILE;
16222
+ const tags = { deviceId: source.deviceId };
16223
+ const config2 = await this.api.recording.getDeviceConfig.query({ deviceId: source.deviceId });
16224
+ if (config2.enabled === false) {
16225
+ this.ctx.logger.warn("replay: recording source refused \u2014 camera is not recording", {
16226
+ tags
16191
16227
  });
16228
+ return {
16229
+ kind: "refused",
16230
+ reason: `no-recording: device ${source.deviceId} does not have recording enabled`
16231
+ };
16192
16232
  }
16193
- throw new Error(
16194
- "replay: 'recording' source is not wired \u2014 needs a recorder read port spanning a time window (locate + multi-GOP read), and whether this camera\u2019s footage covers the requested window was never verified against the live hub. Use an uploaded clip today."
16195
- );
16233
+ const located = await this.api.recording.locateSegment.query({
16234
+ deviceId: source.deviceId,
16235
+ profile,
16236
+ epochMs: source.fromMs
16237
+ });
16238
+ if (located.kind !== "segment") {
16239
+ this.ctx.logger.warn(
16240
+ "replay: recording source refused \u2014 no footage covers the window start",
16241
+ { tags, meta: { fromMs: source.fromMs, toMs: source.toMs, profile } }
16242
+ );
16243
+ return {
16244
+ kind: "refused",
16245
+ reason: `no-footage: device ${source.deviceId} has no ${profile} recording covering ${new Date(source.fromMs).toISOString()}`
16246
+ };
16247
+ }
16248
+ const window = await this.api.recording.readWindowBytes.query({
16249
+ deviceId: source.deviceId,
16250
+ profile,
16251
+ startMs: located.startMs,
16252
+ fromMs: source.fromMs,
16253
+ toMs: source.toMs
16254
+ });
16255
+ if (window.kind !== "ok") {
16256
+ this.ctx.logger.warn(
16257
+ "replay: recording source refused \u2014 window runs past the covering segment",
16258
+ {
16259
+ tags,
16260
+ meta: { fromMs: source.fromMs, toMs: source.toMs, segmentEndMs: window.segmentEndMs }
16261
+ }
16262
+ );
16263
+ return {
16264
+ kind: "refused",
16265
+ reason: `window-spans-multiple-segments: footage for device ${source.deviceId} changes segment at ${window.segmentEndMs}, before the requested end ${source.toMs} \u2014 shorten the window or align it to one segment`
16266
+ };
16267
+ }
16268
+ if (!window.reachesRequestedEnd) {
16269
+ this.ctx.logger.warn(
16270
+ "replay: recording window bytes truncated by the recorder read safety cap",
16271
+ { tags, meta: { fromMs: source.fromMs, toMs: source.toMs, gopDurMs: window.gopDurMs } }
16272
+ );
16273
+ }
16274
+ return { kind: "ok", bytes: window.data, startMs: window.gopStartMs };
16196
16275
  }
16197
16276
  async listReplayRuns(input) {
16198
16277
  if (this.replayStore === null) return [];