hyperframes 0.8.142 → 0.8.144
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-YS2RCWEK.js → add-CWRTVGDB.js} +18 -17
- package/dist/{animatedGifPrep-SAMCDT7T.js → animatedGifPrep-JTOHIWI4.js} +15 -14
- package/dist/{asset-BLWC2NWB.js → asset-KU2Z4UFM.js} +4 -4
- package/dist/{assetCataloger-FVNBBRP3.js → assetCataloger-3CS2P7PA.js} +8 -7
- package/dist/{auth-DRZB7OAC.js → auth-IDSOIQWG.js} +1 -1
- package/dist/{autoUpdate-I5I3BOJK.js → autoUpdate-373B4BA2.js} +2 -2
- package/dist/{backgroundChecks-VR2QHGNY.js → backgroundChecks-53Q3NAMG.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-4YUHQI6X.js → batchRender-UKZA6BME.js} +11 -10
- package/dist/{beats-7OT7ALV3.js → beats-N5JGEOFR.js} +33 -28
- package/dist/{benchmark-Q2XQCRTR.js → benchmark-CUN3LN4V.js} +15 -14
- package/dist/{browser-7VAWQQZC.js → browser-NCZT5AP6.js} +40 -56
- package/dist/{bundleWithLocalizedFonts-DDMLLL2R.js → bundleWithLocalizedFonts-FWC7PE2D.js} +2 -2
- package/dist/{capture-HEJS6M5A.js → capture-QBLG3VJR.js} +26 -25
- package/dist/{capture-U7ABPKS5.js → capture-YLS32UHT.js} +2 -2
- package/dist/{catalog-DVJ3XPDN.js → catalog-HIWNAFHW.js} +19 -18
- package/dist/{catch-up-DF6AVSN2.js → catch-up-HGS7KIAC.js} +5 -5
- package/dist/{check-KXORRWAE.js → check-FH6UYYLO.js} +78 -66
- package/dist/{checkBrowser-J7HZJTRH.js → checkBrowser-RNWNOMCZ.js} +93 -41
- package/dist/{chunk-R2JEKLDZ.js → chunk-2DA2BBNY.js} +3 -3
- package/dist/{chunk-TQFG7HTI.js → chunk-3RM2ETQT.js} +0 -168
- package/dist/{chunk-YFT3BL7N.js → chunk-3VYNNWVA.js} +30 -12
- package/dist/{chunk-7FCDA4RH.js → chunk-3XTEXWNN.js} +8 -5
- package/dist/{chunk-PFCFR3KZ.js → chunk-442JNXRM.js} +1 -1
- package/dist/{chunk-BPZVDBHY.js → chunk-4LAR447G.js} +1 -1
- package/dist/{chunk-AM5L5YIA.js → chunk-4OA4LDTU.js} +1 -1
- package/dist/{chunk-DZEVA7DI.js → chunk-4OC7MQNB.js} +2 -2
- package/dist/{chunk-75JJVANE.js → chunk-54QISXOC.js} +3 -3
- package/dist/{chunk-FZK35GT4.js → chunk-5HJ343GH.js} +1 -1
- package/dist/{chunk-IDX52YJN.js → chunk-5VMR4WBS.js} +458 -159
- package/dist/{chunk-OSUKETIH.js → chunk-5WADG7PR.js} +1 -1
- package/dist/{chunk-JYYOAKHS.js → chunk-6IIGT7BX.js} +1 -1
- package/dist/{chunk-6UYZB4TY.js → chunk-7RAYSCYT.js} +4 -4
- package/dist/{chunk-WTMUCJ6Z.js → chunk-7SDCIWHQ.js} +11 -2
- package/dist/{chunk-LAOCA3MQ.js → chunk-A4Q7W4LV.js} +13 -8
- package/dist/{chunk-B6SMSALD.js → chunk-A53CBD24.js} +18 -12
- package/dist/{chunk-4PFZPP2N.js → chunk-A6S35NYA.js} +54 -16
- package/dist/{chunk-JOTLPC6S.js → chunk-AUBOF2PN.js} +1 -1
- package/dist/{chunk-WCJGTLPF.js → chunk-BHVFTYSI.js} +6 -3
- package/dist/chunk-BSQ7XANM.js +210 -0
- package/dist/{chunk-LCSMOLGX.js → chunk-BYSBRF24.js} +69 -8
- package/dist/{chunk-U7E736AJ.js → chunk-DDIVWXN7.js} +32 -14
- package/dist/{chunk-KOEWEFHK.js → chunk-DIRJ322I.js} +10 -5
- package/dist/{chunk-HMUF7FEO.js → chunk-EX6C672Y.js} +2 -2
- package/dist/{chunk-QGRFOOLE.js → chunk-F2SMS24Q.js} +288 -85
- package/dist/{chunk-5Z63TBTX.js → chunk-FH7WODJH.js} +1 -1
- package/dist/{chunk-XTLBLWOV.js → chunk-GEWKFRFI.js} +1 -1
- package/dist/{chunk-RVCWIIZ7.js → chunk-GV5R5B7Q.js} +1 -1
- package/dist/{chunk-TY6XOHEM.js → chunk-GZM2EFOE.js} +3 -3
- package/dist/chunk-GZVP4TWN.js +76 -0
- package/dist/{chunk-QXPCARFE.js → chunk-I5KY7EZP.js} +1 -1
- package/dist/{chunk-IW2CKH6A.js → chunk-IEIJRMLZ.js} +94 -59
- package/dist/chunk-IR35BCLK.js +13 -0
- package/dist/{chunk-JFKL3WAE.js → chunk-IWFLMU2N.js} +104 -19
- package/dist/{chunk-EFDXGAAD.js → chunk-JEHFZQYT.js} +1 -1
- package/dist/{chunk-IHFUKJOR.js → chunk-K2FLNJVB.js} +8 -2
- package/dist/{chunk-KDPYVTEU.js → chunk-KBB4RG6F.js} +6 -1
- package/dist/{chunk-LJAU4TPS.js → chunk-KFKZ3ISO.js} +2 -2
- package/dist/{sherpa-ZYTNFHD5.js → chunk-L5TUCXWR.js} +11 -44
- package/dist/{chunk-KYN364DU.js → chunk-LC3RQURJ.js} +7 -9
- package/dist/{chunk-IMY4QQTQ.js → chunk-LFX2HEYP.js} +8 -8
- package/dist/{chunk-PXSM4HPB.js → chunk-MAYCCGMQ.js} +12 -3
- package/dist/{chunk-OZUQF4NB.js → chunk-MQZVBHEY.js} +123 -8
- package/dist/{chunk-FWRNQOLT.js → chunk-MXNDKDFT.js} +3 -3
- package/dist/{chunk-A4KQZXZI.js → chunk-NE55C6S5.js} +4 -2
- package/dist/{chunk-GYE63V4P.js → chunk-NOPBBSLA.js} +24 -19
- package/dist/{chunk-H2X52H2N.js → chunk-NV4RXIQ7.js} +3 -3
- package/dist/{chunk-WGVOWWVK.js → chunk-OIDN7G42.js} +3 -3
- package/dist/{chunk-DPHJKWP5.js → chunk-OUSLVRO4.js} +107 -108
- package/dist/{chunk-JLKMWY2T.js → chunk-P6WH6QTV.js} +8 -5
- package/dist/{chunk-7M4KAPAA.js → chunk-PUGIRFPX.js} +2 -2
- package/dist/{chunk-6RAD4BCC.js → chunk-QATWIE4K.js} +2 -2
- package/dist/{chunk-D2KNWIQ6.js → chunk-QFSCCQIR.js} +1 -1
- package/dist/{chunk-XGMNQQTJ.js → chunk-RG3QWWIE.js} +1 -1
- package/dist/{chunk-MVTUV4TP.js → chunk-RI53TS2Y.js} +14 -6
- package/dist/{chunk-LQASVU3I.js → chunk-RVFEDAUV.js} +12 -4
- package/dist/{chunk-GK4AIFIH.js → chunk-SDYDNHEE.js} +34 -3
- package/dist/{chunk-WSGY5CDQ.js → chunk-SLCFKB3Y.js} +14 -10
- package/dist/{chunk-2NCHQUPX.js → chunk-SQLK7SJL.js} +1 -1
- package/dist/{chunk-PMRBLBUP.js → chunk-T42U2W2R.js} +14 -2
- package/dist/chunk-UPFUC4DB.js +136 -0
- package/dist/{chunk-IXCTG2WY.js → chunk-VYSUZEJG.js} +5 -5
- package/dist/chunk-WJB7WI4J.js +3753 -0
- package/dist/{chunk-YEIFI443.js → chunk-WPNE5NEP.js} +1 -1
- package/dist/{chunk-YHRZQJXH.js → chunk-XGFHYAYN.js} +265 -68
- package/dist/{chunk-LBKDQKWV.js → chunk-XU5GGV6R.js} +1 -1
- package/dist/{chunk-LTGGTEMA.js → chunk-Y6UPYWJS.js} +2 -2
- package/dist/{chunk-OKCR3JTO.js → chunk-YJXHMIZA.js} +13 -10
- package/dist/{chunk-JU6Y5TTZ.js → chunk-YUAXFWSQ.js} +3 -3
- package/dist/{chunk-TVCRUG2S.js → chunk-ZEUONDQG.js} +1 -1
- package/dist/{chunk-THXFSPLV.js → chunk-ZIRIDSDB.js} +1 -1
- package/dist/{chunk-6UM3R7PP.js → chunk-ZOA2SC4P.js} +129 -60
- package/dist/{clean-KTHTV5G5.js → clean-KPX27IKJ.js} +27 -26
- package/dist/cli.js +70 -160
- package/dist/{cloud-ARSZWALC.js → cloud-P7DAOISN.js} +4 -4
- package/dist/{cloudrun-NM7RXOCL.js → cloudrun-XM56TBOH.js} +13 -12
- package/dist/commands/contrast-audit.browser.js +60 -4
- package/dist/commands/layout-audit.browser.js +68 -0
- package/dist/{compare-CY4UO3RW.js → compare-F6YAFZZM.js} +37 -36
- package/dist/{compiler-VTH2D3ID.js → compiler-JZ4GNFMG.js} +16 -10
- package/dist/{component-FRUIAXIR.js → component-3CFOGIQA.js} +5 -5
- package/dist/{compositions-CUSY6ZYY.js → compositions-CPCMWF24.js} +38 -33
- package/dist/{delete-KT3VHP4L.js → delete-GYVKOKB6.js} +4 -4
- package/dist/{deterministicFonts-F6WIVL2W.js → deterministicFonts-U2BIMGPG.js} +3 -3
- package/dist/{dist-7SZU7MA7.js → dist-KDJTFJHA.js} +7 -6
- package/dist/{dist-2GC6XKRQ.js → dist-YPWQDWJ4.js} +42 -40
- package/dist/{doctor-4ROTBL5U.js → doctor-GWI3B6JT.js} +21 -20
- package/dist/{dom-IROKGVJI.js → dom-37K6YOH4.js} +2 -2
- package/dist/{events-QJDVMKSR.js → events-BRRUDPPT.js} +3 -3
- package/dist/{events-Q5PXT5UC.js → events-V2QFN4YM.js} +2 -2
- package/dist/{feedback-PQ7TWESA.js → feedback-CXMD7FWL.js} +14 -13
- package/dist/{ffmpeg-QIUFEFTY.js → ffmpeg-T5SH2EFJ.js} +3 -2
- package/dist/{figma-JN5DDBDS.js → figma-YABDWD66.js} +3 -3
- package/dist/fontLocalizeCli.js +18 -17
- package/dist/{get-KT6OTERO.js → get-L2SOQKHX.js} +4 -4
- package/dist/{gpuPolicy-LCBWVGEH.js → gpuPolicy-NBJNWHYS.js} +19 -16
- package/dist/{grade-compare-VS3RP73G.js → grade-compare-STXQPKYQ.js} +39 -38
- package/dist/{help-AD6FIZIO.js → help-4H6BSFLB.js} +2 -2
- package/dist/{history-ZRWYRY7R.js → history-4IQOLTHJ.js} +6 -6
- package/dist/hyperframe-runtime.js +56 -56
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +56 -56
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/hyperframes-slideshow.global.js +23 -23
- package/dist/{info-WBBQA2AX.js → info-2CZOTNBC.js} +17 -16
- package/dist/{init-A2XBDFGF.js → init-6BXK42VS.js} +46 -46
- package/dist/{inspect-CLTZHA5O.js → inspect-VKU47TBH.js} +38 -37
- package/dist/{installer-WQDRVXH4.js → installer-T2BSSUDY.js} +11 -10
- package/dist/{keyframes-3FYOK6H6.js → keyframes-IEYVHRPI.js} +10 -10
- package/dist/{lambda-Y2PA2BGS.js → lambda-TIKG25CK.js} +12 -11
- package/dist/{layout-4KP5O7IJ.js → layout-XOI4PT7A.js} +38 -37
- package/dist/{lint-V4GRFHOJ.js → lint-DWCMVIX5.js} +14 -13
- package/dist/{lintProject-RG2KGAXB.js → lintProject-UBZ3TDHU.js} +7 -6
- package/dist/{list-OQ4VED6E.js → list-7GXKG5QM.js} +5 -5
- package/dist/{login-W47NKN5S.js → login-BQ5L2UHX.js} +4 -4
- package/dist/{manager-CT3D67CG.js → manager-BPSR6D3V.js} +2 -2
- package/dist/{manager-524RPYO7.js → manager-RFZNG5YT.js} +2 -2
- package/dist/{manager-DXB7JDHU.js → manager-YKMHR6LN.js} +9 -6
- package/dist/{media-treatment-HJYL3EME.js → media-treatment-IFB7OSZ3.js} +24 -22
- package/dist/{models-DYBAD2GQ.js → models-TY4PJ545.js} +68 -27
- package/dist/{motionShot-7NE2YBB3.js → motionShot-WXMFHICW.js} +33 -33
- package/dist/{normalize-IGLHCWTF.js → normalize-U3SL5VG3.js} +19 -8
- package/dist/{normalize-audio-INKCLMS2.js → normalize-audio-RFD776HQ.js} +10 -6
- package/dist/{open-TYC5THVY.js → open-7IXXGO2E.js} +5 -5
- package/dist/parakeetRunner-IHLYYMTZ.js +67 -0
- package/dist/{pipeline-EVKEPQEW.js → pipeline-TXKPLL2U.js} +20 -18
- package/dist/{play-QMZEM6X3.js → play-J2KP64UK.js} +31 -29
- package/dist/{preflight-Y5DN5D37.js → preflight-B4AFK3RG.js} +4 -4
- package/dist/{present-Y7IK3C4W.js → present-MHHNRVRA.js} +26 -25
- package/dist/{preview-5OQRYE6A.js → preview-CONUOFFQ.js} +28 -27
- package/dist/{producer-Q6G6JU7B.js → producer-ZI46K7HH.js} +1 -1
- package/dist/{projectConfig-RJN3DKDB.js → projectConfig-FGGVG5MV.js} +13 -12
- package/dist/{publication-QCPGGN3U.js → publication-TXFBMW6B.js} +8 -7
- package/dist/{publish-VZLFIGYY.js → publish-YZSPBFRD.js} +18 -17
- package/dist/{remove-background-TKOEHBA5.js → remove-background-4ATVLHLG.js} +22 -20
- package/dist/{render-KIIIF6PS.js → render-6FBYLYGJ.js} +20 -19
- package/dist/{render-XTXE5CRU.js → render-GFW5BPES.js} +13 -12
- package/dist/{render-X3NFNVEQ.js → render-MH4IUPWB.js} +41 -40
- package/dist/{render-batch-M55YZLZ5.js → render-batch-XUCZ2PUJ.js} +13 -12
- package/dist/{renderCancellation-X5BEHAZU.js → renderCancellation-XF7M7IUP.js} +18 -16
- package/dist/renderSetupWorker.js +37 -48
- package/dist/{resolver-GAMXRA42.js → resolver-W7AYCQGS.js} +10 -9
- package/dist/sherpa-LDUB5NLY.js +75 -0
- package/dist/sherpaWorker.js +2 -1
- package/dist/skills/hyperframes-cli/references/doctor-browser.md +24 -1
- package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +1 -1
- package/dist/skills/media-use/scripts/lib/cache.mjs +28 -14
- package/dist/skills/media-use/scripts/lib/cache.test.mjs +110 -0
- package/dist/skills/media-use/scripts/lib/duck.mjs +2 -1
- package/dist/skills/media-use/scripts/lib/duck.test.mjs +87 -0
- package/dist/skills/media-use/scripts/lib/probe.mjs +6 -1
- package/dist/skills/media-use/scripts/lib/probe.test.mjs +73 -0
- package/dist/skills/media-use/scripts/lib/words.mjs +1 -1
- package/dist/{skills-CZHVAD3J.js → skills-VENERS5X.js} +7 -7
- package/dist/{skillsUpdateCheck-6WLLX53A.js → skillsUpdateCheck-UXFYTACG.js} +3 -3
- package/dist/{slideshow-EJ4IMY6P.js → slideshow-4OJEPWNZ.js} +1 -1
- package/dist/{snapshot-K7BTJPYQ.js → snapshot-C6T74HTI.js} +44 -40
- package/dist/{src-YUW26JU5.js → src-FS5RSVEI.js} +17 -16
- package/dist/{src-3I3RH3V5.js → src-VUJ2FUFK.js} +18 -13
- package/dist/{staticProjectServer-IKCSGN2B.js → staticProjectServer-V2RZ4DWD.js} +23 -22
- package/dist/studio/assets/hyperframes-player-Bw_jIaSX.js +473 -0
- package/dist/studio/assets/{index-B-wffzRH.js → index-BVM9NuWl.js} +153 -153
- package/dist/studio/assets/{index-D3Vij9t1.js → index-Cj7W_c7p.js} +1 -1
- package/dist/studio/assets/{index-D5q2_7Gw.js → index-DyH9dT7b.js} +1 -1
- package/dist/studio/{chunk-TFLU2MGI.js → chunk-3GHTHPZD.js} +8 -1
- package/dist/studio/chunk-3GHTHPZD.js.map +1 -0
- package/dist/studio/{chunk-H2DUOXTF.js → chunk-G3EDKRZH.js} +3 -3
- package/dist/studio/{chunk-UYWON5AX.js → chunk-PEAKCOUW.js} +7 -3
- package/dist/studio/chunk-PEAKCOUW.js.map +1 -0
- package/dist/studio/{chunk-LOVBLSMR.js → chunk-Y2OHHNM2.js} +35 -19
- package/dist/studio/chunk-Y2OHHNM2.js.map +1 -0
- package/dist/studio/{domEditingLayers-KBTPQKGA.js → domEditingLayers-PYST4G45.js} +3 -3
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +261 -230
- package/dist/studio/index.js.map +1 -1
- package/dist/studio/player.js +3 -3
- package/dist/studio/ui.d.ts +4 -5
- package/dist/studio/ui.js +1 -1
- package/dist/{studio-api-SQ3NXFSM.js → studio-api-TQE7KWKO.js} +22 -17
- package/dist/{studioServer-VVEO7XCH.js → studioServer-CYKSQK7L.js} +81 -64
- package/dist/{synthesize-77PTU6C5.js → synthesize-UCI4X5O7.js} +6 -6
- package/dist/{telemetry-FQDKR5XA.js → telemetry-A23PPRD6.js} +3 -3
- package/dist/{timeline-N6ZASFNE.js → timeline-T3QSGT3A.js} +143 -99
- package/dist/{tokens-2OIIBBCW.js → tokens-RT5HWA6C.js} +4 -4
- package/dist/{transcribe-7YTUCXDX.js → transcribe-FOLNOC5G.js} +116 -52
- package/dist/{transcribe-4F6MK73N.js → transcribe-HCF64LQK.js} +23 -20
- package/dist/{tts-TOHSPWEG.js → tts-B6AHFQEO.js} +6 -6
- package/dist/{updateCheck-AZ2COZFR.js → updateCheck-27PIP3AA.js} +2 -2
- package/dist/{upgrade-QAJEYB4X.js → upgrade-TVGXBUPJ.js} +2 -2
- package/dist/{urlDownloader-LSMFEO3R.js → urlDownloader-KBLBJKRL.js} +14 -13
- package/dist/{validate-GW5MDL36.js → validate-ZMYGK4QI.js} +40 -27
- package/dist/{video-PRNOKUK3.js → video-AVE5GIGM.js} +15 -14
- package/package.json +1 -1
- package/dist/chunk-J4UX3RS3.js +0 -3717
- package/dist/studio/assets/hyperframes-player-sAsRuV73.js +0 -473
- package/dist/studio/chunk-LOVBLSMR.js.map +0 -1
- package/dist/studio/chunk-TFLU2MGI.js.map +0 -1
- package/dist/studio/chunk-UYWON5AX.js.map +0 -1
- package/dist/{chunk-HBBJFK6I.js → chunk-APDLWWFG.js} +0 -0
- package/dist/{screenshotCapture-TEUIWXWN.js → screenshotCapture-3JXXUZND.js} +3 -3
- /package/dist/studio/{chunk-H2DUOXTF.js.map → chunk-G3EDKRZH.js.map} +0 -0
- /package/dist/studio/{domEditingLayers-KBTPQKGA.js.map → domEditingLayers-PYST4G45.js.map} +0 -0
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
renderFrameIdForRenderId,
|
|
27
27
|
resolveAuthoredTimingWindow,
|
|
28
28
|
toFps
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-3VYNNWVA.js";
|
|
30
30
|
import {
|
|
31
31
|
AUDIO_GROUP_RENDER_ID_ATTR,
|
|
32
32
|
COLOR_GRADING_SOURCE_HIDDEN_ATTR,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
shiftRateLane,
|
|
56
56
|
sourceTimeAt,
|
|
57
57
|
timeAtSourceTime
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-WJB7WI4J.js";
|
|
59
59
|
import {
|
|
60
60
|
clampAudioGain,
|
|
61
61
|
isAudibleVideoElement
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
import {
|
|
71
71
|
isWithinProjectRoot,
|
|
72
72
|
resolveProjectRelativeSrc
|
|
73
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-IWFLMU2N.js";
|
|
74
74
|
import {
|
|
75
75
|
FFMPEG_PATH_ENV,
|
|
76
76
|
FFPROBE_PATH_ENV,
|
|
@@ -79,10 +79,10 @@ import {
|
|
|
79
79
|
import {
|
|
80
80
|
parseNumeric,
|
|
81
81
|
parseStartExpression
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-3RM2ETQT.js";
|
|
83
83
|
import {
|
|
84
84
|
parseHTML
|
|
85
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-SDYDNHEE.js";
|
|
86
86
|
|
|
87
87
|
// ../engine/src/config.ts
|
|
88
88
|
import { tmpdir } from "os";
|
|
@@ -553,9 +553,97 @@ function applyConcreteGpuScreenshotClamp(currentForceScreenshot, resolvedGpuMode
|
|
|
553
553
|
});
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
// ../engine/src/utils/sdrCaptureColor.ts
|
|
557
|
+
var SDR_RGB_TO_BT709_FILTER = "scale=out_color_matrix=bt709:out_range=tv:flags=neighbor";
|
|
558
|
+
var SDR_CAPTURE_TO_BT709_FILTER = `scale=in_color_matrix=bt601:in_range=pc:flags=neighbor,format=gbrp,${SDR_RGB_TO_BT709_FILTER}`;
|
|
559
|
+
|
|
560
|
+
// ../parsers/dist/htmlAttributeSpans.js
|
|
561
|
+
function readAttributeValue(html, at) {
|
|
562
|
+
while (/[\t\n\f\r ]/.test(html.charAt(at))) at++;
|
|
563
|
+
const quote = ['"', "'"].includes(html.charAt(at)) ? html.charAt(at) : "";
|
|
564
|
+
if (quote) at++;
|
|
565
|
+
const valueStart = at;
|
|
566
|
+
if (quote) {
|
|
567
|
+
const closing = html.indexOf(quote, at);
|
|
568
|
+
at = closing === -1 ? html.length : closing;
|
|
569
|
+
} else {
|
|
570
|
+
while (at < html.length && !/[\t\n\f\r >]/.test(html[at])) at++;
|
|
571
|
+
}
|
|
572
|
+
const valueEnd = at;
|
|
573
|
+
const end = quote ? Math.min(valueEnd + 1, html.length) : valueEnd;
|
|
574
|
+
return { value: html.slice(valueStart, valueEnd), valueStart, valueEnd, quote, end };
|
|
575
|
+
}
|
|
576
|
+
function readAttribute(html, start) {
|
|
577
|
+
let at = start;
|
|
578
|
+
while (at < html.length && !/[\t\n\f\r =/>]/.test(html[at])) at++;
|
|
579
|
+
if (at === start) return null;
|
|
580
|
+
const name = html.slice(start, at).toLowerCase();
|
|
581
|
+
const nameEnd = at;
|
|
582
|
+
while (/[\t\n\f\r ]/.test(html[at] ?? "")) at++;
|
|
583
|
+
if (html[at] !== "=") return { kind: "boolean", name, start, end: nameEnd };
|
|
584
|
+
return { kind: "value", name, start, ...readAttributeValue(html, at + 1) };
|
|
585
|
+
}
|
|
586
|
+
function readOpeningTag(html, start, name, at) {
|
|
587
|
+
const attributes = [];
|
|
588
|
+
while (at < html.length) {
|
|
589
|
+
while (/[\t\n\f\r /]/.test(html[at] ?? "")) at++;
|
|
590
|
+
if (at >= html.length || html[at] === ">") break;
|
|
591
|
+
const attribute = readAttribute(html, at);
|
|
592
|
+
if (attribute === null) {
|
|
593
|
+
at++;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
attributes.push(attribute);
|
|
597
|
+
at = attribute.end;
|
|
598
|
+
}
|
|
599
|
+
const closed = html[at] === ">";
|
|
600
|
+
return {
|
|
601
|
+
name: name.toLowerCase(),
|
|
602
|
+
start,
|
|
603
|
+
bodyEnd: at,
|
|
604
|
+
end: closed ? at + 1 : at,
|
|
605
|
+
closed,
|
|
606
|
+
rawTextEnd: null,
|
|
607
|
+
attributes
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function scanHtmlOpeningTags(html) {
|
|
611
|
+
const tags = [];
|
|
612
|
+
const opening = /<!--[\s\S]*?(?:--!?>|$)|<([a-z][^\t\n\f\r />]*)/gi;
|
|
613
|
+
let match;
|
|
614
|
+
while ((match = opening.exec(html)) !== null) {
|
|
615
|
+
if (!match[1]) continue;
|
|
616
|
+
const tag = readOpeningTag(html, match.index, match[1], opening.lastIndex);
|
|
617
|
+
tags.push(tag);
|
|
618
|
+
opening.lastIndex = tag.end;
|
|
619
|
+
if ([
|
|
620
|
+
"script",
|
|
621
|
+
"style",
|
|
622
|
+
"textarea",
|
|
623
|
+
"title",
|
|
624
|
+
"xmp",
|
|
625
|
+
"iframe",
|
|
626
|
+
"noembed",
|
|
627
|
+
"noframes",
|
|
628
|
+
"plaintext"
|
|
629
|
+
].includes(tag.name)) {
|
|
630
|
+
if (tag.name === "plaintext") {
|
|
631
|
+
tag.rawTextEnd = html.length;
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
const closing = new RegExp(`</${tag.name}(?=[\\t\\n\\f\\r />])`, "gi");
|
|
635
|
+
closing.lastIndex = tag.end;
|
|
636
|
+
const end = closing.exec(html);
|
|
637
|
+
tag.rawTextEnd = end?.index ?? html.length;
|
|
638
|
+
opening.lastIndex = tag.rawTextEnd;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return tags;
|
|
642
|
+
}
|
|
643
|
+
|
|
556
644
|
// ../engine/src/services/browserManager.ts
|
|
557
645
|
import { execSync } from "child_process";
|
|
558
|
-
import { existsSync, readdirSync } from "fs";
|
|
646
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
559
647
|
import { join as join2 } from "path";
|
|
560
648
|
import { homedir } from "os";
|
|
561
649
|
|
|
@@ -797,6 +885,7 @@ async function getPuppeteer() {
|
|
|
797
885
|
if (!_puppeteer) throw new Error("Neither puppeteer nor puppeteer-core found");
|
|
798
886
|
return _puppeteer;
|
|
799
887
|
}
|
|
888
|
+
var GPU_PROBE_TIMEOUT_MS = 15e3;
|
|
800
889
|
async function probeHardwareWebGlInfo(ppt, options) {
|
|
801
890
|
let probeBrowser;
|
|
802
891
|
try {
|
|
@@ -806,37 +895,44 @@ async function probeHardwareWebGlInfo(ppt, options) {
|
|
|
806
895
|
args: options.args,
|
|
807
896
|
defaultViewport: { width: 64, height: 64 },
|
|
808
897
|
executablePath: options.executablePath,
|
|
809
|
-
timeout: options.browserTimeout
|
|
810
|
-
|
|
811
|
-
const page = await probeBrowser.newPage();
|
|
812
|
-
return await page.evaluate(() => {
|
|
813
|
-
const unavailable = { hasWebGL: false, vendor: "", renderer: "" };
|
|
814
|
-
const c = document.createElement("canvas");
|
|
815
|
-
let gl = c.getContext("webgl");
|
|
816
|
-
if (gl === null) {
|
|
817
|
-
gl = c.getContext("experimental-webgl");
|
|
818
|
-
}
|
|
819
|
-
if (gl === null) return unavailable;
|
|
820
|
-
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
|
821
|
-
let vendorParam = gl.VENDOR;
|
|
822
|
-
let rendererParam = gl.RENDERER;
|
|
823
|
-
if (ext !== null) {
|
|
824
|
-
vendorParam = ext.UNMASKED_VENDOR_WEBGL;
|
|
825
|
-
rendererParam = ext.UNMASKED_RENDERER_WEBGL;
|
|
826
|
-
}
|
|
827
|
-
const vendor = gl.getParameter(vendorParam);
|
|
828
|
-
const renderer = gl.getParameter(rendererParam);
|
|
829
|
-
return {
|
|
830
|
-
hasWebGL: true,
|
|
831
|
-
vendor: vendor == null ? "" : String(vendor),
|
|
832
|
-
renderer: renderer == null ? "" : String(renderer)
|
|
833
|
-
};
|
|
898
|
+
timeout: options.browserTimeout,
|
|
899
|
+
waitForInitialPage: false
|
|
834
900
|
});
|
|
901
|
+
return await rejectAfter(
|
|
902
|
+
readWebGlInfo(probeBrowser),
|
|
903
|
+
GPU_PROBE_TIMEOUT_MS,
|
|
904
|
+
`GPU probe page did not answer within ${GPU_PROBE_TIMEOUT_MS}ms`
|
|
905
|
+
);
|
|
835
906
|
} finally {
|
|
836
|
-
|
|
837
|
-
});
|
|
907
|
+
if (probeBrowser) await closeBrowserAfterFailedProbe(probeBrowser);
|
|
838
908
|
}
|
|
839
909
|
}
|
|
910
|
+
async function readWebGlInfo(browser) {
|
|
911
|
+
const page = await browser.newPage();
|
|
912
|
+
return await page.evaluate(() => {
|
|
913
|
+
const unavailable = { hasWebGL: false, vendor: "", renderer: "" };
|
|
914
|
+
const c = document.createElement("canvas");
|
|
915
|
+
let gl = c.getContext("webgl");
|
|
916
|
+
if (gl === null) {
|
|
917
|
+
gl = c.getContext("experimental-webgl");
|
|
918
|
+
}
|
|
919
|
+
if (gl === null) return unavailable;
|
|
920
|
+
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
|
921
|
+
let vendorParam = gl.VENDOR;
|
|
922
|
+
let rendererParam = gl.RENDERER;
|
|
923
|
+
if (ext !== null) {
|
|
924
|
+
vendorParam = ext.UNMASKED_VENDOR_WEBGL;
|
|
925
|
+
rendererParam = ext.UNMASKED_RENDERER_WEBGL;
|
|
926
|
+
}
|
|
927
|
+
const vendor = gl.getParameter(vendorParam);
|
|
928
|
+
const renderer = gl.getParameter(rendererParam);
|
|
929
|
+
return {
|
|
930
|
+
hasWebGL: true,
|
|
931
|
+
vendor: vendor == null ? "" : String(vendor),
|
|
932
|
+
renderer: renderer == null ? "" : String(renderer)
|
|
933
|
+
};
|
|
934
|
+
});
|
|
935
|
+
}
|
|
840
936
|
function compareBrowserVersionsDescending(left, right) {
|
|
841
937
|
const parse = (value) => {
|
|
842
938
|
const version = value.slice(value.indexOf("-") + 1);
|
|
@@ -867,6 +963,14 @@ var CACHED_HEADLESS_SHELL_EXECUTABLES = {
|
|
|
867
963
|
function cachedHeadlessShellExecutable(hostPlatform = process.platform, hostArch = process.arch) {
|
|
868
964
|
return CACHED_HEADLESS_SHELL_EXECUTABLES[`${hostPlatform}/${hostArch}`];
|
|
869
965
|
}
|
|
966
|
+
function isNonEmptyFile(path) {
|
|
967
|
+
try {
|
|
968
|
+
const stat = statSync(path, { throwIfNoEntry: false });
|
|
969
|
+
return stat?.isFile() === true && stat.size > 0;
|
|
970
|
+
} catch {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
870
974
|
function findCachedHeadlessShell(baseDir) {
|
|
871
975
|
if (!existsSync(baseDir)) return void 0;
|
|
872
976
|
const executable = cachedHeadlessShellExecutable();
|
|
@@ -877,7 +981,7 @@ function findCachedHeadlessShell(baseDir) {
|
|
|
877
981
|
for (const version of versions) {
|
|
878
982
|
if (exceedsChromeCeiling(version, ceiling)) continue;
|
|
879
983
|
const binary = join2(baseDir, version, ...executable);
|
|
880
|
-
if (
|
|
984
|
+
if (isNonEmptyFile(binary)) return binary;
|
|
881
985
|
}
|
|
882
986
|
} catch {
|
|
883
987
|
}
|
|
@@ -933,15 +1037,15 @@ async function awaitBeforeDeadline(operation, deadline, label) {
|
|
|
933
1037
|
if (remainingMs <= 0) {
|
|
934
1038
|
throw new Error(`beginFrame probe timeout before ${label}`);
|
|
935
1039
|
}
|
|
1040
|
+
return rejectAfter(operation, remainingMs, `beginFrame probe timeout during ${label}`);
|
|
1041
|
+
}
|
|
1042
|
+
async function rejectAfter(operation, timeoutMs, message) {
|
|
936
1043
|
let timeout;
|
|
937
1044
|
try {
|
|
938
1045
|
return await Promise.race([
|
|
939
1046
|
operation,
|
|
940
1047
|
new Promise((_, reject) => {
|
|
941
|
-
timeout = setTimeout(
|
|
942
|
-
() => reject(new Error(`beginFrame probe timeout during ${label}`)),
|
|
943
|
-
remainingMs
|
|
944
|
-
);
|
|
1048
|
+
timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
945
1049
|
})
|
|
946
1050
|
]);
|
|
947
1051
|
} finally {
|
|
@@ -1355,8 +1459,9 @@ function buildChromeArgs(options, config) {
|
|
|
1355
1459
|
return chromeArgs;
|
|
1356
1460
|
}
|
|
1357
1461
|
function compositionRequiresWebGpu(html) {
|
|
1358
|
-
for (const
|
|
1359
|
-
|
|
1462
|
+
for (const tag of scanHtmlOpeningTags(html)) {
|
|
1463
|
+
const names = tag.attributes.map((attribute) => attribute.name);
|
|
1464
|
+
if (names.includes("data-composition-id")) return names.includes("data-requires-webgpu");
|
|
1360
1465
|
}
|
|
1361
1466
|
return false;
|
|
1362
1467
|
}
|
|
@@ -1959,7 +2064,7 @@ function parseObjectPositionAxis(value, axis) {
|
|
|
1959
2064
|
if (lower === "center" || lower === "") return 0.5;
|
|
1960
2065
|
if (lower.endsWith("%")) {
|
|
1961
2066
|
const pct = parseFloat(lower) / 100;
|
|
1962
|
-
return Number.isFinite(pct) ?
|
|
2067
|
+
return Number.isFinite(pct) ? pct : 0.5;
|
|
1963
2068
|
}
|
|
1964
2069
|
if (axis === "x" || axis === "y") return 0.5;
|
|
1965
2070
|
return 0.5;
|
|
@@ -5166,6 +5271,7 @@ async function constructCaptureSession(input) {
|
|
|
5166
5271
|
browserConsoleBuffer: [],
|
|
5167
5272
|
scriptLoadFailures: [],
|
|
5168
5273
|
pageErrors: [],
|
|
5274
|
+
subTimelineWaitMemo: sessionOptions.subTimelineWaitMemo ?? {},
|
|
5169
5275
|
warnings: [],
|
|
5170
5276
|
capturePerf: {
|
|
5171
5277
|
frames: 0,
|
|
@@ -5314,15 +5420,52 @@ async function pollHfReady(page, timeoutMs, intervalMs = 100) {
|
|
|
5314
5420
|
State: __hf=${diag.hasHf}, seek=${diag.hasSeek}, player=${diag.hasPlayer}, renderReady=${diag.renderReady}, duration=${diag.duration}`
|
|
5315
5421
|
);
|
|
5316
5422
|
}
|
|
5317
|
-
async function
|
|
5423
|
+
async function listUnregisteredTimelineIds(page) {
|
|
5424
|
+
const evaluated = await page.evaluate(`(function() {
|
|
5425
|
+
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
5426
|
+
var timelines = window.__timelines || {};
|
|
5427
|
+
var m = [];
|
|
5428
|
+
for (var i = 0; i < hosts.length; i++) {
|
|
5429
|
+
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
5430
|
+
var id = hosts[i].getAttribute("data-composition-id");
|
|
5431
|
+
if (id && !timelines[id]) m.push(id);
|
|
5432
|
+
}
|
|
5433
|
+
return m;
|
|
5434
|
+
})()`);
|
|
5435
|
+
return Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
|
|
5436
|
+
}
|
|
5437
|
+
async function waitForSubCompositionTimelines(session, page, timeoutMs) {
|
|
5438
|
+
const memo = session.subTimelineWaitMemo;
|
|
5439
|
+
session.subTimelineWaitOutcome = await pollSubCompositionTimelines(page, timeoutMs, {
|
|
5440
|
+
getScriptLoadFailures: () => session.scriptLoadFailures,
|
|
5441
|
+
onPending: (ids) => {
|
|
5442
|
+
session.pendingTimelineIds = [...ids];
|
|
5443
|
+
},
|
|
5444
|
+
shouldStop: () => session.vfxFailure !== void 0,
|
|
5445
|
+
knownUnregisteredIds: memo.unregisteredIds
|
|
5446
|
+
});
|
|
5447
|
+
const cleanTimeout = session.subTimelineWaitOutcome === "timeout" && session.vfxFailure === void 0 && session.pageErrors.length === 0 && session.scriptLoadFailures.length === 0;
|
|
5448
|
+
if (cleanTimeout) {
|
|
5449
|
+
memo.unregisteredIds = session.pendingTimelineIds ?? [];
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
async function pollSubCompositionTimelines(page, timeoutMs, {
|
|
5453
|
+
intervalMs = 150,
|
|
5454
|
+
getScriptLoadFailures,
|
|
5455
|
+
scriptFailureGraceMs = 2e3,
|
|
5456
|
+
onPending,
|
|
5457
|
+
shouldStop,
|
|
5458
|
+
knownUnregisteredIds = []
|
|
5459
|
+
} = {}) {
|
|
5318
5460
|
const expression = `(function() {
|
|
5319
5461
|
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
5320
5462
|
if (hosts.length === 0) return true;
|
|
5321
5463
|
var timelines = window.__timelines || {};
|
|
5464
|
+
var known = ${JSON.stringify(knownUnregisteredIds)};
|
|
5322
5465
|
for (var i = 0; i < hosts.length; i++) {
|
|
5323
5466
|
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
5324
5467
|
var id = hosts[i].getAttribute("data-composition-id");
|
|
5325
|
-
if (!id) continue;
|
|
5468
|
+
if (!id || known.indexOf(id) !== -1) continue;
|
|
5326
5469
|
if (!timelines[id]) return false;
|
|
5327
5470
|
}
|
|
5328
5471
|
return true;
|
|
@@ -5346,7 +5489,11 @@ async function pollSubCompositionTimelines(page, timeoutMs, intervalMs = 150, ge
|
|
|
5346
5489
|
}
|
|
5347
5490
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
5348
5491
|
}
|
|
5349
|
-
|
|
5492
|
+
const pendingIds = ready && knownUnregisteredIds.length === 0 ? [] : await listUnregisteredTimelineIds(page);
|
|
5493
|
+
if (ready && pendingIds.length > 0 && (getScriptLoadFailures?.() ?? []).length > 0) {
|
|
5494
|
+
scriptFailureBail = true;
|
|
5495
|
+
}
|
|
5496
|
+
if (ready && pendingIds.length === 0) {
|
|
5350
5497
|
await page.evaluate(`(function() {
|
|
5351
5498
|
if (typeof window.__hfForceTimelineRebind === "function") {
|
|
5352
5499
|
window.__hfForceTimelineRebind();
|
|
@@ -5354,18 +5501,6 @@ async function pollSubCompositionTimelines(page, timeoutMs, intervalMs = 150, ge
|
|
|
5354
5501
|
})()`);
|
|
5355
5502
|
return "ready";
|
|
5356
5503
|
}
|
|
5357
|
-
const evaluated = await page.evaluate(`(function() {
|
|
5358
|
-
var hosts = document.querySelectorAll("[data-composition-id]");
|
|
5359
|
-
var timelines = window.__timelines || {};
|
|
5360
|
-
var m = [];
|
|
5361
|
-
for (var i = 0; i < hosts.length; i++) {
|
|
5362
|
-
if (hosts[i].hasAttribute("data-no-timeline")) continue;
|
|
5363
|
-
var id = hosts[i].getAttribute("data-composition-id");
|
|
5364
|
-
if (id && !timelines[id]) m.push(id);
|
|
5365
|
-
}
|
|
5366
|
-
return m;
|
|
5367
|
-
})()`);
|
|
5368
|
-
const pendingIds = Array.isArray(evaluated) ? evaluated.map((id) => String(id)) : [];
|
|
5369
5504
|
onPending?.(pendingIds);
|
|
5370
5505
|
const missing = pendingIds.join(", ");
|
|
5371
5506
|
if (scriptFailureBail) {
|
|
@@ -5735,17 +5870,7 @@ async function initializeSession(session) {
|
|
|
5735
5870
|
const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
|
|
5736
5871
|
await pollHfReady(page, pageReadyTimeout);
|
|
5737
5872
|
logInitPhase("pollHfReady complete");
|
|
5738
|
-
session
|
|
5739
|
-
page,
|
|
5740
|
-
pageReadyTimeout,
|
|
5741
|
-
void 0,
|
|
5742
|
-
() => session.scriptLoadFailures,
|
|
5743
|
-
void 0,
|
|
5744
|
-
(ids) => {
|
|
5745
|
-
session.pendingTimelineIds = [...ids];
|
|
5746
|
-
},
|
|
5747
|
-
() => session.vfxFailure !== void 0
|
|
5748
|
-
);
|
|
5873
|
+
await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
|
|
5749
5874
|
logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
|
|
5750
5875
|
assertVfxIntact(session);
|
|
5751
5876
|
recordSubTimelineWarning(session, pageReadyTimeout);
|
|
@@ -5851,17 +5976,7 @@ async function initializeSession(session) {
|
|
|
5851
5976
|
const pageReadyTimeout = session.config?.playerReadyTimeout ?? DEFAULT_CONFIG.playerReadyTimeout;
|
|
5852
5977
|
await pollHfReady(page, pageReadyTimeout);
|
|
5853
5978
|
logInitPhase("pollHfReady complete");
|
|
5854
|
-
session
|
|
5855
|
-
page,
|
|
5856
|
-
pageReadyTimeout,
|
|
5857
|
-
void 0,
|
|
5858
|
-
() => session.scriptLoadFailures,
|
|
5859
|
-
void 0,
|
|
5860
|
-
(ids) => {
|
|
5861
|
-
session.pendingTimelineIds = [...ids];
|
|
5862
|
-
},
|
|
5863
|
-
() => session.vfxFailure !== void 0
|
|
5864
|
-
);
|
|
5979
|
+
await waitForSubCompositionTimelines(session, page, pageReadyTimeout);
|
|
5865
5980
|
logInitPhase(`pollSubCompositionTimelines complete (${session.subTimelineWaitOutcome})`);
|
|
5866
5981
|
assertVfxIntact(session);
|
|
5867
5982
|
recordSubTimelineWarning(session, pageReadyTimeout);
|
|
@@ -6298,7 +6413,10 @@ async function createStaticVerificationPage(session) {
|
|
|
6298
6413
|
});
|
|
6299
6414
|
await page.evaluate(`window.__hfFlushSync?.()`);
|
|
6300
6415
|
await pollHfReady(page, pageReadyTimeout);
|
|
6301
|
-
await pollSubCompositionTimelines(page, pageReadyTimeout
|
|
6416
|
+
await pollSubCompositionTimelines(page, pageReadyTimeout, {
|
|
6417
|
+
getScriptLoadFailures: () => session.scriptLoadFailures,
|
|
6418
|
+
knownUnregisteredIds: session.subTimelineWaitMemo.unregisteredIds
|
|
6419
|
+
});
|
|
6302
6420
|
await applyVideoMetadataHints(page, session.options.videoMetadataHints);
|
|
6303
6421
|
const skipVideoIds = session.options.skipReadinessVideoIds ?? [];
|
|
6304
6422
|
await Promise.all([
|
|
@@ -7006,7 +7124,7 @@ async function captureFramesBatchPipelined(session, frameIndices, times) {
|
|
|
7006
7124
|
return results;
|
|
7007
7125
|
}
|
|
7008
7126
|
async function discardWarmupCapture(session, frameIndex = 0, time = 0, innerCapture = captureFrameCore) {
|
|
7009
|
-
const perfBefore = { ...session.capturePerf };
|
|
7127
|
+
const perfBefore = { ...session.capturePerf, frameMs: [...session.capturePerf.frameMs] };
|
|
7010
7128
|
const hasDamageBefore = session.beginFrameHasDamageCount;
|
|
7011
7129
|
const noDamageBefore = session.beginFrameNoDamageCount;
|
|
7012
7130
|
const dedupCountBefore = session.staticDedupCount;
|
|
@@ -7227,7 +7345,7 @@ import {
|
|
|
7227
7345
|
openSync,
|
|
7228
7346
|
readFileSync as readFileSync3,
|
|
7229
7347
|
readdirSync as readdirSync2,
|
|
7230
|
-
statSync as
|
|
7348
|
+
statSync as statSync3,
|
|
7231
7349
|
writeFileSync as writeFileSync2,
|
|
7232
7350
|
writeSync
|
|
7233
7351
|
} from "fs";
|
|
@@ -7421,7 +7539,7 @@ var H264_ENCODER_BY_GPU = {
|
|
|
7421
7539
|
qsv: "h264_qsv",
|
|
7422
7540
|
amf: "h264_amf"
|
|
7423
7541
|
};
|
|
7424
|
-
var
|
|
7542
|
+
var GPU_PROBE_TIMEOUT_MS2 = 2e3;
|
|
7425
7543
|
var GPU_PROBE_KILL_GRACE_MS = 1e3;
|
|
7426
7544
|
function getCompiledGpuEncoders(ffmpegEncodersStdout) {
|
|
7427
7545
|
return GPU_ENCODER_CANDIDATES.filter(
|
|
@@ -7538,7 +7656,7 @@ async function canUseGpuEncoder(encoder) {
|
|
|
7538
7656
|
});
|
|
7539
7657
|
trackChildProcess(ffmpeg);
|
|
7540
7658
|
const outcome = await new ManagedChildProcess(ffmpeg, {
|
|
7541
|
-
deadlineAtMs: Date.now() +
|
|
7659
|
+
deadlineAtMs: Date.now() + GPU_PROBE_TIMEOUT_MS2,
|
|
7542
7660
|
terminationGraceMs: GPU_PROBE_KILL_GRACE_MS
|
|
7543
7661
|
}).wait();
|
|
7544
7662
|
const usable = outcome.reason === "exit" && outcome.exitCode === 0;
|
|
@@ -7554,7 +7672,7 @@ async function canUseGpuEncoder(encoder) {
|
|
|
7554
7672
|
function logGpuProbeFailure(encoder, result) {
|
|
7555
7673
|
if (!isGpuProbeDebugEnabled()) return;
|
|
7556
7674
|
if (result.code === 0 && !result.error && !result.timedOut) return;
|
|
7557
|
-
const reason = result.error ? result.error.message : result.timedOut ? `timed out after ${
|
|
7675
|
+
const reason = result.error ? result.error.message : result.timedOut ? `timed out after ${GPU_PROBE_TIMEOUT_MS2}ms` : `exit=${String(result.code)} signal=${String(result.signal ?? "")}`;
|
|
7558
7676
|
const stderr = result.stderr?.trim();
|
|
7559
7677
|
console.warn(`[gpuEncoder] ${encoder} probe failed: ${reason}${stderr ? `
|
|
7560
7678
|
${stderr}` : ""}`);
|
|
@@ -7661,10 +7779,6 @@ function withEvenDimensionPad(vfChain, pixelFormat, width, height) {
|
|
|
7661
7779
|
return vfChain ? `${vfChain},${EVEN_DIMENSION_PAD}` : EVEN_DIMENSION_PAD;
|
|
7662
7780
|
}
|
|
7663
7781
|
|
|
7664
|
-
// ../engine/src/utils/sdrCaptureColor.ts
|
|
7665
|
-
var SDR_RGB_TO_BT709_FILTER = "scale=out_color_matrix=bt709:out_range=tv:flags=neighbor";
|
|
7666
|
-
var SDR_CAPTURE_TO_BT709_FILTER = `scale=in_color_matrix=bt601:in_range=pc:flags=neighbor,format=gbrp,${SDR_RGB_TO_BT709_FILTER}`;
|
|
7667
|
-
|
|
7668
7782
|
// ../engine/src/utils/runFfmpeg.ts
|
|
7669
7783
|
import { spawn as spawn2 } from "child_process";
|
|
7670
7784
|
var FFMPEG_SIGTERM_EXIT_LINE = /^Exiting normally, received signal 15\.?\r?$/m;
|
|
@@ -7783,7 +7897,7 @@ async function runFfmpegPipeline(producerArgs, consumerArgs, opts) {
|
|
|
7783
7897
|
|
|
7784
7898
|
// ../engine/src/utils/ffprobe.ts
|
|
7785
7899
|
import { spawn as spawn3 } from "child_process";
|
|
7786
|
-
import { createReadStream, readFileSync, statSync } from "fs";
|
|
7900
|
+
import { createReadStream, readFileSync, statSync as statSync2 } from "fs";
|
|
7787
7901
|
import { StringDecoder } from "string_decoder";
|
|
7788
7902
|
import { basename } from "path";
|
|
7789
7903
|
var FFPROBE_STDERR_MAX_BYTES = 8 * 1024;
|
|
@@ -7886,7 +8000,8 @@ var audioMetadataCache = /* @__PURE__ */ new Map();
|
|
|
7886
8000
|
var mediaProbeOutputCache = /* @__PURE__ */ new Map();
|
|
7887
8001
|
var mediaProbeOutputSignalCaches = /* @__PURE__ */ new WeakMap();
|
|
7888
8002
|
var MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES = 128;
|
|
7889
|
-
var
|
|
8003
|
+
var AUDIO_DURATION_PROBE_MARGIN_SECONDS = 0.05;
|
|
8004
|
+
var AUDIO_DURATION_PROBE_TAIL_SECONDS = 1;
|
|
7890
8005
|
var STILL_IMAGE_DEMUXERS = /* @__PURE__ */ new Set([
|
|
7891
8006
|
"apng",
|
|
7892
8007
|
"bmp_pipe",
|
|
@@ -7938,12 +8053,28 @@ function isPngImageProbe(output) {
|
|
|
7938
8053
|
}
|
|
7939
8054
|
function mediaFileIdentity(filePath) {
|
|
7940
8055
|
try {
|
|
7941
|
-
const stat =
|
|
8056
|
+
const stat = statSync2(filePath, { bigint: true });
|
|
7942
8057
|
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(":");
|
|
7943
8058
|
} catch {
|
|
7944
8059
|
return null;
|
|
7945
8060
|
}
|
|
7946
8061
|
}
|
|
8062
|
+
function readMetadataCache(cache, key, identity) {
|
|
8063
|
+
const cached = cache.get(key);
|
|
8064
|
+
return cached?.identity === identity ? cached.promise : void 0;
|
|
8065
|
+
}
|
|
8066
|
+
function writeMetadataCache(cache, key, identity, promise) {
|
|
8067
|
+
const entry = { identity, promise };
|
|
8068
|
+
cache.set(key, entry);
|
|
8069
|
+
void promise.then(
|
|
8070
|
+
() => {
|
|
8071
|
+
if (identity === null && cache.get(key) === entry) cache.delete(key);
|
|
8072
|
+
},
|
|
8073
|
+
() => {
|
|
8074
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
8075
|
+
}
|
|
8076
|
+
);
|
|
8077
|
+
}
|
|
7947
8078
|
async function probeMediaOutput(filePath, signal) {
|
|
7948
8079
|
let cache = mediaProbeOutputCache;
|
|
7949
8080
|
if (signal) {
|
|
@@ -7995,7 +8126,7 @@ async function readFileRange(filePath, start, length, signal) {
|
|
|
7995
8126
|
async function hasCompletePngStructure(filePath, signal) {
|
|
7996
8127
|
try {
|
|
7997
8128
|
signal?.throwIfAborted();
|
|
7998
|
-
const fileSize =
|
|
8129
|
+
const fileSize = statSync2(filePath).size;
|
|
7999
8130
|
const signature = await readFileRange(filePath, 0, 8, signal);
|
|
8000
8131
|
signal?.throwIfAborted();
|
|
8001
8132
|
if (signature.length !== 8 || !signature.equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
@@ -8126,7 +8257,8 @@ function parseFrameRate(frameRateStr) {
|
|
|
8126
8257
|
return rounded > 0 ? rounded : 0.01;
|
|
8127
8258
|
}
|
|
8128
8259
|
async function extractMediaMetadata(filePath) {
|
|
8129
|
-
const
|
|
8260
|
+
const identity = mediaFileIdentity(filePath);
|
|
8261
|
+
const cached = readMetadataCache(videoMetadataCache, filePath, identity);
|
|
8130
8262
|
if (cached) return cached;
|
|
8131
8263
|
const probePromise = (async () => {
|
|
8132
8264
|
let stillImageMetaMemo;
|
|
@@ -8210,12 +8342,7 @@ async function extractMediaMetadata(filePath) {
|
|
|
8210
8342
|
frames
|
|
8211
8343
|
};
|
|
8212
8344
|
})();
|
|
8213
|
-
videoMetadataCache
|
|
8214
|
-
probePromise.catch(() => {
|
|
8215
|
-
if (videoMetadataCache.get(filePath) === probePromise) {
|
|
8216
|
-
videoMetadataCache.delete(filePath);
|
|
8217
|
-
}
|
|
8218
|
-
});
|
|
8345
|
+
writeMetadataCache(videoMetadataCache, filePath, identity, probePromise);
|
|
8219
8346
|
return probePromise;
|
|
8220
8347
|
}
|
|
8221
8348
|
function resolveStreamWindow(filePath, metadata) {
|
|
@@ -8231,12 +8358,13 @@ async function extractFinalVideoFrameTimestamp(filePath, metadata, signal) {
|
|
|
8231
8358
|
videoStreamDurationSeconds: videoDurationSeconds,
|
|
8232
8359
|
cacheKey
|
|
8233
8360
|
} = resolveStreamWindow(filePath, metadata);
|
|
8361
|
+
const identity = mediaFileIdentity(filePath);
|
|
8234
8362
|
let probeCache = finalVideoFrameTimestampCache;
|
|
8235
8363
|
if (signal) {
|
|
8236
8364
|
probeCache = finalVideoFrameTimestampSignalCaches.get(signal) ?? /* @__PURE__ */ new Map();
|
|
8237
8365
|
finalVideoFrameTimestampSignalCaches.set(signal, probeCache);
|
|
8238
8366
|
}
|
|
8239
|
-
const cached = probeCache
|
|
8367
|
+
const cached = readMetadataCache(probeCache, cacheKey, identity);
|
|
8240
8368
|
if (cached) return cached;
|
|
8241
8369
|
const probePromise = (async () => {
|
|
8242
8370
|
if (!(videoDurationSeconds > 0) || !Number.isFinite(videoDurationSeconds)) {
|
|
@@ -8271,17 +8399,13 @@ async function extractFinalVideoFrameTimestamp(filePath, metadata, signal) {
|
|
|
8271
8399
|
}
|
|
8272
8400
|
return Math.min(Math.max(timestamp - videoStreamStartSeconds, 0), videoDurationSeconds);
|
|
8273
8401
|
})();
|
|
8274
|
-
probeCache
|
|
8275
|
-
probePromise.catch(() => {
|
|
8276
|
-
if (probeCache.get(cacheKey) === probePromise) {
|
|
8277
|
-
probeCache.delete(cacheKey);
|
|
8278
|
-
}
|
|
8279
|
-
});
|
|
8402
|
+
writeMetadataCache(probeCache, cacheKey, identity, probePromise);
|
|
8280
8403
|
return probePromise;
|
|
8281
8404
|
}
|
|
8282
8405
|
var extractVideoMetadata = extractMediaMetadata;
|
|
8283
8406
|
async function extractAudioMetadata(filePath, options) {
|
|
8284
|
-
const
|
|
8407
|
+
const identity = mediaFileIdentity(filePath);
|
|
8408
|
+
const cached = options?.signal ? void 0 : readMetadataCache(audioMetadataCache, filePath, identity);
|
|
8285
8409
|
if (cached) return cached;
|
|
8286
8410
|
const probePromise = (async () => {
|
|
8287
8411
|
const output = await probeMediaOutput(filePath, options?.signal);
|
|
@@ -8291,29 +8415,16 @@ async function extractAudioMetadata(filePath, options) {
|
|
|
8291
8415
|
const streamDuration = audioStream.duration ? parseFloat(audioStream.duration) : void 0;
|
|
8292
8416
|
const sampleRate = audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100;
|
|
8293
8417
|
const audioCodec = audioStream.codec_name || "unknown";
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
"stream=nb_read_packets",
|
|
8305
|
-
"-print_format",
|
|
8306
|
-
"json"
|
|
8307
|
-
],
|
|
8308
|
-
options?.signal
|
|
8309
|
-
);
|
|
8310
|
-
const packetOutput = parseProbeJson(packetStdout);
|
|
8311
|
-
const packetCount = Number(packetOutput.streams[0]?.nb_read_packets);
|
|
8312
|
-
if (Number.isFinite(packetCount) && packetCount > 0) {
|
|
8313
|
-
durationSeconds = packetCount * AAC_LC_SAMPLES_PER_PACKET / sampleRate;
|
|
8314
|
-
}
|
|
8315
|
-
} catch (error) {
|
|
8316
|
-
if (options?.signal?.aborted) throw error;
|
|
8418
|
+
if (sampleRate > 0) {
|
|
8419
|
+
const decoded = await probeDecodedAudioDuration(
|
|
8420
|
+
filePath,
|
|
8421
|
+
sampleRate,
|
|
8422
|
+
audioStartSeconds(audioStream, output.format),
|
|
8423
|
+
durationSeconds,
|
|
8424
|
+
options?.signal
|
|
8425
|
+
);
|
|
8426
|
+
if (decoded !== null) {
|
|
8427
|
+
durationSeconds = reconcileAudioDuration(durationSeconds, decoded, output.format);
|
|
8317
8428
|
}
|
|
8318
8429
|
}
|
|
8319
8430
|
return {
|
|
@@ -8326,14 +8437,58 @@ async function extractAudioMetadata(filePath, options) {
|
|
|
8326
8437
|
};
|
|
8327
8438
|
})();
|
|
8328
8439
|
if (options?.signal) return probePromise;
|
|
8329
|
-
audioMetadataCache
|
|
8330
|
-
probePromise.catch(() => {
|
|
8331
|
-
if (audioMetadataCache.get(filePath) === probePromise) {
|
|
8332
|
-
audioMetadataCache.delete(filePath);
|
|
8333
|
-
}
|
|
8334
|
-
});
|
|
8440
|
+
writeMetadataCache(audioMetadataCache, filePath, identity, probePromise);
|
|
8335
8441
|
return probePromise;
|
|
8336
8442
|
}
|
|
8443
|
+
function audioStartSeconds(stream, format) {
|
|
8444
|
+
const start = parseFloat(stream.start_time ?? format.start_time ?? "");
|
|
8445
|
+
return Number.isFinite(start) ? start : 0;
|
|
8446
|
+
}
|
|
8447
|
+
function reconcileAudioDuration(containerSeconds, decodedSeconds, format) {
|
|
8448
|
+
const difference = decodedSeconds - containerSeconds;
|
|
8449
|
+
const trustShorter = format.format_name === "aac";
|
|
8450
|
+
if (difference > AUDIO_DURATION_PROBE_MARGIN_SECONDS) return decodedSeconds;
|
|
8451
|
+
if (trustShorter && difference < -AUDIO_DURATION_PROBE_MARGIN_SECONDS) return decodedSeconds;
|
|
8452
|
+
return containerSeconds;
|
|
8453
|
+
}
|
|
8454
|
+
async function probeDecodedAudioDuration(filePath, sampleRate, startSeconds, containerDurationSeconds, signal) {
|
|
8455
|
+
const probe = async (readInterval) => {
|
|
8456
|
+
const args = [
|
|
8457
|
+
"-select_streams",
|
|
8458
|
+
"a:0",
|
|
8459
|
+
"-show_entries",
|
|
8460
|
+
"frame=best_effort_timestamp_time,nb_samples",
|
|
8461
|
+
"-of",
|
|
8462
|
+
"csv=p=0"
|
|
8463
|
+
];
|
|
8464
|
+
if (readInterval) args.unshift("-read_intervals", readInterval);
|
|
8465
|
+
try {
|
|
8466
|
+
const stdout = await runFfprobe(
|
|
8467
|
+
filePath,
|
|
8468
|
+
args,
|
|
8469
|
+
signal,
|
|
8470
|
+
// CSV, not JSON: a tail truncated under the size cap still parses.
|
|
8471
|
+
{ retainTail: true, maxChars: 64 * 1024 }
|
|
8472
|
+
);
|
|
8473
|
+
return stdout.split("\n").map((line) => {
|
|
8474
|
+
const [timestampText, samplesText] = line.trim().split(",");
|
|
8475
|
+
const timestamp = Number(timestampText);
|
|
8476
|
+
const nbSamples = Number(samplesText);
|
|
8477
|
+
return Number.isFinite(timestamp) && Number.isFinite(nbSamples) ? { timestamp, nbSamples } : void 0;
|
|
8478
|
+
}).filter((frame) => frame !== void 0).at(-1);
|
|
8479
|
+
} catch (error) {
|
|
8480
|
+
if (signal?.aborted) throw signal.reason ?? error;
|
|
8481
|
+
return void 0;
|
|
8482
|
+
}
|
|
8483
|
+
};
|
|
8484
|
+
const tailStart = Math.max(
|
|
8485
|
+
startSeconds,
|
|
8486
|
+
startSeconds + containerDurationSeconds - AUDIO_DURATION_PROBE_TAIL_SECONDS
|
|
8487
|
+
);
|
|
8488
|
+
const lastFrame = await probe(`${tailStart}%`) ?? await probe();
|
|
8489
|
+
if (!lastFrame) return null;
|
|
8490
|
+
return lastFrame.timestamp + lastFrame.nbSamples / sampleRate - startSeconds;
|
|
8491
|
+
}
|
|
8337
8492
|
var keyframeCache = /* @__PURE__ */ new Map();
|
|
8338
8493
|
var PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS = 2;
|
|
8339
8494
|
async function analyzeKeyframeIntervals(filePath, metadata) {
|
|
@@ -8781,7 +8936,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
8781
8936
|
failureReason: isExternalFfmpegInterruption(result) ? "external_interruption" : void 0
|
|
8782
8937
|
};
|
|
8783
8938
|
}
|
|
8784
|
-
const fileSize = existsSync4(outputPath) ?
|
|
8939
|
+
const fileSize = existsSync4(outputPath) ? statSync3(outputPath).size : 0;
|
|
8785
8940
|
return {
|
|
8786
8941
|
success: true,
|
|
8787
8942
|
outputPath,
|
|
@@ -8915,7 +9070,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
8915
9070
|
failureReason: concatResult.externalInterruption ? "external_interruption" : void 0
|
|
8916
9071
|
};
|
|
8917
9072
|
}
|
|
8918
|
-
const fileSize = existsSync4(outputPath) ?
|
|
9073
|
+
const fileSize = existsSync4(outputPath) ? statSync3(outputPath).size : 0;
|
|
8919
9074
|
return {
|
|
8920
9075
|
success: true,
|
|
8921
9076
|
outputPath,
|
|
@@ -9147,7 +9302,7 @@ function quantizationTableError(buffer, start, end) {
|
|
|
9147
9302
|
// ../engine/src/services/streamingEncoder.ts
|
|
9148
9303
|
import { spawn as spawn4 } from "child_process";
|
|
9149
9304
|
import { once } from "events";
|
|
9150
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync3, statSync as
|
|
9305
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, statSync as statSync4 } from "fs";
|
|
9151
9306
|
import { dirname as dirname3 } from "path";
|
|
9152
9307
|
function createFrameReorderBuffer(startFrame, endFrame) {
|
|
9153
9308
|
let cursor = startFrame;
|
|
@@ -9512,7 +9667,7 @@ FFmpeg stopped after ${streamingTimeout} ms without consuming a frame.` : "";
|
|
|
9512
9667
|
}) ? "external_interruption" : void 0
|
|
9513
9668
|
};
|
|
9514
9669
|
}
|
|
9515
|
-
const fileSize = existsSync5(outputPath) ?
|
|
9670
|
+
const fileSize = existsSync5(outputPath) ? statSync4(outputPath).size : 0;
|
|
9516
9671
|
return { success: true, durationMs, fileSize };
|
|
9517
9672
|
},
|
|
9518
9673
|
getExitStatus: () => exitStatus,
|
|
@@ -9608,6 +9763,7 @@ function isMemberGroupHidden(groupsById, el) {
|
|
|
9608
9763
|
// ../engine/src/services/videoFrameExtractor.ts
|
|
9609
9764
|
import { copyFileSync as copyFileSync2, existsSync as existsSync8, linkSync as linkSync2, mkdirSync as mkdirSync6, rmSync as rmSync4 } from "fs";
|
|
9610
9765
|
import { join as join10 } from "path";
|
|
9766
|
+
import { cpus } from "os";
|
|
9611
9767
|
|
|
9612
9768
|
// ../engine/src/services/referenceResolver.ts
|
|
9613
9769
|
function findReferenceTargetEl(doc, refId) {
|
|
@@ -9731,7 +9887,7 @@ import {
|
|
|
9731
9887
|
readdirSync as readdirSync3,
|
|
9732
9888
|
rmdirSync,
|
|
9733
9889
|
rmSync as rmSync2,
|
|
9734
|
-
statSync as
|
|
9890
|
+
statSync as statSync5,
|
|
9735
9891
|
unlinkSync
|
|
9736
9892
|
} from "fs";
|
|
9737
9893
|
import { createHash, randomUUID } from "crypto";
|
|
@@ -10341,7 +10497,7 @@ async function fetchToPartial(url, partialPath, controller, options) {
|
|
|
10341
10497
|
const readableStream = Readable.fromWeb(response.body);
|
|
10342
10498
|
const fileStream = createWriteStream(partialPath, { flags: "wx" });
|
|
10343
10499
|
await pipeline(readableStream, inspector, fileStream, { signal: controller.signal });
|
|
10344
|
-
const localSize =
|
|
10500
|
+
const localSize = statSync5(partialPath).size;
|
|
10345
10501
|
const sha256Bytes = sha256.digest();
|
|
10346
10502
|
const localSha256 = sha256Bytes.toString("hex");
|
|
10347
10503
|
const md5Base64 = md5.digest("base64");
|
|
@@ -10755,7 +10911,7 @@ import {
|
|
|
10755
10911
|
readdirSync as readdirSync5,
|
|
10756
10912
|
renameSync,
|
|
10757
10913
|
rmSync as rmSync3,
|
|
10758
|
-
statSync as
|
|
10914
|
+
statSync as statSync6,
|
|
10759
10915
|
utimesSync,
|
|
10760
10916
|
writeFileSync as writeFileSync3
|
|
10761
10917
|
} from "fs";
|
|
@@ -10782,7 +10938,7 @@ function extractedFrameIndex(file, format) {
|
|
|
10782
10938
|
}
|
|
10783
10939
|
return ordinal - 1;
|
|
10784
10940
|
}
|
|
10785
|
-
function framePathsFromDirectory(outputDir, format) {
|
|
10941
|
+
function framePathsFromDirectory(outputDir, format, allowGaps = false) {
|
|
10786
10942
|
const suffix = `.${format}`;
|
|
10787
10943
|
const indexed = /* @__PURE__ */ new Map();
|
|
10788
10944
|
for (const file of readdirSync4(outputDir)) {
|
|
@@ -10795,6 +10951,7 @@ function framePathsFromDirectory(outputDir, format) {
|
|
|
10795
10951
|
}
|
|
10796
10952
|
indexed.set(index, join8(outputDir, file));
|
|
10797
10953
|
}
|
|
10954
|
+
if (allowGaps) return new Map([...indexed].sort(([a], [b]) => a - b));
|
|
10798
10955
|
const ordered = /* @__PURE__ */ new Map();
|
|
10799
10956
|
for (let index = 0; index < indexed.size; index += 1) {
|
|
10800
10957
|
const path = indexed.get(index);
|
|
@@ -10815,7 +10972,7 @@ var SCHEMA_PREFIX = "hfcache-v6-";
|
|
|
10815
10972
|
var KEY_HEX_CHARS = 16;
|
|
10816
10973
|
function readKeyStat(videoPath) {
|
|
10817
10974
|
try {
|
|
10818
|
-
const stat =
|
|
10975
|
+
const stat = statSync6(videoPath);
|
|
10819
10976
|
return { mtimeMs: Math.floor(stat.mtimeMs), size: stat.size };
|
|
10820
10977
|
} catch {
|
|
10821
10978
|
return null;
|
|
@@ -10949,7 +11106,7 @@ function removeDir(path) {
|
|
|
10949
11106
|
}
|
|
10950
11107
|
function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
|
|
10951
11108
|
try {
|
|
10952
|
-
const dirStat =
|
|
11109
|
+
const dirStat = statSync6(dir);
|
|
10953
11110
|
if (isPartialChild(name) && now - dirStat.mtimeMs >= minAgeMs) {
|
|
10954
11111
|
remove(dir);
|
|
10955
11112
|
stats.agedPartialsRemoved += 1;
|
|
@@ -10957,7 +11114,7 @@ function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
|
|
|
10957
11114
|
}
|
|
10958
11115
|
let lastUseMs = dirStat.mtimeMs;
|
|
10959
11116
|
try {
|
|
10960
|
-
lastUseMs =
|
|
11117
|
+
lastUseMs = statSync6(join9(dir, COMPLETE_SENTINEL)).mtimeMs;
|
|
10961
11118
|
} catch {
|
|
10962
11119
|
}
|
|
10963
11120
|
return { dir, size: directorySizeBytes(dir), lastUseMs, ageMs: now - lastUseMs };
|
|
@@ -10967,7 +11124,7 @@ function collectGcEntry(dir, name, now, minAgeMs, stats, remove) {
|
|
|
10967
11124
|
}
|
|
10968
11125
|
function gcSweepDue(rootDir, maxAgeMs) {
|
|
10969
11126
|
try {
|
|
10970
|
-
return Date.now() -
|
|
11127
|
+
return Date.now() - statSync6(join9(rootDir, GC_MARKER)).mtimeMs > maxAgeMs;
|
|
10971
11128
|
} catch {
|
|
10972
11129
|
return true;
|
|
10973
11130
|
}
|
|
@@ -11465,6 +11622,69 @@ function restoreSourceColourFilter(metadata) {
|
|
|
11465
11622
|
].filter(([, value]) => value && value !== "unknown" && value !== "reserved");
|
|
11466
11623
|
return tags.length > 0 ? [`setparams=${tags.map(([key, value]) => `${key}=${value}`).join(":")}`] : [];
|
|
11467
11624
|
}
|
|
11625
|
+
var EXTRACTION_SEGMENT_SECONDS = 120;
|
|
11626
|
+
function splitFrameRanges(ranges, segmentFrames) {
|
|
11627
|
+
const segments = [];
|
|
11628
|
+
for (const range of ranges) {
|
|
11629
|
+
for (let offset = 0; offset < range.frames; offset += segmentFrames) {
|
|
11630
|
+
segments.push({
|
|
11631
|
+
firstFrame: range.firstFrame + offset,
|
|
11632
|
+
frames: Math.min(segmentFrames, range.frames - offset)
|
|
11633
|
+
});
|
|
11634
|
+
}
|
|
11635
|
+
}
|
|
11636
|
+
return segments;
|
|
11637
|
+
}
|
|
11638
|
+
async function runSegmentedExtraction(job) {
|
|
11639
|
+
const { decodeArgs, filterAndEncodeArgs, videoPath, startTime, fps, segments } = job;
|
|
11640
|
+
const segmentCount = segments.length;
|
|
11641
|
+
const failed = new AbortController();
|
|
11642
|
+
const signal = job.runOptions.signal ? AbortSignal.any([job.runOptions.signal, failed.signal]) : failed.signal;
|
|
11643
|
+
const results = [];
|
|
11644
|
+
let next = 0;
|
|
11645
|
+
const worker = async () => {
|
|
11646
|
+
while (next < segmentCount && !signal.aborted) {
|
|
11647
|
+
const index = next++;
|
|
11648
|
+
const { firstFrame, frames } = segments[index];
|
|
11649
|
+
const result = await runFfmpeg(
|
|
11650
|
+
[
|
|
11651
|
+
...decodeArgs,
|
|
11652
|
+
"-noaccurate_seek",
|
|
11653
|
+
"-ss",
|
|
11654
|
+
String(startTime + firstFrame / fps),
|
|
11655
|
+
"-i",
|
|
11656
|
+
videoPath,
|
|
11657
|
+
// Read one frame past the segment and keep exactly its own frames.
|
|
11658
|
+
"-t",
|
|
11659
|
+
String((frames + 1) / fps),
|
|
11660
|
+
"-frames:v",
|
|
11661
|
+
String(frames),
|
|
11662
|
+
"-start_number",
|
|
11663
|
+
String(firstFrame + 1),
|
|
11664
|
+
...filterAndEncodeArgs
|
|
11665
|
+
],
|
|
11666
|
+
{ ...job.runOptions, signal }
|
|
11667
|
+
);
|
|
11668
|
+
results[index] = result;
|
|
11669
|
+
if (!result.success) failed.abort();
|
|
11670
|
+
}
|
|
11671
|
+
};
|
|
11672
|
+
const workers = Math.min(segmentCount, Math.max(1, Math.floor(cpus().length / 2)));
|
|
11673
|
+
await Promise.all(Array.from({ length: workers }, worker));
|
|
11674
|
+
const ran = results.filter(Boolean);
|
|
11675
|
+
const failure = ran.find((r) => !r.success && r.terminationReason !== "abort") ?? ran.find((r) => !r.success);
|
|
11676
|
+
if (failure) return failure;
|
|
11677
|
+
if (ran.length < segmentCount) {
|
|
11678
|
+
return {
|
|
11679
|
+
success: false,
|
|
11680
|
+
exitCode: null,
|
|
11681
|
+
stderr: "",
|
|
11682
|
+
durationMs: 0,
|
|
11683
|
+
terminationReason: "abort"
|
|
11684
|
+
};
|
|
11685
|
+
}
|
|
11686
|
+
return ran[ran.length - 1];
|
|
11687
|
+
}
|
|
11468
11688
|
async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
|
|
11469
11689
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
11470
11690
|
const { outputDir, quality = 95 } = options;
|
|
@@ -11504,6 +11724,9 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
11504
11724
|
const toneMappedToSdr = options.toneMapHdrToSdr === true && isHdr && await toneMapsWithZscale();
|
|
11505
11725
|
const decodeWithVideoToolbox = isHdr && isMacOS && !toneMappedToSdr;
|
|
11506
11726
|
const sampleCfrAtOutputFps = !options.finalFrameOnly && !metadata.isVFR;
|
|
11727
|
+
if (options.frameRanges && !sampleCfrAtOutputFps) {
|
|
11728
|
+
throw new Error("frameRanges extraction needs a constant-frame-rate source");
|
|
11729
|
+
}
|
|
11507
11730
|
const args = [];
|
|
11508
11731
|
if (decodeWithVideoToolbox) {
|
|
11509
11732
|
args.push("-hwaccel", "videotoolbox");
|
|
@@ -11511,6 +11734,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
11511
11734
|
if (codecMayHaveAlpha(metadata.videoCodec)) {
|
|
11512
11735
|
args.push("-c:v", decoderForCodec(metadata.videoCodec));
|
|
11513
11736
|
}
|
|
11737
|
+
const decodeArgs = [...args];
|
|
11514
11738
|
if (options.finalFrameOnly) {
|
|
11515
11739
|
args.push("-i", videoPath, "-ss", String(startTime), "-frames:v", "1");
|
|
11516
11740
|
} else {
|
|
@@ -11566,8 +11790,21 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
11566
11790
|
runOptions
|
|
11567
11791
|
);
|
|
11568
11792
|
} else {
|
|
11569
|
-
|
|
11570
|
-
|
|
11793
|
+
const filterArgs = vfFilters.length > 0 ? ["-vf", vfFilters.join(",")] : [];
|
|
11794
|
+
const segmentFrames = Math.round(EXTRACTION_SEGMENT_SECONDS * fps);
|
|
11795
|
+
const totalFrames = extractionFrameCountForDuration(duration, normalizedFps, false);
|
|
11796
|
+
processResult = sampleCfrAtOutputFps && (options.frameRanges || totalFrames > segmentFrames) ? await runSegmentedExtraction({
|
|
11797
|
+
decodeArgs,
|
|
11798
|
+
filterAndEncodeArgs: [...filterArgs, ...encodeArgs],
|
|
11799
|
+
videoPath,
|
|
11800
|
+
startTime,
|
|
11801
|
+
fps,
|
|
11802
|
+
segments: splitFrameRanges(
|
|
11803
|
+
options.frameRanges ?? [{ firstFrame: 0, frames: totalFrames }],
|
|
11804
|
+
segmentFrames
|
|
11805
|
+
),
|
|
11806
|
+
runOptions
|
|
11807
|
+
}) : await runFfmpeg([...args, ...filterArgs, ...encodeArgs], runOptions);
|
|
11571
11808
|
}
|
|
11572
11809
|
if (processResult.failureReason === "external_interruption") {
|
|
11573
11810
|
throw new VideoSourceExtractionError(
|
|
@@ -11606,7 +11843,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
11606
11843
|
diagnostic
|
|
11607
11844
|
);
|
|
11608
11845
|
}
|
|
11609
|
-
const framePaths = framePathsFromDirectory(videoOutputDir, format);
|
|
11846
|
+
const framePaths = framePathsFromDirectory(videoOutputDir, format, !!options.frameRanges);
|
|
11610
11847
|
if (framePaths.size === 0 && duration > 0) {
|
|
11611
11848
|
throw new VideoSourceExtractionError(
|
|
11612
11849
|
"zero_output",
|
|
@@ -11615,7 +11852,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
11615
11852
|
`FFmpeg exited successfully but produced no frames (start=${startTime}, duration=${duration})`
|
|
11616
11853
|
);
|
|
11617
11854
|
}
|
|
11618
|
-
if (framePaths.size > 0 && duration > 0 && !options.finalFrameOnly) {
|
|
11855
|
+
if (framePaths.size > 0 && duration > 0 && !options.finalFrameOnly && !options.frameRanges) {
|
|
11619
11856
|
const expectedAtFps = extractionFrameCountForDuration(duration, normalizedFps, metadata.isVFR);
|
|
11620
11857
|
if (expectedAtFps > 0 && framePaths.size <= expectedAtFps * 0.6) {
|
|
11621
11858
|
const correctedDuration = startTime + framePaths.size / fps;
|
|
@@ -12298,6 +12535,57 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
12298
12535
|
}
|
|
12299
12536
|
return rehydratePublishedCache(work, cacheTarget);
|
|
12300
12537
|
}
|
|
12538
|
+
async function deferExtraction(work) {
|
|
12539
|
+
if (work.finalFrameOnly || work.metadata.isVFR || work.sdrToHdrTransfer !== void 0 || work.hdrToSdrTransformKey !== void 0) {
|
|
12540
|
+
return null;
|
|
12541
|
+
}
|
|
12542
|
+
const expectedFrames = extractionFrameCountForDuration(
|
|
12543
|
+
work.videoDuration,
|
|
12544
|
+
configuredFps,
|
|
12545
|
+
false
|
|
12546
|
+
);
|
|
12547
|
+
const tailFrames = Math.min(expectedFrames, Math.ceil(fps));
|
|
12548
|
+
if (expectedFrames <= tailFrames) return null;
|
|
12549
|
+
const probeDir = join10(options.outputDir, `${work.video.id}.tail-probe`);
|
|
12550
|
+
try {
|
|
12551
|
+
const tail = await extractVideoFramesRange(
|
|
12552
|
+
work.videoPath,
|
|
12553
|
+
work.video.id,
|
|
12554
|
+
work.extractionMediaStart,
|
|
12555
|
+
work.videoDuration,
|
|
12556
|
+
{
|
|
12557
|
+
...scopedExtractionOptions(work),
|
|
12558
|
+
frameRanges: [{ firstFrame: expectedFrames - tailFrames, frames: tailFrames }]
|
|
12559
|
+
},
|
|
12560
|
+
signal,
|
|
12561
|
+
config,
|
|
12562
|
+
probeDir
|
|
12563
|
+
);
|
|
12564
|
+
const totalFrames = expectedFrames - tailFrames + tail.totalFrames;
|
|
12565
|
+
if (totalFrames <= expectedFrames * 0.6) return null;
|
|
12566
|
+
const outputDir = join10(options.outputDir, work.video.id);
|
|
12567
|
+
mkdirSync6(outputDir, { recursive: true });
|
|
12568
|
+
return {
|
|
12569
|
+
videoId: work.video.id,
|
|
12570
|
+
srcPath: work.videoPath,
|
|
12571
|
+
outputDir,
|
|
12572
|
+
framePattern: `${FRAME_FILENAME_PREFIX}%05d.${work.format}`,
|
|
12573
|
+
fps,
|
|
12574
|
+
totalFrames,
|
|
12575
|
+
metadata: work.metadata,
|
|
12576
|
+
framePaths: /* @__PURE__ */ new Map(),
|
|
12577
|
+
deferredRange: {
|
|
12578
|
+
startTime: work.extractionMediaStart,
|
|
12579
|
+
durationSeconds: work.videoDuration,
|
|
12580
|
+
format: work.format
|
|
12581
|
+
}
|
|
12582
|
+
};
|
|
12583
|
+
} catch {
|
|
12584
|
+
return null;
|
|
12585
|
+
} finally {
|
|
12586
|
+
rmSync4(probeDir, { recursive: true, force: true });
|
|
12587
|
+
}
|
|
12588
|
+
}
|
|
12301
12589
|
async function executeDirectMiss(miss, maxTransientRetries = options.maxTransientRetries ?? 0) {
|
|
12302
12590
|
try {
|
|
12303
12591
|
return { result: await extractDirectMiss(miss, maxTransientRetries) };
|
|
@@ -12422,8 +12710,17 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
12422
12710
|
}
|
|
12423
12711
|
}
|
|
12424
12712
|
const uniqueOutcomes = /* @__PURE__ */ new Map();
|
|
12713
|
+
if (options.deferRangeExtraction) {
|
|
12714
|
+
const works = [...uniqueWorks.values()];
|
|
12715
|
+
const deferred = await Promise.all(works.map(deferExtraction));
|
|
12716
|
+
works.forEach((work, i) => {
|
|
12717
|
+
const result = deferred[i];
|
|
12718
|
+
if (result) uniqueOutcomes.set(work.dedupeKey, { result });
|
|
12719
|
+
});
|
|
12720
|
+
}
|
|
12425
12721
|
const cacheMisses = [];
|
|
12426
12722
|
for (const work of uniqueWorks.values()) {
|
|
12723
|
+
if (uniqueOutcomes.has(work.dedupeKey)) continue;
|
|
12427
12724
|
const lookup = lookupCacheFor(work);
|
|
12428
12725
|
if ("work" in lookup) {
|
|
12429
12726
|
cacheMisses.push(lookup);
|
|
@@ -12479,7 +12776,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
12479
12776
|
errors.push(item.error);
|
|
12480
12777
|
} else if ("result" in item) {
|
|
12481
12778
|
extracted.push(item.result);
|
|
12482
|
-
totalFramesExtracted += item.result.totalFrames;
|
|
12779
|
+
if (!item.result.deferredRange) totalFramesExtracted += item.result.totalFrames;
|
|
12483
12780
|
}
|
|
12484
12781
|
}
|
|
12485
12782
|
const sweepDue = breakdown.cacheMisses > 0 || cacheRootDir !== void 0 && gcSweepDue(cacheRootDir, GC_STALENESS_MS);
|
|
@@ -14716,7 +15013,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
14716
15013
|
}
|
|
14717
15014
|
|
|
14718
15015
|
// ../engine/src/services/parallelCoordinator.ts
|
|
14719
|
-
import { cpus, freemem } from "os";
|
|
15016
|
+
import { cpus as cpus2, freemem } from "os";
|
|
14720
15017
|
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync6 } from "fs";
|
|
14721
15018
|
import { copyFile, readFile as readFile2, rename } from "fs/promises";
|
|
14722
15019
|
import { join as join15 } from "path";
|
|
@@ -14831,7 +15128,7 @@ var MIN_WORKERS = 1;
|
|
|
14831
15128
|
var MAX_WORKER_DIAGNOSTIC_LINES = 8;
|
|
14832
15129
|
var ABSOLUTE_MAX_WORKERS = 24;
|
|
14833
15130
|
function defaultSafeMaxWorkers() {
|
|
14834
|
-
return Math.max(6, Math.min(16, Math.floor(
|
|
15131
|
+
return Math.max(6, Math.min(16, Math.floor(cpus2().length / 8)));
|
|
14835
15132
|
}
|
|
14836
15133
|
var MIN_FRAMES_PER_WORKER = 30;
|
|
14837
15134
|
function shouldDisableBrowserPoolForParallelWorker({
|
|
@@ -14876,7 +15173,7 @@ function formatWorkerFailure(result) {
|
|
|
14876
15173
|
return `${base}; diagnostics: ${diagnostics}`;
|
|
14877
15174
|
}
|
|
14878
15175
|
function computeWorkerSizing(totalFrames, requested, config) {
|
|
14879
|
-
const cpuCount =
|
|
15176
|
+
const cpuCount = cpus2().length;
|
|
14880
15177
|
const totalMemoryMb = getSystemTotalMb();
|
|
14881
15178
|
const heapLimitMb = Math.round(getHeapStatistics().heap_size_limit / (1024 * 1024));
|
|
14882
15179
|
const heapBasedWorkers = Math.max(
|
|
@@ -15434,7 +15731,7 @@ async function mergeWorkerFrames(workDir, tasks, outputDir) {
|
|
|
15434
15731
|
}
|
|
15435
15732
|
function getSystemResources() {
|
|
15436
15733
|
return {
|
|
15437
|
-
cpuCores:
|
|
15734
|
+
cpuCores: cpus2().length,
|
|
15438
15735
|
totalMemoryMB: getSystemTotalMb(),
|
|
15439
15736
|
freeMemoryMB: Math.round(freemem() / (1024 * 1024)),
|
|
15440
15737
|
recommendedWorkers: calculateOptimalWorkers(1e3)
|
|
@@ -15444,7 +15741,7 @@ function getSystemResources() {
|
|
|
15444
15741
|
// ../engine/src/services/fileServer.ts
|
|
15445
15742
|
import { Hono } from "hono";
|
|
15446
15743
|
import { serve } from "@hono/node-server";
|
|
15447
|
-
import { readFileSync as readFileSync7, openSync as openSync4, fstatSync, closeSync as closeSync4, statSync as
|
|
15744
|
+
import { readFileSync as readFileSync7, openSync as openSync4, fstatSync, closeSync as closeSync4, statSync as statSync7, constants } from "fs";
|
|
15448
15745
|
import { join as join16, extname as extname3 } from "path";
|
|
15449
15746
|
var MIME_TYPES = {
|
|
15450
15747
|
".html": "text/html; charset=utf-8",
|
|
@@ -15481,7 +15778,7 @@ function readRegularFile(filePath) {
|
|
|
15481
15778
|
} catch (error) {
|
|
15482
15779
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
|
|
15483
15780
|
return null;
|
|
15484
|
-
if (!
|
|
15781
|
+
if (!statSync7(filePath, { throwIfNoEntry: false })?.isFile()) return null;
|
|
15485
15782
|
throw error;
|
|
15486
15783
|
}
|
|
15487
15784
|
try {
|
|
@@ -15909,6 +16206,7 @@ export {
|
|
|
15909
16206
|
resolveConfig,
|
|
15910
16207
|
shouldClampToScreenshotForConcreteGpu,
|
|
15911
16208
|
applyConcreteGpuScreenshotClamp,
|
|
16209
|
+
SDR_RGB_TO_BT709_FILTER,
|
|
15912
16210
|
BrowserLeasePool,
|
|
15913
16211
|
setHostHandlesSigint,
|
|
15914
16212
|
resolveHeadlessShellPath,
|
|
@@ -15918,6 +16216,7 @@ export {
|
|
|
15918
16216
|
releaseBrowser,
|
|
15919
16217
|
drainBrowserPool,
|
|
15920
16218
|
closeBrowserPool,
|
|
16219
|
+
usesSoftwareWebGpu,
|
|
15921
16220
|
buildChromeArgs,
|
|
15922
16221
|
compositionRequiresWebGpu,
|
|
15923
16222
|
WebGpuUnavailableError,
|