hyperframes 0.8.180 → 0.8.183

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 (215) hide show
  1. package/bin/hyperframes.mjs +3 -1
  2. package/dist/{add-HUVEWSVU.js → add-HW3ZGR36.js} +22 -22
  3. package/dist/{animatedGifPrep-V6GJQOEB.js → animatedGifPrep-EPLN37ZN.js} +13 -13
  4. package/dist/api.js +28 -28
  5. package/dist/{asset-ZGQHDMIO.js → asset-TOASKL3Z.js} +3 -3
  6. package/dist/{assetCataloger-MZWCOV6C.js → assetCataloger-PZN5HYG2.js} +15 -13
  7. package/dist/{audio-XDWHLNIW.js → audio-F5OIQVJO.js} +29 -29
  8. package/dist/{auth-VIV3M7S6.js → auth-YVQLRS3T.js} +4 -4
  9. package/dist/{autoUpdate-R32XDL42.js → autoUpdate-EG4ZFW4D.js} +2 -2
  10. package/dist/{backgroundChecks-A2N6EVCP.js → backgroundChecks-WINNQVR2.js} +3 -3
  11. package/dist/backgroundChecksWorker.js +3 -3
  12. package/dist/{batchRender-3AYDQRBI.js → batchRender-PASZTMEE.js} +7 -7
  13. package/dist/{beats-2O423Z6Q.js → beats-KGIOEBLW.js} +12 -12
  14. package/dist/{benchmark-YXP3PTZP.js → benchmark-ZNY2L7HN.js} +24 -24
  15. package/dist/{browser-M47BNOTJ.js → browser-REAO7RU4.js} +20 -20
  16. package/dist/{bundleWithLocalizedFonts-GCIHOMIU.js → bundleWithLocalizedFonts-OAYLYC4W.js} +2 -2
  17. package/dist/{capture-IV3Y2MT4.js → capture-IK6GWQDD.js} +8 -5
  18. package/dist/{capture-BT2ZXN5P.js → capture-WOSF276U.js} +49 -41
  19. package/dist/{captureCompositionFrame-BMZEKPOU.js → captureCompositionFrame-DDZNKYRE.js} +21 -20
  20. package/dist/{captureTimeout-K2JNVVCI.js → captureTimeout-RIMG6IYH.js} +1 -1
  21. package/dist/{catalog-GI6P22KI.js → catalog-AKTXS7OJ.js} +42 -42
  22. package/dist/{catch-up-QS6HUHAO.js → catch-up-O3VZMPVE.js} +15 -15
  23. package/dist/check-6E46NIWI.js +116 -0
  24. package/dist/checkBrowser-UNZHQFNH.js +117 -0
  25. package/dist/{chunk-ST2ARHQ5.js → chunk-3BA2GP4H.js} +5 -5
  26. package/dist/{chunk-7Z6AARJR.js → chunk-3OLIGYWN.js} +3 -3
  27. package/dist/{chunk-OR5WCFWZ.js → chunk-3Q47QC46.js} +2 -2
  28. package/dist/{chunk-Q2ASTBSG.js → chunk-4FQKS7BJ.js} +16 -14
  29. package/dist/{chunk-G2ZMCNRN.js → chunk-4UFJWRHG.js} +527 -283
  30. package/dist/{chunk-537IYAEE.js → chunk-4UMPIPCQ.js} +9 -1
  31. package/dist/{chunk-65CLIHK2.js → chunk-5MBLWFLM.js} +1 -1
  32. package/dist/{chunk-UVEQLDKB.js → chunk-5OGDBU4G.js} +3 -2
  33. package/dist/{chunk-L3YOGIPG.js → chunk-66LREXGS.js} +1 -1
  34. package/dist/{chunk-W4SLIGGM.js → chunk-73LTCCZI.js} +249 -161
  35. package/dist/{chunk-GZVP4TWN.js → chunk-7JAD4MEW.js} +1 -1
  36. package/dist/{chunk-NAMGMP3R.js → chunk-7KVVNF7B.js} +1 -1
  37. package/dist/{chunk-GK2FIKIQ.js → chunk-ADDHW2FS.js} +214 -37
  38. package/dist/{chunk-WUQUR33W.js → chunk-AKFADCMS.js} +1 -1
  39. package/dist/{chunk-2VJNKE7C.js → chunk-ANR6WOC3.js} +1 -1
  40. package/dist/{chunk-L527FLMV.js → chunk-ATMDOUNN.js} +2 -2
  41. package/dist/{chunk-ZJPCQBU7.js → chunk-B2SZZFWW.js} +4 -4
  42. package/dist/{chunk-TO6JNTML.js → chunk-BP4VNTZL.js} +13 -8
  43. package/dist/{chunk-3YXTFXT7.js → chunk-CAHTO62O.js} +8 -8
  44. package/dist/{chunk-MPWLGNM2.js → chunk-CGK4XZ2J.js} +1 -1
  45. package/dist/{chunk-NOXGG4C7.js → chunk-CJYAJ3QM.js} +37 -33
  46. package/dist/{chunk-F5VMPELS.js → chunk-COTLNID2.js} +16 -16
  47. package/dist/{chunk-E24HSMZV.js → chunk-CYBXXSC4.js} +65 -14
  48. package/dist/{chunk-NZZYJOMJ.js → chunk-CZUAX3BV.js} +6 -5
  49. package/dist/{chunk-K7RGIADJ.js → chunk-DEORFAWP.js} +12 -12
  50. package/dist/{chunk-GV5R5B7Q.js → chunk-DGECWPTL.js} +1 -1
  51. package/dist/{chunk-BRHI7EIA.js → chunk-DKPNNYBZ.js} +4 -0
  52. package/dist/{chunk-R46P7TGY.js → chunk-DLC2FICN.js} +7 -7
  53. package/dist/{chunk-D3ZWBHZP.js → chunk-DYAPHNV6.js} +3 -2
  54. package/dist/{chunk-RGHQID2E.js → chunk-EHSBBBR2.js} +5 -3
  55. package/dist/{chunk-IWTRHPTP.js → chunk-EVAQPW6O.js} +8 -8
  56. package/dist/{chunk-S3RYRF3W.js → chunk-F6WHPHLF.js} +1 -1
  57. package/dist/chunk-FG4BBFZI.js +122 -0
  58. package/dist/{chunk-V6MQP32U.js → chunk-GAGMLVCV.js} +2 -2
  59. package/dist/chunk-GMNHAOMJ.js +3805 -0
  60. package/dist/{chunk-KIO3R4QN.js → chunk-GPE3WWTO.js} +16 -6
  61. package/dist/{chunk-ULWZYLCV.js → chunk-GTE5WZAM.js} +72 -140
  62. package/dist/{chunk-2JDLESMS.js → chunk-HLBODNGW.js} +1 -1
  63. package/dist/{chunk-KMAAOHLO.js → chunk-HRPWRZGF.js} +1 -1
  64. package/dist/{chunk-UPFUC4DB.js → chunk-HTIUNMTP.js} +1 -1
  65. package/dist/{chunk-FCUTQM2P.js → chunk-IC2WYONA.js} +5 -5
  66. package/dist/{chunk-VE3OKVSD.js → chunk-ITSLKUQM.js} +2 -2
  67. package/dist/{chunk-YJDB4FLT.js → chunk-JQYJXUJW.js} +1 -1
  68. package/dist/{chunk-PBN6GBJE.js → chunk-JVBRJMOP.js} +14 -1
  69. package/dist/{chunk-XH262OWN.js → chunk-K2Z5DZBS.js} +1 -1
  70. package/dist/{chunk-RHWJKSGH.js → chunk-KANNRHY7.js} +1 -1
  71. package/dist/{chunk-Y5APFFMD.js → chunk-KRFAX5JK.js} +1 -1
  72. package/dist/{chunk-4J7DHVN2.js → chunk-LBWRQ4XA.js} +1 -1
  73. package/dist/{chunk-MQOE5RE4.js → chunk-MIZDJVPZ.js} +9 -9
  74. package/dist/{chunk-32GKETR7.js → chunk-MMQ52NIZ.js} +74 -0
  75. package/dist/chunk-MOEGCMKI.js +180 -0
  76. package/dist/{chunk-E6RPWJVC.js → chunk-NAUKCKCJ.js} +5 -1
  77. package/dist/{chunk-JBCU6NR5.js → chunk-NZ3BE2QE.js} +56 -55
  78. package/dist/{chunk-VKMP3IVR.js → chunk-O7N3A7PE.js} +2 -2
  79. package/dist/{chunk-3QENZQSL.js → chunk-OH56PXFH.js} +5 -5
  80. package/dist/{chunk-H4YZZ2VQ.js → chunk-PFD25NAC.js} +1 -1
  81. package/dist/{check-HE5E4Q7Q.js → chunk-Q6UYKWBG.js} +198 -142
  82. package/dist/{chunk-3DKN5WKL.js → chunk-QH3T4WI2.js} +77 -6
  83. package/dist/{chunk-RGXAN3YU.js → chunk-QX4GGPMY.js} +2 -2
  84. package/dist/{chunk-A3NFBMYX.js → chunk-R6OV4J4K.js} +6 -6
  85. package/dist/{chunk-LL5VQ4WS.js → chunk-RZXUQR42.js} +2 -2
  86. package/dist/{chunk-SEMOZNVE.js → chunk-SAQYJLI6.js} +1 -1
  87. package/dist/{chunk-55P7GDT6.js → chunk-TJB4GAV6.js} +2 -2
  88. package/dist/{checkBrowser-BHLYP3YW.js → chunk-TOHA5NAU.js} +187 -114
  89. package/dist/{chunk-RXQHSGAQ.js → chunk-TXGZZKDF.js} +2 -2
  90. package/dist/{chunk-KZ3VI5K6.js → chunk-U5YVWG5W.js} +3 -3
  91. package/dist/{chunk-ICMXC3MI.js → chunk-U6RXZNZE.js} +31 -18
  92. package/dist/{chunk-XO6CWU7D.js → chunk-UNOSCGBG.js} +2 -2
  93. package/dist/{chunk-NXYBR4QX.js → chunk-VAK7A2N6.js} +16 -16
  94. package/dist/{chunk-OFY6JTZC.js → chunk-VDJO4DJ7.js} +2 -2
  95. package/dist/{chunk-LBQFLUIY.js → chunk-VFBAKPRC.js} +9 -49
  96. package/dist/{chunk-K3KANDZQ.js → chunk-VH5KESLX.js} +1 -1
  97. package/dist/{chunk-22QHO26F.js → chunk-W64L7IFS.js} +1 -1
  98. package/dist/{chunk-4IEH6EAC.js → chunk-WT4PYV2J.js} +4 -6
  99. package/dist/{chunk-XFCLWWL3.js → chunk-X3IA5RYH.js} +2 -2
  100. package/dist/{chunk-UZOZ2W2E.js → chunk-XAQDR5Y5.js} +2 -2
  101. package/dist/{chunk-BI52RNRW.js → chunk-XJHKJXT4.js} +103 -20
  102. package/dist/{chunk-HFZ4UILX.js → chunk-XOVHO3TT.js} +25 -14
  103. package/dist/{chunk-VARUKXOZ.js → chunk-XR5BSEUF.js} +7 -15
  104. package/dist/{chunk-IZM562YJ.js → chunk-Z4R35NJS.js} +4 -4
  105. package/dist/{chunk-AMX53HVX.js → chunk-ZFO3A3QL.js} +2 -2
  106. package/dist/{clean-TQOW24OF.js → clean-WNEIVBF3.js} +40 -40
  107. package/dist/cli.js +68 -64
  108. package/dist/{cloud-7TOVPUMY.js → cloud-6GSSDCJL.js} +4 -4
  109. package/dist/{cloudrun-2BKGY53X.js → cloudrun-4NEJRE4T.js} +8 -8
  110. package/dist/commands/layout-audit.browser.js +15 -4
  111. package/dist/commands/motion-signature.browser.js +52 -0
  112. package/dist/{compare-WDV3Q6WC.js → compare-2JWQ5ABA.js} +50 -47
  113. package/dist/{compiler-U3W6CJW7.js → compiler-27KQD7KW.js} +6 -6
  114. package/dist/{component-U66GBIUO.js → component-HCRET3MH.js} +4 -4
  115. package/dist/{compositions-Y63YGLCE.js → compositions-HTSQ6SUJ.js} +28 -29
  116. package/dist/{contactSheet-27SRU7A4.js → contactSheet-CAGMKI3E.js} +3 -1
  117. package/dist/{delete-TBAIAD7M.js → delete-GHQ5UYM7.js} +9 -9
  118. package/dist/{deploy-BIWOSWS3.js → deploy-I3Z3NAVA.js} +11 -11
  119. package/dist/{destroy-QBLMYDBF.js → destroy-4B5HW5FH.js} +5 -5
  120. package/dist/{deterministicFonts-L7YEEFGI.js → deterministicFonts-JFLSSKRB.js} +2 -2
  121. package/dist/{dist-2FMTAVGL.js → dist-SENYPQGW.js} +4 -4
  122. package/dist/{dist-VO7OXOFK.js → dist-WRDLWRMS.js} +5 -5
  123. package/dist/{docs-MPOM3PHH.js → docs-UEWLIP6D.js} +1 -1
  124. package/dist/{doctor-GC4REGB6.js → doctor-UGFVQZHI.js} +27 -27
  125. package/dist/{events-PUUP24FT.js → events-BLJGWMNP.js} +9 -9
  126. package/dist/{events-4NMQVUZR.js → events-YS4OVX3I.js} +6 -6
  127. package/dist/{feedback-BIDXK7TE.js → feedback-AZ3SJ5HF.js} +35 -35
  128. package/dist/{ffmpeg-2VJDEWQQ.js → ffmpeg-2AECXG7V.js} +15 -15
  129. package/dist/{figma-THMXYLMU.js → figma-DRYIJQHV.js} +3 -3
  130. package/dist/fontLocalizeCli.js +23 -23
  131. package/dist/{get-5T4B7ERH.js → get-DDEAOFPB.js} +9 -9
  132. package/dist/{gpuPolicy-GLUATV5I.js → gpuPolicy-UDJT2MQK.js} +13 -13
  133. package/dist/{grade-compare-2A65YEHZ.js → grade-compare-LJKK5NSY.js} +55 -50
  134. package/dist/{help-JFBDXFRC.js → help-TQBGYOID.js} +1 -1
  135. package/dist/{history-OJEWFWMS.js → history-5TOOCF2Y.js} +15 -15
  136. package/dist/hyperframe.manifest.json +1 -1
  137. package/dist/hyperframe.runtime.iife.js +17 -17
  138. package/dist/hyperframes-player.global.js +8 -3
  139. package/dist/{info-KJZBU3SZ.js → info-ENI5MP4W.js} +20 -20
  140. package/dist/{init-6V4ELU4Z.js → init-JXPXWV3G.js} +68 -68
  141. package/dist/{inspect-WGBZZUZU.js → inspect-WZNYQ5AB.js} +45 -44
  142. package/dist/{installer-EPRSH6QI.js → installer-3SGRBLJW.js} +7 -7
  143. package/dist/{keyframes-57LYKC2M.js → keyframes-HEOC4CT4.js} +16 -16
  144. package/dist/{lambda-5P5X2X5E.js → lambda-2VHQ4AL2.js} +15 -15
  145. package/dist/{layout-PM2VAUT2.js → layout-7WEUNO6T.js} +45 -44
  146. package/dist/{lint-DXS6QRYZ.js → lint-5YMAF3L7.js} +23 -23
  147. package/dist/{lintProject-ARKJXDQF.js → lintProject-C2ONSH2U.js} +4 -4
  148. package/dist/{list-6IBSX7FU.js → list-BIE3X7CO.js} +10 -10
  149. package/dist/{login-RY3PWK44.js → login-YDIGJWCG.js} +7 -7
  150. package/dist/{logout-DKJANASY.js → logout-DDC4QLVK.js} +3 -3
  151. package/dist/{manager-N3COY6BJ.js → manager-D2UV6RDQ.js} +16 -16
  152. package/dist/{manager-V2QCCPOJ.js → manager-UHWEKPXC.js} +8 -3
  153. package/dist/{media-treatment-3HEZR7ZS.js → media-treatment-FSKHPMWA.js} +33 -33
  154. package/dist/{media-use-4XXGABIL.js → media-use-JBN57JE3.js} +1 -1
  155. package/dist/{models-FEULJWSX.js → models-COEK6LXG.js} +24 -24
  156. package/dist/{motionShot-BZTEIZEO.js → motionShot-HYZ2ZGJQ.js} +28 -26
  157. package/dist/{normalize-audio-O4X4LPLK.js → normalize-audio-UXR6WQ7Q.js} +12 -12
  158. package/dist/{open-H3MX4TI2.js → open-OFT2GIDG.js} +24 -24
  159. package/dist/{parakeetRunner-5ABMPCI6.js → parakeetRunner-4NZ3QXS3.js} +25 -25
  160. package/dist/{pipeline-RJ4DOVWX.js → pipeline-ZKE54JMG.js} +18 -18
  161. package/dist/{play-3PBKEZIX.js → play-2VG77FT6.js} +39 -38
  162. package/dist/{policies-5NTZOHJA.js → policies-XXNGXWHV.js} +1 -1
  163. package/dist/{preflight-3RABQJT3.js → preflight-QTWDIM5C.js} +18 -18
  164. package/dist/{present-6SNRG3GI.js → present-OONXQLJG.js} +33 -33
  165. package/dist/{preview-LSDFCJ3H.js → preview-6RZJFU76.js} +49 -49
  166. package/dist/{producer-SUZDRXHJ.js → producer-6YM3UH3B.js} +1 -1
  167. package/dist/{progress-I37JB7R4.js → progress-NHJIHAAG.js} +2 -2
  168. package/dist/{projectConfig-5QCNTGQT.js → projectConfig-YMW4P2X6.js} +9 -9
  169. package/dist/{publication-2QT7VVE2.js → publication-SEINUAPZ.js} +5 -5
  170. package/dist/{publish-53EBL7OO.js → publish-KZBTBWFO.js} +27 -27
  171. package/dist/{refresh-MGCVWX56.js → refresh-RXTOPRBB.js} +3 -3
  172. package/dist/{remove-background-4G4MX5WK.js → remove-background-BIQNMIR7.js} +31 -31
  173. package/dist/{render-RXKUYTYI.js → render-65K3K6SD.js} +31 -31
  174. package/dist/{render-SKY6FKYB.js → render-FIH37HJA.js} +101 -96
  175. package/dist/{render-75CRB3BW.js → render-LW76HOMJ.js} +8 -8
  176. package/dist/{render-batch-CEVWUYBP.js → render-batch-POLEEYD4.js} +8 -8
  177. package/dist/renderSetupWorker.js +12 -9
  178. package/dist/{resolver-4ICE2AT6.js → resolver-O2SED3XB.js} +7 -7
  179. package/dist/{screenshotCapture-HXEZ3TNJ.js → screenshotCapture-UBY6TT75.js} +33 -6
  180. package/dist/{sherpa-TMX3WBTM.js → sherpa-HTFFFTVQ.js} +24 -24
  181. package/dist/{sites-3ZBYB5X4.js → sites-MAVH57MO.js} +2 -2
  182. package/dist/skills/hyperframes/references/review-loop.md +1 -1
  183. package/dist/{skills-MCWFAGIP.js → skills-J6MBGS5O.js} +17 -17
  184. package/dist/{skillsUpdateCheck-NXOI35BW.js → skillsUpdateCheck-ZAXNLBF3.js} +3 -3
  185. package/dist/{snapshot-ESZMKQ6J.js → snapshot-TL7TTFS6.js} +72 -71
  186. package/dist/{src-XRTZOBO5.js → src-EL6HGIS4.js} +35 -26
  187. package/dist/{src-GYYWBQXJ.js → src-Z5H5N5T4.js} +19 -19
  188. package/dist/{state-KAXGHF77.js → state-ORRPOLMQ.js} +2 -2
  189. package/dist/{staticProjectServer-GYPUQGI7.js → staticProjectServer-EO7X7RAY.js} +20 -20
  190. package/dist/{status-BV7QAUQD.js → status-CM7IQTU2.js} +6 -6
  191. package/dist/studio/assets/{hyperframes-player-1wdLVP-x.js → hyperframes-player-hz_VqxBZ.js} +7 -2
  192. package/dist/studio/assets/{index-CaWPPTQ1.js → index-CV_ojgp5.js} +1 -1
  193. package/dist/studio/assets/{index-mdtW0Ug-.js → index-S1ceEagc.js} +71 -71
  194. package/dist/studio/index.html +1 -1
  195. package/dist/{studio-api-IXGX5ZFI.js → studio-api-CUZDC5B3.js} +17 -17
  196. package/dist/{studioServer-XVGYU6O3.js → studioServer-EZPRV43Y.js} +90 -84
  197. package/dist/{telemetry-E355EUDZ.js → telemetry-LFUVHARP.js} +10 -10
  198. package/dist/{telemetry-CQYHCCPL.js → telemetry-QG5AANZ4.js} +1 -1
  199. package/dist/{timeline-VWH6WSD7.js → timeline-GSH743JE.js} +41 -41
  200. package/dist/{tokens-IOAVF4UI.js → tokens-3ZMPSH7B.js} +3 -3
  201. package/dist/{transcribe-EGUXJGD4.js → transcribe-4SHNDPWW.js} +47 -47
  202. package/dist/{transcribe-EJA7OOHH.js → transcribe-TV7JBZQS.js} +19 -19
  203. package/dist/{tts-EDK3QS7M.js → tts-A2WILYGN.js} +6 -6
  204. package/dist/{updateCheck-U2CVTKPF.js → updateCheck-W7FTFOQF.js} +2 -2
  205. package/dist/{upgrade-CWEKQ4JO.js → upgrade-7YYYNAPF.js} +3 -3
  206. package/dist/{urlDownloader-DY5BLAFA.js → urlDownloader-PQSUS2CP.js} +12 -12
  207. package/dist/{validate-XOMNMKD6.js → validate-GCHAEVXM.js} +39 -38
  208. package/dist/{video-22NN6OEH.js → video-KGN7NJS5.js} +16 -14
  209. package/dist/warmCheck.js +227 -0
  210. package/dist/warmCheckClient.js +139 -0
  211. package/package.json +4 -2
  212. package/dist/chunk-BAZ5LCR3.js +0 -3805
  213. package/dist/hyperframe-runtime.js +0 -1125
  214. package/dist/{chunk-B4VT6YU2.js → chunk-QGOYNPQT.js} +3 -3
  215. package/dist/{synthesize-I2C24XJ5.js → synthesize-PKTB5ATG.js} +1 -1
@@ -5,12 +5,10 @@ var require = __hf_createRequire(import.meta.url);
5
5
  var __filename = __hf_fileURLToPath(import.meta.url);
6
6
  var __dirname = __hf_dirname(__filename);
7
7
  import {
8
- parseEncoderOptions
9
- } from "./chunk-KLIKCX6Q.js";
10
- import {
11
- RENDER_TEMP_DIR,
12
- createStudioPositionSeekReapplyScript
13
- } from "./chunk-EVMY7IPL.js";
8
+ EncoderInterruptedError,
9
+ encoderFailureError,
10
+ prepareAnimatedGifInputs
11
+ } from "./chunk-ATMDOUNN.js";
14
12
  import {
15
13
  GENERIC_FAMILIES,
16
14
  collectFontFamilyCustomProperties,
@@ -18,12 +16,14 @@ import {
18
16
  iterateFontFamilyDeclarations,
19
17
  normalizeSystemFontPrimaryFamilies,
20
18
  resolveFontFamilyDeclarationFamilies
21
- } from "./chunk-B4VT6YU2.js";
19
+ } from "./chunk-QGOYNPQT.js";
22
20
  import {
23
- EncoderInterruptedError,
24
- encoderFailureError,
25
- prepareAnimatedGifInputs
26
- } from "./chunk-L527FLMV.js";
21
+ parseEncoderOptions
22
+ } from "./chunk-KLIKCX6Q.js";
23
+ import {
24
+ RENDER_TEMP_DIR,
25
+ createStudioPositionSeekReapplyScript
26
+ } from "./chunk-EVMY7IPL.js";
27
27
  import {
28
28
  BROWSER_GPU_NOT_SOFTWARE,
29
29
  CaptureFailure,
@@ -37,6 +37,7 @@ import {
37
37
  ManagedChildProcess,
38
38
  NotMediaPayloadError,
39
39
  VIDEO_FRAME_FORMATS,
40
+ aacSegmentCount,
40
41
  analyzeCompositionHdr,
41
42
  analyzeKeyframeIntervals,
42
43
  appendRenderProvenanceArgs,
@@ -79,6 +80,7 @@ import {
79
80
  distributeFrames,
80
81
  distributeFramesInterleaved,
81
82
  downloadToTemp,
83
+ encodeAac,
82
84
  encodeFramesChunkedConcat,
83
85
  encodeFramesFromDir,
84
86
  executeParallelCapture,
@@ -112,6 +114,7 @@ import {
112
114
  isVideoFrameFormat,
113
115
  isVideoHiddenBeforeStreamStart,
114
116
  isVideoPastTimelineEnd,
117
+ measureVideoShownBoxes,
115
118
  mergeWorkerFrames,
116
119
  muxVideoWithAudio,
117
120
  normalizeObjectFit,
@@ -138,7 +141,6 @@ import {
138
141
  resolveFinalFrameExtractionWindow,
139
142
  resolveHeadlessShellPath,
140
143
  resolvePlayableVideoDuration,
141
- resolveReferencedStart,
142
144
  resolveVideoExtractionWindow,
143
145
  runFfmpeg,
144
146
  runVideoExtractionWithRetry,
@@ -152,46 +154,35 @@ import {
152
154
  validateEngineConfigSnapshot,
153
155
  verifyDiskDrawElementSamples,
154
156
  writeCapturedFrame
155
- } from "./chunk-W4SLIGGM.js";
156
- import {
157
- assignBundledRuntimeCompositionIds,
158
- buildVariablesByCompScript,
159
- emitMountedModuleScripts,
160
- emitRootCompositionVariableStyles,
161
- ensureExternalLinkTag,
162
- ensureExternalScriptTag,
163
- extractStandaloneEntryFromIndex,
164
- inlineSubCompositions,
165
- isTemplateEntry,
166
- parsesAsScript,
167
- prepareFlattenedInnerRoot
168
- } from "./chunk-4IEH6EAC.js";
169
- import {
170
- LOW_MEMORY_TOTAL_MB_THRESHOLD,
171
- getCgroupMemoryLimitMb,
172
- getSystemTotalMb
173
- } from "./chunk-W2LGJSEN.js";
157
+ } from "./chunk-73LTCCZI.js";
174
158
  import {
175
159
  DISTRIBUTED_FPS,
176
160
  HF_VFX_ATTR,
177
- MEDIA_START_BASIS_ATTR,
178
161
  chainCapture,
179
162
  clearGifFramesBeforeNext,
180
- compositionOriginSeconds,
181
- cutToHostSlots,
182
163
  fpsToFfmpegArg,
183
164
  fpsToNumber,
184
165
  gifClearsAfterLeavingFrameInPlace,
185
166
  gsapCdnDist,
186
- hostInPointSeconds,
187
167
  isDistributedFps,
188
168
  parseFps,
189
169
  parseVfxChain,
190
- readMediaStartBasis,
191
- resolveAbsoluteMediaStartSeconds,
192
170
  resolveAuthoredTimingWindow,
193
171
  toFps
194
- } from "./chunk-LBQFLUIY.js";
172
+ } from "./chunk-VFBAKPRC.js";
173
+ import {
174
+ assignBundledRuntimeCompositionIds,
175
+ buildVariablesByCompScript,
176
+ emitMountedModuleScripts,
177
+ emitRootCompositionVariableStyles,
178
+ ensureExternalLinkTag,
179
+ ensureExternalScriptTag,
180
+ extractStandaloneEntryFromIndex,
181
+ inlineSubCompositions,
182
+ isTemplateEntry,
183
+ parsesAsScript,
184
+ prepareFlattenedInnerRoot
185
+ } from "./chunk-WT4PYV2J.js";
195
186
  import {
196
187
  COMPOSITION_SOURCE_URL,
197
188
  HF_COLOR_GRADING_ATTR,
@@ -219,7 +210,7 @@ import {
219
210
  shouldClampResolvedMediaDuration,
220
211
  sourceTimeAt,
221
212
  styleElementsFor
222
- } from "./chunk-BAZ5LCR3.js";
213
+ } from "./chunk-GMNHAOMJ.js";
223
214
  import {
224
215
  MAX_AUDIO_GAIN
225
216
  } from "./chunk-UJKQWGFF.js";
@@ -234,24 +225,36 @@ import {
234
225
  normalizeResolutionFlag,
235
226
  suggestMatchingPreset
236
227
  } from "./chunk-YMS5FOMC.js";
228
+ import {
229
+ TRANSITIONS,
230
+ convertTransfer,
231
+ crossfade
232
+ } from "./chunk-3GXQPCJC.js";
237
233
  import {
238
234
  redactKnownPaths,
239
235
  redactTelemetryString
240
236
  } from "./chunk-634HSJ3J.js";
241
237
  import {
242
238
  chromeMajorCeiling
243
- } from "./chunk-537IYAEE.js";
239
+ } from "./chunk-4UMPIPCQ.js";
244
240
  import {
245
- lintHyperframeHtml
246
- } from "./chunk-GK2FIKIQ.js";
241
+ lintHyperframeHtml,
242
+ resolveReferencedStart
243
+ } from "./chunk-ADDHW2FS.js";
247
244
  import {
248
245
  isUnresolvedAssetPlaceholder,
249
246
  readProjectFile,
250
247
  resolveProjectRelativeSrc
251
248
  } from "./chunk-6BNTGNGD.js";
252
249
  import {
253
- checkSubCompositionUsability
254
- } from "./chunk-32GKETR7.js";
250
+ MEDIA_START_BASIS_ATTR,
251
+ checkSubCompositionUsability,
252
+ compositionOriginSeconds,
253
+ cutToHostSlots,
254
+ hostInPointSeconds,
255
+ readMediaStartBasis,
256
+ resolveAbsoluteMediaStartSeconds
257
+ } from "./chunk-MMQ52NIZ.js";
255
258
  import {
256
259
  CSS_URL_RE,
257
260
  decodedUrlPath,
@@ -264,10 +267,10 @@ import {
264
267
  parseHTML
265
268
  } from "./chunk-SDYDNHEE.js";
266
269
  import {
267
- TRANSITIONS,
268
- convertTransfer,
269
- crossfade
270
- } from "./chunk-3GXQPCJC.js";
270
+ LOW_MEMORY_TOTAL_MB_THRESHOLD,
271
+ getCgroupMemoryLimitMb,
272
+ getSystemTotalMb
273
+ } from "./chunk-W2LGJSEN.js";
271
274
 
272
275
  // ../producer/src/services/renderOrchestrator.ts
273
276
  import {
@@ -1595,7 +1598,7 @@ function pidScope() {
1595
1598
  return {
1596
1599
  host: hostname(),
1597
1600
  // Without /proc (macOS, Windows) the boot time, to the minute, stands in for the boot id.
1598
- boot: attempt(() => readFileSync2("/proc/sys/kernel/random/boot_id", "utf-8").trim()) ?? String(Math.round(Date.now() / 6e4 - uptime() / 60)),
1601
+ boot: attempt(() => readFileSync2("/proc/sys/kernel/random/boot_id", "utf-8").trim()) ?? attempt(() => String(Math.round(Date.now() / 6e4 - uptime() / 60))),
1599
1602
  pidns: attempt(() => readlinkSync("/proc/self/ns/pid"))
1600
1603
  };
1601
1604
  }
@@ -1613,9 +1616,14 @@ function alive(pid) {
1613
1616
  return error.code === "EPERM";
1614
1617
  }
1615
1618
  }
1616
- function inThisPidScope(owner) {
1619
+ function knownPidScope() {
1617
1620
  const scope = pidScope();
1618
- return owner.host === scope.host && owner.boot === scope.boot && owner.pidns === scope.pidns;
1621
+ const unknown = scope.boot === void 0 || process.platform === "linux" && scope.pidns === void 0;
1622
+ return unknown ? void 0 : scope;
1623
+ }
1624
+ function inThisPidScope(owner) {
1625
+ const scope = knownPidScope();
1626
+ return scope !== void 0 && owner.host === scope.host && owner.boot === scope.boot && owner.pidns === scope.pidns;
1619
1627
  }
1620
1628
  function ownerState(dir) {
1621
1629
  let owner;
@@ -1680,7 +1688,9 @@ async function defaultArtifactDurationProbe(path) {
1680
1688
  const meta = await extractMediaMetadata(path);
1681
1689
  return {
1682
1690
  durationSeconds: meta.durationSeconds,
1683
- frames: meta.frames
1691
+ frames: meta.frames,
1692
+ width: meta.width,
1693
+ height: meta.height
1684
1694
  };
1685
1695
  }
1686
1696
  function buildArtifactExpectation(input) {
@@ -1689,7 +1699,9 @@ function buildArtifactExpectation(input) {
1689
1699
  const expectation = {
1690
1700
  expectedDurationSeconds: input.durationSeconds,
1691
1701
  fps: input.fps,
1692
- expectedFrames: input.expectedFrames
1702
+ expectedFrames: input.expectedFrames,
1703
+ expectedWidth: input.width,
1704
+ expectedHeight: input.height
1693
1705
  };
1694
1706
  if (input.outputFormat !== "hls") return expectation;
1695
1707
  return {
@@ -1757,6 +1769,16 @@ function assertFrameCountWithinTolerance(expectedFrames, probedFrames, stagingPa
1757
1769
  `Render artifact is truncated: expected ${expectedFrames} frames, probed ${probedFrames} frames (shortfall ${shortfall} frames). Artifact: ${stagingPath}`
1758
1770
  );
1759
1771
  }
1772
+ function assertSizeMatches(expected, probed, stagingPath) {
1773
+ const { expectedWidth: width, expectedHeight: height } = expected;
1774
+ if (width === void 0 || height === void 0) return;
1775
+ if (probed.width === void 0 || probed.height === void 0) return;
1776
+ const fits = (want, got) => got === want || got === want + want % 2;
1777
+ if (fits(width, probed.width) && fits(height, probed.height)) return;
1778
+ throw new Error(
1779
+ `Render artifact has the wrong size: expected ${width}x${height}, probed ${probed.width}x${probed.height}. Artifact: ${stagingPath}`
1780
+ );
1781
+ }
1760
1782
  var ArtifactTransaction = class {
1761
1783
  constructor(destinationPath, kind, fileSystem = defaultFileSystem, durationProbe = defaultArtifactDurationProbe) {
1762
1784
  this.kind = kind;
@@ -1829,6 +1851,7 @@ var ArtifactTransaction = class {
1829
1851
  if (expected.expectedFrames !== void 0) {
1830
1852
  assertFrameCountWithinTolerance(expected.expectedFrames, probed.frames, probePath);
1831
1853
  }
1854
+ assertSizeMatches(expected, probed, probePath);
1832
1855
  }
1833
1856
  async runDurationProbe(probePath) {
1834
1857
  try {
@@ -3307,7 +3330,9 @@ function aggregateDrawElement(perfs, de) {
3307
3330
  if (perfs.length === 0) return void 0;
3308
3331
  const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
3309
3332
  const gateReasons = [
3310
- ...new Set(perfs.map((p) => p.deGateReason).filter((r) => !!r))
3333
+ ...new Set(
3334
+ [de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
3335
+ )
3311
3336
  ].sort();
3312
3337
  const gpuRenderers = [
3313
3338
  ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
@@ -6579,7 +6604,7 @@ async function readLocalFont(absPath) {
6579
6604
  return { kind: "inline", buffer: Buffer.concat(chunks, totalBytes) };
6580
6605
  }
6581
6606
  async function embedLocalFontFaces(html, projectDir) {
6582
- const { fontToDataUri: toDataUri } = await import("./deterministicFonts-L7YEEFGI.js");
6607
+ const { fontToDataUri: toDataUri } = await import("./deterministicFonts-JFLSSKRB.js");
6583
6608
  const styleBlockRe = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
6584
6609
  const fontFaceRe = /@font-face\s*\{([^}]*)\}/gi;
6585
6610
  let result = html;
@@ -8336,6 +8361,7 @@ async function runAudioStage(input) {
8336
8361
  audios,
8337
8362
  masterFxChain,
8338
8363
  urlByAbsentLocalPath,
8364
+ keepEncoderInputAt,
8339
8365
  abortSignal,
8340
8366
  assertNotAborted,
8341
8367
  log
@@ -8355,7 +8381,13 @@ async function runAudioStage(input) {
8355
8381
  audioOutputPath,
8356
8382
  duration,
8357
8383
  abortSignal,
8358
- { ffmpegProcessTimeout, audioGain, masterFxChain, urlByAbsentLocalPath },
8384
+ {
8385
+ ffmpegProcessTimeout,
8386
+ audioGain,
8387
+ masterFxChain,
8388
+ urlByAbsentLocalPath,
8389
+ keepEncoderInputAt
8390
+ },
8359
8391
  compiledDir
8360
8392
  );
8361
8393
  } catch (err) {
@@ -8455,13 +8487,29 @@ function inspectDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, free
8455
8487
  }
8456
8488
  return { available: false, estimatedBytes, freeBytes };
8457
8489
  }
8458
- function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis) {
8490
+ function diskCaptureRemedy(cfg, outputFormat, durationSeconds, workerCount) {
8491
+ const oneWorker = explainStreamingEncodeGate(cfg, outputFormat, 1, durationSeconds);
8492
+ if (workerCount > 1 && oneWorker.enabled) {
8493
+ return `This render stores frames because it captures with ${workerCount} workers. Free up disk space, or re-run this render with --workers 1 to stream frames into the encoder instead. One worker is slower, so keep the default worker count for renders that fit on disk.`;
8494
+ }
8495
+ switch (oneWorker.reason) {
8496
+ case "format_excluded":
8497
+ return `${outputFormat} output stores every frame at any worker count. Free up disk space or render to mp4.`;
8498
+ case "disabled_by_config":
8499
+ return "PRODUCER_ENABLE_STREAMING_ENCODE=false sends every render to disk. Free up disk space or unset it.";
8500
+ case "duration_cap":
8501
+ return "The streaming duration cap (PRODUCER_STREAMING_ENCODE_DURATION_CAP_ENABLED=true) sends renders longer than PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS to disk at any worker count. Free up disk space, unset the cap, or use --low-memory-mode, which streams past it on one worker.";
8502
+ default:
8503
+ return "Free up disk space.";
8504
+ }
8505
+ }
8506
+ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remedy) {
8459
8507
  const basisNote = basis === "measured" ? ` (measured from the first ${DISK_PROJECTION_SAMPLE_FRAMES} frames)` : "";
8460
8508
  return new Error(
8461
- `Disk capture may need ~${(needBytes / 1e6).toFixed(1)} MB of temporary frame storage${basisNote}, but only ${(freeBytes / 1e6).toFixed(1)} MB is free at ${framesDir}. Disk capture stores every frame as an image file (JPEG, or PNG for alpha). mp4/mov renders stream instead: always at --workers 1, and multi-worker on Linux BeginFrame capture. This render landed on disk because the output is png-sequence or gif, because it is multi-worker screenshot capture (opt in with HF_CAPTURE_PARALLEL_STREAM=true), or because streaming was switched off: HF_CAPTURE_PARALLEL_STREAM=false, PRODUCER_ENABLE_STREAMING_ENCODE=false, or the duration cap (PRODUCER_STREAMING_ENCODE_DURATION_CAP_ENABLED=true) on a render longer than PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS. HDR and shader-transition renders never reach this check; they run their own compositor. Re-run with --workers 1, use --low-memory-mode, or free up disk space.`
8509
+ `Disk capture may need ~${(needBytes / 1e6).toFixed(1)} MB of temporary frame storage${basisNote}, but only ${(freeBytes / 1e6).toFixed(1)} MB is free at ${framesDir}. Disk capture stores every frame as an image file (JPEG, or PNG for alpha). ${remedy}`
8462
8510
  );
8463
8511
  }
8464
- function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeDiskBytes) {
8512
+ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy, freeDiskBytes) {
8465
8513
  const headroom = inspectDiskCaptureHeadroom(
8466
8514
  framesDir,
8467
8515
  totalFrames,
@@ -8473,7 +8521,8 @@ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeD
8473
8521
  headroom.estimatedBytes,
8474
8522
  headroom.freeBytes,
8475
8523
  framesDir,
8476
- "estimate"
8524
+ "estimate",
8525
+ remedy
8477
8526
  );
8478
8527
  }
8479
8528
  function measureCaptureFrameBytes(workDir, framesDir) {
@@ -8493,7 +8542,7 @@ function measureCaptureFrameBytes(workDir, framesDir) {
8493
8542
  return total;
8494
8543
  }
8495
8544
  function createDiskCaptureProjection(input) {
8496
- const { framesDir, totalFrames, measureBytes, freeDiskBytes = freeDiskBytesAt } = input;
8545
+ const { framesDir, totalFrames, measureBytes, remedy, freeDiskBytes = freeDiskBytesAt } = input;
8497
8546
  let checked = false;
8498
8547
  return (capturedFrames) => {
8499
8548
  if (checked || capturedFrames < DISK_PROJECTION_SAMPLE_FRAMES) return;
@@ -8505,7 +8554,7 @@ function createDiskCaptureProjection(input) {
8505
8554
  if (remaining <= 0 || freeBytes === null) return;
8506
8555
  const needBytes = Math.ceil(measured / capturedFrames * remaining);
8507
8556
  if (needBytes > freeBytes * 0.9) {
8508
- throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured");
8557
+ throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured", remedy);
8509
8558
  }
8510
8559
  };
8511
8560
  }
@@ -8547,11 +8596,13 @@ async function runCaptureStage(input) {
8547
8596
  }
8548
8597
  }
8549
8598
  const captureOptions = buildCaptureOptions();
8550
- assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions);
8599
+ const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
8600
+ assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy);
8551
8601
  const checkDiskProjection = createDiskCaptureProjection({
8552
8602
  framesDir,
8553
8603
  totalFrames,
8554
- measureBytes: () => measureCaptureFrameBytes(workDir, framesDir)
8604
+ measureBytes: () => measureCaptureFrameBytes(workDir, framesDir),
8605
+ remedy
8555
8606
  });
8556
8607
  if (workerCount > 1) {
8557
8608
  const attempts = await executeDiskCaptureWithAdaptiveRetry({
@@ -9727,8 +9778,10 @@ function fpsToFfmpegArg2(fps) {
9727
9778
  }
9728
9779
  var KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA = ["-reinit_filter", "0"];
9729
9780
  var WRITE_EVERY_FRAME_WHOLE = ["-gifflags", "0"];
9781
+ var FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE = ["-xerror"];
9730
9782
  function framesInput(input, fpsArg) {
9731
9783
  return [
9784
+ ...FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE,
9732
9785
  "-framerate",
9733
9786
  fpsArg,
9734
9787
  ...KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA,
@@ -9974,28 +10027,82 @@ import { extname as extname3 } from "path";
9974
10027
  import { spawn } from "child_process";
9975
10028
  import { mkdtempSync as mkdtempSync3, renameSync as renameSync3, rmSync as rmSync7 } from "fs";
9976
10029
  import { dirname as dirname8, join as join24 } from "path";
10030
+
10031
+ // ../producer/src/services/render/audioTruePeak.ts
10032
+ var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
10033
+ function checkedDbfs(value) {
10034
+ if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
10035
+ throw new Error("[audioTruePeak] FFmpeg did not report AAC true peak");
10036
+ }
10037
+ return value;
10038
+ }
10039
+ async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
10040
+ let firstBlock;
10041
+ let laterBlocks = Number.NEGATIVE_INFINITY;
10042
+ let partialLine = "";
10043
+ const readLines = (chunk) => {
10044
+ const lines = (partialLine + chunk).split(/\r|\n/);
10045
+ partialLine = lines.pop() ?? "";
10046
+ for (const line of lines) {
10047
+ const ftpk = /FTPK:([^d]*)dBFS/.exec(line)?.[1];
10048
+ if (!ftpk) continue;
10049
+ const block = Math.max(...ftpk.trim().split(/\s+/).map(toDbfs));
10050
+ if (firstBlock === void 0) firstBlock = block;
10051
+ else laterBlocks = Math.max(laterBlocks, block);
10052
+ }
10053
+ };
10054
+ const result = await runFfmpeg(
10055
+ ["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
10056
+ { signal, timeout: timeoutMs, onStderr: readLines }
10057
+ );
10058
+ if (!result.success) {
10059
+ throw new Error(`[audioTruePeak] ${describeFfmpegFailure(result, timeoutMs)}`);
10060
+ }
10061
+ const summary = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
10062
+ return {
10063
+ peak: checkedDbfs(toDbfs(summary.at(-1)?.[1] ?? "nan")),
10064
+ firstBlock: checkedDbfs(firstBlock ?? Number.NEGATIVE_INFINITY),
10065
+ laterBlocks: checkedDbfs(laterBlocks)
10066
+ };
10067
+ }
10068
+ async function measureTruePeakDbfs(audioPath, options) {
10069
+ const { timeoutMs, signal } = options;
10070
+ const wholeFile = async () => (await scanTruePeak(audioPath, "", timeoutMs, signal)).peak;
10071
+ const { durationSeconds, sampleRate } = await extractAudioMetadata(audioPath, { signal });
10072
+ const block = 4800;
10073
+ const count = sampleRate === 48e3 && Number.isFinite(durationSeconds) ? aacSegmentCount(durationSeconds) : 1;
10074
+ if (count === 1) return wholeFile();
10075
+ const cut = (k) => k * Math.floor(durationSeconds * 10 / count) * block;
10076
+ const peers = new AbortController();
10077
+ const segmentSignal = signal ? AbortSignal.any([signal, peers.signal]) : peers.signal;
10078
+ const scans = await Promise.all(
10079
+ Array.from({ length: count }, (_, k) => {
10080
+ const start = k === 0 ? 0 : cut(k) - block;
10081
+ const end = k === count - 1 ? "" : `:end_sample=${cut(k + 1)}`;
10082
+ return scanTruePeak(
10083
+ audioPath,
10084
+ `atrim=start_sample=${start}${end},`,
10085
+ timeoutMs,
10086
+ segmentSignal
10087
+ );
10088
+ })
10089
+ ).catch((error) => {
10090
+ peers.abort();
10091
+ throw error;
10092
+ });
10093
+ const last = scans[count - 1];
10094
+ const counted = Math.max(scans[0].peak, ...scans.slice(1).map((scan) => scan.laterBlocks));
10095
+ if (last.peak > last.firstBlock || last.peak <= counted) return Math.max(counted, last.peak);
10096
+ return wholeFile();
10097
+ }
10098
+
10099
+ // ../producer/src/services/render/audioPadTrim.ts
9977
10100
  var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
9978
10101
  var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
9979
10102
  var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
9980
10103
  var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
9981
- function buildAacTruePeakCorrectionArgs(inputPath, outputPath, targetDurationSeconds, attenuationDb) {
9982
- return [
9983
- "-i",
9984
- inputPath,
9985
- "-map",
9986
- "0:a:0",
9987
- "-vn",
9988
- "-af",
9989
- `volume=${attenuationDb.toFixed(3)}dB`,
9990
- "-t",
9991
- formatSeconds(targetDurationSeconds),
9992
- "-c:a",
9993
- "aac",
9994
- "-b:a",
9995
- "192k",
9996
- "-y",
9997
- outputPath
9998
- ];
10104
+ function aacTruePeakCorrectionFilter(targetDurationSeconds, attenuationDb) {
10105
+ return `volume=${attenuationDb.toFixed(3)}dB,apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
9999
10106
  }
10000
10107
  function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
10001
10108
  const delta = targetDurationSeconds - sourceDurationSeconds;
@@ -10010,53 +10117,13 @@ function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, tar
10010
10117
  if (delta > 0) {
10011
10118
  return {
10012
10119
  operation: "pad",
10013
- steps: [
10014
- {
10015
- kind: "normalize",
10016
- args: [
10017
- "-i",
10018
- audioPath,
10019
- "-af",
10020
- `apad,atrim=0:${targetSec}`,
10021
- "-t",
10022
- targetSec,
10023
- "-c:a",
10024
- "aac",
10025
- "-b:a",
10026
- "192k",
10027
- "-y",
10028
- outputPath
10029
- ]
10030
- }
10031
- ],
10120
+ steps: [{ kind: "normalize", filter: `apad,atrim=0:${targetSec}` }],
10032
10121
  cleanupPaths: []
10033
10122
  };
10034
10123
  }
10035
10124
  return {
10036
10125
  operation: "trim",
10037
- steps: [
10038
- {
10039
- kind: "trim",
10040
- args: [
10041
- "-i",
10042
- audioPath,
10043
- "-af",
10044
- `atrim=duration=${targetSec},asetpts=PTS-STARTPTS`,
10045
- // `atrim` limits decoded samples but does not cap muxer timestamps
10046
- // introduced by encoder delay/flush. The output duration contract
10047
- // is enforced at the container boundary as well; without `-t`, a
10048
- // long primed source can retain its original tail after re-encode.
10049
- "-t",
10050
- targetSec,
10051
- "-c:a",
10052
- "aac",
10053
- "-b:a",
10054
- "192k",
10055
- "-y",
10056
- outputPath
10057
- ]
10058
- }
10059
- ],
10126
+ steps: [{ kind: "trim", filter: `atrim=duration=${targetSec},asetpts=PTS-STARTPTS` }],
10060
10127
  cleanupPaths: []
10061
10128
  };
10062
10129
  }
@@ -10073,7 +10140,8 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10073
10140
  const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
10074
10141
  const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
10075
10142
  const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
10076
- const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg ? void 0 : (audioPath, signal) => defaultProbeAudioTruePeakDbfs(audioPath, timeoutMs, signal));
10143
+ const encode = input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal);
10144
+ const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg || input.encodeAac ? void 0 : (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }));
10077
10145
  const [videoResult, audioResult] = await Promise.allSettled([
10078
10146
  probeVideo(input.videoPath),
10079
10147
  probeAudio(input.audioPath, input.signal)
@@ -10120,7 +10188,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10120
10188
  );
10121
10189
  try {
10122
10190
  for (const step of plan2.steps) {
10123
- const ffmpegResult = await runner(step.args);
10191
+ const ffmpegResult = step.kind === "copy" ? await runner(step.args) : await encode(input.audioPath, input.outputPath, targetDurationSeconds, step.filter);
10124
10192
  if (!ffmpegResult.success) {
10125
10193
  return {
10126
10194
  success: false,
@@ -10152,7 +10220,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10152
10220
  targetDurationSeconds,
10153
10221
  signal: input.signal,
10154
10222
  probeTruePeak,
10155
- runner
10223
+ encode
10156
10224
  });
10157
10225
  if (!correction.success) {
10158
10226
  return {
@@ -10182,8 +10250,8 @@ async function constrainAacTruePeak(input) {
10182
10250
  audioPath: input.audioPath,
10183
10251
  targetDurationSeconds: input.targetDurationSeconds,
10184
10252
  signal: input.signal,
10185
- probeTruePeak: (audioPath, signal) => defaultProbeAudioTruePeakDbfs(audioPath, timeoutMs, signal),
10186
- runner: (args) => defaultRunFfmpeg(args, timeoutMs, input.signal)
10253
+ probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
10254
+ encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
10187
10255
  });
10188
10256
  }
10189
10257
  async function enforceAacTruePeak(input) {
@@ -10210,13 +10278,11 @@ async function enforceAacTruePeak(input) {
10210
10278
  }
10211
10279
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
10212
10280
  attenuationDb += correctionTargetDbfs - truePeakDbfs;
10213
- const result = await input.runner(
10214
- buildAacTruePeakCorrectionArgs(
10215
- input.audioPath,
10216
- correctedPath,
10217
- input.targetDurationSeconds,
10218
- attenuationDb
10219
- )
10281
+ const result = await input.encode(
10282
+ input.audioPath,
10283
+ correctedPath,
10284
+ input.targetDurationSeconds,
10285
+ aacTruePeakCorrectionFilter(input.targetDurationSeconds, attenuationDb)
10220
10286
  );
10221
10287
  if (!result.success) {
10222
10288
  return {
@@ -10319,24 +10385,19 @@ async function defaultProbeAudioInfo(audioPath, signal) {
10319
10385
  audioCodec: metadata.audioCodec
10320
10386
  };
10321
10387
  }
10322
- async function defaultProbeAudioTruePeakDbfs(audioPath, timeoutMs, signal) {
10323
- const result = await runFfmpeg(
10324
- ["-i", audioPath, "-map", "0:a:0", "-vn", "-af", "ebur128=peak=true", "-f", "null", "-"],
10325
- { signal, timeout: timeoutMs }
10326
- );
10327
- if (!result.success) {
10328
- throw new Error(`[audioPadTrim] ${describeFfmpegFailure(result, timeoutMs)}`);
10329
- }
10330
- const matches = [...result.stderr.matchAll(/^\s*Peak:\s*([+-]?(?:[\d.]+|inf)) dBFS$/gim)];
10331
- const token = matches.at(-1)?.[1]?.toLowerCase();
10332
- const value = token === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
10333
- if (Number.isNaN(value) || value === Number.POSITIVE_INFINITY) {
10334
- throw new Error("[audioPadTrim] FFmpeg did not report AAC true peak");
10335
- }
10336
- return value;
10337
- }
10338
10388
  async function defaultRunFfmpeg(args, timeoutMs, signal) {
10339
- const result = await runFfmpeg(args, { signal, timeout: timeoutMs });
10389
+ return toStepResult(await runFfmpeg(args, { signal, timeout: timeoutMs }), timeoutMs);
10390
+ }
10391
+ function defaultAacEncoder(timeoutMs, signal, pcmSourcePath) {
10392
+ return async (inputPath, outputPath, durationSeconds, filter) => {
10393
+ const options = pcmSourcePath ? { signal, timeout: timeoutMs, filter } : { signal, timeout: timeoutMs, filter, segments: 1 };
10394
+ return toStepResult(
10395
+ await encodeAac(pcmSourcePath ?? inputPath, outputPath, durationSeconds, options),
10396
+ timeoutMs
10397
+ );
10398
+ };
10399
+ }
10400
+ function toStepResult(result, timeoutMs) {
10340
10401
  if (result.success) return { success: true };
10341
10402
  return {
10342
10403
  success: false,
@@ -10524,7 +10585,7 @@ function assertOutputScaleSupported(deviceScaleFactor, route) {
10524
10585
  const reason = UNSUPPORTED_REASON2[route];
10525
10586
  if (reason === null) return;
10526
10587
  throw new Error(
10527
- `[Render] --resolution other than the composition's size is not supported on the "${route}" capture route: ${reason}. Render at the composition's size, or as mp4 with page-side compositing.`
10588
+ `[Render] --resolution other than the composition's size is not supported on the "${route}" capture route: ${reason}. Render at the composition's size; or, for HDR footage, add --sdr to render this size in standard colour; or, for shader transitions, render as mp4.`
10528
10589
  );
10529
10590
  }
10530
10591
 
@@ -10742,7 +10803,7 @@ function getNextRetryWorkerCount(currentWorkers) {
10742
10803
  return Math.max(1, Math.floor(currentWorkers / 2));
10743
10804
  }
10744
10805
  function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
10745
- if (platform2 === "win32") return join25(systemTempDir, "hf-render-");
10806
+ if (platform2 === "win32" || platform2 === "darwin") return join25(systemTempDir, "hf-render-");
10746
10807
  return join25(dirname9(outputPath), `work-${jobId}-`);
10747
10808
  }
10748
10809
  var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
@@ -11236,6 +11297,9 @@ function resolveDeShortBand(args) {
11236
11297
  function shouldPreferSingleWorkerDrawElement(args) {
11237
11298
  return args.workerCount > 1 && typeof args.requestedWorkers !== "number" && args.useDrawElement && !args.deCompileGate && !args.forceScreenshot && outputUsesH264Pipeline(args.outputFormat) && args.minFrames > 0 && args.totalFrames >= args.minFrames && args.singleWorkerStreamingOk && !args.layeredOrEffectRoute && !args.supersampling && !args.probeDeGated && !args.experimentalParallelDeOptIn;
11238
11299
  }
11300
+ function isProbeDeGated(session) {
11301
+ return session !== null && session.captureMode !== "drawelement" && !session.deInitDeferred;
11302
+ }
11239
11303
  function resolveInversionRetryPlan(args) {
11240
11304
  if (args.deWorkerInversion !== "inverted") return null;
11241
11305
  const workerCount = args.isMemoryExhaustion ? 1 : args.preInversionWorkerCount;
@@ -11317,7 +11381,8 @@ async function closeOrphanedProbeForRetry(probe, closer, log, retryContext) {
11317
11381
  }
11318
11382
  function resolveParallelCaptureMode(args) {
11319
11383
  const supersampling = (args.deviceScaleFactor ?? 1) !== 1;
11320
- return args.headlessShell && args.platform === "linux" && !args.forceScreenshot && !supersampling ? "beginframe" : "screenshot";
11384
+ const headlessShell = args.headlessShellPath !== void 0;
11385
+ return headlessShell && args.platform === "linux" && !args.forceScreenshot && !supersampling ? "beginframe" : "screenshot";
11321
11386
  }
11322
11387
  function isCaptureParallelStreamRouterEnabled(env, resolvedMode) {
11323
11388
  const raw = env.HF_CAPTURE_PARALLEL_STREAM?.trim().toLowerCase();
@@ -11925,6 +11990,7 @@ async function executeRenderPipeline(input) {
11925
11990
  );
11926
11991
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
11927
11992
  const hasHdrContent = Boolean(effectiveHdr && nativeHdrIds.size > 0);
11993
+ if (hasHdrContent) assertOutputScaleSupported(deviceScaleFactor, "hdr_layered");
11928
11994
  const deSingleMinFramesRaw = process.env.HF_DE_SINGLE_MIN_FRAMES;
11929
11995
  const deSingleMinFramesNum = deSingleMinFramesRaw === void 0 || deSingleMinFramesRaw.trim() === "" ? 900 : Number(deSingleMinFramesRaw);
11930
11996
  const deSingleMinFrames = Number.isFinite(deSingleMinFramesNum) ? deSingleMinFramesNum : 900;
@@ -11965,7 +12031,7 @@ async function executeRenderPipeline(input) {
11965
12031
  singleWorkerStreamingOk: shouldUseStreamingEncode(cfg, outputFormat, 1, job.duration),
11966
12032
  layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
11967
12033
  supersampling: deviceScaleFactor !== 1,
11968
- probeDeGated: probeSession !== null && probeSession.captureMode !== "drawelement" && !probeSession.deInitDeferred,
12034
+ probeDeGated: isProbeDeGated(probeSession),
11969
12035
  experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || // Verified parallel DE streaming (opt-in) wants its parallelism kept.
11970
12036
  process.env.HF_DE_PARALLEL_STREAM === "true"
11971
12037
  };
@@ -11974,14 +12040,14 @@ async function executeRenderPipeline(input) {
11974
12040
  ...deInversionArgs,
11975
12041
  minFrames: Math.min(deSingleMinFrames, deShortBandMinFrames)
11976
12042
  });
11977
- const deShortBand = resolveDeShortBand({
12043
+ let deShortBand = resolveDeShortBand({
11978
12044
  invertAtBaseFloor,
11979
12045
  invertAtBandFloor,
11980
12046
  bandEnabled: deShortBandEnabled,
11981
12047
  bandOpen: deShortBandOpen,
11982
12048
  elementCountSource: compositionElementCountSource
11983
12049
  });
11984
- const deInversionEligible = deShortBandRoute && deShortBand === "applied" ? invertAtBandFloor : invertAtBaseFloor;
12050
+ let deInversionEligible = deShortBandRoute && deShortBand === "applied" ? invertAtBandFloor : invertAtBaseFloor;
11985
12051
  const deInversionEffectiveMinFrames = deShortBandRoute && deShortBand === "applied" ? Math.min(deSingleMinFrames, deShortBandMinFrames) : deSingleMinFrames;
11986
12052
  const deParallelRouterEnabled = isDeParallelRouterEnabled(process.env);
11987
12053
  const deParallelMinFramesRaw = process.env.HF_DE_PARALLEL_MIN_FRAMES;
@@ -11990,7 +12056,7 @@ async function executeRenderPipeline(input) {
11990
12056
  const deParallelMinMemRaw = process.env.HF_DE_PARALLEL_MIN_MEM_MB;
11991
12057
  const deParallelMinMemNum = deParallelMinMemRaw === void 0 || deParallelMinMemRaw.trim() === "" ? 24576 : Number(deParallelMinMemRaw);
11992
12058
  const deParallelMinMemoryMb = Number.isFinite(deParallelMinMemNum) ? deParallelMinMemNum : 24576;
11993
- const deParallelRouterEligible = shouldPreferParallelDrawElement({
12059
+ let deParallelRouterEligible = shouldPreferParallelDrawElement({
11994
12060
  workerCount: WOULD_RESOLVE_MULTI_WORKER,
11995
12061
  requestedWorkers: job.config.workers,
11996
12062
  useDrawElement: cfg.useDrawElement,
@@ -12001,7 +12067,7 @@ async function executeRenderPipeline(input) {
12001
12067
  minFrames: deParallelMinFrames,
12002
12068
  layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
12003
12069
  supersampling: deviceScaleFactor !== 1,
12004
- probeDeGated: probeSession !== null && probeSession.captureMode !== "drawelement" && !probeSession.deInitDeferred,
12070
+ probeDeGated: isProbeDeGated(probeSession),
12005
12071
  experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
12006
12072
  routerEnabled: deParallelRouterEnabled,
12007
12073
  // Router pins 3 workers for the streaming path; don't pin when a
@@ -12017,6 +12083,35 @@ async function executeRenderPipeline(input) {
12017
12083
  totalMemoryMb: Math.round(totalmem2() / (1024 * 1024)),
12018
12084
  minMemoryMb: deParallelMinMemoryMb
12019
12085
  });
12086
+ if (probeSession === null && (deInversionEligible || deParallelRouterEligible)) {
12087
+ try {
12088
+ probeSession = await createCaptureSession(
12089
+ activeFileServer.url,
12090
+ framesDir,
12091
+ buildCaptureOptions(),
12092
+ null,
12093
+ { ...cfg, forceScreenshot: captureForceScreenshot }
12094
+ );
12095
+ await initializeSession(probeSession);
12096
+ } catch (error) {
12097
+ assertNotAborted();
12098
+ if (probeSession) {
12099
+ lastBrowserConsole = probeSession.browserConsoleBuffer;
12100
+ await closeCaptureSession(probeSession).catch(() => {
12101
+ });
12102
+ probeSession = null;
12103
+ }
12104
+ log.warn("[Render] Fast capture gate check failed; routing without it.", {
12105
+ error: error instanceof Error ? error.message : String(error)
12106
+ });
12107
+ }
12108
+ if (isProbeDeGated(probeSession)) {
12109
+ deInversionEligible = false;
12110
+ deParallelRouterEligible = false;
12111
+ deShortBand = void 0;
12112
+ }
12113
+ }
12114
+ const deProbeGateReason = isProbeDeGated(probeSession) ? probeSession.deGateReason : void 0;
12020
12115
  let workerCount;
12021
12116
  const captureStageObservationData = (extra = {}) => ({
12022
12117
  stagePhase: "capturing",
@@ -12204,7 +12299,7 @@ async function executeRenderPipeline(input) {
12204
12299
  }
12205
12300
  const parallelCaptureMode = resolveParallelCaptureMode({
12206
12301
  platform: process.platform,
12207
- headlessShell: resolveHeadlessShellPath(cfg) !== null,
12302
+ headlessShellPath: resolveHeadlessShellPath(cfg),
12208
12303
  forceScreenshot: captureForceScreenshot,
12209
12304
  deviceScaleFactor: captureOptions.deviceScaleFactor
12210
12305
  });
@@ -12976,7 +13071,9 @@ async function executeRenderPipeline(input) {
12976
13071
  outputFormat,
12977
13072
  durationSeconds: job.duration,
12978
13073
  fps: fpsToNumber(job.config.fps),
12979
- expectedFrames: captureTotalFrames
13074
+ expectedFrames: captureTotalFrames,
13075
+ width: outputWidth,
13076
+ height: outputHeight
12980
13077
  })
12981
13078
  );
12982
13079
  const totalElapsed = Date.now() - pipelineStart;
@@ -13012,6 +13109,7 @@ async function executeRenderPipeline(input) {
13012
13109
  drawElement: {
13013
13110
  compileGate: deCompileGate,
13014
13111
  clampReason: deClampReason,
13112
+ probeGateReason: deProbeGateReason,
13015
13113
  workerInversion: deWorkerInversion,
13016
13114
  preInversionWorkers: deWorkerInversion ? preRoutingWorkerCount : void 0,
13017
13115
  compositionElementCount,
@@ -13564,6 +13662,29 @@ function buildChunkCaptureOptions(input) {
13564
13662
  requiresWebGpu: compositionRequiresWebGpu(input.html)
13565
13663
  };
13566
13664
  }
13665
+ function shownDeferredFrameIndexes(planVideos, frames, fps) {
13666
+ const shown = /* @__PURE__ */ new Map();
13667
+ const deferred = planVideos.extracted.filter((video) => video.deferredRange);
13668
+ if (deferred.length === 0) return shown;
13669
+ const table = createFrameLookupTable(
13670
+ planVideos.videos,
13671
+ deferred.map((video) => ({
13672
+ ...video,
13673
+ outputDir: "",
13674
+ framePaths: new Map(Array.from({ length: video.totalFrames }, (_, i) => [i, String(i)]))
13675
+ })),
13676
+ resolveRenderFpsConfig(fps).value
13677
+ );
13678
+ for (const frame of frames) {
13679
+ const globalTime = frame * fps.den / fps.num;
13680
+ for (const [videoId, payload] of table.getActiveFramePayloads(globalTime)) {
13681
+ const indexes = shown.get(videoId) ?? /* @__PURE__ */ new Set();
13682
+ indexes.add(payload.frameIndex);
13683
+ shown.set(videoId, indexes);
13684
+ }
13685
+ }
13686
+ return shown;
13687
+ }
13567
13688
  async function extractDeferredVideoFramesForChunk(input) {
13568
13689
  const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
13569
13690
  const remoteSources = input.remoteSources ?? {};
@@ -13580,24 +13701,7 @@ async function extractDeferredVideoFramesForChunk(input) {
13580
13701
  });
13581
13702
  }
13582
13703
  if (deferred.length === 0) return [];
13583
- const table = createFrameLookupTable(
13584
- input.planVideos.videos,
13585
- deferred.map((video) => ({
13586
- ...video,
13587
- outputDir: input.outputRoot,
13588
- framePaths: new Map(Array.from({ length: video.totalFrames }, (_, i) => [i, String(i)]))
13589
- })),
13590
- resolveRenderFpsConfig(input.fps).value
13591
- );
13592
- const shown = /* @__PURE__ */ new Map();
13593
- for (const frame of input.frames) {
13594
- const globalTime = frame * input.fps.den / input.fps.num;
13595
- for (const [videoId, payload] of table.getActiveFramePayloads(globalTime)) {
13596
- const indexes = shown.get(videoId) ?? /* @__PURE__ */ new Set();
13597
- indexes.add(payload.frameIndex);
13598
- shown.set(videoId, indexes);
13599
- }
13600
- }
13704
+ const shown = shownDeferredFrameIndexes(input.planVideos, input.frames, input.fps);
13601
13705
  return Promise.all(
13602
13706
  deferred.map(async (video) => {
13603
13707
  const outputDir = join27(input.outputRoot, video.videoId);
@@ -13623,7 +13727,8 @@ async function extractDeferredVideoFramesForChunk(input) {
13623
13727
  fps: input.fps,
13624
13728
  outputDir: input.outputRoot,
13625
13729
  format: range.format,
13626
- frameRanges: contiguousRuns(indexes)
13730
+ frameRanges: contiguousRuns(indexes),
13731
+ shownBox: input.shownBoxes?.get(video.videoId)
13627
13732
  },
13628
13733
  void 0,
13629
13734
  input.cfg
@@ -14275,6 +14380,20 @@ function withDuration(buf, target, duration) {
14275
14380
  function timescaleOf(buf, header) {
14276
14381
  return buf.readUInt32BE(header.bodyStart + (buf[header.bodyStart] === 1 ? 20 : 12));
14277
14382
  }
14383
+ var TRACK_ID_AT = { tkhd: [12, 20], mvhd: [96, 108] };
14384
+ function idOf(buf, target) {
14385
+ return buf.readUInt32BE(
14386
+ target.bodyStart + TRACK_ID_AT[target.type][buf[target.bodyStart] === 1 ? 1 : 0]
14387
+ );
14388
+ }
14389
+ function withId(boxBytes, type, id) {
14390
+ const header = boxAt(boxBytes, 0, 0, boxBytes.length);
14391
+ boxBytes.writeUInt32BE(
14392
+ id,
14393
+ header.bodyStart + TRACK_ID_AT[type][boxBytes[header.bodyStart] === 1 ? 1 : 0]
14394
+ );
14395
+ return boxBytes;
14396
+ }
14278
14397
  function buildStbl(stsd, track) {
14279
14398
  const stts = runs(track.durations);
14280
14399
  const stsc = [];
@@ -14316,24 +14435,48 @@ function planTracksOf(tracks, plan2, index) {
14316
14435
  }
14317
14436
  return matched;
14318
14437
  }
14319
- function buildStitchedHeader(layouts) {
14320
- const plan2 = readTracks(layouts[0].moov);
14321
- const chunks = layouts.map((layout, i) => planTracksOf(readTracks(layout.moov), plan2, i));
14438
+ function sourceTracks(layouts, audio) {
14439
+ const chunkPlan = readTracks(layouts[0].moov);
14440
+ const audioTracks = audio ? readTracks(audio.moov) : [];
14441
+ if (audio && (audioTracks.length !== 1 || audioTracks[0].handler !== "soun")) {
14442
+ throw new Error("[mp4Stitch] the separate audio file must hold one audio track");
14443
+ }
14444
+ const plan2 = [...chunkPlan, ...audioTracks];
14445
+ const chunks = layouts.map((layout, i) => {
14446
+ const tracks = readTracks(layout.moov);
14447
+ if (audio && tracks.some((track) => track.handler === "soun")) {
14448
+ throw new Error(`[mp4Stitch] chunk ${i} carries its own audio`);
14449
+ }
14450
+ return planTracksOf(tracks, plan2, i);
14451
+ });
14452
+ return {
14453
+ plan: plan2,
14454
+ chunkTrackCount: chunkPlan.length,
14455
+ sources: audio ? [[...chunkPlan.map(() => null), audioTracks[0]], ...chunks] : chunks
14456
+ };
14457
+ }
14458
+ function buildStitchedHeader(layouts, audio) {
14459
+ const { plan: plan2, chunkTrackCount, sources } = sourceTracks(layouts, audio);
14460
+ const sourceLayouts = audio ? [audio, ...layouts] : layouts;
14322
14461
  const payloadBases = [];
14323
14462
  let payloadBytes = 0;
14324
- for (const layout of layouts) {
14463
+ for (const layout of sourceLayouts) {
14325
14464
  payloadBases.push(payloadBytes);
14326
14465
  payloadBytes += layout.payloadEnd - layout.payloadStart;
14327
14466
  }
14328
14467
  const template = layouts[0].moov;
14329
14468
  const root = boxAt(template, 0, 0, template.length);
14330
- const movieTimescale = timescaleOf(template, child(template, root, "mvhd"));
14469
+ const mvhd = child(template, root, "mvhd");
14470
+ const movieTimescale = timescaleOf(template, mvhd);
14471
+ const chunkTrackIds = plan2.slice(0, chunkTrackCount).map((track) => idOf(template, child(template, track.trak, "tkhd")));
14472
+ const audioTrackId = Math.max(...chunkTrackIds) + 1;
14331
14473
  const buildMoov = (dataStart2) => {
14332
14474
  const traks = plan2.map((track, t) => {
14333
- const parts = chunks.flatMap((tracks, chunk) => {
14475
+ const parts = sources.flatMap((tracks, chunk) => {
14334
14476
  const part = tracks[t];
14335
14477
  return part ? [{ ...part, chunk }] : [];
14336
14478
  });
14479
+ const moov = t < chunkTrackCount ? template : audio.moov;
14337
14480
  const merged = {
14338
14481
  durations: parts.flatMap((part) => part.durations),
14339
14482
  sizes: parts.flatMap((part) => part.sizes),
@@ -14342,9 +14485,9 @@ function buildStitchedHeader(layouts) {
14342
14485
  chunkOffsets: parts.flatMap(
14343
14486
  (part) => part.chunkOffsets.map((offset) => {
14344
14487
  const i = part.chunk;
14345
- const { payloadStart, payloadEnd } = layouts[i];
14488
+ const { payloadStart, payloadEnd } = sourceLayouts[i];
14346
14489
  if (offset < payloadStart || offset >= payloadEnd) {
14347
- throw new Error(`[mp4Stitch] chunk ${i} has samples outside its mdat`);
14490
+ throw new Error(`[mp4Stitch] source ${i} has samples outside its mdat`);
14348
14491
  }
14349
14492
  return offset - payloadStart + payloadBases[i] + dataStart2;
14350
14493
  })
@@ -14360,20 +14503,25 @@ function buildStitchedHeader(layouts) {
14360
14503
  elst.writeBigInt64BE(BigInt(track.mediaTime), 12);
14361
14504
  elst.writeUInt32BE(65536, 20);
14362
14505
  const edts = track.mediaTime ? box("edts", fullBox("elst", 1, elst)) : Buffer.alloc(0);
14363
- const trak = rebuild(template, track.trak, {
14364
- tkhd: (tkhd) => Buffer.concat([withDuration(template, tkhd, shown), edts]),
14506
+ const tkhdOf = (tkhd) => {
14507
+ const timed = withDuration(moov, tkhd, shown);
14508
+ return t < chunkTrackCount ? timed : withId(timed, "tkhd", audioTrackId);
14509
+ };
14510
+ const trak = rebuild(moov, track.trak, {
14511
+ tkhd: (tkhd) => Buffer.concat([tkhdOf(tkhd), edts]),
14365
14512
  edts: () => Buffer.alloc(0),
14366
- mdia: (mdia) => rebuild(template, mdia, {
14367
- mdhd: (mdhd) => withDuration(template, mdhd, mediaDuration),
14368
- minf: (minf) => rebuild(template, minf, { stbl: () => buildStbl(track.stsd, merged) })
14513
+ mdia: (mdia) => rebuild(moov, mdia, {
14514
+ mdhd: (mdhd) => withDuration(moov, mdhd, mediaDuration),
14515
+ minf: (minf) => rebuild(moov, minf, { stbl: () => buildStbl(track.stsd, merged) })
14369
14516
  })
14370
14517
  });
14371
14518
  return { trak, shown };
14372
14519
  });
14373
14520
  let next = 0;
14521
+ const movieHeader = withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown)));
14374
14522
  return rebuild(template, root, {
14375
- mvhd: (mvhd) => withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown))),
14376
- trak: () => traks[next++].trak
14523
+ mvhd: () => audio ? withId(movieHeader, "mvhd", audioTrackId + 1) : movieHeader,
14524
+ trak: () => ++next < chunkTrackCount ? traks[next - 1].trak : Buffer.concat(traks.slice(next - 1).map((t) => t.trak))
14377
14525
  });
14378
14526
  };
14379
14527
  const ftyp = layouts[0].ftyp;
@@ -14406,15 +14554,16 @@ async function copyPayload(path, layout, index, out) {
14406
14554
  );
14407
14555
  }
14408
14556
  }
14409
- async function writeStitchedMp4(chunkPaths, outputPath) {
14410
- const layouts = await Promise.all(chunkPaths.map((path) => readMp4Layout(fileRange(path))));
14411
- const header = buildStitchedHeader(layouts);
14557
+ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
14558
+ const sourcePaths = audioPath ? [audioPath, ...chunkPaths] : chunkPaths;
14559
+ const layouts = await Promise.all(sourcePaths.map((path) => readMp4Layout(fileRange(path))));
14560
+ const header = audioPath ? buildStitchedHeader(layouts.slice(1), layouts[0]) : buildStitchedHeader(layouts);
14412
14561
  mkdirSync10(dirname11(outputPath), { recursive: true });
14413
14562
  const workDir = mkdtempSync4(`${outputPath}.stitch-`);
14414
14563
  const out = createWriteStream(join30(workDir, "out.mp4"));
14415
14564
  try {
14416
14565
  out.write(header);
14417
- for (const [i, path] of chunkPaths.entries()) await copyPayload(path, layouts[i], i, out);
14566
+ for (const [i, path] of sourcePaths.entries()) await copyPayload(path, layouts[i], i, out);
14418
14567
  out.end();
14419
14568
  await finished(out);
14420
14569
  renameSync4(join30(workDir, "out.mp4"), outputPath);
@@ -14985,7 +15134,8 @@ function buildLockedRenderConfig(input) {
14985
15134
  chunkCount: input.chunkCount,
14986
15135
  runtimeEnv: input.runtimeEnv,
14987
15136
  variables: config.variables,
14988
- ...input.chunksCarryAudio ? { chunksCarryAudio: true } : {}
15137
+ ...input.chunksCarryAudio ? { chunksCarryAudio: true } : {},
15138
+ ...input.separateAudio ? { separateAudio: true } : {}
14989
15139
  };
14990
15140
  }
14991
15141
  function carriesChunkAudio(config, options) {
@@ -15080,8 +15230,10 @@ function createVideoSourceShipper(input) {
15080
15230
  return result;
15081
15231
  };
15082
15232
  }
15083
- async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, options = {}) {
15084
- const planDir = executionPlanDir;
15233
+ function plansSeparateAudio(config, options) {
15234
+ return options.deferAudio === true && config.format === "mp4" && config.cfr !== true;
15235
+ }
15236
+ async function compilePlanComposition(projectDir, config, planDir, options) {
15085
15237
  rejectUnsupportedDistributedFormat(config);
15086
15238
  validateNoGpuEncode({
15087
15239
  useGpu: false,
@@ -15209,7 +15361,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15209
15361
  if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
15210
15362
  if (probeResult.probeSession) {
15211
15363
  try {
15212
- const { closeCaptureSession: closeCaptureSession2 } = await import("./src-XRTZOBO5.js");
15364
+ const { closeCaptureSession: closeCaptureSession2 } = await import("./src-EL6HGIS4.js");
15213
15365
  await closeCaptureSession2(probeResult.probeSession);
15214
15366
  } catch (err) {
15215
15367
  log.warn("[plan] probe session close failed", {
@@ -15217,6 +15369,100 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15217
15369
  });
15218
15370
  }
15219
15371
  }
15372
+ return {
15373
+ projectDir,
15374
+ log,
15375
+ sizeLimitBytes,
15376
+ abortSignal,
15377
+ assertNotAborted,
15378
+ cfg,
15379
+ job,
15380
+ workDir,
15381
+ compiledDir,
15382
+ finalCompiledDir,
15383
+ remoteFiles,
15384
+ urlByAbsentLocalPath,
15385
+ localPathByUrl,
15386
+ compileResult,
15387
+ compiled,
15388
+ composition,
15389
+ deviceScaleFactor,
15390
+ forceScreenshot,
15391
+ width,
15392
+ height,
15393
+ probeResult,
15394
+ totalFrames
15395
+ };
15396
+ }
15397
+ function mixPlanAudio(c, compiledDir, seconds, pcmPath) {
15398
+ return runAudioStage({
15399
+ projectDir: c.projectDir,
15400
+ workDir: c.workDir,
15401
+ compiledDir,
15402
+ urlByAbsentLocalPath: c.urlByAbsentLocalPath,
15403
+ duration: seconds,
15404
+ ffmpegProcessTimeout: c.cfg.ffmpegProcessTimeout,
15405
+ audioGain: c.cfg.audioGain,
15406
+ audios: c.composition.audios,
15407
+ masterFxChain: c.composition.masterFxChain,
15408
+ ...pcmPath ? { keepEncoderInputAt: pcmPath } : {},
15409
+ abortSignal: c.abortSignal,
15410
+ assertNotAborted: c.assertNotAborted
15411
+ });
15412
+ }
15413
+ async function extractPlanVideos(c, deferRangeExtraction) {
15414
+ const extractResult = await runExtractVideosStage({
15415
+ projectDir: c.projectDir,
15416
+ compiledDir: c.compiledDir,
15417
+ job: c.job,
15418
+ cfg: c.cfg,
15419
+ composition: c.composition,
15420
+ abortSignal: c.abortSignal,
15421
+ assertNotAborted: c.assertNotAborted,
15422
+ materializeSymlinks: true,
15423
+ deferRangeExtraction,
15424
+ urlByAbsentLocalPath: c.urlByAbsentLocalPath
15425
+ });
15426
+ if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
15427
+ if (extractResult.extractionResult) {
15428
+ assertVideoExtractionSucceeded(extractResult.extractionResult);
15429
+ }
15430
+ return extractResult;
15431
+ }
15432
+ async function holdPlanAudioPeak(c, audioPath, pcmPath, seconds) {
15433
+ const peak = await constrainAacTruePeak({
15434
+ audioPath,
15435
+ ...existsSync20(pcmPath) ? { sourcePath: pcmPath } : {},
15436
+ targetDurationSeconds: seconds,
15437
+ signal: c.abortSignal,
15438
+ timeoutMs: c.cfg.ffmpegProcessTimeout
15439
+ }).finally(() => rmSync10(pcmPath, { force: true }));
15440
+ if (!peak.success) throw encoderFailureError("[plan] audio true-peak check failed", peak);
15441
+ }
15442
+ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, options = {}) {
15443
+ const planDir = executionPlanDir;
15444
+ const c = await compilePlanComposition(projectDir, config, planDir, options);
15445
+ const {
15446
+ log,
15447
+ sizeLimitBytes,
15448
+ cfg,
15449
+ job,
15450
+ workDir,
15451
+ compiledDir,
15452
+ finalCompiledDir,
15453
+ remoteFiles,
15454
+ urlByAbsentLocalPath,
15455
+ localPathByUrl,
15456
+ compileResult,
15457
+ compiled,
15458
+ composition,
15459
+ deviceScaleFactor,
15460
+ forceScreenshot,
15461
+ width,
15462
+ height,
15463
+ probeResult,
15464
+ totalFrames
15465
+ } = c;
15220
15466
  const megapixels = compileResult.outputWidth * compileResult.outputHeight / 1e6;
15221
15467
  let maxParallel = resolveMaxParallelChunks(config, totalFrames, megapixels, log);
15222
15468
  const measureFrameCost = chunkSizingUsesFrameCost(config) && planNeedsFrameCostMeasurement(compiled.html);
@@ -15227,24 +15473,9 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15227
15473
  observedAt: "pre-extract",
15228
15474
  log
15229
15475
  });
15230
- stageStarted = Date.now();
15231
- const extractResult = await runExtractVideosStage({
15232
- projectDir,
15233
- compiledDir,
15234
- job,
15235
- cfg,
15236
- composition,
15237
- abortSignal,
15238
- assertNotAborted,
15239
- materializeSymlinks: true,
15240
- deferRangeExtraction: options.deferVideoExtraction,
15241
- urlByAbsentLocalPath
15242
- });
15476
+ let stageStarted = Date.now();
15477
+ const extractResult = await extractPlanVideos(c, options.deferVideoExtraction);
15243
15478
  logPlanStage(log, "extract", stageStarted);
15244
- if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
15245
- if (extractResult.extractionResult) {
15246
- assertVideoExtractionSucceeded(extractResult.extractionResult);
15247
- }
15248
15479
  const stagedVideoFrames = join33(compiledDir, "__hyperframes_video_frames");
15249
15480
  const videoFramesDst = join33(planDir, "video-frames");
15250
15481
  if (existsSync20(videoFramesDst)) rmSync10(videoFramesDst, { recursive: true, force: true });
@@ -15300,23 +15531,18 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15300
15531
  "utf-8"
15301
15532
  );
15302
15533
  const digests = options.digests ?? /* @__PURE__ */ new Map();
15303
- const chunksCarryAudio2 = carriesChunkAudio(config, options);
15304
- const audioSeconds = chunksCarryAudio2 ? totalFrames / config.fps : probeResult.duration;
15534
+ const separateAudio = plansSeparateAudio(config, options);
15535
+ const chunksCarryAudio = !separateAudio && carriesChunkAudio(config, options);
15536
+ const audioSeconds = chunksCarryAudio ? totalFrames / config.fps : probeResult.duration;
15537
+ const planMixPcmPath = join33(workDir, "plan-mix.wav");
15305
15538
  stageStarted = Date.now();
15306
15539
  const [audioOutcome, overlapOutcome, frameCostOutcome] = await Promise.allSettled([
15307
- runAudioStage({
15308
- projectDir,
15309
- workDir,
15310
- compiledDir: finalCompiledDir,
15311
- urlByAbsentLocalPath,
15312
- duration: audioSeconds,
15313
- ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
15314
- audioGain: cfg.audioGain,
15315
- audios: composition.audios,
15316
- masterFxChain: composition.masterFxChain,
15317
- abortSignal,
15318
- assertNotAborted
15319
- }).finally(() => logPlanStage(log, "audio", stageStarted)),
15540
+ separateAudio ? null : mixPlanAudio(
15541
+ c,
15542
+ finalCompiledDir,
15543
+ audioSeconds,
15544
+ chunksCarryAudio ? planMixPcmPath : void 0
15545
+ ).finally(() => logPlanStage(log, "audio", stageStarted)),
15320
15546
  (async () => {
15321
15547
  for (const dir of [finalCompiledDir, videoFramesDst, videoSourcesDir]) {
15322
15548
  await prehashPlanFiles(dir, digests);
@@ -15366,22 +15592,16 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15366
15592
  );
15367
15593
  const chunks = buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize);
15368
15594
  const audioResult = audioOutcome.value;
15369
- if (audioResult.audioError) {
15595
+ if (audioResult?.audioError) {
15370
15596
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
15371
15597
  }
15372
15598
  const planAudioPath = join33(planDir, PLAN_AUDIO_RELATIVE_PATH);
15373
- if (audioResult.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15599
+ if (audioResult?.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15374
15600
  renameSync5(audioResult.audioOutputPath, planAudioPath);
15375
15601
  }
15376
- if (chunksCarryAudio2 && existsSync20(planAudioPath)) {
15602
+ if (chunksCarryAudio && existsSync20(planAudioPath)) {
15377
15603
  stageStarted = Date.now();
15378
- const peak = await constrainAacTruePeak({
15379
- audioPath: planAudioPath,
15380
- targetDurationSeconds: audioSeconds,
15381
- signal: abortSignal,
15382
- timeoutMs: cfg.ffmpegProcessTimeout
15383
- });
15384
- if (!peak.success) throw encoderFailureError("[plan] audio true-peak check failed", peak);
15604
+ await holdPlanAudioPeak(c, planAudioPath, planMixPcmPath, audioSeconds);
15385
15605
  logPlanStage(log, "audio-peak", stageStarted);
15386
15606
  if (options.sliceChunkAudio) {
15387
15607
  stageStarted = Date.now();
@@ -15407,7 +15627,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15407
15627
  effectiveChunkSize,
15408
15628
  chunkCount,
15409
15629
  runtimeEnv,
15410
- chunksCarryAudio: chunksCarryAudio2
15630
+ chunksCarryAudio,
15631
+ separateAudio
15411
15632
  });
15412
15633
  const compositionJson = {
15413
15634
  durationSeconds: job.duration ?? 0,
@@ -15452,7 +15673,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15452
15673
  fontSnapshotSha,
15453
15674
  durationSeconds: job.duration ?? 0,
15454
15675
  totalFrames,
15455
- hasAudio: audioResult.hasAudio,
15676
+ hasAudio: audioResult?.hasAudio ?? false,
15456
15677
  digests
15457
15678
  });
15458
15679
  logPlanStage(log, "freeze", stageStarted);
@@ -16803,7 +17024,7 @@ function startServer(options = {}) {
16803
17024
  });
16804
17025
  async function shutdown(signal) {
16805
17026
  log.info(`Received ${signal}, shutting down`);
16806
- const { drainBrowserPool } = await import("./src-XRTZOBO5.js");
17027
+ const { drainBrowserPool } = await import("./src-EL6HGIS4.js");
16807
17028
  await drainBrowserPool().catch(() => {
16808
17029
  });
16809
17030
  const handle = await Promise.race([
@@ -17427,7 +17648,8 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
17427
17648
  )
17428
17649
  ),
17429
17650
  chunksCarryAudio: options.chunksCarryAudio,
17430
- sliceChunkAudio: true
17651
+ sliceChunkAudio: true,
17652
+ deferAudio: options.deferAudio
17431
17653
  });
17432
17654
  return await publishExecutionPlan(stagingRoot, publisher, { digests, log: config.logger });
17433
17655
  } catch (error) {
@@ -17753,7 +17975,8 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
17753
17975
  return mergePngFrameDirs(chunkPaths, outputPath, plan2.totalFrames, audioPath, start);
17754
17976
  }
17755
17977
  if (chunksAreStitchedMp4(planDir, cfr)) {
17756
- await writeStitchedMp4(chunkPaths, outputPath);
17978
+ const separateAudio = stitchesSeparateAudio(planDir) && audioPath !== null;
17979
+ await writeStitchedMp4(chunkPaths, outputPath, separateAudio ? audioPath : void 0);
17757
17980
  return {
17758
17981
  outputPath,
17759
17982
  durationMs: Date.now() - start,
@@ -17951,16 +18174,20 @@ async function runAssembleFfmpeg(args, step, signal, timeout) {
17951
18174
  }
17952
18175
  }
17953
18176
  function chunksAreStitchedMp4(planDir, cfr) {
17954
- if (!chunksCarryAudio(planDir)) return false;
18177
+ const { chunksCarryAudio, separateAudio } = lockedAudio(planDir);
18178
+ if (!chunksCarryAudio && !separateAudio) return false;
17955
18179
  if (cfr) {
17956
- throw new Error("[assemble] cfr=true cannot re-encode a plan whose mp4 chunks carry audio");
18180
+ throw new Error("[assemble] cfr=true cannot re-encode a plan whose mp4 chunks are stitched");
17957
18181
  }
17958
18182
  return true;
17959
18183
  }
17960
- function chunksCarryAudio(planDir) {
18184
+ function stitchesSeparateAudio(planDir) {
18185
+ return lockedAudio(planDir).separateAudio === true;
18186
+ }
18187
+ function lockedAudio(planDir) {
17961
18188
  const encoderJsonPath = join40(planDir, "meta", "encoder.json");
17962
- if (!existsSync26(encoderJsonPath)) return false;
17963
- return JSON.parse(readFileSync13(encoderJsonPath, "utf-8")).chunksCarryAudio === true;
18189
+ if (!existsSync26(encoderJsonPath)) return {};
18190
+ return JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
17964
18191
  }
17965
18192
  function readCfrEncoder(planDir, format) {
17966
18193
  if (format !== "mp4") {
@@ -18244,34 +18471,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18244
18471
  const framesDir = join41(workDir, "captured-frames");
18245
18472
  mkdirSync17(framesDir, { recursive: true });
18246
18473
  const videoFramesDir = `${workDir}.video-frames`;
18247
- const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
18248
- planDir,
18249
- planVideos,
18250
- frames: Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i),
18251
- fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
18252
- outputRoot: videoFramesDir,
18253
- cfg,
18254
- remoteSources
18255
- }) : [];
18474
+ const chunkFrameNumbers = Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i);
18475
+ const chunkFps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
18476
+ const deferredVideos = planVideos?.extracted.filter((video) => video.deferredRange) ?? [];
18477
+ const shownFrames = planVideos ? shownDeferredFrameIndexes(planVideos, chunkFrameNumbers, chunkFps) : /* @__PURE__ */ new Map();
18256
18478
  const remoteVideoIds = new Set(
18257
18479
  planVideos?.extracted.filter(
18258
18480
  (video) => video.deferredRange && Object.hasOwn(remoteSources, video.deferredRange.sourcePath)
18259
18481
  ).map((video) => video.videoId)
18260
18482
  );
18261
- const remoteVideos = deferredVideoFrames.filter((video) => remoteVideoIds.has(video.videoId));
18262
- const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable(
18263
- planVideos.videos,
18264
- [
18265
- ...rebuildExtractedFramesFromPlanDir(
18266
- planDir,
18267
- planVideos.extracted.filter((video) => !video.deferredRange),
18268
- v2Manifest === null ? "dense-v1" : "sparse-v2"
18269
- ),
18270
- ...deferredVideoFrames
18271
- ],
18272
- resolveRenderFpsConfig(job.config.fps).value
18273
- ) : null;
18274
- const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
18275
18483
  const fileServer = await createFileServer({
18276
18484
  projectDir: compiledDir,
18277
18485
  compiledDir,
@@ -18287,8 +18495,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18287
18495
  videoCount: planVideos?.videos.length ?? 0,
18288
18496
  html: readFileSync14(join41(compiledDir, "index.html"), "utf-8"),
18289
18497
  // The plan ships a deferred source only to chunks that show it; don't wait on the rest.
18290
- skipReadinessVideoIds: deferredVideoFrames.filter((video) => video.framePaths.size === 0 || remoteVideoIds.has(video.videoId)).map((video) => video.videoId),
18291
- videoMetadataHints: collectVideoMetadataHints(remoteVideos)
18498
+ skipReadinessVideoIds: deferredVideos.filter(
18499
+ (video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
18500
+ ).map((video) => video.videoId),
18501
+ videoMetadataHints: collectVideoMetadataHints(
18502
+ deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
18503
+ )
18292
18504
  });
18293
18505
  const chunkWorkerCount = calculateOptimalWorkers(framesInChunk, void 0, cfg);
18294
18506
  let session = null;
@@ -18301,6 +18513,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18301
18513
  const capturePerfs = [];
18302
18514
  const captureAttempts = [];
18303
18515
  let forceScreenshotForChunk = encoder.forceScreenshot;
18516
+ let shownBoxes;
18304
18517
  try {
18305
18518
  if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
18306
18519
  const bootStarted = Date.now();
@@ -18311,6 +18524,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18311
18524
  cfg
18312
18525
  );
18313
18526
  sessionBootMs = Date.now() - bootStarted;
18527
+ shownBoxes = await measureVideoShownBoxes(
18528
+ session.page,
18529
+ [...shownFrames.keys()],
18530
+ chunkFrameNumbers.map((frame) => frame * chunkFps.den / chunkFps.num)
18531
+ );
18314
18532
  const browserSelectedScreenshot = session.launchCaptureMode === "screenshot";
18315
18533
  const beginFrameStalled = !browserSelectedScreenshot && await beginFrameSessionNeedsScreenshotFallback(session);
18316
18534
  if (browserSelectedScreenshot) {
@@ -18352,6 +18570,29 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18352
18570
  session = null;
18353
18571
  }
18354
18572
  }
18573
+ const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
18574
+ planDir,
18575
+ planVideos,
18576
+ frames: chunkFrameNumbers,
18577
+ fps: chunkFps,
18578
+ outputRoot: videoFramesDir,
18579
+ cfg,
18580
+ remoteSources,
18581
+ shownBoxes
18582
+ }) : [];
18583
+ const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable(
18584
+ planVideos.videos,
18585
+ [
18586
+ ...rebuildExtractedFramesFromPlanDir(
18587
+ planDir,
18588
+ planVideos.extracted.filter((video) => !video.deferredRange),
18589
+ v2Manifest === null ? "dense-v1" : "sparse-v2"
18590
+ ),
18591
+ ...deferredVideoFrames
18592
+ ],
18593
+ resolveRenderFpsConfig(job.config.fps).value
18594
+ ) : null;
18595
+ const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
18355
18596
  const captureStarted = Date.now();
18356
18597
  captureMode = await runCaptureWithScreenshotFallback({
18357
18598
  forceScreenshot: forceScreenshotForChunk,
@@ -18542,6 +18783,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18542
18783
  encodeStageMs,
18543
18784
  workers: chunkWorkerCount,
18544
18785
  captureMode,
18786
+ // Device-pixel box each video's frames were extracted to cover; a video not listed kept full size.
18787
+ videoShownBoxes: Object.fromEntries(shownBoxes ?? []),
18545
18788
  sha256,
18546
18789
  outputKind,
18547
18790
  producerVersion: plan2.producerVersion,
@@ -18611,9 +18854,10 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
18611
18854
  }
18612
18855
 
18613
18856
  // ../producer/src/services/distributed/projectHash.ts
18614
- import { readdirSync as readdirSync12, readFileSync as readFileSync15, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
18857
+ import { closeSync as closeSync8, openSync as openSync7, readdirSync as readdirSync12, readSync as readSync5, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
18615
18858
  import { createHash as createHash7 } from "crypto";
18616
18859
  import { join as join43 } from "path";
18860
+ var READ_CHUNK_BYTES = 256 * 1024;
18617
18861
 
18618
18862
  export {
18619
18863
  createConsoleLogger,