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