hyperframes 0.8.189 → 0.8.191

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 (181) hide show
  1. package/dist/{add-TIKUM2YX.js → add-S2ZQYG52.js} +11 -11
  2. package/dist/{animatedGifPrep-25X4G5IE.js → animatedGifPrep-3JA2CQ3U.js} +6 -6
  3. package/dist/api.js +14 -14
  4. package/dist/{asset-TPKLIP4R.js → asset-2CNTAPOC.js} +2 -2
  5. package/dist/{assetCataloger-PQAIV3FI.js → assetCataloger-5HXLXL7C.js} +6 -6
  6. package/dist/{audio-6RLPKB5X.js → audio-DZTSYHXS.js} +12 -12
  7. package/dist/{auth-O7QSTP6Y.js → auth-GX3M6TJ4.js} +1 -1
  8. package/dist/{autoUpdate-TIEJODP7.js → autoUpdate-EDIWE4Z5.js} +2 -2
  9. package/dist/{backgroundChecks-K2AXYDYG.js → backgroundChecks-VPUPJ7UI.js} +3 -3
  10. package/dist/backgroundChecksWorker.js +3 -3
  11. package/dist/{batchRender-ORVWYJAH.js → batchRender-JHIIDBJR.js} +3 -3
  12. package/dist/{beats-2VTB2ATR.js → beats-PZRVTRYW.js} +4 -4
  13. package/dist/{benchmark-NLNBL6YR.js → benchmark-22UFDW2N.js} +8 -8
  14. package/dist/{browser-M5UV6VJB.js → browser-VRDWYIUM.js} +3 -3
  15. package/dist/{bundleWithLocalizedFonts-EDD2XDS2.js → bundleWithLocalizedFonts-23QDKFSN.js} +2 -2
  16. package/dist/{capture-WHWEE5FE.js → capture-NFCBXHDE.js} +2 -2
  17. package/dist/{capture-JSYVNFKO.js → capture-TOCLXYMJ.js} +9 -9
  18. package/dist/{captureCompositionFrame-JANGQCIA.js → captureCompositionFrame-BEM6MF7F.js} +13 -11
  19. package/dist/{catalog-MPOOKNSZ.js → catalog-NXS35KM7.js} +11 -11
  20. package/dist/{catch-up-6KZV3UMZ.js → catch-up-6WJBUI2C.js} +4 -4
  21. package/dist/{check-G273NWK7.js → check-WVEBMWN5.js} +33 -33
  22. package/dist/{checkBrowser-G53E2ZQI.js → checkBrowser-RT45HJ36.js} +28 -28
  23. package/dist/{chunk-ZPMF6GAQ.js → chunk-2WNVHZHA.js} +11 -7
  24. package/dist/{chunk-TSFA2Y5C.js → chunk-3NRB7YXW.js} +3 -2
  25. package/dist/{chunk-44TJOEH5.js → chunk-3WJT2JCX.js} +32 -25
  26. package/dist/{chunk-VRQU564T.js → chunk-3XI7MIZB.js} +1 -1
  27. package/dist/{chunk-AYBKDHYD.js → chunk-7A65UCMI.js} +1 -1
  28. package/dist/{chunk-HUG47UEE.js → chunk-7FF36MAD.js} +1 -1
  29. package/dist/{chunk-6RA4TWGV.js → chunk-7LUJE3II.js} +1 -1
  30. package/dist/{chunk-GWYMAII7.js → chunk-7NKJS5KM.js} +1 -1
  31. package/dist/{chunk-37EPEFXM.js → chunk-7ZJEQY7Q.js} +1 -1
  32. package/dist/{chunk-AQ3L7DVB.js → chunk-ALEF2F5N.js} +2 -2
  33. package/dist/{chunk-74Y43RJZ.js → chunk-AQB5M53R.js} +3 -3
  34. package/dist/{chunk-M6FHSQZ3.js → chunk-BFFLKQVB.js} +3 -3
  35. package/dist/{chunk-CTF7BYCV.js → chunk-CINKQY6M.js} +3 -3
  36. package/dist/{chunk-BZQLTNMB.js → chunk-COETAXRN.js} +1 -1
  37. package/dist/{chunk-SGFMCNLY.js → chunk-COWQITWP.js} +1 -1
  38. package/dist/{chunk-3X6LH25W.js → chunk-CXMXJEVY.js} +1 -1
  39. package/dist/{chunk-NDBWI7OA.js → chunk-DOOSC34Z.js} +5 -5
  40. package/dist/{chunk-F45HHA3M.js → chunk-EFCKV7MY.js} +1 -1
  41. package/dist/{chunk-R5FS7GFU.js → chunk-FBDOHH3Q.js} +1 -1
  42. package/dist/{chunk-3OLOVRWS.js → chunk-GLG5JWLI.js} +1 -1
  43. package/dist/{chunk-WYCFMNU3.js → chunk-GM3WIOO6.js} +1 -1
  44. package/dist/{chunk-FJF5JBYU.js → chunk-HYW6HTAK.js} +1 -1
  45. package/dist/{chunk-KS4HMJJD.js → chunk-JGTRVG32.js} +1 -1
  46. package/dist/{chunk-Z2BW2WJS.js → chunk-JJJCFKMF.js} +1 -1
  47. package/dist/{chunk-4DZ3HBKF.js → chunk-JOLZ3BSL.js} +1 -1
  48. package/dist/{chunk-IFXOMIBP.js → chunk-JTPHFYZI.js} +3 -3
  49. package/dist/{chunk-TREMHDWT.js → chunk-JYSSPQDT.js} +56 -21
  50. package/dist/{chunk-OHXHBMF4.js → chunk-KAC2WR7P.js} +1 -1
  51. package/dist/{chunk-JZDVIDFH.js → chunk-KGMHMYWI.js} +2 -2
  52. package/dist/{chunk-2CYM34M7.js → chunk-KHCTBZGJ.js} +3 -3
  53. package/dist/{chunk-TPTQHOXU.js → chunk-KV72BP7O.js} +9 -9
  54. package/dist/{chunk-5HJJEHGE.js → chunk-LBNEH6IQ.js} +1 -1
  55. package/dist/chunk-LM265QNK.js +3896 -0
  56. package/dist/{chunk-ASZ4IQB4.js → chunk-MSM6VRQ4.js} +2 -2
  57. package/dist/{chunk-GBT7P3OQ.js → chunk-NJGTNWQY.js} +5 -5
  58. package/dist/{chunk-HXM2ISEK.js → chunk-NMDUSCCE.js} +377 -233
  59. package/dist/{chunk-5544QB3U.js → chunk-OV6KHXLX.js} +1 -1
  60. package/dist/{chunk-QYQ2EYOI.js → chunk-RE3EEBWC.js} +3 -3
  61. package/dist/{chunk-XJVXROOL.js → chunk-RQBB7PYT.js} +2 -2
  62. package/dist/{chunk-TFUAKV6O.js → chunk-RWYUK7LM.js} +1 -1
  63. package/dist/{chunk-JIPUWKLA.js → chunk-SMEF3TRJ.js} +26 -14
  64. package/dist/{chunk-CSYSEPWL.js → chunk-T5YF32WV.js} +5 -5
  65. package/dist/{chunk-B634ZCY2.js → chunk-TADJOIFW.js} +1 -1
  66. package/dist/{chunk-IPEP346Y.js → chunk-TFGO5YMI.js} +1 -1
  67. package/dist/{chunk-HQIFMCQN.js → chunk-TFMZ4RKM.js} +85 -38
  68. package/dist/{chunk-565X6Q4X.js → chunk-TJKE4EYX.js} +16 -8
  69. package/dist/{chunk-WMJJCHVQ.js → chunk-TRZVS6TL.js} +2 -2
  70. package/dist/{chunk-NSJ5HD6A.js → chunk-U5GTDAQL.js} +5 -5
  71. package/dist/{chunk-4QFKGRTB.js → chunk-UNR3P7IF.js} +2 -2
  72. package/dist/{chunk-SMF6YAUL.js → chunk-VYQYVYQ5.js} +2 -2
  73. package/dist/{chunk-T37XXYRK.js → chunk-W3CS2KFV.js} +1 -1
  74. package/dist/{chunk-KLWUB37U.js → chunk-WHAEJX26.js} +7 -7
  75. package/dist/{chunk-ANYBRVPF.js → chunk-WJEVUPVD.js} +1 -1
  76. package/dist/{chunk-QMFI4N7J.js → chunk-WQ3X5OH4.js} +1 -1
  77. package/dist/{chunk-HNMDLKA4.js → chunk-WY3TQ2PT.js} +1 -1
  78. package/dist/{chunk-B52BJM5K.js → chunk-XMSQRDAK.js} +2 -2
  79. package/dist/{chunk-E7TI5PAQ.js → chunk-XWOCXIKR.js} +1 -1
  80. package/dist/{chunk-E676Y7AY.js → chunk-ZHKUIC7X.js} +42 -14
  81. package/dist/{clean-PS4VYGQ6.js → clean-3VIU2BSK.js} +14 -14
  82. package/dist/cli.js +53 -53
  83. package/dist/{cloud-E47CE5VQ.js → cloud-NA3J4XW2.js} +4 -4
  84. package/dist/{cloudrun-3J4SQBXE.js → cloudrun-4MHOF7UT.js} +4 -4
  85. package/dist/commands/layout-audit.browser.js +64 -3
  86. package/dist/{compare-EAKHXBDO.js → compare-ZR7XRPPT.js} +29 -26
  87. package/dist/{compiler-23MT6CX6.js → compiler-MA2ELZPG.js} +3 -3
  88. package/dist/{component-QBOXYJJS.js → component-ZJSOPUAM.js} +3 -3
  89. package/dist/{compositions-QC3EO3MJ.js → compositions-WA5V2SHF.js} +10 -10
  90. package/dist/{delete-TXEOLPC6.js → delete-UC427UEA.js} +2 -2
  91. package/dist/{deploy-3B3WJ6PM.js → deploy-B5UO4UIK.js} +1 -1
  92. package/dist/{dist-F4UJFK4S.js → dist-BPK37SAB.js} +2 -2
  93. package/dist/{dist-JH32CBQL.js → dist-XGPSPKKP.js} +1 -1
  94. package/dist/docs/rendering.md +2 -2
  95. package/dist/{doctor-2CRFH4B3.js → doctor-ZERCTKEK.js} +10 -10
  96. package/dist/{events-GUIOSBIB.js → events-CNIXB2QK.js} +3 -3
  97. package/dist/{events-KVTX3QFV.js → events-DCOZQERS.js} +2 -2
  98. package/dist/{feedback-VQIOSTIB.js → feedback-YBD5ITTY.js} +6 -6
  99. package/dist/{ffmpeg-BPCZGXFW.js → ffmpeg-V4BBFDTG.js} +6 -6
  100. package/dist/{figma-QIOKCH26.js → figma-Z45ZYVGE.js} +3 -3
  101. package/dist/fontLocalizeCli.js +8 -8
  102. package/dist/{get-TU45SNXF.js → get-23NXQWCD.js} +2 -2
  103. package/dist/{gpuPolicy-AWAJBGYY.js → gpuPolicy-H6ABHVEB.js} +6 -6
  104. package/dist/{grade-compare-LDNUXX3Q.js → grade-compare-ZPX2G3UV.js} +26 -26
  105. package/dist/{help-YUCHJKKL.js → help-EP4CDPLO.js} +1 -1
  106. package/dist/{history-MMHFUMIG.js → history-E5OSXO74.js} +6 -6
  107. package/dist/hyperframe.manifest.json +1 -1
  108. package/dist/hyperframe.runtime.iife.js +61 -61
  109. package/dist/hyperframes-player.global.js +3 -3
  110. package/dist/{info-HBHOFSC2.js → info-2HHGSW5H.js} +7 -7
  111. package/dist/{init-3E4GUXIZ.js → init-ENEG6RZT.js} +26 -26
  112. package/dist/{inspect-YNUPJVIH.js → inspect-PVQK4E2A.js} +26 -26
  113. package/dist/{installer-WLCRP33K.js → installer-YGGLEOSF.js} +4 -4
  114. package/dist/{keyframes-Z47GHCGO.js → keyframes-UWSYS2SL.js} +6 -6
  115. package/dist/{lambda-CA4PEUO2.js → lambda-L3LPXC2M.js} +7 -7
  116. package/dist/{layout-YPV6PIKF.js → layout-JVL4QREC.js} +26 -26
  117. package/dist/{lint-3AVVQ74A.js → lint-JXQ72DDB.js} +7 -7
  118. package/dist/{lintProject-XM3DLUKG.js → lintProject-F7IC7WY4.js} +1 -1
  119. package/dist/{list-JH52Y735.js → list-UFMLM3VV.js} +2 -2
  120. package/dist/{login-G7NPJUAW.js → login-CKGMWQJN.js} +4 -4
  121. package/dist/{manager-2LSMMZY4.js → manager-U6OP3EXB.js} +7 -7
  122. package/dist/{media-treatment-BVMMD5EN.js → media-treatment-MFMEWCDY.js} +12 -12
  123. package/dist/{models-P3TNRVRV.js → models-QA3TJ75Y.js} +10 -10
  124. package/dist/{motionShot-LK62QHNE.js → motionShot-NV2OPJHD.js} +14 -14
  125. package/dist/{normalize-audio-Y7BAQ7AR.js → normalize-audio-JWJRUKK2.js} +4 -4
  126. package/dist/{open-CCFFLAT7.js → open-PTJNDRER.js} +11 -11
  127. package/dist/{parakeetRunner-N2BWSOSD.js → parakeetRunner-B5FCWWOO.js} +11 -11
  128. package/dist/{pipeline-3VT2DQGU.js → pipeline-N26NA6ZG.js} +7 -7
  129. package/dist/{play-G4I5IFHO.js → play-45D6X6TV.js} +17 -17
  130. package/dist/{preflight-2KU2QLCZ.js → preflight-MFOHPUOI.js} +7 -7
  131. package/dist/{present-EJ7XHGQO.js → present-IFBUDI4D.js} +13 -13
  132. package/dist/{preview-O23BYFAY.js → preview-MQRLXQOV.js} +18 -17
  133. package/dist/{producer-UJQVLKLP.js → producer-2QG7HLAW.js} +1 -1
  134. package/dist/{projectConfig-QFVIBT37.js → projectConfig-I4FTZXHB.js} +6 -6
  135. package/dist/{publication-4UYGFB2X.js → publication-3GOFXCGU.js} +2 -2
  136. package/dist/{publish-MHLVJLCR.js → publish-3B2TZCFH.js} +8 -8
  137. package/dist/{remove-background-N7AHTZWP.js → remove-background-RK4BJDBF.js} +8 -8
  138. package/dist/{render-6HQV4LZJ.js → render-BV6FWFX3.js} +7 -6
  139. package/dist/{render-EI2ODKOP.js → render-PW3XGS7B.js} +28 -28
  140. package/dist/{render-CDATEVDJ.js → render-SJ4HFPFU.js} +13 -13
  141. package/dist/{render-batch-F7HMC7XH.js → render-batch-GKX54O6N.js} +7 -6
  142. package/dist/renderSetupWorker.js +1 -1
  143. package/dist/{resolver-63DB5JCY.js → resolver-RQJ7CNXD.js} +4 -4
  144. package/dist/{sherpa-AK3MTPL3.js → sherpa-XZPG7QFF.js} +10 -10
  145. package/dist/{sites-5PWHAMEG.js → sites-Q2S3DZY4.js} +2 -1
  146. package/dist/skills/hyperframes/references/captions.md +1 -1
  147. package/dist/skills/hyperframes/references/motion.md +1 -1
  148. package/dist/skills/hyperframes-cli/references/cloudrun.md +1 -1
  149. package/dist/skills/hyperframes-cli/references/lambda.md +1 -1
  150. package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +2 -2
  151. package/dist/skills/hyperframes-cli/references/preview-render.md +1 -1
  152. package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +0 -8
  153. package/dist/skills/media-use/audio/references/transcribe.md +2 -2
  154. package/dist/skills/media-use/references/audio.md +1 -1
  155. package/dist/{skills-EVIDL7PV.js → skills-EYLB2V6F.js} +5 -5
  156. package/dist/{skillsUpdateCheck-YCNJYDJO.js → skillsUpdateCheck-TE6K5ROM.js} +3 -3
  157. package/dist/{snapshot-WSYZKR3C.js → snapshot-FE4T3SCN.js} +48 -33
  158. package/dist/{src-KTD6WTEV.js → src-SEJ7LNNB.js} +7 -7
  159. package/dist/{src-YNXBU4ZQ.js → src-ZOXSEJCD.js} +5 -5
  160. package/dist/{staticProjectServer-D2D3BKBP.js → staticProjectServer-LSVIRDRO.js} +14 -14
  161. package/dist/studio/assets/{hyperframes-player-CkK8Zijf.js → hyperframes-player-Bv9hQ3v7.js} +1 -1
  162. package/dist/studio/assets/index-CBO8uz7-.css +1 -0
  163. package/dist/studio/assets/{index-B2JcCaRF.js → index-D_QqfWhx.js} +133 -132
  164. package/dist/studio/assets/{index-ATnRoBug.js → index-Df14bh-L.js} +1 -1
  165. package/dist/studio/index.html +2 -2
  166. package/dist/{studio-api-3ZGQPE5J.js → studio-api-SL2Z76NU.js} +7 -7
  167. package/dist/{studioServer-VWM4J7ZP.js → studioServer-BADWVIHU.js} +44 -44
  168. package/dist/{telemetry-BEVI64BB.js → telemetry-RN6VYEID.js} +3 -3
  169. package/dist/{timeline-P57FUXJQ.js → timeline-JBTCIPLU.js} +15 -15
  170. package/dist/{tokens-OUQY6LPD.js → tokens-A7V7UHNK.js} +2 -2
  171. package/dist/{transcribe-CK66VIU4.js → transcribe-E4BOSNM4.js} +9 -9
  172. package/dist/{transcribe-QUPX67KC.js → transcribe-NTUCJGFI.js} +16 -16
  173. package/dist/{updateCheck-XZ43CH3L.js → updateCheck-M7UAYLUR.js} +2 -2
  174. package/dist/{upgrade-7QQNGI7I.js → upgrade-A2COSIMB.js} +2 -2
  175. package/dist/{urlDownloader-ESAEL6YX.js → urlDownloader-MBSQYART.js} +5 -5
  176. package/dist/{validate-5GZ6KFRU.js → validate-NODZYBHY.js} +20 -20
  177. package/dist/{video-AQNVR6PE.js → video-LBCUUYK7.js} +6 -6
  178. package/dist/warmCheck.js +33 -33
  179. package/package.json +1 -1
  180. package/dist/chunk-BPX7LZG6.js +0 -3896
  181. package/dist/studio/assets/index-CESvLzz0.css +0 -1
@@ -26,7 +26,7 @@ import {
26
26
  renderFrameIdForRenderId,
27
27
  resolveAuthoredTimingWindow,
28
28
  toFps
29
- } from "./chunk-WYCFMNU3.js";
29
+ } from "./chunk-GM3WIOO6.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-LM265QNK.js";
67
67
  import {
68
68
  clampAudioGain,
69
69
  clipGain,
@@ -83,7 +83,7 @@ import {
83
83
  } from "./chunk-4UMPIPCQ.js";
84
84
  import {
85
85
  resolveReferencedStart
86
- } from "./chunk-TSFA2Y5C.js";
86
+ } from "./chunk-3NRB7YXW.js";
87
87
  import {
88
88
  FFMPEG_PATH_ENV,
89
89
  FFPROBE_PATH_ENV,
@@ -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);
@@ -3715,6 +3735,7 @@ async function produceDrawElementFrameBatch(page, times, width, height, quality
3715
3735
  const { t, fid } = frame;
3716
3736
  try {
3717
3737
  if (aw.__hf && typeof aw.__hf.seek === "function") aw.__hf.seek(t);
3738
+ await aw.__hfWaitForSeekCompletion?.();
3718
3739
  await aw.__hfDecodeDynamicCssBackgroundImages?.();
3719
3740
  aw.__hf3d?.update();
3720
3741
  const accel = (aw.__hf_accel_canvases ?? []).filter((c) => root.contains(c));
@@ -5488,6 +5509,13 @@ async function constructCaptureSession(input) {
5488
5509
  }
5489
5510
  });
5490
5511
  await installMediaRenderIdBridge(page);
5512
+ if (options.skipReadinessVideoIds?.length) {
5513
+ await page.evaluateOnNewDocument(
5514
+ holdRenderFrameVideos,
5515
+ options.skipReadinessVideoIds,
5516
+ MEDIA_RENDER_ID_ATTR
5517
+ );
5518
+ }
5491
5519
  if (useDrawElement) {
5492
5520
  await page.evaluateOnNewDocument(instrumentAcceleratedCanvases);
5493
5521
  }
@@ -5711,16 +5739,60 @@ async function listUnregisteredTimelineIds(page) {
5711
5739
  })()`);
5712
5740
  return Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
5713
5741
  }
5714
- async function waitForSubCompositionTimelines(session, page, timeoutMs) {
5742
+ function isScriptFailure(session) {
5743
+ const outcome = session.subTimelineWaitOutcome;
5744
+ return outcome === "script_failure" || outcome === "timeout" && session.pageErrors.length > 0;
5745
+ }
5746
+ async function isolateUnregisteredCompositions(page) {
5747
+ const isolated = await page.evaluate(`(function() {
5748
+ var timelines = (window.__timelines = window.__timelines || {});
5749
+ var out = [];
5750
+ if (!window.gsap) return out;
5751
+ var broken = [];
5752
+ var live = 0;
5753
+ var hosts = document.querySelectorAll("[data-composition-id]");
5754
+ for (var i = 0; i < hosts.length; i++) {
5755
+ var host = hosts[i];
5756
+ var id = host.getAttribute("data-composition-id");
5757
+ if (!id || host.hasAttribute("data-no-timeline")) continue;
5758
+ var isRoot = !host.parentElement || !host.parentElement.closest("[data-composition-id]");
5759
+ if (timelines[id]) {
5760
+ if (!isRoot) live++;
5761
+ } else if (isRoot) {
5762
+ return out;
5763
+ } else {
5764
+ broken.push(host);
5765
+ }
5766
+ }
5767
+ if (live === 0) return out;
5768
+ broken.forEach(function(host) {
5769
+ var tl = window.gsap.timeline({ paused: true });
5770
+ var duration = parseFloat(host.getAttribute("data-duration"));
5771
+ if (duration > 0) tl.set({}, {}, duration);
5772
+ var id = host.getAttribute("data-composition-id");
5773
+ timelines[id] = tl;
5774
+ out.push({ compositionId: id, elementId: host.id || host.getAttribute("data-hf-id") || null });
5775
+ });
5776
+ if (out.length && typeof window.__hfForceTimelineRebind === "function") window.__hfForceTimelineRebind();
5777
+ return out;
5778
+ })()`);
5779
+ return Array.isArray(isolated) ? isolated : [];
5780
+ }
5781
+ async function waitForSubCompositionTimelines(session, page, timeoutMs, options = {}) {
5715
5782
  const memo = session.subTimelineWaitMemo;
5716
5783
  session.subTimelineWaitOutcome = await pollSubCompositionTimelines(page, timeoutMs, {
5717
5784
  getScriptLoadFailures: () => session.scriptLoadFailures,
5785
+ getPageErrors: () => session.pageErrors,
5786
+ scriptErrorGraceMs: options.scriptErrorGraceMs,
5718
5787
  onPending: (ids) => {
5719
5788
  session.pendingTimelineIds = [...ids];
5720
5789
  },
5721
5790
  shouldStop: () => session.vfxFailure !== void 0,
5722
5791
  knownUnregisteredIds: memo.unregisteredIds
5723
5792
  });
5793
+ if (isScriptFailure(session) && session.scriptLoadFailures.length === 0) {
5794
+ session.isolatedCompositions = await isolateUnregisteredCompositions(page);
5795
+ }
5724
5796
  const cleanTimeout = session.subTimelineWaitOutcome === "timeout" && session.vfxFailure === void 0 && session.pageErrors.length === 0 && session.scriptLoadFailures.length === 0;
5725
5797
  if (cleanTimeout) {
5726
5798
  memo.unregisteredIds = session.pendingTimelineIds ?? [];
@@ -5730,6 +5802,8 @@ async function pollSubCompositionTimelines(page, timeoutMs, {
5730
5802
  intervalMs = 150,
5731
5803
  getScriptLoadFailures,
5732
5804
  scriptFailureGraceMs = 2e3,
5805
+ getPageErrors,
5806
+ scriptErrorGraceMs = 5e3,
5733
5807
  onPending,
5734
5808
  shouldStop,
5735
5809
  knownUnregisteredIds = []
@@ -5751,16 +5825,22 @@ async function pollSubCompositionTimelines(page, timeoutMs, {
5751
5825
  const deadline = start + timeoutMs;
5752
5826
  let ready = false;
5753
5827
  let scriptFailureBail = false;
5828
+ let errorSeenAt;
5754
5829
  for (; ; ) {
5755
5830
  ready = Boolean(await page.evaluate(expression));
5756
5831
  if (ready || shouldStop?.()) break;
5757
5832
  const now = Date.now();
5758
5833
  if (now >= deadline) break;
5759
5834
  const failures = getScriptLoadFailures?.() ?? [];
5760
- if (failures.length > 0 && now - start >= scriptFailureGraceMs) {
5835
+ const errors = getPageErrors?.() ?? [];
5836
+ const failed = failures.length > 0 ? failures : errors;
5837
+ const graceMs = failures.length > 0 ? scriptFailureGraceMs : scriptErrorGraceMs;
5838
+ if (failed.length > 0) errorSeenAt ??= now;
5839
+ const sinceFailure = failures.length > 0 ? now - start : now - (errorSeenAt ?? now);
5840
+ if (failed.length > 0 && sinceFailure >= graceMs) {
5761
5841
  scriptFailureBail = true;
5762
5842
  console.warn(
5763
- `[FrameCapture] Sub-composition timeline wait cut short after ${now - start}ms: script resource(s) failed to load (${failures.join(", ")}), so the timeline registration they carry can never arrive. Fix the script reference; the render proceeds without those animations.`
5843
+ `[FrameCapture] Sub-composition timeline wait cut short after ${now - start}ms: script(s) failed (${failed.join(", ")}), so the timeline registration they carry can never arrive. Fix the script.`
5764
5844
  );
5765
5845
  break;
5766
5846
  }
@@ -5794,7 +5874,7 @@ async function pollVideosReady(page, skipIds, timeoutMs, intervalMs = 100) {
5794
5874
  return Boolean(
5795
5875
  await page.evaluate((skipIdList) => {
5796
5876
  const skip = new Set(skipIdList);
5797
- const vids = Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id));
5877
+ const vids = Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id)).filter((v) => !v.closest("[data-hidden]"));
5798
5878
  return vids.length === 0 || vids.every((v) => {
5799
5879
  const ve = v;
5800
5880
  if (ve.readyState >= 2) return true;
@@ -5908,15 +5988,31 @@ function recordCaptureWarnings(session, warnings) {
5908
5988
  function recordSubTimelineWarning(session, timeoutMs) {
5909
5989
  const outcome = session.subTimelineWaitOutcome;
5910
5990
  if (outcome === "ready" || !outcome) return;
5911
- const threwThenTimedOut = outcome === "timeout" && session.pageErrors.length > 0;
5912
- const scriptFailure = outcome === "script_failure" || threwThenTimedOut;
5991
+ const threwThenTimedOut = session.pageErrors.length > 0 && session.scriptLoadFailures.length === 0;
5992
+ const scriptFailure = isScriptFailure(session);
5913
5993
  const sources = [...session.scriptLoadFailures, ...session.pageErrors];
5914
5994
  const pending = session.pendingTimelineIds ?? [];
5915
5995
  const pendingSuffix = pending.length > 0 ? ` (still unregistered: ${pending.join(", ")})` : "";
5996
+ const isolated = session.isolatedCompositions ?? [];
5997
+ if (isolated.length > 0) {
5998
+ recordCaptureWarnings(session, [
5999
+ {
6000
+ code: "script_error_isolated",
6001
+ message: `A composition script failed, so its animation was skipped and the render went on: ${isolated.map((c) => c.elementId ?? c.compositionId).join(", ")} (${sources.join(", ")}).`,
6002
+ details: {
6003
+ sources,
6004
+ pendingCompositionIds: [...pending],
6005
+ isolatedElements: isolated,
6006
+ failureOwner: "user"
6007
+ }
6008
+ }
6009
+ ]);
6010
+ return;
6011
+ }
5916
6012
  recordCaptureWarnings(session, [
5917
6013
  {
5918
6014
  code: scriptFailure ? "sub_timeline_script_failure" : "sub_timeline_readiness_timeout",
5919
- message: threwThenTimedOut ? `A composition script threw and a timeline did not register within ${timeoutMs}ms${pendingSuffix} (${sources.join(", ")}). Fix the error; a composition animated by CSS or rAF rather than a GSAP timeline must mark its host with data-no-timeline.` : scriptFailure ? `A sub-composition timeline script failed to load (${sources.join(", ")})` : `Sub-composition timelines did not become ready within ${timeoutMs}ms${pendingSuffix}. This can be intentional: a composition driven by CSS animations or rAF never registers window.__timelines[id], and marking its host with data-no-timeline skips the wait entirely. Otherwise, a composition that sets up asynchronously must register window.__timelines[id] once setup completes.`,
6015
+ message: threwThenTimedOut ? `A composition script threw before its timeline registered${pendingSuffix} (${sources.join(", ")}). Fix the error; a composition animated by CSS or rAF rather than a GSAP timeline must mark its host with data-no-timeline.` : scriptFailure ? `A sub-composition timeline script failed to load (${sources.join(", ")})` : `Sub-composition timelines did not become ready within ${timeoutMs}ms${pendingSuffix}. This can be intentional: a composition driven by CSS animations or rAF never registers window.__timelines[id], and marking its host with data-no-timeline skips the wait entirely. Otherwise, a composition that sets up asynchronously must register window.__timelines[id] once setup completes.`,
5920
6016
  details: {
5921
6017
  timeoutMs,
5922
6018
  sources,
@@ -6192,7 +6288,7 @@ async function initializeSession(session) {
6192
6288
  if (!videosReady) {
6193
6289
  const failedVideos = await page.evaluate((skipIdList) => {
6194
6290
  const skip = new Set(skipIdList);
6195
- return Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id)).filter((v) => v.readyState < 2 && !v.error).map((v) => v.src || v.getAttribute("src") || "(no src)").join(", ");
6291
+ return Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id)).filter((v) => !v.closest("[data-hidden]")).filter((v) => v.readyState < 2 && !v.error).map((v) => v.src || v.getAttribute("src") || "(no src)").join(", ");
6196
6292
  }, skipVideoIds);
6197
6293
  console.warn(
6198
6294
  `[FrameCapture] Some video elements did not decode within ${pageReadyTimeout}ms: ${failedVideos}. Continuing render. Affected videos will appear as blank/black frames.`
@@ -6288,7 +6384,7 @@ async function initializeSession(session) {
6288
6384
  if (!bfVideosReady) {
6289
6385
  const failedVideos = await page.evaluate((skipIdList) => {
6290
6386
  const skip = new Set(skipIdList);
6291
- return Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id)).filter((v) => v.readyState < 2 && !v.error).map((v) => v.src || v.getAttribute("src") || "(no src)").join(", ");
6387
+ return Array.from(document.querySelectorAll("video")).filter((v) => !skip.has(v.id)).filter((v) => !v.closest("[data-hidden]")).filter((v) => v.readyState < 2 && !v.error).map((v) => v.src || v.getAttribute("src") || "(no src)").join(", ");
6292
6388
  }, bfSkipVideoIds);
6293
6389
  console.warn(
6294
6390
  `[FrameCapture] Some video elements did not decode within ${pageReadyTimeout}ms: ${failedVideos}. Continuing render. Affected videos will appear as blank/black frames.`
@@ -7001,6 +7097,44 @@ function resolveSessionMotionBlur(session) {
7001
7097
  }
7002
7098
  return plan;
7003
7099
  }
7100
+ async function judgeSmallDrawElementFrame(session, frameIndex, buffer, redraw) {
7101
+ const sizes = session.deFrameSizes ??= [];
7102
+ const sorted = sizes.length >= 12 ? [...sizes].sort((a, b) => a - b) : null;
7103
+ const median = sorted ? sorted[sorted.length >> 1] ?? 0 : 0;
7104
+ const floor = Math.max(session.deAbsFloor ?? 2e4, median * 0.12);
7105
+ let kept = buffer;
7106
+ if (buffer.length < floor && !session.deAcceptedSmall?.equals(buffer)) {
7107
+ const suspect = () => new DrawElementCaptureError(
7108
+ frameIndex,
7109
+ `suspect small frame (${buffer.length}B < ${Math.round(floor)}B)`
7110
+ );
7111
+ if (session.beginFrameTimeTicks > 0) throw suspect();
7112
+ const retry = await redraw();
7113
+ if (retry.equals(buffer)) {
7114
+ session.deAbsFloor = Math.min(session.deAbsFloor ?? 2e4, Math.floor(buffer.length / 2));
7115
+ session.deAcceptedSmall = buffer;
7116
+ } else if (retry.length < floor) {
7117
+ throw suspect();
7118
+ } else {
7119
+ kept = retry;
7120
+ }
7121
+ }
7122
+ if (sizes.length >= 60) sizes.shift();
7123
+ sizes.push(kept.length);
7124
+ return kept;
7125
+ }
7126
+ async function redrawDrawElementFrame(session, frameIndex, time, seekOptions, heldVideoTime) {
7127
+ const { page, options } = session;
7128
+ await prepareFrameForCapture(session, frameIndex, time, seekOptions, heldVideoTime);
7129
+ return captureDrawElementFrame(
7130
+ page,
7131
+ options.width,
7132
+ options.height,
7133
+ options.format ?? "jpeg",
7134
+ options.quality ?? 80,
7135
+ true
7136
+ );
7137
+ }
7004
7138
  async function captureFrameSurface(session, frameIndex, time, seekOptions, heldVideoTime) {
7005
7139
  const { page, options } = session;
7006
7140
  const { quantizedTime, seekMs, beforeCaptureMs } = await prepareFrameForCapture(
@@ -7060,19 +7194,12 @@ async function captureFrameSurface(session, frameIndex, time, seekOptions, heldV
7060
7194
  session.beginFrameTimeTicks === 0
7061
7195
  );
7062
7196
  if ((options.format ?? "jpeg") !== "png" && process.env.HF_FORCE_DRAWELEMENT !== "1") {
7063
- const sizes = session.deFrameSizes ??= [];
7064
- const sorted = sizes.length >= 12 ? [...sizes].sort((a, b) => a - b) : null;
7065
- const median = sorted ? sorted[sorted.length >> 1] ?? 0 : 0;
7066
- const floor = Math.max(2e4, median * 0.12);
7067
- if (screenshotBuffer.length < floor) {
7068
- throw new DrawElementCaptureError(
7069
- frameIndex,
7070
- `suspect small frame (${screenshotBuffer.length}B < ${Math.round(floor)}B)`
7071
- );
7072
- } else {
7073
- if (sizes.length >= 60) sizes.shift();
7074
- sizes.push(screenshotBuffer.length);
7075
- }
7197
+ screenshotBuffer = await judgeSmallDrawElementFrame(
7198
+ session,
7199
+ frameIndex,
7200
+ screenshotBuffer,
7201
+ () => redrawDrawElementFrame(session, frameIndex, time, seekOptions, heldVideoTime)
7202
+ );
7076
7203
  }
7077
7204
  } catch (err) {
7078
7205
  if (isRecoverableDrawElementError(err)) {
@@ -8853,7 +8980,9 @@ async function extractAudioMetadata(filePath, options) {
8853
8980
  channels: audioStream.channels || 2,
8854
8981
  audioCodec,
8855
8982
  bitrate: output.format.bit_rate ? parseInt(output.format.bit_rate) : void 0,
8856
- latestStreamLeadSeconds: latestStreamLeadSeconds(output)
8983
+ latestStreamLeadSeconds: latestStreamLeadSeconds(output),
8984
+ audioStreamLeadSeconds: streamLeadSeconds(output, audioStream),
8985
+ audioStreamCount: output.streams.filter((s) => s.codec_type === "audio").length
8857
8986
  };
8858
8987
  })();
8859
8988
  if (options?.signal) return probePromise;
@@ -14021,6 +14150,9 @@ function clampFadesToDuration(fades, duration) {
14021
14150
  return { fadeIn: fadeIn * scale, fadeOut: fadeOut * scale };
14022
14151
  }
14023
14152
 
14153
+ // ../engine/src/services/audioMixer.ts
14154
+ import { cpus as cpus2 } from "os";
14155
+
14024
14156
  // ../engine/src/services/aacEncode.ts
14025
14157
  import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync5, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "fs";
14026
14158
  import { dirname as dirname5, join as join11 } from "path";
@@ -15136,6 +15268,7 @@ function escapeExpressionCommas(expression) {
15136
15268
  function legacyFilterScriptOptionIsUnsupported(stderr) {
15137
15269
  return /filter_complex_script/i.test(stderr) && /(?:unrecognized option|option (?:was )?not found)/i.test(stderr);
15138
15270
  }
15271
+ var inPrepareSlot = createConcurrencyLimit(Math.max(2, Math.floor(cpus2().length / 2)));
15139
15272
  var MAX_VOLUME_SEGMENTS = 32;
15140
15273
  var VOLUME_SIMPLIFY_EPSILON = 5e-3;
15141
15274
  var STEREO_CHANNEL_FILTER = "pan=stereo|FL=FL+FC|FR=FR+FC";
@@ -15409,7 +15542,8 @@ function parseAudioElements(html) {
15409
15542
  return elements;
15410
15543
  }
15411
15544
  var FAIL_ON_READ_ERROR_INSTEAD_OF_ENDING_THERE = ["-xerror"];
15412
- async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, duration, playbackRate = 1, signal, config) {
15545
+ var SIDECAR_WARM_UP_SECONDS = 1;
15546
+ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, duration, playbackRate = 1, signal, config, audioSidecarUrl) {
15413
15547
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
15414
15548
  const outputDir = dirname7(outputPath);
15415
15549
  if (!existsSync11(outputDir)) mkdirSync7(outputDir, { recursive: true });
@@ -15423,18 +15557,21 @@ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, durat
15423
15557
  const inputSeek = mediaStart >= (metadata?.latestStreamLeadSeconds ?? 0);
15424
15558
  const start = formatFilterNumber(mediaStart);
15425
15559
  const timestampTrim = inputSeek ? null : `atrim=start=${start}:end=${formatFilterNumber(mediaStart + sourceDuration)},asetpts=PTS-${start}/TB,aresample=async=1:first_pts=0`;
15560
+ const fromSidecar = audioSidecarUrl !== void 0 && inputSeek && normalizedPlaybackRate === 1;
15561
+ const warmUp = fromSidecar ? Math.min(SIDECAR_WARM_UP_SECONDS, mediaStart) : 0;
15426
15562
  const outputArgs = preparedAudioOutputArgs(
15427
15563
  metadata,
15428
15564
  normalizedPlaybackRate,
15429
15565
  duration,
15430
- timestampTrim
15566
+ warmUp > 0 ? `atrim=start=${warmUp},asetpts=PTS-STARTPTS` : timestampTrim
15431
15567
  );
15568
+ const readFrom = fromSidecar ? audioSidecarUrl : srcPath;
15432
15569
  const args = [
15433
15570
  ...readsUrl ? FAIL_ON_READ_ERROR_INSTEAD_OF_ENDING_THERE : [],
15434
- ...urlInputArgs(srcPath),
15435
- ...inputSeek ? ["-ss", String(mediaStart), "-t", String(sourceDuration)] : [],
15571
+ ...urlInputArgs(readFrom),
15572
+ ...inputSeek ? ["-ss", String(mediaStart - warmUp), "-t", String(sourceDuration + warmUp)] : [],
15436
15573
  "-i",
15437
- srcPath
15574
+ readFrom
15438
15575
  ];
15439
15576
  args.push("-vn", "-acodec", "pcm_s16le", "-ar", "48000", ...outputArgs);
15440
15577
  if (normalizedPlaybackRate !== 1) args.push("-t", String(duration));
@@ -15862,216 +15999,223 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
15862
15999
  else signal.addEventListener("abort", () => internalController.abort(), { once: true });
15863
16000
  }
15864
16001
  await Promise.all(
15865
- elements.map(async (element, index) => {
15866
- if (effectiveSignal.aborted) {
15867
- failures.push({
15868
- stage: "cancelled",
15869
- reason: "cancelled",
15870
- owner: "user",
15871
- retryable: false,
15872
- elementId: element.id,
15873
- detail: boundedDetail(`Cancelled audio element ${element.id}`)
15874
- });
15875
- return;
15876
- }
15877
- try {
15878
- let srcPath = element.src;
15879
- let readInPlace = false;
15880
- if (!isHttpUrl(srcPath)) {
15881
- srcPath = resolveProjectRelativeSrc(element.src, baseDir, compiledDir);
15882
- const remoteUrl = config?.urlByAbsentLocalPath?.get(srcPath);
15883
- if (remoteUrl) {
15884
- srcPath = remoteUrl;
15885
- readInPlace = true;
15886
- }
15887
- }
15888
- if (isHttpUrl(srcPath) && !readInPlace) {
15889
- try {
15890
- srcPath = await downloadToTemp(
15891
- srcPath,
15892
- workDir,
15893
- void 0,
15894
- effectiveSignal,
15895
- void 0,
15896
- {
15897
- onTelemetry: writeUrlDownloadTelemetry
15898
- }
15899
- );
15900
- } catch (err) {
15901
- failures.push(downloadFailure(err, element.id));
15902
- return;
15903
- }
15904
- }
15905
- if (!readInPlace && !existsSync11(srcPath)) {
15906
- failures.push({
15907
- stage: "source",
15908
- reason: "source_not_found",
15909
- owner: "user",
15910
- retryable: false,
15911
- elementId: element.id,
15912
- detail: boundedDetail(missingSourceMessage(element.id, element.src, baseDir, srcPath))
15913
- });
15914
- return;
15915
- }
15916
- if (await isNotMediaPayload(srcPath)) {
16002
+ elements.map(
16003
+ (element, index) => inPrepareSlot(async () => {
16004
+ if (effectiveSignal.aborted) {
15917
16005
  failures.push({
15918
- stage: "source",
15919
- reason: "invalid_media",
16006
+ stage: "cancelled",
16007
+ reason: "cancelled",
15920
16008
  owner: "user",
15921
16009
  retryable: false,
15922
16010
  elementId: element.id,
15923
- detail: boundedDetail(
15924
- `Audio element ${element.id} source is a text document (HTML/XML/JSON), not media`
15925
- )
16011
+ detail: boundedDetail(`Cancelled audio element ${element.id}`)
15926
16012
  });
15927
16013
  return;
15928
16014
  }
15929
- if (element.end - element.start <= 0) {
15930
- let metadata;
15931
- try {
15932
- metadata = await extractAudioMetadata(
16015
+ try {
16016
+ let srcPath = element.src;
16017
+ let readInPlace = false;
16018
+ let audioSidecarUrl;
16019
+ if (!isHttpUrl(srcPath)) {
16020
+ srcPath = resolveProjectRelativeSrc(element.src, baseDir, compiledDir);
16021
+ const remoteUrl = config?.urlByAbsentLocalPath?.get(srcPath);
16022
+ if (remoteUrl) {
16023
+ audioSidecarUrl = config?.audioSidecarUrlByLocalPath?.get(srcPath);
16024
+ srcPath = remoteUrl;
16025
+ readInPlace = true;
16026
+ }
16027
+ }
16028
+ if (isHttpUrl(srcPath) && !readInPlace) {
16029
+ try {
16030
+ srcPath = await downloadToTemp(
16031
+ srcPath,
16032
+ workDir,
16033
+ void 0,
16034
+ effectiveSignal,
16035
+ void 0,
16036
+ {
16037
+ onTelemetry: writeUrlDownloadTelemetry
16038
+ }
16039
+ );
16040
+ } catch (err) {
16041
+ failures.push(downloadFailure(err, element.id));
16042
+ return;
16043
+ }
16044
+ }
16045
+ if (!readInPlace && !existsSync11(srcPath)) {
16046
+ failures.push({
16047
+ stage: "source",
16048
+ reason: "source_not_found",
16049
+ owner: "user",
16050
+ retryable: false,
16051
+ elementId: element.id,
16052
+ detail: boundedDetail(
16053
+ missingSourceMessage(element.id, element.src, baseDir, srcPath)
16054
+ )
16055
+ });
16056
+ return;
16057
+ }
16058
+ if (await isNotMediaPayload(srcPath)) {
16059
+ failures.push({
16060
+ stage: "source",
16061
+ reason: "invalid_media",
16062
+ owner: "user",
16063
+ retryable: false,
16064
+ elementId: element.id,
16065
+ detail: boundedDetail(
16066
+ `Audio element ${element.id} source is a text document (HTML/XML/JSON), not media`
16067
+ )
16068
+ });
16069
+ return;
16070
+ }
16071
+ if (element.end - element.start <= 0) {
16072
+ let metadata;
16073
+ try {
16074
+ metadata = await extractAudioMetadata(
16075
+ srcPath,
16076
+ readInPlace ? { signal: effectiveSignal } : void 0
16077
+ );
16078
+ } catch (err) {
16079
+ failures.push(
16080
+ probeFailure(err instanceof Error ? err.message : String(err), element.id)
16081
+ );
16082
+ return;
16083
+ }
16084
+ const effectiveDuration = timeAtSourceTime(
16085
+ normalizeRateSpec(element.playbackRate),
16086
+ metadata.durationSeconds - element.mediaStart
16087
+ );
16088
+ element.end = element.start + (effectiveDuration > 0 ? effectiveDuration : metadata.durationSeconds);
16089
+ }
16090
+ let audioSrcPath = srcPath;
16091
+ if (element.type === "video") {
16092
+ const extractedPath = join14(workDir, `${element.id}-extracted.wav`);
16093
+ const extractResult = await extractAudioSegment(
15933
16094
  srcPath,
15934
- readInPlace ? { signal: effectiveSignal } : void 0
16095
+ extractedPath,
16096
+ "extract",
16097
+ element.mediaStart,
16098
+ element.end - element.start,
16099
+ element.playbackRate,
16100
+ effectiveSignal,
16101
+ config,
16102
+ audioSidecarUrl
15935
16103
  );
15936
- } catch (err) {
15937
- failures.push(
15938
- probeFailure(err instanceof Error ? err.message : String(err), element.id)
16104
+ if (!extractResult.success) {
16105
+ failures.push(
16106
+ extractResult.failure ? { ...extractResult.failure, elementId: element.id } : {
16107
+ stage: "extract",
16108
+ reason: "ffmpeg_failed",
16109
+ owner: "system",
16110
+ retryable: false,
16111
+ elementId: element.id,
16112
+ detail: boundedDetail(`Audio extract failed for element ${element.id}`)
16113
+ }
16114
+ );
16115
+ return;
16116
+ }
16117
+ audioSrcPath = extractedPath;
16118
+ } else {
16119
+ const trimmedPath = join14(workDir, `${element.id}-trimmed.wav`);
16120
+ const prepResult = await extractAudioSegment(
16121
+ srcPath,
16122
+ trimmedPath,
16123
+ "prepare",
16124
+ element.mediaStart,
16125
+ element.end - element.start,
16126
+ element.playbackRate,
16127
+ effectiveSignal,
16128
+ config
15939
16129
  );
15940
- return;
16130
+ if (!prepResult.success) {
16131
+ failures.push(
16132
+ prepResult.failure ? { ...prepResult.failure, elementId: element.id } : {
16133
+ stage: "prepare",
16134
+ reason: "ffmpeg_failed",
16135
+ owner: "system",
16136
+ retryable: false,
16137
+ elementId: element.id,
16138
+ detail: boundedDetail(`Audio prepare failed for element ${element.id}`)
16139
+ }
16140
+ );
16141
+ return;
16142
+ }
16143
+ audioSrcPath = trimmedPath;
15941
16144
  }
15942
- const effectiveDuration = timeAtSourceTime(
15943
- normalizeRateSpec(element.playbackRate),
15944
- metadata.durationSeconds - element.mediaStart
15945
- );
15946
- element.end = element.start + (effectiveDuration > 0 ? effectiveDuration : metadata.durationSeconds);
15947
- }
15948
- let audioSrcPath = srcPath;
15949
- if (element.type === "video") {
15950
- const extractedPath = join14(workDir, `${element.id}-extracted.wav`);
15951
- const extractResult = await extractAudioSegment(
15952
- srcPath,
15953
- extractedPath,
15954
- "extract",
15955
- element.mediaStart,
16145
+ const chain = element.fxChain ? parseAudioFxChain(element.fxChain) : void 0;
16146
+ const automation = element.automation ? resolveAutomation(parseAutomation(element.automation), chain) : null;
16147
+ const gainBeforeFx = chain !== void 0 && enabledAudioFxNodes(chain).length > 0;
16148
+ const laneKeyframes = automation ? volumeLaneKeyframes(
16149
+ automation,
16150
+ element.start,
15956
16151
  element.end - element.start,
15957
- element.playbackRate,
15958
- effectiveSignal,
15959
- config
15960
- );
15961
- if (!extractResult.success) {
15962
- failures.push(
15963
- extractResult.failure ? { ...extractResult.failure, elementId: element.id } : {
15964
- stage: "extract",
15965
- reason: "ffmpeg_failed",
15966
- owner: "system",
15967
- retryable: false,
15968
- elementId: element.id,
15969
- detail: boundedDetail(`Audio extract failed for element ${element.id}`)
16152
+ element.volume ?? 1,
16153
+ gainBeforeFx
16154
+ ) : null;
16155
+ const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
16156
+ const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
16157
+ const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
16158
+ keyframes: envelopeKeyframes,
16159
+ trackStart: element.start,
16160
+ baseVolume: element.volume ?? 1
16161
+ } : null;
16162
+ let bakedEnvelope = false;
16163
+ let tailSeconds = 0;
16164
+ if (chain) {
16165
+ tailSeconds = chainTailSeconds(chain, automation ?? void 0);
16166
+ const fxResult = await applyAudioFxChain(
16167
+ audioSrcPath,
16168
+ chain,
16169
+ join14(workDir, `${element.id}-fx.wav`),
16170
+ {
16171
+ trackId: element.id,
16172
+ signal: effectiveSignal,
16173
+ ...automation ? { automation } : {},
16174
+ ...envelope ? { envelope } : {},
16175
+ ...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
15970
16176
  }
15971
16177
  );
15972
- return;
16178
+ audioSrcPath = fxResult.path;
16179
+ bakedEnvelope = fxResult.envelopeBaked;
15973
16180
  }
15974
- audioSrcPath = extractedPath;
15975
- } else {
15976
- const trimmedPath = join14(workDir, `${element.id}-trimmed.wav`);
15977
- const prepResult = await extractAudioSegment(
15978
- srcPath,
15979
- trimmedPath,
15980
- "prepare",
15981
- element.mediaStart,
15982
- element.end - element.start,
15983
- element.playbackRate,
15984
- effectiveSignal,
15985
- config
15986
- );
15987
- if (!prepResult.success) {
15988
- failures.push(
15989
- prepResult.failure ? { ...prepResult.failure, elementId: element.id } : {
15990
- stage: "prepare",
15991
- reason: "ffmpeg_failed",
15992
- owner: "system",
15993
- retryable: false,
15994
- elementId: element.id,
15995
- detail: boundedDetail(`Audio prepare failed for element ${element.id}`)
15996
- }
16181
+ if (envelope && !bakedEnvelope) {
16182
+ bakedEnvelope = applyVolumeEnvelopeToWav(
16183
+ audioSrcPath,
16184
+ envelope.keyframes,
16185
+ envelope.trackStart,
16186
+ envelope.baseVolume
15997
16187
  );
15998
- return;
15999
16188
  }
16000
- audioSrcPath = trimmedPath;
16001
- }
16002
- const chain = element.fxChain ? parseAudioFxChain(element.fxChain) : void 0;
16003
- const automation = element.automation ? resolveAutomation(parseAutomation(element.automation), chain) : null;
16004
- const gainBeforeFx = chain !== void 0 && enabledAudioFxNodes(chain).length > 0;
16005
- const laneKeyframes = automation ? volumeLaneKeyframes(
16006
- automation,
16007
- element.start,
16008
- element.end - element.start,
16009
- element.volume ?? 1,
16010
- gainBeforeFx
16011
- ) : null;
16012
- const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
16013
- const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
16014
- const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
16015
- keyframes: envelopeKeyframes,
16016
- trackStart: element.start,
16017
- baseVolume: element.volume ?? 1
16018
- } : null;
16019
- let bakedEnvelope = false;
16020
- let tailSeconds = 0;
16021
- if (chain) {
16022
- tailSeconds = chainTailSeconds(chain, automation ?? void 0);
16023
- const fxResult = await applyAudioFxChain(
16024
- audioSrcPath,
16025
- chain,
16026
- join14(workDir, `${element.id}-fx.wav`),
16027
- {
16028
- trackId: element.id,
16029
- signal: effectiveSignal,
16030
- ...automation ? { automation } : {},
16031
- ...envelope ? { envelope } : {},
16032
- ...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
16033
- }
16034
- );
16035
- audioSrcPath = fxResult.path;
16036
- bakedEnvelope = fxResult.envelopeBaked;
16037
- }
16038
- if (envelope && !bakedEnvelope) {
16039
- bakedEnvelope = applyVolumeEnvelopeToWav(
16040
- audioSrcPath,
16041
- envelope.keyframes,
16042
- envelope.trackStart,
16043
- envelope.baseVolume
16044
- );
16189
+ const track = {
16190
+ id: element.id,
16191
+ srcPath: audioSrcPath,
16192
+ start: element.start,
16193
+ end: element.end,
16194
+ mediaStart: element.mediaStart,
16195
+ duration: element.end - element.start,
16196
+ // Gain is already in the samples when baked, so mix at unity.
16197
+ volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
16198
+ volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
16199
+ ...element.fadeIn ? { fadeIn: element.fadeIn } : {},
16200
+ ...element.fadeOut ? { fadeOut: element.fadeOut } : {},
16201
+ ...tailSeconds > 0 ? { tailSeconds } : {}
16202
+ };
16203
+ preparedTracks[index] = track;
16204
+ } catch (err) {
16205
+ if (err instanceof AudioFxRenderError) throw err;
16206
+ failures.push({
16207
+ stage: "internal",
16208
+ reason: "internal",
16209
+ owner: "system",
16210
+ retryable: false,
16211
+ elementId: element.id,
16212
+ detail: boundedDetail(
16213
+ `Audio processing failed for element ${element.id}: ${err instanceof Error ? err.message : String(err)}`
16214
+ )
16215
+ });
16045
16216
  }
16046
- const track = {
16047
- id: element.id,
16048
- srcPath: audioSrcPath,
16049
- start: element.start,
16050
- end: element.end,
16051
- mediaStart: element.mediaStart,
16052
- duration: element.end - element.start,
16053
- // Gain is already in the samples when baked, so mix at unity.
16054
- volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
16055
- volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
16056
- ...element.fadeIn ? { fadeIn: element.fadeIn } : {},
16057
- ...element.fadeOut ? { fadeOut: element.fadeOut } : {},
16058
- ...tailSeconds > 0 ? { tailSeconds } : {}
16059
- };
16060
- preparedTracks[index] = track;
16061
- } catch (err) {
16062
- if (err instanceof AudioFxRenderError) throw err;
16063
- failures.push({
16064
- stage: "internal",
16065
- reason: "internal",
16066
- owner: "system",
16067
- retryable: false,
16068
- elementId: element.id,
16069
- detail: boundedDetail(
16070
- `Audio processing failed for element ${element.id}: ${err instanceof Error ? err.message : String(err)}`
16071
- )
16072
- });
16073
- }
16074
- })
16217
+ }, effectiveSignal)
16218
+ )
16075
16219
  ).catch((err) => {
16076
16220
  internalController.abort();
16077
16221
  try {
@@ -16254,7 +16398,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
16254
16398
  }
16255
16399
 
16256
16400
  // ../engine/src/services/parallelCoordinator.ts
16257
- import { cpus as cpus2, freemem } from "os";
16401
+ import { cpus as cpus3, freemem } from "os";
16258
16402
  import { existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync6 } from "fs";
16259
16403
  import { copyFile, readFile as readFile2, rename } from "fs/promises";
16260
16404
  import { join as join16 } from "path";
@@ -16369,7 +16513,7 @@ var MIN_WORKERS = 1;
16369
16513
  var MAX_WORKER_DIAGNOSTIC_LINES = 8;
16370
16514
  var ABSOLUTE_MAX_WORKERS = 24;
16371
16515
  function defaultSafeMaxWorkers() {
16372
- return Math.max(6, Math.min(16, Math.floor(cpus2().length / 8)));
16516
+ return Math.max(6, Math.min(16, Math.floor(cpus3().length / 8)));
16373
16517
  }
16374
16518
  var MIN_FRAMES_PER_WORKER = 30;
16375
16519
  function shouldDisableBrowserPoolForParallelWorker({
@@ -16409,7 +16553,7 @@ function formatWorkerFailure(result) {
16409
16553
  return `${base}; diagnostics: ${diagnostics}`;
16410
16554
  }
16411
16555
  function computeWorkerSizing(totalFrames, requested, config) {
16412
- const cpuCount = cpus2().length;
16556
+ const cpuCount = cpus3().length;
16413
16557
  const totalMemoryMb = getSystemTotalMb();
16414
16558
  const heapLimitMb = Math.round(getHeapStatistics().heap_size_limit / (1024 * 1024));
16415
16559
  const heapBasedWorkers = Math.max(
@@ -16964,7 +17108,7 @@ async function mergeWorkerFrames(workDir, tasks, outputDir) {
16964
17108
  }
16965
17109
  function getSystemResources() {
16966
17110
  return {
16967
- cpuCores: cpus2().length,
17111
+ cpuCores: cpus3().length,
16968
17112
  totalMemoryMB: getSystemTotalMb(),
16969
17113
  freeMemoryMB: Math.round(freemem() / (1024 * 1024)),
16970
17114
  recommendedWorkers: calculateOptimalWorkers(1e3)