hyperframes 0.8.107 → 0.8.109
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-DXZ3HNQI.js → add-7NAK3TQ6.js} +15 -15
- package/dist/{animatedGifPrep-2IQB4DGE.js → animatedGifPrep-NMGFOSIL.js} +6 -6
- package/dist/{asset-KZIG3MZN.js → asset-X7GUEAHC.js} +2 -2
- package/dist/{assetCataloger-4GE6PIUA.js → assetCataloger-FXEHOUA3.js} +2 -2
- package/dist/{auth-5IYF3H67.js → auth-EBWBTIGU.js} +2 -2
- package/dist/{autoUpdate-5C3FXM6Y.js → autoUpdate-DJBTMGAR.js} +2 -2
- package/dist/{backgroundChecks-75WGW4NB.js → backgroundChecks-QUMUVLYU.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-FKKS3YBX.js → batchRender-ZTHFMHZY.js} +3 -3
- package/dist/{beats-ISR36NSK.js → beats-XUYNHYGB.js} +18 -12
- package/dist/{benchmark-U6TQ6DS2.js → benchmark-L45UHXEE.js} +12 -12
- package/dist/{browser-VDS6QAVA.js → browser-EM76PRMN.js} +9 -9
- package/dist/{bundleWithLocalizedFonts-PMWWW5YG.js → bundleWithLocalizedFonts-4Q3ETTHW.js} +2 -2
- package/dist/{capture-BKA3UCZS.js → capture-4OPH4PMK.js} +7 -7
- package/dist/{capture-5KE34KCD.js → capture-OG6532RA.js} +2 -2
- package/dist/{catalog-ZXOSU27O.js → catalog-V42BPLQ4.js} +15 -15
- package/dist/{check-MZMZCXPN.js → check-3XQLHR2I.js} +30 -29
- package/dist/{checkBrowser-UIZJXYCQ.js → checkBrowser-56BV4TW3.js} +29 -28
- package/dist/{chunk-TI5Z4SWP.js → chunk-2IN77NP6.js} +3 -3
- package/dist/{chunk-WBVPQC7J.js → chunk-2JXJ5SGK.js} +1 -1
- package/dist/{chunk-QH6UFY2O.js → chunk-32P7FTFC.js} +4 -4
- package/dist/{chunk-7BI5BYMQ.js → chunk-3CPYTXZH.js} +5 -5
- package/dist/{chunk-OGTR33KN.js → chunk-3T4RQK6C.js} +2 -2
- package/dist/{chunk-HGL4WJDI.js → chunk-44UOKZIC.js} +9 -9
- package/dist/{chunk-KTPQ67TX.js → chunk-4PY6UPHN.js} +1 -1
- package/dist/{chunk-SGBOZ2HF.js → chunk-5ER3PYYP.js} +1 -1
- package/dist/{normalize-audio-J2PG6YXH.js → chunk-5ON5GH3X.js} +101 -209
- package/dist/{chunk-NHTRUAMD.js → chunk-AWUZ3AUD.js} +1 -1
- package/dist/{chunk-VMDEGITC.js → chunk-B25H4M4V.js} +1 -1
- package/dist/{chunk-QM2NUN2B.js → chunk-BAVM2XBR.js} +87 -3
- package/dist/{chunk-4W4UDPCH.js → chunk-C4TZBEHY.js} +2 -2
- package/dist/{chunk-FLUBCMNT.js → chunk-CFC3EEDQ.js} +1 -1
- package/dist/{chunk-3CYAZWF2.js → chunk-CPO5INJ5.js} +3 -3
- package/dist/{chunk-C3KEF3UA.js → chunk-CQRF7VU3.js} +1 -1
- package/dist/{chunk-6QWHIARJ.js → chunk-E4AYSXMS.js} +1 -1
- package/dist/{chunk-BRTGJ73L.js → chunk-HQGJLZB6.js} +2 -2
- package/dist/{chunk-HJCTKNXF.js → chunk-I5WWEBDG.js} +1 -1
- package/dist/{chunk-GLTHJJIF.js → chunk-ITBEN3QD.js} +2 -2
- package/dist/{chunk-3LBFLW7E.js → chunk-JTTAS7N6.js} +1 -1
- package/dist/{chunk-T4Z4UNSX.js → chunk-JWPSBANO.js} +3 -3
- package/dist/{chunk-P7XRDAWU.js → chunk-LAQEZHOH.js} +1 -1
- package/dist/{chunk-73VPF3SS.js → chunk-M224DAMG.js} +1 -1
- package/dist/{chunk-PU2IOWX4.js → chunk-MGFVNTIB.js} +2 -2
- package/dist/{chunk-NX6G66F4.js → chunk-MP25ID6H.js} +2 -2
- package/dist/{chunk-4DJXCVQJ.js → chunk-MR2LOG32.js} +577 -66
- package/dist/{chunk-3WG63KFW.js → chunk-N32ITJ7L.js} +25 -11
- package/dist/{chunk-D5EJCP26.js → chunk-OF3RKOPU.js} +79 -64
- package/dist/{chunk-J6CSEDK7.js → chunk-OORTZAJ7.js} +4 -4
- package/dist/{chunk-L7XDI3DT.js → chunk-RB7AHUOR.js} +1 -1
- package/dist/{chunk-YVEZT6WT.js → chunk-TBYUUOJG.js} +1 -1
- package/dist/{chunk-T3BOWXPX.js → chunk-TIZQC44K.js} +1 -1
- package/dist/{chunk-R4IB642V.js → chunk-U4TDAJZE.js} +2 -2
- package/dist/{chunk-TPPIERG3.js → chunk-WMN4NBJQ.js} +3 -3
- package/dist/{chunk-AO4PBESP.js → chunk-X2QJUTZL.js} +7 -7
- package/dist/{chunk-SKPL32FI.js → chunk-XKNYXDJN.js} +1 -1
- package/dist/{chunk-JNY3VJWB.js → chunk-XYVFPDNU.js} +1 -1
- package/dist/{chunk-Z4UAXNWV.js → chunk-YIXEH4TF.js} +2 -2
- package/dist/chunk-YLVI7VL5.js +3541 -0
- package/dist/{chunk-ADP6TMYP.js → chunk-YRTKK45P.js} +8 -8
- package/dist/{chunk-I73Z6OOE.js → chunk-ZKBSOLPY.js} +1 -1
- package/dist/{chunk-YY5J75H5.js → chunk-ZU54D6MI.js} +135 -5
- package/dist/{clean-46MZVA37.js → clean-45QMNXLN.js} +15 -14
- package/dist/cli.js +50 -50
- package/dist/{cloud-GAZQGPCW.js → cloud-YRYTNHMQ.js} +4 -4
- package/dist/{cloudrun-ZWL7SLDD.js → cloudrun-LVZ4OPPC.js} +3 -3
- package/dist/{compare-66AAJESM.js → compare-GODD3JD2.js} +24 -23
- package/dist/{compiler-3N7XQRH6.js → compiler-C32VXLE2.js} +3 -3
- package/dist/{component-3OCOHK2B.js → component-UMNTDLG3.js} +3 -3
- package/dist/{compositions-H2BP4N6F.js → compositions-EIRLTOYW.js} +11 -11
- package/dist/{delete-WNJLVPGO.js → delete-7P55EZOW.js} +2 -2
- package/dist/{dist-AY2RBEXL.js → dist-FMHQMEMX.js} +1 -1
- package/dist/{dist-HSEMXPG3.js → dist-YUIPB7OX.js} +8 -2
- package/dist/{doctor-IFOZS45N.js → doctor-UYSIF4PX.js} +10 -10
- package/dist/{events-6TH3FU3F.js → events-A2MYVK2Z.js} +9 -9
- package/dist/{events-5EKFJCCZ.js → events-LXYVOH3X.js} +8 -8
- package/dist/{feedback-QRQNMORT.js → feedback-46DWSEAZ.js} +10 -10
- package/dist/{figma-WONEPLUQ.js → figma-C5DHRI6P.js} +3 -3
- package/dist/fontLocalizeCli.js +8 -8
- package/dist/{get-C7FCYNI2.js → get-IRSUDICU.js} +2 -2
- package/dist/{gpuPolicy-BXXQF3VC.js → gpuPolicy-CA6KRFQQ.js} +6 -6
- package/dist/{grade-compare-SCCSZ5P3.js → grade-compare-BLHXB3UR.js} +24 -23
- package/dist/{help-FJOUNXVD.js → help-HMYF4Y3E.js} +1 -1
- package/dist/{history-OZY36NEC.js → history-K5BD64QP.js} +15 -14
- package/dist/hyperframe-runtime.js +54 -54
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +54 -54
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/{info-RG4SZQ2C.js → info-QR2FYDJ7.js} +11 -11
- package/dist/{init-YGZCDKSS.js → init-L6BY7IV4.js} +22 -22
- package/dist/{inspect-X547EZ4I.js → inspect-D3MC4VH3.js} +25 -24
- package/dist/{installer-6J24NTIV.js → installer-NBEN3ZIP.js} +4 -4
- package/dist/{keyframes-EIWEION2.js → keyframes-TPMFVG6O.js} +12 -12
- package/dist/{lambda-DSZDMP7P.js → lambda-BFHJMMCG.js} +4 -4
- package/dist/{layout-B7KCBLBH.js → layout-AOYCSBIL.js} +25 -24
- package/dist/{lint-RBDG4ERJ.js → lint-L4MN7QPF.js} +12 -12
- package/dist/{lintProject-CMPAR7BS.js → lintProject-X7L7EZWZ.js} +1 -1
- package/dist/{list-BPHPHLDJ.js → list-LKXCKBRX.js} +2 -2
- package/dist/{login-HV7KBW2J.js → login-4WEDFAIY.js} +4 -4
- package/dist/{media-treatment-RS6BZSQC.js → media-treatment-H6YAFA3H.js} +12 -12
- package/dist/{models-K4U7GRXV.js → models-RILNQV3Z.js} +7 -7
- package/dist/{motionShot-5AI2DALY.js → motionShot-5F4DCMWC.js} +10 -10
- package/dist/normalize-audio-DFK5FTE5.js +276 -0
- package/dist/{pipeline-HIUESSET.js → pipeline-5UY45ELH.js} +6 -6
- package/dist/{play-TTLQXBAH.js → play-HFD4CPWF.js} +20 -19
- package/dist/{preflight-63ODQ625.js → preflight-3V5IHGWQ.js} +7 -7
- package/dist/{present-XKGZ26FQ.js → present-JGDYENOT.js} +16 -15
- package/dist/{preview-O2YEKMYJ.js → preview-GLI7KBCA.js} +17 -17
- package/dist/{producer-ZTQIJO7E.js → producer-7JGAPEAF.js} +1 -1
- package/dist/{projectConfig-VVFH7XW5.js → projectConfig-JHEWPAA5.js} +6 -6
- package/dist/{publication-NALPAHDC.js → publication-7XA2H7UB.js} +2 -2
- package/dist/{publish-G7OL45EL.js → publish-257VZMWI.js} +8 -8
- package/dist/{remove-background-LOLKFKLT.js → remove-background-EV6XVADN.js} +7 -7
- package/dist/{render-I65IU7RM.js → render-4C3YKVZI.js} +3 -3
- package/dist/{render-ULQLUIWD.js → render-K6YASB4G.js} +12 -12
- package/dist/{render-EXSRG666.js → render-Y6LOQDCD.js} +21 -21
- package/dist/{render-batch-RSNCREPB.js → render-batch-AFDBPDMM.js} +3 -3
- package/dist/{renderCancellation-2WB4SGXR.js → renderCancellation-VIZELWDD.js} +6 -6
- package/dist/renderSetupWorker.js +1 -1
- package/dist/{resolver-CVN2IM2R.js → resolver-S72XBTEQ.js} +4 -4
- package/dist/{sherpa-OJUI54HG.js → sherpa-BAADWCHB.js} +7 -7
- package/dist/{skills-CPS5E5YP.js → skills-TEVVW6TR.js} +11 -11
- package/dist/{skillsUpdateCheck-3IT5CJWU.js → skillsUpdateCheck-4Y3NJPB5.js} +3 -3
- package/dist/{snapshot-MSGC7HTA.js → snapshot-63X5B7GR.js} +25 -24
- package/dist/{src-SV7CNZXN.js → src-GD7RGV5U.js} +5 -5
- package/dist/{src-EZBJT4VH.js → src-GRC63B6L.js} +7 -7
- package/dist/{staticProjectServer-5JAGMYT7.js → staticProjectServer-SFINO3DE.js} +14 -13
- package/dist/{status-4W3EJYEH.js → status-LXQL3L3H.js} +6 -6
- package/dist/studio/assets/{hyperframes-player-h4mktSdm.js → hyperframes-player-CJYtikAy.js} +1 -1
- package/dist/studio/assets/{index-lrT-gfHa.css → index-BQZBDqMs.css} +1 -1
- package/dist/studio/assets/{index-zoqPmBF6.js → index-CMSgrT2F.js} +1 -1
- package/dist/studio/assets/{index-CM1xA_jD.js → index-CYP6dMd7.js} +212 -210
- package/dist/studio/assets/{index-D8GKc2D9.js → index-HTFVHUJ4.js} +1 -1
- package/dist/studio/{chunk-NCUBAVGK.js → chunk-5VSXRGIX.js} +4 -4
- package/dist/studio/chunk-5VSXRGIX.js.map +1 -0
- package/dist/studio/chunk-6LUNLTHR.js +611 -0
- package/dist/studio/chunk-6LUNLTHR.js.map +1 -0
- package/dist/studio/{chunk-46XP7J3B.js → chunk-PQFEBX32.js} +5 -3
- package/dist/studio/chunk-PQFEBX32.js.map +1 -0
- package/dist/studio/{chunk-4OZC426H.js → chunk-QUXZVHOJ.js} +10 -504
- package/dist/studio/chunk-QUXZVHOJ.js.map +1 -0
- package/dist/studio/{domEditingLayers-AQBDFNUC.js → domEditingLayers-JQPQGGT7.js} +3 -3
- package/dist/studio/index.d.ts +53 -4
- package/dist/studio/index.html +2 -2
- package/dist/studio/index.js +15746 -13173
- package/dist/studio/index.js.map +1 -1
- package/dist/studio/player.js +3 -3
- package/dist/studio/styles.css +1 -1
- package/dist/studio/ui.js +1 -1
- package/dist/{studio-api-L2TJ2JTK.js → studio-api-WRSCXPWS.js} +8 -7
- package/dist/{studioServer-UV7YRYWN.js → studioServer-HPG3FVTJ.js} +44 -38
- package/dist/{system-OL4MXRPK.js → system-EEWYEHYX.js} +6 -6
- package/dist/{telemetry-WRUU725E.js → telemetry-UIZ5O7NL.js} +9 -9
- package/dist/{timeline-P43A53BW.js → timeline-L56HML42.js} +41 -21
- package/dist/{tokens-NUHTJM2V.js → tokens-DLPYLGLW.js} +2 -2
- package/dist/{transcribe-FSVNIFGF.js → transcribe-FBPTYMVZ.js} +12 -12
- package/dist/{transcribe-TRLZJK73.js → transcribe-GCKJF5A6.js} +7 -7
- package/dist/{updateCheck-33TAG46I.js → updateCheck-GQBMZGU3.js} +2 -2
- package/dist/{upgrade-AJ6QBTH2.js → upgrade-6LR7ZK3Q.js} +2 -2
- package/dist/{urlDownloader-MP6GJIDV.js → urlDownloader-NYXPEBUN.js} +5 -5
- package/dist/{validate-VL3A6VTJ.js → validate-VY6RJRHE.js} +15 -15
- package/dist/{video-KO7VT63P.js → video-KWQ5TSVD.js} +6 -6
- package/package.json +1 -1
- package/dist/chunk-PMLZPNIB.js +0 -3431
- package/dist/studio/chunk-46XP7J3B.js.map +0 -1
- package/dist/studio/chunk-4OZC426H.js.map +0 -1
- package/dist/studio/chunk-DI4LCJHL.js +0 -102
- package/dist/studio/chunk-DI4LCJHL.js.map +0 -1
- package/dist/studio/chunk-NCUBAVGK.js.map +0 -1
- /package/dist/studio/{domEditingLayers-AQBDFNUC.js.map → domEditingLayers-JQPQGGT7.js.map} +0 -0
|
@@ -5,23 +5,33 @@ var require = __hf_createRequire(import.meta.url);
|
|
|
5
5
|
var __filename = __hf_fileURLToPath(import.meta.url);
|
|
6
6
|
var __dirname = __hf_dirname(__filename);
|
|
7
7
|
import {
|
|
8
|
+
MEDIA_LINK_ATTR,
|
|
8
9
|
dedupeClonedCompositionId,
|
|
9
10
|
findTargetElement,
|
|
10
11
|
isHTMLElement,
|
|
11
12
|
parseSourceDocument,
|
|
12
13
|
patchElementInHtml,
|
|
13
14
|
probeElementInSource,
|
|
15
|
+
relinkSplitHalves,
|
|
16
|
+
relinkSplitHalvesInHtml,
|
|
14
17
|
removeElementFromHtml,
|
|
15
18
|
splitElementInHtml,
|
|
16
19
|
unwrapElementsFromHtml,
|
|
17
20
|
wrapElementsInHtml
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-ZU54D6MI.js";
|
|
19
22
|
import {
|
|
20
23
|
atomicTempPath,
|
|
21
24
|
createFileAtomically,
|
|
22
25
|
isAtomicTempPath,
|
|
23
26
|
replaceFileAtomically
|
|
24
27
|
} from "./chunk-WEATBQTR.js";
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_TARGET_LUFS,
|
|
30
|
+
absoluteLoudnessPlan,
|
|
31
|
+
measureAudio,
|
|
32
|
+
requiredFfmpeg,
|
|
33
|
+
resolveLocalAudioPath
|
|
34
|
+
} from "./chunk-5ON5GH3X.js";
|
|
25
35
|
import {
|
|
26
36
|
PROXY_PARAMS_VERSION,
|
|
27
37
|
PROXY_PENDING_RETRY_AFTER_SECONDS,
|
|
@@ -47,7 +57,7 @@ import {
|
|
|
47
57
|
shouldPrewarmProxy,
|
|
48
58
|
waitForProxy,
|
|
49
59
|
walkDir
|
|
50
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-MGFVNTIB.js";
|
|
51
61
|
import {
|
|
52
62
|
findTimelineScript,
|
|
53
63
|
parseGsapScriptAcorn
|
|
@@ -66,7 +76,7 @@ import {
|
|
|
66
76
|
formatRenderOutputTimestamp,
|
|
67
77
|
parseFps,
|
|
68
78
|
sameInstant
|
|
69
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-B25H4M4V.js";
|
|
70
80
|
import {
|
|
71
81
|
MagicString,
|
|
72
82
|
VALID_CANVAS_RESOLUTIONS,
|
|
@@ -75,15 +85,17 @@ import {
|
|
|
75
85
|
import {
|
|
76
86
|
addScenePartsManifest,
|
|
77
87
|
ensureHfIds
|
|
78
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-U4TDAJZE.js";
|
|
79
89
|
import {
|
|
80
90
|
findStartTags,
|
|
81
91
|
injectScriptsIntoHtml,
|
|
82
92
|
injectTagsAtHeadStart,
|
|
83
93
|
insertBeforeCloseTag,
|
|
84
94
|
isFullHtmlDocument,
|
|
95
|
+
resolveRateSpec,
|
|
96
|
+
sourceTimeAt,
|
|
85
97
|
stripEmbeddedRuntimeScripts
|
|
86
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-YLVI7VL5.js";
|
|
87
99
|
import {
|
|
88
100
|
ProjectRootMissingError,
|
|
89
101
|
folderGone,
|
|
@@ -96,12 +108,13 @@ import {
|
|
|
96
108
|
import {
|
|
97
109
|
isWithinProjectRoot,
|
|
98
110
|
readProjectFile
|
|
99
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-BAVM2XBR.js";
|
|
100
112
|
import {
|
|
101
113
|
FFPROBE_PATH_ENV,
|
|
102
114
|
findFfBinary
|
|
103
115
|
} from "./chunk-PJ3HDUCY.js";
|
|
104
116
|
import {
|
|
117
|
+
readMediaOffsetSeconds,
|
|
105
118
|
rewriteAssetPaths,
|
|
106
119
|
rewriteCssAssetUrls,
|
|
107
120
|
rewriteInlineStyleAssetUrls
|
|
@@ -200,7 +213,7 @@ function stampFileHfIds(filePath) {
|
|
|
200
213
|
}
|
|
201
214
|
}
|
|
202
215
|
|
|
203
|
-
// ../studio-server/dist/chunk-
|
|
216
|
+
// ../studio-server/dist/chunk-EPL4YKGI.js
|
|
204
217
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
205
218
|
import { mkdir as mkdir2, readdir as readdir3, rm as rm3, rmdir, writeFile } from "fs/promises";
|
|
206
219
|
import {
|
|
@@ -261,15 +274,21 @@ function onFileOverwritten(listener) {
|
|
|
261
274
|
overwriteListeners.add(listener);
|
|
262
275
|
return () => overwriteListeners.delete(listener);
|
|
263
276
|
}
|
|
264
|
-
function recordFileWriteReceipt(filePath, {
|
|
277
|
+
function recordFileWriteReceipt(filePath, {
|
|
278
|
+
overwrote,
|
|
279
|
+
path,
|
|
280
|
+
version,
|
|
281
|
+
writeToken
|
|
282
|
+
}) {
|
|
283
|
+
const receipt = { path, version, writeToken };
|
|
265
284
|
const absPath = realFilePath(filePath);
|
|
266
285
|
if (overwrote !== void 0)
|
|
267
286
|
for (const listener of overwriteListeners) listener(absPath, receipt.version, overwrote);
|
|
268
287
|
const now = Date.now();
|
|
269
|
-
for (const [
|
|
288
|
+
for (const [path2, list] of receipts) {
|
|
270
289
|
const live = list.filter((entry) => now - entry.recordedAt < RECEIPT_TTL_MS);
|
|
271
|
-
if (live.length > 0) receipts.set(
|
|
272
|
-
else receipts.delete(
|
|
290
|
+
if (live.length > 0) receipts.set(path2, live);
|
|
291
|
+
else receipts.delete(path2);
|
|
273
292
|
}
|
|
274
293
|
const from = overwrote === void 0 ? void 0 : fileContentVersion(overwrote);
|
|
275
294
|
receipts.set(absPath, [
|
|
@@ -5106,16 +5125,25 @@ import { createHash as createHash32, randomUUID as randomUUID22 } from "crypto";
|
|
|
5106
5125
|
import { readFileSync as readFileSync8 } from "fs";
|
|
5107
5126
|
import { existsSync as existsSync7, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
|
|
5108
5127
|
import { join as join12, resolve as resolve52 } from "path";
|
|
5128
|
+
import { execFile } from "child_process";
|
|
5129
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
|
|
5130
|
+
import { dirname as dirname52, join as join13, relative as relative4, sep as sep22 } from "path";
|
|
5131
|
+
import { createHash as createHash42, randomBytes } from "crypto";
|
|
5132
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5133
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
|
|
5134
|
+
import { join as join14, resolve as resolve6 } from "path";
|
|
5135
|
+
import { execFile as execFile2, spawn as spawn2 } from "child_process";
|
|
5136
|
+
import { promisify } from "util";
|
|
5109
5137
|
import { closeSync as closeSync32, constants as constants3, fstatSync as fstatSync3, openSync as openSync32, readSync } from "fs";
|
|
5110
5138
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
5111
|
-
import { existsSync as
|
|
5112
|
-
import { basename as basename22, dirname as
|
|
5113
|
-
import { existsSync as
|
|
5139
|
+
import { existsSync as existsSync11 } from "fs";
|
|
5140
|
+
import { basename as basename22, dirname as dirname6, extname as extname2, join as join15, posix } from "path";
|
|
5141
|
+
import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
|
|
5114
5142
|
import { homedir as homedir2 } from "os";
|
|
5115
|
-
import { join as
|
|
5116
|
-
import { createHash as
|
|
5117
|
-
import { readFileSync as
|
|
5118
|
-
import { join as
|
|
5143
|
+
import { join as join16 } from "path";
|
|
5144
|
+
import { createHash as createHash5 } from "crypto";
|
|
5145
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync5, rmSync as rmSync5 } from "fs";
|
|
5146
|
+
import { join as join17 } from "path";
|
|
5119
5147
|
async function filterCompositionFiles(projectDir, files) {
|
|
5120
5148
|
const htmlFiles = files.filter((f) => f.endsWith(".html") && !isInHiddenOrVendorDir(f));
|
|
5121
5149
|
const checks = await Promise.all(
|
|
@@ -6152,7 +6180,7 @@ function bakeGroupTransformIntoMembers(html, groupId, members, groupCenter) {
|
|
|
6152
6180
|
const rad = grot * Math.PI / 180;
|
|
6153
6181
|
const cos = Math.cos(rad);
|
|
6154
6182
|
const sin = Math.sin(rad);
|
|
6155
|
-
const
|
|
6183
|
+
const round32 = (n) => Math.round(n * 1e3) / 1e3;
|
|
6156
6184
|
let script = block.scriptText;
|
|
6157
6185
|
for (const m of members) {
|
|
6158
6186
|
const memberSel = `#${m.id}`;
|
|
@@ -6169,27 +6197,27 @@ function bakeGroupTransformIntoMembers(html, groupId, members, groupCenter) {
|
|
|
6169
6197
|
const visY = groupCenter.cy + gscale * (sin * dx + cos * dy) + gy;
|
|
6170
6198
|
const newProps = {
|
|
6171
6199
|
...mProps,
|
|
6172
|
-
x:
|
|
6173
|
-
y:
|
|
6200
|
+
x: round32(visX - m.cx),
|
|
6201
|
+
y: round32(visY - m.cy)
|
|
6174
6202
|
};
|
|
6175
6203
|
if (gz !== 0) newProps.z = (typeof mProps.z === "number" ? mProps.z : 0) + gz;
|
|
6176
6204
|
if (grot !== 0) {
|
|
6177
|
-
newProps.rotation =
|
|
6205
|
+
newProps.rotation = round32(
|
|
6178
6206
|
(typeof mProps.rotation === "number" ? mProps.rotation : 0) + grot
|
|
6179
6207
|
);
|
|
6180
6208
|
}
|
|
6181
6209
|
if (gscale !== 1) {
|
|
6182
|
-
newProps.scale =
|
|
6210
|
+
newProps.scale = round32((typeof mProps.scale === "number" ? mProps.scale : 1) * gscale);
|
|
6183
6211
|
}
|
|
6184
6212
|
const pivoted = /* @__PURE__ */ new Set(["x", "y", "z", "rotation", "scale"]);
|
|
6185
6213
|
for (const [k, v] of Object.entries(gt)) {
|
|
6186
6214
|
if (pivoted.has(k) || typeof v !== "number") continue;
|
|
6187
6215
|
if (k === "scaleX" || k === "scaleY") {
|
|
6188
|
-
if (v !== 1) newProps[k] =
|
|
6216
|
+
if (v !== 1) newProps[k] = round32((typeof mProps[k] === "number" ? mProps[k] : 1) * v);
|
|
6189
6217
|
} else if (k === "transformPerspective") {
|
|
6190
6218
|
if (typeof mProps[k] !== "number") newProps[k] = v;
|
|
6191
6219
|
} else if (v !== 0) {
|
|
6192
|
-
newProps[k] =
|
|
6220
|
+
newProps[k] = round32((typeof mProps[k] === "number" ? mProps[k] : 0) + v);
|
|
6193
6221
|
}
|
|
6194
6222
|
}
|
|
6195
6223
|
for (const s of [...sets].reverse()) {
|
|
@@ -6360,7 +6388,7 @@ async function prepareGsapMutationScript(c, res, firstMutation) {
|
|
|
6360
6388
|
let block = extractGsapScriptBlock(html);
|
|
6361
6389
|
if (!block && (firstMutation.type === "add" || firstMutation.type === "add-with-keyframes")) {
|
|
6362
6390
|
const compId = html.match(/data-composition-id="([^"]+)"/)?.[1] ?? "main";
|
|
6363
|
-
const { GSAP_CDN } = await import("./dist-
|
|
6391
|
+
const { GSAP_CDN } = await import("./dist-YUIPB7OX.js");
|
|
6364
6392
|
const bootstrap = [
|
|
6365
6393
|
`<script src="${GSAP_CDN}"></script>`,
|
|
6366
6394
|
"<script>",
|
|
@@ -7098,6 +7126,7 @@ async function foldAtomicCutFile(c, file, absPath, before, writer) {
|
|
|
7098
7126
|
let after = before;
|
|
7099
7127
|
let splitCount = 0;
|
|
7100
7128
|
const skippedSelectors = /* @__PURE__ */ new Set();
|
|
7129
|
+
const rightHalfIds = [];
|
|
7101
7130
|
const respond = (data, status) => status ? c.json(data, status) : c.json(data);
|
|
7102
7131
|
const orderedTargets = file.targets.map((cut, index) => ({ cut, index })).sort((left, right) => {
|
|
7103
7132
|
const locatorKey = (entry) => !entry.target.id && !entry.target.hfId && entry.target.selector ? entry.target.selector : null;
|
|
@@ -7128,6 +7157,7 @@ async function foldAtomicCutFile(c, file, absPath, before, writer) {
|
|
|
7128
7157
|
}
|
|
7129
7158
|
after = split.html;
|
|
7130
7159
|
splitCount++;
|
|
7160
|
+
rightHalfIds.push(split.newId);
|
|
7131
7161
|
if (!cut.originalId) continue;
|
|
7132
7162
|
const block = extractGsapScriptBlock(after);
|
|
7133
7163
|
if (!block) continue;
|
|
@@ -7154,6 +7184,7 @@ async function foldAtomicCutFile(c, file, absPath, before, writer) {
|
|
|
7154
7184
|
after = block.replaceScript(script);
|
|
7155
7185
|
}
|
|
7156
7186
|
}
|
|
7187
|
+
after = relinkSplitHalvesInHtml(after, rightHalfIds);
|
|
7157
7188
|
return {
|
|
7158
7189
|
path: file.path,
|
|
7159
7190
|
absPath,
|
|
@@ -8944,7 +8975,8 @@ function registerRenderRoutes(api, adapter) {
|
|
|
8944
8975
|
progress: current.progress,
|
|
8945
8976
|
status: current.status,
|
|
8946
8977
|
stage: current.stage,
|
|
8947
|
-
error: current.error
|
|
8978
|
+
error: current.error,
|
|
8979
|
+
...current.status === "complete" && current.audioLoweredDb !== void 0 ? { audioLoweredDb: current.audioLoweredDb } : {}
|
|
8948
8980
|
})
|
|
8949
8981
|
});
|
|
8950
8982
|
if (current.status !== "rendering") break;
|
|
@@ -9053,11 +9085,13 @@ function registerRenderRoutes(api, adapter) {
|
|
|
9053
9085
|
const metaPath = join10(rendersDir, `${rid}.meta.json`);
|
|
9054
9086
|
let status = "complete";
|
|
9055
9087
|
let durationMs;
|
|
9088
|
+
let audioLoweredDb;
|
|
9056
9089
|
if (existsSync6(metaPath)) {
|
|
9057
9090
|
try {
|
|
9058
9091
|
const meta = JSON.parse(readFileSync72(metaPath, "utf-8"));
|
|
9059
9092
|
if (meta.status === "failed" && !existsSync6(fp)) status = "failed";
|
|
9060
9093
|
if (meta.durationMs) durationMs = meta.durationMs;
|
|
9094
|
+
if (typeof meta.audioLoweredDb === "number") audioLoweredDb = meta.audioLoweredDb;
|
|
9061
9095
|
} catch {
|
|
9062
9096
|
}
|
|
9063
9097
|
}
|
|
@@ -9067,7 +9101,8 @@ function registerRenderRoutes(api, adapter) {
|
|
|
9067
9101
|
size: stat22.size,
|
|
9068
9102
|
createdAt: stat22.mtimeMs,
|
|
9069
9103
|
status,
|
|
9070
|
-
durationMs
|
|
9104
|
+
durationMs,
|
|
9105
|
+
...audioLoweredDb !== void 0 ? { audioLoweredDb } : {}
|
|
9071
9106
|
};
|
|
9072
9107
|
}).sort((a, b) => b.createdAt - a.createdAt);
|
|
9073
9108
|
for (const file of files) {
|
|
@@ -9100,10 +9135,10 @@ var ThumbnailGenerationCoordinator = class {
|
|
|
9100
9135
|
if (signal.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
|
|
9101
9136
|
let entry = this.entries.get(key);
|
|
9102
9137
|
if (!entry) {
|
|
9103
|
-
let
|
|
9138
|
+
let resolve7;
|
|
9104
9139
|
let reject;
|
|
9105
9140
|
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
9106
|
-
|
|
9141
|
+
resolve7 = resolvePromise;
|
|
9107
9142
|
reject = rejectPromise;
|
|
9108
9143
|
});
|
|
9109
9144
|
entry = {
|
|
@@ -9113,7 +9148,7 @@ var ThumbnailGenerationCoordinator = class {
|
|
|
9113
9148
|
state: "queued",
|
|
9114
9149
|
work,
|
|
9115
9150
|
promise,
|
|
9116
|
-
resolve:
|
|
9151
|
+
resolve: resolve7,
|
|
9117
9152
|
reject
|
|
9118
9153
|
};
|
|
9119
9154
|
this.entries.set(key, entry);
|
|
@@ -9127,7 +9162,7 @@ var ThumbnailGenerationCoordinator = class {
|
|
|
9127
9162
|
return /* @__PURE__ */ new Set([...this.entries.keys(), ...[...this.activeEntries].map((entry) => entry.key)]);
|
|
9128
9163
|
}
|
|
9129
9164
|
lease(entry, signal) {
|
|
9130
|
-
return new Promise((
|
|
9165
|
+
return new Promise((resolve7, reject) => {
|
|
9131
9166
|
let released = false;
|
|
9132
9167
|
const release = () => {
|
|
9133
9168
|
if (released) return;
|
|
@@ -9151,7 +9186,7 @@ var ThumbnailGenerationCoordinator = class {
|
|
|
9151
9186
|
entry.promise.then(
|
|
9152
9187
|
(value) => {
|
|
9153
9188
|
release();
|
|
9154
|
-
|
|
9189
|
+
resolve7(value);
|
|
9155
9190
|
},
|
|
9156
9191
|
(reason) => {
|
|
9157
9192
|
release();
|
|
@@ -9226,14 +9261,14 @@ async function generateThumbnail(path, signal) {
|
|
|
9226
9261
|
async function resolveSource(adapter, id, url) {
|
|
9227
9262
|
const project = await adapter.resolveProject(id);
|
|
9228
9263
|
if (!project) throw new HTTPException(404);
|
|
9229
|
-
let
|
|
9264
|
+
let relative5;
|
|
9230
9265
|
try {
|
|
9231
|
-
|
|
9266
|
+
relative5 = requestSubPath(url, "projects/:id/image-thumbnail");
|
|
9232
9267
|
} catch {
|
|
9233
9268
|
throw new HTTPException(400);
|
|
9234
9269
|
}
|
|
9235
|
-
if (!/\.jpe?g$/i.test(
|
|
9236
|
-
const path = pinWithinProject(project.dir,
|
|
9270
|
+
if (!/\.jpe?g$/i.test(relative5)) throw new HTTPException(415);
|
|
9271
|
+
const path = pinWithinProject(project.dir, relative5);
|
|
9237
9272
|
if (!path) throw new HTTPException(404);
|
|
9238
9273
|
const info = await stat2(path).catch(() => {
|
|
9239
9274
|
throw new HTTPException(404);
|
|
@@ -9574,6 +9609,479 @@ function registerWaveformRoutes(api, adapter) {
|
|
|
9574
9609
|
return c.json({ peaks });
|
|
9575
9610
|
});
|
|
9576
9611
|
}
|
|
9612
|
+
var FREEZE_HOLD_SECONDS = 2;
|
|
9613
|
+
var EPSILON = 1e-3;
|
|
9614
|
+
var round3 = (value) => Math.round(value * 1e3) / 1e3;
|
|
9615
|
+
function numberAttr(el, name) {
|
|
9616
|
+
const raw = el.getAttribute(name);
|
|
9617
|
+
if (raw === null || raw.trim() === "") return null;
|
|
9618
|
+
const value = Number(raw);
|
|
9619
|
+
return Number.isFinite(value) ? value : null;
|
|
9620
|
+
}
|
|
9621
|
+
function trackOf(el) {
|
|
9622
|
+
return numberAttr(el, "data-track-index") ?? numberAttr(el, "data-layer") ?? 0;
|
|
9623
|
+
}
|
|
9624
|
+
function freezeFrameMediaTime(input) {
|
|
9625
|
+
const rate = resolveRateSpec(input.automation, input.playbackRate);
|
|
9626
|
+
return input.mediaStart + sourceTimeAt(rate, Math.max(0, input.playhead - input.clipStart));
|
|
9627
|
+
}
|
|
9628
|
+
function freezeExtractArgs(src, mediaTime, output) {
|
|
9629
|
+
return ["-n", "-ss", String(round3(mediaTime)), "-i", src, "-frames:v", "1", output];
|
|
9630
|
+
}
|
|
9631
|
+
var randomStillToken = () => randomBytes(4).toString("hex");
|
|
9632
|
+
function freezeStillFileName(clipId, playhead, token = randomStillToken()) {
|
|
9633
|
+
const stem = clipId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 48) || "clip";
|
|
9634
|
+
const idHash = createHash42("sha256").update(clipId).digest("hex").slice(0, 10);
|
|
9635
|
+
return `${stem}-${idHash}-${Math.round(playhead * 1e3)}-${token}.png`;
|
|
9636
|
+
}
|
|
9637
|
+
function readFreezeSource(html, target, playhead) {
|
|
9638
|
+
const { document: document2 } = parseSourceDocument(html);
|
|
9639
|
+
const el = findTargetElement(document2, target);
|
|
9640
|
+
if (!el || el.tagName.toLowerCase() !== "video") return null;
|
|
9641
|
+
const src = el.getAttribute("src");
|
|
9642
|
+
const start = numberAttr(el, "data-start");
|
|
9643
|
+
const duration = numberAttr(el, "data-duration");
|
|
9644
|
+
if (!src || start === null || duration === null) return null;
|
|
9645
|
+
if (playhead <= start + EPSILON || playhead >= start + duration - EPSILON) return null;
|
|
9646
|
+
return {
|
|
9647
|
+
id: el.getAttribute("id") || "clip",
|
|
9648
|
+
src,
|
|
9649
|
+
mediaTime: freezeFrameMediaTime({
|
|
9650
|
+
clipStart: start,
|
|
9651
|
+
playhead,
|
|
9652
|
+
mediaStart: readMediaOffsetSeconds((name) => el.getAttribute(name)),
|
|
9653
|
+
playbackRate: numberAttr(el, "data-playback-rate") ?? 1,
|
|
9654
|
+
automation: el.getAttribute("data-automation")
|
|
9655
|
+
})
|
|
9656
|
+
};
|
|
9657
|
+
}
|
|
9658
|
+
function targetOf(el) {
|
|
9659
|
+
const hfId = el.getAttribute("data-hf-id");
|
|
9660
|
+
return { ...hfId ? { hfId } : {}, id: el.getAttribute("id") };
|
|
9661
|
+
}
|
|
9662
|
+
function spans(el, time) {
|
|
9663
|
+
const start = numberAttr(el, "data-start");
|
|
9664
|
+
const duration = numberAttr(el, "data-duration");
|
|
9665
|
+
if (start === null || duration === null) return false;
|
|
9666
|
+
return time > start + EPSILON && time < start + duration - EPSILON;
|
|
9667
|
+
}
|
|
9668
|
+
function uniqueId(document2, base) {
|
|
9669
|
+
let id = base;
|
|
9670
|
+
for (let n = 2; document2.getElementById(id); n++) id = `${base}-${n}`;
|
|
9671
|
+
return id;
|
|
9672
|
+
}
|
|
9673
|
+
function shiftTracks(document2, tracks, from, by) {
|
|
9674
|
+
for (const el of document2.querySelectorAll("[data-start]")) {
|
|
9675
|
+
if (el.hasAttribute("data-composition-id") || !tracks.has(trackOf(el))) continue;
|
|
9676
|
+
const start = numberAttr(el, "data-start");
|
|
9677
|
+
if (start === null || start < from - EPSILON) continue;
|
|
9678
|
+
el.setAttribute("data-start", String(round3(start + by)));
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
function growRootToFit(document2) {
|
|
9682
|
+
const root = document2.querySelector("[data-composition-id]");
|
|
9683
|
+
const rootDuration = root ? numberAttr(root, "data-duration") : null;
|
|
9684
|
+
if (!root || rootDuration === null) return;
|
|
9685
|
+
let end = rootDuration;
|
|
9686
|
+
for (const el of root.querySelectorAll("[data-start][data-duration]")) {
|
|
9687
|
+
const start = numberAttr(el, "data-start");
|
|
9688
|
+
const duration = numberAttr(el, "data-duration");
|
|
9689
|
+
if (start !== null && duration !== null) end = Math.max(end, start + duration);
|
|
9690
|
+
}
|
|
9691
|
+
if (end > rootDuration + EPSILON) root.setAttribute("data-duration", String(round3(end)));
|
|
9692
|
+
}
|
|
9693
|
+
var COPIED_PICTURE_ATTRS = ["class", "style", "data-color-grading"];
|
|
9694
|
+
function buildStill(document2, video, input) {
|
|
9695
|
+
const img = document2.createElement("img");
|
|
9696
|
+
img.setAttribute("id", input.id);
|
|
9697
|
+
for (const name of COPIED_PICTURE_ATTRS) {
|
|
9698
|
+
const value = video.getAttribute(name);
|
|
9699
|
+
if (value !== null) img.setAttribute(name, value);
|
|
9700
|
+
}
|
|
9701
|
+
if (!img.classList.contains("clip")) img.classList.add("clip");
|
|
9702
|
+
img.setAttribute("src", input.imageSrc);
|
|
9703
|
+
img.setAttribute("alt", "");
|
|
9704
|
+
img.setAttribute("data-start", String(round3(input.playhead)));
|
|
9705
|
+
img.setAttribute("data-duration", String(input.holdSeconds));
|
|
9706
|
+
img.setAttribute("data-track-index", String(trackOf(video)));
|
|
9707
|
+
img.setAttribute("data-timeline-label", "Freeze");
|
|
9708
|
+
return img;
|
|
9709
|
+
}
|
|
9710
|
+
function linkedPartners(document2, video) {
|
|
9711
|
+
const link = video.getAttribute(MEDIA_LINK_ATTR);
|
|
9712
|
+
if (!link) return [];
|
|
9713
|
+
return Array.from(document2.querySelectorAll(`[${MEDIA_LINK_ATTR}]`)).filter(
|
|
9714
|
+
(el) => el !== video && el.getAttribute(MEDIA_LINK_ATTR) === link
|
|
9715
|
+
);
|
|
9716
|
+
}
|
|
9717
|
+
function splitAll(html, elements, time) {
|
|
9718
|
+
const rightHalfIds = [];
|
|
9719
|
+
let next = html;
|
|
9720
|
+
for (const el of elements) {
|
|
9721
|
+
const split = splitElementInHtml(
|
|
9722
|
+
next,
|
|
9723
|
+
targetOf(el),
|
|
9724
|
+
time,
|
|
9725
|
+
`${el.getAttribute("id") || "clip"}-split`,
|
|
9726
|
+
{
|
|
9727
|
+
start: numberAttr(el, "data-start") ?? 0,
|
|
9728
|
+
duration: numberAttr(el, "data-duration") ?? 0,
|
|
9729
|
+
track: trackOf(el)
|
|
9730
|
+
}
|
|
9731
|
+
);
|
|
9732
|
+
if (!split.matched || !split.newId) return null;
|
|
9733
|
+
next = split.html;
|
|
9734
|
+
rightHalfIds.push(split.newId);
|
|
9735
|
+
}
|
|
9736
|
+
return { html: next, rightHalfIds };
|
|
9737
|
+
}
|
|
9738
|
+
function applyFreezeFrameToHtml(html, input) {
|
|
9739
|
+
const hold = input.holdSeconds ?? FREEZE_HOLD_SECONDS;
|
|
9740
|
+
const { document: before } = parseSourceDocument(html);
|
|
9741
|
+
const video = findTargetElement(before, input.target);
|
|
9742
|
+
if (!video || !isHTMLElement(video) || !spans(video, input.playhead)) return null;
|
|
9743
|
+
const partners = linkedPartners(before, video);
|
|
9744
|
+
const tracks = /* @__PURE__ */ new Set([trackOf(video), ...partners.map(trackOf)]);
|
|
9745
|
+
const cut = splitAll(
|
|
9746
|
+
html,
|
|
9747
|
+
[video, ...partners.filter((el) => spans(el, input.playhead))],
|
|
9748
|
+
input.playhead
|
|
9749
|
+
);
|
|
9750
|
+
if (!cut) return null;
|
|
9751
|
+
const { document: document2, wrappedFragment } = parseSourceDocument(cut.html);
|
|
9752
|
+
relinkSplitHalves(document2, cut.rightHalfIds);
|
|
9753
|
+
shiftTracks(document2, tracks, input.playhead, hold);
|
|
9754
|
+
const leftHalf = findTargetElement(document2, input.target);
|
|
9755
|
+
if (!leftHalf?.parentElement) return null;
|
|
9756
|
+
const freezeId = uniqueId(document2, `${video.getAttribute("id") || "clip"}-freeze`);
|
|
9757
|
+
const still = buildStill(document2, leftHalf, {
|
|
9758
|
+
id: freezeId,
|
|
9759
|
+
imageSrc: input.imageSrc,
|
|
9760
|
+
playhead: input.playhead,
|
|
9761
|
+
holdSeconds: hold
|
|
9762
|
+
});
|
|
9763
|
+
leftHalf.parentElement.insertBefore(still, leftHalf.nextSibling);
|
|
9764
|
+
growRootToFit(document2);
|
|
9765
|
+
return {
|
|
9766
|
+
html: ensureHfIds(wrappedFragment ? document2.body.innerHTML || "" : document2.toString()),
|
|
9767
|
+
freezeId
|
|
9768
|
+
};
|
|
9769
|
+
}
|
|
9770
|
+
var EXTRACT_TIMEOUT_MS = 3e4;
|
|
9771
|
+
var ffmpegExtractor = (args) => {
|
|
9772
|
+
const ffmpeg = findFfBinary("ffmpeg", { configuredMustExist: true });
|
|
9773
|
+
if (!ffmpeg) return Promise.resolve({ ok: false, error: "ffmpeg not found" });
|
|
9774
|
+
return new Promise((resolvePromise) => {
|
|
9775
|
+
execFile(
|
|
9776
|
+
ffmpeg,
|
|
9777
|
+
args,
|
|
9778
|
+
{ timeout: EXTRACT_TIMEOUT_MS, windowsHide: true },
|
|
9779
|
+
(error, _o, stderr) => resolvePromise(
|
|
9780
|
+
error ? { ok: false, error: String(stderr || error.message) } : { ok: true }
|
|
9781
|
+
)
|
|
9782
|
+
);
|
|
9783
|
+
});
|
|
9784
|
+
};
|
|
9785
|
+
function isFreezeFrameRequest(value) {
|
|
9786
|
+
if (typeof value !== "object" || value === null) return false;
|
|
9787
|
+
const body = value;
|
|
9788
|
+
return typeof body.path === "string" && body.path.length > 0 && typeof body.expectedVersion === "string" && typeof body.target === "object" && body.target !== null && typeof body.playhead === "number" && Number.isFinite(body.playhead);
|
|
9789
|
+
}
|
|
9790
|
+
var FREEZE_DIR = ["assets", "freeze"];
|
|
9791
|
+
function readExpected(absPath, expectedVersion) {
|
|
9792
|
+
let content;
|
|
9793
|
+
try {
|
|
9794
|
+
content = readFileSync11(absPath, "utf-8");
|
|
9795
|
+
} catch {
|
|
9796
|
+
return { error: "not found", status: 404 };
|
|
9797
|
+
}
|
|
9798
|
+
return fileContentVersion(content) === expectedVersion ? { content } : { error: "file conflict", status: 409 };
|
|
9799
|
+
}
|
|
9800
|
+
async function extractStill(projectDir, absPath, source, playhead, tools) {
|
|
9801
|
+
const fileDir = dirname52(absPath);
|
|
9802
|
+
const mediaPath = pinWithinProject(projectDir, relative4(projectDir, join13(fileDir, source.src)));
|
|
9803
|
+
if (!mediaPath) return { error: `forbidden media path: ${source.src}`, status: 403 };
|
|
9804
|
+
const freezeDir = join13(projectDir, ...FREEZE_DIR);
|
|
9805
|
+
const fileName = freezeStillFileName(source.id, playhead, tools.stillToken());
|
|
9806
|
+
mkdirWithinProject(projectDir, freezeDir);
|
|
9807
|
+
const imagePath = pinWithinProject(projectDir, join13(...FREEZE_DIR, fileName));
|
|
9808
|
+
if (!imagePath || dirname52(imagePath) !== freezeDir) {
|
|
9809
|
+
return { error: `forbidden freeze path: ${fileName}`, status: 403 };
|
|
9810
|
+
}
|
|
9811
|
+
if (existsSync8(imagePath))
|
|
9812
|
+
return { error: `freeze still already exists: ${fileName}`, status: 409 };
|
|
9813
|
+
const extracted = await tools.extract(freezeExtractArgs(mediaPath, source.mediaTime, imagePath));
|
|
9814
|
+
if (!extracted.ok) {
|
|
9815
|
+
return {
|
|
9816
|
+
error: `Could not extract the frame: ${extracted.error ?? "ffmpeg failed"}`,
|
|
9817
|
+
status: 500
|
|
9818
|
+
};
|
|
9819
|
+
}
|
|
9820
|
+
const depth = relative4(projectDir, fileDir).split(sep22).filter(Boolean).length;
|
|
9821
|
+
return { imageSrc: `${"../".repeat(depth)}${FREEZE_DIR.join("/")}/${fileName}` };
|
|
9822
|
+
}
|
|
9823
|
+
function writeFolded(projectDir, absPath, path, before, after, options) {
|
|
9824
|
+
if (readFileSync11(absPath, "utf-8") !== before) {
|
|
9825
|
+
return { error: `file conflict: ${path}`, status: 409 };
|
|
9826
|
+
}
|
|
9827
|
+
const backup = snapshotBeforeWrite(projectDir, absPath);
|
|
9828
|
+
if (backup.error) return { error: `Failed to create backup: ${backup.error}`, status: 500 };
|
|
9829
|
+
const writeToken = createWriteToken(options.token);
|
|
9830
|
+
replaceFileAtomically(absPath, after, statSync8(absPath).mode);
|
|
9831
|
+
const version = fileContentVersion(after);
|
|
9832
|
+
recordFileWriteReceipt(absPath, { path, version, writeToken, overwrote: before });
|
|
9833
|
+
return { version, writeToken, backupPath: backupPathForResponse(projectDir, backup.backupPath) };
|
|
9834
|
+
}
|
|
9835
|
+
function registerFreezeFrameRoutes(api, adapter, extract = ffmpegExtractor, stillToken = randomStillToken) {
|
|
9836
|
+
api.post("/projects/:id/file-mutations/freeze-frame", async (c) => {
|
|
9837
|
+
const body = await c.req.json().catch(() => null);
|
|
9838
|
+
if (!isFreezeFrameRequest(body)) {
|
|
9839
|
+
return c.json({ error: "path, expectedVersion, target and playhead required" }, 400);
|
|
9840
|
+
}
|
|
9841
|
+
const project = await adapter.resolveProject(c.req.param("id"));
|
|
9842
|
+
if (!project) return c.json({ error: "not found" }, 404);
|
|
9843
|
+
const absPath = pinWithinProject(project.dir, body.path);
|
|
9844
|
+
if (!absPath) return c.json({ error: `forbidden path: ${body.path}` }, 403);
|
|
9845
|
+
const read = readExpected(absPath, body.expectedVersion);
|
|
9846
|
+
if ("error" in read) return c.json({ error: `${read.error}: ${body.path}` }, read.status);
|
|
9847
|
+
const before = read.content;
|
|
9848
|
+
const source = readFreezeSource(before, body.target, body.playhead);
|
|
9849
|
+
if (!source) return c.json({ error: "Move the playhead inside a video clip to freeze" }, 400);
|
|
9850
|
+
const still = await extractStill(project.dir, absPath, source, body.playhead, {
|
|
9851
|
+
extract,
|
|
9852
|
+
stillToken
|
|
9853
|
+
});
|
|
9854
|
+
if ("error" in still) return c.json({ error: still.error }, still.status);
|
|
9855
|
+
const { imageSrc } = still;
|
|
9856
|
+
const folded = applyFreezeFrameToHtml(before, {
|
|
9857
|
+
target: body.target,
|
|
9858
|
+
playhead: body.playhead,
|
|
9859
|
+
imageSrc
|
|
9860
|
+
});
|
|
9861
|
+
if (!folded) return c.json({ error: "Freeze target was not found in the file" }, 400);
|
|
9862
|
+
const written = writeFolded(project.dir, absPath, body.path, before, folded.html, {
|
|
9863
|
+
token: body.transactionToken ?? c.req.header("X-Hyperframes-Write-Token")
|
|
9864
|
+
});
|
|
9865
|
+
if ("error" in written) return c.json({ error: written.error }, written.status);
|
|
9866
|
+
const { version, writeToken, backupPath } = written;
|
|
9867
|
+
return c.json({
|
|
9868
|
+
ok: true,
|
|
9869
|
+
path: body.path,
|
|
9870
|
+
before,
|
|
9871
|
+
after: folded.html,
|
|
9872
|
+
version,
|
|
9873
|
+
writeToken,
|
|
9874
|
+
backupPath,
|
|
9875
|
+
freezeId: folded.freezeId,
|
|
9876
|
+
imageSrc
|
|
9877
|
+
});
|
|
9878
|
+
});
|
|
9879
|
+
}
|
|
9880
|
+
var defaultMeasure = (file, window2) => measureAudio(requiredFfmpeg(), file, window2);
|
|
9881
|
+
var InvalidField = class extends Error {
|
|
9882
|
+
};
|
|
9883
|
+
function numberField(body, name, fallback, valid) {
|
|
9884
|
+
const raw = body[name];
|
|
9885
|
+
if (raw === void 0 || raw === null) return fallback;
|
|
9886
|
+
if (typeof raw === "number" && Number.isFinite(raw) && valid(raw)) return raw;
|
|
9887
|
+
throw new InvalidField(`${name} is out of range`);
|
|
9888
|
+
}
|
|
9889
|
+
function readNormalizeRequest(body) {
|
|
9890
|
+
const src = typeof body.src === "string" ? body.src.trim() : "";
|
|
9891
|
+
if (!src) throw new InvalidField("src is required");
|
|
9892
|
+
const duration = numberField(body, "duration", 0, (v) => v > 0);
|
|
9893
|
+
return {
|
|
9894
|
+
id: typeof body.id === "string" && body.id ? body.id : src,
|
|
9895
|
+
src,
|
|
9896
|
+
window: {
|
|
9897
|
+
mediaStart: numberField(body, "mediaStart", 0, (v) => v >= 0),
|
|
9898
|
+
duration: duration > 0 ? duration : null,
|
|
9899
|
+
playbackRate: numberField(body, "playbackRate", 1, (v) => v > 0)
|
|
9900
|
+
},
|
|
9901
|
+
volume: numberField(body, "volume", 1, (v) => v >= 0),
|
|
9902
|
+
targetLufs: numberField(body, "targetLufs", DEFAULT_TARGET_LUFS, (v) => v < 0)
|
|
9903
|
+
};
|
|
9904
|
+
}
|
|
9905
|
+
function parseNormalizeRequest(body) {
|
|
9906
|
+
try {
|
|
9907
|
+
return readNormalizeRequest(body);
|
|
9908
|
+
} catch (error) {
|
|
9909
|
+
if (error instanceof InvalidField) return error.message;
|
|
9910
|
+
throw error;
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
function isRecord(value) {
|
|
9914
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9915
|
+
}
|
|
9916
|
+
function registerLoudnessRoutes(api, adapter, measure = defaultMeasure) {
|
|
9917
|
+
api.post("/projects/:id/loudness/normalize", async (c) => {
|
|
9918
|
+
const project = await adapter.resolveProject(c.req.param("id"));
|
|
9919
|
+
if (!project) return c.json({ error: "not found" }, 404);
|
|
9920
|
+
const body = await c.req.json().catch(() => null);
|
|
9921
|
+
if (!isRecord(body)) return c.json({ error: "invalid body" }, 400);
|
|
9922
|
+
const request = parseNormalizeRequest(body);
|
|
9923
|
+
if (typeof request === "string") return c.json({ error: request }, 400);
|
|
9924
|
+
let file;
|
|
9925
|
+
try {
|
|
9926
|
+
file = resolveLocalAudioPath(project.dir, request.src);
|
|
9927
|
+
} catch (error) {
|
|
9928
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);
|
|
9929
|
+
}
|
|
9930
|
+
if (!existsSync9(file)) return c.json({ error: "file not found" }, 404);
|
|
9931
|
+
try {
|
|
9932
|
+
const measurement = await measure(file, request.window);
|
|
9933
|
+
const plan = absoluteLoudnessPlan(
|
|
9934
|
+
{ id: request.id, volume: request.volume, ...measurement },
|
|
9935
|
+
request.targetLufs
|
|
9936
|
+
);
|
|
9937
|
+
return c.json({ plan });
|
|
9938
|
+
} catch (error) {
|
|
9939
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 422);
|
|
9940
|
+
}
|
|
9941
|
+
});
|
|
9942
|
+
}
|
|
9943
|
+
var PEAK_BIN_SECONDS = 0.05;
|
|
9944
|
+
var PEAK_SAMPLE_RATE = 48e3;
|
|
9945
|
+
var execFileAsync = promisify(execFile2);
|
|
9946
|
+
var PEAK_MAP_CACHE_VERSION = "peaks-v2";
|
|
9947
|
+
function createPeakAccumulator(sampleRate = PEAK_SAMPLE_RATE, binSeconds = PEAK_BIN_SECONDS, channels = 2) {
|
|
9948
|
+
return {
|
|
9949
|
+
bins: [],
|
|
9950
|
+
frame: 0,
|
|
9951
|
+
carry: Buffer.alloc(0),
|
|
9952
|
+
framesPerBin: Math.max(1, Math.round(sampleRate * binSeconds)),
|
|
9953
|
+
channels: Math.max(1, channels)
|
|
9954
|
+
};
|
|
9955
|
+
}
|
|
9956
|
+
function framePeak(data, offset, channels) {
|
|
9957
|
+
let peak = 0;
|
|
9958
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
9959
|
+
peak = Math.max(peak, Math.abs(data.readFloatLE(offset + ch * 4)));
|
|
9960
|
+
}
|
|
9961
|
+
return peak;
|
|
9962
|
+
}
|
|
9963
|
+
function accumulatePeaks(acc, chunk) {
|
|
9964
|
+
const data = acc.carry.length > 0 ? Buffer.concat([acc.carry, chunk]) : chunk;
|
|
9965
|
+
const frameBytes = 4 * acc.channels;
|
|
9966
|
+
const whole = data.length - data.length % frameBytes;
|
|
9967
|
+
for (let offset = 0; offset < whole; offset += frameBytes) {
|
|
9968
|
+
const peak = framePeak(data, offset, acc.channels);
|
|
9969
|
+
const bin = Math.floor(acc.frame / acc.framesPerBin);
|
|
9970
|
+
if (acc.bins.length <= bin) acc.bins.push(0);
|
|
9971
|
+
if (peak > (acc.bins[bin] ?? 0)) acc.bins[bin] = peak;
|
|
9972
|
+
acc.frame += 1;
|
|
9973
|
+
}
|
|
9974
|
+
acc.carry = Buffer.from(data.subarray(whole));
|
|
9975
|
+
}
|
|
9976
|
+
function finishPeaks(acc) {
|
|
9977
|
+
return acc.bins.map((peak) => Number(peak.toFixed(4)));
|
|
9978
|
+
}
|
|
9979
|
+
async function probeChannels(mediaPath) {
|
|
9980
|
+
const { stdout } = await execFileAsync(
|
|
9981
|
+
findFfBinary("ffprobe") ?? "ffprobe",
|
|
9982
|
+
[
|
|
9983
|
+
"-v",
|
|
9984
|
+
"error",
|
|
9985
|
+
"-select_streams",
|
|
9986
|
+
"a:0",
|
|
9987
|
+
"-show_entries",
|
|
9988
|
+
"stream=channels",
|
|
9989
|
+
"-of",
|
|
9990
|
+
"csv=p=0",
|
|
9991
|
+
"--",
|
|
9992
|
+
mediaPath
|
|
9993
|
+
],
|
|
9994
|
+
{ encoding: "utf8", timeout: 3e4, windowsHide: true }
|
|
9995
|
+
);
|
|
9996
|
+
const channels = Number.parseInt(stdout.trim(), 10);
|
|
9997
|
+
if (!Number.isFinite(channels) || channels < 1) throw new Error("no audio stream");
|
|
9998
|
+
return channels;
|
|
9999
|
+
}
|
|
10000
|
+
async function decodePeakMap(mediaPath) {
|
|
10001
|
+
const channels = await probeChannels(mediaPath);
|
|
10002
|
+
return new Promise((resolvePromise, reject) => {
|
|
10003
|
+
const proc = spawn2(
|
|
10004
|
+
findFfBinary("ffmpeg") ?? "ffmpeg",
|
|
10005
|
+
[
|
|
10006
|
+
"-v",
|
|
10007
|
+
"error",
|
|
10008
|
+
"-i",
|
|
10009
|
+
mediaPath,
|
|
10010
|
+
"-map",
|
|
10011
|
+
"0:a:0",
|
|
10012
|
+
"-vn",
|
|
10013
|
+
"-ar",
|
|
10014
|
+
String(PEAK_SAMPLE_RATE),
|
|
10015
|
+
"-f",
|
|
10016
|
+
"f32le",
|
|
10017
|
+
"pipe:1"
|
|
10018
|
+
],
|
|
10019
|
+
{ stdio: ["ignore", "pipe", "ignore"], windowsHide: true }
|
|
10020
|
+
);
|
|
10021
|
+
const acc = createPeakAccumulator(PEAK_SAMPLE_RATE, PEAK_BIN_SECONDS, channels);
|
|
10022
|
+
proc.stdout?.on("data", (chunk) => accumulatePeaks(acc, chunk));
|
|
10023
|
+
proc.on("close", (code) => {
|
|
10024
|
+
if (code !== 0) {
|
|
10025
|
+
reject(new Error(`ffmpeg failed decoding audio (exit ${code})`));
|
|
10026
|
+
return;
|
|
10027
|
+
}
|
|
10028
|
+
if (acc.frame === 0) {
|
|
10029
|
+
reject(new Error("ffmpeg produced no audio samples"));
|
|
10030
|
+
return;
|
|
10031
|
+
}
|
|
10032
|
+
resolvePromise(finishPeaks(acc));
|
|
10033
|
+
});
|
|
10034
|
+
proc.on("error", reject);
|
|
10035
|
+
});
|
|
10036
|
+
}
|
|
10037
|
+
function buildPeakMapCacheKey(assetPath, fingerprint) {
|
|
10038
|
+
const name = assetPath.replace(/[/\\]/g, "_");
|
|
10039
|
+
return `${PEAK_MAP_CACHE_VERSION}_${name}_${fingerprint.size}-${Math.round(fingerprint.mtimeMs)}.json`;
|
|
10040
|
+
}
|
|
10041
|
+
var decodesInFlight = /* @__PURE__ */ new Map();
|
|
10042
|
+
function decodeOnce(cachePath, mediaPath, decode) {
|
|
10043
|
+
const pending = decodesInFlight.get(cachePath);
|
|
10044
|
+
if (pending) return pending;
|
|
10045
|
+
const started = decode(mediaPath).finally(() => decodesInFlight.delete(cachePath));
|
|
10046
|
+
decodesInFlight.set(cachePath, started);
|
|
10047
|
+
return started;
|
|
10048
|
+
}
|
|
10049
|
+
function readCachedBins(cacheDir, cachePath) {
|
|
10050
|
+
try {
|
|
10051
|
+
if (!isWaveformCacheDirectory(cacheDir) || !existsSync10(cachePath)) return null;
|
|
10052
|
+
const parsed = JSON.parse(readFileSync12(cachePath, "utf-8"));
|
|
10053
|
+
return Array.isArray(parsed) && parsed.every((n) => typeof n === "number") ? parsed : null;
|
|
10054
|
+
} catch {
|
|
10055
|
+
return null;
|
|
10056
|
+
}
|
|
10057
|
+
}
|
|
10058
|
+
function registerPeakRoutes(api, adapter, decode = decodePeakMap) {
|
|
10059
|
+
api.get("/projects/:id/peaks/*", async (c) => {
|
|
10060
|
+
const project = await adapter.resolveProject(c.req.param("id"));
|
|
10061
|
+
if (!project) return c.json({ error: "not found" }, 404);
|
|
10062
|
+
const assetPath = requestSubPath(c.req.url, "projects/:id/peaks");
|
|
10063
|
+
const mediaPath = resolve6(project.dir, assetPath);
|
|
10064
|
+
if (!isWithinProjectRoot(project.dir, mediaPath))
|
|
10065
|
+
return c.json({ error: "file not found" }, 404);
|
|
10066
|
+
const stats = statSync9(mediaPath, { throwIfNoEntry: false });
|
|
10067
|
+
if (!stats?.isFile()) return c.json({ error: "file not found" }, 404);
|
|
10068
|
+
const cacheDir = join14(project.dir, ".waveform-cache");
|
|
10069
|
+
const cachePath = join14(cacheDir, buildPeakMapCacheKey(assetPath, stats));
|
|
10070
|
+
const cached = readCachedBins(cacheDir, cachePath);
|
|
10071
|
+
if (cached) return c.json({ binSeconds: PEAK_BIN_SECONDS, bins: cached });
|
|
10072
|
+
let bins;
|
|
10073
|
+
try {
|
|
10074
|
+
bins = await decodeOnce(cachePath, mediaPath, decode);
|
|
10075
|
+
} catch {
|
|
10076
|
+
return c.json({ error: "failed to decode audio" }, 500);
|
|
10077
|
+
}
|
|
10078
|
+
try {
|
|
10079
|
+
writeWaveformCache(cachePath, bins);
|
|
10080
|
+
} catch {
|
|
10081
|
+
}
|
|
10082
|
+
return c.json({ binSeconds: PEAK_BIN_SECONDS, bins });
|
|
10083
|
+
});
|
|
10084
|
+
}
|
|
9577
10085
|
var MAX_FONT_RESULTS = 2e3;
|
|
9578
10086
|
var GOOGLE_FONTS_METADATA_URL = "https://fonts.google.com/metadata/fonts";
|
|
9579
10087
|
var GOOGLE_FONTS_FETCH_TIMEOUT_MS = 3e3;
|
|
@@ -9601,7 +10109,7 @@ var GOOGLE_FONT_FALLBACKS = [
|
|
|
9601
10109
|
"Outfit",
|
|
9602
10110
|
"JetBrains Mono"
|
|
9603
10111
|
];
|
|
9604
|
-
function
|
|
10112
|
+
function isRecord2(value) {
|
|
9605
10113
|
return typeof value === "object" && value !== null;
|
|
9606
10114
|
}
|
|
9607
10115
|
function collectFontsFromDir(dir) {
|
|
@@ -9625,10 +10133,10 @@ function listInstalledFontFamilies() {
|
|
|
9625
10133
|
return cachedFonts;
|
|
9626
10134
|
}
|
|
9627
10135
|
function parseGoogleFontMetadata(value) {
|
|
9628
|
-
if (!
|
|
10136
|
+
if (!isRecord2(value) || !Array.isArray(value.familyMetadataList)) return [];
|
|
9629
10137
|
const families = [];
|
|
9630
10138
|
for (const entry of value.familyMetadataList) {
|
|
9631
|
-
if (!
|
|
10139
|
+
if (!isRecord2(entry) || typeof entry.family !== "string") continue;
|
|
9632
10140
|
families.push(entry.family);
|
|
9633
10141
|
}
|
|
9634
10142
|
return families;
|
|
@@ -9728,14 +10236,14 @@ function registerRegistryRoutes(api, adapter) {
|
|
|
9728
10236
|
}
|
|
9729
10237
|
});
|
|
9730
10238
|
}
|
|
9731
|
-
function
|
|
10239
|
+
function isRecord3(value) {
|
|
9732
10240
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9733
10241
|
}
|
|
9734
10242
|
function isFiniteNumber(value) {
|
|
9735
10243
|
return typeof value === "number" && Number.isFinite(value);
|
|
9736
10244
|
}
|
|
9737
10245
|
function isStringRecord(value) {
|
|
9738
|
-
return
|
|
10246
|
+
return isRecord3(value) && Object.values(value).every((v) => typeof v === "string");
|
|
9739
10247
|
}
|
|
9740
10248
|
function hasString(value, key) {
|
|
9741
10249
|
return typeof value[key] === "string";
|
|
@@ -9753,20 +10261,20 @@ function hasOptionalNumber(value, key) {
|
|
|
9753
10261
|
return value[key] === void 0 || isFiniteNumber(value[key]);
|
|
9754
10262
|
}
|
|
9755
10263
|
function isBoundingBox(value) {
|
|
9756
|
-
return
|
|
10264
|
+
return isRecord3(value) && ["x", "y", "width", "height"].every((key) => isFiniteNumber(value[key]));
|
|
9757
10265
|
}
|
|
9758
10266
|
function isTarget(value) {
|
|
9759
|
-
if (!
|
|
10267
|
+
if (!isRecord3(value)) return false;
|
|
9760
10268
|
return hasOptionalNullableString(value, "id") && hasOptionalString(value, "hfId") && hasOptionalString(value, "selector") && hasOptionalNumber(value, "selectorIndex");
|
|
9761
10269
|
}
|
|
9762
10270
|
function isTextField(value) {
|
|
9763
|
-
return
|
|
10271
|
+
return isRecord3(value) && hasRequiredStrings(value, ["key", "label", "value", "tagName"]) && ["self", "child", "text-node"].includes(value.source);
|
|
9764
10272
|
}
|
|
9765
10273
|
function isTextFields(value) {
|
|
9766
10274
|
return Array.isArray(value) && value.every(isTextField);
|
|
9767
10275
|
}
|
|
9768
10276
|
function isSelectionSnapshot(value) {
|
|
9769
|
-
if (!
|
|
10277
|
+
if (!isRecord3(value)) return false;
|
|
9770
10278
|
const checks = [
|
|
9771
10279
|
value.schemaVersion === 1 && hasRequiredStrings(value, [
|
|
9772
10280
|
"projectId",
|
|
@@ -9784,7 +10292,7 @@ function isSelectionSnapshot(value) {
|
|
|
9784
10292
|
isStringRecord(value.inlineStyles),
|
|
9785
10293
|
isStringRecord(value.computedStyles),
|
|
9786
10294
|
isTextFields(value.textFields),
|
|
9787
|
-
|
|
10295
|
+
isRecord3(value.capabilities)
|
|
9788
10296
|
];
|
|
9789
10297
|
return checks.every(Boolean);
|
|
9790
10298
|
}
|
|
@@ -9808,7 +10316,7 @@ function registerSelectionRoutes(api, adapter) {
|
|
|
9808
10316
|
} catch {
|
|
9809
10317
|
return c.json({ error: "invalid json" }, 400);
|
|
9810
10318
|
}
|
|
9811
|
-
if (!
|
|
10319
|
+
if (!isRecord3(body) || !("selection" in body)) {
|
|
9812
10320
|
return c.json({ error: "missing selection" }, 400);
|
|
9813
10321
|
}
|
|
9814
10322
|
if (body.selection === null) {
|
|
@@ -9860,7 +10368,7 @@ function uniqueAssetPath(projectDir, assetPath, taken) {
|
|
|
9860
10368
|
const ext = extname2(assetPath);
|
|
9861
10369
|
const withoutExt = assetPath.slice(0, -ext.length);
|
|
9862
10370
|
let candidate = assetPath;
|
|
9863
|
-
for (let index = 2; taken.has(candidate) ||
|
|
10371
|
+
for (let index = 2; taken.has(candidate) || existsSync11(join15(projectDir, candidate)); index++) {
|
|
9864
10372
|
candidate = `${withoutExt}-${index}${ext}`;
|
|
9865
10373
|
}
|
|
9866
10374
|
return candidate;
|
|
@@ -9921,7 +10429,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
9921
10429
|
}
|
|
9922
10430
|
const filePath = resolveWithinProject(project.dir, assetPath);
|
|
9923
10431
|
if (!filePath) return c.json({ error: "forbidden" }, 403);
|
|
9924
|
-
if (!
|
|
10432
|
+
if (!existsSync11(filePath)) return c.json({ error: "media not found" }, 404);
|
|
9925
10433
|
return c.json({ path: assetPath, metadata: await readMediaMetadata(filePath) });
|
|
9926
10434
|
});
|
|
9927
10435
|
api.post(
|
|
@@ -9943,7 +10451,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
9943
10451
|
}
|
|
9944
10452
|
const inputPath = resolveWithinProject(project.dir, inputAssetPath);
|
|
9945
10453
|
if (!inputPath) return c.json({ error: "forbidden" }, 403);
|
|
9946
|
-
if (!
|
|
10454
|
+
if (!existsSync11(inputPath)) return c.json({ error: "input media not found" }, 404);
|
|
9947
10455
|
const inputIsVideo = isVideoPath(inputAssetPath);
|
|
9948
10456
|
const inputIsImage = isImagePath(inputAssetPath);
|
|
9949
10457
|
if (!inputIsVideo && !inputIsImage) {
|
|
@@ -9979,8 +10487,8 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
9979
10487
|
return c.json({ error: "forbidden" }, 403);
|
|
9980
10488
|
}
|
|
9981
10489
|
}
|
|
9982
|
-
mkdirWithinProject(project.dir,
|
|
9983
|
-
if (backgroundOutputPath) mkdirWithinProject(project.dir,
|
|
10490
|
+
mkdirWithinProject(project.dir, dirname6(outputPath));
|
|
10491
|
+
if (backgroundOutputPath) mkdirWithinProject(project.dir, dirname6(backgroundOutputPath));
|
|
9984
10492
|
const jobId = makeJobId(project.id, mediaJobs);
|
|
9985
10493
|
const state = adapter.startBackgroundRemoval({
|
|
9986
10494
|
project,
|
|
@@ -10036,10 +10544,10 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
10036
10544
|
});
|
|
10037
10545
|
}
|
|
10038
10546
|
function readGlobalAssets(home = homedir2()) {
|
|
10039
|
-
const manifestPath =
|
|
10040
|
-
if (!
|
|
10547
|
+
const manifestPath = join16(home, ".media", "manifest.jsonl");
|
|
10548
|
+
if (!existsSync12(manifestPath)) return [];
|
|
10041
10549
|
const out = [];
|
|
10042
|
-
for (const line of
|
|
10550
|
+
for (const line of readFileSync13(manifestPath, "utf8").split("\n")) {
|
|
10043
10551
|
if (!line.trim()) continue;
|
|
10044
10552
|
try {
|
|
10045
10553
|
const rec = JSON.parse(line);
|
|
@@ -10222,6 +10730,9 @@ function createStudioApi(adapter) {
|
|
|
10222
10730
|
registerSelectionRoutes(api, adapter);
|
|
10223
10731
|
registerMediaRoutes(api, adapter);
|
|
10224
10732
|
registerWaveformRoutes(api, adapter);
|
|
10733
|
+
registerFreezeFrameRoutes(api, adapter);
|
|
10734
|
+
registerLoudnessRoutes(api, adapter);
|
|
10735
|
+
registerPeakRoutes(api, adapter);
|
|
10225
10736
|
registerFontRoutes(api);
|
|
10226
10737
|
registerRegistryRoutes(api, adapter);
|
|
10227
10738
|
registerGlobalAssetRoutes(api);
|
|
@@ -10237,23 +10748,23 @@ function duplicateElementInHtml(source, target, newId, at) {
|
|
|
10237
10748
|
if (duration === null || numericAttribute(element, "data-start") === null) {
|
|
10238
10749
|
return { html: source, matched: false, newId: null };
|
|
10239
10750
|
}
|
|
10240
|
-
const
|
|
10751
|
+
const uniqueId2 = nextUniqueId(document2, newId);
|
|
10241
10752
|
rippleElements(document2, element, at, duration, track);
|
|
10242
10753
|
const clone = element.cloneNode(true);
|
|
10243
10754
|
if (!isHTMLElement(clone)) return { html: source, matched: false, newId: null };
|
|
10244
|
-
clone.setAttribute("id",
|
|
10755
|
+
clone.setAttribute("id", uniqueId2);
|
|
10245
10756
|
dedupeClonedCompositionId(document2, clone);
|
|
10246
10757
|
stripStableIds(clone);
|
|
10247
10758
|
clone.setAttribute("data-start", String(at));
|
|
10248
10759
|
element.parentElement.insertBefore(clone, element.nextSibling);
|
|
10249
10760
|
const html = wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
|
|
10250
|
-
return { html: ensureHfIds(html), matched: true, newId:
|
|
10761
|
+
return { html: ensureHfIds(html), matched: true, newId: uniqueId2 };
|
|
10251
10762
|
}
|
|
10252
10763
|
function nextUniqueId(document2, newId) {
|
|
10253
|
-
let
|
|
10764
|
+
let uniqueId2 = newId;
|
|
10254
10765
|
let suffix = 2;
|
|
10255
|
-
while (document2.getElementById(
|
|
10256
|
-
return
|
|
10766
|
+
while (document2.getElementById(uniqueId2)) uniqueId2 = `${newId}-${suffix++}`;
|
|
10767
|
+
return uniqueId2;
|
|
10257
10768
|
}
|
|
10258
10769
|
function rippleElements(document2, element, at, duration, track) {
|
|
10259
10770
|
for (const candidate of Array.from(document2.querySelectorAll("[data-start][data-duration]"))) {
|
|
@@ -10278,13 +10789,13 @@ function numericAttribute(element, name) {
|
|
|
10278
10789
|
return Number.isFinite(value) ? value : null;
|
|
10279
10790
|
}
|
|
10280
10791
|
function createPreviewDocumentStore(projectDir, salt) {
|
|
10281
|
-
const dir =
|
|
10282
|
-
const fileFor = (key) =>
|
|
10792
|
+
const dir = join17(projectDir, ".hyperframes", "preview");
|
|
10793
|
+
const fileFor = (key) => join17(dir, `${createHash5("sha256").update(`${salt}
|
|
10283
10794
|
${key}`).digest("hex").slice(0, 32)}.html`);
|
|
10284
10795
|
return {
|
|
10285
10796
|
read(key) {
|
|
10286
10797
|
try {
|
|
10287
|
-
return
|
|
10798
|
+
return readFileSync14(fileFor(key), "utf-8");
|
|
10288
10799
|
} catch {
|
|
10289
10800
|
return null;
|
|
10290
10801
|
}
|
|
@@ -10295,7 +10806,7 @@ ${key}`).digest("hex").slice(0, 32)}.html`);
|
|
|
10295
10806
|
const file = fileFor(key);
|
|
10296
10807
|
replaceFileAtomically(file, html, 420);
|
|
10297
10808
|
for (const name of readdirSync5(dir)) {
|
|
10298
|
-
if (
|
|
10809
|
+
if (join17(dir, name) !== file) rmSync5(join17(dir, name), { force: true });
|
|
10299
10810
|
}
|
|
10300
10811
|
} catch {
|
|
10301
10812
|
}
|