@hyperframes/studio-server 0.8.116 → 0.8.118
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/index.js +52 -36
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3258,6 +3258,7 @@ import {
|
|
|
3258
3258
|
stripEmbeddedRuntimeScripts as stripEmbeddedRuntimeScripts2
|
|
3259
3259
|
} from "@hyperframes/core/compiler";
|
|
3260
3260
|
import { STUDIO_PREVIEW_MARK_META } from "@hyperframes/core/studio-preview-mark";
|
|
3261
|
+
import { gsapCdnDist as gsapCdnDist2, motionPathPluginUrl } from "@hyperframes/core/gsap-cdn";
|
|
3261
3262
|
import { injectTagsAtHeadStart } from "@hyperframes/core/compiler/html-document";
|
|
3262
3263
|
import { isWithinProjectRoot } from "@hyperframes/parsers/asset-resolution";
|
|
3263
3264
|
|
|
@@ -3272,6 +3273,7 @@ import {
|
|
|
3272
3273
|
} from "@hyperframes/core";
|
|
3273
3274
|
import { stripEmbeddedRuntimeScripts } from "@hyperframes/core/compiler";
|
|
3274
3275
|
import { isFullHtmlDocument } from "@hyperframes/core/compiler/html-document";
|
|
3276
|
+
import { gsapCdnDist } from "@hyperframes/core/gsap-cdn";
|
|
3275
3277
|
function rewriteRelativePaths(root, compPath, projectDir) {
|
|
3276
3278
|
const assetExists = (path) => existsSync4(join6(projectDir, path));
|
|
3277
3279
|
rewriteAssetPaths(
|
|
@@ -3421,7 +3423,7 @@ ${compHeadContent}`;
|
|
|
3421
3423
|
}
|
|
3422
3424
|
if (!headContent.includes("gsap")) {
|
|
3423
3425
|
headContent += `
|
|
3424
|
-
<script src="
|
|
3426
|
+
<script src="${gsapCdnDist()}gsap.min.js"></script>`;
|
|
3425
3427
|
}
|
|
3426
3428
|
const htmlOpen = htmlAttrs ? `<html ${htmlAttrs}>` : "<html>";
|
|
3427
3429
|
const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : "<body>";
|
|
@@ -3572,10 +3574,9 @@ function lazyPreviewImages(html) {
|
|
|
3572
3574
|
|
|
3573
3575
|
// src/routes/preview.ts
|
|
3574
3576
|
var PROJECT_SIGNATURE_META = "hyperframes-project-signature";
|
|
3575
|
-
var
|
|
3576
|
-
var
|
|
3577
|
-
var
|
|
3578
|
-
var GSAP_MOTION_PATH_CDN_SCRIPT = `<script src="https://cdn.jsdelivr.net/npm/gsap@${GSAP_CDN_VERSION}/dist/MotionPathPlugin.min.js"></script>`;
|
|
3577
|
+
var GSAP_CDN_SCRIPT = `<script src="${gsapCdnDist2()}gsap.min.js"></script>`;
|
|
3578
|
+
var GSAP_CUSTOM_EASE_CDN_SCRIPT = `<script src="${gsapCdnDist2()}CustomEase.min.js"></script>`;
|
|
3579
|
+
var GSAP_MOTION_PATH_CDN_SCRIPT = `<script src="${motionPathPluginUrl()}"></script>`;
|
|
3579
3580
|
function injectProjectSignature(html, signature) {
|
|
3580
3581
|
const tag = `<meta name="${PROJECT_SIGNATURE_META}" content="${signature}">`;
|
|
3581
3582
|
if (html.includes(`name="${PROJECT_SIGNATURE_META}"`)) {
|
|
@@ -3632,8 +3633,8 @@ function injectMotionPathPluginIfNeeded(html) {
|
|
|
3632
3633
|
const gsapScript = /<script\b[^>]*\bsrc=["'][^"']*\/gsap(\.min)?\.js["'][^>]*>\s*<\/script>/i;
|
|
3633
3634
|
const match = html.match(gsapScript);
|
|
3634
3635
|
if (match) {
|
|
3635
|
-
const version = match[0].match(/gsap@([\d.]+)/)?.[1]
|
|
3636
|
-
const pluginTag = `<script src="
|
|
3636
|
+
const version = match[0].match(/gsap@([\d.]+)/)?.[1];
|
|
3637
|
+
const pluginTag = `<script src="${motionPathPluginUrl(version)}"></script>`;
|
|
3637
3638
|
const end = html.indexOf(match[0]) + match[0].length;
|
|
3638
3639
|
return html.slice(0, end) + "\n" + pluginTag + html.slice(end);
|
|
3639
3640
|
}
|
|
@@ -3665,7 +3666,7 @@ function injectStudioMotionScript(html, projectDir, activeCompositionPath) {
|
|
|
3665
3666
|
var GSAP_FALLBACK_ATTR = "data-hf-gsap-fallback";
|
|
3666
3667
|
var GSAP_CDN_FALLBACK_SCRIPT = `<script ${GSAP_FALLBACK_ATTR}>
|
|
3667
3668
|
(function(){
|
|
3668
|
-
var cdnBase="
|
|
3669
|
+
var cdnBase="${gsapCdnDist2()}";
|
|
3669
3670
|
var loaded={};
|
|
3670
3671
|
function loadFallback(file){
|
|
3671
3672
|
if(loaded[file])return loaded[file];
|
|
@@ -4890,7 +4891,7 @@ function registerWaveformRoutes(api, adapter) {
|
|
|
4890
4891
|
|
|
4891
4892
|
// src/routes/freezeFrame.ts
|
|
4892
4893
|
import { execFile } from "child_process";
|
|
4893
|
-
import {
|
|
4894
|
+
import { closeSync as closeSync3, openSync as openSync3, readFileSync as readFileSync11, rmSync as rmSync5, statSync as statSync8 } from "fs";
|
|
4894
4895
|
import { dirname as dirname5, join as join13, relative as relative4, sep as sep2 } from "path";
|
|
4895
4896
|
import { findFfBinary as findFfBinary3 } from "@hyperframes/parsers/ff-binaries";
|
|
4896
4897
|
import { replaceFileAtomically as replaceFileAtomically3 } from "@hyperframes/core/atomic-file";
|
|
@@ -4918,7 +4919,7 @@ function freezeFrameMediaTime(input) {
|
|
|
4918
4919
|
return input.mediaStart + sourceTimeAt(rate, Math.max(0, input.playhead - input.clipStart));
|
|
4919
4920
|
}
|
|
4920
4921
|
function freezeExtractArgs(src, mediaTime, output) {
|
|
4921
|
-
return ["-
|
|
4922
|
+
return ["-y", "-ss", String(round3(mediaTime)), "-i", src, "-frames:v", "1", output];
|
|
4922
4923
|
}
|
|
4923
4924
|
var randomStillToken = () => randomBytes(4).toString("hex");
|
|
4924
4925
|
function freezeStillFileName(clipId, playhead, token = randomStillToken()) {
|
|
@@ -5091,6 +5092,15 @@ function readExpected(absPath, expectedVersion) {
|
|
|
5091
5092
|
}
|
|
5092
5093
|
return fileContentVersion(content) === expectedVersion ? { content } : { error: "file conflict", status: 409 };
|
|
5093
5094
|
}
|
|
5095
|
+
function claimStillNameBeforeExtracting(path) {
|
|
5096
|
+
try {
|
|
5097
|
+
closeSync3(openSync3(path, "wx"));
|
|
5098
|
+
return true;
|
|
5099
|
+
} catch (error) {
|
|
5100
|
+
if (error.code === "EEXIST") return false;
|
|
5101
|
+
throw error;
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5094
5104
|
async function extractStill(projectDir, absPath, source, playhead, tools) {
|
|
5095
5105
|
const fileDir = dirname5(absPath);
|
|
5096
5106
|
const mediaPath = pinWithinProject(projectDir, relative4(projectDir, join13(fileDir, source.src)));
|
|
@@ -5102,17 +5112,18 @@ async function extractStill(projectDir, absPath, source, playhead, tools) {
|
|
|
5102
5112
|
if (!imagePath || dirname5(imagePath) !== freezeDir) {
|
|
5103
5113
|
return { error: `forbidden freeze path: ${fileName}`, status: 403 };
|
|
5104
5114
|
}
|
|
5105
|
-
if (
|
|
5115
|
+
if (!claimStillNameBeforeExtracting(imagePath))
|
|
5106
5116
|
return { error: `freeze still already exists: ${fileName}`, status: 409 };
|
|
5107
5117
|
const extracted = await tools.extract(freezeExtractArgs(mediaPath, source.mediaTime, imagePath));
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
};
|
|
5118
|
+
const partialOrNoFrame = !extracted.ok || statSync8(imagePath).size === 0;
|
|
5119
|
+
if (partialOrNoFrame) {
|
|
5120
|
+
rmSync5(imagePath, { force: true });
|
|
5121
|
+
const reason = extracted.ok ? "no frame at this time" : extracted.error ?? "ffmpeg failed";
|
|
5122
|
+
return { error: `Could not extract the frame: ${reason}`, status: 500 };
|
|
5113
5123
|
}
|
|
5114
5124
|
const depth = relative4(projectDir, fileDir).split(sep2).filter(Boolean).length;
|
|
5115
|
-
|
|
5125
|
+
const stillPath = `${FREEZE_DIR.join("/")}/${fileName}`;
|
|
5126
|
+
return { imageSrc: `${"../".repeat(depth)}${stillPath}`, stillPath, imagePath };
|
|
5116
5127
|
}
|
|
5117
5128
|
function writeFolded(projectDir, absPath, path, before, after, options) {
|
|
5118
5129
|
if (readFileSync11(absPath, "utf-8") !== before) {
|
|
@@ -5146,17 +5157,21 @@ function registerFreezeFrameRoutes(api, adapter, extract = ffmpegExtractor, stil
|
|
|
5146
5157
|
stillToken
|
|
5147
5158
|
});
|
|
5148
5159
|
if ("error" in still) return c.json({ error: still.error }, still.status);
|
|
5149
|
-
const { imageSrc } = still;
|
|
5160
|
+
const { imageSrc, stillPath, imagePath } = still;
|
|
5150
5161
|
const folded = applyFreezeFrameToHtml(before, {
|
|
5151
5162
|
target: body.target,
|
|
5152
5163
|
playhead: body.playhead,
|
|
5153
5164
|
imageSrc
|
|
5154
5165
|
});
|
|
5155
|
-
|
|
5166
|
+
const failAndRemoveStill = (error, status) => {
|
|
5167
|
+
rmSync5(imagePath, { force: true });
|
|
5168
|
+
return c.json({ error }, status);
|
|
5169
|
+
};
|
|
5170
|
+
if (!folded) return failAndRemoveStill("Freeze target was not found in the file", 400);
|
|
5156
5171
|
const written = writeFolded(project.dir, absPath, body.path, before, folded.html, {
|
|
5157
5172
|
token: body.transactionToken ?? c.req.header("X-Hyperframes-Write-Token")
|
|
5158
5173
|
});
|
|
5159
|
-
if ("error" in written) return
|
|
5174
|
+
if ("error" in written) return failAndRemoveStill(written.error, written.status);
|
|
5160
5175
|
const { version, writeToken, backupPath } = written;
|
|
5161
5176
|
return c.json({
|
|
5162
5177
|
ok: true,
|
|
@@ -5167,13 +5182,14 @@ function registerFreezeFrameRoutes(api, adapter, extract = ffmpegExtractor, stil
|
|
|
5167
5182
|
writeToken,
|
|
5168
5183
|
backupPath,
|
|
5169
5184
|
freezeId: folded.freezeId,
|
|
5170
|
-
imageSrc
|
|
5185
|
+
imageSrc,
|
|
5186
|
+
stillPath
|
|
5171
5187
|
});
|
|
5172
5188
|
});
|
|
5173
5189
|
}
|
|
5174
5190
|
|
|
5175
5191
|
// src/routes/loudness.ts
|
|
5176
|
-
import { existsSync as
|
|
5192
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5177
5193
|
var defaultMeasure = (file, window) => measureAudio(requiredFfmpeg(), file, window);
|
|
5178
5194
|
var InvalidField = class extends Error {
|
|
5179
5195
|
};
|
|
@@ -5224,7 +5240,7 @@ function registerLoudnessRoutes(api, adapter, measure = defaultMeasure) {
|
|
|
5224
5240
|
} catch (error) {
|
|
5225
5241
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 400);
|
|
5226
5242
|
}
|
|
5227
|
-
if (!
|
|
5243
|
+
if (!existsSync8(file)) return c.json({ error: "file not found" }, 404);
|
|
5228
5244
|
try {
|
|
5229
5245
|
const measurement = await measure(file, request.window);
|
|
5230
5246
|
const plan = absoluteLoudnessPlan(
|
|
@@ -5239,7 +5255,7 @@ function registerLoudnessRoutes(api, adapter, measure = defaultMeasure) {
|
|
|
5239
5255
|
}
|
|
5240
5256
|
|
|
5241
5257
|
// src/routes/peaks.ts
|
|
5242
|
-
import { existsSync as
|
|
5258
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
|
|
5243
5259
|
import { join as join14, resolve as resolve6 } from "path";
|
|
5244
5260
|
import { isWithinProjectRoot as isWithinProjectRoot3 } from "@hyperframes/parsers/asset-resolution";
|
|
5245
5261
|
|
|
@@ -5357,7 +5373,7 @@ function decodeOnce(cachePath, mediaPath, decode) {
|
|
|
5357
5373
|
}
|
|
5358
5374
|
function readCachedBins(cacheDir, cachePath) {
|
|
5359
5375
|
try {
|
|
5360
|
-
if (!isWaveformCacheDirectory(cacheDir) || !
|
|
5376
|
+
if (!isWaveformCacheDirectory(cacheDir) || !existsSync9(cachePath)) return null;
|
|
5361
5377
|
const parsed = JSON.parse(readFileSync12(cachePath, "utf-8"));
|
|
5362
5378
|
return Array.isArray(parsed) && parsed.every((n) => typeof n === "number") ? parsed : null;
|
|
5363
5379
|
} catch {
|
|
@@ -5393,7 +5409,7 @@ function registerPeakRoutes(api, adapter, decode = decodePeakMap) {
|
|
|
5393
5409
|
}
|
|
5394
5410
|
|
|
5395
5411
|
// src/routes/fonts.ts
|
|
5396
|
-
import { closeSync as
|
|
5412
|
+
import { closeSync as closeSync4, constants, fstatSync as fstatSync3, openSync as openSync4, readSync } from "fs";
|
|
5397
5413
|
import {
|
|
5398
5414
|
collectFontFileEntries,
|
|
5399
5415
|
fontDirectories,
|
|
@@ -5500,7 +5516,7 @@ function registerFontRoutes(api) {
|
|
|
5500
5516
|
if (!located) return c.json({ error: "font not found" }, 404);
|
|
5501
5517
|
let fd;
|
|
5502
5518
|
try {
|
|
5503
|
-
fd =
|
|
5519
|
+
fd = openSync4(located.path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
5504
5520
|
} catch {
|
|
5505
5521
|
return c.json({ error: "font file not accessible" }, 404);
|
|
5506
5522
|
}
|
|
@@ -5522,7 +5538,7 @@ function registerFontRoutes(api) {
|
|
|
5522
5538
|
} catch {
|
|
5523
5539
|
return c.json({ error: "failed to read font file" }, 500);
|
|
5524
5540
|
} finally {
|
|
5525
|
-
|
|
5541
|
+
closeSync4(fd);
|
|
5526
5542
|
}
|
|
5527
5543
|
});
|
|
5528
5544
|
}
|
|
@@ -5658,7 +5674,7 @@ function registerSelectionRoutes(api, adapter) {
|
|
|
5658
5674
|
|
|
5659
5675
|
// src/routes/media.ts
|
|
5660
5676
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
5661
|
-
import { existsSync as
|
|
5677
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5662
5678
|
import { basename as basename2, dirname as dirname6, extname, join as join15, posix } from "path";
|
|
5663
5679
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5664
5680
|
".mp4",
|
|
@@ -5696,7 +5712,7 @@ function uniqueAssetPath(projectDir, assetPath, taken) {
|
|
|
5696
5712
|
const ext = extname(assetPath);
|
|
5697
5713
|
const withoutExt = assetPath.slice(0, -ext.length);
|
|
5698
5714
|
let candidate = assetPath;
|
|
5699
|
-
for (let index = 2; taken.has(candidate) ||
|
|
5715
|
+
for (let index = 2; taken.has(candidate) || existsSync10(join15(projectDir, candidate)); index++) {
|
|
5700
5716
|
candidate = `${withoutExt}-${index}${ext}`;
|
|
5701
5717
|
}
|
|
5702
5718
|
return candidate;
|
|
@@ -5757,7 +5773,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
5757
5773
|
}
|
|
5758
5774
|
const filePath = resolveWithinProject(project.dir, assetPath);
|
|
5759
5775
|
if (!filePath) return c.json({ error: "forbidden" }, 403);
|
|
5760
|
-
if (!
|
|
5776
|
+
if (!existsSync10(filePath)) return c.json({ error: "media not found" }, 404);
|
|
5761
5777
|
return c.json({ path: assetPath, metadata: await readMediaMetadata(filePath) });
|
|
5762
5778
|
});
|
|
5763
5779
|
api.post(
|
|
@@ -5779,7 +5795,7 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
5779
5795
|
}
|
|
5780
5796
|
const inputPath = resolveWithinProject(project.dir, inputAssetPath);
|
|
5781
5797
|
if (!inputPath) return c.json({ error: "forbidden" }, 403);
|
|
5782
|
-
if (!
|
|
5798
|
+
if (!existsSync10(inputPath)) return c.json({ error: "input media not found" }, 404);
|
|
5783
5799
|
const inputIsVideo = isVideoPath(inputAssetPath);
|
|
5784
5800
|
const inputIsImage = isImagePath(inputAssetPath);
|
|
5785
5801
|
if (!inputIsVideo && !inputIsImage) {
|
|
@@ -5873,12 +5889,12 @@ function registerMediaRoutes(api, adapter, options = {}) {
|
|
|
5873
5889
|
}
|
|
5874
5890
|
|
|
5875
5891
|
// src/routes/globalAssets.ts
|
|
5876
|
-
import { existsSync as
|
|
5892
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
|
|
5877
5893
|
import { homedir } from "os";
|
|
5878
5894
|
import { join as join16 } from "path";
|
|
5879
5895
|
function readGlobalAssets(home = homedir()) {
|
|
5880
5896
|
const manifestPath = join16(home, ".media", "manifest.jsonl");
|
|
5881
|
-
if (!
|
|
5897
|
+
if (!existsSync11(manifestPath)) return [];
|
|
5882
5898
|
const out = [];
|
|
5883
5899
|
for (const line of readFileSync13(manifestPath, "utf8").split("\n")) {
|
|
5884
5900
|
if (!line.trim()) continue;
|
|
@@ -6132,7 +6148,7 @@ function numericAttribute(element, name) {
|
|
|
6132
6148
|
|
|
6133
6149
|
// src/helpers/previewDocumentStore.ts
|
|
6134
6150
|
import { createHash as createHash6 } from "crypto";
|
|
6135
|
-
import { readFileSync as readFileSync14, readdirSync as readdirSync5, rmSync as
|
|
6151
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync5, rmSync as rmSync6 } from "fs";
|
|
6136
6152
|
import { join as join17 } from "path";
|
|
6137
6153
|
import { replaceFileAtomically as replaceFileAtomically4 } from "@hyperframes/core/atomic-file";
|
|
6138
6154
|
function createPreviewDocumentStore(projectDir, salt) {
|
|
@@ -6153,7 +6169,7 @@ ${key}`).digest("hex").slice(0, 32)}.html`);
|
|
|
6153
6169
|
const file = fileFor(key);
|
|
6154
6170
|
replaceFileAtomically4(file, html, 420);
|
|
6155
6171
|
for (const name of readdirSync5(dir)) {
|
|
6156
|
-
if (join17(dir, name) !== file)
|
|
6172
|
+
if (join17(dir, name) !== file) rmSync6(join17(dir, name), { force: true });
|
|
6157
6173
|
}
|
|
6158
6174
|
} catch {
|
|
6159
6175
|
}
|