hyperframes 0.8.188 → 0.8.190

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 (167) hide show
  1. package/dist/{add-UORF6235.js → add-DBKN5H7H.js} +11 -11
  2. package/dist/{animatedGifPrep-23MLSEXF.js → animatedGifPrep-YVBKKLQY.js} +5 -5
  3. package/dist/api.js +13 -13
  4. package/dist/{asset-DOKDVHCW.js → asset-2OHQKUMQ.js} +2 -2
  5. package/dist/{assetCataloger-CD27MHEY.js → assetCataloger-O3QAUWIK.js} +5 -5
  6. package/dist/{audio-3NMZHNNU.js → audio-IR3JGCMM.js} +11 -11
  7. package/dist/{auth-AWKTSKGC.js → auth-J6TAJLGD.js} +1 -1
  8. package/dist/{autoUpdate-VBOI4CNU.js → autoUpdate-D7BJJUT2.js} +2 -2
  9. package/dist/{backgroundChecks-S4R454YU.js → backgroundChecks-AILHY2YZ.js} +3 -3
  10. package/dist/backgroundChecksWorker.js +3 -3
  11. package/dist/{batchRender-EGPYNDHK.js → batchRender-QZKFVWEI.js} +3 -3
  12. package/dist/{beats-YENEQUOA.js → beats-6LOEKZPB.js} +4 -4
  13. package/dist/{benchmark-THSJIQG7.js → benchmark-RJMHI3YW.js} +8 -8
  14. package/dist/{browser-BDMVQ6WJ.js → browser-Q3Q36FE3.js} +3 -3
  15. package/dist/{bundleWithLocalizedFonts-3QG722YD.js → bundleWithLocalizedFonts-RWHFMSFL.js} +2 -2
  16. package/dist/{capture-Z6BGCTK3.js → capture-2PNTGVYV.js} +8 -8
  17. package/dist/{capture-WM4XDMFX.js → capture-FO3ZZCT4.js} +2 -2
  18. package/dist/{captureCompositionFrame-EMHSAIBZ.js → captureCompositionFrame-WTMLWVEJ.js} +11 -11
  19. package/dist/{catalog-7TV6UNCG.js → catalog-LUZYPUYT.js} +11 -11
  20. package/dist/{catch-up-63IUDZRW.js → catch-up-TUTLUTFP.js} +4 -4
  21. package/dist/{check-7RHZL4DV.js → check-K2TBTJZS.js} +34 -33
  22. package/dist/{checkBrowser-PF56CT5X.js → checkBrowser-JT7CXDT6.js} +29 -30
  23. package/dist/{chunk-I7C5GUEQ.js → chunk-2FFOLLU2.js} +3 -3
  24. package/dist/{chunk-OKSYDQHY.js → chunk-3QLT727N.js} +3 -3
  25. package/dist/{chunk-NFHRGHCJ.js → chunk-45U3KY7Z.js} +4 -4
  26. package/dist/{chunk-MJKUCR3C.js → chunk-56IDMP37.js} +2 -2
  27. package/dist/{chunk-YV4WT7CJ.js → chunk-56IOFIYB.js} +12 -1
  28. package/dist/{chunk-HKUZCSNZ.js → chunk-6HYHHMX3.js} +2 -2
  29. package/dist/{chunk-46VJ2UAV.js → chunk-76ITJYXX.js} +5 -5
  30. package/dist/{chunk-YZ73IPW2.js → chunk-7JHTNUF2.js} +2 -2
  31. package/dist/{chunk-2WUDFZCR.js → chunk-A5YHWTSP.js} +2 -2
  32. package/dist/{chunk-HPQSUPR7.js → chunk-AYFF3GX6.js} +1 -1
  33. package/dist/{chunk-W2KGJHUL.js → chunk-B7JFAXDP.js} +2 -2
  34. package/dist/{chunk-LWYZN7HS.js → chunk-BT64TTSE.js} +219 -79
  35. package/dist/{chunk-ZIRIDSDB.js → chunk-BWPY64WA.js} +1 -1
  36. package/dist/{chunk-MTPPEEYZ.js → chunk-CWVSJA52.js} +16 -8
  37. package/dist/{chunk-BWEZDN6U.js → chunk-CWYS2UNQ.js} +1 -1
  38. package/dist/chunk-DIV37NUE.js +3896 -0
  39. package/dist/{chunk-DA4TQFSF.js → chunk-DRTIIAIZ.js} +1 -1
  40. package/dist/{chunk-IJYR4TTA.js → chunk-DX7VEUBO.js} +1 -1
  41. package/dist/{chunk-V6NM4KL3.js → chunk-E7DXKMKU.js} +2 -2
  42. package/dist/{chunk-LWY3PFJ7.js → chunk-EPOYPP36.js} +2 -2
  43. package/dist/{chunk-BU4HRISA.js → chunk-EQF6TVWB.js} +91 -24
  44. package/dist/{chunk-KDBPZOXK.js → chunk-EU5HTY3E.js} +11 -11
  45. package/dist/{chunk-KY7375C4.js → chunk-EUUHVDMN.js} +1 -1
  46. package/dist/{chunk-6HYPEIH7.js → chunk-F6DNM676.js} +2 -2
  47. package/dist/{chunk-2DHCCBCN.js → chunk-FANFO5HI.js} +9 -9
  48. package/dist/{chunk-FWVILDOZ.js → chunk-FUHI34T4.js} +3 -3
  49. package/dist/{chunk-GYBPAKBM.js → chunk-FVD44EQX.js} +53 -31
  50. package/dist/{chunk-MXXX6WVC.js → chunk-GGVGOY6M.js} +1 -1
  51. package/dist/{chunk-XPVN4R66.js → chunk-HJZ5CPZA.js} +31 -7
  52. package/dist/{chunk-NMOTNNP2.js → chunk-IARWN467.js} +1 -1
  53. package/dist/{chunk-OJOZXWLC.js → chunk-JB7LML5B.js} +3 -3
  54. package/dist/{chunk-UF6RCYA5.js → chunk-JLSXXMRU.js} +1 -1
  55. package/dist/{chunk-JYC53CW4.js → chunk-K4664CDS.js} +97 -30
  56. package/dist/{chunk-T3OXKSRG.js → chunk-KNQQZRB2.js} +2 -2
  57. package/dist/{chunk-L243NE72.js → chunk-KUMUK6QS.js} +7 -7
  58. package/dist/{chunk-6HZ7TSJJ.js → chunk-L34DPWY4.js} +15 -11
  59. package/dist/{chunk-YCW345M4.js → chunk-OAPYPSEB.js} +357 -211
  60. package/dist/{chunk-6Q5CFUAP.js → chunk-OOAVKK2W.js} +1 -1
  61. package/dist/{chunk-FSTLXRIN.js → chunk-OTQXYTQZ.js} +1 -1
  62. package/dist/{chunk-44QWRPGE.js → chunk-R4QASDTN.js} +2 -2
  63. package/dist/{chunk-NFLZNSUZ.js → chunk-RBGNU7VD.js} +1 -1
  64. package/dist/{chunk-5DPV7LU3.js → chunk-RDHEG7RX.js} +2 -2
  65. package/dist/{chunk-FOLMMQF3.js → chunk-S2IEO6TH.js} +13 -13
  66. package/dist/{chunk-6OWNUXTU.js → chunk-S7C6EM7N.js} +1 -1
  67. package/dist/{chunk-KWSEIVR6.js → chunk-STJ2ANMF.js} +1 -1
  68. package/dist/{chunk-DLFAVRF3.js → chunk-SUSSMCWL.js} +1 -1
  69. package/dist/{chunk-U6RXZNZE.js → chunk-TMBUBLZB.js} +55 -23
  70. package/dist/{chunk-SMF6YAUL.js → chunk-TSQAPPRS.js} +1 -1
  71. package/dist/{chunk-PMXR3IUH.js → chunk-TTZTGXKG.js} +1 -1
  72. package/dist/{chunk-DG4ME554.js → chunk-UZ7RJDEC.js} +1 -1
  73. package/dist/{chunk-X556BGL7.js → chunk-WBUO6WH6.js} +1 -1
  74. package/dist/{chunk-IA2IDJAG.js → chunk-WCT6KGGT.js} +1 -1
  75. package/dist/{chunk-57XL23DE.js → chunk-WLG4OYH7.js} +1 -1
  76. package/dist/{chunk-JWRT2IXO.js → chunk-XBGEWNGQ.js} +1 -1
  77. package/dist/{chunk-B2P4JRUJ.js → chunk-XNLYMHX2.js} +1 -1
  78. package/dist/{chunk-PQLFJYGP.js → chunk-Y5L5I4US.js} +5 -5
  79. package/dist/{chunk-5OZI55AC.js → chunk-YT5VLUHM.js} +1 -1
  80. package/dist/{chunk-FH7WODJH.js → chunk-ZFFKWHXN.js} +8 -0
  81. package/dist/{clean-43O47OTN.js → clean-UVTWBPXK.js} +14 -14
  82. package/dist/cli.js +53 -53
  83. package/dist/{cloud-ZFWKFFSU.js → cloud-M42IPKFD.js} +4 -4
  84. package/dist/{cloudrun-XBBESKEU.js → cloudrun-CTNX64R6.js} +4 -4
  85. package/dist/{compare-OW4ELTFZ.js → compare-MQI7TG5M.js} +26 -26
  86. package/dist/{compiler-23MT6CX6.js → compiler-EJE3HLCS.js} +2 -2
  87. package/dist/{component-SDV7LWH6.js → component-QSYOSFVV.js} +3 -3
  88. package/dist/{compositions-DI5QZCEK.js → compositions-ITDFE7K4.js} +9 -9
  89. package/dist/{delete-QY2NSQ2G.js → delete-NTGTENKJ.js} +2 -2
  90. package/dist/{deploy-MJVC4EFY.js → deploy-AOJOXEYX.js} +1 -1
  91. package/dist/{dist-RFTGYT26.js → dist-NHW2JQCA.js} +4 -2
  92. package/dist/{doctor-7ZOECPHR.js → doctor-Q6DAYVSQ.js} +9 -9
  93. package/dist/{events-XVQQGECC.js → events-BQIBYPCF.js} +2 -2
  94. package/dist/{events-WIX4JM2K.js → events-PV3CDO4A.js} +3 -3
  95. package/dist/{feedback-GBYBR6UO.js → feedback-BZH26JQE.js} +6 -6
  96. package/dist/{ffmpeg-QGQBYMO3.js → ffmpeg-KU7RNT3U.js} +5 -5
  97. package/dist/{figma-2CMKUGJQ.js → figma-PBGZUEIL.js} +3 -3
  98. package/dist/fontLocalizeCli.js +7 -7
  99. package/dist/{get-RWCNUOHU.js → get-MM2JZ5ET.js} +2 -2
  100. package/dist/{gpuPolicy-UGWSN32D.js → gpuPolicy-DQE2CD5T.js} +5 -5
  101. package/dist/{grade-compare-J2MX7HZL.js → grade-compare-YTDQ7YXW.js} +27 -27
  102. package/dist/{help-SH3ID7MV.js → help-M4B2DN7E.js} +1 -1
  103. package/dist/{history-C7V5QH7J.js → history-ZW4UYKAJ.js} +7 -7
  104. package/dist/hyperframe.manifest.json +1 -1
  105. package/dist/hyperframe.runtime.iife.js +62 -62
  106. package/dist/hyperframes-player.global.js +3 -3
  107. package/dist/{info-ZN3JJ3IN.js → info-L7LQAXLK.js} +7 -7
  108. package/dist/{init-KRNGSRNH.js → init-HTSQ535A.js} +25 -25
  109. package/dist/{inspect-AYDZXTX7.js → inspect-EWF2THRR.js} +27 -27
  110. package/dist/{installer-N6AIK6XQ.js → installer-PQKH2S7C.js} +4 -4
  111. package/dist/{keyframes-2RFUCEDK.js → keyframes-X2QKJ4DP.js} +6 -6
  112. package/dist/{lambda-VW3CF6EE.js → lambda-BURM66GP.js} +6 -6
  113. package/dist/{layout-OE64APE7.js → layout-3Z3SJGZZ.js} +27 -27
  114. package/dist/{lint-UXJTX5WS.js → lint-OMNZ2K5J.js} +6 -6
  115. package/dist/{list-KOL6JPZJ.js → list-5ZSQEH6N.js} +2 -2
  116. package/dist/{login-MAQXOPDG.js → login-YNCQPHLA.js} +4 -4
  117. package/dist/{manager-3LZKRK7P.js → manager-N6XXRQN3.js} +6 -6
  118. package/dist/{media-treatment-COLSXXQJ.js → media-treatment-EOKBRJPY.js} +11 -11
  119. package/dist/{models-XXWMGOD7.js → models-6T4CCKUV.js} +9 -9
  120. package/dist/{motionShot-RECBNTBZ.js → motionShot-KXKMYZ3A.js} +16 -16
  121. package/dist/{normalize-audio-LKVGHZWR.js → normalize-audio-VWB7RHHA.js} +4 -4
  122. package/dist/{open-UEQVSRKH.js → open-32SQCNUP.js} +11 -11
  123. package/dist/{parakeetRunner-QVPCZ36U.js → parakeetRunner-BXW2WX6Q.js} +10 -10
  124. package/dist/{pipeline-5VSMQBKW.js → pipeline-V5ONFRCY.js} +6 -6
  125. package/dist/{play-MNEABYN4.js → play-BBL262JE.js} +17 -17
  126. package/dist/{preflight-ONJ74DWQ.js → preflight-LCNGQFNY.js} +6 -6
  127. package/dist/{present-GQ24GTYX.js → present-RY4HO4ZQ.js} +13 -13
  128. package/dist/{preview-ALL7O4RG.js → preview-BLLPCCLG.js} +16 -16
  129. package/dist/{producer-43L2UDF6.js → producer-QG4AQ35S.js} +1 -1
  130. package/dist/{projectConfig-VJXUXK7U.js → projectConfig-LN3NR5OM.js} +6 -6
  131. package/dist/{publication-L6E5NTFV.js → publication-TXMCS4KM.js} +2 -2
  132. package/dist/{publish-LCLBJEPR.js → publish-57VURD7Z.js} +7 -7
  133. package/dist/{remove-background-PDL4KPR4.js → remove-background-VEWVIO3A.js} +7 -7
  134. package/dist/{render-OPCRLZVL.js → render-4E7QDVOI.js} +12 -12
  135. package/dist/{render-7HVKJ7T4.js → render-SP7WAUB4.js} +5 -5
  136. package/dist/{render-J33ZHR6I.js → render-XMN3NJIQ.js} +27 -27
  137. package/dist/{render-batch-ZHHIGXU3.js → render-batch-7ULP5RIJ.js} +5 -5
  138. package/dist/{resolver-KNNIMMBM.js → resolver-D3XERO6U.js} +4 -4
  139. package/dist/{sherpa-GI62H3H5.js → sherpa-5B5G426W.js} +9 -9
  140. package/dist/skills/hyperframes/references/motion.md +1 -1
  141. package/dist/{skills-IUTD6VQO.js → skills-RXHRF7O7.js} +5 -5
  142. package/dist/{skillsUpdateCheck-XZE7MJG6.js → skillsUpdateCheck-JOTDZJAE.js} +3 -3
  143. package/dist/{snapshot-XEXTHLZP.js → snapshot-DN4MP4VN.js} +31 -31
  144. package/dist/{src-6STRGCDC.js → src-W6Y5ZYRQ.js} +6 -6
  145. package/dist/{src-ZPIV4PGX.js → src-XSDYRKLG.js} +6 -4
  146. package/dist/{staticProjectServer-HJLT7MHE.js → staticProjectServer-JMFGTPMX.js} +15 -13
  147. package/dist/studio/assets/{hyperframes-player-DwWIKKZG.js → hyperframes-player-0HXJPmYC.js} +1 -1
  148. package/dist/studio/assets/{index-CESvLzz0.css → index-B3vk3hfU.css} +1 -1
  149. package/dist/studio/assets/{index-M8lEpS0Q.js → index-BYw97F07.js} +127 -127
  150. package/dist/studio/assets/{index-Cjwz7_13.js → index-DsSfhjUS.js} +1 -1
  151. package/dist/studio/index.html +2 -2
  152. package/dist/{studio-api-OBHEB5ND.js → studio-api-SZ4OMFPN.js} +7 -7
  153. package/dist/{studioServer-KRIZFYML.js → studioServer-LWO33H7D.js} +44 -44
  154. package/dist/{telemetry-N5OOVBCP.js → telemetry-K3AFDKOP.js} +3 -3
  155. package/dist/{timeline-YUFH65OG.js → timeline-NYQOAQDH.js} +15 -15
  156. package/dist/{tokens-K4VYLSWJ.js → tokens-K7YWUVOK.js} +2 -2
  157. package/dist/{transcribe-GMQXKDYN.js → transcribe-ANPUQDZD.js} +8 -8
  158. package/dist/{transcribe-CY4CVO54.js → transcribe-QAUMODXM.js} +14 -14
  159. package/dist/{updateCheck-2625RXK4.js → updateCheck-RNR4HMDI.js} +2 -2
  160. package/dist/{upgrade-6ZY5MSBQ.js → upgrade-MKUGZZPA.js} +2 -2
  161. package/dist/{urlDownloader-PVJWG7V3.js → urlDownloader-5USPXLCS.js} +4 -4
  162. package/dist/{validate-XSRHEE7E.js → validate-OJHPDLC5.js} +21 -21
  163. package/dist/{video-MBFHC54F.js → video-BCKRR6LU.js} +5 -5
  164. package/dist/warmCheck.js +34 -35
  165. package/package.json +1 -1
  166. package/dist/chunk-BPX7LZG6.js +0 -3896
  167. package/dist/chunk-KANNRHY7.js +0 -53
@@ -26,7 +26,7 @@ import {
26
26
  renderFrameIdForRenderId,
27
27
  resolveAuthoredTimingWindow,
28
28
  toFps
29
- } from "./chunk-YV4WT7CJ.js";
29
+ } from "./chunk-56IOFIYB.js";
30
30
  import {
31
31
  AUDIO_GROUP_RENDER_ID_ATTR,
32
32
  COLOR_GRADING_SOURCE_HIDDEN_ATTR,
@@ -63,7 +63,7 @@ import {
63
63
  sourceTimeAt,
64
64
  timeAtSourceTime,
65
65
  volumeSegmentSampleCount
66
- } from "./chunk-BPX7LZG6.js";
66
+ } from "./chunk-DIV37NUE.js";
67
67
  import {
68
68
  clampAudioGain,
69
69
  clipGain,
@@ -1090,7 +1090,6 @@ var BEGINFRAME_ONLY_FLAGS = /* @__PURE__ */ new Set([
1090
1090
  function stripBeginFrameFlags(args) {
1091
1091
  return args.filter((a) => !BEGINFRAME_ONLY_FLAGS.has(a));
1092
1092
  }
1093
- var BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS = 10;
1094
1093
  var BEGINFRAME_PROBE_TIMEOUT_MS = 2e3;
1095
1094
  var BEGINFRAME_PROBE_CLEANUP_TIMEOUT_MS = 250;
1096
1095
  async function awaitBeforeDeadline(operation, deadline, label) {
@@ -1174,7 +1173,8 @@ async function probeBeginFrameSupport(browser, timeoutMs = BEGINFRAME_PROBE_TIME
1174
1173
  let bytes = Buffer.alloc(0);
1175
1174
  let isPng = false;
1176
1175
  let attempts = 0;
1177
- for (attempts = 1; attempts <= BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS; attempts += 1) {
1176
+ while (!isPng && Date.now() < deadline) {
1177
+ attempts += 1;
1178
1178
  const response = await awaitBeforeDeadline(
1179
1179
  client.send("HeadlessExperimental.beginFrame", {
1180
1180
  frameTimeTicks: 1e3 + (attempts - 1) * 33,
@@ -1187,7 +1187,7 @@ async function probeBeginFrameSupport(browser, timeoutMs = BEGINFRAME_PROBE_TIME
1187
1187
  const screenshot = response.screenshotData ?? "";
1188
1188
  bytes = screenshot ? Buffer.from(screenshot, "base64") : Buffer.alloc(0);
1189
1189
  isPng = bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71;
1190
- if (isPng) break;
1190
+ if (isPng || Date.now() + 10 >= deadline) break;
1191
1191
  await awaitBeforeDeadline(
1192
1192
  new Promise((resolveDelay) => setTimeout(resolveDelay, 10)),
1193
1193
  deadline,
@@ -1196,7 +1196,7 @@ async function probeBeginFrameSupport(browser, timeoutMs = BEGINFRAME_PROBE_TIME
1196
1196
  }
1197
1197
  if (!isPng) {
1198
1198
  throw new Error(
1199
- `beginFrame screenshot returned ${bytes.length} bytes after ${BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS} attempts with signature ${bytes.length >= 4 ? bytes.subarray(0, 4).toString("hex") : "<empty>"}`
1199
+ `beginFrame screenshot returned ${bytes.length} bytes after ${attempts} attempts in ${Date.now() - started}ms with signature ${bytes.length >= 4 ? bytes.subarray(0, 4).toString("hex") : "<empty>"}`
1200
1200
  );
1201
1201
  }
1202
1202
  await awaitBeforeDeadline(client.detach(), deadline, "CDP detach").catch(() => {
@@ -1691,6 +1691,24 @@ identically under Docker. Consider:
1691
1691
  import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
1692
1692
  import { join as join3 } from "path";
1693
1693
 
1694
+ // ../engine/src/services/renderFrameVideoHold.ts
1695
+ function holdRenderFrameVideos(ids, renderIdAttr) {
1696
+ const held = new Set(ids);
1697
+ window.__HF_RENDER_FRAME_VIDEO_IDS = ids;
1698
+ const hold = (el) => {
1699
+ if (el instanceof HTMLVideoElement && held.has(el.getAttribute(renderIdAttr) || el.id))
1700
+ el.preload = "none";
1701
+ };
1702
+ new MutationObserver((records) => {
1703
+ for (const record2 of records)
1704
+ for (const node of record2.addedNodes) {
1705
+ if (node.nodeType !== 1) continue;
1706
+ hold(node);
1707
+ for (const video of node.querySelectorAll("video")) hold(video);
1708
+ }
1709
+ }).observe(document, { childList: true, subtree: true });
1710
+ }
1711
+
1694
1712
  // ../engine/src/services/drawElementCaptureError.ts
1695
1713
  var DrawElementCaptureError = class extends Error {
1696
1714
  frameIndex;
@@ -2548,15 +2566,17 @@ async function readPageMotionBlur(page) {
2548
2566
  const report = api && typeof api === "object" ? Reflect.get(api, "report") : null;
2549
2567
  const got = typeof report === "function" ? report() : null;
2550
2568
  if (!got || typeof got !== "object") return null;
2569
+ const tier = Reflect.get(got, "tier");
2551
2570
  return {
2552
2571
  state: String(Reflect.get(got, "state")),
2553
2572
  code: String(Reflect.get(got, "reasonCode")),
2554
2573
  movers: Number(Reflect.get(got, "movers")),
2555
- failedFrames: Number(Reflect.get(got, "failedFrames")) || 0
2574
+ failedFrames: Number(Reflect.get(got, "failedFrames")) || 0,
2575
+ tier: typeof tier === "string" ? tier : null
2556
2576
  };
2557
2577
  });
2558
2578
  }
2559
- var formatPageMotionBlur = (r) => `[motion-blur] ${r.state} reasonCode=${r.code} movers=${r.movers} failedFrames=${r.failedFrames}`;
2579
+ var formatPageMotionBlur = (r) => `[motion-blur] ${r.state} reasonCode=${r.code} movers=${r.movers} failedFrames=${r.failedFrames}${r.tier ? ` tier=${r.tier}` : ""}`;
2560
2580
  async function describePageMotionBlur(page) {
2561
2581
  const r = await readPageMotionBlur(page);
2562
2582
  return r && formatPageMotionBlur(r);
@@ -5488,6 +5508,13 @@ async function constructCaptureSession(input) {
5488
5508
  }
5489
5509
  });
5490
5510
  await installMediaRenderIdBridge(page);
5511
+ if (options.skipReadinessVideoIds?.length) {
5512
+ await page.evaluateOnNewDocument(
5513
+ holdRenderFrameVideos,
5514
+ options.skipReadinessVideoIds,
5515
+ MEDIA_RENDER_ID_ATTR
5516
+ );
5517
+ }
5491
5518
  if (useDrawElement) {
5492
5519
  await page.evaluateOnNewDocument(instrumentAcceleratedCanvases);
5493
5520
  }
@@ -8993,6 +9020,12 @@ async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSecond
8993
9020
  };
8994
9021
  }
8995
9022
 
9023
+ // ../engine/src/services/softwareEncoderThreads.ts
9024
+ import { availableParallelism } from "os";
9025
+ function appendSoftwareEncoderThreadsArg(args, parallelism = availableParallelism()) {
9026
+ args.push("-threads", String(Math.max(1, Math.floor(parallelism))));
9027
+ }
9028
+
8996
9029
  // ../engine/src/utils/renderProvenance.ts
8997
9030
  import { readFileSync as readFileSync2 } from "fs";
8998
9031
  import { dirname, join as join5 } from "path";
@@ -9217,6 +9250,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
9217
9250
  } else {
9218
9251
  const encoderName = codec === "h264" ? "libx264" : "libx265";
9219
9252
  args.push("-c:v", encoderName, "-preset", preset);
9253
+ appendSoftwareEncoderThreadsArg(args);
9220
9254
  if (bitrate) args.push("-b:v", bitrate);
9221
9255
  else args.push("-crf", String(quality));
9222
9256
  const gop = resolveLockedGopSize(options);
@@ -9921,6 +9955,7 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
9921
9955
  } else {
9922
9956
  const encoderName = codec === "h264" ? "libx264" : "libx265";
9923
9957
  args.push("-c:v", encoderName, "-preset", preset);
9958
+ appendSoftwareEncoderThreadsArg(args);
9924
9959
  if (bitrate) args.push("-b:v", bitrate);
9925
9960
  else args.push("-crf", String(quality));
9926
9961
  const lockedGop = resolveLockedGopSize(options);
@@ -12472,6 +12507,7 @@ function resolveVideoExtractionDuration(video, metadata, timelineEnd) {
12472
12507
  return resolveVideoExtractionWindow(video, metadata, timelineEnd).durationSeconds;
12473
12508
  }
12474
12509
  var ALPHA_CAPABLE_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8", "prores"]);
12510
+ var SIDECAR_ALPHA_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8"]);
12475
12511
  function codecMayHaveAlpha(codec) {
12476
12512
  return ALPHA_CAPABLE_CODECS.has((codec ?? "").toLowerCase());
12477
12513
  }
@@ -12479,7 +12515,8 @@ function decoderForCodec(codec) {
12479
12515
  return alphaDecoderFor(codec) ?? (codec ?? "").toLowerCase();
12480
12516
  }
12481
12517
  function resolveFrameFormat(metadata, requested) {
12482
- if (metadata.hasAlpha || codecMayHaveAlpha(metadata.videoCodec)) return "png";
12518
+ if (metadata.hasAlpha || SIDECAR_ALPHA_CODECS.has((metadata.videoCodec ?? "").toLowerCase()))
12519
+ return "png";
12483
12520
  if (requested === "png" || requested === "jpg") return requested;
12484
12521
  return "jpg";
12485
12522
  }
@@ -14011,6 +14048,9 @@ function clampFadesToDuration(fades, duration) {
14011
14048
  return { fadeIn: fadeIn * scale, fadeOut: fadeOut * scale };
14012
14049
  }
14013
14050
 
14051
+ // ../engine/src/services/audioMixer.ts
14052
+ import { cpus as cpus2 } from "os";
14053
+
14014
14054
  // ../engine/src/services/aacEncode.ts
14015
14055
  import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync5, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "fs";
14016
14056
  import { dirname as dirname5, join as join11 } from "path";
@@ -15126,6 +15166,7 @@ function escapeExpressionCommas(expression) {
15126
15166
  function legacyFilterScriptOptionIsUnsupported(stderr) {
15127
15167
  return /filter_complex_script/i.test(stderr) && /(?:unrecognized option|option (?:was )?not found)/i.test(stderr);
15128
15168
  }
15169
+ var inPrepareSlot = createConcurrencyLimit(Math.max(2, Math.floor(cpus2().length / 2)));
15129
15170
  var MAX_VOLUME_SEGMENTS = 32;
15130
15171
  var VOLUME_SIMPLIFY_EPSILON = 5e-3;
15131
15172
  var STEREO_CHANNEL_FILTER = "pan=stereo|FL=FL+FC|FR=FR+FC";
@@ -15239,7 +15280,8 @@ function boundedDetail(message, maxLength = 2e3) {
15239
15280
  /(^|[\s"'(])\/.+?(?=:\s[A-Z]|\s(?:ENOENT|EACCES|EPERM)\b|\r?$)/gm,
15240
15281
  "$1<redacted-path>"
15241
15282
  );
15242
- return redacted.length <= maxLength ? redacted : `${redacted.slice(0, maxLength - 1)}\u2026`;
15283
+ const head = Math.floor(maxLength / 4);
15284
+ return redacted.length <= maxLength ? redacted : `${redacted.slice(0, head)}\u2026${redacted.slice(-(maxLength - head - 1))}`;
15243
15285
  }
15244
15286
  function probeFailure(message, elementId) {
15245
15287
  const unavailable = /(?:not found|ENOENT|spawn)/i.test(message);
@@ -15303,7 +15345,7 @@ function ffmpegFailure(stage, result, timeoutMs, elementId) {
15303
15345
  elementId,
15304
15346
  subprocess: audioSubprocessEvidence(result),
15305
15347
  detail: boundedDetail(
15306
- result.error?.message ? `${describeFfmpegFailure(result, timeoutMs)}: ${result.error.message}` : describeFfmpegFailure(result, timeoutMs)
15348
+ `(ran ${result.durationMs} ms, limit ${timeoutMs} ms) ` + (result.error?.message ? `${describeFfmpegFailure(result, timeoutMs)}: ${result.error.message}` : describeFfmpegFailure(result, timeoutMs))
15307
15349
  )
15308
15350
  };
15309
15351
  }
@@ -15500,8 +15542,13 @@ async function generateSilence(outputPath, duration, signal, config) {
15500
15542
  failure
15501
15543
  };
15502
15544
  }
15503
- async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config) {
15504
- const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
15545
+ var MIX_TIMEOUT_MS_PER_FILM_SECOND = 100;
15546
+ function mixProcessTimeout(config, totalDuration) {
15547
+ const configured = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
15548
+ return Math.max(configured, Math.ceil(totalDuration * MIX_TIMEOUT_MS_PER_FILM_SECOND));
15549
+ }
15550
+ async function mixAudioTracksFlat(tracks, outputPath, totalDuration, signal, config) {
15551
+ const ffmpegProcessTimeout = mixProcessTimeout(config, totalDuration);
15505
15552
  const masterOutputGain = config?.audioGain ?? DEFAULT_CONFIG.audioGain;
15506
15553
  if (tracks.length === 0) {
15507
15554
  const result2 = await generateSilence(outputPath, totalDuration, signal, config);
@@ -15621,8 +15668,8 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
15621
15668
  function groupNormalizeOptionUnsupported(stderr) {
15622
15669
  return /normalize/i.test(stderr) && /(?:unrecognized option|option (?:was )?not found|no option name)/i.test(stderr);
15623
15670
  }
15624
- async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal, config) {
15625
- const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
15671
+ async function mixGroupMembersFlat(memberTracks, outputPath, totalDuration, signal, config) {
15672
+ const ffmpegProcessTimeout = mixProcessTimeout(config, totalDuration);
15626
15673
  const outputDir = dirname7(outputPath);
15627
15674
  if (!existsSync11(outputDir)) mkdirSync7(outputDir, { recursive: true });
15628
15675
  const buildInputFilters = (ignoreKeyframes, padMembers) => memberTracks.map((track, i) => {
@@ -15707,6 +15754,100 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
15707
15754
  }
15708
15755
  return { success: true, degradedAutomation };
15709
15756
  }
15757
+ var MAX_MIX_INPUTS = 96;
15758
+ async function sumInSlices(tracks, scratchDir, totalDuration, signal, config) {
15759
+ const wavTrack = (id, srcPath) => ({
15760
+ id,
15761
+ srcPath,
15762
+ start: 0,
15763
+ end: totalDuration,
15764
+ mediaStart: 0,
15765
+ duration: totalDuration,
15766
+ volume: 1
15767
+ });
15768
+ let running = null;
15769
+ let degradedAutomation = false;
15770
+ for (let at = 0; at < tracks.length; at += MAX_MIX_INPUTS) {
15771
+ const id = `sum-${at / MAX_MIX_INPUTS}`;
15772
+ const slicePath = join14(scratchDir, `${id}-slice.wav`);
15773
+ const slice = await mixGroupMembersFlat(
15774
+ tracks.slice(at, at + MAX_MIX_INPUTS),
15775
+ slicePath,
15776
+ totalDuration,
15777
+ signal,
15778
+ config
15779
+ );
15780
+ if (!slice.success) return slice;
15781
+ degradedAutomation ||= slice.degradedAutomation === true;
15782
+ if (!running) {
15783
+ running = wavTrack(id, slicePath);
15784
+ continue;
15785
+ }
15786
+ const foldedPath = join14(scratchDir, `${id}.wav`);
15787
+ const folded = await mixGroupMembersFlat(
15788
+ [running, wavTrack(`${id}-slice`, slicePath)],
15789
+ foldedPath,
15790
+ totalDuration,
15791
+ signal,
15792
+ config
15793
+ );
15794
+ rmSync8(running.srcPath, { force: true });
15795
+ rmSync8(slicePath, { force: true });
15796
+ if (!folded.success) return folded;
15797
+ running = wavTrack(id, foldedPath);
15798
+ }
15799
+ if (!running) throw new Error("[audioMixer] sumInSlices needs at least one track");
15800
+ return { success: true, track: running, degradedAutomation };
15801
+ }
15802
+ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal, config) {
15803
+ if (memberTracks.length <= MAX_MIX_INPUTS) {
15804
+ return mixGroupMembersFlat(memberTracks, outputPath, totalDuration, signal, config);
15805
+ }
15806
+ mkdirSync7(dirname7(outputPath), { recursive: true });
15807
+ const scratchDir = mkdtempSync7(join14(dirname7(outputPath), ".mix-batches-"));
15808
+ try {
15809
+ const summed = await sumInSlices(memberTracks, scratchDir, totalDuration, signal, config);
15810
+ if (!summed.success) return summed;
15811
+ renameSync3(summed.track.srcPath, outputPath);
15812
+ return { success: true, degradedAutomation: summed.degradedAutomation };
15813
+ } finally {
15814
+ rmSync8(scratchDir, { recursive: true, force: true });
15815
+ }
15816
+ }
15817
+ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config) {
15818
+ if (tracks.length <= MAX_MIX_INPUTS) {
15819
+ return mixAudioTracksFlat(tracks, outputPath, totalDuration, signal, config);
15820
+ }
15821
+ mkdirSync7(dirname7(outputPath), { recursive: true });
15822
+ const scratchDir = mkdtempSync7(join14(dirname7(outputPath), ".mix-batches-"));
15823
+ try {
15824
+ const summed = await sumInSlices(tracks, scratchDir, totalDuration, signal, config);
15825
+ if (!summed.success) {
15826
+ return {
15827
+ success: false,
15828
+ outputPath,
15829
+ durationMs: 0,
15830
+ tracksProcessed: 0,
15831
+ error: summed.error,
15832
+ failures: [summed.failure]
15833
+ };
15834
+ }
15835
+ const mixed = await mixAudioTracksFlat(
15836
+ [summed.track],
15837
+ outputPath,
15838
+ totalDuration,
15839
+ signal,
15840
+ config
15841
+ );
15842
+ return mixed.success ? {
15843
+ ...mixed,
15844
+ tracksProcessed: tracks.length,
15845
+ error: summed.degradedAutomation ? "Volume automation exceeded this ffmpeg build's expression limits; rendered at base volume" : mixed.error
15846
+ } : mixed;
15847
+ } finally {
15848
+ rmSync8(scratchDir, { recursive: true, force: true });
15849
+ }
15850
+ }
15710
15851
  async function mixThroughFloatSum(tracks, masterChain, outputPath, workDir, totalDuration, signal, config) {
15711
15852
  const sumPath = join14(workDir, "master-sum.wav");
15712
15853
  const sum = await mixAudioTracks(tracks, sumPath, totalDuration, signal, config);
@@ -15752,216 +15893,220 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
15752
15893
  else signal.addEventListener("abort", () => internalController.abort(), { once: true });
15753
15894
  }
15754
15895
  await Promise.all(
15755
- elements.map(async (element, index) => {
15756
- if (effectiveSignal.aborted) {
15757
- failures.push({
15758
- stage: "cancelled",
15759
- reason: "cancelled",
15760
- owner: "user",
15761
- retryable: false,
15762
- elementId: element.id,
15763
- detail: boundedDetail(`Cancelled audio element ${element.id}`)
15764
- });
15765
- return;
15766
- }
15767
- try {
15768
- let srcPath = element.src;
15769
- let readInPlace = false;
15770
- if (!isHttpUrl(srcPath)) {
15771
- srcPath = resolveProjectRelativeSrc(element.src, baseDir, compiledDir);
15772
- const remoteUrl = config?.urlByAbsentLocalPath?.get(srcPath);
15773
- if (remoteUrl) {
15774
- srcPath = remoteUrl;
15775
- readInPlace = true;
15776
- }
15777
- }
15778
- if (isHttpUrl(srcPath) && !readInPlace) {
15779
- try {
15780
- srcPath = await downloadToTemp(
15781
- srcPath,
15782
- workDir,
15783
- void 0,
15784
- effectiveSignal,
15785
- void 0,
15786
- {
15787
- onTelemetry: writeUrlDownloadTelemetry
15788
- }
15789
- );
15790
- } catch (err) {
15791
- failures.push(downloadFailure(err, element.id));
15792
- return;
15793
- }
15794
- }
15795
- if (!readInPlace && !existsSync11(srcPath)) {
15896
+ elements.map(
15897
+ (element, index) => inPrepareSlot(async () => {
15898
+ if (effectiveSignal.aborted) {
15796
15899
  failures.push({
15797
- stage: "source",
15798
- reason: "source_not_found",
15900
+ stage: "cancelled",
15901
+ reason: "cancelled",
15799
15902
  owner: "user",
15800
15903
  retryable: false,
15801
15904
  elementId: element.id,
15802
- detail: boundedDetail(missingSourceMessage(element.id, element.src, baseDir, srcPath))
15905
+ detail: boundedDetail(`Cancelled audio element ${element.id}`)
15803
15906
  });
15804
15907
  return;
15805
15908
  }
15806
- if (await isNotMediaPayload(srcPath)) {
15807
- failures.push({
15808
- stage: "source",
15809
- reason: "invalid_media",
15810
- owner: "user",
15811
- retryable: false,
15812
- elementId: element.id,
15813
- detail: boundedDetail(
15814
- `Audio element ${element.id} source is a text document (HTML/XML/JSON), not media`
15815
- )
15816
- });
15817
- return;
15818
- }
15819
- if (element.end - element.start <= 0) {
15820
- let metadata;
15821
- try {
15822
- metadata = await extractAudioMetadata(
15909
+ try {
15910
+ let srcPath = element.src;
15911
+ let readInPlace = false;
15912
+ if (!isHttpUrl(srcPath)) {
15913
+ srcPath = resolveProjectRelativeSrc(element.src, baseDir, compiledDir);
15914
+ const remoteUrl = config?.urlByAbsentLocalPath?.get(srcPath);
15915
+ if (remoteUrl) {
15916
+ srcPath = remoteUrl;
15917
+ readInPlace = true;
15918
+ }
15919
+ }
15920
+ if (isHttpUrl(srcPath) && !readInPlace) {
15921
+ try {
15922
+ srcPath = await downloadToTemp(
15923
+ srcPath,
15924
+ workDir,
15925
+ void 0,
15926
+ effectiveSignal,
15927
+ void 0,
15928
+ {
15929
+ onTelemetry: writeUrlDownloadTelemetry
15930
+ }
15931
+ );
15932
+ } catch (err) {
15933
+ failures.push(downloadFailure(err, element.id));
15934
+ return;
15935
+ }
15936
+ }
15937
+ if (!readInPlace && !existsSync11(srcPath)) {
15938
+ failures.push({
15939
+ stage: "source",
15940
+ reason: "source_not_found",
15941
+ owner: "user",
15942
+ retryable: false,
15943
+ elementId: element.id,
15944
+ detail: boundedDetail(
15945
+ missingSourceMessage(element.id, element.src, baseDir, srcPath)
15946
+ )
15947
+ });
15948
+ return;
15949
+ }
15950
+ if (await isNotMediaPayload(srcPath)) {
15951
+ failures.push({
15952
+ stage: "source",
15953
+ reason: "invalid_media",
15954
+ owner: "user",
15955
+ retryable: false,
15956
+ elementId: element.id,
15957
+ detail: boundedDetail(
15958
+ `Audio element ${element.id} source is a text document (HTML/XML/JSON), not media`
15959
+ )
15960
+ });
15961
+ return;
15962
+ }
15963
+ if (element.end - element.start <= 0) {
15964
+ let metadata;
15965
+ try {
15966
+ metadata = await extractAudioMetadata(
15967
+ srcPath,
15968
+ readInPlace ? { signal: effectiveSignal } : void 0
15969
+ );
15970
+ } catch (err) {
15971
+ failures.push(
15972
+ probeFailure(err instanceof Error ? err.message : String(err), element.id)
15973
+ );
15974
+ return;
15975
+ }
15976
+ const effectiveDuration = timeAtSourceTime(
15977
+ normalizeRateSpec(element.playbackRate),
15978
+ metadata.durationSeconds - element.mediaStart
15979
+ );
15980
+ element.end = element.start + (effectiveDuration > 0 ? effectiveDuration : metadata.durationSeconds);
15981
+ }
15982
+ let audioSrcPath = srcPath;
15983
+ if (element.type === "video") {
15984
+ const extractedPath = join14(workDir, `${element.id}-extracted.wav`);
15985
+ const extractResult = await extractAudioSegment(
15823
15986
  srcPath,
15824
- readInPlace ? { signal: effectiveSignal } : void 0
15987
+ extractedPath,
15988
+ "extract",
15989
+ element.mediaStart,
15990
+ element.end - element.start,
15991
+ element.playbackRate,
15992
+ effectiveSignal,
15993
+ config
15825
15994
  );
15826
- } catch (err) {
15827
- failures.push(
15828
- probeFailure(err instanceof Error ? err.message : String(err), element.id)
15995
+ if (!extractResult.success) {
15996
+ failures.push(
15997
+ extractResult.failure ? { ...extractResult.failure, elementId: element.id } : {
15998
+ stage: "extract",
15999
+ reason: "ffmpeg_failed",
16000
+ owner: "system",
16001
+ retryable: false,
16002
+ elementId: element.id,
16003
+ detail: boundedDetail(`Audio extract failed for element ${element.id}`)
16004
+ }
16005
+ );
16006
+ return;
16007
+ }
16008
+ audioSrcPath = extractedPath;
16009
+ } else {
16010
+ const trimmedPath = join14(workDir, `${element.id}-trimmed.wav`);
16011
+ const prepResult = await extractAudioSegment(
16012
+ srcPath,
16013
+ trimmedPath,
16014
+ "prepare",
16015
+ element.mediaStart,
16016
+ element.end - element.start,
16017
+ element.playbackRate,
16018
+ effectiveSignal,
16019
+ config
15829
16020
  );
15830
- return;
16021
+ if (!prepResult.success) {
16022
+ failures.push(
16023
+ prepResult.failure ? { ...prepResult.failure, elementId: element.id } : {
16024
+ stage: "prepare",
16025
+ reason: "ffmpeg_failed",
16026
+ owner: "system",
16027
+ retryable: false,
16028
+ elementId: element.id,
16029
+ detail: boundedDetail(`Audio prepare failed for element ${element.id}`)
16030
+ }
16031
+ );
16032
+ return;
16033
+ }
16034
+ audioSrcPath = trimmedPath;
15831
16035
  }
15832
- const effectiveDuration = timeAtSourceTime(
15833
- normalizeRateSpec(element.playbackRate),
15834
- metadata.durationSeconds - element.mediaStart
15835
- );
15836
- element.end = element.start + (effectiveDuration > 0 ? effectiveDuration : metadata.durationSeconds);
15837
- }
15838
- let audioSrcPath = srcPath;
15839
- if (element.type === "video") {
15840
- const extractedPath = join14(workDir, `${element.id}-extracted.wav`);
15841
- const extractResult = await extractAudioSegment(
15842
- srcPath,
15843
- extractedPath,
15844
- "extract",
15845
- element.mediaStart,
16036
+ const chain = element.fxChain ? parseAudioFxChain(element.fxChain) : void 0;
16037
+ const automation = element.automation ? resolveAutomation(parseAutomation(element.automation), chain) : null;
16038
+ const gainBeforeFx = chain !== void 0 && enabledAudioFxNodes(chain).length > 0;
16039
+ const laneKeyframes = automation ? volumeLaneKeyframes(
16040
+ automation,
16041
+ element.start,
15846
16042
  element.end - element.start,
15847
- element.playbackRate,
15848
- effectiveSignal,
15849
- config
15850
- );
15851
- if (!extractResult.success) {
15852
- failures.push(
15853
- extractResult.failure ? { ...extractResult.failure, elementId: element.id } : {
15854
- stage: "extract",
15855
- reason: "ffmpeg_failed",
15856
- owner: "system",
15857
- retryable: false,
15858
- elementId: element.id,
15859
- detail: boundedDetail(`Audio extract failed for element ${element.id}`)
16043
+ element.volume ?? 1,
16044
+ gainBeforeFx
16045
+ ) : null;
16046
+ const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
16047
+ const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
16048
+ const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
16049
+ keyframes: envelopeKeyframes,
16050
+ trackStart: element.start,
16051
+ baseVolume: element.volume ?? 1
16052
+ } : null;
16053
+ let bakedEnvelope = false;
16054
+ let tailSeconds = 0;
16055
+ if (chain) {
16056
+ tailSeconds = chainTailSeconds(chain, automation ?? void 0);
16057
+ const fxResult = await applyAudioFxChain(
16058
+ audioSrcPath,
16059
+ chain,
16060
+ join14(workDir, `${element.id}-fx.wav`),
16061
+ {
16062
+ trackId: element.id,
16063
+ signal: effectiveSignal,
16064
+ ...automation ? { automation } : {},
16065
+ ...envelope ? { envelope } : {},
16066
+ ...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
15860
16067
  }
15861
16068
  );
15862
- return;
16069
+ audioSrcPath = fxResult.path;
16070
+ bakedEnvelope = fxResult.envelopeBaked;
15863
16071
  }
15864
- audioSrcPath = extractedPath;
15865
- } else {
15866
- const trimmedPath = join14(workDir, `${element.id}-trimmed.wav`);
15867
- const prepResult = await extractAudioSegment(
15868
- srcPath,
15869
- trimmedPath,
15870
- "prepare",
15871
- element.mediaStart,
15872
- element.end - element.start,
15873
- element.playbackRate,
15874
- effectiveSignal,
15875
- config
15876
- );
15877
- if (!prepResult.success) {
15878
- failures.push(
15879
- prepResult.failure ? { ...prepResult.failure, elementId: element.id } : {
15880
- stage: "prepare",
15881
- reason: "ffmpeg_failed",
15882
- owner: "system",
15883
- retryable: false,
15884
- elementId: element.id,
15885
- detail: boundedDetail(`Audio prepare failed for element ${element.id}`)
15886
- }
16072
+ if (envelope && !bakedEnvelope) {
16073
+ bakedEnvelope = applyVolumeEnvelopeToWav(
16074
+ audioSrcPath,
16075
+ envelope.keyframes,
16076
+ envelope.trackStart,
16077
+ envelope.baseVolume
15887
16078
  );
15888
- return;
15889
16079
  }
15890
- audioSrcPath = trimmedPath;
15891
- }
15892
- const chain = element.fxChain ? parseAudioFxChain(element.fxChain) : void 0;
15893
- const automation = element.automation ? resolveAutomation(parseAutomation(element.automation), chain) : null;
15894
- const gainBeforeFx = chain !== void 0 && enabledAudioFxNodes(chain).length > 0;
15895
- const laneKeyframes = automation ? volumeLaneKeyframes(
15896
- automation,
15897
- element.start,
15898
- element.end - element.start,
15899
- element.volume ?? 1,
15900
- gainBeforeFx
15901
- ) : null;
15902
- const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
15903
- const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
15904
- const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
15905
- keyframes: envelopeKeyframes,
15906
- trackStart: element.start,
15907
- baseVolume: element.volume ?? 1
15908
- } : null;
15909
- let bakedEnvelope = false;
15910
- let tailSeconds = 0;
15911
- if (chain) {
15912
- tailSeconds = chainTailSeconds(chain, automation ?? void 0);
15913
- const fxResult = await applyAudioFxChain(
15914
- audioSrcPath,
15915
- chain,
15916
- join14(workDir, `${element.id}-fx.wav`),
15917
- {
15918
- trackId: element.id,
15919
- signal: effectiveSignal,
15920
- ...automation ? { automation } : {},
15921
- ...envelope ? { envelope } : {},
15922
- ...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
15923
- }
15924
- );
15925
- audioSrcPath = fxResult.path;
15926
- bakedEnvelope = fxResult.envelopeBaked;
15927
- }
15928
- if (envelope && !bakedEnvelope) {
15929
- bakedEnvelope = applyVolumeEnvelopeToWav(
15930
- audioSrcPath,
15931
- envelope.keyframes,
15932
- envelope.trackStart,
15933
- envelope.baseVolume
15934
- );
16080
+ const track = {
16081
+ id: element.id,
16082
+ srcPath: audioSrcPath,
16083
+ start: element.start,
16084
+ end: element.end,
16085
+ mediaStart: element.mediaStart,
16086
+ duration: element.end - element.start,
16087
+ // Gain is already in the samples when baked, so mix at unity.
16088
+ volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
16089
+ volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
16090
+ ...element.fadeIn ? { fadeIn: element.fadeIn } : {},
16091
+ ...element.fadeOut ? { fadeOut: element.fadeOut } : {},
16092
+ ...tailSeconds > 0 ? { tailSeconds } : {}
16093
+ };
16094
+ preparedTracks[index] = track;
16095
+ } catch (err) {
16096
+ if (err instanceof AudioFxRenderError) throw err;
16097
+ failures.push({
16098
+ stage: "internal",
16099
+ reason: "internal",
16100
+ owner: "system",
16101
+ retryable: false,
16102
+ elementId: element.id,
16103
+ detail: boundedDetail(
16104
+ `Audio processing failed for element ${element.id}: ${err instanceof Error ? err.message : String(err)}`
16105
+ )
16106
+ });
15935
16107
  }
15936
- const track = {
15937
- id: element.id,
15938
- srcPath: audioSrcPath,
15939
- start: element.start,
15940
- end: element.end,
15941
- mediaStart: element.mediaStart,
15942
- duration: element.end - element.start,
15943
- // Gain is already in the samples when baked, so mix at unity.
15944
- volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
15945
- volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
15946
- ...element.fadeIn ? { fadeIn: element.fadeIn } : {},
15947
- ...element.fadeOut ? { fadeOut: element.fadeOut } : {},
15948
- ...tailSeconds > 0 ? { tailSeconds } : {}
15949
- };
15950
- preparedTracks[index] = track;
15951
- } catch (err) {
15952
- if (err instanceof AudioFxRenderError) throw err;
15953
- failures.push({
15954
- stage: "internal",
15955
- reason: "internal",
15956
- owner: "system",
15957
- retryable: false,
15958
- elementId: element.id,
15959
- detail: boundedDetail(
15960
- `Audio processing failed for element ${element.id}: ${err instanceof Error ? err.message : String(err)}`
15961
- )
15962
- });
15963
- }
15964
- })
16108
+ }, effectiveSignal)
16109
+ )
15965
16110
  ).catch((err) => {
15966
16111
  internalController.abort();
15967
16112
  try {
@@ -16144,7 +16289,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
16144
16289
  }
16145
16290
 
16146
16291
  // ../engine/src/services/parallelCoordinator.ts
16147
- import { cpus as cpus2, freemem } from "os";
16292
+ import { cpus as cpus3, freemem } from "os";
16148
16293
  import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync6 } from "fs";
16149
16294
  import { copyFile, readFile as readFile2, rename } from "fs/promises";
16150
16295
  import { join as join16 } from "path";
@@ -16259,7 +16404,7 @@ var MIN_WORKERS = 1;
16259
16404
  var MAX_WORKER_DIAGNOSTIC_LINES = 8;
16260
16405
  var ABSOLUTE_MAX_WORKERS = 24;
16261
16406
  function defaultSafeMaxWorkers() {
16262
- return Math.max(6, Math.min(16, Math.floor(cpus2().length / 8)));
16407
+ return Math.max(6, Math.min(16, Math.floor(cpus3().length / 8)));
16263
16408
  }
16264
16409
  var MIN_FRAMES_PER_WORKER = 30;
16265
16410
  function shouldDisableBrowserPoolForParallelWorker({
@@ -16299,7 +16444,7 @@ function formatWorkerFailure(result) {
16299
16444
  return `${base}; diagnostics: ${diagnostics}`;
16300
16445
  }
16301
16446
  function computeWorkerSizing(totalFrames, requested, config) {
16302
- const cpuCount = cpus2().length;
16447
+ const cpuCount = cpus3().length;
16303
16448
  const totalMemoryMb = getSystemTotalMb();
16304
16449
  const heapLimitMb = Math.round(getHeapStatistics().heap_size_limit / (1024 * 1024));
16305
16450
  const heapBasedWorkers = Math.max(
@@ -16854,7 +16999,7 @@ async function mergeWorkerFrames(workDir, tasks, outputDir) {
16854
16999
  }
16855
17000
  function getSystemResources() {
16856
17001
  return {
16857
- cpuCores: cpus2().length,
17002
+ cpuCores: cpus3().length,
16858
17003
  totalMemoryMB: getSystemTotalMb(),
16859
17004
  freeMemoryMB: Math.round(freemem() / (1024 * 1024)),
16860
17005
  recommendedWorkers: calculateOptimalWorkers(1e3)
@@ -17396,6 +17541,7 @@ export {
17396
17541
  extractVideoMetadata,
17397
17542
  extractAudioMetadata,
17398
17543
  analyzeKeyframeIntervals,
17544
+ appendSoftwareEncoderThreadsArg,
17399
17545
  PROVENANCE_RENDERER_TAG,
17400
17546
  PROVENANCE_VERSION_TAG,
17401
17547
  PROVENANCE_RENDERER_NAME,