hyperframes 0.8.192 → 0.8.193

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 (168) hide show
  1. package/dist/{add-TZU2UW7A.js → add-JC6DKZKC.js} +11 -11
  2. package/dist/{animatedGifPrep-NLAXC3VV.js → animatedGifPrep-S6KXJGEL.js} +5 -5
  3. package/dist/api.js +13 -13
  4. package/dist/{asset-JAJXA6LB.js → asset-XITFIFE6.js} +2 -2
  5. package/dist/{assetCataloger-4WTX3J4I.js → assetCataloger-K5FNKG35.js} +5 -5
  6. package/dist/{audio-XLWC7JKS.js → audio-364UHTY4.js} +11 -11
  7. package/dist/{auth-5K6X3NPJ.js → auth-5JEESRE6.js} +1 -1
  8. package/dist/{autoUpdate-RNV7SKGB.js → autoUpdate-RVVYEHAA.js} +3 -3
  9. package/dist/{backgroundChecks-QXLVZVGX.js → backgroundChecks-DCBIYWIQ.js} +3 -3
  10. package/dist/backgroundChecksWorker.js +3 -3
  11. package/dist/{batchRender-THKSPDZR.js → batchRender-ZZ4QMBAA.js} +3 -3
  12. package/dist/{beats-K7CGCFVL.js → beats-YEL65M6G.js} +7 -7
  13. package/dist/{benchmark-PHRDIXNL.js → benchmark-RBTRPA76.js} +8 -8
  14. package/dist/{browser-GN6R5ZSK.js → browser-BVAYXT4R.js} +3 -3
  15. package/dist/{bundleWithLocalizedFonts-C325UNR4.js → bundleWithLocalizedFonts-JLB2OYMT.js} +2 -2
  16. package/dist/{capture-6UBQFHZQ.js → capture-GBXTMYLZ.js} +9 -9
  17. package/dist/{capture-BZ7APOGQ.js → capture-NIFX6IRS.js} +2 -2
  18. package/dist/{captureCompositionFrame-VORSCTWC.js → captureCompositionFrame-ATNNKUL5.js} +10 -10
  19. package/dist/{catalog-PHU2RUZ3.js → catalog-AGIF5SM5.js} +11 -11
  20. package/dist/{catch-up-HCNRM46J.js → catch-up-SZ3ULDTI.js} +4 -4
  21. package/dist/{check-TJ5ZLB4I.js → check-YLQS3TF4.js} +33 -33
  22. package/dist/{checkBrowser-DWP7ZXJ7.js → checkBrowser-QH4FMCBO.js} +28 -28
  23. package/dist/{chunk-7LLRMYP6.js → chunk-2LSCYS2H.js} +2 -2
  24. package/dist/{chunk-KZOZN4PI.js → chunk-2NERNYCJ.js} +1 -1
  25. package/dist/{chunk-RHYOWDI6.js → chunk-2SFDWMSW.js} +2 -2
  26. package/dist/{chunk-AEXVVABM.js → chunk-3W4B6G7Y.js} +1 -1
  27. package/dist/{chunk-E5V6WEMK.js → chunk-4LXZJIVY.js} +9 -9
  28. package/dist/{chunk-YWRQHRDY.js → chunk-4PPWAQYV.js} +7 -7
  29. package/dist/{chunk-WOKVXK4J.js → chunk-4TKJWWPL.js} +1 -1
  30. package/dist/{chunk-NXMYHJAL.js → chunk-4VYPICIA.js} +29 -1
  31. package/dist/{chunk-6WNLQI63.js → chunk-5J6TWLFG.js} +3 -3
  32. package/dist/{chunk-ULTKB5FC.js → chunk-6DLCOQR4.js} +461 -427
  33. package/dist/{chunk-CXRZBWSH.js → chunk-6LWUPIKD.js} +9 -9
  34. package/dist/{chunk-5IPKQHOL.js → chunk-6XUJVEY2.js} +1 -1
  35. package/dist/{chunk-MGBUWPXX.js → chunk-7HTH5PFK.js} +1 -1
  36. package/dist/{chunk-32NB4VMH.js → chunk-7QNIXBQD.js} +1 -1
  37. package/dist/{chunk-AR46ND2Q.js → chunk-7YKJF5JV.js} +1 -1
  38. package/dist/{chunk-7RSY7SZN.js → chunk-7YSFE4SZ.js} +1 -1
  39. package/dist/{chunk-IMSI7IFW.js → chunk-AR3DQYRR.js} +17 -17
  40. package/dist/{chunk-XX2PA4L3.js → chunk-BJAS2JLW.js} +1 -1
  41. package/dist/{chunk-J2ASTCU6.js → chunk-BP4EMEGH.js} +1 -1
  42. package/dist/{chunk-4HCJOFMB.js → chunk-C4Y3ZREE.js} +20 -1
  43. package/dist/{chunk-7UZC4N62.js → chunk-CGTZOY53.js} +1 -1
  44. package/dist/{chunk-OQL4AJ4X.js → chunk-D625D4TV.js} +4 -4
  45. package/dist/{chunk-R2U2G4OS.js → chunk-DSQ72G5I.js} +1 -1
  46. package/dist/{chunk-VRAXIQ6I.js → chunk-DYNHVCBF.js} +1 -1
  47. package/dist/{chunk-SBBQFPCJ.js → chunk-E77ALFLW.js} +1 -1
  48. package/dist/{chunk-5TRFLZ5Z.js → chunk-EFXMETRK.js} +1 -1
  49. package/dist/{chunk-MEE4NTPZ.js → chunk-F5LLORBP.js} +2 -2
  50. package/dist/{chunk-A4OI5ZNX.js → chunk-GKHMSSGY.js} +1 -1
  51. package/dist/{chunk-AMZJFCOX.js → chunk-GR5DQKDR.js} +5 -5
  52. package/dist/{chunk-W24SZ5FL.js → chunk-KU3UFQN7.js} +5 -8
  53. package/dist/{chunk-QX4TB4NH.js → chunk-L4JB7LI4.js} +320 -75
  54. package/dist/{chunk-K2AKVYNR.js → chunk-L5YLA54L.js} +2 -2
  55. package/dist/{chunk-3FV5BVWS.js → chunk-LFUVTS6V.js} +2 -2
  56. package/dist/{chunk-4E75LHAL.js → chunk-LMNZLZMJ.js} +1 -1
  57. package/dist/{chunk-LOKCBZYF.js → chunk-M3KZBDPB.js} +3 -3
  58. package/dist/{chunk-VOM2HLUI.js → chunk-MFUNDJEV.js} +3 -3
  59. package/dist/{chunk-7JJMT6LN.js → chunk-OFV3WSOE.js} +1 -1
  60. package/dist/{chunk-L2YGSAOL.js → chunk-OFYW4J7Y.js} +2 -2
  61. package/dist/{chunk-T6O5OLN2.js → chunk-QKX7ITER.js} +2 -2
  62. package/dist/{chunk-BV6YE455.js → chunk-RTLPWOO6.js} +1 -1
  63. package/dist/{chunk-BPKLUHFT.js → chunk-SOY2FPOR.js} +5 -5
  64. package/dist/{chunk-YMQQXPD5.js → chunk-TY3LDJA3.js} +5 -12
  65. package/dist/{chunk-2HP5GIQ3.js → chunk-TZEKVLPV.js} +1 -1
  66. package/dist/{chunk-QWLES6YV.js → chunk-UB7BIDBS.js} +3 -3
  67. package/dist/{chunk-JCDYDF54.js → chunk-UIC7B5KW.js} +1 -1
  68. package/dist/{chunk-2J3NA3OB.js → chunk-UNATIAOT.js} +7 -7
  69. package/dist/chunk-USWPZXEA.js +3897 -0
  70. package/dist/{chunk-ZEMDV6W2.js → chunk-V7ZA2BNW.js} +1 -1
  71. package/dist/{chunk-PFRG5P32.js → chunk-VLLFORE2.js} +1 -1
  72. package/dist/{chunk-EKGGSAWR.js → chunk-VPC4XIVI.js} +5 -5
  73. package/dist/{chunk-6XVKMBUA.js → chunk-VW63MHQU.js} +1 -1
  74. package/dist/{chunk-MVIJTXN5.js → chunk-WFS6S65A.js} +12 -12
  75. package/dist/{chunk-VNPADTDD.js → chunk-WIVOXGEP.js} +2 -2
  76. package/dist/{chunk-J2BSUF6X.js → chunk-X5CECNCK.js} +1 -1
  77. package/dist/{chunk-SK7TR7BL.js → chunk-XQPA4W3M.js} +2 -2
  78. package/dist/{chunk-BEOK6F26.js → chunk-YS4JUGNI.js} +2 -2
  79. package/dist/{chunk-ODBYZIOH.js → chunk-ZH7J2ZUL.js} +12 -12
  80. package/dist/{chunk-XF77MK3J.js → chunk-ZT45TCY3.js} +1 -1
  81. package/dist/{chunk-3CXIX6XS.js → chunk-ZTKFRXJB.js} +3 -3
  82. package/dist/{chunk-GANST5K3.js → chunk-ZZPBXRYQ.js} +1 -1
  83. package/dist/{clean-2RNCTDBT.js → clean-5HGESK5A.js} +14 -14
  84. package/dist/cli.js +54 -54
  85. package/dist/{cloud-NFIKESNZ.js → cloud-746PHS32.js} +4 -4
  86. package/dist/{cloudrun-7224BCX3.js → cloudrun-DO7WFLSM.js} +4 -4
  87. package/dist/{compare-HL44MXVX.js → compare-66YPRNG2.js} +26 -26
  88. package/dist/{compiler-WF5QTXV7.js → compiler-R4UYDWXO.js} +2 -2
  89. package/dist/{component-ZBZXSJBR.js → component-EQ3H6RFP.js} +3 -3
  90. package/dist/{compositions-76P7LDDX.js → compositions-ZZ7P5HT2.js} +9 -9
  91. package/dist/{contactSheet-43ISAESM.js → contactSheet-SZBJDOMT.js} +1 -1
  92. package/dist/{delete-4YZBYOHJ.js → delete-Z5MSLJSR.js} +2 -2
  93. package/dist/{deploy-UGUUGYLF.js → deploy-RGSAC2K5.js} +1 -1
  94. package/dist/{dist-5KKCRFPT.js → dist-RKK4YLAD.js} +2 -2
  95. package/dist/{doctor-GGVUK4NB.js → doctor-W4ZZDRTU.js} +9 -9
  96. package/dist/{events-OXPTHVDB.js → events-BSSL4A4W.js} +2 -2
  97. package/dist/{events-V2VU4J3B.js → events-UACT7PX3.js} +3 -3
  98. package/dist/{feedback-HFZ5SCUK.js → feedback-47ILDLQG.js} +6 -6
  99. package/dist/{ffmpeg-3VHY7GO3.js → ffmpeg-IEHSUNUT.js} +5 -5
  100. package/dist/{figma-7SEZTLBJ.js → figma-PLFIIYRU.js} +3 -3
  101. package/dist/fontLocalizeCli.js +7 -7
  102. package/dist/{get-Y2S2PXCM.js → get-PDWGJYNL.js} +2 -2
  103. package/dist/{gpuPolicy-UHPOWRV5.js → gpuPolicy-6UBXO6ME.js} +5 -5
  104. package/dist/{grade-compare-4VTXSQM5.js → grade-compare-5VWLQGM7.js} +26 -26
  105. package/dist/{help-HXNOACHI.js → help-DS3KMWYO.js} +1 -1
  106. package/dist/{history-XDJUI4LC.js → history-OC326NVZ.js} +9 -8
  107. package/dist/hyperframe.manifest.json +1 -1
  108. package/dist/hyperframe.runtime.iife.js +51 -51
  109. package/dist/hyperframes-player.global.js +1 -1
  110. package/dist/{info-KYRI3EZV.js → info-AUIFV6MX.js} +7 -7
  111. package/dist/{init-IL7EWMT5.js → init-MRTWWDFW.js} +25 -25
  112. package/dist/{inspect-TBNH23UC.js → inspect-55NOAXI2.js} +26 -26
  113. package/dist/{installer-O2YMFSI4.js → installer-YSFFGBBV.js} +4 -4
  114. package/dist/{keyframes-4GF62KAJ.js → keyframes-OO2BX2IB.js} +6 -6
  115. package/dist/{lambda-JFL6M4XZ.js → lambda-CXXY6QT3.js} +6 -6
  116. package/dist/{layout-7MAEFR65.js → layout-57GJXKDK.js} +26 -26
  117. package/dist/{lint-4AYHKYAM.js → lint-J4FK3Z6G.js} +6 -6
  118. package/dist/{list-3DP3D3MW.js → list-VDQUJ76B.js} +2 -2
  119. package/dist/{login-T54WQYRQ.js → login-TJSEALWW.js} +4 -4
  120. package/dist/{manager-QROK2IH3.js → manager-4PLYEEWP.js} +6 -6
  121. package/dist/{media-treatment-4CXF7MSG.js → media-treatment-GXP4PMJH.js} +11 -11
  122. package/dist/{models-IS3I6JKU.js → models-HQQQKEEP.js} +9 -9
  123. package/dist/{motionShot-DVYJZOTQ.js → motionShot-2BT5D7DC.js} +13 -13
  124. package/dist/{normalize-audio-4527JIK4.js → normalize-audio-SOLFMHNP.js} +4 -4
  125. package/dist/{open-5JE46SAX.js → open-KHEUODFQ.js} +11 -11
  126. package/dist/{parakeetRunner-7JIJGJJ7.js → parakeetRunner-BHU5NCAX.js} +10 -10
  127. package/dist/{pipeline-YEN3WFD2.js → pipeline-WFV5HDTD.js} +6 -6
  128. package/dist/{play-WFX67I7M.js → play-GB34CANJ.js} +17 -17
  129. package/dist/{preflight-D377HVUZ.js → preflight-REF3VSJ7.js} +6 -6
  130. package/dist/{present-XTSEKAC7.js → present-SIUTA7TD.js} +13 -13
  131. package/dist/{preview-5XH7EK6O.js → preview-T2GE7Z4A.js} +16 -16
  132. package/dist/{producer-TFFYF4ZM.js → producer-23KVQZAB.js} +1 -1
  133. package/dist/{projectConfig-6YUJHMCL.js → projectConfig-DBSNBPNP.js} +6 -6
  134. package/dist/{publication-JK33Y55E.js → publication-IXPUKYWJ.js} +2 -2
  135. package/dist/{publish-JHDGXWXU.js → publish-3BA5VHXJ.js} +7 -7
  136. package/dist/{remove-background-P44MYAPJ.js → remove-background-RMSCJVVQ.js} +7 -7
  137. package/dist/{render-RJ3I4E3A.js → render-6RNE2MLU.js} +12 -12
  138. package/dist/{render-RBINWWP5.js → render-XGOZMMWX.js} +27 -27
  139. package/dist/{render-HJ34EGMN.js → render-Y6CTQ3YW.js} +5 -5
  140. package/dist/{render-batch-3D6XQYUC.js → render-batch-VUR6N6EY.js} +5 -5
  141. package/dist/{resolver-HCLTFZUW.js → resolver-GJCUH57W.js} +4 -4
  142. package/dist/{sherpa-IPOUJM6Q.js → sherpa-VVAKRCPL.js} +9 -9
  143. package/dist/skills/hyperframes/references/format.md +1 -0
  144. package/dist/{skills-P67QEERC.js → skills-ZHBURVQG.js} +5 -5
  145. package/dist/{skillsUpdateCheck-IFMLRVIF.js → skillsUpdateCheck-VMDAL2O2.js} +3 -3
  146. package/dist/{snapshot-4YM4NNWJ.js → snapshot-KEOG56SL.js} +46 -41
  147. package/dist/{src-SSV66L3F.js → src-BH5NICGQ.js} +14 -4
  148. package/dist/{src-DCD3YMXX.js → src-TZLGFRRX.js} +6 -6
  149. package/dist/{staticProjectServer-C7PPGLEQ.js → staticProjectServer-BKXL5CXB.js} +14 -14
  150. package/dist/studio/assets/{hyperframes-player-ChN3cOr0.js → hyperframes-player-BMcUdmDb.js} +1 -1
  151. package/dist/studio/assets/{index-CHu83hHM.js → index-Ca-mgTnS.js} +4 -4
  152. package/dist/studio/assets/{index-BYbFcRR0.js → index-Ci6GuloR.js} +1 -1
  153. package/dist/studio/index.html +1 -1
  154. package/dist/{studio-api-6YZ7PIQR.js → studio-api-A5U6QAMC.js} +9 -7
  155. package/dist/{studioServer-EWHSE4GC.js → studioServer-5EC5CP4W.js} +43 -43
  156. package/dist/{telemetry-GGLBHZRZ.js → telemetry-BCKJXDSW.js} +3 -3
  157. package/dist/{timeline-NKHQJXJ2.js → timeline-AZWAXTAY.js} +15 -15
  158. package/dist/{tokens-FWNNDJ6R.js → tokens-X7NC3V7C.js} +2 -2
  159. package/dist/{transcribe-VD2TMMC2.js → transcribe-7NCY7QZ2.js} +8 -8
  160. package/dist/{transcribe-TRPKXZ7L.js → transcribe-SH26H755.js} +14 -14
  161. package/dist/{updateCheck-CUPS6LU3.js → updateCheck-Y7FUWQM4.js} +2 -2
  162. package/dist/{upgrade-23XO6MEX.js → upgrade-ZHGEVL65.js} +2 -2
  163. package/dist/{urlDownloader-YOAOONCK.js → urlDownloader-FPILIT7M.js} +4 -4
  164. package/dist/{validate-BWIW24G5.js → validate-KQOZR4F6.js} +20 -20
  165. package/dist/{video-XRXJS4GC.js → video-N2QAETFB.js} +5 -5
  166. package/dist/warmCheck.js +33 -33
  167. package/package.json +1 -1
  168. package/dist/chunk-B2XLXD7V.js +0 -3897
@@ -8,7 +8,7 @@ import {
8
8
  EncoderInterruptedError,
9
9
  encoderFailureError,
10
10
  prepareAnimatedGifInputs
11
- } from "./chunk-YMQQXPD5.js";
11
+ } from "./chunk-TY3LDJA3.js";
12
12
  import {
13
13
  GENERIC_FAMILIES,
14
14
  collectFontFamilyCustomProperties,
@@ -74,6 +74,7 @@ import {
74
74
  createFrameReorderBuffer,
75
75
  createVideoFrameInjector,
76
76
  declareContentAddressedUrl,
77
+ declareProbeFactsUrl,
77
78
  decodePng,
78
79
  decodePngToRgb48le,
79
80
  deriveBeginFrameProbeTimeTicks,
@@ -137,6 +138,7 @@ import {
137
138
  readWebGlVendorInfoFromCanvas,
138
139
  recaptureDrawElementFrameForVerify,
139
140
  removeDomLayerMask,
141
+ renameWithRetry,
140
142
  resampleRgb48leObjectFit,
141
143
  resolveBrowserGpuMode,
142
144
  resolveConfig,
@@ -157,7 +159,7 @@ import {
157
159
  validateEngineConfigSnapshot,
158
160
  verifyDiskDrawElementSamples,
159
161
  writeCapturedFrame
160
- } from "./chunk-QX4TB4NH.js";
162
+ } from "./chunk-L4JB7LI4.js";
161
163
  import {
162
164
  DISTRIBUTED_FPS,
163
165
  HF_VFX_ATTR,
@@ -172,7 +174,7 @@ import {
172
174
  parseVfxChain,
173
175
  resolveAuthoredTimingWindow,
174
176
  toFps
175
- } from "./chunk-MGBUWPXX.js";
177
+ } from "./chunk-7HTH5PFK.js";
176
178
  import {
177
179
  assignBundledRuntimeCompositionIds,
178
180
  buildVariablesByCompScript,
@@ -185,7 +187,7 @@ import {
185
187
  isTemplateEntry,
186
188
  parsesAsScript,
187
189
  prepareFlattenedInnerRoot
188
- } from "./chunk-AEXVVABM.js";
190
+ } from "./chunk-3W4B6G7Y.js";
189
191
  import {
190
192
  COMPOSITION_SOURCE_URL,
191
193
  HF_COLOR_GRADING_ATTR,
@@ -213,7 +215,7 @@ import {
213
215
  shouldClampResolvedMediaDuration,
214
216
  sourceTimeAt,
215
217
  styleElementsFor
216
- } from "./chunk-B2XLXD7V.js";
218
+ } from "./chunk-USWPZXEA.js";
217
219
  import {
218
220
  MAX_AUDIO_GAIN
219
221
  } from "./chunk-X7HBSTCZ.js";
@@ -1570,16 +1572,7 @@ var RenderExecutionContext = class {
1570
1572
  };
1571
1573
 
1572
1574
  // ../producer/src/services/render/artifactTransaction.ts
1573
- import {
1574
- closeSync as closeSync2,
1575
- existsSync as existsSync5,
1576
- fstatSync as fstatSync2,
1577
- openSync as openSync2,
1578
- readSync,
1579
- readdirSync as readdirSync2,
1580
- renameSync,
1581
- rmSync as rmSync3
1582
- } from "fs";
1575
+ import { closeSync as closeSync2, existsSync as existsSync5, fstatSync as fstatSync2, openSync as openSync2, readSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "fs";
1583
1576
  import { basename as basename3, dirname as dirname4, extname as extname2, join as join5, resolve as resolve4 } from "path";
1584
1577
 
1585
1578
  // ../producer/src/services/render/renderDirOwner.ts
@@ -1692,7 +1685,7 @@ function createRenderWorkDir(prefix, outputDir) {
1692
1685
  // ../producer/src/services/render/artifactTransaction.ts
1693
1686
  var defaultFileSystem = {
1694
1687
  existsSync: existsSync5,
1695
- renameSync,
1688
+ renameSync: (source, destination) => renameWithRetry(source, destination),
1696
1689
  rmSync: rmSync3
1697
1690
  };
1698
1691
  async function defaultArtifactDurationProbe(path) {
@@ -2079,36 +2072,8 @@ function replanAfterFailure(plan2, failure) {
2079
2072
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, rmSync as rmSync5, statSync as statSync2 } from "fs";
2080
2073
  import { join as join9 } from "path";
2081
2074
 
2082
- // ../producer/src/services/render/captureStageError.ts
2083
- var CaptureStageError = class extends CaptureFailure {
2084
- browserConsole;
2085
- constructor(input) {
2086
- const classified = classifyCaptureFailure(input.cause);
2087
- super({
2088
- kind: classified.kind,
2089
- message: normalizeErrorMessage(input.cause),
2090
- cause: input.cause,
2091
- workerDiagnostics: classified.workerDiagnostics
2092
- });
2093
- this.name = "CaptureStageError";
2094
- this.browserConsole = input.browserConsole.slice();
2095
- if (input.cause instanceof Error && input.cause.stack) {
2096
- this.stack = input.cause.stack;
2097
- }
2098
- }
2099
- };
2100
- function wrapCaptureStageError(error, browserConsole) {
2101
- if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
2102
- return new CaptureStageError({ cause: error, browserConsole });
2103
- }
2104
- function getCaptureStageBrowserConsole(error) {
2105
- if (error instanceof CaptureStageError) return error.browserConsole;
2106
- return [];
2107
- }
2108
-
2109
- // ../producer/src/services/hdrCompositor.ts
2110
- import { readSync as readSync2, closeSync as closeSync3 } from "fs";
2111
- import { join as join6 } from "path";
2075
+ // ../producer/src/services/render/perfSummary.ts
2076
+ import { arch, cpus, platform, totalmem } from "os";
2112
2077
 
2113
2078
  // ../producer/src/services/render/hdrPerf.ts
2114
2079
  function createHdrPerfCollector() {
@@ -2214,7 +2179,257 @@ function finalizeHdrPerf(perf) {
2214
2179
  };
2215
2180
  }
2216
2181
 
2182
+ // ../producer/src/services/render/perfSummary.ts
2183
+ function worstSubTimelineWaitOutcome(perfs) {
2184
+ const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
2185
+ if (outcomes.length === 0) return void 0;
2186
+ if (outcomes.includes("script_failure")) return "script_failure";
2187
+ if (outcomes.includes("timeout")) return "timeout";
2188
+ return "ready";
2189
+ }
2190
+ function pushWorkerDedupPerfs(results, sink) {
2191
+ for (const r of results) {
2192
+ if (r.perf) sink.push(r.perf);
2193
+ }
2194
+ }
2195
+ function pushWorkerPageErrors(results, sink) {
2196
+ for (const result of results) {
2197
+ for (const line of result.pageErrorLines ?? []) if (!sink.includes(line)) sink.push(line);
2198
+ }
2199
+ }
2200
+ function withWorkerPageErrors(consoleLines, workerLines) {
2201
+ return [...consoleLines, ...workerLines.filter((line) => !consoleLines.includes(line))];
2202
+ }
2203
+ function roundDb(value) {
2204
+ return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
2205
+ }
2206
+ function aggregateDrawElement(perfs, de) {
2207
+ if (perfs.length === 0) return void 0;
2208
+ const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
2209
+ const gateReasons = [
2210
+ ...new Set(
2211
+ [de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
2212
+ )
2213
+ ].sort();
2214
+ const gpuRenderers = [
2215
+ ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
2216
+ ].sort();
2217
+ const drain = de.drainStats;
2218
+ return {
2219
+ mode: modes.join("|") || "unknown",
2220
+ compileGate: de.compileGate,
2221
+ clampReason: de.clampReason,
2222
+ workerInversion: de.workerInversion ?? "none",
2223
+ preInversionWorkers: de.preInversionWorkers,
2224
+ compositionElementCount: de.compositionElementCount,
2225
+ compositionElementCountSource: de.compositionElementCountSource,
2226
+ compositionElementTags: de.compositionElementTags,
2227
+ arollVideoCount: de.arollVideoCount,
2228
+ heygenVideoCount: de.heygenVideoCount,
2229
+ adaptersUsed: de.adaptersUsed,
2230
+ audioCount: de.audioCount,
2231
+ imageCount: de.imageCount,
2232
+ subCompositionCount: de.subCompositionCount,
2233
+ audioGroupCount: de.audioGroupCount,
2234
+ colorGradingCount: de.colorGradingCount,
2235
+ hasLut: de.hasLut,
2236
+ rootBodyMismatch: de.rootBodyMismatch,
2237
+ rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
2238
+ vfxHostCount: de.vfxHostCount,
2239
+ vfxCapture: de.vfxCapture,
2240
+ vfxTypes: de.vfxTypes,
2241
+ shortBand: de.shortBand,
2242
+ parallelRouter: de.parallelRouter ?? "none",
2243
+ preRouterWorkers: de.preRouterWorkers,
2244
+ gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
2245
+ gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
2246
+ workerEncode: perfs.some((p) => p.deWorkerEncode),
2247
+ verifyArmed: perfs.reduce((sum2, p) => sum2 + (p.deVerifyArmed ?? 0), 0),
2248
+ verifyChecked: drain?.verifyChecked ?? 0,
2249
+ verifyMinDb: roundDb(drain?.verifyMinDb),
2250
+ verifyInitMs: perfs.reduce((sum2, p) => sum2 + (p.deVerifyInitMs ?? 0), 0),
2251
+ selfVerifyFallback: de.selfVerifyFallback,
2252
+ fallbackReason: de.fallbackReason,
2253
+ fallbackFailedDb: roundDb(de.fallbackFailedDb),
2254
+ fallbackFrameIndex: de.fallbackFrameIndex,
2255
+ fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
2256
+ blankSuspects: drain?.blankSuspects ?? 0,
2257
+ blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
2258
+ blankRecaptures: drain?.blankRecaptures ?? 0,
2259
+ boundaryFrames: perfs.reduce((sum2, p) => sum2 + (p.deBoundaryFrames ?? 0), 0),
2260
+ ncprFallbacks: perfs.reduce((sum2, p) => sum2 + (p.deNcprFallbacks ?? 0), 0),
2261
+ frameTimeouts: perfs.reduce((sum2, p) => sum2 + (p.deFrameTimeouts ?? 0), 0)
2262
+ };
2263
+ }
2264
+ function maxDefined(values) {
2265
+ const present = values.filter((v) => typeof v === "number");
2266
+ return present.length > 0 ? Math.max(...present) : void 0;
2267
+ }
2268
+ function aggregateChromeMemory(perfs) {
2269
+ const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
2270
+ if (sampled.length === 0) return void 0;
2271
+ return {
2272
+ browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
2273
+ rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
2274
+ // Last samples are per session; summing approximates the whole fleet's
2275
+ // footprint at the end of capture (workers run concurrently).
2276
+ rssLastMb: sampled.reduce((sum2, p) => sum2 + (p.chromeRssLastMb ?? 0), 0),
2277
+ gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
2278
+ samples: sampled.reduce((sum2, p) => sum2 + (p.chromeMemorySamples ?? 0), 0)
2279
+ };
2280
+ }
2281
+ function aggregateDedup(perfs) {
2282
+ if (perfs.length === 0) return void 0;
2283
+ const armed = perfs.some((p) => p.staticDedupArmed);
2284
+ const skipReasons = armed ? [] : [
2285
+ ...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
2286
+ ].sort();
2287
+ const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
2288
+ const verificationOutcomes = [
2289
+ ...new Set(
2290
+ verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
2291
+ )
2292
+ ].sort();
2293
+ return {
2294
+ enabled: perfs.some((p) => p.staticDedupEnabled),
2295
+ armed,
2296
+ predictedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupPredicted ?? 0), 0),
2297
+ reusedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupReused ?? 0), 0),
2298
+ skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
2299
+ ...verificationPerfs.length === 0 ? {} : {
2300
+ verifiedFrames: verificationPerfs.reduce(
2301
+ (sum2, perf) => sum2 + (perf.staticDedupVerified ?? 0),
2302
+ 0
2303
+ ),
2304
+ verificationOutcomes,
2305
+ plannedRuns: verificationPerfs.reduce(
2306
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationPlannedRuns ?? 0),
2307
+ 0
2308
+ ),
2309
+ completedRuns: verificationPerfs.reduce(
2310
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationCompletedRuns ?? 0),
2311
+ 0
2312
+ ),
2313
+ screenshots: verificationPerfs.reduce(
2314
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationScreenshots ?? 0),
2315
+ 0
2316
+ ),
2317
+ seeks: verificationPerfs.reduce(
2318
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationSeeks ?? 0),
2319
+ 0
2320
+ ),
2321
+ comparisons: verificationPerfs.reduce(
2322
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationComparisons ?? 0),
2323
+ 0
2324
+ ),
2325
+ verificationElapsedMs: verificationPerfs.reduce(
2326
+ (sum2, perf) => sum2 + (perf.staticDedupVerificationElapsedMs ?? 0),
2327
+ 0
2328
+ )
2329
+ }
2330
+ };
2331
+ }
2332
+ function aggregateBeginFrameReuse(perfs) {
2333
+ const noDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameNoDamage ?? 0), 0);
2334
+ const hasDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameHasDamage ?? 0), 0);
2335
+ if (noDamageFrames + hasDamageFrames === 0) return void 0;
2336
+ return { noDamageFrames, hasDamageFrames };
2337
+ }
2338
+ function buildRenderPerfSummary(input) {
2339
+ return {
2340
+ renderId: input.job.id,
2341
+ totalElapsedMs: input.totalElapsedMs,
2342
+ // RenderPerfSummary surfaces fps as a decimal because it lands in JSON
2343
+ // payloads (CLI telemetry, regression-harness reports) where a single
2344
+ // number is friendlier than `{num,den}`. Callers needing the rational
2345
+ // back can read `job.config.fps`.
2346
+ fps: fpsToNumber(input.job.config.fps),
2347
+ quality: input.job.config.quality,
2348
+ workers: input.workerCount,
2349
+ workerSizing: input.workerSizing,
2350
+ chunkedEncode: input.enableChunkedEncode,
2351
+ chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
2352
+ compositionDurationSeconds: input.compositionDurationSeconds,
2353
+ totalFrames: input.totalFrames,
2354
+ resolution: { width: input.outputWidth, height: input.outputHeight },
2355
+ videoCount: input.videoCount,
2356
+ audioCount: input.audioCount,
2357
+ stages: input.perfStages,
2358
+ videoExtractBreakdown: input.videoExtractBreakdown,
2359
+ tmpPeakBytes: input.tmpPeakBytes,
2360
+ captureCalibration: input.captureCalibration ? {
2361
+ sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
2362
+ p95Ms: input.captureCalibration.estimate.p95Ms,
2363
+ multiplier: input.captureCalibration.estimate.multiplier,
2364
+ reasons: input.captureCalibration.estimate.reasons
2365
+ } : void 0,
2366
+ captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
2367
+ hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
2368
+ hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
2369
+ observability: input.observability,
2370
+ captureAvgMs: input.totalFrames > 0 ? Math.round(
2371
+ (input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
2372
+ ) : void 0,
2373
+ subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
2374
+ captureP50Ms: (() => {
2375
+ const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
2376
+ if (withSamples.length === 0) return void 0;
2377
+ return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
2378
+ })(),
2379
+ peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
2380
+ peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
2381
+ staticDedup: aggregateDedup(input.dedupPerfs),
2382
+ chromeMemory: aggregateChromeMemory(input.dedupPerfs),
2383
+ beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
2384
+ drawElement: aggregateDrawElement(
2385
+ input.dedupPerfs,
2386
+ input.drawElement ?? { selfVerifyFallback: false }
2387
+ ),
2388
+ host: {
2389
+ platform: platform(),
2390
+ arch: arch(),
2391
+ cpuCount: cpus().length,
2392
+ totalMemMb: Math.round(totalmem() / (1024 * 1024)),
2393
+ nodeVersion: process.version,
2394
+ gpuDisabled: input.gpuDisabled
2395
+ }
2396
+ };
2397
+ }
2398
+
2399
+ // ../producer/src/services/render/captureStageError.ts
2400
+ var CaptureStageError = class extends CaptureFailure {
2401
+ browserConsole;
2402
+ constructor(input) {
2403
+ const classified = classifyCaptureFailure(input.cause);
2404
+ super({
2405
+ kind: classified.kind,
2406
+ message: normalizeErrorMessage(input.cause),
2407
+ cause: input.cause,
2408
+ workerDiagnostics: classified.workerDiagnostics,
2409
+ pageErrorLines: classified.pageErrorLines
2410
+ });
2411
+ this.name = "CaptureStageError";
2412
+ this.browserConsole = withWorkerPageErrors(input.browserConsole, [
2413
+ ...classified.pageErrorLines
2414
+ ]);
2415
+ if (input.cause instanceof Error && input.cause.stack) {
2416
+ this.stack = input.cause.stack;
2417
+ }
2418
+ }
2419
+ };
2420
+ function wrapCaptureStageError(error, browserConsole) {
2421
+ if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
2422
+ return new CaptureStageError({ cause: error, browserConsole });
2423
+ }
2424
+ function getCaptureStageBrowserConsole(error) {
2425
+ if (error instanceof CaptureStageError) return error.browserConsole;
2426
+ if (error instanceof CaptureFailure) return [...error.pageErrorLines];
2427
+ return [];
2428
+ }
2429
+
2217
2430
  // ../producer/src/services/hdrCompositor.ts
2431
+ import { readSync as readSync2, closeSync as closeSync3 } from "fs";
2432
+ import { join as join6 } from "path";
2218
2433
  function countNonZeroAlpha(rgba) {
2219
2434
  let n = 0;
2220
2435
  for (let p = 3; p < rgba.length; p += 4) {
@@ -3249,309 +3464,100 @@ async function captureTransitionFrameOnWorker(a) {
3249
3464
  function ensureFrameWritten(frameWritten, frameIndex, encoder) {
3250
3465
  if (frameWritten) return;
3251
3466
  const reason = encoder?.getExitError();
3252
- const base = `Streaming encoder exited before frame ${frameIndex} was written`;
3253
- throw encoderFailureError(base, {
3254
- error: reason,
3255
- failureReason: encoder?.getExitFailureReason?.()
3256
- });
3257
- }
3258
- function cleanupEndedHdrVideos(args) {
3259
- if (process.env.KEEP_TEMP === "1") return;
3260
- const {
3261
- time,
3262
- activeTransition,
3263
- hdrVideoEndTimes,
3264
- cleanedUpVideos,
3265
- hdrVideoFrameSources,
3266
- sceneElements,
3267
- log
3268
- } = args;
3269
- for (const [videoId, endTime] of hdrVideoEndTimes) {
3270
- if (time > endTime && !cleanedUpVideos.has(videoId)) {
3271
- const stillNeeded = activeTransition && (sceneElements[activeTransition.fromScene]?.includes(videoId) || sceneElements[activeTransition.toScene]?.includes(videoId));
3272
- if (!stillNeeded) {
3273
- const frameSource = hdrVideoFrameSources.get(videoId);
3274
- if (frameSource) {
3275
- cleanupHdrVideoFrameSource(frameSource, log);
3276
- hdrVideoFrameSources.delete(videoId);
3277
- }
3278
- cleanedUpVideos.add(videoId);
3279
- }
3280
- }
3281
- }
3282
- }
3283
-
3284
- // ../producer/src/services/render/segmentPlan.ts
3285
- var DEFAULT_SEGMENT_FRAMES = 3e3;
3286
- var MIN_SEGMENT_FRAMES = 30;
3287
- function assertPositiveInteger(name, value) {
3288
- if (!Number.isInteger(value) || value <= 0) {
3289
- throw new Error(`[segmentPlan] ${name} must be a positive integer (got ${String(value)})`);
3290
- }
3291
- }
3292
- function planSegments(totalFrames, segmentFrames) {
3293
- assertPositiveInteger("totalFrames", totalFrames);
3294
- assertPositiveInteger("segmentFrames", segmentFrames);
3295
- const slices = [];
3296
- for (let start = 0, index = 0; start < totalFrames; start += segmentFrames, index += 1) {
3297
- slices.push({
3298
- index,
3299
- startFrame: start,
3300
- endFrame: Math.min(totalFrames, start + segmentFrames)
3301
- });
3302
- }
3303
- return slices;
3304
- }
3305
- function resolveSegmentFrames(env) {
3306
- const raw = env.HF_SEGMENT_FRAMES;
3307
- if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_FRAMES;
3308
- const parsed = Number(raw);
3309
- if (!Number.isInteger(parsed)) return DEFAULT_SEGMENT_FRAMES;
3310
- return Math.max(MIN_SEGMENT_FRAMES, parsed);
3311
- }
3312
-
3313
- // ../producer/src/services/render/segmentQueue.ts
3314
- function createSegmentQueue(slices) {
3315
- let cursor = 0;
3316
- return {
3317
- next: () => slices[cursor++],
3318
- remaining: () => Math.max(0, slices.length - cursor)
3319
- };
3320
- }
3321
-
3322
- // ../producer/src/services/render/segmentRecycle.ts
3323
- var DEFAULT_SEGMENT_BROWSER_RECYCLE = 3;
3324
- function resolveSegmentBrowserRecycle(env) {
3325
- const raw = env.HF_SEGMENT_BROWSER_RECYCLE;
3326
- if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_BROWSER_RECYCLE;
3327
- const parsed = Number(raw);
3328
- return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_SEGMENT_BROWSER_RECYCLE;
3329
- }
3330
- var TARGET_LOSS_PATTERNS = [/target closed/i, /detached frame/i, /session closed/i];
3331
- function isTargetLossError(error) {
3332
- if (!(error instanceof Error)) return false;
3333
- return TARGET_LOSS_PATTERNS.some((re) => re.test(error.message));
3334
- }
3335
-
3336
- // ../producer/src/services/render/stages/captureStreamingStage.ts
3337
- import { mkdtemp, writeFile } from "fs/promises";
3338
- import { tmpdir } from "os";
3339
- import { join as join8 } from "path";
3340
-
3341
- // ../producer/src/services/render/perfSummary.ts
3342
- import { arch, cpus, platform, totalmem } from "os";
3343
- function worstSubTimelineWaitOutcome(perfs) {
3344
- const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
3345
- if (outcomes.length === 0) return void 0;
3346
- if (outcomes.includes("script_failure")) return "script_failure";
3347
- if (outcomes.includes("timeout")) return "timeout";
3348
- return "ready";
3349
- }
3350
- function pushWorkerDedupPerfs(results, sink) {
3351
- for (const r of results) {
3352
- if (r.perf) sink.push(r.perf);
3353
- }
3354
- }
3355
- function roundDb(value) {
3356
- return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
3357
- }
3358
- function aggregateDrawElement(perfs, de) {
3359
- if (perfs.length === 0) return void 0;
3360
- const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
3361
- const gateReasons = [
3362
- ...new Set(
3363
- [de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
3364
- )
3365
- ].sort();
3366
- const gpuRenderers = [
3367
- ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
3368
- ].sort();
3369
- const drain = de.drainStats;
3370
- return {
3371
- mode: modes.join("|") || "unknown",
3372
- compileGate: de.compileGate,
3373
- clampReason: de.clampReason,
3374
- workerInversion: de.workerInversion ?? "none",
3375
- preInversionWorkers: de.preInversionWorkers,
3376
- compositionElementCount: de.compositionElementCount,
3377
- compositionElementCountSource: de.compositionElementCountSource,
3378
- compositionElementTags: de.compositionElementTags,
3379
- arollVideoCount: de.arollVideoCount,
3380
- heygenVideoCount: de.heygenVideoCount,
3381
- adaptersUsed: de.adaptersUsed,
3382
- audioCount: de.audioCount,
3383
- imageCount: de.imageCount,
3384
- subCompositionCount: de.subCompositionCount,
3385
- audioGroupCount: de.audioGroupCount,
3386
- colorGradingCount: de.colorGradingCount,
3387
- hasLut: de.hasLut,
3388
- rootBodyMismatch: de.rootBodyMismatch,
3389
- rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
3390
- vfxHostCount: de.vfxHostCount,
3391
- vfxCapture: de.vfxCapture,
3392
- vfxTypes: de.vfxTypes,
3393
- shortBand: de.shortBand,
3394
- parallelRouter: de.parallelRouter ?? "none",
3395
- preRouterWorkers: de.preRouterWorkers,
3396
- gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
3397
- gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
3398
- workerEncode: perfs.some((p) => p.deWorkerEncode),
3399
- verifyArmed: perfs.reduce((sum2, p) => sum2 + (p.deVerifyArmed ?? 0), 0),
3400
- verifyChecked: drain?.verifyChecked ?? 0,
3401
- verifyMinDb: roundDb(drain?.verifyMinDb),
3402
- verifyInitMs: perfs.reduce((sum2, p) => sum2 + (p.deVerifyInitMs ?? 0), 0),
3403
- selfVerifyFallback: de.selfVerifyFallback,
3404
- fallbackReason: de.fallbackReason,
3405
- fallbackFailedDb: roundDb(de.fallbackFailedDb),
3406
- fallbackFrameIndex: de.fallbackFrameIndex,
3407
- fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
3408
- blankSuspects: drain?.blankSuspects ?? 0,
3409
- blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
3410
- blankRecaptures: drain?.blankRecaptures ?? 0,
3411
- boundaryFrames: perfs.reduce((sum2, p) => sum2 + (p.deBoundaryFrames ?? 0), 0),
3412
- ncprFallbacks: perfs.reduce((sum2, p) => sum2 + (p.deNcprFallbacks ?? 0), 0),
3413
- frameTimeouts: perfs.reduce((sum2, p) => sum2 + (p.deFrameTimeouts ?? 0), 0)
3414
- };
3415
- }
3416
- function maxDefined(values) {
3417
- const present = values.filter((v) => typeof v === "number");
3418
- return present.length > 0 ? Math.max(...present) : void 0;
3419
- }
3420
- function aggregateChromeMemory(perfs) {
3421
- const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
3422
- if (sampled.length === 0) return void 0;
3423
- return {
3424
- browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
3425
- rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
3426
- // Last samples are per session; summing approximates the whole fleet's
3427
- // footprint at the end of capture (workers run concurrently).
3428
- rssLastMb: sampled.reduce((sum2, p) => sum2 + (p.chromeRssLastMb ?? 0), 0),
3429
- gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
3430
- samples: sampled.reduce((sum2, p) => sum2 + (p.chromeMemorySamples ?? 0), 0)
3431
- };
3432
- }
3433
- function aggregateDedup(perfs) {
3434
- if (perfs.length === 0) return void 0;
3435
- const armed = perfs.some((p) => p.staticDedupArmed);
3436
- const skipReasons = armed ? [] : [
3437
- ...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
3438
- ].sort();
3439
- const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
3440
- const verificationOutcomes = [
3441
- ...new Set(
3442
- verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
3443
- )
3444
- ].sort();
3445
- return {
3446
- enabled: perfs.some((p) => p.staticDedupEnabled),
3447
- armed,
3448
- predictedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupPredicted ?? 0), 0),
3449
- reusedFrames: perfs.reduce((sum2, p) => sum2 + (p.staticDedupReused ?? 0), 0),
3450
- skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
3451
- ...verificationPerfs.length === 0 ? {} : {
3452
- verifiedFrames: verificationPerfs.reduce(
3453
- (sum2, perf) => sum2 + (perf.staticDedupVerified ?? 0),
3454
- 0
3455
- ),
3456
- verificationOutcomes,
3457
- plannedRuns: verificationPerfs.reduce(
3458
- (sum2, perf) => sum2 + (perf.staticDedupVerificationPlannedRuns ?? 0),
3459
- 0
3460
- ),
3461
- completedRuns: verificationPerfs.reduce(
3462
- (sum2, perf) => sum2 + (perf.staticDedupVerificationCompletedRuns ?? 0),
3463
- 0
3464
- ),
3465
- screenshots: verificationPerfs.reduce(
3466
- (sum2, perf) => sum2 + (perf.staticDedupVerificationScreenshots ?? 0),
3467
- 0
3468
- ),
3469
- seeks: verificationPerfs.reduce(
3470
- (sum2, perf) => sum2 + (perf.staticDedupVerificationSeeks ?? 0),
3471
- 0
3472
- ),
3473
- comparisons: verificationPerfs.reduce(
3474
- (sum2, perf) => sum2 + (perf.staticDedupVerificationComparisons ?? 0),
3475
- 0
3476
- ),
3477
- verificationElapsedMs: verificationPerfs.reduce(
3478
- (sum2, perf) => sum2 + (perf.staticDedupVerificationElapsedMs ?? 0),
3479
- 0
3480
- )
3467
+ const base = `Streaming encoder exited before frame ${frameIndex} was written`;
3468
+ throw encoderFailureError(base, {
3469
+ error: reason,
3470
+ failureReason: encoder?.getExitFailureReason?.()
3471
+ });
3472
+ }
3473
+ function cleanupEndedHdrVideos(args) {
3474
+ if (process.env.KEEP_TEMP === "1") return;
3475
+ const {
3476
+ time,
3477
+ activeTransition,
3478
+ hdrVideoEndTimes,
3479
+ cleanedUpVideos,
3480
+ hdrVideoFrameSources,
3481
+ sceneElements,
3482
+ log
3483
+ } = args;
3484
+ for (const [videoId, endTime] of hdrVideoEndTimes) {
3485
+ if (time > endTime && !cleanedUpVideos.has(videoId)) {
3486
+ const stillNeeded = activeTransition && (sceneElements[activeTransition.fromScene]?.includes(videoId) || sceneElements[activeTransition.toScene]?.includes(videoId));
3487
+ if (!stillNeeded) {
3488
+ const frameSource = hdrVideoFrameSources.get(videoId);
3489
+ if (frameSource) {
3490
+ cleanupHdrVideoFrameSource(frameSource, log);
3491
+ hdrVideoFrameSources.delete(videoId);
3492
+ }
3493
+ cleanedUpVideos.add(videoId);
3494
+ }
3481
3495
  }
3482
- };
3496
+ }
3483
3497
  }
3484
- function aggregateBeginFrameReuse(perfs) {
3485
- const noDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameNoDamage ?? 0), 0);
3486
- const hasDamageFrames = perfs.reduce((sum2, p) => sum2 + (p.beginFrameHasDamage ?? 0), 0);
3487
- if (noDamageFrames + hasDamageFrames === 0) return void 0;
3488
- return { noDamageFrames, hasDamageFrames };
3498
+
3499
+ // ../producer/src/services/render/segmentPlan.ts
3500
+ var DEFAULT_SEGMENT_FRAMES = 3e3;
3501
+ var MIN_SEGMENT_FRAMES = 30;
3502
+ function assertPositiveInteger(name, value) {
3503
+ if (!Number.isInteger(value) || value <= 0) {
3504
+ throw new Error(`[segmentPlan] ${name} must be a positive integer (got ${String(value)})`);
3505
+ }
3489
3506
  }
3490
- function buildRenderPerfSummary(input) {
3507
+ function planSegments(totalFrames, segmentFrames) {
3508
+ assertPositiveInteger("totalFrames", totalFrames);
3509
+ assertPositiveInteger("segmentFrames", segmentFrames);
3510
+ const slices = [];
3511
+ for (let start = 0, index = 0; start < totalFrames; start += segmentFrames, index += 1) {
3512
+ slices.push({
3513
+ index,
3514
+ startFrame: start,
3515
+ endFrame: Math.min(totalFrames, start + segmentFrames)
3516
+ });
3517
+ }
3518
+ return slices;
3519
+ }
3520
+ function resolveSegmentFrames(env) {
3521
+ const raw = env.HF_SEGMENT_FRAMES;
3522
+ if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_FRAMES;
3523
+ const parsed = Number(raw);
3524
+ if (!Number.isInteger(parsed)) return DEFAULT_SEGMENT_FRAMES;
3525
+ return Math.max(MIN_SEGMENT_FRAMES, parsed);
3526
+ }
3527
+
3528
+ // ../producer/src/services/render/segmentQueue.ts
3529
+ function createSegmentQueue(slices) {
3530
+ let cursor = 0;
3491
3531
  return {
3492
- renderId: input.job.id,
3493
- totalElapsedMs: input.totalElapsedMs,
3494
- // RenderPerfSummary surfaces fps as a decimal because it lands in JSON
3495
- // payloads (CLI telemetry, regression-harness reports) where a single
3496
- // number is friendlier than `{num,den}`. Callers needing the rational
3497
- // back can read `job.config.fps`.
3498
- fps: fpsToNumber(input.job.config.fps),
3499
- quality: input.job.config.quality,
3500
- workers: input.workerCount,
3501
- workerSizing: input.workerSizing,
3502
- chunkedEncode: input.enableChunkedEncode,
3503
- chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
3504
- compositionDurationSeconds: input.compositionDurationSeconds,
3505
- totalFrames: input.totalFrames,
3506
- resolution: { width: input.outputWidth, height: input.outputHeight },
3507
- videoCount: input.videoCount,
3508
- audioCount: input.audioCount,
3509
- stages: input.perfStages,
3510
- videoExtractBreakdown: input.videoExtractBreakdown,
3511
- tmpPeakBytes: input.tmpPeakBytes,
3512
- captureCalibration: input.captureCalibration ? {
3513
- sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
3514
- p95Ms: input.captureCalibration.estimate.p95Ms,
3515
- multiplier: input.captureCalibration.estimate.multiplier,
3516
- reasons: input.captureCalibration.estimate.reasons
3517
- } : void 0,
3518
- captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
3519
- hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
3520
- hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
3521
- observability: input.observability,
3522
- captureAvgMs: input.totalFrames > 0 ? Math.round(
3523
- (input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
3524
- ) : void 0,
3525
- subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
3526
- captureP50Ms: (() => {
3527
- const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
3528
- if (withSamples.length === 0) return void 0;
3529
- return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
3530
- })(),
3531
- peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
3532
- peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
3533
- staticDedup: aggregateDedup(input.dedupPerfs),
3534
- chromeMemory: aggregateChromeMemory(input.dedupPerfs),
3535
- beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
3536
- drawElement: aggregateDrawElement(
3537
- input.dedupPerfs,
3538
- input.drawElement ?? { selfVerifyFallback: false }
3539
- ),
3540
- host: {
3541
- platform: platform(),
3542
- arch: arch(),
3543
- cpuCount: cpus().length,
3544
- totalMemMb: Math.round(totalmem() / (1024 * 1024)),
3545
- nodeVersion: process.version,
3546
- gpuDisabled: input.gpuDisabled
3547
- }
3532
+ next: () => slices[cursor++],
3533
+ remaining: () => Math.max(0, slices.length - cursor)
3548
3534
  };
3549
3535
  }
3550
3536
 
3537
+ // ../producer/src/services/render/segmentRecycle.ts
3538
+ var DEFAULT_SEGMENT_BROWSER_RECYCLE = 3;
3539
+ function resolveSegmentBrowserRecycle(env) {
3540
+ const raw = env.HF_SEGMENT_BROWSER_RECYCLE;
3541
+ if (raw === void 0 || raw.trim() === "") return DEFAULT_SEGMENT_BROWSER_RECYCLE;
3542
+ const parsed = Number(raw);
3543
+ return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_SEGMENT_BROWSER_RECYCLE;
3544
+ }
3545
+ var TARGET_LOSS_PATTERNS = [/target closed/i, /detached frame/i, /session closed/i];
3546
+ function isTargetLossError(error) {
3547
+ if (!(error instanceof Error)) return false;
3548
+ return TARGET_LOSS_PATTERNS.some((re) => re.test(error.message));
3549
+ }
3550
+
3551
3551
  // ../producer/src/services/render/stages/captureStreamingStage.ts
3552
+ import { mkdtemp, writeFile } from "fs/promises";
3553
+ import { tmpdir } from "os";
3554
+ import { join as join8 } from "path";
3552
3555
  var DEFAULT_CAPTURE_STALL_MS = 6e4;
3553
3556
  var DE_STALL_POLL_MS = 5e3;
3554
3557
  var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
3558
+ function poolFailure(error, probeSession) {
3559
+ return wrapCaptureStageError(error, probeSession?.browserConsoleBuffer ?? []);
3560
+ }
3555
3561
  var ParallelCaptureStallError = class extends Error {
3556
3562
  variant;
3557
3563
  constructor(args) {
@@ -3998,7 +4004,7 @@ async function runCaptureStreamingStage(input) {
3998
4004
  }
3999
4005
  );
4000
4006
  } catch (err) {
4001
- if (parallelDrainError) throw parallelDrainError;
4007
+ if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
4002
4008
  if (stalled && abortSignal?.aborted !== true) {
4003
4009
  throw new ParallelCaptureStallError({
4004
4010
  variant: "pool",
@@ -4009,13 +4015,15 @@ async function runCaptureStreamingStage(input) {
4009
4015
  phaseSummary: phaseSummary()
4010
4016
  });
4011
4017
  }
4012
- throw err;
4018
+ throw poolFailure(err, probeSession);
4013
4019
  } finally {
4014
4020
  clearInterval(stallTimer);
4015
4021
  abortSignal?.removeEventListener("abort", forwardParentAbort);
4016
4022
  }
4017
- if (parallelDrainError) throw parallelDrainError;
4023
+ if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
4018
4024
  pushWorkerDedupPerfs(workerResults, dedupPerfs);
4025
+ const workerPageErrors = [];
4026
+ pushWorkerPageErrors(workerResults, workerPageErrors);
4019
4027
  if (parallelGuardRan) {
4020
4028
  deDrainStats = parallelStats;
4021
4029
  }
@@ -4025,6 +4033,7 @@ async function runCaptureStreamingStage(input) {
4025
4033
  await closeCaptureSession(probeSession);
4026
4034
  probeSession = null;
4027
4035
  }
4036
+ lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
4028
4037
  } else {
4029
4038
  const videoInjector = createRenderVideoFrameInjector();
4030
4039
  const session = probeSession ?? await createCaptureSession(
@@ -4471,7 +4480,7 @@ async function runCaptureSegmentedStage(input) {
4471
4480
 
4472
4481
  // ../producer/src/services/render/segmentManifest.ts
4473
4482
  import { createHash as createHash2 } from "crypto";
4474
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
4483
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
4475
4484
  import { join as join10 } from "path";
4476
4485
  var MANIFEST_FILENAME = "segments.json";
4477
4486
  function computeSegmentPlanHash(input) {
@@ -4528,7 +4537,7 @@ function writeSegmentManifest(segmentDir, manifest) {
4528
4537
  mkdirSync4(segmentDir, { recursive: true });
4529
4538
  const tmp = join10(segmentDir, `${MANIFEST_FILENAME}.${process.pid}.tmp`);
4530
4539
  writeFileSync3(tmp, JSON.stringify(manifest, null, 2), "utf-8");
4531
- renameSync2(tmp, join10(segmentDir, MANIFEST_FILENAME));
4540
+ renameWithRetry(tmp, join10(segmentDir, MANIFEST_FILENAME));
4532
4541
  }
4533
4542
  async function probeSegmentFrameCount(path, probe) {
4534
4543
  try {
@@ -8401,7 +8410,7 @@ import { rmSync as rmSync7 } from "fs";
8401
8410
 
8402
8411
  // ../producer/src/services/render/audioPadTrim.ts
8403
8412
  import { spawn } from "child_process";
8404
- import { mkdtempSync as mkdtempSync3, renameSync as renameSync3, rmSync as rmSync6 } from "fs";
8413
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync6 } from "fs";
8405
8414
  import { dirname as dirname6, join as join16 } from "path";
8406
8415
 
8407
8416
  // ../producer/src/services/render/audioTruePeak.ts
@@ -8648,7 +8657,7 @@ async function enforceAacTruePeak(input) {
8648
8657
  }
8649
8658
  if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
8650
8659
  if (measuredPath !== correctedPath) return { success: true };
8651
- renameSync3(correctedPath, input.audioPath);
8660
+ renameWithRetry(correctedPath, input.audioPath);
8652
8661
  return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
8653
8662
  }
8654
8663
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
@@ -8838,6 +8847,7 @@ async function runAudioStage(input) {
8838
8847
  const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
8839
8848
  let audioLoweredDb;
8840
8849
  if (result.hasAudio) {
8850
+ const guardStart = Date.now();
8841
8851
  const peak = await constrainAacTruePeak({
8842
8852
  audioPath: result.audioOutputPath,
8843
8853
  sourcePath: pcmSourcePath,
@@ -8847,6 +8857,7 @@ async function runAudioStage(input) {
8847
8857
  });
8848
8858
  if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
8849
8859
  audioLoweredDb = peak.loweredDb;
8860
+ input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
8850
8861
  }
8851
8862
  return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
8852
8863
  } finally {
@@ -8872,6 +8883,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
8872
8883
  let hasAudio = false;
8873
8884
  let audioError;
8874
8885
  let audioFailures;
8886
+ let audioPhaseMs;
8875
8887
  if (audios.length > 0) {
8876
8888
  let audioResult;
8877
8889
  try {
@@ -8921,6 +8933,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
8921
8933
  assertAudioNotInterrupted(audioResult.failures);
8922
8934
  hasAudio = audioResult.success;
8923
8935
  audioFailures = audioResult.failures;
8936
+ audioPhaseMs = audioResult.phaseMs;
8924
8937
  if (!hasAudio) {
8925
8938
  audioError = audioResult.error ?? "audio mix failed for an unknown reason";
8926
8939
  for (const failure of audioResult.failures ?? []) {
@@ -8939,7 +8952,8 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
8939
8952
  audioOutputPath,
8940
8953
  hasAudio,
8941
8954
  audioError,
8942
- audioFailures
8955
+ audioFailures,
8956
+ audioPhaseMs
8943
8957
  };
8944
8958
  }
8945
8959
 
@@ -9054,6 +9068,7 @@ async function runCaptureStage(input) {
9054
9068
  let { workerCount } = plan2;
9055
9069
  const { forceScreenshot } = plan2;
9056
9070
  let lastBrowserConsole = [];
9071
+ const workerPageErrors = [];
9057
9072
  let captureBeyondViewport = probeSession?.options.captureBeyondViewport;
9058
9073
  const captureCfg = cfg.forceScreenshot === forceScreenshot ? cfg : { ...cfg, forceScreenshot };
9059
9074
  if (frameRange !== void 0) {
@@ -9091,6 +9106,7 @@ async function runCaptureStage(input) {
9091
9106
  abortSignal,
9092
9107
  frameRangeStart: frameRange?.startFrame,
9093
9108
  dedupPerfs,
9109
+ pageErrorLines: workerPageErrors,
9094
9110
  onProgress: (progress) => {
9095
9111
  if (progress.latestWorkerPhase) {
9096
9112
  if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
@@ -9111,6 +9127,11 @@ async function runCaptureStage(input) {
9111
9127
  },
9112
9128
  cfg: captureCfg,
9113
9129
  log
9130
+ }).catch((error) => {
9131
+ throw wrapCaptureStageError(
9132
+ error,
9133
+ withWorkerPageErrors(probeSession?.browserConsoleBuffer ?? [], workerPageErrors)
9134
+ );
9114
9135
  });
9115
9136
  captureAttempts.push(...attempts);
9116
9137
  const lastAttempt = attempts[attempts.length - 1];
@@ -9123,6 +9144,7 @@ async function runCaptureStage(input) {
9123
9144
  await closeCaptureSession(probeSession);
9124
9145
  probeSession = null;
9125
9146
  }
9147
+ lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
9126
9148
  } else {
9127
9149
  const sequential = await runSequentialDiskCapture(
9128
9150
  input,
@@ -10400,7 +10422,7 @@ function videoEncoderOptions(input, engineCfg) {
10400
10422
  };
10401
10423
  }
10402
10424
  function canEncodeWhileCapturing(input, workerCount, cpuCount) {
10403
- return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg" && input.lockGopForChunkConcat !== true;
10425
+ return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg";
10404
10426
  }
10405
10427
  async function readLandedFrame(workerPath, mergedPath) {
10406
10428
  if (workerPath !== void 0) {
@@ -11055,6 +11077,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
11055
11077
  options.cfg
11056
11078
  );
11057
11079
  pushWorkerDedupPerfs(workerResults, options.dedupPerfs);
11080
+ if (options.pageErrorLines) pushWorkerPageErrors(workerResults, options.pageErrorLines);
11058
11081
  } finally {
11059
11082
  await mergeWorkerFrames(attemptWorkDir, tasks, options.framesDir);
11060
11083
  }
@@ -13433,7 +13456,6 @@ import {
13433
13456
  existsSync as existsSync20,
13434
13457
  linkSync,
13435
13458
  mkdirSync as mkdirSync12,
13436
- renameSync as renameSync5,
13437
13459
  rmSync as rmSync11,
13438
13460
  writeFileSync as writeFileSync8
13439
13461
  } from "fs";
@@ -14360,14 +14382,7 @@ import { writeFileSync as writeFileSync7, writeSync } from "fs";
14360
14382
  import { join as join30 } from "path";
14361
14383
 
14362
14384
  // ../producer/src/services/distributed/mp4Stitch.ts
14363
- import {
14364
- createReadStream as createReadStream4,
14365
- createWriteStream,
14366
- mkdirSync as mkdirSync10,
14367
- mkdtempSync as mkdtempSync4,
14368
- renameSync as renameSync4,
14369
- rmSync as rmSync10
14370
- } from "fs";
14385
+ import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync10 } from "fs";
14371
14386
  import { open } from "fs/promises";
14372
14387
  import { dirname as dirname11, join as join29 } from "path";
14373
14388
  import { finished, pipeline } from "stream/promises";
@@ -14741,7 +14756,7 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
14741
14756
  for (const [i, path] of sourcePaths.entries()) await copyPayload(path, layouts[i], i, out);
14742
14757
  out.end();
14743
14758
  await finished(out);
14744
- renameSync4(join29(workDir, "out.mp4"), outputPath);
14759
+ renameWithRetry(join29(workDir, "out.mp4"), outputPath);
14745
14760
  } finally {
14746
14761
  out.destroy();
14747
14762
  rmSync10(workDir, { recursive: true, force: true });
@@ -15467,7 +15482,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
15467
15482
  const needsAlpha = outputNeedsAlpha(config.format);
15468
15483
  const remoteFiles = Object.entries(options.remoteFiles ?? {});
15469
15484
  const remoteLocalPath = (rel) => join32(projectDir, ...rel.split("/"));
15470
- for (const [, file] of remoteFiles) declareContentAddressedUrl(file.url, file.sha256);
15485
+ for (const [, file] of remoteFiles) {
15486
+ declareContentAddressedUrl(file.url, file.sha256);
15487
+ if (file.probeFactsUrl) declareProbeFactsUrl(file.url, file.probeFactsUrl);
15488
+ }
15471
15489
  const urlByAbsentLocalPath = new Map(
15472
15490
  remoteFiles.map(([rel, file]) => [remoteLocalPath(rel), file.url])
15473
15491
  );
@@ -15542,7 +15560,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
15542
15560
  if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
15543
15561
  if (probeResult.probeSession) {
15544
15562
  try {
15545
- const { closeCaptureSession: closeCaptureSession2 } = await import("./src-SSV66L3F.js");
15563
+ const { closeCaptureSession: closeCaptureSession2 } = await import("./src-BH5NICGQ.js");
15546
15564
  await closeCaptureSession2(probeResult.probeSession);
15547
15565
  } catch (err) {
15548
15566
  log.warn("[plan] probe session close failed", {
@@ -15589,7 +15607,8 @@ function mixPlanAudio(c, compiledDir) {
15589
15607
  audios: c.composition.audios,
15590
15608
  masterFxChain: c.composition.masterFxChain,
15591
15609
  abortSignal: c.abortSignal,
15592
- assertNotAborted: c.assertNotAborted
15610
+ assertNotAborted: c.assertNotAborted,
15611
+ log: c.log
15593
15612
  });
15594
15613
  }
15595
15614
  async function extractPlanVideos(c, deferRangeExtraction) {
@@ -15608,6 +15627,7 @@ async function extractPlanVideos(c, deferRangeExtraction) {
15608
15627
  if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
15609
15628
  if (extractResult.extractionResult) {
15610
15629
  assertVideoExtractionSucceeded(extractResult.extractionResult);
15630
+ c.log.info("[plan] extract phases", { ...extractResult.extractionResult.phaseBreakdown });
15611
15631
  }
15612
15632
  return extractResult;
15613
15633
  }
@@ -15653,12 +15673,12 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15653
15673
  const videoSourcesDir = join32(planDir, "video-sources");
15654
15674
  rmSync11(videoSourcesDir, { recursive: true, force: true });
15655
15675
  if (existsSync20(stagedVideoFrames)) {
15656
- renameSync5(stagedVideoFrames, videoFramesDst);
15676
+ renameWithRetry(stagedVideoFrames, videoFramesDst);
15657
15677
  } else {
15658
15678
  mkdirSync12(videoFramesDst, { recursive: true });
15659
15679
  }
15660
15680
  if (existsSync20(finalCompiledDir)) rmSync11(finalCompiledDir, { recursive: true, force: true });
15661
- renameSync5(compiledDir, finalCompiledDir);
15681
+ renameWithRetry(compiledDir, finalCompiledDir);
15662
15682
  const shipVideoSource = createVideoSourceShipper({
15663
15683
  planDir,
15664
15684
  projectDir,
@@ -15761,7 +15781,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
15761
15781
  }
15762
15782
  const planAudioPath = join32(planDir, PLAN_AUDIO_RELATIVE_PATH);
15763
15783
  if (audioResult?.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15764
- renameSync5(audioResult.audioOutputPath, planAudioPath);
15784
+ renameWithRetry(audioResult.audioOutputPath, planAudioPath);
15765
15785
  }
15766
15786
  if (chunksCarryAudio && existsSync20(planAudioPath)) {
15767
15787
  if (options.sliceChunkAudio) {
@@ -17193,7 +17213,7 @@ function startServer(options = {}) {
17193
17213
  });
17194
17214
  async function shutdown(signal) {
17195
17215
  log.info(`Received ${signal}, shutting down`);
17196
- const { drainBrowserPool } = await import("./src-SSV66L3F.js");
17216
+ const { drainBrowserPool } = await import("./src-BH5NICGQ.js");
17197
17217
  await drainBrowserPool().catch(() => {
17198
17218
  });
17199
17219
  const handle = await Promise.race([
@@ -17237,7 +17257,6 @@ import {
17237
17257
  mkdtempSync as mkdtempSync7,
17238
17258
  readFileSync as readFileSync12,
17239
17259
  readdirSync as readdirSync9,
17240
- renameSync as renameSync7,
17241
17260
  rmSync as rmSync14,
17242
17261
  statSync as statSync9,
17243
17262
  writeFileSync as writeFileSync11
@@ -17252,7 +17271,6 @@ import {
17252
17271
  linkSync as linkSync2,
17253
17272
  mkdirSync as mkdirSync14,
17254
17273
  mkdtempSync as mkdtempSync6,
17255
- renameSync as renameSync6,
17256
17274
  rmSync as rmSync13,
17257
17275
  statSync as statSync8,
17258
17276
  writeFileSync as writeFileSync10
@@ -17345,7 +17363,7 @@ var LocalPlanV2ArtifactPublisher = class {
17345
17363
  if (!canFallbackToCopy(error)) throw error;
17346
17364
  copyFileSync5(blob.sourcePath, temporaryPath);
17347
17365
  }
17348
- renameSync6(temporaryPath, destinationPath);
17366
+ renameWithRetry(temporaryPath, destinationPath);
17349
17367
  } finally {
17350
17368
  rmSync13(stagingDir, { recursive: true, force: true });
17351
17369
  }
@@ -17359,7 +17377,7 @@ var LocalPlanV2ArtifactPublisher = class {
17359
17377
  }
17360
17378
  }
17361
17379
  writeFileSync10(join37(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
17362
- renameSync6(this.temporaryDir, this.destinationDir);
17380
+ renameWithRetry(this.temporaryDir, this.destinationDir);
17363
17381
  this.#committed = true;
17364
17382
  }
17365
17383
  async abort() {
@@ -17626,7 +17644,7 @@ function writeBlob(sourcePath, destinationPath) {
17626
17644
  const temporaryPath = join38(temporaryDir, "blob");
17627
17645
  try {
17628
17646
  copyFileSync6(sourcePath, temporaryPath);
17629
- renameSync7(temporaryPath, destinationPath);
17647
+ renameWithRetry(temporaryPath, destinationPath);
17630
17648
  } finally {
17631
17649
  rmSync14(temporaryDir, { recursive: true, force: true });
17632
17650
  }
@@ -17758,7 +17776,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
17758
17776
  canonicalJsonStringify(publication.manifest),
17759
17777
  "utf-8"
17760
17778
  );
17761
- renameSync7(tempDir, planV2Dir);
17779
+ renameWithRetry(tempDir, planV2Dir);
17762
17780
  return resultFromManifest(planV2Dir, publication.manifest);
17763
17781
  } catch (error) {
17764
17782
  rmSync14(tempDir, { recursive: true, force: true });
@@ -18043,7 +18061,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
18043
18061
  }),
18044
18062
  "utf-8"
18045
18063
  );
18046
- renameSync7(tempDir, destinationDir);
18064
+ renameWithRetry(tempDir, destinationDir);
18047
18065
  } catch (error) {
18048
18066
  rmSync14(tempDir, { recursive: true, force: true });
18049
18067
  throw error;
@@ -18121,7 +18139,6 @@ import {
18121
18139
  mkdtempSync as mkdtempSync8,
18122
18140
  readFileSync as readFileSync13,
18123
18141
  readdirSync as readdirSync10,
18124
- renameSync as renameSync8,
18125
18142
  rmSync as rmSync15,
18126
18143
  statSync as statSync10,
18127
18144
  writeFileSync as writeFileSync12
@@ -18181,7 +18198,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
18181
18198
  const finalPath = join39(workDir, `output.${format}`);
18182
18199
  const audio = audioPath !== null && existsSync26(audioPath) ? audioPath : null;
18183
18200
  await writeJoined(ctx, videoSources, audio, finalPath);
18184
- renameSync8(finalPath, outputPath);
18201
+ renameWithRetry(finalPath, outputPath);
18185
18202
  log.info("[assemble] stage timings", {
18186
18203
  ...stageMs,
18187
18204
  chunks: chunkPaths.length,
@@ -18433,6 +18450,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
18433
18450
  // ../producer/src/services/distributed/renderChunk.ts
18434
18451
  import { randomBytes } from "crypto";
18435
18452
  import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync14, readdirSync as readdirSync11, rmSync as rmSync16, writeFileSync as writeFileSync13 } from "fs";
18453
+ import { cpus as cpus3 } from "os";
18436
18454
  import { join as join40 } from "path";
18437
18455
  var LEGACY_DISTRIBUTED_VP9_CPU_USED = 2;
18438
18456
  function validatePlanVideosForChunk(value) {
@@ -18721,6 +18739,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18721
18739
  let forceScreenshotForChunk = encoder.forceScreenshot;
18722
18740
  let shownBoxes;
18723
18741
  let extraction;
18742
+ const overlap = { current: null };
18724
18743
  try {
18725
18744
  if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
18726
18745
  const bootStarted = Date.now();
@@ -18808,6 +18827,59 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18808
18827
  videoFrameLookup,
18809
18828
  frameCacheOptions(cfg, chunkWorkerCount)
18810
18829
  );
18830
+ const isPngSequence = plan2.dimensions.format === "png-sequence";
18831
+ outputKind = isPngSequence ? "frame-dir" : "file";
18832
+ const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
18833
+ const basePreset = getEncoderPreset(job.config.quality, presetFormat, void 0);
18834
+ const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
18835
+ const effectiveQuality = encoder.crf ?? preset.quality;
18836
+ const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
18837
+ const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
18838
+ if (isPngSequence) {
18839
+ if (!existsSync27(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
18840
+ } else {
18841
+ const outDir = join40(outputChunkPath, "..");
18842
+ if (!existsSync27(outDir)) mkdirSync17(outDir, { recursive: true });
18843
+ }
18844
+ const encodeInput = {
18845
+ job,
18846
+ log,
18847
+ outputPath: videoOnlyPath,
18848
+ framesDir,
18849
+ videoOnlyPath,
18850
+ width: Math.round(plan2.dimensions.width * encoder.deviceScaleFactor),
18851
+ height: Math.round(plan2.dimensions.height * encoder.deviceScaleFactor),
18852
+ needsAlpha: plan2.dimensions.format !== "mp4",
18853
+ captureImageFormat: captureOptions.format ?? "jpeg",
18854
+ // The encoder writes video only; audio joins at assemble time or in the
18855
+ // chunk that carries audio. Suppressing `hasAudio` skips the png-sequence audio
18856
+ // sidecar AND the mp4 audio mux.
18857
+ hasAudio: false,
18858
+ isPngSequence,
18859
+ // `DistributedFormat` has no "gif" member — distributed chunks are
18860
+ // always video segments (gif renders in-process only).
18861
+ isGif: false,
18862
+ preset,
18863
+ effectiveQuality,
18864
+ effectiveBitrate,
18865
+ engineConfig: {
18866
+ ffmpegEncodeTimeout: cfg.ffmpegEncodeTimeout,
18867
+ vp9CpuUsed: resolveLockedVp9CpuUsed(encoder) ?? cfg.vp9CpuUsed
18868
+ },
18869
+ // Distributed chunks emit a single ffmpeg call per chunk; the
18870
+ // in-process per-chunk-within-chunk path would re-split our
18871
+ // already-chunked work.
18872
+ enableChunkedEncode: false,
18873
+ chunkedEncodeSize: framesInChunk,
18874
+ abortSignal: void 0,
18875
+ assertNotAborted: () => {
18876
+ },
18877
+ // GOP === framesInChunk + force-keyframe at frame 0 → the chunk's
18878
+ // first frame is an IDR keyframe and concat-copy at assemble time
18879
+ // round-trips losslessly.
18880
+ lockGopForChunkConcat: !isPngSequence,
18881
+ gopSize: framesInChunk
18882
+ };
18811
18883
  const captureStarted = Date.now();
18812
18884
  captureMode = await runCaptureWithScreenshotFallback({
18813
18885
  forceScreenshot: forceScreenshotForChunk,
@@ -18840,6 +18912,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18840
18912
  }
18841
18913
  const captureSession = session;
18842
18914
  session = null;
18915
+ overlap.current = canEncodeWhileCapturing(encodeInput, chunkWorkerCount, cpus3().length) ? encodeWhileCapturing(encodeInput, framesInChunk) : null;
18843
18916
  await runCaptureStage({
18844
18917
  fileServer,
18845
18918
  workDir,
@@ -18859,7 +18932,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18859
18932
  abortSignal: void 0,
18860
18933
  assertNotAborted: () => {
18861
18934
  },
18862
- frameRange: range
18935
+ frameRange: range,
18936
+ onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame)
18937
+ }).catch(async (captureError) => {
18938
+ await overlap.current?.abort();
18939
+ overlap.current = null;
18940
+ throw captureError;
18863
18941
  });
18864
18942
  return observedChunkCaptureMode(capturePerfs);
18865
18943
  },
@@ -18891,60 +18969,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18891
18969
  log
18892
18970
  );
18893
18971
  framesEncoded = framesInChunk;
18894
- const isPngSequence = plan2.dimensions.format === "png-sequence";
18895
- outputKind = isPngSequence ? "frame-dir" : "file";
18896
- const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
18897
- const basePreset = getEncoderPreset(job.config.quality, presetFormat, void 0);
18898
- const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
18899
- const effectiveQuality = encoder.crf ?? preset.quality;
18900
- const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
18901
- const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
18902
- if (isPngSequence) {
18903
- if (!existsSync27(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
18904
- } else {
18905
- const outDir = join40(outputChunkPath, "..");
18906
- if (!existsSync27(outDir)) mkdirSync17(outDir, { recursive: true });
18907
- }
18908
18972
  const encodeStarted = Date.now();
18909
- await runEncodeStage({
18910
- job,
18911
- log,
18912
- outputPath: videoOnlyPath,
18913
- framesDir,
18914
- videoOnlyPath,
18915
- width: Math.round(plan2.dimensions.width * encoder.deviceScaleFactor),
18916
- height: Math.round(plan2.dimensions.height * encoder.deviceScaleFactor),
18917
- needsAlpha: plan2.dimensions.format !== "mp4",
18918
- captureImageFormat: captureOptions.format ?? "jpeg",
18919
- // The encoder writes video only; audio joins at assemble time or in the
18920
- // chunk that carries audio. Suppressing `hasAudio` skips the png-sequence audio
18921
- // sidecar AND the mp4 audio mux.
18922
- hasAudio: false,
18923
- isPngSequence,
18924
- // `DistributedFormat` has no "gif" member — distributed chunks are
18925
- // always video segments (gif renders in-process only).
18926
- isGif: false,
18927
- preset,
18928
- effectiveQuality,
18929
- effectiveBitrate,
18930
- engineConfig: {
18931
- ffmpegEncodeTimeout: cfg.ffmpegEncodeTimeout,
18932
- vp9CpuUsed: resolveLockedVp9CpuUsed(encoder) ?? cfg.vp9CpuUsed
18933
- },
18934
- // Distributed chunks emit a single ffmpeg call per chunk; the
18935
- // in-process per-chunk-within-chunk path would re-split our
18936
- // already-chunked work.
18937
- enableChunkedEncode: false,
18938
- chunkedEncodeSize: framesInChunk,
18939
- abortSignal: void 0,
18940
- assertNotAborted: () => {
18941
- },
18942
- // GOP === framesInChunk + force-keyframe at frame 0 → the chunk's
18943
- // first frame is an IDR keyframe and concat-copy at assemble time
18944
- // round-trips losslessly.
18945
- lockGopForChunkConcat: !isPngSequence,
18946
- gopSize: framesInChunk
18947
- });
18973
+ const overlapped = overlap.current;
18974
+ overlap.current = null;
18975
+ await (overlapped ? overlapped.finish() : runEncodeStage(encodeInput));
18948
18976
  if (encoder.chunksCarryAudio) {
18949
18977
  const audioScratchPath = `${outputChunkPath}.audio.m4a`;
18950
18978
  const audioChunks = options?.frameRange ? chunksWithSubRange(chunks, chunkIndex, range) : { chunks, index: chunkIndex };
@@ -18962,6 +18990,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18962
18990
  }
18963
18991
  encodeStageMs = Date.now() - encodeStarted;
18964
18992
  } finally {
18993
+ await overlap.current?.abort().catch(() => {
18994
+ });
18965
18995
  await extraction?.catch(() => {
18966
18996
  });
18967
18997
  if (session) {
@@ -19074,6 +19104,10 @@ var READ_CHUNK_BYTES = 256 * 1024;
19074
19104
  import { extname as extname7 } from "path";
19075
19105
  var AUDIO_SIDECAR_MIN_BYTES = 32 * 1024 * 1024;
19076
19106
 
19107
+ // ../producer/src/services/distributed/probeFacts.ts
19108
+ import { writeFileSync as writeFileSync14 } from "fs";
19109
+ import { extname as extname8 } from "path";
19110
+
19077
19111
  export {
19078
19112
  createConsoleLogger,
19079
19113
  defaultLogger,