hyperframes 0.8.143 → 0.8.145

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 (202) hide show
  1. package/README.md +3 -2
  2. package/dist/{add-Q5EYDUAY.js → add-B6OUH46W.js} +12 -12
  3. package/dist/{animatedGifPrep-H2DUQEZ3.js → animatedGifPrep-MIWRDJIO.js} +7 -7
  4. package/dist/{asset-C7JBYYX4.js → asset-XJ2B2PU3.js} +2 -2
  5. package/dist/{assetCataloger-FRO4G6OU.js → assetCataloger-A5ZLSCMZ.js} +3 -3
  6. package/dist/{auth-FRWLCO36.js → auth-5HOVCZMV.js} +1 -1
  7. package/dist/{autoUpdate-E4OZXQK6.js → autoUpdate-WVNL5K3D.js} +2 -2
  8. package/dist/{backgroundChecks-FSUVHN5L.js → backgroundChecks-IGF54SFH.js} +3 -3
  9. package/dist/backgroundChecksWorker.js +3 -3
  10. package/dist/{batchRender-4U73BYTU.js → batchRender-T6JEGCQV.js} +4 -4
  11. package/dist/{beats-U6KJWF35.js → beats-37T6Y6QP.js} +13 -13
  12. package/dist/{benchmark-5ONT7V7B.js → benchmark-ZVVLOWPP.js} +9 -9
  13. package/dist/{browser-R4DU46IE.js → browser-4SSJOLH4.js} +39 -55
  14. package/dist/{bundleWithLocalizedFonts-VH55VOAL.js → bundleWithLocalizedFonts-XWK423QP.js} +2 -2
  15. package/dist/{capture-OGQAEYKO.js → capture-2SF7YP57.js} +2 -2
  16. package/dist/{capture-26G2DPSI.js → capture-KZXOPBMO.js} +18 -18
  17. package/dist/{catalog-RKORTIUE.js → catalog-NLD4A2C6.js} +12 -12
  18. package/dist/{catch-up-4OBERRQO.js → catch-up-4BSRN6IR.js} +4 -4
  19. package/dist/{check-P2XDN57S.js → check-IA44QQWM.js} +66 -55
  20. package/dist/{checkBrowser-NTGJGAEA.js → checkBrowser-VM35KZMW.js} +81 -30
  21. package/dist/{chunk-LVX6NK36.js → chunk-3IBMBNYY.js} +1 -1
  22. package/dist/{chunk-6XMFEICO.js → chunk-3KU3PFDW.js} +77 -92
  23. package/dist/{chunk-PJU63L6W.js → chunk-4ORLNTET.js} +1 -1
  24. package/dist/{chunk-DF2BHILQ.js → chunk-4QWD6JYN.js} +3 -3
  25. package/dist/{chunk-BT27OYYX.js → chunk-5NRXZGKK.js} +18 -12
  26. package/dist/{chunk-UM2GRYM3.js → chunk-5WVW7P23.js} +3 -3
  27. package/dist/{chunk-I7QGQAZM.js → chunk-6LGNTUZN.js} +7 -7
  28. package/dist/{chunk-LAOCA3MQ.js → chunk-A4Q7W4LV.js} +13 -8
  29. package/dist/{chunk-4PFZPP2N.js → chunk-A6S35NYA.js} +54 -16
  30. package/dist/{chunk-QRKLPWEX.js → chunk-ADF3X6GM.js} +11 -2
  31. package/dist/chunk-AUWHD7RA.js +3753 -0
  32. package/dist/{chunk-W35EKBS6.js → chunk-BFACGEII.js} +2 -2
  33. package/dist/{chunk-HW4AKJ5F.js → chunk-BIAC7IJT.js} +1 -1
  34. package/dist/{chunk-ZLW2V65P.js → chunk-BSQ7XANM.js} +3 -2
  35. package/dist/{chunk-LCSMOLGX.js → chunk-BYSBRF24.js} +69 -8
  36. package/dist/{chunk-2LBBHMKT.js → chunk-CFUXNBWH.js} +1 -1
  37. package/dist/{chunk-BFDJ63J5.js → chunk-DDIVWXN7.js} +1 -1
  38. package/dist/{chunk-KOEWEFHK.js → chunk-DIRJ322I.js} +10 -5
  39. package/dist/{chunk-FVYXDXEV.js → chunk-DWUKJQTO.js} +2 -2
  40. package/dist/{chunk-POLR33RB.js → chunk-EPE2HRJK.js} +15 -17
  41. package/dist/{chunk-UICWP23D.js → chunk-FBCULCAL.js} +3 -3
  42. package/dist/{chunk-F26LCYRY.js → chunk-FDT6KQV4.js} +142 -81
  43. package/dist/{chunk-P7THMINS.js → chunk-FXJJCPPB.js} +1 -1
  44. package/dist/{chunk-BYK73CR7.js → chunk-GBGJ7A3D.js} +5 -5
  45. package/dist/{sherpa-LVIUEAGF.js → chunk-GEMJYBRU.js} +11 -45
  46. package/dist/{chunk-KO5QBV73.js → chunk-GMAJB2P6.js} +1 -1
  47. package/dist/{chunk-KQLGTGE5.js → chunk-GRY3PN3K.js} +2 -2
  48. package/dist/{chunk-7UYBQ6NF.js → chunk-GXJEGZL2.js} +30 -20
  49. package/dist/{chunk-KJJLA674.js → chunk-GZVP4TWN.js} +1 -46
  50. package/dist/{chunk-QVYVUCWD.js → chunk-HB7FJOY4.js} +1 -1
  51. package/dist/{chunk-QFN7NVWM.js → chunk-ICKDMTLA.js} +1 -1
  52. package/dist/{chunk-XONFUK3M.js → chunk-IEIJRMLZ.js} +47 -35
  53. package/dist/{chunk-PAGIICKN.js → chunk-IHTRYDFN.js} +1 -1
  54. package/dist/chunk-IR35BCLK.js +13 -0
  55. package/dist/{chunk-KDPYVTEU.js → chunk-KBB4RG6F.js} +6 -1
  56. package/dist/{chunk-ZQQ3SEIE.js → chunk-KIFHYXPB.js} +99 -31
  57. package/dist/{chunk-TAMQYQGJ.js → chunk-LAFJSYDL.js} +1 -1
  58. package/dist/{chunk-U3X3TPBW.js → chunk-MAYCCGMQ.js} +11 -2
  59. package/dist/{chunk-HC7EVDEL.js → chunk-OB5MJR4W.js} +1 -1
  60. package/dist/{chunk-N3DQEVSI.js → chunk-OTHDVXOJ.js} +8 -5
  61. package/dist/{chunk-Y6QL2LGO.js → chunk-PLR7Z6TC.js} +1 -1
  62. package/dist/{chunk-OBXQRJWQ.js → chunk-QATWIE4K.js} +1 -1
  63. package/dist/{chunk-HLYOEIWM.js → chunk-QLSM7K5S.js} +2 -2
  64. package/dist/{chunk-LW6HO6U3.js → chunk-R42WWQVB.js} +1 -1
  65. package/dist/{chunk-HMT2RPZU.js → chunk-U5QY7KTN.js} +13 -9
  66. package/dist/chunk-UPFUC4DB.js +136 -0
  67. package/dist/{chunk-33RZPZ4K.js → chunk-UXO2L7HJ.js} +366 -156
  68. package/dist/{chunk-GNBGPUJ7.js → chunk-VHFEMFYL.js} +3 -3
  69. package/dist/{chunk-4M4EC7JX.js → chunk-VRW6EBHV.js} +12 -1
  70. package/dist/{chunk-VIVBA6E6.js → chunk-XBX4QJP7.js} +1 -1
  71. package/dist/{chunk-CLOH4LIB.js → chunk-XIHCMVMG.js} +2 -2
  72. package/dist/{chunk-TVCRUG2S.js → chunk-ZEUONDQG.js} +1 -1
  73. package/dist/{chunk-OSSHSIMD.js → chunk-ZF7SXQJ7.js} +1 -1
  74. package/dist/{chunk-5FISWJ37.js → chunk-ZIYZ77MR.js} +4 -4
  75. package/dist/{chunk-3NE7ZYU2.js → chunk-ZSVUAVMT.js} +3 -3
  76. package/dist/{clean-YHGPY33K.js → clean-RDKJB5FK.js} +18 -18
  77. package/dist/cli.js +68 -56
  78. package/dist/{cloud-DOFI5ISR.js → cloud-QVPVVNCW.js} +4 -4
  79. package/dist/{cloudrun-KTZ4XDHL.js → cloudrun-3RE2UCQG.js} +4 -4
  80. package/dist/{compare-SMMIZN2C.js → compare-LEDOY7MO.js} +25 -25
  81. package/dist/{compiler-TEXQWW6N.js → compiler-F54OZZPM.js} +10 -4
  82. package/dist/{component-B3GCNGSS.js → component-N42M67WY.js} +3 -3
  83. package/dist/{compositions-OQNZZTKF.js → compositions-VB2J2LZI.js} +11 -11
  84. package/dist/{delete-EL5LVSEP.js → delete-Q6RWMJ4Z.js} +2 -2
  85. package/dist/{dist-5UVIRMSM.js → dist-KDJTFJHA.js} +2 -2
  86. package/dist/{dist-XN234YFQ.js → dist-Q4BI5A73.js} +4 -4
  87. package/dist/{doctor-UOL3OI2A.js → doctor-JMMUHDRV.js} +13 -13
  88. package/dist/{events-K2DB2PBC.js → events-6ZBSIBE5.js} +2 -2
  89. package/dist/{events-NIZBS6S7.js → events-RGUAHIBW.js} +3 -3
  90. package/dist/{feedback-QMP4E6L7.js → feedback-FIID6MZW.js} +9 -9
  91. package/dist/{ffmpeg-QIUFEFTY.js → ffmpeg-T5SH2EFJ.js} +3 -2
  92. package/dist/{figma-2AUJRNVC.js → figma-BL3JJC36.js} +3 -3
  93. package/dist/fontLocalizeCli.js +9 -9
  94. package/dist/{get-P5NC7SRM.js → get-P46GS7S6.js} +2 -2
  95. package/dist/{gpuPolicy-7N5MCE2G.js → gpuPolicy-AQ2BPLBK.js} +11 -9
  96. package/dist/{grade-compare-HV5AFZ6P.js → grade-compare-GDCWU7ZZ.js} +27 -27
  97. package/dist/{help-3TSTXBLN.js → help-HJR3RWFG.js} +2 -2
  98. package/dist/{history-D65XFJ6D.js → history-GGO5AWJB.js} +5 -5
  99. package/dist/hyperframe-runtime.js +59 -59
  100. package/dist/hyperframe.manifest.json +1 -1
  101. package/dist/hyperframe.runtime.iife.js +59 -59
  102. package/dist/hyperframes-player.global.js +3 -3
  103. package/dist/{info-TXV7MLFO.js → info-3GFWUBJC.js} +8 -8
  104. package/dist/{init-OSDMJZJV.js → init-36OPU2ZG.js} +35 -36
  105. package/dist/{inspect-RQQSAYIJ.js → inspect-F2HEL5ON.js} +26 -26
  106. package/dist/{installer-CDOB32HE.js → installer-NI6P6GC4.js} +5 -5
  107. package/dist/{keyframes-V75Q6GRM.js → keyframes-7OY3LMAZ.js} +6 -6
  108. package/dist/{lambda-RODV4RCX.js → lambda-6GJIWNJ3.js} +5 -5
  109. package/dist/{layout-IZAI5SFC.js → layout-FTMLNL5A.js} +26 -26
  110. package/dist/{lint-PLXOXY5R.js → lint-U6POHUON.js} +8 -8
  111. package/dist/{lintProject-OQ6H6XBR.js → lintProject-UBZ3TDHU.js} +2 -2
  112. package/dist/{list-BQBPNKDZ.js → list-IP5KI5Q2.js} +2 -2
  113. package/dist/{login-NP6F4FHC.js → login-4VWTVPHG.js} +4 -4
  114. package/dist/{manager-CT3D67CG.js → manager-BPSR6D3V.js} +2 -2
  115. package/dist/{manager-GXYF4AVO.js → manager-YKMHR6LN.js} +8 -5
  116. package/dist/{media-treatment-PXGMZ4A4.js → media-treatment-XGV44AGV.js} +16 -13
  117. package/dist/{models-CEZM6XZ7.js → models-UCXB6UR5.js} +59 -19
  118. package/dist/{motionShot-R4GHYWYM.js → motionShot-WPVNBXKA.js} +21 -22
  119. package/dist/{normalize-H6KUHHEI.js → normalize-U3SL5VG3.js} +2 -2
  120. package/dist/{normalize-audio-MQ7NFCOV.js → normalize-audio-TYJ3UEHO.js} +6 -6
  121. package/dist/{open-AKQTGNXF.js → open-HENOLVB2.js} +4 -4
  122. package/dist/parakeetRunner-VFQ6WFTD.js +67 -0
  123. package/dist/{pipeline-SYJYP2RM.js → pipeline-6WPSC3H4.js} +10 -9
  124. package/dist/{play-NBH4B7PL.js → play-OI53WNUM.js} +19 -19
  125. package/dist/{preflight-Y5DN5D37.js → preflight-B4AFK3RG.js} +4 -4
  126. package/dist/{present-XAX55CFS.js → present-NGMEQ4NO.js} +15 -15
  127. package/dist/{preview-OLANKNFA.js → preview-VT2HWRWZ.js} +19 -19
  128. package/dist/{producer-Q6YNZZUJ.js → producer-7TYEAUF7.js} +1 -1
  129. package/dist/{projectConfig-EM26P45P.js → projectConfig-B2AF5DXY.js} +7 -7
  130. package/dist/{publication-OXWJZKSX.js → publication-2PANQVC6.js} +3 -3
  131. package/dist/{publish-4VBWP2UB.js → publish-O5OL42BG.js} +11 -11
  132. package/dist/{remove-background-REB7427G.js → remove-background-T2JM6UVP.js} +11 -10
  133. package/dist/{render-K25YOG3M.js → render-OLPKOJRU.js} +11 -11
  134. package/dist/{render-3CVZ2SZG.js → render-W7OFXDOJ.js} +4 -4
  135. package/dist/{render-TSE75QUL.js → render-Z253WHBK.js} +26 -26
  136. package/dist/{render-batch-GGUOK3DS.js → render-batch-46IIBFKL.js} +4 -4
  137. package/dist/{renderCancellation-PZRWB53I.js → renderCancellation-QTZOCDJ2.js} +10 -9
  138. package/dist/renderSetupWorker.js +32 -44
  139. package/dist/{resolver-S5VK6UCB.js → resolver-J2PL5FVJ.js} +5 -5
  140. package/dist/sherpa-ZF42V3RN.js +75 -0
  141. package/dist/sherpaWorker.js +2 -1
  142. package/dist/skills/hyperframes/scripts/lib/frame-packets-core.mjs +2 -20
  143. package/dist/skills/hyperframes/scripts/lib/main-module.mjs +13 -0
  144. package/dist/skills/hyperframes/scripts/lib/main-module.test.mjs +51 -0
  145. package/dist/skills/hyperframes/scripts/plugin-cli.mjs +4 -3
  146. package/dist/skills/hyperframes/scripts/plugin-cli.test.mjs +5 -1
  147. package/dist/skills/hyperframes-cli/SKILL.md +2 -0
  148. package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +1 -1
  149. package/dist/skills/media-use/audio/scripts/heygen-voice.mjs +2 -2
  150. package/dist/skills/media-use/audio/scripts/lib/main-module.mjs +13 -0
  151. package/dist/skills/media-use/scripts/lib/cube-validate.mjs +2 -3
  152. package/dist/skills/media-use/scripts/lib/main-module.mjs +13 -0
  153. package/dist/{skills-I65FU5HD.js → skills-ACDOO3PX.js} +7 -7
  154. package/dist/{skillsUpdateCheck-J57RNNIV.js → skillsUpdateCheck-L5OXVVPU.js} +3 -3
  155. package/dist/{snapshot-YROOGY22.js → snapshot-UUVNZOW5.js} +28 -28
  156. package/dist/{src-JO5JOV7Y.js → src-BSLKXEGX.js} +10 -6
  157. package/dist/{src-X426BZAG.js → src-KS3AWFGT.js} +8 -8
  158. package/dist/{staticProjectServer-5VMBS262.js → staticProjectServer-2TTVGPQH.js} +15 -15
  159. package/dist/studio/assets/{hyperframes-player-BXSuksFh.js → hyperframes-player-DZ5JrMDg.js} +3 -3
  160. package/dist/studio/assets/{index-FgyO_5Kv.js → index-BOs6C0aZ.js} +1 -1
  161. package/dist/studio/assets/{index-BgOVCqjo.js → index-BqFRhNZa.js} +1 -1
  162. package/dist/studio/assets/{index-J7jpL4pj.js → index-D8W0a0Z9.js} +149 -149
  163. package/dist/studio/{chunk-TFLU2MGI.js → chunk-6JY3MTIX.js} +108 -2
  164. package/dist/studio/{chunk-H2DUOXTF.js → chunk-NZPJUZKT.js} +100 -4
  165. package/dist/studio/{chunk-UYWON5AX.js → chunk-TP6D3U37.js} +7 -100
  166. package/dist/studio/{chunk-LOVBLSMR.js → chunk-Y2OHHNM2.js} +35 -19
  167. package/dist/studio/{domEditingLayers-KBTPQKGA.js → domEditingLayers-PTP2ICGM.js} +3 -3
  168. package/dist/studio/index.d.ts +8 -5
  169. package/dist/studio/index.html +1 -1
  170. package/dist/studio/index.js +545 -495
  171. package/dist/studio/player.js +3 -3
  172. package/dist/studio/ui.d.ts +4 -5
  173. package/dist/studio/ui.js +1 -1
  174. package/dist/{studio-api-VRBZUGAE.js → studio-api-WYWF7XRC.js} +9 -9
  175. package/dist/{studioServer-DBNKZ6I6.js → studioServer-3FG4YTP7.js} +72 -54
  176. package/dist/{telemetry-CXAX3TO3.js → telemetry-PAOOB4T4.js} +3 -3
  177. package/dist/{timeline-VQ34WH7J.js → timeline-JIIJKKPC.js} +69 -47
  178. package/dist/{tokens-62R7VFAZ.js → tokens-3QRAM75Z.js} +2 -2
  179. package/dist/{transcribe-XOHPPL2O.js → transcribe-QSBP5MUG.js} +115 -51
  180. package/dist/{transcribe-7KR7IPJA.js → transcribe-SZB6UMXE.js} +14 -12
  181. package/dist/{tts-MUV42JXT.js → tts-B6AHFQEO.js} +1 -1
  182. package/dist/{updateCheck-POYNH3B5.js → updateCheck-V3DSB26K.js} +2 -2
  183. package/dist/{upgrade-NBNOT7J2.js → upgrade-L5GISOEV.js} +2 -2
  184. package/dist/{urlDownloader-3NS63A7B.js → urlDownloader-52FERT4X.js} +6 -6
  185. package/dist/{validate-Y2QDHAEP.js → validate-HVVLX7KO.js} +20 -20
  186. package/dist/{video-SJDMTSTL.js → video-MEOWRMFG.js} +7 -7
  187. package/package.json +1 -1
  188. package/dist/chunk-6TPZSTCL.js +0 -3681
  189. package/dist/studio/chunk-EEXSYY5F.js.map +0 -1
  190. package/dist/studio/chunk-H2DUOXTF.js.map +0 -1
  191. package/dist/studio/chunk-LOVBLSMR.js.map +0 -1
  192. package/dist/studio/chunk-PZ5AY32C.js.map +0 -1
  193. package/dist/studio/chunk-TFLU2MGI.js.map +0 -1
  194. package/dist/studio/chunk-UYWON5AX.js.map +0 -1
  195. package/dist/studio/domEditingLayers-KBTPQKGA.js.map +0 -1
  196. package/dist/studio/index.css.map +0 -1
  197. package/dist/studio/index.js.map +0 -1
  198. package/dist/studio/player.js.map +0 -1
  199. package/dist/studio/styles/tailwind-preset.js.map +0 -1
  200. package/dist/studio/ui.js.map +0 -1
  201. package/dist/{screenshotCapture-TEUIWXWN.js → screenshotCapture-3JXXUZND.js} +3 -3
  202. package/dist/{synthesize-C3IWDAXF.js → synthesize-UCI4X5O7.js} +4 -4
@@ -26,7 +26,7 @@ import {
26
26
  renderFrameIdForRenderId,
27
27
  resolveAuthoredTimingWindow,
28
28
  toFps
29
- } from "./chunk-4M4EC7JX.js";
29
+ } from "./chunk-VRW6EBHV.js";
30
30
  import {
31
31
  AUDIO_GROUP_RENDER_ID_ATTR,
32
32
  COLOR_GRADING_SOURCE_HIDDEN_ATTR,
@@ -55,7 +55,7 @@ import {
55
55
  shiftRateLane,
56
56
  sourceTimeAt,
57
57
  timeAtSourceTime
58
- } from "./chunk-6TPZSTCL.js";
58
+ } from "./chunk-AUWHD7RA.js";
59
59
  import {
60
60
  clampAudioGain,
61
61
  isAudibleVideoElement
@@ -557,9 +557,93 @@ function applyConcreteGpuScreenshotClamp(currentForceScreenshot, resolvedGpuMode
557
557
  var SDR_RGB_TO_BT709_FILTER = "scale=out_color_matrix=bt709:out_range=tv:flags=neighbor";
558
558
  var SDR_CAPTURE_TO_BT709_FILTER = `scale=in_color_matrix=bt601:in_range=pc:flags=neighbor,format=gbrp,${SDR_RGB_TO_BT709_FILTER}`;
559
559
 
560
+ // ../parsers/dist/htmlAttributeSpans.js
561
+ function readAttributeValue(html, at) {
562
+ while (/[\t\n\f\r ]/.test(html.charAt(at))) at++;
563
+ const quote = ['"', "'"].includes(html.charAt(at)) ? html.charAt(at) : "";
564
+ if (quote) at++;
565
+ const valueStart = at;
566
+ if (quote) {
567
+ const closing = html.indexOf(quote, at);
568
+ at = closing === -1 ? html.length : closing;
569
+ } else {
570
+ while (at < html.length && !/[\t\n\f\r >]/.test(html[at])) at++;
571
+ }
572
+ const valueEnd = at;
573
+ const end = quote ? Math.min(valueEnd + 1, html.length) : valueEnd;
574
+ return { value: html.slice(valueStart, valueEnd), valueStart, valueEnd, quote, end };
575
+ }
576
+ function readAttribute(html, start) {
577
+ let at = start;
578
+ while (at < html.length && !/[\t\n\f\r =/>]/.test(html[at])) at++;
579
+ if (at === start) return null;
580
+ const name = html.slice(start, at).toLowerCase();
581
+ const nameEnd = at;
582
+ while (/[\t\n\f\r ]/.test(html[at] ?? "")) at++;
583
+ if (html[at] !== "=") return { kind: "boolean", name, start, end: nameEnd };
584
+ return { kind: "value", name, start, ...readAttributeValue(html, at + 1) };
585
+ }
586
+ function readOpeningTag(html, start, name, at) {
587
+ const attributes = [];
588
+ while (at < html.length) {
589
+ while (/[\t\n\f\r /]/.test(html[at] ?? "")) at++;
590
+ if (at >= html.length || html[at] === ">") break;
591
+ const attribute = readAttribute(html, at);
592
+ if (attribute === null) {
593
+ at++;
594
+ continue;
595
+ }
596
+ attributes.push(attribute);
597
+ at = attribute.end;
598
+ }
599
+ const closed = html[at] === ">";
600
+ return {
601
+ name: name.toLowerCase(),
602
+ start,
603
+ bodyEnd: at,
604
+ end: closed ? at + 1 : at,
605
+ closed,
606
+ rawTextEnd: null,
607
+ attributes
608
+ };
609
+ }
610
+ function scanHtmlOpeningTags(html) {
611
+ const tags = [];
612
+ const opening = /<!--[\s\S]*?(?:--!?>|$)|<([a-z][^\t\n\f\r />]*)/gi;
613
+ let match;
614
+ while ((match = opening.exec(html)) !== null) {
615
+ if (!match[1]) continue;
616
+ const tag = readOpeningTag(html, match.index, match[1], opening.lastIndex);
617
+ tags.push(tag);
618
+ opening.lastIndex = tag.end;
619
+ if ([
620
+ "script",
621
+ "style",
622
+ "textarea",
623
+ "title",
624
+ "xmp",
625
+ "iframe",
626
+ "noembed",
627
+ "noframes",
628
+ "plaintext"
629
+ ].includes(tag.name)) {
630
+ if (tag.name === "plaintext") {
631
+ tag.rawTextEnd = html.length;
632
+ break;
633
+ }
634
+ const closing = new RegExp(`</${tag.name}(?=[\\t\\n\\f\\r />])`, "gi");
635
+ closing.lastIndex = tag.end;
636
+ const end = closing.exec(html);
637
+ tag.rawTextEnd = end?.index ?? html.length;
638
+ opening.lastIndex = tag.rawTextEnd;
639
+ }
640
+ }
641
+ return tags;
642
+ }
643
+
560
644
  // ../engine/src/services/browserManager.ts
561
645
  import { execSync } from "child_process";
562
- import { existsSync, readdirSync } from "fs";
646
+ import { existsSync, readdirSync, statSync } from "fs";
563
647
  import { join as join2 } from "path";
564
648
  import { homedir } from "os";
565
649
 
@@ -801,6 +885,7 @@ async function getPuppeteer() {
801
885
  if (!_puppeteer) throw new Error("Neither puppeteer nor puppeteer-core found");
802
886
  return _puppeteer;
803
887
  }
888
+ var GPU_PROBE_TIMEOUT_MS = 15e3;
804
889
  async function probeHardwareWebGlInfo(ppt, options) {
805
890
  let probeBrowser;
806
891
  try {
@@ -810,37 +895,44 @@ async function probeHardwareWebGlInfo(ppt, options) {
810
895
  args: options.args,
811
896
  defaultViewport: { width: 64, height: 64 },
812
897
  executablePath: options.executablePath,
813
- timeout: options.browserTimeout
814
- });
815
- const page = await probeBrowser.newPage();
816
- return await page.evaluate(() => {
817
- const unavailable = { hasWebGL: false, vendor: "", renderer: "" };
818
- const c = document.createElement("canvas");
819
- let gl = c.getContext("webgl");
820
- if (gl === null) {
821
- gl = c.getContext("experimental-webgl");
822
- }
823
- if (gl === null) return unavailable;
824
- const ext = gl.getExtension("WEBGL_debug_renderer_info");
825
- let vendorParam = gl.VENDOR;
826
- let rendererParam = gl.RENDERER;
827
- if (ext !== null) {
828
- vendorParam = ext.UNMASKED_VENDOR_WEBGL;
829
- rendererParam = ext.UNMASKED_RENDERER_WEBGL;
830
- }
831
- const vendor = gl.getParameter(vendorParam);
832
- const renderer = gl.getParameter(rendererParam);
833
- return {
834
- hasWebGL: true,
835
- vendor: vendor == null ? "" : String(vendor),
836
- renderer: renderer == null ? "" : String(renderer)
837
- };
898
+ timeout: options.browserTimeout,
899
+ waitForInitialPage: false
838
900
  });
901
+ return await rejectAfter(
902
+ readWebGlInfo(probeBrowser),
903
+ GPU_PROBE_TIMEOUT_MS,
904
+ `GPU probe page did not answer within ${GPU_PROBE_TIMEOUT_MS}ms`
905
+ );
839
906
  } finally {
840
- await probeBrowser?.close().catch(() => {
841
- });
907
+ if (probeBrowser) await closeBrowserAfterFailedProbe(probeBrowser);
842
908
  }
843
909
  }
910
+ async function readWebGlInfo(browser) {
911
+ const page = await browser.newPage();
912
+ return await page.evaluate(() => {
913
+ const unavailable = { hasWebGL: false, vendor: "", renderer: "" };
914
+ const c = document.createElement("canvas");
915
+ let gl = c.getContext("webgl");
916
+ if (gl === null) {
917
+ gl = c.getContext("experimental-webgl");
918
+ }
919
+ if (gl === null) return unavailable;
920
+ const ext = gl.getExtension("WEBGL_debug_renderer_info");
921
+ let vendorParam = gl.VENDOR;
922
+ let rendererParam = gl.RENDERER;
923
+ if (ext !== null) {
924
+ vendorParam = ext.UNMASKED_VENDOR_WEBGL;
925
+ rendererParam = ext.UNMASKED_RENDERER_WEBGL;
926
+ }
927
+ const vendor = gl.getParameter(vendorParam);
928
+ const renderer = gl.getParameter(rendererParam);
929
+ return {
930
+ hasWebGL: true,
931
+ vendor: vendor == null ? "" : String(vendor),
932
+ renderer: renderer == null ? "" : String(renderer)
933
+ };
934
+ });
935
+ }
844
936
  function compareBrowserVersionsDescending(left, right) {
845
937
  const parse = (value) => {
846
938
  const version = value.slice(value.indexOf("-") + 1);
@@ -871,6 +963,14 @@ var CACHED_HEADLESS_SHELL_EXECUTABLES = {
871
963
  function cachedHeadlessShellExecutable(hostPlatform = process.platform, hostArch = process.arch) {
872
964
  return CACHED_HEADLESS_SHELL_EXECUTABLES[`${hostPlatform}/${hostArch}`];
873
965
  }
966
+ function isNonEmptyFile(path) {
967
+ try {
968
+ const stat = statSync(path, { throwIfNoEntry: false });
969
+ return stat?.isFile() === true && stat.size > 0;
970
+ } catch {
971
+ return false;
972
+ }
973
+ }
874
974
  function findCachedHeadlessShell(baseDir) {
875
975
  if (!existsSync(baseDir)) return void 0;
876
976
  const executable = cachedHeadlessShellExecutable();
@@ -881,7 +981,7 @@ function findCachedHeadlessShell(baseDir) {
881
981
  for (const version of versions) {
882
982
  if (exceedsChromeCeiling(version, ceiling)) continue;
883
983
  const binary = join2(baseDir, version, ...executable);
884
- if (existsSync(binary)) return binary;
984
+ if (isNonEmptyFile(binary)) return binary;
885
985
  }
886
986
  } catch {
887
987
  }
@@ -937,15 +1037,15 @@ async function awaitBeforeDeadline(operation, deadline, label) {
937
1037
  if (remainingMs <= 0) {
938
1038
  throw new Error(`beginFrame probe timeout before ${label}`);
939
1039
  }
1040
+ return rejectAfter(operation, remainingMs, `beginFrame probe timeout during ${label}`);
1041
+ }
1042
+ async function rejectAfter(operation, timeoutMs, message) {
940
1043
  let timeout;
941
1044
  try {
942
1045
  return await Promise.race([
943
1046
  operation,
944
1047
  new Promise((_, reject) => {
945
- timeout = setTimeout(
946
- () => reject(new Error(`beginFrame probe timeout during ${label}`)),
947
- remainingMs
948
- );
1048
+ timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
949
1049
  })
950
1050
  ]);
951
1051
  } finally {
@@ -1359,8 +1459,9 @@ function buildChromeArgs(options, config) {
1359
1459
  return chromeArgs;
1360
1460
  }
1361
1461
  function compositionRequiresWebGpu(html) {
1362
- for (const [tag] of html.matchAll(/<(?:[^<>"']|"[^"]*"|'[^']*')*>/g)) {
1363
- if (/\bdata-composition-id\b/i.test(tag)) return /\bdata-requires-webgpu(?:\s|=|>)/i.test(tag);
1462
+ for (const tag of scanHtmlOpeningTags(html)) {
1463
+ const names = tag.attributes.map((attribute) => attribute.name);
1464
+ if (names.includes("data-composition-id")) return names.includes("data-requires-webgpu");
1364
1465
  }
1365
1466
  return false;
1366
1467
  }
@@ -5170,6 +5271,7 @@ async function constructCaptureSession(input) {
5170
5271
  browserConsoleBuffer: [],
5171
5272
  scriptLoadFailures: [],
5172
5273
  pageErrors: [],
5274
+ subTimelineWaitMemo: sessionOptions.subTimelineWaitMemo ?? {},
5173
5275
  warnings: [],
5174
5276
  capturePerf: {
5175
5277
  frames: 0,
@@ -5318,15 +5420,52 @@ async function pollHfReady(page, timeoutMs, intervalMs = 100) {
5318
5420
  State: __hf=${diag.hasHf}, seek=${diag.hasSeek}, player=${diag.hasPlayer}, renderReady=${diag.renderReady}, duration=${diag.duration}`
5319
5421
  );
5320
5422
  }
5321
- async function pollSubCompositionTimelines(page, timeoutMs, intervalMs = 150, getScriptLoadFailures, scriptFailureGraceMs = 2e3, onPending, shouldStop) {
5423
+ async function listUnregisteredTimelineIds(page) {
5424
+ const evaluated = await page.evaluate(`(function() {
5425
+ var hosts = document.querySelectorAll("[data-composition-id]");
5426
+ var timelines = window.__timelines || {};
5427
+ var m = [];
5428
+ for (var i = 0; i < hosts.length; i++) {
5429
+ if (hosts[i].hasAttribute("data-no-timeline")) continue;
5430
+ var id = hosts[i].getAttribute("data-composition-id");
5431
+ if (id && !timelines[id]) m.push(id);
5432
+ }
5433
+ return m;
5434
+ })()`);
5435
+ return Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
5436
+ }
5437
+ async function waitForSubCompositionTimelines(session, page, timeoutMs) {
5438
+ const memo = session.subTimelineWaitMemo;
5439
+ session.subTimelineWaitOutcome = await pollSubCompositionTimelines(page, timeoutMs, {
5440
+ getScriptLoadFailures: () => session.scriptLoadFailures,
5441
+ onPending: (ids) => {
5442
+ session.pendingTimelineIds = [...ids];
5443
+ },
5444
+ shouldStop: () => session.vfxFailure !== void 0,
5445
+ knownUnregisteredIds: memo.unregisteredIds
5446
+ });
5447
+ const cleanTimeout = session.subTimelineWaitOutcome === "timeout" && session.vfxFailure === void 0 && session.pageErrors.length === 0 && session.scriptLoadFailures.length === 0;
5448
+ if (cleanTimeout) {
5449
+ memo.unregisteredIds = session.pendingTimelineIds ?? [];
5450
+ }
5451
+ }
5452
+ async function pollSubCompositionTimelines(page, timeoutMs, {
5453
+ intervalMs = 150,
5454
+ getScriptLoadFailures,
5455
+ scriptFailureGraceMs = 2e3,
5456
+ onPending,
5457
+ shouldStop,
5458
+ knownUnregisteredIds = []
5459
+ } = {}) {
5322
5460
  const expression = `(function() {
5323
5461
  var hosts = document.querySelectorAll("[data-composition-id]");
5324
5462
  if (hosts.length === 0) return true;
5325
5463
  var timelines = window.__timelines || {};
5464
+ var known = ${JSON.stringify(knownUnregisteredIds)};
5326
5465
  for (var i = 0; i < hosts.length; i++) {
5327
5466
  if (hosts[i].hasAttribute("data-no-timeline")) continue;
5328
5467
  var id = hosts[i].getAttribute("data-composition-id");
5329
- if (!id) continue;
5468
+ if (!id || known.indexOf(id) !== -1) continue;
5330
5469
  if (!timelines[id]) return false;
5331
5470
  }
5332
5471
  return true;
@@ -5350,7 +5489,11 @@ async function pollSubCompositionTimelines(page, timeoutMs, intervalMs = 150, ge
5350
5489
  }
5351
5490
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
5352
5491
  }
5353
- if (ready) {
5492
+ const pendingIds = ready && knownUnregisteredIds.length === 0 ? [] : await listUnregisteredTimelineIds(page);
5493
+ if (ready && pendingIds.length > 0 && (getScriptLoadFailures?.() ?? []).length > 0) {
5494
+ scriptFailureBail = true;
5495
+ }
5496
+ if (ready && pendingIds.length === 0) {
5354
5497
  await page.evaluate(`(function() {
5355
5498
  if (typeof window.__hfForceTimelineRebind === "function") {
5356
5499
  window.__hfForceTimelineRebind();
@@ -5358,18 +5501,6 @@ async function pollSubCompositionTimelines(page, timeoutMs, intervalMs = 150, ge
5358
5501
  })()`);
5359
5502
  return "ready";
5360
5503
  }
5361
- const evaluated = await page.evaluate(`(function() {
5362
- var hosts = document.querySelectorAll("[data-composition-id]");
5363
- var timelines = window.__timelines || {};
5364
- var m = [];
5365
- for (var i = 0; i < hosts.length; i++) {
5366
- if (hosts[i].hasAttribute("data-no-timeline")) continue;
5367
- var id = hosts[i].getAttribute("data-composition-id");
5368
- if (id && !timelines[id]) m.push(id);
5369
- }
5370
- return m;
5371
- })()`);
5372
- const pendingIds = Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
5373
5504
  onPending?.(pendingIds);
5374
5505
  const missing = pendingIds.join(", ");
5375
5506
  if (scriptFailureBail) {
@@ -5739,17 +5870,7 @@ async function initializeSession(session) {
5739
5870
  const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
5740
5871
  await pollHfReady(page, pageReadyTimeout);
5741
5872
  logInitPhase("pollHfReady complete");
5742
- session.subTimelineWaitOutcome = await pollSubCompositionTimelines(
5743
- page,
5744
- pageReadyTimeout,
5745
- void 0,
5746
- () => session.scriptLoadFailures,
5747
- void 0,
5748
- (ids) => {
5749
- session.pendingTimelineIds = [...ids];
5750
- },
5751
- () => session.vfxFailure !== void 0
5752
- );
5873
+ await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
5753
5874
  logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
5754
5875
  assertVfxIntact(session);
5755
5876
  recordSubTimelineWarning(session, pageReadyTimeout);
@@ -5855,17 +5976,7 @@ async function initializeSession(session) {
5855
5976
  const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
5856
5977
  await pollHfReady(page, pageReadyTimeout);
5857
5978
  logInitPhase("pollHfReady complete");
5858
- session.subTimelineWaitOutcome = await pollSubCompositionTimelines(
5859
- page,
5860
- pageReadyTimeout,
5861
- void 0,
5862
- () => session.scriptLoadFailures,
5863
- void 0,
5864
- (ids) => {
5865
- session.pendingTimelineIds = [...ids];
5866
- },
5867
- () => session.vfxFailure !== void 0
5868
- );
5979
+ await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
5869
5980
  logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
5870
5981
  assertVfxIntact(session);
5871
5982
  recordSubTimelineWarning(session, pageReadyTimeout);
@@ -6302,7 +6413,10 @@ async function createStaticVerificationPage(session) {
6302
6413
  });
6303
6414
  await page.evaluate(`window.__hfFlushSync?.()`);
6304
6415
  await pollHfReady(page, pageReadyTimeout);
6305
- await pollSubCompositionTimelines(page, pageReadyTimeout);
6416
+ await pollSubCompositionTimelines(page, pageReadyTimeout, {
6417
+ getScriptLoadFailures: () => session.scriptLoadFailures,
6418
+ knownUnregisteredIds: session.subTimelineWaitMemo.unregisteredIds
6419
+ });
6306
6420
  await applyVideoMetadataHints(page, session.options.videoMetadataHints);
6307
6421
  const skipVideoIds = session.options.skipReadinessVideoIds ?? [];
6308
6422
  await Promise.all([
@@ -7231,7 +7345,7 @@ import {
7231
7345
  openSync,
7232
7346
  readFileSync as readFileSync3,
7233
7347
  readdirSync as readdirSync2,
7234
- statSync as statSync2,
7348
+ statSync as statSync3,
7235
7349
  writeFileSync as writeFileSync2,
7236
7350
  writeSync
7237
7351
  } from "fs";
@@ -7425,7 +7539,7 @@ var H264_ENCODER_BY_GPU = {
7425
7539
  qsv: "h264_qsv",
7426
7540
  amf: "h264_amf"
7427
7541
  };
7428
- var GPU_PROBE_TIMEOUT_MS = 2e3;
7542
+ var GPU_PROBE_TIMEOUT_MS2 = 2e3;
7429
7543
  var GPU_PROBE_KILL_GRACE_MS = 1e3;
7430
7544
  function getCompiledGpuEncoders(ffmpegEncodersStdout) {
7431
7545
  return GPU_ENCODER_CANDIDATES.filter(
@@ -7542,7 +7656,7 @@ async function canUseGpuEncoder(encoder) {
7542
7656
  });
7543
7657
  trackChildProcess(ffmpeg);
7544
7658
  const outcome = await new ManagedChildProcess(ffmpeg, {
7545
- deadlineAtMs: Date.now() + GPU_PROBE_TIMEOUT_MS,
7659
+ deadlineAtMs: Date.now() + GPU_PROBE_TIMEOUT_MS2,
7546
7660
  terminationGraceMs: GPU_PROBE_KILL_GRACE_MS
7547
7661
  }).wait();
7548
7662
  const usable = outcome.reason === "exit" && outcome.exitCode === 0;
@@ -7558,7 +7672,7 @@ async function canUseGpuEncoder(encoder) {
7558
7672
  function logGpuProbeFailure(encoder, result) {
7559
7673
  if (!isGpuProbeDebugEnabled()) return;
7560
7674
  if (result.code === 0 && !result.error && !result.timedOut) return;
7561
- const reason = result.error ? result.error.message : result.timedOut ? `timed out after ${GPU_PROBE_TIMEOUT_MS}ms` : `exit=${String(result.code)} signal=${String(result.signal ?? "")}`;
7675
+ const reason = result.error ? result.error.message : result.timedOut ? `timed out after ${GPU_PROBE_TIMEOUT_MS2}ms` : `exit=${String(result.code)} signal=${String(result.signal ?? "")}`;
7562
7676
  const stderr = result.stderr?.trim();
7563
7677
  console.warn(`[gpuEncoder] ${encoder} probe failed: ${reason}${stderr ? `
7564
7678
  ${stderr}` : ""}`);
@@ -7720,6 +7834,11 @@ ${tail}` : windowsMessage;
7720
7834
  ffmpeg stderr (tail):
7721
7835
  ${tail}` : `FFmpeg exited with code ${exitCode}`;
7722
7836
  }
7837
+ function describeFfmpegFailure(result, timeoutMs) {
7838
+ const error = formatFfmpegError(result.exitCode, result.stderr);
7839
+ if (result.terminationReason !== "deadline") return error;
7840
+ return `FFmpeg timed out after ${timeoutMs} ms (ffmpegProcessTimeout; long renders can raise FFMPEG_PROCESS_TIMEOUT_MS). ${error}`;
7841
+ }
7723
7842
  function spawnFfmpeg(args) {
7724
7843
  const ffmpeg = spawn2(getFfmpegBinary(), args, { windowsHide: true });
7725
7844
  trackChildProcess(ffmpeg);
@@ -7743,7 +7862,16 @@ function toResult(outcome, stderr = outcome.stderr) {
7743
7862
  }
7744
7863
  return result;
7745
7864
  }
7865
+ var ABORTED_BEFORE_START = {
7866
+ success: false,
7867
+ exitCode: null,
7868
+ signal: null,
7869
+ stderr: "",
7870
+ durationMs: 0,
7871
+ terminationReason: "abort"
7872
+ };
7746
7873
  async function runFfmpeg(args, opts) {
7874
+ if (opts?.signal?.aborted) return { ...ABORTED_BEFORE_START };
7747
7875
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
7748
7876
  const managed = new ManagedChildProcess(spawnFfmpeg(args), {
7749
7877
  signal: opts?.signal,
@@ -7753,6 +7881,7 @@ async function runFfmpeg(args, opts) {
7753
7881
  return toResult(await managed.wait());
7754
7882
  }
7755
7883
  async function runFfmpegPipeline(producerArgs, consumerArgs, opts) {
7884
+ if (opts?.signal?.aborted) return { ...ABORTED_BEFORE_START };
7756
7885
  const deadlineAtMs = Date.now() + (opts?.timeout ?? DEFAULT_TIMEOUT);
7757
7886
  const producer = spawnFfmpeg(producerArgs);
7758
7887
  const consumer = spawnFfmpeg(consumerArgs);
@@ -7783,7 +7912,7 @@ async function runFfmpegPipeline(producerArgs, consumerArgs, opts) {
7783
7912
 
7784
7913
  // ../engine/src/utils/ffprobe.ts
7785
7914
  import { spawn as spawn3 } from "child_process";
7786
- import { createReadStream, readFileSync, statSync } from "fs";
7915
+ import { createReadStream, readFileSync, statSync as statSync2 } from "fs";
7787
7916
  import { StringDecoder } from "string_decoder";
7788
7917
  import { basename } from "path";
7789
7918
  var FFPROBE_STDERR_MAX_BYTES = 8 * 1024;
@@ -7886,7 +8015,9 @@ var audioMetadataCache = /* @__PURE__ */ new Map();
7886
8015
  var mediaProbeOutputCache = /* @__PURE__ */ new Map();
7887
8016
  var mediaProbeOutputSignalCaches = /* @__PURE__ */ new WeakMap();
7888
8017
  var MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES = 128;
7889
- var AAC_LC_SAMPLES_PER_PACKET = 1024;
8018
+ var AUDIO_DURATION_PROBE_MARGIN_SECONDS = 0.05;
8019
+ var AUDIO_DURATION_PROBE_TAIL_SECONDS = 1;
8020
+ var FFPROBE_READ_INTERVAL_EXPLICIT_FAR_END = "+2147483647";
7890
8021
  var STILL_IMAGE_DEMUXERS = /* @__PURE__ */ new Set([
7891
8022
  "apng",
7892
8023
  "bmp_pipe",
@@ -7938,7 +8069,7 @@ function isPngImageProbe(output) {
7938
8069
  }
7939
8070
  function mediaFileIdentity(filePath) {
7940
8071
  try {
7941
- const stat = statSync(filePath, { bigint: true });
8072
+ const stat = statSync2(filePath, { bigint: true });
7942
8073
  return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(":");
7943
8074
  } catch {
7944
8075
  return null;
@@ -8011,7 +8142,7 @@ async function readFileRange(filePath, start, length, signal) {
8011
8142
  async function hasCompletePngStructure(filePath, signal) {
8012
8143
  try {
8013
8144
  signal?.throwIfAborted();
8014
- const fileSize = statSync(filePath).size;
8145
+ const fileSize = statSync2(filePath).size;
8015
8146
  const signature = await readFileRange(filePath, 0, 8, signal);
8016
8147
  signal?.throwIfAborted();
8017
8148
  if (signature.length !== 8 || !signature.equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
@@ -8300,29 +8431,16 @@ async function extractAudioMetadata(filePath, options) {
8300
8431
  const streamDuration = audioStream.duration ? parseFloat(audioStream.duration) : void 0;
8301
8432
  const sampleRate = audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100;
8302
8433
  const audioCodec = audioStream.codec_name || "unknown";
8303
- const isAacLc = /^\s*LC\s*$/i.test(audioStream.profile ?? "");
8304
- if (audioCodec === "aac" && isAacLc && sampleRate > 0) {
8305
- try {
8306
- const packetStdout = await runFfprobe(
8307
- filePath,
8308
- [
8309
- "-select_streams",
8310
- "a:0",
8311
- "-count_packets",
8312
- "-show_entries",
8313
- "stream=nb_read_packets",
8314
- "-print_format",
8315
- "json"
8316
- ],
8317
- options?.signal
8318
- );
8319
- const packetOutput = parseProbeJson(packetStdout);
8320
- const packetCount = Number(packetOutput.streams[0]?.nb_read_packets);
8321
- if (Number.isFinite(packetCount) && packetCount > 0) {
8322
- durationSeconds = packetCount * AAC_LC_SAMPLES_PER_PACKET / sampleRate;
8323
- }
8324
- } catch (error) {
8325
- if (options?.signal?.aborted) throw error;
8434
+ if (sampleRate > 0) {
8435
+ const decoded = await probeDecodedAudioDuration(
8436
+ filePath,
8437
+ sampleRate,
8438
+ audioStartSeconds(audioStream, output.format),
8439
+ durationSeconds,
8440
+ options?.signal
8441
+ );
8442
+ if (decoded !== null) {
8443
+ durationSeconds = reconcileAudioDuration(durationSeconds, decoded, output.format);
8326
8444
  }
8327
8445
  }
8328
8446
  return {
@@ -8338,6 +8456,55 @@ async function extractAudioMetadata(filePath, options) {
8338
8456
  writeMetadataCache(audioMetadataCache, filePath, identity, probePromise);
8339
8457
  return probePromise;
8340
8458
  }
8459
+ function audioStartSeconds(stream, format) {
8460
+ const start = parseFloat(stream.start_time ?? format.start_time ?? "");
8461
+ return Number.isFinite(start) ? start : 0;
8462
+ }
8463
+ function reconcileAudioDuration(containerSeconds, decodedSeconds, format) {
8464
+ const difference = decodedSeconds - containerSeconds;
8465
+ const trustShorter = format.format_name === "aac";
8466
+ if (difference > AUDIO_DURATION_PROBE_MARGIN_SECONDS) return decodedSeconds;
8467
+ if (trustShorter && difference < -AUDIO_DURATION_PROBE_MARGIN_SECONDS) return decodedSeconds;
8468
+ return containerSeconds;
8469
+ }
8470
+ async function probeDecodedAudioDuration(filePath, sampleRate, startSeconds, containerDurationSeconds, signal) {
8471
+ const probe = async (readInterval) => {
8472
+ const args = [
8473
+ "-select_streams",
8474
+ "a:0",
8475
+ "-show_entries",
8476
+ "frame=best_effort_timestamp_time,nb_samples",
8477
+ "-of",
8478
+ "csv=p=0"
8479
+ ];
8480
+ if (readInterval) args.unshift("-read_intervals", readInterval);
8481
+ try {
8482
+ const stdout = await runFfprobe(
8483
+ filePath,
8484
+ args,
8485
+ signal,
8486
+ // CSV, not JSON: a tail truncated under the size cap still parses.
8487
+ { retainTail: true, maxChars: 64 * 1024 }
8488
+ );
8489
+ return stdout.split("\n").map((line) => {
8490
+ const [timestampText, samplesText] = line.trim().split(",");
8491
+ const timestamp = Number(timestampText);
8492
+ const nbSamples = Number(samplesText);
8493
+ return Number.isFinite(timestamp) && Number.isFinite(nbSamples) ? { timestamp, nbSamples } : void 0;
8494
+ }).filter((frame) => frame !== void 0).at(-1);
8495
+ } catch (error) {
8496
+ if (signal?.aborted) throw signal.reason ?? error;
8497
+ return void 0;
8498
+ }
8499
+ };
8500
+ const tailStart = Math.max(
8501
+ startSeconds,
8502
+ startSeconds + containerDurationSeconds - AUDIO_DURATION_PROBE_TAIL_SECONDS
8503
+ );
8504
+ const lastFrame = await probe(`${tailStart}%${FFPROBE_READ_INTERVAL_EXPLICIT_FAR_END}`) ?? await probe();
8505
+ if (!lastFrame) return null;
8506
+ return lastFrame.timestamp + lastFrame.nbSamples / sampleRate - startSeconds;
8507
+ }
8341
8508
  var keyframeCache = /* @__PURE__ */ new Map();
8342
8509
  var PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS = 2;
8343
8510
  async function analyzeKeyframeIntervals(filePath, metadata) {
@@ -8785,7 +8952,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
8785
8952
  failureReason: isExternalFfmpegInterruption(result) ? "external_interruption" : void 0
8786
8953
  };
8787
8954
  }
8788
- const fileSize = existsSync4(outputPath) ? statSync2(outputPath).size : 0;
8955
+ const fileSize = existsSync4(outputPath) ? statSync3(outputPath).size : 0;
8789
8956
  return {
8790
8957
  success: true,
8791
8958
  outputPath,
@@ -8919,7 +9086,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
8919
9086
  failureReason: concatResult.externalInterruption ? "external_interruption" : void 0
8920
9087
  };
8921
9088
  }
8922
- const fileSize = existsSync4(outputPath) ? statSync2(outputPath).size : 0;
9089
+ const fileSize = existsSync4(outputPath) ? statSync3(outputPath).size : 0;
8923
9090
  return {
8924
9091
  success: true,
8925
9092
  outputPath,
@@ -8973,7 +9140,7 @@ async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, confi
8973
9140
  success: result.success,
8974
9141
  outputPath,
8975
9142
  durationMs: result.durationMs,
8976
- error: !result.success ? formatFfmpegError(result.exitCode, result.stderr) : void 0,
9143
+ error: !result.success ? describeFfmpegFailure(result, processTimeout) : void 0,
8977
9144
  failureReason: result.failureReason
8978
9145
  };
8979
9146
  }
@@ -9065,7 +9232,7 @@ async function packageHls(videoPath, audioPath, outputDir, options) {
9065
9232
  success: result.success,
9066
9233
  outputPath: outputDir,
9067
9234
  durationMs: result.durationMs,
9068
- error: !result.success ? formatFfmpegError(result.exitCode, result.stderr) : void 0,
9235
+ error: !result.success ? describeFfmpegFailure(result, processTimeout) : void 0,
9069
9236
  failureReason: result.failureReason
9070
9237
  };
9071
9238
  }
@@ -9098,7 +9265,7 @@ async function applyFaststart(inputPath, outputPath, signal, config, fps, onSeco
9098
9265
  success: result.success,
9099
9266
  outputPath,
9100
9267
  durationMs: result.durationMs,
9101
- error: !result.success ? formatFfmpegError(result.exitCode, result.stderr) : void 0,
9268
+ error: !result.success ? describeFfmpegFailure(result, processTimeout) : void 0,
9102
9269
  failureReason: result.failureReason
9103
9270
  };
9104
9271
  }
@@ -9151,7 +9318,7 @@ function quantizationTableError(buffer, start, end) {
9151
9318
  // ../engine/src/services/streamingEncoder.ts
9152
9319
  import { spawn as spawn4 } from "child_process";
9153
9320
  import { once } from "events";
9154
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, statSync as statSync3 } from "fs";
9321
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, statSync as statSync4 } from "fs";
9155
9322
  import { dirname as dirname3 } from "path";
9156
9323
  function createFrameReorderBuffer(startFrame, endFrame) {
9157
9324
  let cursor = startFrame;
@@ -9516,7 +9683,7 @@ FFmpeg stopped after ${streamingTimeout} ms without consuming a frame.` : "";
9516
9683
  }) ? "external_interruption" : void 0
9517
9684
  };
9518
9685
  }
9519
- const fileSize = existsSync5(outputPath) ? statSync3(outputPath).size : 0;
9686
+ const fileSize = existsSync5(outputPath) ? statSync4(outputPath).size : 0;
9520
9687
  return { success: true, durationMs, fileSize };
9521
9688
  },
9522
9689
  getExitStatus: () => exitStatus,
@@ -9614,6 +9781,36 @@ import { copyFileSync as copyFileSync2, existsSync as existsSync8, linkSync as l
9614
9781
  import { join as join10 } from "path";
9615
9782
  import { cpus } from "os";
9616
9783
 
9784
+ // ../engine/src/utils/concurrencyLimit.ts
9785
+ function createConcurrencyLimit(limit) {
9786
+ let active = 0;
9787
+ const waiting = [];
9788
+ const waitForSlot = (signal) => new Promise((resolve) => {
9789
+ const granted = () => {
9790
+ signal?.removeEventListener("abort", aborted);
9791
+ resolve(true);
9792
+ };
9793
+ const aborted = () => {
9794
+ waiting.splice(waiting.indexOf(granted), 1);
9795
+ resolve(false);
9796
+ };
9797
+ waiting.push(granted);
9798
+ signal?.addEventListener("abort", aborted, { once: true });
9799
+ });
9800
+ return async (task, signal) => {
9801
+ if (signal?.aborted) return task();
9802
+ if (active < limit) active++;
9803
+ else if (!await waitForSlot(signal)) return task();
9804
+ try {
9805
+ return await task();
9806
+ } finally {
9807
+ const next = waiting.shift();
9808
+ if (next) next();
9809
+ else active--;
9810
+ }
9811
+ };
9812
+ }
9813
+
9617
9814
  // ../engine/src/services/referenceResolver.ts
9618
9815
  function findReferenceTargetEl(doc, refId) {
9619
9816
  return doc.getElementById(refId) ?? doc.querySelector(`[data-composition-id="${refId}"]`);
@@ -9736,7 +9933,7 @@ import {
9736
9933
  readdirSync as readdirSync3,
9737
9934
  rmdirSync,
9738
9935
  rmSync as rmSync2,
9739
- statSync as statSync4,
9936
+ statSync as statSync5,
9740
9937
  unlinkSync
9741
9938
  } from "fs";
9742
9939
  import { createHash, randomUUID } from "crypto";
@@ -10346,7 +10543,7 @@ async function fetchToPartial(url, partialPath, controller, options) {
10346
10543
  const readableStream = Readable.fromWeb(response.body);
10347
10544
  const fileStream = createWriteStream(partialPath, { flags: "wx" });
10348
10545
  await pipeline(readableStream, inspector, fileStream, { signal: controller.signal });
10349
- const localSize = statSync4(partialPath).size;
10546
+ const localSize = statSync5(partialPath).size;
10350
10547
  const sha256Bytes = sha256.digest();
10351
10548
  const localSha256 = sha256Bytes.toString("hex");
10352
10549
  const md5Base64 = md5.digest("base64");
@@ -10760,7 +10957,7 @@ import {
10760
10957
  readdirSync as readdirSync5,
10761
10958
  renameSync,
10762
10959
  rmSync as rmSync3,
10763
- statSync as statSync5,
10960
+ statSync as statSync6,
10764
10961
  utimesSync,
10765
10962
  writeFileSync as writeFileSync3
10766
10963
  } from "fs";
@@ -10821,7 +11018,7 @@ var SCHEMA_PREFIX = "hfcache-v6-";
10821
11018
  var KEY_HEX_CHARS = 16;
10822
11019
  function readKeyStat(videoPath) {
10823
11020
  try {
10824
- const stat = statSync5(videoPath);
11021
+ const stat = statSync6(videoPath);
10825
11022
  return { mtimeMs: Math.floor(stat.mtimeMs), size: stat.size };
10826
11023
  } catch {
10827
11024
  return null;
@@ -10955,7 +11152,7 @@ function removeDir(path) {
10955
11152
  }
10956
11153
  function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
10957
11154
  try {
10958
- const dirStat = statSync5(dir);
11155
+ const dirStat = statSync6(dir);
10959
11156
  if (isPartialChild(name) && now - dirStat.mtimeMs >= minAgeMs) {
10960
11157
  remove(dir);
10961
11158
  stats.agedPartialsRemoved += 1;
@@ -10963,7 +11160,7 @@ function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
10963
11160
  }
10964
11161
  let lastUseMs = dirStat.mtimeMs;
10965
11162
  try {
10966
- lastUseMs = statSync5(join9(dir, COMPLETE_SENTINEL)).mtimeMs;
11163
+ lastUseMs = statSync6(join9(dir, COMPLETE_SENTINEL)).mtimeMs;
10967
11164
  } catch {
10968
11165
  }
10969
11166
  return { dir, size: directorySizeBytes(dir), lastUseMs, ageMs: now - lastUseMs };
@@ -10973,7 +11170,7 @@ function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
10973
11170
  }
10974
11171
  function gcSweepDue(rootDir, maxAgeMs) {
10975
11172
  try {
10976
- return Date.now() - statSync5(join9(rootDir, GC_MARKER)).mtimeMs > maxAgeMs;
11173
+ return Date.now() - statSync6(join9(rootDir, GC_MARKER)).mtimeMs > maxAgeMs;
10977
11174
  } catch {
10978
11175
  return true;
10979
11176
  }
@@ -11472,6 +11669,8 @@ function restoreSourceColourFilter(metadata) {
11472
11669
  return tags.length > 0 ? [`setparams=${tags.map(([key, value]) => `${key}=${value}`).join(":")}`] : [];
11473
11670
  }
11474
11671
  var EXTRACTION_SEGMENT_SECONDS = 120;
11672
+ var EXTRACTION_SLOTS = Math.max(1, Math.floor(cpus().length / 2));
11673
+ var inExtractionSlot = createConcurrencyLimit(EXTRACTION_SLOTS);
11475
11674
  function splitFrameRanges(ranges, segmentFrames) {
11476
11675
  const segments = [];
11477
11676
  for (const range of ranges) {
@@ -11495,30 +11694,33 @@ async function runSegmentedExtraction(job) {
11495
11694
  while (next < segmentCount && !signal.aborted) {
11496
11695
  const index = next++;
11497
11696
  const { firstFrame, frames } = segments[index];
11498
- const result = await runFfmpeg(
11499
- [
11500
- ...decodeArgs,
11501
- "-noaccurate_seek",
11502
- "-ss",
11503
- String(startTime + firstFrame / fps),
11504
- "-i",
11505
- videoPath,
11506
- // Read one frame past the segment and keep exactly its own frames.
11507
- "-t",
11508
- String((frames + 1) / fps),
11509
- "-frames:v",
11510
- String(frames),
11511
- "-start_number",
11512
- String(firstFrame + 1),
11513
- ...filterAndEncodeArgs
11514
- ],
11515
- { ...job.runOptions, signal }
11697
+ const result = await inExtractionSlot(
11698
+ () => runFfmpeg(
11699
+ [
11700
+ ...decodeArgs,
11701
+ "-noaccurate_seek",
11702
+ "-ss",
11703
+ String(startTime + firstFrame / fps),
11704
+ "-i",
11705
+ videoPath,
11706
+ // Read one frame past the segment and keep exactly its own frames.
11707
+ "-t",
11708
+ String((frames + 1) / fps),
11709
+ "-frames:v",
11710
+ String(frames),
11711
+ "-start_number",
11712
+ String(firstFrame + 1),
11713
+ ...filterAndEncodeArgs
11714
+ ],
11715
+ { ...job.runOptions, signal }
11716
+ ),
11717
+ signal
11516
11718
  );
11517
11719
  results[index] = result;
11518
11720
  if (!result.success) failed.abort();
11519
11721
  }
11520
11722
  };
11521
- const workers = Math.min(segmentCount, Math.max(1, Math.floor(cpus().length / 2)));
11723
+ const workers = Math.min(segmentCount, EXTRACTION_SLOTS);
11522
11724
  await Promise.all(Array.from({ length: workers }, worker));
11523
11725
  const ran = results.filter(Boolean);
11524
11726
  const failure = ran.find((r) => !r.success && r.terminationReason !== "abort") ?? ran.find((r) => !r.success);
@@ -11623,20 +11825,23 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
11623
11825
  if (resampleVfrToCfr) args.push("-fps_mode", "cfr", "-r", ffmpegFps);
11624
11826
  let processResult;
11625
11827
  if (resampleVfrToCfr && vfFilters.length > 0 && !isHdr && NUT_RAW_PIXEL_FORMATS.has(metadata.pixelFormat ?? "")) {
11626
- processResult = await runFfmpegPipeline(
11627
- [...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"],
11628
- [
11629
- "-f",
11630
- "nut",
11631
- "-i",
11632
- "pipe:0",
11633
- "-vf",
11634
- [...restoreSourceColourFilter(metadata), ...vfFilters].join(","),
11635
- "-fps_mode",
11636
- "passthrough",
11637
- ...encodeArgs
11638
- ],
11639
- runOptions
11828
+ processResult = await inExtractionSlot(
11829
+ () => runFfmpegPipeline(
11830
+ [...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"],
11831
+ [
11832
+ "-f",
11833
+ "nut",
11834
+ "-i",
11835
+ "pipe:0",
11836
+ "-vf",
11837
+ [...restoreSourceColourFilter(metadata), ...vfFilters].join(","),
11838
+ "-fps_mode",
11839
+ "passthrough",
11840
+ ...encodeArgs
11841
+ ],
11842
+ runOptions
11843
+ ),
11844
+ signal
11640
11845
  );
11641
11846
  } else {
11642
11847
  const filterArgs = vfFilters.length > 0 ? ["-vf", vfFilters.join(",")] : [];
@@ -11653,7 +11858,10 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
11653
11858
  segmentFrames
11654
11859
  ),
11655
11860
  runOptions
11656
- }) : await runFfmpeg([...args, ...filterArgs, ...encodeArgs], runOptions);
11861
+ }) : await inExtractionSlot(
11862
+ () => runFfmpeg([...args, ...filterArgs, ...encodeArgs], runOptions),
11863
+ signal
11864
+ );
11657
11865
  }
11658
11866
  if (processResult.failureReason === "external_interruption") {
11659
11867
  throw new VideoSourceExtractionError(
@@ -15590,7 +15798,7 @@ function getSystemResources() {
15590
15798
  // ../engine/src/services/fileServer.ts
15591
15799
  import { Hono } from "hono";
15592
15800
  import { serve } from "@hono/node-server";
15593
- import { readFileSync as readFileSync7, openSync as openSync4, fstatSync, closeSync as closeSync4, statSync as statSync6, constants } from "fs";
15801
+ import { readFileSync as readFileSync7, openSync as openSync4, fstatSync, closeSync as closeSync4, statSync as statSync7, constants } from "fs";
15594
15802
  import { join as join16, extname as extname3 } from "path";
15595
15803
  var MIME_TYPES = {
15596
15804
  ".html": "text/html; charset=utf-8",
@@ -15627,7 +15835,7 @@ function readRegularFile(filePath) {
15627
15835
  } catch (error) {
15628
15836
  if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
15629
15837
  return null;
15630
- if (!statSync6(filePath, { throwIfNoEntry: false })?.isFile()) return null;
15838
+ if (!statSync7(filePath, { throwIfNoEntry: false })?.isFile()) return null;
15631
15839
  throw error;
15632
15840
  }
15633
15841
  try {
@@ -16065,6 +16273,7 @@ export {
16065
16273
  releaseBrowser,
16066
16274
  drainBrowserPool,
16067
16275
  closeBrowserPool,
16276
+ usesSoftwareWebGpu,
16068
16277
  buildChromeArgs,
16069
16278
  compositionRequiresWebGpu,
16070
16279
  WebGpuUnavailableError,
@@ -16144,6 +16353,7 @@ export {
16144
16353
  analyzeCompositionHdr,
16145
16354
  isExternalFfmpegInterruption,
16146
16355
  formatFfmpegError,
16356
+ describeFfmpegFailure,
16147
16357
  runFfmpeg,
16148
16358
  probeMediaProfile,
16149
16359
  extractMediaMetadata,