hyperframes 0.2.3-alpha.1 → 0.2.3
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/cli.js +348 -89
- package/dist/docker/Dockerfile.render +33 -0
- package/dist/hyperframe-runtime.js +5 -5
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +5 -5
- package/dist/skills/hyperframes/SKILL.md +24 -4
- package/dist/studio/assets/{index-DA_l-VKo.js → index-B0VCLOXQ.js} +15 -15
- package/dist/studio/index.html +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.2.3
|
|
57
|
+
VERSION = true ? "0.2.3" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -2631,8 +2631,8 @@ function flushSync() {
|
|
|
2631
2631
|
eventQueue = [];
|
|
2632
2632
|
const payload = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
|
|
2633
2633
|
try {
|
|
2634
|
-
const { spawn:
|
|
2635
|
-
const child =
|
|
2634
|
+
const { spawn: spawn11 } = __require("child_process");
|
|
2635
|
+
const child = spawn11(
|
|
2636
2636
|
process.execPath,
|
|
2637
2637
|
[
|
|
2638
2638
|
"-e",
|
|
@@ -4561,6 +4561,28 @@ ${right.raw}`)
|
|
|
4561
4561
|
}
|
|
4562
4562
|
return findings;
|
|
4563
4563
|
},
|
|
4564
|
+
// gsap_infinite_repeat
|
|
4565
|
+
({ scripts }) => {
|
|
4566
|
+
const findings = [];
|
|
4567
|
+
for (const script of scripts) {
|
|
4568
|
+
const content = script.content;
|
|
4569
|
+
const pattern = /repeat\s*:\s*-1(?!\d)/g;
|
|
4570
|
+
let match;
|
|
4571
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
4572
|
+
const contextStart = Math.max(0, match.index - 60);
|
|
4573
|
+
const contextEnd = Math.min(content.length, match.index + match[0].length + 60);
|
|
4574
|
+
const snippet = content.slice(contextStart, contextEnd).trim();
|
|
4575
|
+
findings.push({
|
|
4576
|
+
code: "gsap_infinite_repeat",
|
|
4577
|
+
severity: "error",
|
|
4578
|
+
message: "GSAP tween uses `repeat: -1` (infinite). Infinite repeats break the deterministic capture engine which seeks to exact frame times. Use a finite repeat count calculated from the composition duration: `repeat: Math.ceil(duration / cycleDuration) - 1`.",
|
|
4579
|
+
fixHint: "Replace `repeat: -1` with a finite count, e.g. `repeat: Math.ceil(totalDuration / singleCycleDuration) - 1`.",
|
|
4580
|
+
snippet: truncateSnippet(snippet)
|
|
4581
|
+
});
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
return findings;
|
|
4585
|
+
},
|
|
4564
4586
|
// scene_layer_missing_visibility_kill
|
|
4565
4587
|
({ scripts, tags }) => {
|
|
4566
4588
|
const findings = [];
|
|
@@ -5643,6 +5665,7 @@ var init_mime = __esm({
|
|
|
5643
5665
|
".webp": "image/webp",
|
|
5644
5666
|
".ico": "image/x-icon",
|
|
5645
5667
|
".mp4": "video/mp4",
|
|
5668
|
+
".mov": "video/quicktime",
|
|
5646
5669
|
".webm": "video/webm",
|
|
5647
5670
|
".mp3": "audio/mpeg",
|
|
5648
5671
|
".wav": "audio/wav",
|
|
@@ -18532,7 +18555,9 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18532
18555
|
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
18533
18556
|
if (!project) return c2.json({ error: "not found" }, 404);
|
|
18534
18557
|
const body = await c2.req.json().catch(() => ({}));
|
|
18535
|
-
const
|
|
18558
|
+
const VALID_FORMATS = /* @__PURE__ */ new Set(["mp4", "webm", "mov"]);
|
|
18559
|
+
const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
18560
|
+
const format = VALID_FORMATS.has(body.format ?? "") ? body.format : "mp4";
|
|
18536
18561
|
const fps = body.fps === 24 || body.fps === 60 ? body.fps : 30;
|
|
18537
18562
|
const quality = ["draft", "standard", "high"].includes(body.quality ?? "") ? body.quality : "standard";
|
|
18538
18563
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -18541,7 +18566,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18541
18566
|
const jobId = `${project.id}_${datePart}_${timePart}`;
|
|
18542
18567
|
const rendersDir = adapter2.rendersDir(project);
|
|
18543
18568
|
if (!existsSync10(rendersDir)) mkdirSync6(rendersDir, { recursive: true });
|
|
18544
|
-
const ext = format
|
|
18569
|
+
const ext = FORMAT_EXT2[format] ?? ".mp4";
|
|
18545
18570
|
const outputPath = join12(rendersDir, `${jobId}${ext}`);
|
|
18546
18571
|
const jobState = adapter2.startRender({
|
|
18547
18572
|
project,
|
|
@@ -18594,14 +18619,23 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18594
18619
|
}
|
|
18595
18620
|
});
|
|
18596
18621
|
});
|
|
18622
|
+
const RENDER_MIME = {
|
|
18623
|
+
".mp4": "video/mp4",
|
|
18624
|
+
".webm": "video/webm",
|
|
18625
|
+
".mov": "video/quicktime"
|
|
18626
|
+
};
|
|
18627
|
+
const RENDER_EXTENSIONS = Object.keys(RENDER_MIME);
|
|
18628
|
+
function renderContentType(filePath) {
|
|
18629
|
+
const ext = RENDER_EXTENSIONS.find((e) => filePath.endsWith(e));
|
|
18630
|
+
return (ext && RENDER_MIME[ext]) ?? "video/mp4";
|
|
18631
|
+
}
|
|
18597
18632
|
api.get("/render/:jobId/view", (c2) => {
|
|
18598
18633
|
const { jobId } = c2.req.param();
|
|
18599
18634
|
const job = renderJobs.get(jobId);
|
|
18600
18635
|
if (!job?.outputPath || !existsSync10(job.outputPath)) {
|
|
18601
18636
|
return c2.json({ error: "not found" }, 404);
|
|
18602
18637
|
}
|
|
18603
|
-
const
|
|
18604
|
-
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
18638
|
+
const contentType = renderContentType(job.outputPath);
|
|
18605
18639
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
18606
18640
|
const content = readFileSync11(job.outputPath);
|
|
18607
18641
|
return new Response(content, {
|
|
@@ -18619,8 +18653,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18619
18653
|
if (!job?.outputPath || !existsSync10(job.outputPath)) {
|
|
18620
18654
|
return c2.json({ error: "not found" }, 404);
|
|
18621
18655
|
}
|
|
18622
|
-
const
|
|
18623
|
-
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
18656
|
+
const contentType = renderContentType(job.outputPath);
|
|
18624
18657
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
18625
18658
|
const content = readFileSync11(job.outputPath);
|
|
18626
18659
|
return new Response(content, {
|
|
@@ -18635,7 +18668,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18635
18668
|
for (const [, state] of renderJobs) {
|
|
18636
18669
|
if (state.id === jobId && state.outputPath) {
|
|
18637
18670
|
const dir = state.outputPath.replace(/\/[^/]+$/, "");
|
|
18638
|
-
for (const ext of [".mp4", ".webm", ".meta.json"]) {
|
|
18671
|
+
for (const ext of [".mp4", ".webm", ".mov", ".meta.json"]) {
|
|
18639
18672
|
const fp = join12(dir, `${jobId}${ext}`);
|
|
18640
18673
|
if (existsSync10(fp)) unlinkSync4(fp);
|
|
18641
18674
|
}
|
|
@@ -18653,8 +18686,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18653
18686
|
const rendersDir = adapter2.rendersDir(project);
|
|
18654
18687
|
const fp = join12(rendersDir, filename);
|
|
18655
18688
|
if (!existsSync10(fp)) return c2.json({ error: "not found" }, 404);
|
|
18656
|
-
const
|
|
18657
|
-
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
18689
|
+
const contentType = renderContentType(fp);
|
|
18658
18690
|
const content = readFileSync11(fp);
|
|
18659
18691
|
return new Response(content, {
|
|
18660
18692
|
headers: {
|
|
@@ -18670,10 +18702,10 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
18670
18702
|
if (!project) return c2.json({ error: "not found" }, 404);
|
|
18671
18703
|
const rendersDir = adapter2.rendersDir(project);
|
|
18672
18704
|
if (!existsSync10(rendersDir)) return c2.json({ renders: [] });
|
|
18673
|
-
const files = readdirSync5(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm")).map((f) => {
|
|
18705
|
+
const files = readdirSync5(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).map((f) => {
|
|
18674
18706
|
const fp = join12(rendersDir, f);
|
|
18675
18707
|
const stat = statSync3(fp);
|
|
18676
|
-
const rid = f.replace(/\.(mp4|webm)$/, "");
|
|
18708
|
+
const rid = f.replace(/\.(mp4|webm|mov)$/, "");
|
|
18677
18709
|
const metaPath = join12(rendersDir, `${rid}.meta.json`);
|
|
18678
18710
|
let status = "complete";
|
|
18679
18711
|
let durationMs;
|
|
@@ -20561,6 +20593,14 @@ function getEncoderPreset(quality, format = "mp4") {
|
|
|
20561
20593
|
pixelFormat: "yuva420p"
|
|
20562
20594
|
};
|
|
20563
20595
|
}
|
|
20596
|
+
if (format === "mov") {
|
|
20597
|
+
return {
|
|
20598
|
+
preset: "4444",
|
|
20599
|
+
quality: base.quality,
|
|
20600
|
+
codec: "prores",
|
|
20601
|
+
pixelFormat: "yuva444p10le"
|
|
20602
|
+
};
|
|
20603
|
+
}
|
|
20564
20604
|
return { ...base, pixelFormat: "yuv420p" };
|
|
20565
20605
|
}
|
|
20566
20606
|
function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
@@ -20610,6 +20650,13 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
20610
20650
|
args.push("-c:v", encoderName, "-preset", preset);
|
|
20611
20651
|
if (bitrate) args.push("-b:v", bitrate);
|
|
20612
20652
|
else args.push("-crf", String(quality));
|
|
20653
|
+
const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
|
|
20654
|
+
const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
20655
|
+
if (preset === "ultrafast") {
|
|
20656
|
+
args.push(xParamsFlag, `aq-mode=3:${colorParams}`);
|
|
20657
|
+
} else {
|
|
20658
|
+
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
20659
|
+
}
|
|
20613
20660
|
}
|
|
20614
20661
|
} else if (codec === "vp9") {
|
|
20615
20662
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
@@ -20621,8 +20668,30 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
20621
20668
|
}
|
|
20622
20669
|
} else if (codec === "prores") {
|
|
20623
20670
|
args.push("-c:v", "prores_ks", "-profile:v", preset, "-vendor", "apl0");
|
|
20671
|
+
args.push("-pix_fmt", pixelFormat);
|
|
20624
20672
|
return [...args, "-y", outputPath];
|
|
20625
20673
|
}
|
|
20674
|
+
if (codec === "h264" || codec === "h265") {
|
|
20675
|
+
args.push(
|
|
20676
|
+
"-colorspace:v",
|
|
20677
|
+
"bt709",
|
|
20678
|
+
"-color_primaries:v",
|
|
20679
|
+
"bt709",
|
|
20680
|
+
"-color_trc:v",
|
|
20681
|
+
"bt709",
|
|
20682
|
+
"-color_range",
|
|
20683
|
+
"tv"
|
|
20684
|
+
);
|
|
20685
|
+
if (gpuEncoder === "vaapi") {
|
|
20686
|
+
const vfIdx = args.indexOf("-vf");
|
|
20687
|
+
if (vfIdx !== -1) {
|
|
20688
|
+
args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
|
|
20689
|
+
}
|
|
20690
|
+
} else if (!shouldUseGpu) {
|
|
20691
|
+
args.push("-vf", "scale=in_range=pc:out_range=tv");
|
|
20692
|
+
}
|
|
20693
|
+
args.push("-video_track_timescale", "90000");
|
|
20694
|
+
}
|
|
20626
20695
|
if (gpuEncoder !== "vaapi") {
|
|
20627
20696
|
args.push("-pix_fmt", pixelFormat);
|
|
20628
20697
|
}
|
|
@@ -20746,7 +20815,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
20746
20815
|
}
|
|
20747
20816
|
const startNumber = i * chunkSize;
|
|
20748
20817
|
const framesInChunk = Math.min(chunkSize, files.length - startNumber);
|
|
20749
|
-
const ext = outputPath.endsWith(".webm") ? ".webm" : ".mp4";
|
|
20818
|
+
const ext = outputPath.endsWith(".webm") ? ".webm" : outputPath.endsWith(".mov") ? ".mov" : ".mp4";
|
|
20750
20819
|
const chunkPath = join17(chunkDir, `chunk_${String(i).padStart(4, "0")}${ext}`);
|
|
20751
20820
|
const inputPath = join17(framesDir, framePattern);
|
|
20752
20821
|
const inputArgs = [
|
|
@@ -20840,9 +20909,12 @@ async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, confi
|
|
|
20840
20909
|
const outputDir = dirname4(outputPath);
|
|
20841
20910
|
if (!existsSync15(outputDir)) mkdirSync9(outputDir, { recursive: true });
|
|
20842
20911
|
const isWebm = outputPath.endsWith(".webm");
|
|
20912
|
+
const isMov = outputPath.endsWith(".mov");
|
|
20843
20913
|
const args = ["-i", videoPath, "-i", audioPath, "-c:v", "copy"];
|
|
20844
20914
|
if (isWebm) {
|
|
20845
20915
|
args.push("-c:a", "libopus", "-b:a", "128k");
|
|
20916
|
+
} else if (isMov) {
|
|
20917
|
+
args.push("-c:a", "aac", "-b:a", "192k");
|
|
20846
20918
|
} else {
|
|
20847
20919
|
args.push("-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart");
|
|
20848
20920
|
}
|
|
@@ -20865,7 +20937,7 @@ async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, confi
|
|
|
20865
20937
|
};
|
|
20866
20938
|
}
|
|
20867
20939
|
async function applyFaststart(inputPath, outputPath, signal, config) {
|
|
20868
|
-
if (outputPath.endsWith(".webm")) {
|
|
20940
|
+
if (outputPath.endsWith(".webm") || outputPath.endsWith(".mov")) {
|
|
20869
20941
|
if (inputPath !== outputPath) copyFileSync(inputPath, outputPath);
|
|
20870
20942
|
return { success: true, outputPath, durationMs: 0 };
|
|
20871
20943
|
}
|
|
@@ -20993,6 +21065,13 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
20993
21065
|
args.push("-c:v", encoderName, "-preset", preset);
|
|
20994
21066
|
if (bitrate) args.push("-b:v", bitrate);
|
|
20995
21067
|
else args.push("-crf", String(quality));
|
|
21068
|
+
const xParamsFlag = codec === "h264" ? "-x264-params" : "-x265-params";
|
|
21069
|
+
const colorParams = "colorprim=bt709:transfer=bt709:colormatrix=bt709";
|
|
21070
|
+
if (preset === "ultrafast") {
|
|
21071
|
+
args.push(xParamsFlag, `aq-mode=3:${colorParams}`);
|
|
21072
|
+
} else {
|
|
21073
|
+
args.push(xParamsFlag, `aq-mode=3:aq-strength=0.8:deblock=1,1:${colorParams}`);
|
|
21074
|
+
}
|
|
20996
21075
|
}
|
|
20997
21076
|
} else if (codec === "vp9") {
|
|
20998
21077
|
args.push("-c:v", "libvpx-vp9", "-b:v", bitrate || "0", "-crf", String(quality));
|
|
@@ -21004,8 +21083,30 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
21004
21083
|
}
|
|
21005
21084
|
} else if (codec === "prores") {
|
|
21006
21085
|
args.push("-c:v", "prores_ks", "-profile:v", preset, "-vendor", "apl0");
|
|
21086
|
+
args.push("-pix_fmt", pixelFormat);
|
|
21007
21087
|
return [...args, "-y", outputPath];
|
|
21008
21088
|
}
|
|
21089
|
+
if (codec === "h264" || codec === "h265") {
|
|
21090
|
+
args.push(
|
|
21091
|
+
"-colorspace:v",
|
|
21092
|
+
"bt709",
|
|
21093
|
+
"-color_primaries:v",
|
|
21094
|
+
"bt709",
|
|
21095
|
+
"-color_trc:v",
|
|
21096
|
+
"bt709",
|
|
21097
|
+
"-color_range",
|
|
21098
|
+
"tv"
|
|
21099
|
+
);
|
|
21100
|
+
if (gpuEncoder === "vaapi") {
|
|
21101
|
+
const vfIdx = args.indexOf("-vf");
|
|
21102
|
+
if (vfIdx !== -1) {
|
|
21103
|
+
args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
|
|
21104
|
+
}
|
|
21105
|
+
} else if (!shouldUseGpu) {
|
|
21106
|
+
args.push("-vf", "scale=in_range=pc:out_range=tv");
|
|
21107
|
+
}
|
|
21108
|
+
args.push("-video_track_timescale", "90000");
|
|
21109
|
+
}
|
|
21009
21110
|
if (gpuEncoder !== "vaapi") {
|
|
21010
21111
|
args.push("-pix_fmt", pixelFormat);
|
|
21011
21112
|
}
|
|
@@ -22915,6 +23016,17 @@ async function bundleToSingleHtml(projectDir, options) {
|
|
|
22915
23016
|
const contentHtml = contentRoot ? contentRoot.innerHTML || "" : compDoc.body.innerHTML || "";
|
|
22916
23017
|
const contentDoc = parseHTMLContent(contentHtml);
|
|
22917
23018
|
const innerRoot = compId ? contentDoc.querySelector(`[data-composition-id="${compId}"]`) : contentDoc.querySelector("[data-composition-id]");
|
|
23019
|
+
if (!contentRoot && compDoc.head) {
|
|
23020
|
+
for (const s of [...compDoc.head.querySelectorAll("style")]) {
|
|
23021
|
+
compStyleChunks.push(rewriteCssAssetUrls(s.textContent || "", src));
|
|
23022
|
+
}
|
|
23023
|
+
for (const s of [...compDoc.head.querySelectorAll("script")]) {
|
|
23024
|
+
const externalSrc = (s.getAttribute("src") || "").trim();
|
|
23025
|
+
if (externalSrc && !compExternalScriptSrcs.includes(externalSrc)) {
|
|
23026
|
+
compExternalScriptSrcs.push(externalSrc);
|
|
23027
|
+
}
|
|
23028
|
+
}
|
|
23029
|
+
}
|
|
22918
23030
|
for (const s of [...contentDoc.querySelectorAll("style")]) {
|
|
22919
23031
|
compStyleChunks.push(rewriteCssAssetUrls(s.textContent || "", src));
|
|
22920
23032
|
s.remove();
|
|
@@ -24095,6 +24207,26 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
24095
24207
|
const contentDoc = parseHTML(contentHtml).document;
|
|
24096
24208
|
const innerRoot = compId ? contentDoc.querySelector(`[data-composition-id="${compId}"]`) : contentDoc.querySelector("[data-composition-id]");
|
|
24097
24209
|
const inferredCompId = innerRoot?.getAttribute("data-composition-id")?.trim() || null;
|
|
24210
|
+
if (!templateEl) {
|
|
24211
|
+
const compHead = compDoc.querySelector("head");
|
|
24212
|
+
if (compHead) {
|
|
24213
|
+
for (const styleEl of compHead.querySelectorAll("style")) {
|
|
24214
|
+
const css = rewriteCssAssetUrls(styleEl.textContent || "", srcPath);
|
|
24215
|
+
const scopeId = compId || inferredCompId;
|
|
24216
|
+
if (scopeId && css.trim()) {
|
|
24217
|
+
collectedStyles.push(scopeCssToComposition(css, scopeId));
|
|
24218
|
+
} else {
|
|
24219
|
+
collectedStyles.push(css);
|
|
24220
|
+
}
|
|
24221
|
+
}
|
|
24222
|
+
for (const scriptEl of compHead.querySelectorAll("script")) {
|
|
24223
|
+
const src = (scriptEl.getAttribute("src") || "").trim();
|
|
24224
|
+
if (src && !collectedExternalScriptSrcs.includes(src)) {
|
|
24225
|
+
collectedExternalScriptSrcs.push(src);
|
|
24226
|
+
}
|
|
24227
|
+
}
|
|
24228
|
+
}
|
|
24229
|
+
}
|
|
24098
24230
|
for (const styleEl of contentDoc.querySelectorAll("style")) {
|
|
24099
24231
|
const css = rewriteCssAssetUrls(styleEl.textContent || "", srcPath);
|
|
24100
24232
|
const scopeId = compId || inferredCompId;
|
|
@@ -24702,7 +24834,9 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
24702
24834
|
const cfg = { ...job.config.producerConfig ?? resolveConfig() };
|
|
24703
24835
|
const outputFormat = job.config.format ?? "mp4";
|
|
24704
24836
|
const isWebm = outputFormat === "webm";
|
|
24705
|
-
|
|
24837
|
+
const isMov = outputFormat === "mov";
|
|
24838
|
+
const needsAlpha = isWebm || isMov;
|
|
24839
|
+
if (needsAlpha) {
|
|
24706
24840
|
cfg.forceScreenshot = true;
|
|
24707
24841
|
}
|
|
24708
24842
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
|
@@ -24791,8 +24925,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
24791
24925
|
width,
|
|
24792
24926
|
height,
|
|
24793
24927
|
fps: job.config.fps,
|
|
24794
|
-
format:
|
|
24795
|
-
quality:
|
|
24928
|
+
format: needsAlpha ? "png" : "jpeg",
|
|
24929
|
+
quality: needsAlpha ? void 0 : 80
|
|
24796
24930
|
};
|
|
24797
24931
|
probeSession = await createCaptureSession(
|
|
24798
24932
|
fileServer.url,
|
|
@@ -25032,11 +25166,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
25032
25166
|
width,
|
|
25033
25167
|
height,
|
|
25034
25168
|
fps: job.config.fps,
|
|
25035
|
-
format:
|
|
25036
|
-
quality:
|
|
25169
|
+
format: needsAlpha ? "png" : "jpeg",
|
|
25170
|
+
quality: needsAlpha ? void 0 : job.config.quality === "draft" ? 80 : 95
|
|
25037
25171
|
};
|
|
25038
25172
|
const workerCount = calculateOptimalWorkers(job.totalFrames, job.config.workers, cfg);
|
|
25039
|
-
const
|
|
25173
|
+
const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
25174
|
+
const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
|
|
25040
25175
|
const videoOnlyPath = join26(workDir, `video-only${videoExt}`);
|
|
25041
25176
|
const preset = getEncoderPreset(job.config.quality, outputFormat);
|
|
25042
25177
|
job.framesRendered = 0;
|
|
@@ -25222,7 +25357,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
25222
25357
|
perfStages.captureMs = Date.now() - stage4Start;
|
|
25223
25358
|
const stage5Start = Date.now();
|
|
25224
25359
|
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
|
25225
|
-
const frameExt =
|
|
25360
|
+
const frameExt = needsAlpha ? "png" : "jpg";
|
|
25226
25361
|
const framePattern = `frame_%06d.${frameExt}`;
|
|
25227
25362
|
const encoderOpts = {
|
|
25228
25363
|
fps: job.config.fps,
|
|
@@ -25316,7 +25451,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
25316
25451
|
}
|
|
25317
25452
|
if (job.config.debug) {
|
|
25318
25453
|
if (existsSync26(outputPath)) {
|
|
25319
|
-
const debugOutput = join26(workDir,
|
|
25454
|
+
const debugOutput = join26(workDir, `output${videoExt}`);
|
|
25320
25455
|
copyFileSync2(outputPath, debugOutput);
|
|
25321
25456
|
}
|
|
25322
25457
|
} else {
|
|
@@ -25590,7 +25725,7 @@ function parseRenderOptions(body) {
|
|
|
25590
25725
|
const debug = body.debug === true;
|
|
25591
25726
|
const outputPath = typeof body.outputPath === "string" && body.outputPath.trim().length > 0 ? body.outputPath : typeof body.output === "string" && body.output.trim().length > 0 ? body.output : null;
|
|
25592
25727
|
const entryFile = typeof body.entryFile === "string" && body.entryFile.trim().length > 0 ? body.entryFile.trim() : void 0;
|
|
25593
|
-
const format = ["mp4", "webm"].includes(body.format) ? body.format : void 0;
|
|
25728
|
+
const format = ["mp4", "webm", "mov"].includes(body.format) ? body.format : void 0;
|
|
25594
25729
|
return { outputPath, fps, quality, workers, useGpu, debug, entryFile, format };
|
|
25595
25730
|
}
|
|
25596
25731
|
async function prepareRenderBody(body) {
|
|
@@ -26195,7 +26330,7 @@ function createStudioServer(options) {
|
|
|
26195
26330
|
await executeRenderJob2(job, opts.project.dir, opts.outputPath, onProgress);
|
|
26196
26331
|
state.status = "complete";
|
|
26197
26332
|
state.progress = 100;
|
|
26198
|
-
const metaPath = opts.outputPath.replace(/\.(mp4|webm)$/, ".meta.json");
|
|
26333
|
+
const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
|
|
26199
26334
|
writeFileSync10(
|
|
26200
26335
|
metaPath,
|
|
26201
26336
|
JSON.stringify({ status: "complete", durationMs: Date.now() - startTime })
|
|
@@ -26204,7 +26339,7 @@ function createStudioServer(options) {
|
|
|
26204
26339
|
state.status = "failed";
|
|
26205
26340
|
state.error = err instanceof Error ? err.message : String(err);
|
|
26206
26341
|
try {
|
|
26207
|
-
const metaPath = opts.outputPath.replace(/\.(mp4|webm)$/, ".meta.json");
|
|
26342
|
+
const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
|
|
26208
26343
|
writeFileSync10(metaPath, JSON.stringify({ status: "failed" }));
|
|
26209
26344
|
} catch {
|
|
26210
26345
|
}
|
|
@@ -26403,7 +26538,7 @@ async function runDevMode(dir, projectName) {
|
|
|
26403
26538
|
console.log();
|
|
26404
26539
|
console.log(` ${c.dim("Press Ctrl+C to stop")}`);
|
|
26405
26540
|
console.log();
|
|
26406
|
-
const urlToOpen = `${frontendUrl}
|
|
26541
|
+
const urlToOpen = `${frontendUrl}#project/${pName}`;
|
|
26407
26542
|
import("open").then((mod) => mod.default(urlToOpen)).catch(() => {
|
|
26408
26543
|
});
|
|
26409
26544
|
child.stdout?.removeListener("data", handleOutput);
|
|
@@ -27520,30 +27655,149 @@ __export(render_exports, {
|
|
|
27520
27655
|
default: () => render_default,
|
|
27521
27656
|
examples: () => examples4
|
|
27522
27657
|
});
|
|
27523
|
-
import {
|
|
27524
|
-
import { cpus as cpus3, freemem as freemem3 } from "os";
|
|
27525
|
-
import { resolve as resolve20, dirname as dirname14, join as join33 } from "path";
|
|
27658
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync23, statSync as statSync12, writeFileSync as writeFileSync12, rmSync as rmSync8 } from "fs";
|
|
27659
|
+
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir3 } from "os";
|
|
27660
|
+
import { resolve as resolve20, dirname as dirname14, join as join33, basename as basename6 } from "path";
|
|
27661
|
+
import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
|
|
27526
27662
|
function defaultWorkerCount() {
|
|
27527
27663
|
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
27528
27664
|
}
|
|
27665
|
+
function dockerImageTag(version) {
|
|
27666
|
+
return `${DOCKER_IMAGE_PREFIX}:${version}`;
|
|
27667
|
+
}
|
|
27668
|
+
function resolveDockerfilePath() {
|
|
27669
|
+
const builtPath = resolve20(__dirname, "docker", "Dockerfile.render");
|
|
27670
|
+
const devPath = resolve20(__dirname, "..", "src", "docker", "Dockerfile.render");
|
|
27671
|
+
for (const p of [builtPath, devPath]) {
|
|
27672
|
+
try {
|
|
27673
|
+
statSync12(p);
|
|
27674
|
+
return p;
|
|
27675
|
+
} catch {
|
|
27676
|
+
continue;
|
|
27677
|
+
}
|
|
27678
|
+
}
|
|
27679
|
+
throw new Error("Dockerfile.render not found \u2014 CLI package may be corrupted");
|
|
27680
|
+
}
|
|
27681
|
+
function dockerImageExists(tag) {
|
|
27682
|
+
try {
|
|
27683
|
+
execFileSync5("docker", ["image", "inspect", tag], { stdio: "pipe", timeout: 1e4 });
|
|
27684
|
+
return true;
|
|
27685
|
+
} catch {
|
|
27686
|
+
return false;
|
|
27687
|
+
}
|
|
27688
|
+
}
|
|
27689
|
+
function ensureDockerImage(version, quiet) {
|
|
27690
|
+
const tag = dockerImageTag(version);
|
|
27691
|
+
if (dockerImageExists(tag)) {
|
|
27692
|
+
if (!quiet) console.log(c.dim(` Docker image: ${tag} (cached)`));
|
|
27693
|
+
return tag;
|
|
27694
|
+
}
|
|
27695
|
+
if (!quiet) console.log(c.dim(` Building Docker image: ${tag}...`));
|
|
27696
|
+
const dockerfilePath = resolveDockerfilePath();
|
|
27697
|
+
const tmpDir = join33(tmpdir3(), `hyperframes-docker-${Date.now()}`);
|
|
27698
|
+
mkdirSync20(tmpDir, { recursive: true });
|
|
27699
|
+
writeFileSync12(join33(tmpDir, "Dockerfile"), readFileSync23(dockerfilePath));
|
|
27700
|
+
try {
|
|
27701
|
+
execFileSync5(
|
|
27702
|
+
"docker",
|
|
27703
|
+
[
|
|
27704
|
+
"build",
|
|
27705
|
+
"--platform",
|
|
27706
|
+
"linux/amd64",
|
|
27707
|
+
"--build-arg",
|
|
27708
|
+
`HYPERFRAMES_VERSION=${version}`,
|
|
27709
|
+
"-t",
|
|
27710
|
+
tag,
|
|
27711
|
+
tmpDir
|
|
27712
|
+
],
|
|
27713
|
+
{ stdio: quiet ? "pipe" : "inherit", timeout: 6e5 }
|
|
27714
|
+
);
|
|
27715
|
+
} catch (error) {
|
|
27716
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27717
|
+
throw new Error(`Failed to build Docker image: ${message}`);
|
|
27718
|
+
} finally {
|
|
27719
|
+
rmSync8(tmpDir, { recursive: true, force: true });
|
|
27720
|
+
}
|
|
27721
|
+
if (!quiet) console.log(c.dim(` Docker image: ${tag} (built)`));
|
|
27722
|
+
return tag;
|
|
27723
|
+
}
|
|
27529
27724
|
async function renderDocker(projectDir, outputPath, options) {
|
|
27530
|
-
const producer = await loadProducer();
|
|
27531
27725
|
const startTime = Date.now();
|
|
27532
|
-
|
|
27726
|
+
const dockerVersion = isDevMode() ? "latest" : VERSION;
|
|
27727
|
+
if (!options.quiet && isDevMode()) {
|
|
27728
|
+
console.log(c.dim(" Dev mode: using hyperframes@latest in Docker image"));
|
|
27729
|
+
}
|
|
27730
|
+
let imageTag;
|
|
27731
|
+
try {
|
|
27732
|
+
imageTag = ensureDockerImage(dockerVersion, options.quiet);
|
|
27733
|
+
} catch (error) {
|
|
27734
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27735
|
+
const isDockerMissing = /connect|not found|ENOENT/i.test(message);
|
|
27736
|
+
errorBox(
|
|
27737
|
+
isDockerMissing ? "Docker not available" : "Docker image build failed",
|
|
27738
|
+
message,
|
|
27739
|
+
isDockerMissing ? "Install Docker: https://docs.docker.com/get-docker/" : "Check Docker is running: docker info"
|
|
27740
|
+
);
|
|
27741
|
+
process.exit(1);
|
|
27742
|
+
}
|
|
27743
|
+
const outputDir = dirname14(outputPath);
|
|
27744
|
+
const outputFilename = basename6(outputPath);
|
|
27745
|
+
const dockerArgs = [
|
|
27746
|
+
"run",
|
|
27747
|
+
"--rm",
|
|
27748
|
+
"--platform",
|
|
27749
|
+
"linux/amd64",
|
|
27750
|
+
"--shm-size=2g",
|
|
27751
|
+
// GPU encoding requires host GPU passthrough
|
|
27752
|
+
...options.gpu ? ["--gpus", "all"] : [],
|
|
27753
|
+
"-v",
|
|
27754
|
+
`${resolve20(projectDir)}:/project:ro`,
|
|
27755
|
+
"-v",
|
|
27756
|
+
`${resolve20(outputDir)}:/output`,
|
|
27757
|
+
imageTag,
|
|
27758
|
+
"/project",
|
|
27759
|
+
"--output",
|
|
27760
|
+
`/output/${outputFilename}`,
|
|
27761
|
+
"--fps",
|
|
27762
|
+
String(options.fps),
|
|
27763
|
+
"--quality",
|
|
27764
|
+
options.quality,
|
|
27765
|
+
"--format",
|
|
27766
|
+
options.format,
|
|
27767
|
+
"--workers",
|
|
27768
|
+
String(options.workers),
|
|
27769
|
+
...options.quiet ? ["--quiet"] : [],
|
|
27770
|
+
...options.gpu ? ["--gpu"] : []
|
|
27771
|
+
];
|
|
27772
|
+
if (!options.quiet) {
|
|
27773
|
+
console.log(c.dim(" Running render in Docker container..."));
|
|
27774
|
+
console.log("");
|
|
27775
|
+
}
|
|
27533
27776
|
try {
|
|
27534
|
-
|
|
27535
|
-
|
|
27536
|
-
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27777
|
+
await new Promise((resolvePromise, reject) => {
|
|
27778
|
+
const child = spawn10("docker", dockerArgs, {
|
|
27779
|
+
// When quiet, still show stderr so container errors surface
|
|
27780
|
+
stdio: options.quiet ? ["pipe", "pipe", "inherit"] : "inherit"
|
|
27781
|
+
});
|
|
27782
|
+
child.on("close", (code) => {
|
|
27783
|
+
if (code === 0) resolvePromise();
|
|
27784
|
+
else reject(new Error(`Docker render exited with code ${code}`));
|
|
27785
|
+
});
|
|
27786
|
+
child.on("error", (err) => reject(err));
|
|
27540
27787
|
});
|
|
27541
|
-
await producer.executeRenderJob(job, projectDir, outputPath);
|
|
27542
27788
|
} catch (error) {
|
|
27543
27789
|
handleRenderError(error, options, startTime, true, "Check Docker is running: docker info");
|
|
27544
27790
|
}
|
|
27545
27791
|
const elapsed = Date.now() - startTime;
|
|
27546
|
-
|
|
27792
|
+
trackRenderComplete({
|
|
27793
|
+
durationMs: elapsed,
|
|
27794
|
+
fps: options.fps,
|
|
27795
|
+
quality: options.quality,
|
|
27796
|
+
workers: options.workers,
|
|
27797
|
+
docker: true,
|
|
27798
|
+
gpu: options.gpu,
|
|
27799
|
+
...getMemorySnapshot()
|
|
27800
|
+
});
|
|
27547
27801
|
printRenderComplete(outputPath, elapsed, options.quiet);
|
|
27548
27802
|
}
|
|
27549
27803
|
async function renderLocal(projectDir, outputPath, options) {
|
|
@@ -27616,16 +27870,16 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
27616
27870
|
function printRenderComplete(outputPath, elapsedMs, quiet) {
|
|
27617
27871
|
if (quiet) return;
|
|
27618
27872
|
let fileSize = "unknown";
|
|
27619
|
-
|
|
27620
|
-
|
|
27621
|
-
|
|
27873
|
+
try {
|
|
27874
|
+
fileSize = formatBytes(statSync12(outputPath).size);
|
|
27875
|
+
} catch {
|
|
27622
27876
|
}
|
|
27623
27877
|
const duration = formatDuration(elapsedMs);
|
|
27624
27878
|
console.log("");
|
|
27625
27879
|
console.log(c.success("\u25C7") + " " + c.accent(outputPath));
|
|
27626
27880
|
console.log(" " + c.bold(fileSize) + c.dim(" \xB7 " + duration + " \xB7 completed"));
|
|
27627
27881
|
}
|
|
27628
|
-
var examples4, VALID_FPS, VALID_QUALITY, VALID_FORMAT, CPU_CORE_COUNT, render_default;
|
|
27882
|
+
var examples4, VALID_FPS, VALID_QUALITY, VALID_FORMAT, FORMAT_EXT, CPU_CORE_COUNT, render_default, DOCKER_IMAGE_PREFIX;
|
|
27629
27883
|
var init_render2 = __esm({
|
|
27630
27884
|
"src/commands/render.ts"() {
|
|
27631
27885
|
"use strict";
|
|
@@ -27639,8 +27893,11 @@ var init_render2 = __esm({
|
|
|
27639
27893
|
init_progress();
|
|
27640
27894
|
init_events();
|
|
27641
27895
|
init_system();
|
|
27896
|
+
init_version();
|
|
27897
|
+
init_env();
|
|
27642
27898
|
examples4 = [
|
|
27643
27899
|
["Render to MP4", "hyperframes render --output output.mp4"],
|
|
27900
|
+
["Render transparent overlay (ProRes)", "hyperframes render --format mov --output overlay.mov"],
|
|
27644
27901
|
["Render transparent WebM overlay", "hyperframes render --format webm --output overlay.webm"],
|
|
27645
27902
|
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
|
27646
27903
|
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
|
@@ -27648,12 +27905,13 @@ var init_render2 = __esm({
|
|
|
27648
27905
|
];
|
|
27649
27906
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
27650
27907
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
27651
|
-
VALID_FORMAT = /* @__PURE__ */ new Set(["mp4", "webm"]);
|
|
27908
|
+
VALID_FORMAT = /* @__PURE__ */ new Set(["mp4", "webm", "mov"]);
|
|
27909
|
+
FORMAT_EXT = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
27652
27910
|
CPU_CORE_COUNT = cpus3().length;
|
|
27653
27911
|
render_default = defineCommand({
|
|
27654
27912
|
meta: {
|
|
27655
27913
|
name: "render",
|
|
27656
|
-
description: "Render a composition to MP4 or
|
|
27914
|
+
description: "Render a composition to MP4, WebM, or MOV"
|
|
27657
27915
|
},
|
|
27658
27916
|
args: {
|
|
27659
27917
|
dir: {
|
|
@@ -27677,7 +27935,7 @@ var init_render2 = __esm({
|
|
|
27677
27935
|
},
|
|
27678
27936
|
format: {
|
|
27679
27937
|
type: "string",
|
|
27680
|
-
description: "Output format: mp4, webm (WebM
|
|
27938
|
+
description: "Output format: mp4, webm, mov (MOV/WebM render with transparency)",
|
|
27681
27939
|
default: "mp4"
|
|
27682
27940
|
},
|
|
27683
27941
|
workers: {
|
|
@@ -27722,7 +27980,7 @@ var init_render2 = __esm({
|
|
|
27722
27980
|
const quality = qualityRaw;
|
|
27723
27981
|
const formatRaw = args.format ?? "mp4";
|
|
27724
27982
|
if (!VALID_FORMAT.has(formatRaw)) {
|
|
27725
|
-
errorBox("Invalid format", `Got "${formatRaw}". Must be mp4 or
|
|
27983
|
+
errorBox("Invalid format", `Got "${formatRaw}". Must be mp4, webm, or mov.`);
|
|
27726
27984
|
process.exit(1);
|
|
27727
27985
|
}
|
|
27728
27986
|
const format = formatRaw;
|
|
@@ -27736,7 +27994,7 @@ var init_render2 = __esm({
|
|
|
27736
27994
|
workers = parsed;
|
|
27737
27995
|
}
|
|
27738
27996
|
const rendersDir = resolve20("renders");
|
|
27739
|
-
const ext = format
|
|
27997
|
+
const ext = FORMAT_EXT[format] ?? ".mp4";
|
|
27740
27998
|
const now = /* @__PURE__ */ new Date();
|
|
27741
27999
|
const datePart = now.toISOString().slice(0, 10);
|
|
27742
28000
|
const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
|
|
@@ -27839,6 +28097,7 @@ var init_render2 = __esm({
|
|
|
27839
28097
|
}
|
|
27840
28098
|
}
|
|
27841
28099
|
});
|
|
28100
|
+
DOCKER_IMAGE_PREFIX = "hyperframes-renderer";
|
|
27842
28101
|
}
|
|
27843
28102
|
});
|
|
27844
28103
|
|
|
@@ -28059,7 +28318,7 @@ __export(info_exports, {
|
|
|
28059
28318
|
default: () => info_default,
|
|
28060
28319
|
examples: () => examples6
|
|
28061
28320
|
});
|
|
28062
|
-
import { readFileSync as
|
|
28321
|
+
import { readFileSync as readFileSync24, readdirSync as readdirSync11, statSync as statSync13 } from "fs";
|
|
28063
28322
|
import { join as join34 } from "path";
|
|
28064
28323
|
function totalSize(dir) {
|
|
28065
28324
|
let total = 0;
|
|
@@ -28096,7 +28355,7 @@ var init_info = __esm({
|
|
|
28096
28355
|
},
|
|
28097
28356
|
async run({ args }) {
|
|
28098
28357
|
const project = resolveProject(args.dir);
|
|
28099
|
-
const html =
|
|
28358
|
+
const html = readFileSync24(project.indexPath, "utf-8");
|
|
28100
28359
|
ensureDOMParser();
|
|
28101
28360
|
const parsed = parseHtml(html);
|
|
28102
28361
|
const tracks = new Set(parsed.elements.map((el) => el.zIndex));
|
|
@@ -28152,7 +28411,7 @@ __export(compositions_exports, {
|
|
|
28152
28411
|
default: () => compositions_default,
|
|
28153
28412
|
examples: () => examples7
|
|
28154
28413
|
});
|
|
28155
|
-
import { existsSync as
|
|
28414
|
+
import { existsSync as existsSync34, readFileSync as readFileSync25 } from "fs";
|
|
28156
28415
|
import { resolve as resolve21, dirname as dirname15 } from "path";
|
|
28157
28416
|
function parseCompositions(html, baseDir) {
|
|
28158
28417
|
const parser = new DOMParser();
|
|
@@ -28166,8 +28425,8 @@ function parseCompositions(html, baseDir) {
|
|
|
28166
28425
|
const compositionSrc = div.getAttribute("data-composition-src");
|
|
28167
28426
|
if (compositionSrc) {
|
|
28168
28427
|
const subPath = resolve21(baseDir, compositionSrc);
|
|
28169
|
-
if (
|
|
28170
|
-
const subHtml =
|
|
28428
|
+
if (existsSync34(subPath)) {
|
|
28429
|
+
const subHtml = readFileSync25(subPath, "utf-8");
|
|
28171
28430
|
const subInfo = parseSubComposition(subHtml, id, width, height);
|
|
28172
28431
|
compositions.push({ ...subInfo, source: compositionSrc });
|
|
28173
28432
|
return;
|
|
@@ -28261,7 +28520,7 @@ var init_compositions = __esm({
|
|
|
28261
28520
|
},
|
|
28262
28521
|
async run({ args }) {
|
|
28263
28522
|
const project = resolveProject(args.dir);
|
|
28264
|
-
const html =
|
|
28523
|
+
const html = readFileSync25(project.indexPath, "utf-8");
|
|
28265
28524
|
ensureDOMParser();
|
|
28266
28525
|
const compositions = parseCompositions(html, dirname15(project.indexPath));
|
|
28267
28526
|
if (compositions.length === 0) {
|
|
@@ -28299,7 +28558,7 @@ __export(benchmark_exports, {
|
|
|
28299
28558
|
default: () => benchmark_default,
|
|
28300
28559
|
examples: () => examples8
|
|
28301
28560
|
});
|
|
28302
|
-
import { existsSync as
|
|
28561
|
+
import { existsSync as existsSync35, statSync as statSync14 } from "fs";
|
|
28303
28562
|
import { resolve as resolve22, join as join35 } from "path";
|
|
28304
28563
|
var examples8, DEFAULT_CONFIGS, benchmark_default;
|
|
28305
28564
|
var init_benchmark = __esm({
|
|
@@ -28389,7 +28648,7 @@ var init_benchmark = __esm({
|
|
|
28389
28648
|
await producer.executeRenderJob(job, project.dir, outputPath);
|
|
28390
28649
|
const elapsedMs = Date.now() - startTime;
|
|
28391
28650
|
let fileSize = null;
|
|
28392
|
-
if (
|
|
28651
|
+
if (existsSync35(outputPath)) {
|
|
28393
28652
|
const stat = statSync14(outputPath);
|
|
28394
28653
|
fileSize = stat.size;
|
|
28395
28654
|
}
|
|
@@ -28605,7 +28864,7 @@ __export(transcribe_exports2, {
|
|
|
28605
28864
|
default: () => transcribe_default,
|
|
28606
28865
|
examples: () => examples10
|
|
28607
28866
|
});
|
|
28608
|
-
import { existsSync as
|
|
28867
|
+
import { existsSync as existsSync36, writeFileSync as writeFileSync13 } from "fs";
|
|
28609
28868
|
import { resolve as resolve23, join as join36, extname as extname7 } from "path";
|
|
28610
28869
|
async function importTranscript(inputPath, dir, json) {
|
|
28611
28870
|
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
@@ -28615,7 +28874,7 @@ async function importTranscript(inputPath, dir, json) {
|
|
|
28615
28874
|
process.exit(1);
|
|
28616
28875
|
}
|
|
28617
28876
|
const outPath = join36(dir, "transcript.json");
|
|
28618
|
-
|
|
28877
|
+
writeFileSync13(outPath, JSON.stringify(words, null, 2));
|
|
28619
28878
|
patchCaptionHtml2(dir, words);
|
|
28620
28879
|
if (json) {
|
|
28621
28880
|
console.log(
|
|
@@ -28650,7 +28909,7 @@ async function transcribeAudio(inputPath, dir, opts) {
|
|
|
28650
28909
|
);
|
|
28651
28910
|
}
|
|
28652
28911
|
}
|
|
28653
|
-
|
|
28912
|
+
writeFileSync13(result.transcriptPath, JSON.stringify(words, null, 2));
|
|
28654
28913
|
patchCaptionHtml2(dir, words);
|
|
28655
28914
|
if (opts.json) {
|
|
28656
28915
|
console.log(
|
|
@@ -28731,7 +28990,7 @@ var init_transcribe2 = __esm({
|
|
|
28731
28990
|
},
|
|
28732
28991
|
async run({ args }) {
|
|
28733
28992
|
const inputPath = resolve23(args.input);
|
|
28734
|
-
if (!
|
|
28993
|
+
if (!existsSync36(inputPath)) {
|
|
28735
28994
|
console.error(c.error(`File not found: ${args.input}`));
|
|
28736
28995
|
process.exit(1);
|
|
28737
28996
|
}
|
|
@@ -28752,12 +29011,12 @@ var init_transcribe2 = __esm({
|
|
|
28752
29011
|
});
|
|
28753
29012
|
|
|
28754
29013
|
// src/tts/manager.ts
|
|
28755
|
-
import { existsSync as
|
|
29014
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync21 } from "fs";
|
|
28756
29015
|
import { homedir as homedir6 } from "os";
|
|
28757
29016
|
import { join as join37 } from "path";
|
|
28758
29017
|
async function ensureModel2(model = DEFAULT_MODEL2, options) {
|
|
28759
29018
|
const modelPath = join37(MODELS_DIR2, `${model}.onnx`);
|
|
28760
|
-
if (
|
|
29019
|
+
if (existsSync37(modelPath)) return modelPath;
|
|
28761
29020
|
const url = MODEL_URLS[model];
|
|
28762
29021
|
if (!url) {
|
|
28763
29022
|
throw new Error(
|
|
@@ -28767,18 +29026,18 @@ async function ensureModel2(model = DEFAULT_MODEL2, options) {
|
|
|
28767
29026
|
mkdirSync21(MODELS_DIR2, { recursive: true });
|
|
28768
29027
|
options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
|
|
28769
29028
|
await downloadFile(url, modelPath);
|
|
28770
|
-
if (!
|
|
29029
|
+
if (!existsSync37(modelPath)) {
|
|
28771
29030
|
throw new Error(`Model download failed: ${model}`);
|
|
28772
29031
|
}
|
|
28773
29032
|
return modelPath;
|
|
28774
29033
|
}
|
|
28775
29034
|
async function ensureVoices(options) {
|
|
28776
29035
|
const voicesPath = join37(VOICES_DIR, "voices-v1.0.bin");
|
|
28777
|
-
if (
|
|
29036
|
+
if (existsSync37(voicesPath)) return voicesPath;
|
|
28778
29037
|
mkdirSync21(VOICES_DIR, { recursive: true });
|
|
28779
29038
|
options?.onProgress?.("Downloading voice data (~27 MB)...");
|
|
28780
29039
|
await downloadFile(VOICES_URL, voicesPath);
|
|
28781
|
-
if (!
|
|
29040
|
+
if (!existsSync37(voicesPath)) {
|
|
28782
29041
|
throw new Error("Voice data download failed");
|
|
28783
29042
|
}
|
|
28784
29043
|
return voicesPath;
|
|
@@ -28815,19 +29074,19 @@ var synthesize_exports = {};
|
|
|
28815
29074
|
__export(synthesize_exports, {
|
|
28816
29075
|
synthesize: () => synthesize
|
|
28817
29076
|
});
|
|
28818
|
-
import { execFileSync as
|
|
28819
|
-
import { existsSync as
|
|
29077
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
29078
|
+
import { existsSync as existsSync38, writeFileSync as writeFileSync14, mkdirSync as mkdirSync22 } from "fs";
|
|
28820
29079
|
import { join as join38, dirname as dirname16 } from "path";
|
|
28821
29080
|
import { homedir as homedir7 } from "os";
|
|
28822
29081
|
function findPython() {
|
|
28823
29082
|
for (const name of ["python3", "python"]) {
|
|
28824
29083
|
try {
|
|
28825
|
-
const result =
|
|
29084
|
+
const result = execFileSync6("which", [name], {
|
|
28826
29085
|
encoding: "utf-8",
|
|
28827
29086
|
stdio: ["pipe", "pipe", "pipe"],
|
|
28828
29087
|
timeout: 5e3
|
|
28829
29088
|
}).trim();
|
|
28830
|
-
const version =
|
|
29089
|
+
const version = execFileSync6(result, ["--version"], {
|
|
28831
29090
|
encoding: "utf-8",
|
|
28832
29091
|
stdio: ["pipe", "pipe", "pipe"],
|
|
28833
29092
|
timeout: 5e3
|
|
@@ -28840,7 +29099,7 @@ function findPython() {
|
|
|
28840
29099
|
}
|
|
28841
29100
|
function hasPythonPackage(python, pkg) {
|
|
28842
29101
|
try {
|
|
28843
|
-
|
|
29102
|
+
execFileSync6(python, ["-c", `import ${pkg}`], {
|
|
28844
29103
|
stdio: ["pipe", "pipe", "pipe"],
|
|
28845
29104
|
timeout: 1e4
|
|
28846
29105
|
});
|
|
@@ -28850,9 +29109,9 @@ function hasPythonPackage(python, pkg) {
|
|
|
28850
29109
|
}
|
|
28851
29110
|
}
|
|
28852
29111
|
function ensureSynthScript() {
|
|
28853
|
-
if (!
|
|
29112
|
+
if (!existsSync38(SCRIPT_PATH)) {
|
|
28854
29113
|
mkdirSync22(SCRIPT_DIR, { recursive: true });
|
|
28855
|
-
|
|
29114
|
+
writeFileSync14(SCRIPT_PATH, SYNTH_SCRIPT);
|
|
28856
29115
|
}
|
|
28857
29116
|
return SCRIPT_PATH;
|
|
28858
29117
|
}
|
|
@@ -28882,7 +29141,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
28882
29141
|
mkdirSync22(dirname16(outputPath), { recursive: true });
|
|
28883
29142
|
options?.onProgress?.(`Generating speech with voice ${voice}...`);
|
|
28884
29143
|
try {
|
|
28885
|
-
const stdout2 =
|
|
29144
|
+
const stdout2 = execFileSync6(
|
|
28886
29145
|
python,
|
|
28887
29146
|
[scriptPath, modelPath, voicesPath, text, voice, String(speed), outputPath],
|
|
28888
29147
|
{
|
|
@@ -28891,7 +29150,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
28891
29150
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28892
29151
|
}
|
|
28893
29152
|
);
|
|
28894
|
-
if (!
|
|
29153
|
+
if (!existsSync38(outputPath)) {
|
|
28895
29154
|
throw new Error("Synthesis completed but no output file was created");
|
|
28896
29155
|
}
|
|
28897
29156
|
const lines = stdout2.trim().split("\n");
|
|
@@ -28903,7 +29162,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
28903
29162
|
durationSeconds: result.durationSeconds
|
|
28904
29163
|
};
|
|
28905
29164
|
} catch (err) {
|
|
28906
|
-
if (err instanceof SyntaxError &&
|
|
29165
|
+
if (err instanceof SyntaxError && existsSync38(outputPath)) {
|
|
28907
29166
|
throw new Error(
|
|
28908
29167
|
"Speech was generated but metadata could not be read. Check the output file manually."
|
|
28909
29168
|
);
|
|
@@ -28957,7 +29216,7 @@ __export(tts_exports, {
|
|
|
28957
29216
|
default: () => tts_default,
|
|
28958
29217
|
examples: () => examples11
|
|
28959
29218
|
});
|
|
28960
|
-
import { existsSync as
|
|
29219
|
+
import { existsSync as existsSync39, readFileSync as readFileSync26 } from "fs";
|
|
28961
29220
|
import { resolve as resolve24, extname as extname8 } from "path";
|
|
28962
29221
|
function listVoices(json) {
|
|
28963
29222
|
if (json) {
|
|
@@ -29047,8 +29306,8 @@ var init_tts = __esm({
|
|
|
29047
29306
|
}
|
|
29048
29307
|
let text;
|
|
29049
29308
|
const maybeFile = resolve24(args.input);
|
|
29050
|
-
if (
|
|
29051
|
-
text =
|
|
29309
|
+
if (existsSync39(maybeFile) && extname8(maybeFile).toLowerCase() === ".txt") {
|
|
29310
|
+
text = readFileSync26(maybeFile, "utf-8").trim();
|
|
29052
29311
|
if (!text) {
|
|
29053
29312
|
console.error(c.error("File is empty."));
|
|
29054
29313
|
process.exit(1);
|
|
@@ -29113,7 +29372,7 @@ __export(docs_exports, {
|
|
|
29113
29372
|
default: () => docs_default,
|
|
29114
29373
|
examples: () => examples12
|
|
29115
29374
|
});
|
|
29116
|
-
import { readFileSync as
|
|
29375
|
+
import { readFileSync as readFileSync27, existsSync as existsSync40 } from "fs";
|
|
29117
29376
|
import { resolve as resolve25, dirname as dirname17, join as join39 } from "path";
|
|
29118
29377
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
29119
29378
|
function docsDir() {
|
|
@@ -29121,7 +29380,7 @@ function docsDir() {
|
|
|
29121
29380
|
const dir = dirname17(thisFile);
|
|
29122
29381
|
const devPath = resolve25(dir, "..", "docs");
|
|
29123
29382
|
const builtPath = resolve25(dir, "docs");
|
|
29124
|
-
return
|
|
29383
|
+
return existsSync40(devPath) ? devPath : builtPath;
|
|
29125
29384
|
}
|
|
29126
29385
|
function formatInlineCode(line) {
|
|
29127
29386
|
return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
|
|
@@ -29219,11 +29478,11 @@ var init_docs = __esm({
|
|
|
29219
29478
|
process.exit(1);
|
|
29220
29479
|
}
|
|
29221
29480
|
const filePath = join39(docsDir(), entry.file);
|
|
29222
|
-
if (!
|
|
29481
|
+
if (!existsSync40(filePath)) {
|
|
29223
29482
|
console.error(c.error(`Doc file not found: ${filePath}`));
|
|
29224
29483
|
process.exit(1);
|
|
29225
29484
|
}
|
|
29226
|
-
const content =
|
|
29485
|
+
const content = readFileSync27(filePath, "utf-8");
|
|
29227
29486
|
console.log();
|
|
29228
29487
|
renderMarkdown(content);
|
|
29229
29488
|
}
|
|
@@ -29637,7 +29896,7 @@ var validate_exports = {};
|
|
|
29637
29896
|
__export(validate_exports, {
|
|
29638
29897
|
default: () => validate_default
|
|
29639
29898
|
});
|
|
29640
|
-
import { existsSync as
|
|
29899
|
+
import { existsSync as existsSync41, readFileSync as readFileSync28 } from "fs";
|
|
29641
29900
|
import { resolve as resolve26, join as join40, dirname as dirname18 } from "path";
|
|
29642
29901
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
29643
29902
|
async function validateInBrowser(projectDir, opts) {
|
|
@@ -29653,8 +29912,8 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
29653
29912
|
"dist",
|
|
29654
29913
|
"hyperframe.runtime.iife.js"
|
|
29655
29914
|
);
|
|
29656
|
-
if (
|
|
29657
|
-
const runtimeSource =
|
|
29915
|
+
if (existsSync41(runtimePath)) {
|
|
29916
|
+
const runtimeSource = readFileSync28(runtimePath, "utf-8");
|
|
29658
29917
|
html = html.replace(
|
|
29659
29918
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
29660
29919
|
`<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
|
|
@@ -29670,9 +29929,9 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
29670
29929
|
return;
|
|
29671
29930
|
}
|
|
29672
29931
|
const filePath = join40(projectDir, decodeURIComponent(url));
|
|
29673
|
-
if (
|
|
29932
|
+
if (existsSync41(filePath)) {
|
|
29674
29933
|
res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
|
|
29675
|
-
res.end(
|
|
29934
|
+
res.end(readFileSync28(filePath));
|
|
29676
29935
|
return;
|
|
29677
29936
|
}
|
|
29678
29937
|
res.writeHead(404);
|