hyperframes 0.8.181 → 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-AWQ2EGBU.js → add-HW3ZGR36.js} +22 -22
  3. package/dist/{animatedGifPrep-SENXJ7TN.js → animatedGifPrep-EPLN37ZN.js} +13 -13
  4. package/dist/api.js +28 -28
  5. package/dist/{asset-XBH3MN3Y.js → asset-TOASKL3Z.js} +3 -3
  6. package/dist/{assetCataloger-KH4X2CRO.js → assetCataloger-PZN5HYG2.js} +15 -13
  7. package/dist/{audio-Q7I473UN.js → audio-F5OIQVJO.js} +29 -29
  8. package/dist/{auth-I7CBTEUS.js → auth-YVQLRS3T.js} +4 -4
  9. package/dist/{autoUpdate-KY5TLSN7.js → autoUpdate-EG4ZFW4D.js} +2 -2
  10. package/dist/{backgroundChecks-4Q3VVK5W.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-P4LENNVT.js → beats-KGIOEBLW.js} +12 -12
  14. package/dist/{benchmark-AXOAECN3.js → benchmark-ZNY2L7HN.js} +24 -24
  15. package/dist/{browser-XZKB5HGJ.js → browser-REAO7RU4.js} +20 -20
  16. package/dist/{bundleWithLocalizedFonts-2WCR57IN.js → bundleWithLocalizedFonts-OAYLYC4W.js} +2 -2
  17. package/dist/{capture-NWARHRHR.js → capture-IK6GWQDD.js} +8 -5
  18. package/dist/{capture-4RN4APDF.js → capture-WOSF276U.js} +49 -41
  19. package/dist/{captureCompositionFrame-QOF5BRF4.js → captureCompositionFrame-DDZNKYRE.js} +21 -20
  20. package/dist/{captureTimeout-K2JNVVCI.js → captureTimeout-RIMG6IYH.js} +1 -1
  21. package/dist/{catalog-CRPOPVD5.js → catalog-AKTXS7OJ.js} +42 -42
  22. package/dist/{catch-up-MHWGUQ42.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-Y2RXRPL2.js → chunk-3BA2GP4H.js} +5 -5
  26. package/dist/{chunk-N5SXQCJL.js → chunk-3OLIGYWN.js} +3 -3
  27. package/dist/{chunk-OR5WCFWZ.js → chunk-3Q47QC46.js} +2 -2
  28. package/dist/{chunk-EOADENKV.js → chunk-4FQKS7BJ.js} +16 -14
  29. package/dist/{chunk-HODBPO4A.js → chunk-4UFJWRHG.js} +461 -275
  30. package/dist/{chunk-537IYAEE.js → chunk-4UMPIPCQ.js} +9 -1
  31. package/dist/{chunk-OJTDUF4W.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-YPBIAONI.js → chunk-73LTCCZI.js} +218 -150
  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-VAJIPSN6.js → chunk-ANR6WOC3.js} +1 -1
  40. package/dist/{chunk-4USWWI2Y.js → chunk-ATMDOUNN.js} +2 -2
  41. package/dist/{chunk-XSRUVMXK.js → chunk-B2SZZFWW.js} +4 -4
  42. package/dist/{chunk-TO6JNTML.js → chunk-BP4VNTZL.js} +13 -8
  43. package/dist/{chunk-DJJ7SK4F.js → chunk-CAHTO62O.js} +8 -8
  44. package/dist/{chunk-GNE57O7U.js → chunk-CGK4XZ2J.js} +1 -1
  45. package/dist/{chunk-O5AE7HOP.js → chunk-CJYAJ3QM.js} +37 -33
  46. package/dist/{chunk-5JMZLQT6.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-K26FR4SM.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-5E5EYPZO.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-WITHUCCB.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-IL3GTCQY.js → chunk-GTE5WZAM.js} +72 -140
  62. package/dist/{chunk-55IG255C.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-WUUCEFL5.js → chunk-IC2WYONA.js} +5 -5
  66. package/dist/{chunk-DUN63LHY.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-AOWDVB2T.js → chunk-KRFAX5JK.js} +1 -1
  72. package/dist/{chunk-UIDZRCBG.js → chunk-LBWRQ4XA.js} +1 -1
  73. package/dist/{chunk-T7UHTUUE.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-JMFJI26U.js → chunk-O7N3A7PE.js} +2 -2
  79. package/dist/{chunk-3PULCIY5.js → chunk-OH56PXFH.js} +5 -5
  80. package/dist/{chunk-H4YZZ2VQ.js → chunk-PFD25NAC.js} +1 -1
  81. package/dist/{check-DJCUOUDI.js → chunk-Q6UYKWBG.js} +198 -142
  82. package/dist/{chunk-3DKN5WKL.js → chunk-QH3T4WI2.js} +77 -6
  83. package/dist/{chunk-Y5GY6Y7A.js → chunk-QX4GGPMY.js} +2 -2
  84. package/dist/{chunk-B6JLCPT3.js → chunk-R6OV4J4K.js} +6 -6
  85. package/dist/{chunk-XEWHUPFS.js → chunk-RZXUQR42.js} +2 -2
  86. package/dist/{chunk-YMS6CJCD.js → chunk-SAQYJLI6.js} +1 -1
  87. package/dist/{chunk-OJDXABUF.js → chunk-TJB4GAV6.js} +2 -2
  88. package/dist/{checkBrowser-VES2NF3K.js → chunk-TOHA5NAU.js} +187 -114
  89. package/dist/{chunk-RXQHSGAQ.js → chunk-TXGZZKDF.js} +2 -2
  90. package/dist/{chunk-3XDOGSRD.js → chunk-U5YVWG5W.js} +3 -3
  91. package/dist/{chunk-ICMXC3MI.js → chunk-U6RXZNZE.js} +31 -18
  92. package/dist/{chunk-PF7OWVTL.js → chunk-UNOSCGBG.js} +2 -2
  93. package/dist/{chunk-X7CCSMRR.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-ZUOSILUE.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-6I7V6Q7F.js → chunk-XJHKJXT4.js} +103 -20
  102. package/dist/{chunk-V2UNZ7QP.js → chunk-XOVHO3TT.js} +25 -14
  103. package/dist/{chunk-65WIUIHL.js → chunk-XR5BSEUF.js} +7 -15
  104. package/dist/{chunk-IZM562YJ.js → chunk-Z4R35NJS.js} +4 -4
  105. package/dist/{chunk-4OT3FWVV.js → chunk-ZFO3A3QL.js} +2 -2
  106. package/dist/{clean-GJNDVVDI.js → clean-WNEIVBF3.js} +40 -40
  107. package/dist/cli.js +68 -64
  108. package/dist/{cloud-JCWNMKXC.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-XWM7FAYM.js → compare-2JWQ5ABA.js} +50 -47
  113. package/dist/{compiler-U3W6CJW7.js → compiler-27KQD7KW.js} +6 -6
  114. package/dist/{component-CBRAE325.js → component-HCRET3MH.js} +4 -4
  115. package/dist/{compositions-EZYJEHQ3.js → compositions-HTSQ6SUJ.js} +28 -29
  116. package/dist/{contactSheet-27SRU7A4.js → contactSheet-CAGMKI3E.js} +3 -1
  117. package/dist/{delete-JWWMOITI.js → delete-GHQ5UYM7.js} +9 -9
  118. package/dist/{deploy-UGOV76OH.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-XSKSZRR7.js → doctor-UGFVQZHI.js} +27 -27
  125. package/dist/{events-AIZODZMP.js → events-BLJGWMNP.js} +9 -9
  126. package/dist/{events-RGS4MAH4.js → events-YS4OVX3I.js} +6 -6
  127. package/dist/{feedback-GE3VDZOH.js → feedback-AZ3SJ5HF.js} +35 -35
  128. package/dist/{ffmpeg-SCXZTR6X.js → ffmpeg-2AECXG7V.js} +15 -15
  129. package/dist/{figma-3R4XST4Y.js → figma-DRYIJQHV.js} +3 -3
  130. package/dist/fontLocalizeCli.js +23 -23
  131. package/dist/{get-4FKJP6IM.js → get-DDEAOFPB.js} +9 -9
  132. package/dist/{gpuPolicy-4NNXESM7.js → gpuPolicy-UDJT2MQK.js} +13 -13
  133. package/dist/{grade-compare-FAYVPXUR.js → grade-compare-LJKK5NSY.js} +55 -50
  134. package/dist/{help-C3UQMYXK.js → help-TQBGYOID.js} +1 -1
  135. package/dist/{history-LDGQ2IZJ.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-KMXFLD3Q.js → info-ENI5MP4W.js} +20 -20
  140. package/dist/{init-D7GYHSX5.js → init-JXPXWV3G.js} +68 -68
  141. package/dist/{inspect-CUKWHCGS.js → inspect-WZNYQ5AB.js} +45 -44
  142. package/dist/{installer-EPRSH6QI.js → installer-3SGRBLJW.js} +7 -7
  143. package/dist/{keyframes-XCUPHBW3.js → keyframes-HEOC4CT4.js} +16 -16
  144. package/dist/{lambda-IMORFJHN.js → lambda-2VHQ4AL2.js} +15 -15
  145. package/dist/{layout-JD2IOBMS.js → layout-7WEUNO6T.js} +45 -44
  146. package/dist/{lint-FHLER36Y.js → lint-5YMAF3L7.js} +23 -23
  147. package/dist/{lintProject-ARKJXDQF.js → lintProject-C2ONSH2U.js} +4 -4
  148. package/dist/{list-MGCFG5MA.js → list-BIE3X7CO.js} +10 -10
  149. package/dist/{login-QX6M4UTR.js → login-YDIGJWCG.js} +7 -7
  150. package/dist/{logout-DKJANASY.js → logout-DDC4QLVK.js} +3 -3
  151. package/dist/{manager-IETAVBZS.js → manager-D2UV6RDQ.js} +16 -16
  152. package/dist/{manager-V2QCCPOJ.js → manager-UHWEKPXC.js} +8 -3
  153. package/dist/{media-treatment-MDAQRITV.js → media-treatment-FSKHPMWA.js} +33 -33
  154. package/dist/{media-use-4XXGABIL.js → media-use-JBN57JE3.js} +1 -1
  155. package/dist/{models-NZAOSI5A.js → models-COEK6LXG.js} +24 -24
  156. package/dist/{motionShot-6ZDUM2K4.js → motionShot-HYZ2ZGJQ.js} +28 -26
  157. package/dist/{normalize-audio-7PRYX6QM.js → normalize-audio-UXR6WQ7Q.js} +12 -12
  158. package/dist/{open-I254NY5J.js → open-OFT2GIDG.js} +24 -24
  159. package/dist/{parakeetRunner-TL3TOGUS.js → parakeetRunner-4NZ3QXS3.js} +25 -25
  160. package/dist/{pipeline-YPERVOQM.js → pipeline-ZKE54JMG.js} +18 -18
  161. package/dist/{play-MYI7BYKS.js → play-2VG77FT6.js} +39 -38
  162. package/dist/{policies-5NTZOHJA.js → policies-XXNGXWHV.js} +1 -1
  163. package/dist/{preflight-HRCSZ52D.js → preflight-QTWDIM5C.js} +18 -18
  164. package/dist/{present-WYXQDV4Q.js → present-OONXQLJG.js} +33 -33
  165. package/dist/{preview-535HTWQO.js → preview-6RZJFU76.js} +49 -49
  166. package/dist/{producer-C76CRJ5O.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-OBYHMPD4.js → remove-background-BIQNMIR7.js} +31 -31
  173. package/dist/{render-5BXTEEG7.js → render-65K3K6SD.js} +31 -31
  174. package/dist/{render-3MELF6Y3.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-L4UACXT3.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-DXTQPCAO.js → skills-J6MBGS5O.js} +17 -17
  184. package/dist/{skillsUpdateCheck-ULXRGLDU.js → skillsUpdateCheck-ZAXNLBF3.js} +3 -3
  185. package/dist/{snapshot-PWFHQU5E.js → snapshot-TL7TTFS6.js} +72 -71
  186. package/dist/{src-RDSWHMJS.js → src-EL6HGIS4.js} +33 -26
  187. package/dist/{src-S7NVL5VF.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-DcD3NXpX.js → hyperframes-player-hz_VqxBZ.js} +7 -2
  192. package/dist/studio/assets/{index-BIDKYk0h.js → index-CV_ojgp5.js} +1 -1
  193. package/dist/studio/assets/{index-CFrZAwKo.js → index-S1ceEagc.js} +4 -4
  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-LA5Z7RSS.js → studioServer-EZPRV43Y.js} +90 -84
  197. package/dist/{telemetry-STEMAMAX.js → telemetry-LFUVHARP.js} +10 -10
  198. package/dist/{telemetry-CQYHCCPL.js → telemetry-QG5AANZ4.js} +1 -1
  199. package/dist/{timeline-KLD24EN6.js → timeline-GSH743JE.js} +41 -41
  200. package/dist/{tokens-ICLORAA4.js → tokens-3ZMPSH7B.js} +3 -3
  201. package/dist/{transcribe-A6IKK7VS.js → transcribe-4SHNDPWW.js} +47 -47
  202. package/dist/{transcribe-CTHZLRXF.js → transcribe-TV7JBZQS.js} +19 -19
  203. package/dist/{tts-EDK3QS7M.js → tts-A2WILYGN.js} +6 -6
  204. package/dist/{updateCheck-FHCAAAXA.js → updateCheck-W7FTFOQF.js} +2 -2
  205. package/dist/{upgrade-7EV4F2R7.js → upgrade-7YYYNAPF.js} +3 -3
  206. package/dist/{urlDownloader-MIMGVASJ.js → urlDownloader-PQSUS2CP.js} +12 -12
  207. package/dist/{validate-LZF46W3V.js → validate-GCHAEVXM.js} +39 -38
  208. package/dist/{video-ZC47NFOL.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-4USWWI2Y.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,
@@ -80,6 +80,7 @@ import {
80
80
  distributeFrames,
81
81
  distributeFramesInterleaved,
82
82
  downloadToTemp,
83
+ encodeAac,
83
84
  encodeFramesChunkedConcat,
84
85
  encodeFramesFromDir,
85
86
  executeParallelCapture,
@@ -113,6 +114,7 @@ import {
113
114
  isVideoFrameFormat,
114
115
  isVideoHiddenBeforeStreamStart,
115
116
  isVideoPastTimelineEnd,
117
+ measureVideoShownBoxes,
116
118
  mergeWorkerFrames,
117
119
  muxVideoWithAudio,
118
120
  normalizeObjectFit,
@@ -139,7 +141,6 @@ import {
139
141
  resolveFinalFrameExtractionWindow,
140
142
  resolveHeadlessShellPath,
141
143
  resolvePlayableVideoDuration,
142
- resolveReferencedStart,
143
144
  resolveVideoExtractionWindow,
144
145
  runFfmpeg,
145
146
  runVideoExtractionWithRetry,
@@ -153,46 +154,35 @@ import {
153
154
  validateEngineConfigSnapshot,
154
155
  verifyDiskDrawElementSamples,
155
156
  writeCapturedFrame
156
- } from "./chunk-YPBIAONI.js";
157
- import {
158
- assignBundledRuntimeCompositionIds,
159
- buildVariablesByCompScript,
160
- emitMountedModuleScripts,
161
- emitRootCompositionVariableStyles,
162
- ensureExternalLinkTag,
163
- ensureExternalScriptTag,
164
- extractStandaloneEntryFromIndex,
165
- inlineSubCompositions,
166
- isTemplateEntry,
167
- parsesAsScript,
168
- prepareFlattenedInnerRoot
169
- } from "./chunk-4IEH6EAC.js";
170
- import {
171
- LOW_MEMORY_TOTAL_MB_THRESHOLD,
172
- getCgroupMemoryLimitMb,
173
- getSystemTotalMb
174
- } from "./chunk-W2LGJSEN.js";
157
+ } from "./chunk-73LTCCZI.js";
175
158
  import {
176
159
  DISTRIBUTED_FPS,
177
160
  HF_VFX_ATTR,
178
- MEDIA_START_BASIS_ATTR,
179
161
  chainCapture,
180
162
  clearGifFramesBeforeNext,
181
- compositionOriginSeconds,
182
- cutToHostSlots,
183
163
  fpsToFfmpegArg,
184
164
  fpsToNumber,
185
165
  gifClearsAfterLeavingFrameInPlace,
186
166
  gsapCdnDist,
187
- hostInPointSeconds,
188
167
  isDistributedFps,
189
168
  parseFps,
190
169
  parseVfxChain,
191
- readMediaStartBasis,
192
- resolveAbsoluteMediaStartSeconds,
193
170
  resolveAuthoredTimingWindow,
194
171
  toFps
195
- } 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";
196
186
  import {
197
187
  COMPOSITION_SOURCE_URL,
198
188
  HF_COLOR_GRADING_ATTR,
@@ -220,7 +210,7 @@ import {
220
210
  shouldClampResolvedMediaDuration,
221
211
  sourceTimeAt,
222
212
  styleElementsFor
223
- } from "./chunk-BAZ5LCR3.js";
213
+ } from "./chunk-GMNHAOMJ.js";
224
214
  import {
225
215
  MAX_AUDIO_GAIN
226
216
  } from "./chunk-UJKQWGFF.js";
@@ -235,24 +225,36 @@ import {
235
225
  normalizeResolutionFlag,
236
226
  suggestMatchingPreset
237
227
  } from "./chunk-YMS5FOMC.js";
228
+ import {
229
+ TRANSITIONS,
230
+ convertTransfer,
231
+ crossfade
232
+ } from "./chunk-3GXQPCJC.js";
238
233
  import {
239
234
  redactKnownPaths,
240
235
  redactTelemetryString
241
236
  } from "./chunk-634HSJ3J.js";
242
237
  import {
243
238
  chromeMajorCeiling
244
- } from "./chunk-537IYAEE.js";
239
+ } from "./chunk-4UMPIPCQ.js";
245
240
  import {
246
- lintHyperframeHtml
247
- } from "./chunk-GK2FIKIQ.js";
241
+ lintHyperframeHtml,
242
+ resolveReferencedStart
243
+ } from "./chunk-ADDHW2FS.js";
248
244
  import {
249
245
  isUnresolvedAssetPlaceholder,
250
246
  readProjectFile,
251
247
  resolveProjectRelativeSrc
252
248
  } from "./chunk-6BNTGNGD.js";
253
249
  import {
254
- checkSubCompositionUsability
255
- } 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";
256
258
  import {
257
259
  CSS_URL_RE,
258
260
  decodedUrlPath,
@@ -265,10 +267,10 @@ import {
265
267
  parseHTML
266
268
  } from "./chunk-SDYDNHEE.js";
267
269
  import {
268
- TRANSITIONS,
269
- convertTransfer,
270
- crossfade
271
- } from "./chunk-3GXQPCJC.js";
270
+ LOW_MEMORY_TOTAL_MB_THRESHOLD,
271
+ getCgroupMemoryLimitMb,
272
+ getSystemTotalMb
273
+ } from "./chunk-W2LGJSEN.js";
272
274
 
273
275
  // ../producer/src/services/renderOrchestrator.ts
274
276
  import {
@@ -1596,7 +1598,7 @@ function pidScope() {
1596
1598
  return {
1597
1599
  host: hostname(),
1598
1600
  // Without /proc (macOS, Windows) the boot time, to the minute, stands in for the boot id.
1599
- 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))),
1600
1602
  pidns: attempt(() => readlinkSync("/proc/self/ns/pid"))
1601
1603
  };
1602
1604
  }
@@ -1614,9 +1616,14 @@ function alive(pid) {
1614
1616
  return error.code === "EPERM";
1615
1617
  }
1616
1618
  }
1617
- function inThisPidScope(owner) {
1619
+ function knownPidScope() {
1618
1620
  const scope = pidScope();
1619
- 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;
1620
1627
  }
1621
1628
  function ownerState(dir) {
1622
1629
  let owner;
@@ -1681,7 +1688,9 @@ async function defaultArtifactDurationProbe(path) {
1681
1688
  const meta = await extractMediaMetadata(path);
1682
1689
  return {
1683
1690
  durationSeconds: meta.durationSeconds,
1684
- frames: meta.frames
1691
+ frames: meta.frames,
1692
+ width: meta.width,
1693
+ height: meta.height
1685
1694
  };
1686
1695
  }
1687
1696
  function buildArtifactExpectation(input) {
@@ -1690,7 +1699,9 @@ function buildArtifactExpectation(input) {
1690
1699
  const expectation = {
1691
1700
  expectedDurationSeconds: input.durationSeconds,
1692
1701
  fps: input.fps,
1693
- expectedFrames: input.expectedFrames
1702
+ expectedFrames: input.expectedFrames,
1703
+ expectedWidth: input.width,
1704
+ expectedHeight: input.height
1694
1705
  };
1695
1706
  if (input.outputFormat !== "hls") return expectation;
1696
1707
  return {
@@ -1758,6 +1769,16 @@ function assertFrameCountWithinTolerance(expectedFrames, probedFrames, stagingPa
1758
1769
  `Render artifact is truncated: expected ${expectedFrames} frames, probed ${probedFrames} frames (shortfall ${shortfall} frames). Artifact: ${stagingPath}`
1759
1770
  );
1760
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
+ }
1761
1782
  var ArtifactTransaction = class {
1762
1783
  constructor(destinationPath, kind, fileSystem = defaultFileSystem, durationProbe = defaultArtifactDurationProbe) {
1763
1784
  this.kind = kind;
@@ -1830,6 +1851,7 @@ var ArtifactTransaction = class {
1830
1851
  if (expected.expectedFrames !== void 0) {
1831
1852
  assertFrameCountWithinTolerance(expected.expectedFrames, probed.frames, probePath);
1832
1853
  }
1854
+ assertSizeMatches(expected, probed, probePath);
1833
1855
  }
1834
1856
  async runDurationProbe(probePath) {
1835
1857
  try {
@@ -3308,7 +3330,9 @@ function aggregateDrawElement(perfs, de) {
3308
3330
  if (perfs.length === 0) return void 0;
3309
3331
  const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
3310
3332
  const gateReasons = [
3311
- ...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
+ )
3312
3336
  ].sort();
3313
3337
  const gpuRenderers = [
3314
3338
  ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
@@ -6580,7 +6604,7 @@ async function readLocalFont(absPath) {
6580
6604
  return { kind: "inline", buffer: Buffer.concat(chunks, totalBytes) };
6581
6605
  }
6582
6606
  async function embedLocalFontFaces(html, projectDir) {
6583
- const { fontToDataUri: toDataUri } = await import("./deterministicFonts-L7YEEFGI.js");
6607
+ const { fontToDataUri: toDataUri } = await import("./deterministicFonts-JFLSSKRB.js");
6584
6608
  const styleBlockRe = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
6585
6609
  const fontFaceRe = /@font-face\s*\{([^}]*)\}/gi;
6586
6610
  let result = html;
@@ -8337,6 +8361,7 @@ async function runAudioStage(input) {
8337
8361
  audios,
8338
8362
  masterFxChain,
8339
8363
  urlByAbsentLocalPath,
8364
+ keepEncoderInputAt,
8340
8365
  abortSignal,
8341
8366
  assertNotAborted,
8342
8367
  log
@@ -8356,7 +8381,13 @@ async function runAudioStage(input) {
8356
8381
  audioOutputPath,
8357
8382
  duration,
8358
8383
  abortSignal,
8359
- { ffmpegProcessTimeout, audioGain, masterFxChain, urlByAbsentLocalPath },
8384
+ {
8385
+ ffmpegProcessTimeout,
8386
+ audioGain,
8387
+ masterFxChain,
8388
+ urlByAbsentLocalPath,
8389
+ keepEncoderInputAt
8390
+ },
8360
8391
  compiledDir
8361
8392
  );
8362
8393
  } catch (err) {
@@ -8456,13 +8487,29 @@ function inspectDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, free
8456
8487
  }
8457
8488
  return { available: false, estimatedBytes, freeBytes };
8458
8489
  }
8459
- 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) {
8460
8507
  const basisNote = basis === "measured" ? ` (measured from the first ${DISK_PROJECTION_SAMPLE_FRAMES} frames)` : "";
8461
8508
  return new Error(
8462
- `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}`
8463
8510
  );
8464
8511
  }
8465
- function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeDiskBytes) {
8512
+ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy, freeDiskBytes) {
8466
8513
  const headroom = inspectDiskCaptureHeadroom(
8467
8514
  framesDir,
8468
8515
  totalFrames,
@@ -8474,7 +8521,8 @@ function assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, freeD
8474
8521
  headroom.estimatedBytes,
8475
8522
  headroom.freeBytes,
8476
8523
  framesDir,
8477
- "estimate"
8524
+ "estimate",
8525
+ remedy
8478
8526
  );
8479
8527
  }
8480
8528
  function measureCaptureFrameBytes(workDir, framesDir) {
@@ -8494,7 +8542,7 @@ function measureCaptureFrameBytes(workDir, framesDir) {
8494
8542
  return total;
8495
8543
  }
8496
8544
  function createDiskCaptureProjection(input) {
8497
- const { framesDir, totalFrames, measureBytes, freeDiskBytes = freeDiskBytesAt } = input;
8545
+ const { framesDir, totalFrames, measureBytes, remedy, freeDiskBytes = freeDiskBytesAt } = input;
8498
8546
  let checked = false;
8499
8547
  return (capturedFrames) => {
8500
8548
  if (checked || capturedFrames < DISK_PROJECTION_SAMPLE_FRAMES) return;
@@ -8506,7 +8554,7 @@ function createDiskCaptureProjection(input) {
8506
8554
  if (remaining <= 0 || freeBytes === null) return;
8507
8555
  const needBytes = Math.ceil(measured / capturedFrames * remaining);
8508
8556
  if (needBytes > freeBytes * 0.9) {
8509
- throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured");
8557
+ throw diskCaptureShortfallError(needBytes, freeBytes, framesDir, "measured", remedy);
8510
8558
  }
8511
8559
  };
8512
8560
  }
@@ -8548,11 +8596,13 @@ async function runCaptureStage(input) {
8548
8596
  }
8549
8597
  }
8550
8598
  const captureOptions = buildCaptureOptions();
8551
- assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions);
8599
+ const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
8600
+ assertDiskCaptureHeadroom(framesDir, totalFrames, captureOptions, remedy);
8552
8601
  const checkDiskProjection = createDiskCaptureProjection({
8553
8602
  framesDir,
8554
8603
  totalFrames,
8555
- measureBytes: () => measureCaptureFrameBytes(workDir, framesDir)
8604
+ measureBytes: () => measureCaptureFrameBytes(workDir, framesDir),
8605
+ remedy
8556
8606
  });
8557
8607
  if (workerCount > 1) {
8558
8608
  const attempts = await executeDiskCaptureWithAdaptiveRetry({
@@ -9728,8 +9778,10 @@ function fpsToFfmpegArg2(fps) {
9728
9778
  }
9729
9779
  var KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA = ["-reinit_filter", "0"];
9730
9780
  var WRITE_EVERY_FRAME_WHOLE = ["-gifflags", "0"];
9781
+ var FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE = ["-xerror"];
9731
9782
  function framesInput(input, fpsArg) {
9732
9783
  return [
9784
+ ...FAIL_ON_UNREADABLE_FRAME_INSTEAD_OF_ENDING_THERE,
9733
9785
  "-framerate",
9734
9786
  fpsArg,
9735
9787
  ...KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA,
@@ -10014,12 +10066,13 @@ async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
10014
10066
  };
10015
10067
  }
10016
10068
  async function measureTruePeakDbfs(audioPath, options) {
10017
- const { timeoutMs, signal, segments } = options;
10069
+ const { timeoutMs, signal } = options;
10018
10070
  const wholeFile = async () => (await scanTruePeak(audioPath, "", timeoutMs, signal)).peak;
10071
+ const { durationSeconds, sampleRate } = await extractAudioMetadata(audioPath, { signal });
10019
10072
  const block = 4800;
10020
- const count = segments?.sampleRate === 48e3 ? aacSegmentCount(segments.durationSeconds) : 1;
10021
- if (!segments || count === 1) return wholeFile();
10022
- const cut = (k) => k * Math.floor(segments.durationSeconds * 10 / count) * block;
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;
10023
10076
  const peers = new AbortController();
10024
10077
  const segmentSignal = signal ? AbortSignal.any([signal, peers.signal]) : peers.signal;
10025
10078
  const scans = await Promise.all(
@@ -10048,24 +10101,8 @@ var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
10048
10101
  var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
10049
10102
  var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
10050
10103
  var MAX_TRUE_PEAK_CORRECTION_PASSES = 3;
10051
- function buildAacTruePeakCorrectionArgs(inputPath, outputPath, targetDurationSeconds, attenuationDb) {
10052
- return [
10053
- "-i",
10054
- inputPath,
10055
- "-map",
10056
- "0:a:0",
10057
- "-vn",
10058
- "-af",
10059
- `volume=${attenuationDb.toFixed(3)}dB`,
10060
- "-t",
10061
- formatSeconds(targetDurationSeconds),
10062
- "-c:a",
10063
- "aac",
10064
- "-b:a",
10065
- "192k",
10066
- "-y",
10067
- outputPath
10068
- ];
10104
+ function aacTruePeakCorrectionFilter(targetDurationSeconds, attenuationDb) {
10105
+ return `volume=${attenuationDb.toFixed(3)}dB,apad,atrim=0:${formatSeconds(targetDurationSeconds)}`;
10069
10106
  }
10070
10107
  function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, targetDurationSeconds) {
10071
10108
  const delta = targetDurationSeconds - sourceDurationSeconds;
@@ -10080,53 +10117,13 @@ function buildPadTrimAudioPlan(audioPath, outputPath, sourceDurationSeconds, tar
10080
10117
  if (delta > 0) {
10081
10118
  return {
10082
10119
  operation: "pad",
10083
- steps: [
10084
- {
10085
- kind: "normalize",
10086
- args: [
10087
- "-i",
10088
- audioPath,
10089
- "-af",
10090
- `apad,atrim=0:${targetSec}`,
10091
- "-t",
10092
- targetSec,
10093
- "-c:a",
10094
- "aac",
10095
- "-b:a",
10096
- "192k",
10097
- "-y",
10098
- outputPath
10099
- ]
10100
- }
10101
- ],
10120
+ steps: [{ kind: "normalize", filter: `apad,atrim=0:${targetSec}` }],
10102
10121
  cleanupPaths: []
10103
10122
  };
10104
10123
  }
10105
10124
  return {
10106
10125
  operation: "trim",
10107
- steps: [
10108
- {
10109
- kind: "trim",
10110
- args: [
10111
- "-i",
10112
- audioPath,
10113
- "-af",
10114
- `atrim=duration=${targetSec},asetpts=PTS-STARTPTS`,
10115
- // `atrim` limits decoded samples but does not cap muxer timestamps
10116
- // introduced by encoder delay/flush. The output duration contract
10117
- // is enforced at the container boundary as well; without `-t`, a
10118
- // long primed source can retain its original tail after re-encode.
10119
- "-t",
10120
- targetSec,
10121
- "-c:a",
10122
- "aac",
10123
- "-b:a",
10124
- "192k",
10125
- "-y",
10126
- outputPath
10127
- ]
10128
- }
10129
- ],
10126
+ steps: [{ kind: "trim", filter: `atrim=duration=${targetSec},asetpts=PTS-STARTPTS` }],
10130
10127
  cleanupPaths: []
10131
10128
  };
10132
10129
  }
@@ -10143,7 +10140,8 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10143
10140
  const probeAudio = input.probeAudioInfo ?? defaultProbeAudioInfo;
10144
10141
  const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
10145
10142
  const runner = input.runFfmpeg ?? ((args) => defaultRunFfmpeg(args, timeoutMs, input.signal));
10146
- const probeTruePeak = input.probeAudioTruePeakDbfs ?? (input.runFfmpeg ? void 0 : (audioPath, signal) => measureTruePeakDbfs(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 }));
10147
10145
  const [videoResult, audioResult] = await Promise.allSettled([
10148
10146
  probeVideo(input.videoPath),
10149
10147
  probeAudio(input.audioPath, input.signal)
@@ -10190,7 +10188,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10190
10188
  );
10191
10189
  try {
10192
10190
  for (const step of plan2.steps) {
10193
- 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);
10194
10192
  if (!ffmpegResult.success) {
10195
10193
  return {
10196
10194
  success: false,
@@ -10222,7 +10220,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
10222
10220
  targetDurationSeconds,
10223
10221
  signal: input.signal,
10224
10222
  probeTruePeak,
10225
- runner
10223
+ encode
10226
10224
  });
10227
10225
  if (!correction.success) {
10228
10226
  return {
@@ -10252,12 +10250,8 @@ async function constrainAacTruePeak(input) {
10252
10250
  audioPath: input.audioPath,
10253
10251
  targetDurationSeconds: input.targetDurationSeconds,
10254
10252
  signal: input.signal,
10255
- probeTruePeak: async (audioPath, signal) => {
10256
- const { sampleRate } = await extractAudioMetadata(audioPath, { signal });
10257
- const segments = { durationSeconds: input.targetDurationSeconds, sampleRate };
10258
- return measureTruePeakDbfs(audioPath, { timeoutMs, signal, segments });
10259
- },
10260
- runner: (args) => defaultRunFfmpeg(args, timeoutMs, input.signal)
10253
+ probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
10254
+ encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
10261
10255
  });
10262
10256
  }
10263
10257
  async function enforceAacTruePeak(input) {
@@ -10284,13 +10278,11 @@ async function enforceAacTruePeak(input) {
10284
10278
  }
10285
10279
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
10286
10280
  attenuationDb += correctionTargetDbfs - truePeakDbfs;
10287
- const result = await input.runner(
10288
- buildAacTruePeakCorrectionArgs(
10289
- input.audioPath,
10290
- correctedPath,
10291
- input.targetDurationSeconds,
10292
- attenuationDb
10293
- )
10281
+ const result = await input.encode(
10282
+ input.audioPath,
10283
+ correctedPath,
10284
+ input.targetDurationSeconds,
10285
+ aacTruePeakCorrectionFilter(input.targetDurationSeconds, attenuationDb)
10294
10286
  );
10295
10287
  if (!result.success) {
10296
10288
  return {
@@ -10394,7 +10386,18 @@ async function defaultProbeAudioInfo(audioPath, signal) {
10394
10386
  };
10395
10387
  }
10396
10388
  async function defaultRunFfmpeg(args, timeoutMs, signal) {
10397
- 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) {
10398
10401
  if (result.success) return { success: true };
10399
10402
  return {
10400
10403
  success: false,
@@ -10582,7 +10585,7 @@ function assertOutputScaleSupported(deviceScaleFactor, route) {
10582
10585
  const reason = UNSUPPORTED_REASON2[route];
10583
10586
  if (reason === null) return;
10584
10587
  throw new Error(
10585
- `[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.`
10586
10589
  );
10587
10590
  }
10588
10591
 
@@ -10800,7 +10803,7 @@ function getNextRetryWorkerCount(currentWorkers) {
10800
10803
  return Math.max(1, Math.floor(currentWorkers / 2));
10801
10804
  }
10802
10805
  function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
10803
- if (platform2 === "win32") return join25(systemTempDir, "hf-render-");
10806
+ if (platform2 === "win32" || platform2 === "darwin") return join25(systemTempDir, "hf-render-");
10804
10807
  return join25(dirname9(outputPath), `work-${jobId}-`);
10805
10808
  }
10806
10809
  var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
@@ -11294,6 +11297,9 @@ function resolveDeShortBand(args) {
11294
11297
  function shouldPreferSingleWorkerDrawElement(args) {
11295
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;
11296
11299
  }
11300
+ function isProbeDeGated(session) {
11301
+ return session !== null && session.captureMode !== "drawelement" && !session.deInitDeferred;
11302
+ }
11297
11303
  function resolveInversionRetryPlan(args) {
11298
11304
  if (args.deWorkerInversion !== "inverted") return null;
11299
11305
  const workerCount = args.isMemoryExhaustion ? 1 : args.preInversionWorkerCount;
@@ -11375,7 +11381,8 @@ async function closeOrphanedProbeForRetry(probe, closer, log, retryContext) {
11375
11381
  }
11376
11382
  function resolveParallelCaptureMode(args) {
11377
11383
  const supersampling = (args.deviceScaleFactor ?? 1) !== 1;
11378
- 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";
11379
11386
  }
11380
11387
  function isCaptureParallelStreamRouterEnabled(env, resolvedMode) {
11381
11388
  const raw = env.HF_CAPTURE_PARALLEL_STREAM?.trim().toLowerCase();
@@ -11983,6 +11990,7 @@ async function executeRenderPipeline(input) {
11983
11990
  );
11984
11991
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
11985
11992
  const hasHdrContent = Boolean(effectiveHdr && nativeHdrIds.size > 0);
11993
+ if (hasHdrContent) assertOutputScaleSupported(deviceScaleFactor, "hdr_layered");
11986
11994
  const deSingleMinFramesRaw = process.env.HF_DE_SINGLE_MIN_FRAMES;
11987
11995
  const deSingleMinFramesNum = deSingleMinFramesRaw === void 0 || deSingleMinFramesRaw.trim() === "" ? 900 : Number(deSingleMinFramesRaw);
11988
11996
  const deSingleMinFrames = Number.isFinite(deSingleMinFramesNum) ? deSingleMinFramesNum : 900;
@@ -12023,7 +12031,7 @@ async function executeRenderPipeline(input) {
12023
12031
  singleWorkerStreamingOk: shouldUseStreamingEncode(cfg, outputFormat, 1, job.duration),
12024
12032
  layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
12025
12033
  supersampling: deviceScaleFactor !== 1,
12026
- probeDeGated: probeSession !== null && probeSession.captureMode !== "drawelement" && !probeSession.deInitDeferred,
12034
+ probeDeGated: isProbeDeGated(probeSession),
12027
12035
  experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || // Verified parallel DE streaming (opt-in) wants its parallelism kept.
12028
12036
  process.env.HF_DE_PARALLEL_STREAM === "true"
12029
12037
  };
@@ -12032,14 +12040,14 @@ async function executeRenderPipeline(input) {
12032
12040
  ...deInversionArgs,
12033
12041
  minFrames: Math.min(deSingleMinFrames, deShortBandMinFrames)
12034
12042
  });
12035
- const deShortBand = resolveDeShortBand({
12043
+ let deShortBand = resolveDeShortBand({
12036
12044
  invertAtBaseFloor,
12037
12045
  invertAtBandFloor,
12038
12046
  bandEnabled: deShortBandEnabled,
12039
12047
  bandOpen: deShortBandOpen,
12040
12048
  elementCountSource: compositionElementCountSource
12041
12049
  });
12042
- const deInversionEligible = deShortBandRoute && deShortBand === "applied" ? invertAtBandFloor : invertAtBaseFloor;
12050
+ let deInversionEligible = deShortBandRoute && deShortBand === "applied" ? invertAtBandFloor : invertAtBaseFloor;
12043
12051
  const deInversionEffectiveMinFrames = deShortBandRoute && deShortBand === "applied" ? Math.min(deSingleMinFrames, deShortBandMinFrames) : deSingleMinFrames;
12044
12052
  const deParallelRouterEnabled = isDeParallelRouterEnabled(process.env);
12045
12053
  const deParallelMinFramesRaw = process.env.HF_DE_PARALLEL_MIN_FRAMES;
@@ -12048,7 +12056,7 @@ async function executeRenderPipeline(input) {
12048
12056
  const deParallelMinMemRaw = process.env.HF_DE_PARALLEL_MIN_MEM_MB;
12049
12057
  const deParallelMinMemNum = deParallelMinMemRaw === void 0 || deParallelMinMemRaw.trim() === "" ? 24576 : Number(deParallelMinMemRaw);
12050
12058
  const deParallelMinMemoryMb = Number.isFinite(deParallelMinMemNum) ? deParallelMinMemNum : 24576;
12051
- const deParallelRouterEligible = shouldPreferParallelDrawElement({
12059
+ let deParallelRouterEligible = shouldPreferParallelDrawElement({
12052
12060
  workerCount: WOULD_RESOLVE_MULTI_WORKER,
12053
12061
  requestedWorkers: job.config.workers,
12054
12062
  useDrawElement: cfg.useDrawElement,
@@ -12059,7 +12067,7 @@ async function executeRenderPipeline(input) {
12059
12067
  minFrames: deParallelMinFrames,
12060
12068
  layeredOrEffectRoute: hasHdrContent || compiled.hasShaderTransitions,
12061
12069
  supersampling: deviceScaleFactor !== 1,
12062
- probeDeGated: probeSession !== null && probeSession.captureMode !== "drawelement" && !probeSession.deInitDeferred,
12070
+ probeDeGated: isProbeDeGated(probeSession),
12063
12071
  experimentalParallelDeOptIn: process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true" || process.env.HF_DE_PARALLEL_STREAM === "true",
12064
12072
  routerEnabled: deParallelRouterEnabled,
12065
12073
  // Router pins 3 workers for the streaming path; don't pin when a
@@ -12075,6 +12083,35 @@ async function executeRenderPipeline(input) {
12075
12083
  totalMemoryMb: Math.round(totalmem2() / (1024 * 1024)),
12076
12084
  minMemoryMb: deParallelMinMemoryMb
12077
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;
12078
12115
  let workerCount;
12079
12116
  const captureStageObservationData = (extra = {}) => ({
12080
12117
  stagePhase: "capturing",
@@ -12262,7 +12299,7 @@ async function executeRenderPipeline(input) {
12262
12299
  }
12263
12300
  const parallelCaptureMode = resolveParallelCaptureMode({
12264
12301
  platform: process.platform,
12265
- headlessShell: resolveHeadlessShellPath(cfg) !== null,
12302
+ headlessShellPath: resolveHeadlessShellPath(cfg),
12266
12303
  forceScreenshot: captureForceScreenshot,
12267
12304
  deviceScaleFactor: captureOptions.deviceScaleFactor
12268
12305
  });
@@ -13034,7 +13071,9 @@ async function executeRenderPipeline(input) {
13034
13071
  outputFormat,
13035
13072
  durationSeconds: job.duration,
13036
13073
  fps: fpsToNumber(job.config.fps),
13037
- expectedFrames: captureTotalFrames
13074
+ expectedFrames: captureTotalFrames,
13075
+ width: outputWidth,
13076
+ height: outputHeight
13038
13077
  })
13039
13078
  );
13040
13079
  const totalElapsed = Date.now() - pipelineStart;
@@ -13070,6 +13109,7 @@ async function executeRenderPipeline(input) {
13070
13109
  drawElement: {
13071
13110
  compileGate: deCompileGate,
13072
13111
  clampReason: deClampReason,
13112
+ probeGateReason: deProbeGateReason,
13073
13113
  workerInversion: deWorkerInversion,
13074
13114
  preInversionWorkers: deWorkerInversion ? preRoutingWorkerCount : void 0,
13075
13115
  compositionElementCount,
@@ -13622,6 +13662,29 @@ function buildChunkCaptureOptions(input) {
13622
13662
  requiresWebGpu: compositionRequiresWebGpu(input.html)
13623
13663
  };
13624
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
+ }
13625
13688
  async function extractDeferredVideoFramesForChunk(input) {
13626
13689
  const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
13627
13690
  const remoteSources = input.remoteSources ?? {};
@@ -13638,24 +13701,7 @@ async function extractDeferredVideoFramesForChunk(input) {
13638
13701
  });
13639
13702
  }
13640
13703
  if (deferred.length === 0) return [];
13641
- const table = createFrameLookupTable(
13642
- input.planVideos.videos,
13643
- deferred.map((video) => ({
13644
- ...video,
13645
- outputDir: input.outputRoot,
13646
- framePaths: new Map(Array.from({ length: video.totalFrames }, (_, i) => [i, String(i)]))
13647
- })),
13648
- resolveRenderFpsConfig(input.fps).value
13649
- );
13650
- const shown = /* @__PURE__ */ new Map();
13651
- for (const frame of input.frames) {
13652
- const globalTime = frame * input.fps.den / input.fps.num;
13653
- for (const [videoId, payload] of table.getActiveFramePayloads(globalTime)) {
13654
- const indexes = shown.get(videoId) ?? /* @__PURE__ */ new Set();
13655
- indexes.add(payload.frameIndex);
13656
- shown.set(videoId, indexes);
13657
- }
13658
- }
13704
+ const shown = shownDeferredFrameIndexes(input.planVideos, input.frames, input.fps);
13659
13705
  return Promise.all(
13660
13706
  deferred.map(async (video) => {
13661
13707
  const outputDir = join27(input.outputRoot, video.videoId);
@@ -13681,7 +13727,8 @@ async function extractDeferredVideoFramesForChunk(input) {
13681
13727
  fps: input.fps,
13682
13728
  outputDir: input.outputRoot,
13683
13729
  format: range.format,
13684
- frameRanges: contiguousRuns(indexes)
13730
+ frameRanges: contiguousRuns(indexes),
13731
+ shownBox: input.shownBoxes?.get(video.videoId)
13685
13732
  },
13686
13733
  void 0,
13687
13734
  input.cfg
@@ -14333,6 +14380,20 @@ function withDuration(buf, target, duration) {
14333
14380
  function timescaleOf(buf, header) {
14334
14381
  return buf.readUInt32BE(header.bodyStart + (buf[header.bodyStart] === 1 ? 20 : 12));
14335
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
+ }
14336
14397
  function buildStbl(stsd, track) {
14337
14398
  const stts = runs(track.durations);
14338
14399
  const stsc = [];
@@ -14374,24 +14435,48 @@ function planTracksOf(tracks, plan2, index) {
14374
14435
  }
14375
14436
  return matched;
14376
14437
  }
14377
- function buildStitchedHeader(layouts) {
14378
- const plan2 = readTracks(layouts[0].moov);
14379
- 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;
14380
14461
  const payloadBases = [];
14381
14462
  let payloadBytes = 0;
14382
- for (const layout of layouts) {
14463
+ for (const layout of sourceLayouts) {
14383
14464
  payloadBases.push(payloadBytes);
14384
14465
  payloadBytes += layout.payloadEnd - layout.payloadStart;
14385
14466
  }
14386
14467
  const template = layouts[0].moov;
14387
14468
  const root = boxAt(template, 0, 0, template.length);
14388
- 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;
14389
14473
  const buildMoov = (dataStart2) => {
14390
14474
  const traks = plan2.map((track, t) => {
14391
- const parts = chunks.flatMap((tracks, chunk) => {
14475
+ const parts = sources.flatMap((tracks, chunk) => {
14392
14476
  const part = tracks[t];
14393
14477
  return part ? [{ ...part, chunk }] : [];
14394
14478
  });
14479
+ const moov = t < chunkTrackCount ? template : audio.moov;
14395
14480
  const merged = {
14396
14481
  durations: parts.flatMap((part) => part.durations),
14397
14482
  sizes: parts.flatMap((part) => part.sizes),
@@ -14400,9 +14485,9 @@ function buildStitchedHeader(layouts) {
14400
14485
  chunkOffsets: parts.flatMap(
14401
14486
  (part) => part.chunkOffsets.map((offset) => {
14402
14487
  const i = part.chunk;
14403
- const { payloadStart, payloadEnd } = layouts[i];
14488
+ const { payloadStart, payloadEnd } = sourceLayouts[i];
14404
14489
  if (offset < payloadStart || offset >= payloadEnd) {
14405
- throw new Error(`[mp4Stitch] chunk ${i} has samples outside its mdat`);
14490
+ throw new Error(`[mp4Stitch] source ${i} has samples outside its mdat`);
14406
14491
  }
14407
14492
  return offset - payloadStart + payloadBases[i] + dataStart2;
14408
14493
  })
@@ -14418,20 +14503,25 @@ function buildStitchedHeader(layouts) {
14418
14503
  elst.writeBigInt64BE(BigInt(track.mediaTime), 12);
14419
14504
  elst.writeUInt32BE(65536, 20);
14420
14505
  const edts = track.mediaTime ? box("edts", fullBox("elst", 1, elst)) : Buffer.alloc(0);
14421
- const trak = rebuild(template, track.trak, {
14422
- 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]),
14423
14512
  edts: () => Buffer.alloc(0),
14424
- mdia: (mdia) => rebuild(template, mdia, {
14425
- mdhd: (mdhd) => withDuration(template, mdhd, mediaDuration),
14426
- 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) })
14427
14516
  })
14428
14517
  });
14429
14518
  return { trak, shown };
14430
14519
  });
14431
14520
  let next = 0;
14521
+ const movieHeader = withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown)));
14432
14522
  return rebuild(template, root, {
14433
- mvhd: (mvhd) => withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown))),
14434
- 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))
14435
14525
  });
14436
14526
  };
14437
14527
  const ftyp = layouts[0].ftyp;
@@ -14464,15 +14554,16 @@ async function copyPayload(path, layout, index, out) {
14464
14554
  );
14465
14555
  }
14466
14556
  }
14467
- async function writeStitchedMp4(chunkPaths, outputPath) {
14468
- const layouts = await Promise.all(chunkPaths.map((path) => readMp4Layout(fileRange(path))));
14469
- 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);
14470
14561
  mkdirSync10(dirname11(outputPath), { recursive: true });
14471
14562
  const workDir = mkdtempSync4(`${outputPath}.stitch-`);
14472
14563
  const out = createWriteStream(join30(workDir, "out.mp4"));
14473
14564
  try {
14474
14565
  out.write(header);
14475
- 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);
14476
14567
  out.end();
14477
14568
  await finished(out);
14478
14569
  renameSync4(join30(workDir, "out.mp4"), outputPath);
@@ -15043,7 +15134,8 @@ function buildLockedRenderConfig(input) {
15043
15134
  chunkCount: input.chunkCount,
15044
15135
  runtimeEnv: input.runtimeEnv,
15045
15136
  variables: config.variables,
15046
- ...input.chunksCarryAudio ? { chunksCarryAudio: true } : {}
15137
+ ...input.chunksCarryAudio ? { chunksCarryAudio: true } : {},
15138
+ ...input.separateAudio ? { separateAudio: true } : {}
15047
15139
  };
15048
15140
  }
15049
15141
  function carriesChunkAudio(config, options) {
@@ -15138,8 +15230,10 @@ function createVideoSourceShipper(input) {
15138
15230
  return result;
15139
15231
  };
15140
15232
  }
15141
- async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, options = {}) {
15142
- 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) {
15143
15237
  rejectUnsupportedDistributedFormat(config);
15144
15238
  validateNoGpuEncode({
15145
15239
  useGpu: false,
@@ -15267,7 +15361,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15267
15361
  if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
15268
15362
  if (probeResult.probeSession) {
15269
15363
  try {
15270
- const { closeCaptureSession: closeCaptureSession2 } = await import("./src-RDSWHMJS.js");
15364
+ const { closeCaptureSession: closeCaptureSession2 } = await import("./src-EL6HGIS4.js");
15271
15365
  await closeCaptureSession2(probeResult.probeSession);
15272
15366
  } catch (err) {
15273
15367
  log.warn("[plan] probe session close failed", {
@@ -15275,6 +15369,100 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15275
15369
  });
15276
15370
  }
15277
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;
15278
15466
  const megapixels = compileResult.outputWidth * compileResult.outputHeight / 1e6;
15279
15467
  let maxParallel = resolveMaxParallelChunks(config, totalFrames, megapixels, log);
15280
15468
  const measureFrameCost = chunkSizingUsesFrameCost(config) && planNeedsFrameCostMeasurement(compiled.html);
@@ -15285,24 +15473,9 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15285
15473
  observedAt: "pre-extract",
15286
15474
  log
15287
15475
  });
15288
- stageStarted = Date.now();
15289
- const extractResult = await runExtractVideosStage({
15290
- projectDir,
15291
- compiledDir,
15292
- job,
15293
- cfg,
15294
- composition,
15295
- abortSignal,
15296
- assertNotAborted,
15297
- materializeSymlinks: true,
15298
- deferRangeExtraction: options.deferVideoExtraction,
15299
- urlByAbsentLocalPath
15300
- });
15476
+ let stageStarted = Date.now();
15477
+ const extractResult = await extractPlanVideos(c, options.deferVideoExtraction);
15301
15478
  logPlanStage(log, "extract", stageStarted);
15302
- if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
15303
- if (extractResult.extractionResult) {
15304
- assertVideoExtractionSucceeded(extractResult.extractionResult);
15305
- }
15306
15479
  const stagedVideoFrames = join33(compiledDir, "__hyperframes_video_frames");
15307
15480
  const videoFramesDst = join33(planDir, "video-frames");
15308
15481
  if (existsSync20(videoFramesDst)) rmSync10(videoFramesDst, { recursive: true, force: true });
@@ -15358,23 +15531,18 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15358
15531
  "utf-8"
15359
15532
  );
15360
15533
  const digests = options.digests ?? /* @__PURE__ */ new Map();
15361
- const chunksCarryAudio2 = carriesChunkAudio(config, options);
15362
- 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");
15363
15538
  stageStarted = Date.now();
15364
15539
  const [audioOutcome, overlapOutcome, frameCostOutcome] = await Promise.allSettled([
15365
- runAudioStage({
15366
- projectDir,
15367
- workDir,
15368
- compiledDir: finalCompiledDir,
15369
- urlByAbsentLocalPath,
15370
- duration: audioSeconds,
15371
- ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
15372
- audioGain: cfg.audioGain,
15373
- audios: composition.audios,
15374
- masterFxChain: composition.masterFxChain,
15375
- abortSignal,
15376
- assertNotAborted
15377
- }).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)),
15378
15546
  (async () => {
15379
15547
  for (const dir of [finalCompiledDir, videoFramesDst, videoSourcesDir]) {
15380
15548
  await prehashPlanFiles(dir, digests);
@@ -15424,22 +15592,16 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15424
15592
  );
15425
15593
  const chunks = buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize);
15426
15594
  const audioResult = audioOutcome.value;
15427
- if (audioResult.audioError) {
15595
+ if (audioResult?.audioError) {
15428
15596
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
15429
15597
  }
15430
15598
  const planAudioPath = join33(planDir, PLAN_AUDIO_RELATIVE_PATH);
15431
- if (audioResult.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15599
+ if (audioResult?.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15432
15600
  renameSync5(audioResult.audioOutputPath, planAudioPath);
15433
15601
  }
15434
- if (chunksCarryAudio2 && existsSync20(planAudioPath)) {
15602
+ if (chunksCarryAudio && existsSync20(planAudioPath)) {
15435
15603
  stageStarted = Date.now();
15436
- const peak = await constrainAacTruePeak({
15437
- audioPath: planAudioPath,
15438
- targetDurationSeconds: audioSeconds,
15439
- signal: abortSignal,
15440
- timeoutMs: cfg.ffmpegProcessTimeout
15441
- });
15442
- if (!peak.success) throw encoderFailureError("[plan] audio true-peak check failed", peak);
15604
+ await holdPlanAudioPeak(c, planAudioPath, planMixPcmPath, audioSeconds);
15443
15605
  logPlanStage(log, "audio-peak", stageStarted);
15444
15606
  if (options.sliceChunkAudio) {
15445
15607
  stageStarted = Date.now();
@@ -15465,7 +15627,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15465
15627
  effectiveChunkSize,
15466
15628
  chunkCount,
15467
15629
  runtimeEnv,
15468
- chunksCarryAudio: chunksCarryAudio2
15630
+ chunksCarryAudio,
15631
+ separateAudio
15469
15632
  });
15470
15633
  const compositionJson = {
15471
15634
  durationSeconds: job.duration ?? 0,
@@ -15510,7 +15673,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15510
15673
  fontSnapshotSha,
15511
15674
  durationSeconds: job.duration ?? 0,
15512
15675
  totalFrames,
15513
- hasAudio: audioResult.hasAudio,
15676
+ hasAudio: audioResult?.hasAudio ?? false,
15514
15677
  digests
15515
15678
  });
15516
15679
  logPlanStage(log, "freeze", stageStarted);
@@ -16861,7 +17024,7 @@ function startServer(options = {}) {
16861
17024
  });
16862
17025
  async function shutdown(signal) {
16863
17026
  log.info(`Received ${signal}, shutting down`);
16864
- const { drainBrowserPool } = await import("./src-RDSWHMJS.js");
17027
+ const { drainBrowserPool } = await import("./src-EL6HGIS4.js");
16865
17028
  await drainBrowserPool().catch(() => {
16866
17029
  });
16867
17030
  const handle = await Promise.race([
@@ -17485,7 +17648,8 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
17485
17648
  )
17486
17649
  ),
17487
17650
  chunksCarryAudio: options.chunksCarryAudio,
17488
- sliceChunkAudio: true
17651
+ sliceChunkAudio: true,
17652
+ deferAudio: options.deferAudio
17489
17653
  });
17490
17654
  return await publishExecutionPlan(stagingRoot, publisher, { digests, log: config.logger });
17491
17655
  } catch (error) {
@@ -17811,7 +17975,8 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
17811
17975
  return mergePngFrameDirs(chunkPaths, outputPath, plan2.totalFrames, audioPath, start);
17812
17976
  }
17813
17977
  if (chunksAreStitchedMp4(planDir, cfr)) {
17814
- await writeStitchedMp4(chunkPaths, outputPath);
17978
+ const separateAudio = stitchesSeparateAudio(planDir) && audioPath !== null;
17979
+ await writeStitchedMp4(chunkPaths, outputPath, separateAudio ? audioPath : void 0);
17815
17980
  return {
17816
17981
  outputPath,
17817
17982
  durationMs: Date.now() - start,
@@ -18009,16 +18174,20 @@ async function runAssembleFfmpeg(args, step, signal, timeout) {
18009
18174
  }
18010
18175
  }
18011
18176
  function chunksAreStitchedMp4(planDir, cfr) {
18012
- if (!chunksCarryAudio(planDir)) return false;
18177
+ const { chunksCarryAudio, separateAudio } = lockedAudio(planDir);
18178
+ if (!chunksCarryAudio && !separateAudio) return false;
18013
18179
  if (cfr) {
18014
- 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");
18015
18181
  }
18016
18182
  return true;
18017
18183
  }
18018
- function chunksCarryAudio(planDir) {
18184
+ function stitchesSeparateAudio(planDir) {
18185
+ return lockedAudio(planDir).separateAudio === true;
18186
+ }
18187
+ function lockedAudio(planDir) {
18019
18188
  const encoderJsonPath = join40(planDir, "meta", "encoder.json");
18020
- if (!existsSync26(encoderJsonPath)) return false;
18021
- return JSON.parse(readFileSync13(encoderJsonPath, "utf-8")).chunksCarryAudio === true;
18189
+ if (!existsSync26(encoderJsonPath)) return {};
18190
+ return JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
18022
18191
  }
18023
18192
  function readCfrEncoder(planDir, format) {
18024
18193
  if (format !== "mp4") {
@@ -18302,34 +18471,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18302
18471
  const framesDir = join41(workDir, "captured-frames");
18303
18472
  mkdirSync17(framesDir, { recursive: true });
18304
18473
  const videoFramesDir = `${workDir}.video-frames`;
18305
- const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
18306
- planDir,
18307
- planVideos,
18308
- frames: Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i),
18309
- fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
18310
- outputRoot: videoFramesDir,
18311
- cfg,
18312
- remoteSources
18313
- }) : [];
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();
18314
18478
  const remoteVideoIds = new Set(
18315
18479
  planVideos?.extracted.filter(
18316
18480
  (video) => video.deferredRange && Object.hasOwn(remoteSources, video.deferredRange.sourcePath)
18317
18481
  ).map((video) => video.videoId)
18318
18482
  );
18319
- const remoteVideos = deferredVideoFrames.filter((video) => remoteVideoIds.has(video.videoId));
18320
- const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable(
18321
- planVideos.videos,
18322
- [
18323
- ...rebuildExtractedFramesFromPlanDir(
18324
- planDir,
18325
- planVideos.extracted.filter((video) => !video.deferredRange),
18326
- v2Manifest === null ? "dense-v1" : "sparse-v2"
18327
- ),
18328
- ...deferredVideoFrames
18329
- ],
18330
- resolveRenderFpsConfig(job.config.fps).value
18331
- ) : null;
18332
- const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
18333
18483
  const fileServer = await createFileServer({
18334
18484
  projectDir: compiledDir,
18335
18485
  compiledDir,
@@ -18345,8 +18495,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18345
18495
  videoCount: planVideos?.videos.length ?? 0,
18346
18496
  html: readFileSync14(join41(compiledDir, "index.html"), "utf-8"),
18347
18497
  // The plan ships a deferred source only to chunks that show it; don't wait on the rest.
18348
- skipReadinessVideoIds: deferredVideoFrames.filter((video) => video.framePaths.size === 0 || remoteVideoIds.has(video.videoId)).map((video) => video.videoId),
18349
- 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
+ )
18350
18504
  });
18351
18505
  const chunkWorkerCount = calculateOptimalWorkers(framesInChunk, void 0, cfg);
18352
18506
  let session = null;
@@ -18359,6 +18513,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18359
18513
  const capturePerfs = [];
18360
18514
  const captureAttempts = [];
18361
18515
  let forceScreenshotForChunk = encoder.forceScreenshot;
18516
+ let shownBoxes;
18362
18517
  try {
18363
18518
  if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
18364
18519
  const bootStarted = Date.now();
@@ -18369,6 +18524,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18369
18524
  cfg
18370
18525
  );
18371
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
+ );
18372
18532
  const browserSelectedScreenshot = session.launchCaptureMode === "screenshot";
18373
18533
  const beginFrameStalled = !browserSelectedScreenshot && await beginFrameSessionNeedsScreenshotFallback(session);
18374
18534
  if (browserSelectedScreenshot) {
@@ -18410,6 +18570,29 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18410
18570
  session = null;
18411
18571
  }
18412
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);
18413
18596
  const captureStarted = Date.now();
18414
18597
  captureMode = await runCaptureWithScreenshotFallback({
18415
18598
  forceScreenshot: forceScreenshotForChunk,
@@ -18600,6 +18783,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18600
18783
  encodeStageMs,
18601
18784
  workers: chunkWorkerCount,
18602
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 ?? []),
18603
18788
  sha256,
18604
18789
  outputKind,
18605
18790
  producerVersion: plan2.producerVersion,
@@ -18669,9 +18854,10 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
18669
18854
  }
18670
18855
 
18671
18856
  // ../producer/src/services/distributed/projectHash.ts
18672
- 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";
18673
18858
  import { createHash as createHash7 } from "crypto";
18674
18859
  import { join as join43 } from "path";
18860
+ var READ_CHUNK_BYTES = 256 * 1024;
18675
18861
 
18676
18862
  export {
18677
18863
  createConsoleLogger,