@camstack/addon-pipeline 1.2.320 → 1.2.323

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 (33) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-CO4pZxbd.js → default-detection-model-3nybzgJM.js} +1 -1
  4. package/dist/{default-detection-model-B-iMtrXt.mjs → default-detection-model-BbGqKJrb.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-Bce5_ocm.js → dist-BHwYsqDW.js} +3251 -2624
  8. package/dist/{dist-BK-pWZzI.mjs → dist-CYhfylbW.mjs} +3251 -2624
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-UJwLVGTT.js → node-BPEcDS3W.js} +1 -1
  12. package/dist/{node-DQsiK6Vt.mjs → node-KuSWIyX-.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +3 -3
  14. package/dist/pipeline-runner/index.mjs +3 -3
  15. package/dist/{process-memory-Cn3QEo37.mjs → process-memory-BArnBFK7.mjs} +1 -1
  16. package/dist/{process-memory-DR7jI54j.js → process-memory-Maylk0nL.js} +1 -1
  17. package/dist/recorder/index.js +191 -8
  18. package/dist/recorder/index.mjs +191 -8
  19. package/dist/{segment-demux-js-CbazlBOP.js → segment-demux-js-FYxZEbrp.js} +1 -1
  20. package/dist/{segment-demux-js-CTMbCGRL.mjs → segment-demux-js-am2ssLJA.mjs} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DP4h-J_V.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-XTH7Q8hm.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bm0eNLm_.mjs +26 -0
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-mEJkrfvj.mjs +26 -0
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-CnrH7DvQ.mjs → hostInit-CgW4I0dG.mjs} +2 -2
  28. package/dist/stream-broker/index.js +47 -13
  29. package/dist/stream-broker/index.mjs +47 -13
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BAYDyjOG.mjs +0 -26
  33. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DHNf76xj.mjs +0 -26
@@ -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-Bce5_ocm.js");
5
+ const require_dist = require("../dist-BHwYsqDW.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Sn as hydrateSchema, ct as evaluateZoneRules, dn as DeviceType, kt as motionDetectionCapability, mn as BaseAddon } from "../dist-BK-pWZzI.mjs";
1
+ import { Sn as hydrateSchema, ct as evaluateZoneRules, dn as DeviceType, kt as motionDetectionCapability, mn as BaseAddon } from "../dist-CYhfylbW.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-Bce5_ocm.js");
2
+ const require_dist = require("./dist-BHwYsqDW.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { an as Fmp4BoxSplitter, fn as errMsg, ln as isSoftwareDecode, on as buildFfmpegArgs } from "./dist-BK-pWZzI.mjs";
1
+ import { an as Fmp4BoxSplitter, fn as errMsg, ln as isSoftwareDecode, on as buildFfmpegArgs } from "./dist-CYhfylbW.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-Bce5_ocm.js");
7
- const require_node = require("../node-UJwLVGTT.js");
8
- const require_default_detection_model = require("../default-detection-model-CO4pZxbd.js");
6
+ const require_dist = require("../dist-BHwYsqDW.js");
7
+ const require_node = require("../node-BPEcDS3W.js");
8
+ const require_default_detection_model = require("../default-detection-model-3nybzgJM.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
10
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
11
11
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
@@ -1,6 +1,6 @@
1
- import { D as NativeLeaseAdmissionSchema, En as nodePin, Fn as lazy, In as literal, It as pipelineRunnerCapability, Ln as number, Mn as array, Nn as boolean, Nt as pickDetailCropConvention, O as NativeLeaseSettingsSchema, Pt as pickNativeLeaseOverride, Rn as object, Un as EventCategory, Vn as string, a as BackgroundRunRegistry, bn as defineCustomActions, bt as loadContributionCapability, fn as errMsg, ft as failureContributionCapability, g as DEFAULT_DETAIL_CROP_CONVENTION, jn as _enum, mn as BaseAddon, nt as deriveDetailCropRect, q as cameraOwnsMotionViaSensor, v as DEFAULT_NATIVE_LEASE_SETTINGS, vn as createEvent, w as FailureCounters, yn as customAction } from "../dist-BK-pWZzI.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DQsiK6Vt.mjs";
3
- import { b as RETIRED_MODEL_IDS, c as macroGateVerdict, d as isSessionDecodeReadGone, f as createSessionDecodeCoordinator, l as rootObjectGate, o as placeDetailStep, p as resolveFrameViewGeometry, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as getStepDefinition } from "../default-detection-model-B-iMtrXt.mjs";
1
+ import { D as NativeLeaseAdmissionSchema, En as nodePin, Fn as lazy, In as literal, It as pipelineRunnerCapability, Ln as number, Mn as array, Nn as boolean, Nt as pickDetailCropConvention, O as NativeLeaseSettingsSchema, Pt as pickNativeLeaseOverride, Rn as object, Un as EventCategory, Vn as string, a as BackgroundRunRegistry, bn as defineCustomActions, bt as loadContributionCapability, fn as errMsg, ft as failureContributionCapability, g as DEFAULT_DETAIL_CROP_CONVENTION, jn as _enum, mn as BaseAddon, nt as deriveDetailCropRect, q as cameraOwnsMotionViaSensor, v as DEFAULT_NATIVE_LEASE_SETTINGS, vn as createEvent, w as FailureCounters, yn as customAction } from "../dist-CYhfylbW.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-KuSWIyX-.mjs";
3
+ import { b as RETIRED_MODEL_IDS, c as macroGateVerdict, d as isSessionDecodeReadGone, f as createSessionDecodeCoordinator, l as rootObjectGate, o as placeDetailStep, p as resolveFrameViewGeometry, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as getStepDefinition } from "../default-detection-model-BbGqKJrb.mjs";
4
4
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
6
  import { c as withDetectionIntent, o as preRollRequestOf, t as DETECTION_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
@@ -1,4 +1,4 @@
1
- import { jt as parseProcStatus } from "./dist-BK-pWZzI.mjs";
1
+ import { jt as parseProcStatus } from "./dist-CYhfylbW.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-Bce5_ocm.js");
2
+ const require_dist = require("./dist-BHwYsqDW.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-Bce5_ocm.js");
7
- const require_node = require("../node-UJwLVGTT.js");
6
+ const require_dist = require("../dist-BHwYsqDW.js");
7
+ const require_node = require("../node-BPEcDS3W.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
@@ -6025,12 +6025,18 @@ Object.freeze({
6025
6025
  "lookbackMinutes": 60
6026
6026
  }
6027
6027
  },
6028
- "osd-manager": { "setSlotBinding": { "binding": { "__nested__": {
6029
- "enabled": true,
6030
- "template": "${value}",
6031
- "maxDecimals": 1,
6032
- "fallbackText": ""
6033
- } } } },
6028
+ "osd-manager": {
6029
+ "setSlotBinding": { "binding": { "__nested__": {
6030
+ "enabled": true,
6031
+ "template": "${value}",
6032
+ "maxDecimals": 1,
6033
+ "fallbackText": ""
6034
+ } } },
6035
+ "upsertTemplate": { "template": { "__nested__": {
6036
+ "bindings": [],
6037
+ "parser": ""
6038
+ } } }
6039
+ },
6034
6040
  "pipeline-analytics": {
6035
6041
  "getAudioEvents": { "limit": 1e3 },
6036
6042
  "getKeyEvents": { "limit": 50 },
@@ -6135,6 +6141,7 @@ Object.freeze({
6135
6141
  "addon-routes": ["getRoutes"],
6136
6142
  "addon-widgets-source": ["listWidgets"],
6137
6143
  "broker": ["list", "listProviders"],
6144
+ "calendar-source": ["listCalendars", "listEvents"],
6138
6145
  "custom-model-registry": ["listModels"],
6139
6146
  "data-store-provider": ["histogram", "query"],
6140
6147
  "device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
@@ -7957,6 +7964,127 @@ async function reconcileInterruptedExports(deps) {
7957
7964
  };
7958
7965
  }
7959
7966
  //#endregion
7967
+ //#region src/recorder/addon/export-shrink-for-read.ts
7968
+ /**
7969
+ * Shrink a rendered export so `readExportBytes` can answer under
7970
+ * {@link RECORDING_EXPORT_MAX_READ_BYTES}.
7971
+ *
7972
+ * A ten-hour overnight timelapse with dense slowdown routinely lands at
7973
+ * 80–200 MB. The byte-read ceiling is the roomiest notifier cap (Telegram
7974
+ * 50 MB) AND the base64 envelope bound (D9/D18) — raising it would OOM the
7975
+ * hub and still be refused by the notifier. So the read shrinks, once, into
7976
+ * a temp file sized for delivery, and the original render stays on disk for
7977
+ * the authenticated tap-through.
7978
+ *
7979
+ * Pure argv builders here; the spawn and temp I/O are injected by the
7980
+ * provider so the suite never needs a real ffmpeg.
7981
+ */
7982
+ /** CRF ladder tried until the output fits. Higher = smaller, uglier. */
7983
+ var EXPORT_SHRINK_CRF_LADDER = [
7984
+ 28,
7985
+ 32,
7986
+ 36,
7987
+ 40
7988
+ ];
7989
+ /**
7990
+ * ffmpeg argv that re-encodes a rendered MP4 for notification delivery.
7991
+ *
7992
+ * - `libx264` + `veryfast`: the morning drain must not pin the hub the way
7993
+ * the original night render did.
7994
+ * - `-an`: a timelapse has no audio worth keeping, and dropping it is free
7995
+ * size.
7996
+ * - `640:-2`: notification phones; matching the band the cheapest profile
7997
+ * already aims for.
7998
+ * - `+faststart`: the artifact plane serves the whole file; moov-first is
7999
+ * what a phone needs to start playing before the download finishes.
8000
+ */
8001
+ function buildExportShrinkArgs(input) {
8002
+ return [
8003
+ "-hide_banner",
8004
+ "-loglevel",
8005
+ "error",
8006
+ "-y",
8007
+ "-i",
8008
+ input.inputPath,
8009
+ "-vf",
8010
+ "scale=640:-2",
8011
+ "-c:v",
8012
+ "libx264",
8013
+ "-preset",
8014
+ "veryfast",
8015
+ "-crf",
8016
+ String(input.crf),
8017
+ "-an",
8018
+ "-movflags",
8019
+ "+faststart",
8020
+ input.outputPath
8021
+ ];
8022
+ }
8023
+ /**
8024
+ * Pick the next CRF to try, or `null` when the ladder is exhausted.
8025
+ *
8026
+ * @param attempted — CRFs already tried (may be empty).
8027
+ */
8028
+ function nextShrinkCrf(attempted) {
8029
+ for (const crf of EXPORT_SHRINK_CRF_LADDER) if (!attempted.includes(crf)) return crf;
8030
+ return null;
8031
+ }
8032
+ /**
8033
+ * Re-encode `inputPath` down the CRF ladder until the file is ≤ `maxBytes`.
8034
+ *
8035
+ * @returns the fitted buffer, or `null` when every rung still overflows (or
8036
+ * ffmpeg refuses). Never throws — the caller treats `null` as "refuse
8037
+ * the inline read", same as the historical oversized gate.
8038
+ */
8039
+ async function shrinkExportToBound(inputPath, maxBytes, ports) {
8040
+ const attempted = [];
8041
+ for (;;) {
8042
+ const crf = nextShrinkCrf(attempted);
8043
+ if (crf === null) return null;
8044
+ attempted.push(crf);
8045
+ const outPath = `${ports.writePath}.crf${String(crf)}.mp4`;
8046
+ try {
8047
+ const ffmpeg = await ports.resolveFfmpeg();
8048
+ await runShrinkFfmpeg(ports.spawn, ffmpeg, buildExportShrinkArgs({
8049
+ inputPath,
8050
+ outputPath: outPath,
8051
+ crf
8052
+ }));
8053
+ if (await ports.statBytes(outPath) > maxBytes) {
8054
+ await ports.removeFile(outPath);
8055
+ continue;
8056
+ }
8057
+ const buf = await ports.readFile(outPath);
8058
+ await ports.removeFile(outPath);
8059
+ return buf;
8060
+ } catch {
8061
+ await ports.removeFile(outPath).catch(() => void 0);
8062
+ continue;
8063
+ }
8064
+ }
8065
+ }
8066
+ function runShrinkFfmpeg(spawn, ffmpegPath, args) {
8067
+ return new Promise((resolve, reject) => {
8068
+ const proc = spawn(ffmpegPath, args);
8069
+ let stderr = "";
8070
+ proc.stderr?.on("data", (chunk) => {
8071
+ stderr += chunk.toString("utf8");
8072
+ });
8073
+ proc.on("error", (...args) => {
8074
+ const err = args[0];
8075
+ reject(err instanceof Error ? err : new Error(String(err)));
8076
+ });
8077
+ proc.on("close", (...args) => {
8078
+ const code = args[0];
8079
+ if (code === 0) {
8080
+ resolve();
8081
+ return;
8082
+ }
8083
+ reject(/* @__PURE__ */ new Error(`export shrink ffmpeg exited ${String(code)}: ${stderr.slice(0, 400)}`));
8084
+ });
8085
+ });
8086
+ }
8087
+ //#endregion
7960
8088
  //#region src/recorder/playlist.ts
7961
8089
  /** A VOD variant playlist with an absolute wall-clock anchor per segment. */
7962
8090
  function buildVariantPlaylist(segments) {
@@ -18294,6 +18422,47 @@ function buildRecordingExportProvider(deps) {
18294
18422
  throw new Error(`recording-export: export "${exportId}" has no file on disk`, { cause: err });
18295
18423
  }
18296
18424
  if (size > 52428800) {
18425
+ const shrink = deps.shrinkToBound;
18426
+ if (shrink !== void 0) {
18427
+ deps.logger.info("export byte read: shrinking oversized file for inline delivery", {
18428
+ tags,
18429
+ meta: {
18430
+ exportId,
18431
+ bytes: size,
18432
+ maxBytes: require_dist.RECORDING_EXPORT_MAX_READ_BYTES
18433
+ }
18434
+ });
18435
+ const shrunk = await shrink(file, require_dist.RECORDING_EXPORT_MAX_READ_BYTES);
18436
+ if (shrunk !== null && shrunk.byteLength <= 52428800) {
18437
+ deps.logger.info("export byte read: shrink fitted under the inline ceiling", {
18438
+ tags,
18439
+ meta: {
18440
+ exportId,
18441
+ originalBytes: size,
18442
+ bytes: shrunk.byteLength,
18443
+ maxBytes: require_dist.RECORDING_EXPORT_MAX_READ_BYTES
18444
+ }
18445
+ });
18446
+ return {
18447
+ base64: shrunk.toString("base64"),
18448
+ contentType: EXPORT_CONTENT_TYPE,
18449
+ name: exportFileName({
18450
+ id: rec.id,
18451
+ ...rec.options.title !== void 0 ? { title: rec.options.title } : {}
18452
+ }),
18453
+ bytes: shrunk.byteLength
18454
+ };
18455
+ }
18456
+ deps.logger.warn("export byte read: shrink could not fit under the inline ceiling", {
18457
+ tags,
18458
+ meta: {
18459
+ exportId,
18460
+ originalBytes: size,
18461
+ shrunkBytes: shrunk?.byteLength ?? null,
18462
+ maxBytes: require_dist.RECORDING_EXPORT_MAX_READ_BYTES
18463
+ }
18464
+ });
18465
+ }
18297
18466
  deps.logger.warn("export byte read refused: file is too large to inline", {
18298
18467
  tags,
18299
18468
  meta: {
@@ -21813,6 +21982,20 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
21813
21982
  downloadUrlFor: (id) => `/addon/${RECORDER_ADDON_ID}/${EXPORTS_PREFIX}/${id}.mp4`,
21814
21983
  statBytes: async (p) => (await node_fs.promises.stat(p)).size,
21815
21984
  readFile: (p) => node_fs.promises.readFile(p),
21985
+ shrinkToBound: async (inputPath, maxBytes) => {
21986
+ const writePath = node_path.default.join(this.exportsDirOrThrow(), ".shrink", `${node_path.default.basename(inputPath, ".mp4")}-${(0, node_crypto.randomUUID)()}`);
21987
+ await node_fs.promises.mkdir(node_path.default.dirname(writePath), { recursive: true });
21988
+ return shrinkExportToBound(inputPath, maxBytes, {
21989
+ resolveFfmpeg: this.pinnedFfmpeg,
21990
+ spawn: node_child_process.spawn,
21991
+ writePath,
21992
+ readFile: (p) => node_fs.promises.readFile(p),
21993
+ removeFile: async (p) => {
21994
+ await node_fs.promises.rm(p, { force: true });
21995
+ },
21996
+ statBytes: async (p) => (await node_fs.promises.stat(p)).size
21997
+ });
21998
+ },
21816
21999
  logger: this.ctx.logger.child("export")
21817
22000
  });
21818
22001
  try {
@@ -1,5 +1,5 @@
1
- import { $t as stripRetiredBandPreBufferSec, An as sleep$1, B as VISIT_MERGE_GAP_MS, Bn as record, Bt as recordingExportCapability, C as ExportRecordSchema, Dt as migrateLegacyDeviceSignalConfig, En as nodePin, Et as memoizePinnedFfmpeg, F as RECORDING_SOURCE_CAMSTACK_ADDON, Gt as resolveLocationMode, I as RecordingConfigSchema, K as batteryCapability, L as RecordingSignalStatusSchema, Ln as number, Mn as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nn as boolean, Ot as modeMayWrite, P as RECORDING_SOURCE_CAMSTACK, Rn as object, Rt as recordingArchiveCapability, Sn as hydrateSchema, Tt as mayWriteToLocation, Un as EventCategory, Ut as resolveCreateExportSubject, Vn as string, Vt as recordingSignalCapability, X as clipExportFailureOf, Xt as storageEvictableCapability, Y as clipExportFailure, Zt as storageOccupancyCapability, _ as DEFAULT_EVENTS_BAND_BUFFER_SEC, b as EVENT_OWNER_TYPES, bn as defineCustomActions, bt as loadContributionCapability, dn as DeviceType, dt as exportSubjectOf, fn as errMsg$1, jn as _enum, k as OpsLogEntrySchema, kn as selectAssignedProfileSlots, lt as evictionPolicyForMode, mn as BaseAddon, mt as gbToBytes, o as BatteryStatusSchema, qt as resolveRecordingProfiles, rt as deriveRecordingMode, s as CLIP_BROKER_PACED_RATES, sn as evaluateSensorEdge, tn as videoclipsCapability, tt as deriveBatteryPresence, un as DeviceFeature, ut as evictionPolicyOfLocation, wt as mayReadLocation, x as EVENT_PAD_MS, yn as customAction, yt as isFirstLevelMacroClass, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as preprocess, zt as recordingCapability } from "../dist-BK-pWZzI.mjs";
2
- import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DQsiK6Vt.mjs";
1
+ import { $t as stripRetiredBandPreBufferSec, An as sleep$1, B as VISIT_MERGE_GAP_MS, Bn as record, Bt as recordingExportCapability, C as ExportRecordSchema, Dt as migrateLegacyDeviceSignalConfig, En as nodePin, Et as memoizePinnedFfmpeg, F as RECORDING_SOURCE_CAMSTACK_ADDON, Gt as resolveLocationMode, I as RecordingConfigSchema, K as batteryCapability, L as RecordingSignalStatusSchema, Ln as number, Mn as array, N as RECORDING_EXPORT_MAX_READ_BYTES, Nn as boolean, Ot as modeMayWrite, P as RECORDING_SOURCE_CAMSTACK, Rn as object, Rt as recordingArchiveCapability, Sn as hydrateSchema, Tt as mayWriteToLocation, Un as EventCategory, Ut as resolveCreateExportSubject, Vn as string, Vt as recordingSignalCapability, X as clipExportFailureOf, Xt as storageEvictableCapability, Y as clipExportFailure, Zt as storageOccupancyCapability, _ as DEFAULT_EVENTS_BAND_BUFFER_SEC, b as EVENT_OWNER_TYPES, bn as defineCustomActions, bt as loadContributionCapability, dn as DeviceType, dt as exportSubjectOf, fn as errMsg$1, jn as _enum, k as OpsLogEntrySchema, kn as selectAssignedProfileSlots, lt as evictionPolicyForMode, mn as BaseAddon, mt as gbToBytes, o as BatteryStatusSchema, qt as resolveRecordingProfiles, rt as deriveRecordingMode, s as CLIP_BROKER_PACED_RATES, sn as evaluateSensorEdge, tn as videoclipsCapability, tt as deriveBatteryPresence, un as DeviceFeature, ut as evictionPolicyOfLocation, wt as mayReadLocation, x as EVENT_PAD_MS, yn as customAction, yt as isFirstLevelMacroClass, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as preprocess, zt as recordingCapability } from "../dist-CYhfylbW.mjs";
2
+ import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-KuSWIyX-.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
5
5
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
@@ -6017,12 +6017,18 @@ Object.freeze({
6017
6017
  "lookbackMinutes": 60
6018
6018
  }
6019
6019
  },
6020
- "osd-manager": { "setSlotBinding": { "binding": { "__nested__": {
6021
- "enabled": true,
6022
- "template": "${value}",
6023
- "maxDecimals": 1,
6024
- "fallbackText": ""
6025
- } } } },
6020
+ "osd-manager": {
6021
+ "setSlotBinding": { "binding": { "__nested__": {
6022
+ "enabled": true,
6023
+ "template": "${value}",
6024
+ "maxDecimals": 1,
6025
+ "fallbackText": ""
6026
+ } } },
6027
+ "upsertTemplate": { "template": { "__nested__": {
6028
+ "bindings": [],
6029
+ "parser": ""
6030
+ } } }
6031
+ },
6026
6032
  "pipeline-analytics": {
6027
6033
  "getAudioEvents": { "limit": 1e3 },
6028
6034
  "getKeyEvents": { "limit": 50 },
@@ -6127,6 +6133,7 @@ Object.freeze({
6127
6133
  "addon-routes": ["getRoutes"],
6128
6134
  "addon-widgets-source": ["listWidgets"],
6129
6135
  "broker": ["list", "listProviders"],
6136
+ "calendar-source": ["listCalendars", "listEvents"],
6130
6137
  "custom-model-registry": ["listModels"],
6131
6138
  "data-store-provider": ["histogram", "query"],
6132
6139
  "device-export": ["listExposedDevices", "listSupportedDeviceKinds"],
@@ -7949,6 +7956,127 @@ async function reconcileInterruptedExports(deps) {
7949
7956
  };
7950
7957
  }
7951
7958
  //#endregion
7959
+ //#region src/recorder/addon/export-shrink-for-read.ts
7960
+ /**
7961
+ * Shrink a rendered export so `readExportBytes` can answer under
7962
+ * {@link RECORDING_EXPORT_MAX_READ_BYTES}.
7963
+ *
7964
+ * A ten-hour overnight timelapse with dense slowdown routinely lands at
7965
+ * 80–200 MB. The byte-read ceiling is the roomiest notifier cap (Telegram
7966
+ * 50 MB) AND the base64 envelope bound (D9/D18) — raising it would OOM the
7967
+ * hub and still be refused by the notifier. So the read shrinks, once, into
7968
+ * a temp file sized for delivery, and the original render stays on disk for
7969
+ * the authenticated tap-through.
7970
+ *
7971
+ * Pure argv builders here; the spawn and temp I/O are injected by the
7972
+ * provider so the suite never needs a real ffmpeg.
7973
+ */
7974
+ /** CRF ladder tried until the output fits. Higher = smaller, uglier. */
7975
+ var EXPORT_SHRINK_CRF_LADDER = [
7976
+ 28,
7977
+ 32,
7978
+ 36,
7979
+ 40
7980
+ ];
7981
+ /**
7982
+ * ffmpeg argv that re-encodes a rendered MP4 for notification delivery.
7983
+ *
7984
+ * - `libx264` + `veryfast`: the morning drain must not pin the hub the way
7985
+ * the original night render did.
7986
+ * - `-an`: a timelapse has no audio worth keeping, and dropping it is free
7987
+ * size.
7988
+ * - `640:-2`: notification phones; matching the band the cheapest profile
7989
+ * already aims for.
7990
+ * - `+faststart`: the artifact plane serves the whole file; moov-first is
7991
+ * what a phone needs to start playing before the download finishes.
7992
+ */
7993
+ function buildExportShrinkArgs(input) {
7994
+ return [
7995
+ "-hide_banner",
7996
+ "-loglevel",
7997
+ "error",
7998
+ "-y",
7999
+ "-i",
8000
+ input.inputPath,
8001
+ "-vf",
8002
+ "scale=640:-2",
8003
+ "-c:v",
8004
+ "libx264",
8005
+ "-preset",
8006
+ "veryfast",
8007
+ "-crf",
8008
+ String(input.crf),
8009
+ "-an",
8010
+ "-movflags",
8011
+ "+faststart",
8012
+ input.outputPath
8013
+ ];
8014
+ }
8015
+ /**
8016
+ * Pick the next CRF to try, or `null` when the ladder is exhausted.
8017
+ *
8018
+ * @param attempted — CRFs already tried (may be empty).
8019
+ */
8020
+ function nextShrinkCrf(attempted) {
8021
+ for (const crf of EXPORT_SHRINK_CRF_LADDER) if (!attempted.includes(crf)) return crf;
8022
+ return null;
8023
+ }
8024
+ /**
8025
+ * Re-encode `inputPath` down the CRF ladder until the file is ≤ `maxBytes`.
8026
+ *
8027
+ * @returns the fitted buffer, or `null` when every rung still overflows (or
8028
+ * ffmpeg refuses). Never throws — the caller treats `null` as "refuse
8029
+ * the inline read", same as the historical oversized gate.
8030
+ */
8031
+ async function shrinkExportToBound(inputPath, maxBytes, ports) {
8032
+ const attempted = [];
8033
+ for (;;) {
8034
+ const crf = nextShrinkCrf(attempted);
8035
+ if (crf === null) return null;
8036
+ attempted.push(crf);
8037
+ const outPath = `${ports.writePath}.crf${String(crf)}.mp4`;
8038
+ try {
8039
+ const ffmpeg = await ports.resolveFfmpeg();
8040
+ await runShrinkFfmpeg(ports.spawn, ffmpeg, buildExportShrinkArgs({
8041
+ inputPath,
8042
+ outputPath: outPath,
8043
+ crf
8044
+ }));
8045
+ if (await ports.statBytes(outPath) > maxBytes) {
8046
+ await ports.removeFile(outPath);
8047
+ continue;
8048
+ }
8049
+ const buf = await ports.readFile(outPath);
8050
+ await ports.removeFile(outPath);
8051
+ return buf;
8052
+ } catch {
8053
+ await ports.removeFile(outPath).catch(() => void 0);
8054
+ continue;
8055
+ }
8056
+ }
8057
+ }
8058
+ function runShrinkFfmpeg(spawn, ffmpegPath, args) {
8059
+ return new Promise((resolve, reject) => {
8060
+ const proc = spawn(ffmpegPath, args);
8061
+ let stderr = "";
8062
+ proc.stderr?.on("data", (chunk) => {
8063
+ stderr += chunk.toString("utf8");
8064
+ });
8065
+ proc.on("error", (...args) => {
8066
+ const err = args[0];
8067
+ reject(err instanceof Error ? err : new Error(String(err)));
8068
+ });
8069
+ proc.on("close", (...args) => {
8070
+ const code = args[0];
8071
+ if (code === 0) {
8072
+ resolve();
8073
+ return;
8074
+ }
8075
+ reject(/* @__PURE__ */ new Error(`export shrink ffmpeg exited ${String(code)}: ${stderr.slice(0, 400)}`));
8076
+ });
8077
+ });
8078
+ }
8079
+ //#endregion
7952
8080
  //#region src/recorder/playlist.ts
7953
8081
  /** A VOD variant playlist with an absolute wall-clock anchor per segment. */
7954
8082
  function buildVariantPlaylist(segments) {
@@ -18286,6 +18414,47 @@ function buildRecordingExportProvider(deps) {
18286
18414
  throw new Error(`recording-export: export "${exportId}" has no file on disk`, { cause: err });
18287
18415
  }
18288
18416
  if (size > 52428800) {
18417
+ const shrink = deps.shrinkToBound;
18418
+ if (shrink !== void 0) {
18419
+ deps.logger.info("export byte read: shrinking oversized file for inline delivery", {
18420
+ tags,
18421
+ meta: {
18422
+ exportId,
18423
+ bytes: size,
18424
+ maxBytes: RECORDING_EXPORT_MAX_READ_BYTES
18425
+ }
18426
+ });
18427
+ const shrunk = await shrink(file, RECORDING_EXPORT_MAX_READ_BYTES);
18428
+ if (shrunk !== null && shrunk.byteLength <= 52428800) {
18429
+ deps.logger.info("export byte read: shrink fitted under the inline ceiling", {
18430
+ tags,
18431
+ meta: {
18432
+ exportId,
18433
+ originalBytes: size,
18434
+ bytes: shrunk.byteLength,
18435
+ maxBytes: RECORDING_EXPORT_MAX_READ_BYTES
18436
+ }
18437
+ });
18438
+ return {
18439
+ base64: shrunk.toString("base64"),
18440
+ contentType: EXPORT_CONTENT_TYPE,
18441
+ name: exportFileName({
18442
+ id: rec.id,
18443
+ ...rec.options.title !== void 0 ? { title: rec.options.title } : {}
18444
+ }),
18445
+ bytes: shrunk.byteLength
18446
+ };
18447
+ }
18448
+ deps.logger.warn("export byte read: shrink could not fit under the inline ceiling", {
18449
+ tags,
18450
+ meta: {
18451
+ exportId,
18452
+ originalBytes: size,
18453
+ shrunkBytes: shrunk?.byteLength ?? null,
18454
+ maxBytes: RECORDING_EXPORT_MAX_READ_BYTES
18455
+ }
18456
+ });
18457
+ }
18289
18458
  deps.logger.warn("export byte read refused: file is too large to inline", {
18290
18459
  tags,
18291
18460
  meta: {
@@ -21805,6 +21974,20 @@ var RecorderV2Addon = class extends BaseAddon {
21805
21974
  downloadUrlFor: (id) => `/addon/${RECORDER_ADDON_ID}/${EXPORTS_PREFIX}/${id}.mp4`,
21806
21975
  statBytes: async (p) => (await promises.stat(p)).size,
21807
21976
  readFile: (p) => promises.readFile(p),
21977
+ shrinkToBound: async (inputPath, maxBytes) => {
21978
+ const writePath = path.join(this.exportsDirOrThrow(), ".shrink", `${path.basename(inputPath, ".mp4")}-${randomUUID()}`);
21979
+ await promises.mkdir(path.dirname(writePath), { recursive: true });
21980
+ return shrinkExportToBound(inputPath, maxBytes, {
21981
+ resolveFfmpeg: this.pinnedFfmpeg,
21982
+ spawn,
21983
+ writePath,
21984
+ readFile: (p) => promises.readFile(p),
21985
+ removeFile: async (p) => {
21986
+ await promises.rm(p, { force: true });
21987
+ },
21988
+ statBytes: async (p) => (await promises.stat(p)).size
21989
+ });
21990
+ },
21808
21991
  logger: this.ctx.logger.child("export")
21809
21992
  });
21810
21993
  try {
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-Bce5_ocm.js");
2
+ const require_dist = require("./dist-BHwYsqDW.js");
3
3
  let node_child_process = require("node:child_process");
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
2
- import { fn as errMsg } from "./dist-BK-pWZzI.mjs";
2
+ import { fn as errMsg } from "./dist-CYhfylbW.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -1,7 +1,7 @@
1
- import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DHNf76xj.mjs";
1
+ import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-mEJkrfvj.mjs";
2
2
  import { a as p, i as m, n as h, o as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
3
3
  import { n as y, r as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
4
- import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BAYDyjOG.mjs";
4
+ import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bm0eNLm_.mjs";
5
5
  import { n as C, t as w } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
7
7
  var T = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), E = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), D = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), O = (e) => {
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.292",
21
+ version: "1.2.302",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.222",
36
+ version: "1.2.228",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",