hyperframes 0.1.13 → 0.1.15
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/README.md +5 -1
- package/dist/cli.js +940 -380
- package/dist/hyperframe-runtime.js +8 -4
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +8 -4
- package/dist/skills/hyperframes-captions/SKILL.md +195 -29
- package/dist/skills/hyperframes-compose/SKILL.md +1 -0
- package/dist/skills/hyperframes-compose/house-style.md +2 -2
- package/dist/studio/assets/index-CLmYRLY-.css +1 -0
- package/dist/studio/assets/index-CRvFpc0E.js +84 -0
- package/dist/studio/index.html +2 -2
- package/dist/templates/_shared/CLAUDE.md +14 -2
- package/dist/templates/blank/compositions/captions.html +3 -0
- package/dist/templates/blank/index.html +1 -1
- package/dist/templates/decision-tree/compositions/decision_tree.html +420 -0
- package/dist/templates/decision-tree/index.html +56 -0
- package/dist/templates/generators.ts +32 -0
- package/dist/templates/kinetic-type/compositions/main-graphics.html +588 -0
- package/dist/templates/kinetic-type/index.html +61 -0
- package/dist/templates/nyt-graph/compositions/nyt-chart.html +450 -0
- package/dist/templates/nyt-graph/index.html +51 -0
- package/dist/templates/product-promo/assets/figma-cursors.svg +30 -0
- package/dist/templates/product-promo/assets/figma-logo-pieces.svg +15 -0
- package/dist/templates/product-promo/assets/figma-logo-pills.svg +6 -0
- package/dist/templates/product-promo/compositions/scene1-logo-intro.html +189 -0
- package/dist/templates/product-promo/compositions/scene2-4-canvas.html +570 -0
- package/dist/templates/product-promo/compositions/scene5-logo-outro.html +226 -0
- package/dist/templates/product-promo/index.html +158 -0
- package/package.json +2 -2
- package/dist/studio/assets/index-2uBPlHR_.css +0 -1
- package/dist/studio/assets/index-uQ8cgxb3.js +0 -92
package/dist/cli.js
CHANGED
|
@@ -422,7 +422,7 @@ var VERSION;
|
|
|
422
422
|
var init_version = __esm({
|
|
423
423
|
"src/version.ts"() {
|
|
424
424
|
"use strict";
|
|
425
|
-
VERSION = true ? "0.1.
|
|
425
|
+
VERSION = true ? "0.1.15" : "0.0.0-dev";
|
|
426
426
|
}
|
|
427
427
|
});
|
|
428
428
|
|
|
@@ -2682,7 +2682,7 @@ import { join as join2 } from "path";
|
|
|
2682
2682
|
import { get as httpsGet } from "https";
|
|
2683
2683
|
import { pipeline } from "stream/promises";
|
|
2684
2684
|
function downloadFile(url, dest) {
|
|
2685
|
-
return new Promise((
|
|
2685
|
+
return new Promise((resolve22, reject) => {
|
|
2686
2686
|
const follow = (u) => {
|
|
2687
2687
|
httpsGet(u, (res) => {
|
|
2688
2688
|
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
@@ -2697,7 +2697,7 @@ function downloadFile(url, dest) {
|
|
|
2697
2697
|
return;
|
|
2698
2698
|
}
|
|
2699
2699
|
const file = createWriteStream(dest);
|
|
2700
|
-
pipeline(res, file).then(
|
|
2700
|
+
pipeline(res, file).then(resolve22).catch(reject);
|
|
2701
2701
|
}).on("error", reject);
|
|
2702
2702
|
};
|
|
2703
2703
|
follow(url);
|
|
@@ -2854,7 +2854,7 @@ var init_manager = __esm({
|
|
|
2854
2854
|
"src/whisper/manager.ts"() {
|
|
2855
2855
|
"use strict";
|
|
2856
2856
|
MODELS_DIR = join2(homedir2(), ".cache", "hyperframes", "whisper", "models");
|
|
2857
|
-
DEFAULT_MODEL = "
|
|
2857
|
+
DEFAULT_MODEL = "small.en";
|
|
2858
2858
|
BUILD_DIR = join2(homedir2(), ".cache", "hyperframes", "whisper", "whisper.cpp");
|
|
2859
2859
|
WHISPER_REPO = "https://github.com/ggml-org/whisper.cpp.git";
|
|
2860
2860
|
}
|
|
@@ -3143,6 +3143,169 @@ Examples:
|
|
|
3143
3143
|
}
|
|
3144
3144
|
});
|
|
3145
3145
|
|
|
3146
|
+
// src/whisper/normalize.ts
|
|
3147
|
+
var normalize_exports = {};
|
|
3148
|
+
__export(normalize_exports, {
|
|
3149
|
+
detectFormat: () => detectFormat,
|
|
3150
|
+
loadTranscript: () => loadTranscript,
|
|
3151
|
+
patchCaptionHtml: () => patchCaptionHtml
|
|
3152
|
+
});
|
|
3153
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3154
|
+
import { extname, join as join4 } from "path";
|
|
3155
|
+
function detectFormat(filePath) {
|
|
3156
|
+
const ext = extname(filePath).toLowerCase();
|
|
3157
|
+
if (ext === ".srt") return "srt";
|
|
3158
|
+
if (ext === ".vtt") return "vtt";
|
|
3159
|
+
if (ext === ".json") return detectJsonFormat(JSON.parse(readFileSync3(filePath, "utf-8")));
|
|
3160
|
+
throw new Error(`Unsupported transcript file extension: ${ext}. Use .json, .srt, or .vtt`);
|
|
3161
|
+
}
|
|
3162
|
+
function detectJsonFormat(raw) {
|
|
3163
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
3164
|
+
const obj = raw;
|
|
3165
|
+
if (obj.transcription && Array.isArray(obj.transcription)) return "whisper-cpp";
|
|
3166
|
+
if (obj.words && Array.isArray(obj.words)) return "openai";
|
|
3167
|
+
}
|
|
3168
|
+
if (Array.isArray(raw) && raw[0]?.text !== void 0 && raw[0]?.start !== void 0) {
|
|
3169
|
+
return "words-json";
|
|
3170
|
+
}
|
|
3171
|
+
throw new Error(
|
|
3172
|
+
"Unrecognized JSON transcript format. Expected whisper.cpp (transcription[].tokens), OpenAI API (words[]), or normalized ([{text, start, end}])."
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3175
|
+
function parseWhisperCpp(data) {
|
|
3176
|
+
const words = [];
|
|
3177
|
+
const transcription = data.transcription;
|
|
3178
|
+
for (const seg of transcription ?? []) {
|
|
3179
|
+
for (const token of seg.tokens ?? []) {
|
|
3180
|
+
const text = (token.text ?? "").trim();
|
|
3181
|
+
if (!text || text.startsWith("[_") || text.startsWith("[BLANK")) continue;
|
|
3182
|
+
const isPunctuation = /^[.,!?;:'")\]}>…–—-]+$/.test(text);
|
|
3183
|
+
const lastWord = words[words.length - 1];
|
|
3184
|
+
if (isPunctuation && lastWord) {
|
|
3185
|
+
lastWord.text += text;
|
|
3186
|
+
lastWord.end = round3((token.offsets?.to ?? 0) / 1e3);
|
|
3187
|
+
continue;
|
|
3188
|
+
}
|
|
3189
|
+
words.push({
|
|
3190
|
+
text,
|
|
3191
|
+
start: round3((token.offsets?.from ?? 0) / 1e3),
|
|
3192
|
+
end: round3((token.offsets?.to ?? 0) / 1e3)
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
return words;
|
|
3197
|
+
}
|
|
3198
|
+
function parseOpenAI(data) {
|
|
3199
|
+
const rawWords = data.words ?? [];
|
|
3200
|
+
return rawWords.map((w) => ({
|
|
3201
|
+
text: (w.word ?? w.text ?? "").trim(),
|
|
3202
|
+
start: round3(w.start ?? 0),
|
|
3203
|
+
end: round3(w.end ?? 0)
|
|
3204
|
+
})).filter((w) => w.text.length > 0);
|
|
3205
|
+
}
|
|
3206
|
+
function parseSrt(content) {
|
|
3207
|
+
const blocks = content.trim().split(/\n\n+/);
|
|
3208
|
+
const words = [];
|
|
3209
|
+
for (const block of blocks) {
|
|
3210
|
+
const lines = block.trim().split("\n");
|
|
3211
|
+
const timeLine = lines.find((l) => l.includes("-->"));
|
|
3212
|
+
if (!timeLine) continue;
|
|
3213
|
+
const [startStr, endStr] = timeLine.split("-->").map((s) => s.trim());
|
|
3214
|
+
if (!startStr || !endStr) continue;
|
|
3215
|
+
const text = lines.slice(lines.indexOf(timeLine) + 1).join(" ").replace(/<[^>]+>/g, "").trim();
|
|
3216
|
+
if (!text) continue;
|
|
3217
|
+
words.push({
|
|
3218
|
+
text,
|
|
3219
|
+
start: parseSrtTimestamp(startStr),
|
|
3220
|
+
end: parseSrtTimestamp(endStr)
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
return words;
|
|
3224
|
+
}
|
|
3225
|
+
function parseVtt(content) {
|
|
3226
|
+
const body = content.replace(/^WEBVTT[^\n]*\n/, "").replace(/^[A-Z-]+:.*\n/gm, "");
|
|
3227
|
+
const blocks = body.trim().split(/\n\n+/);
|
|
3228
|
+
const words = [];
|
|
3229
|
+
for (const block of blocks) {
|
|
3230
|
+
const lines = block.trim().split("\n");
|
|
3231
|
+
const timeLine = lines.find((l) => l.includes("-->"));
|
|
3232
|
+
if (!timeLine) continue;
|
|
3233
|
+
const [startStr, endStr] = timeLine.split("-->").map((s) => s.trim());
|
|
3234
|
+
if (!startStr || !endStr) continue;
|
|
3235
|
+
const text = lines.slice(lines.indexOf(timeLine) + 1).join(" ").replace(/<[^>]+>/g, "").trim();
|
|
3236
|
+
if (!text) continue;
|
|
3237
|
+
words.push({
|
|
3238
|
+
text,
|
|
3239
|
+
start: parseVttTimestamp(startStr),
|
|
3240
|
+
end: parseVttTimestamp(endStr)
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
return words;
|
|
3244
|
+
}
|
|
3245
|
+
function parseSrtTimestamp(ts) {
|
|
3246
|
+
const m = ts.match(/(\d+):(\d+):(\d+)[,.](\d+)/);
|
|
3247
|
+
if (!m) return 0;
|
|
3248
|
+
return parseInt(m[1], 10) * 3600 + parseInt(m[2], 10) * 60 + parseInt(m[3], 10) + parseInt(m[4].padEnd(3, "0"), 10) / 1e3;
|
|
3249
|
+
}
|
|
3250
|
+
function parseVttTimestamp(ts) {
|
|
3251
|
+
const parts = ts.split(":");
|
|
3252
|
+
if (parts.length === 3) return parseSrtTimestamp(ts);
|
|
3253
|
+
if (parts.length === 2) {
|
|
3254
|
+
const [min, secMs] = parts;
|
|
3255
|
+
const [sec, ms] = (secMs ?? "0.0").split(".");
|
|
3256
|
+
return parseInt(min, 10) * 60 + parseInt(sec, 10) + parseInt((ms ?? "0").padEnd(3, "0"), 10) / 1e3;
|
|
3257
|
+
}
|
|
3258
|
+
return 0;
|
|
3259
|
+
}
|
|
3260
|
+
function round3(n) {
|
|
3261
|
+
return Math.round(n * 1e3) / 1e3;
|
|
3262
|
+
}
|
|
3263
|
+
function loadTranscript(filePath) {
|
|
3264
|
+
const ext = extname(filePath).toLowerCase();
|
|
3265
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
3266
|
+
if (ext === ".srt") return { words: parseSrt(content), format: "srt" };
|
|
3267
|
+
if (ext === ".vtt") return { words: parseVtt(content), format: "vtt" };
|
|
3268
|
+
const parsed = JSON.parse(content);
|
|
3269
|
+
const format = detectJsonFormat(parsed);
|
|
3270
|
+
const words = format === "whisper-cpp" ? parseWhisperCpp(parsed) : format === "openai" ? parseOpenAI(parsed) : parsed.map((w) => ({
|
|
3271
|
+
text: w.text.trim(),
|
|
3272
|
+
start: round3(w.start),
|
|
3273
|
+
end: round3(w.end)
|
|
3274
|
+
}));
|
|
3275
|
+
return { words, format };
|
|
3276
|
+
}
|
|
3277
|
+
function patchCaptionHtml(dir, words) {
|
|
3278
|
+
if (words.length === 0) return;
|
|
3279
|
+
const wordsJson = JSON.stringify(words, null, 2).replace(/\n/g, "\n ");
|
|
3280
|
+
let htmlFiles;
|
|
3281
|
+
try {
|
|
3282
|
+
htmlFiles = readdirSync2(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join4(e.parentPath ?? e.path, e.name));
|
|
3283
|
+
} catch {
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
for (const file of htmlFiles) {
|
|
3287
|
+
let content = readFileSync3(file, "utf-8");
|
|
3288
|
+
const scriptBlocks = content.match(/<script>[\s\S]*?<\/script>/g) ?? [];
|
|
3289
|
+
let scriptMatch = null;
|
|
3290
|
+
let transcriptMatch = null;
|
|
3291
|
+
for (const block of scriptBlocks) {
|
|
3292
|
+
scriptMatch = scriptMatch ?? block.match(/const script = \[[\s\S]*?\];/);
|
|
3293
|
+
transcriptMatch = transcriptMatch ?? block.match(/const TRANSCRIPT = \[[\s\S]*?\];/);
|
|
3294
|
+
}
|
|
3295
|
+
const match = scriptMatch ?? transcriptMatch;
|
|
3296
|
+
if (match) {
|
|
3297
|
+
const varName = scriptMatch ? "script" : "TRANSCRIPT";
|
|
3298
|
+
content = content.replace(match[0], `const ${varName} = ${wordsJson};`);
|
|
3299
|
+
writeFileSync2(file, content, "utf-8");
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
var init_normalize = __esm({
|
|
3304
|
+
"src/whisper/normalize.ts"() {
|
|
3305
|
+
"use strict";
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
|
|
3146
3309
|
// ../core/src/parsers/gsapParser.ts
|
|
3147
3310
|
function parseObjectLiteral(str) {
|
|
3148
3311
|
const result = {};
|
|
@@ -3533,6 +3696,19 @@ function lintHyperframeHtml(html, options = {}) {
|
|
|
3533
3696
|
)
|
|
3534
3697
|
});
|
|
3535
3698
|
}
|
|
3699
|
+
const clipIds = /* @__PURE__ */ new Map();
|
|
3700
|
+
const clipClasses = /* @__PURE__ */ new Map();
|
|
3701
|
+
for (const tag of tags) {
|
|
3702
|
+
const classAttr = readAttr(tag.raw, "class") || "";
|
|
3703
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
3704
|
+
if (!classes.includes("clip")) continue;
|
|
3705
|
+
const id = readAttr(tag.raw, "id");
|
|
3706
|
+
const info = { tag: tag.name, id: id || "", classes: classAttr };
|
|
3707
|
+
if (id) clipIds.set(`#${id}`, info);
|
|
3708
|
+
for (const cls of classes) {
|
|
3709
|
+
if (cls !== "clip") clipClasses.set(`.${cls}`, info);
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3536
3712
|
const classUsage = countClassUsage(tags);
|
|
3537
3713
|
for (const script of scripts) {
|
|
3538
3714
|
const localTimelineCompId = readRegisteredTimelineCompositionId(script.content);
|
|
@@ -3575,24 +3751,39 @@ ${right.raw}`)
|
|
|
3575
3751
|
});
|
|
3576
3752
|
}
|
|
3577
3753
|
}
|
|
3754
|
+
for (const win of gsapWindows) {
|
|
3755
|
+
const sel = win.targetSelector;
|
|
3756
|
+
const clipInfo = clipIds.get(sel) || clipClasses.get(sel);
|
|
3757
|
+
if (!clipInfo) continue;
|
|
3758
|
+
const elDesc = `<${clipInfo.tag}${clipInfo.id ? ` id="${clipInfo.id}"` : ""} class="${clipInfo.classes}">`;
|
|
3759
|
+
pushFinding({
|
|
3760
|
+
code: "gsap_animates_clip_element",
|
|
3761
|
+
severity: "error",
|
|
3762
|
+
message: `GSAP animation targets a clip element. Selector "${sel}" resolves to element ${elDesc}. The framework manages clip visibility \u2014 animate an inner wrapper instead.`,
|
|
3763
|
+
selector: sel,
|
|
3764
|
+
elementId: clipInfo.id || void 0,
|
|
3765
|
+
fixHint: "Wrap content in a child <div> and target that with GSAP.",
|
|
3766
|
+
snippet: truncateSnippet(win.raw)
|
|
3767
|
+
});
|
|
3768
|
+
}
|
|
3578
3769
|
if (!localTimelineCompId || localTimelineCompId === rootCompositionId) {
|
|
3579
3770
|
continue;
|
|
3580
3771
|
}
|
|
3581
|
-
for (const
|
|
3582
|
-
if (!isSuspiciousGlobalSelector(
|
|
3772
|
+
for (const win of gsapWindows) {
|
|
3773
|
+
if (!isSuspiciousGlobalSelector(win.targetSelector)) {
|
|
3583
3774
|
continue;
|
|
3584
3775
|
}
|
|
3585
|
-
const className = getSingleClassSelector(
|
|
3776
|
+
const className = getSingleClassSelector(win.targetSelector);
|
|
3586
3777
|
if (className && (classUsage.get(className) || 0) < 2) {
|
|
3587
3778
|
continue;
|
|
3588
3779
|
}
|
|
3589
3780
|
pushFinding({
|
|
3590
3781
|
code: "unscoped_gsap_selector",
|
|
3591
3782
|
severity: "warning",
|
|
3592
|
-
message: `Timeline "${localTimelineCompId}" uses unscoped selector "${
|
|
3593
|
-
selector:
|
|
3594
|
-
fixHint: `Scope the selector: \`[data-composition-id="${localTimelineCompId}"] ${
|
|
3595
|
-
snippet: truncateSnippet(
|
|
3783
|
+
message: `Timeline "${localTimelineCompId}" uses unscoped selector "${win.targetSelector}" that will target elements in ALL compositions when bundled, causing data loss (opacity, transforms, etc.).`,
|
|
3784
|
+
selector: win.targetSelector,
|
|
3785
|
+
fixHint: `Scope the selector: \`[data-composition-id="${localTimelineCompId}"] ${win.targetSelector}\` or use a unique id.`,
|
|
3786
|
+
snippet: truncateSnippet(win.raw)
|
|
3596
3787
|
});
|
|
3597
3788
|
}
|
|
3598
3789
|
}
|
|
@@ -3845,6 +4036,58 @@ ${right.raw}`)
|
|
|
3845
4036
|
}
|
|
3846
4037
|
}
|
|
3847
4038
|
}
|
|
4039
|
+
for (const script of scripts) {
|
|
4040
|
+
const content = script.content;
|
|
4041
|
+
const hasExitTween = /\.to\s*\([^,]+,\s*\{[^}]*opacity\s*:\s*0/.test(content);
|
|
4042
|
+
const hasHardKill = /\.set\s*\([^,]+,\s*\{[^}]*(?:visibility\s*:\s*["']hidden["']|opacity\s*:\s*0)/.test(content);
|
|
4043
|
+
const hasCaptionLoop = /forEach|\.forEach\s*\(/.test(content) && /createElement|caption|group|cg-/.test(content);
|
|
4044
|
+
if (hasCaptionLoop && hasExitTween && !hasHardKill) {
|
|
4045
|
+
pushFinding({
|
|
4046
|
+
code: "caption_exit_missing_hard_kill",
|
|
4047
|
+
severity: "warning",
|
|
4048
|
+
message: "Caption exit animations (tl.to with opacity: 0) detected without a hard tl.set kill. Exit tweens can fail when karaoke word-level tweens conflict, leaving captions stuck on screen.",
|
|
4049
|
+
fixHint: 'Add `tl.set(groupEl, { opacity: 0, visibility: "hidden" }, group.end)` after every exit tl.to animation as a deterministic kill.'
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
for (const style of styles) {
|
|
4054
|
+
const content = style.content;
|
|
4055
|
+
const captionBlocks = content.matchAll(
|
|
4056
|
+
/(\.caption[-_]?(?:group|container|text|line|word)|#caption[-_]?container)\s*\{([^}]+)\}/gi
|
|
4057
|
+
);
|
|
4058
|
+
for (const [, selector, body] of captionBlocks) {
|
|
4059
|
+
if (!body) continue;
|
|
4060
|
+
const hasNowrap = /white-space\s*:\s*nowrap/i.test(body);
|
|
4061
|
+
const hasMaxWidth = /max-width/i.test(body);
|
|
4062
|
+
if (hasNowrap && !hasMaxWidth) {
|
|
4063
|
+
pushFinding({
|
|
4064
|
+
code: "caption_text_overflow_risk",
|
|
4065
|
+
severity: "warning",
|
|
4066
|
+
selector: (selector ?? "").trim(),
|
|
4067
|
+
message: `Caption selector "${(selector ?? "").trim()}" has white-space: nowrap but no max-width. Long phrases will clip off-screen.`,
|
|
4068
|
+
fixHint: "Add max-width: 1600px (landscape) or max-width: 900px (portrait) and overflow: hidden."
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
for (const style of styles) {
|
|
4074
|
+
const content = style.content;
|
|
4075
|
+
const captionBlocks = content.matchAll(
|
|
4076
|
+
/(\.caption[-_]?(?:group|container|text|line)|#caption[-_]?container)\s*\{([^}]+)\}/gi
|
|
4077
|
+
);
|
|
4078
|
+
for (const [, selector, body] of captionBlocks) {
|
|
4079
|
+
if (!body) continue;
|
|
4080
|
+
if (/position\s*:\s*relative/i.test(body)) {
|
|
4081
|
+
pushFinding({
|
|
4082
|
+
code: "caption_container_relative_position",
|
|
4083
|
+
severity: "warning",
|
|
4084
|
+
selector: (selector ?? "").trim(),
|
|
4085
|
+
message: `Caption selector "${(selector ?? "").trim()}" uses position: relative which causes overflow and breaks caption stacking.`,
|
|
4086
|
+
fixHint: "Use position: absolute for all caption elements."
|
|
4087
|
+
});
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
3848
4091
|
{
|
|
3849
4092
|
const externalScriptRe = /<script\b[^>]*\bsrc=["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
3850
4093
|
let match;
|
|
@@ -3863,11 +4106,13 @@ ${right.raw}`)
|
|
|
3863
4106
|
}
|
|
3864
4107
|
}
|
|
3865
4108
|
const errorCount = findings.filter((finding) => finding.severity === "error").length;
|
|
3866
|
-
const warningCount = findings.
|
|
4109
|
+
const warningCount = findings.filter((finding) => finding.severity === "warning").length;
|
|
4110
|
+
const infoCount = findings.filter((finding) => finding.severity === "info").length;
|
|
3867
4111
|
return {
|
|
3868
4112
|
ok: errorCount === 0,
|
|
3869
4113
|
errorCount,
|
|
3870
4114
|
warningCount,
|
|
4115
|
+
infoCount,
|
|
3871
4116
|
findings
|
|
3872
4117
|
};
|
|
3873
4118
|
}
|
|
@@ -4243,30 +4488,33 @@ var init_lint = __esm({
|
|
|
4243
4488
|
});
|
|
4244
4489
|
|
|
4245
4490
|
// src/utils/lintProject.ts
|
|
4246
|
-
import { existsSync as existsSync5, readFileSync as
|
|
4247
|
-
import { join as
|
|
4491
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3 } from "fs";
|
|
4492
|
+
import { join as join5, resolve } from "path";
|
|
4248
4493
|
function lintProject(project) {
|
|
4249
4494
|
const results = [];
|
|
4250
4495
|
let totalErrors = 0;
|
|
4251
4496
|
let totalWarnings = 0;
|
|
4252
|
-
|
|
4497
|
+
let totalInfos = 0;
|
|
4498
|
+
const rootHtml = readFileSync4(project.indexPath, "utf-8");
|
|
4253
4499
|
const rootResult = lintHyperframeHtml(rootHtml, { filePath: project.indexPath });
|
|
4254
4500
|
results.push({ file: "index.html", result: rootResult });
|
|
4255
4501
|
totalErrors += rootResult.errorCount;
|
|
4256
4502
|
totalWarnings += rootResult.warningCount;
|
|
4503
|
+
totalInfos += rootResult.infoCount;
|
|
4257
4504
|
const compositionsDir = resolve(project.dir, "compositions");
|
|
4258
4505
|
if (existsSync5(compositionsDir)) {
|
|
4259
|
-
const files =
|
|
4506
|
+
const files = readdirSync3(compositionsDir).filter((f) => f.endsWith(".html"));
|
|
4260
4507
|
for (const file of files) {
|
|
4261
|
-
const filePath =
|
|
4262
|
-
const html =
|
|
4508
|
+
const filePath = join5(compositionsDir, file);
|
|
4509
|
+
const html = readFileSync4(filePath, "utf-8");
|
|
4263
4510
|
const result = lintHyperframeHtml(html, { filePath });
|
|
4264
4511
|
results.push({ file: `compositions/${file}`, result });
|
|
4265
4512
|
totalErrors += result.errorCount;
|
|
4266
4513
|
totalWarnings += result.warningCount;
|
|
4514
|
+
totalInfos += result.infoCount;
|
|
4267
4515
|
}
|
|
4268
4516
|
}
|
|
4269
|
-
return { results, totalErrors, totalWarnings };
|
|
4517
|
+
return { results, totalErrors, totalWarnings, totalInfos };
|
|
4270
4518
|
}
|
|
4271
4519
|
function shouldBlockRender(strictErrors, strictAll, totalErrors, totalWarnings) {
|
|
4272
4520
|
return strictErrors && totalErrors > 0 || strictAll && (totalErrors > 0 || totalWarnings > 0);
|
|
@@ -4279,14 +4527,20 @@ var init_lintProject = __esm({
|
|
|
4279
4527
|
});
|
|
4280
4528
|
|
|
4281
4529
|
// src/utils/lintFormat.ts
|
|
4282
|
-
function formatLintFindings({ results, totalErrors, totalWarnings }, options = {}) {
|
|
4283
|
-
const {
|
|
4530
|
+
function formatLintFindings({ results, totalErrors, totalWarnings, totalInfos }, options = {}) {
|
|
4531
|
+
const {
|
|
4532
|
+
showElementId = true,
|
|
4533
|
+
showSummary = false,
|
|
4534
|
+
errorsFirst = false,
|
|
4535
|
+
verbose = false
|
|
4536
|
+
} = options;
|
|
4284
4537
|
const lines = [];
|
|
4285
4538
|
const multiFile = results.length > 1;
|
|
4286
4539
|
for (const { file, result } of results) {
|
|
4287
4540
|
if (result.findings.length === 0) continue;
|
|
4288
4541
|
const format = (finding) => {
|
|
4289
|
-
|
|
4542
|
+
if (!verbose && finding.severity === "info") return;
|
|
4543
|
+
const prefix = finding.severity === "error" ? c.error("\u2717") : finding.severity === "warning" ? c.warn("\u26A0") : c.dim("\u2139");
|
|
4290
4544
|
const fileLabel = multiFile ? c.dim(`[${file}] `) : "";
|
|
4291
4545
|
const loc = showElementId && finding.elementId ? ` ${c.accent(`[${finding.elementId}]`)}` : "";
|
|
4292
4546
|
lines.push(` ${prefix} ${fileLabel}${c.bold(finding.code)}${loc}: ${finding.message}`);
|
|
@@ -4295,6 +4549,9 @@ function formatLintFindings({ results, totalErrors, totalWarnings }, options = {
|
|
|
4295
4549
|
if (errorsFirst) {
|
|
4296
4550
|
for (const f of result.findings) if (f.severity === "error") format(f);
|
|
4297
4551
|
for (const f of result.findings) if (f.severity === "warning") format(f);
|
|
4552
|
+
if (verbose) {
|
|
4553
|
+
for (const f of result.findings) if (f.severity === "info") format(f);
|
|
4554
|
+
}
|
|
4298
4555
|
} else {
|
|
4299
4556
|
for (const f of result.findings) format(f);
|
|
4300
4557
|
}
|
|
@@ -4302,7 +4559,9 @@ function formatLintFindings({ results, totalErrors, totalWarnings }, options = {
|
|
|
4302
4559
|
if (showSummary) {
|
|
4303
4560
|
const icon = totalErrors > 0 ? c.error("\u25C7") : c.success("\u25C7");
|
|
4304
4561
|
lines.push("");
|
|
4305
|
-
|
|
4562
|
+
const summaryParts = [`${totalErrors} error(s)`, `${totalWarnings} warning(s)`];
|
|
4563
|
+
if (verbose && totalInfos > 0) summaryParts.push(`${totalInfos} info(s)`);
|
|
4564
|
+
lines.push(`${icon} ${summaryParts.join(", ")}`);
|
|
4306
4565
|
}
|
|
4307
4566
|
return lines;
|
|
4308
4567
|
}
|
|
@@ -4357,19 +4616,19 @@ var init_fileWatcher = __esm({
|
|
|
4357
4616
|
});
|
|
4358
4617
|
|
|
4359
4618
|
// ../core/src/studio-api/helpers/safePath.ts
|
|
4360
|
-
import { resolve as resolve2, sep, join as
|
|
4361
|
-
import { readdirSync as
|
|
4619
|
+
import { resolve as resolve2, sep, join as join6 } from "path";
|
|
4620
|
+
import { readdirSync as readdirSync4 } from "fs";
|
|
4362
4621
|
function isSafePath(base, resolved) {
|
|
4363
4622
|
const norm = resolve2(base) + sep;
|
|
4364
4623
|
return resolved.startsWith(norm) || resolved === resolve2(base);
|
|
4365
4624
|
}
|
|
4366
4625
|
function walkDir(dir, prefix = "") {
|
|
4367
4626
|
const files = [];
|
|
4368
|
-
for (const entry of
|
|
4627
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
4369
4628
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
4370
4629
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
4371
4630
|
if (entry.isDirectory()) {
|
|
4372
|
-
files.push(...walkDir(
|
|
4631
|
+
files.push(...walkDir(join6(dir, entry.name), rel));
|
|
4373
4632
|
} else {
|
|
4374
4633
|
files.push(rel);
|
|
4375
4634
|
}
|
|
@@ -4414,7 +4673,7 @@ var init_projects = __esm({
|
|
|
4414
4673
|
});
|
|
4415
4674
|
|
|
4416
4675
|
// ../core/src/studio-api/routes/files.ts
|
|
4417
|
-
import { existsSync as existsSync6, readFileSync as
|
|
4676
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
4418
4677
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
4419
4678
|
function registerFileRoutes(api, adapter2) {
|
|
4420
4679
|
api.get("/projects/:id/files/*", async (c2) => {
|
|
@@ -4425,7 +4684,7 @@ function registerFileRoutes(api, adapter2) {
|
|
|
4425
4684
|
if (!isSafePath(project.dir, file) || !existsSync6(file)) {
|
|
4426
4685
|
return c2.text("not found", 404);
|
|
4427
4686
|
}
|
|
4428
|
-
const content =
|
|
4687
|
+
const content = readFileSync5(file, "utf-8");
|
|
4429
4688
|
return c2.json({ filename: filePath, content });
|
|
4430
4689
|
});
|
|
4431
4690
|
api.put("/projects/:id/files/*", async (c2) => {
|
|
@@ -4439,7 +4698,7 @@ function registerFileRoutes(api, adapter2) {
|
|
|
4439
4698
|
const dir = dirname2(file);
|
|
4440
4699
|
if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
|
|
4441
4700
|
const body = await c2.req.text();
|
|
4442
|
-
|
|
4701
|
+
writeFileSync3(file, body, "utf-8");
|
|
4443
4702
|
return c2.json({ ok: true });
|
|
4444
4703
|
});
|
|
4445
4704
|
}
|
|
@@ -4489,18 +4748,18 @@ var init_mime = __esm({
|
|
|
4489
4748
|
});
|
|
4490
4749
|
|
|
4491
4750
|
// ../core/src/studio-api/helpers/subComposition.ts
|
|
4492
|
-
import { existsSync as existsSync7, readFileSync as
|
|
4493
|
-
import { join as
|
|
4751
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
4752
|
+
import { join as join7 } from "path";
|
|
4494
4753
|
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref) {
|
|
4495
|
-
const compFile =
|
|
4754
|
+
const compFile = join7(projectDir, compPath);
|
|
4496
4755
|
if (!existsSync7(compFile)) return null;
|
|
4497
|
-
const rawComp =
|
|
4756
|
+
const rawComp = readFileSync6(compFile, "utf-8");
|
|
4498
4757
|
const templateMatch = rawComp.match(/<template[^>]*>([\s\S]*)<\/template>/i);
|
|
4499
4758
|
const content = templateMatch?.[1] ?? rawComp;
|
|
4500
|
-
const indexPath =
|
|
4759
|
+
const indexPath = join7(projectDir, "index.html");
|
|
4501
4760
|
let headContent = "";
|
|
4502
4761
|
if (existsSync7(indexPath)) {
|
|
4503
|
-
const indexHtml =
|
|
4762
|
+
const indexHtml = readFileSync6(indexPath, "utf-8");
|
|
4504
4763
|
const headMatch = indexHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
|
|
4505
4764
|
headContent = headMatch?.[1] ?? "";
|
|
4506
4765
|
}
|
|
@@ -4534,7 +4793,7 @@ var init_subComposition = __esm({
|
|
|
4534
4793
|
});
|
|
4535
4794
|
|
|
4536
4795
|
// ../core/src/studio-api/routes/preview.ts
|
|
4537
|
-
import { existsSync as existsSync8, readFileSync as
|
|
4796
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, statSync } from "fs";
|
|
4538
4797
|
import { resolve as resolve4 } from "path";
|
|
4539
4798
|
function registerPreviewRoutes(api, adapter2) {
|
|
4540
4799
|
api.get("/projects/:id/preview", async (c2) => {
|
|
@@ -4545,7 +4804,7 @@ function registerPreviewRoutes(api, adapter2) {
|
|
|
4545
4804
|
if (!bundled) {
|
|
4546
4805
|
const indexPath = resolve4(project.dir, "index.html");
|
|
4547
4806
|
if (!existsSync8(indexPath)) return c2.text("not found", 404);
|
|
4548
|
-
bundled =
|
|
4807
|
+
bundled = readFileSync7(indexPath, "utf-8");
|
|
4549
4808
|
}
|
|
4550
4809
|
if (!bundled.includes("hyperframe.runtime") && !bundled.includes("hyperframes-preview-runtime")) {
|
|
4551
4810
|
const runtimeTag = `<script src="${adapter2.runtimeUrl}"></script>`;
|
|
@@ -4560,7 +4819,7 @@ ${runtimeTag}`;
|
|
|
4560
4819
|
return c2.html(bundled);
|
|
4561
4820
|
} catch {
|
|
4562
4821
|
const file = resolve4(project.dir, "index.html");
|
|
4563
|
-
if (existsSync8(file)) return c2.html(
|
|
4822
|
+
if (existsSync8(file)) return c2.html(readFileSync7(file, "utf-8"));
|
|
4564
4823
|
return c2.text("not found", 404);
|
|
4565
4824
|
}
|
|
4566
4825
|
});
|
|
@@ -4591,7 +4850,7 @@ ${runtimeTag}`;
|
|
|
4591
4850
|
}
|
|
4592
4851
|
const contentType = getMimeType(subPath);
|
|
4593
4852
|
const isText2 = /\.(html|css|js|json|svg|txt|md)$/i.test(subPath);
|
|
4594
|
-
const buffer = isText2 ? Buffer.from(
|
|
4853
|
+
const buffer = isText2 ? Buffer.from(readFileSync7(file, "utf-8"), "utf-8") : readFileSync7(file);
|
|
4595
4854
|
const totalSize2 = buffer.length;
|
|
4596
4855
|
const rangeHeader = c2.req.header("Range");
|
|
4597
4856
|
if (rangeHeader) {
|
|
@@ -4631,8 +4890,8 @@ var init_preview = __esm({
|
|
|
4631
4890
|
});
|
|
4632
4891
|
|
|
4633
4892
|
// ../core/src/studio-api/routes/lint.ts
|
|
4634
|
-
import { readFileSync as
|
|
4635
|
-
import { join as
|
|
4893
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
4894
|
+
import { join as join8 } from "path";
|
|
4636
4895
|
function registerLintRoutes(api, adapter2) {
|
|
4637
4896
|
api.get("/projects/:id/lint", async (c2) => {
|
|
4638
4897
|
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
@@ -4641,7 +4900,7 @@ function registerLintRoutes(api, adapter2) {
|
|
|
4641
4900
|
const htmlFiles = walkDir(project.dir).filter((f) => f.endsWith(".html"));
|
|
4642
4901
|
const allFindings = [];
|
|
4643
4902
|
for (const file of htmlFiles) {
|
|
4644
|
-
const content =
|
|
4903
|
+
const content = readFileSync8(join8(project.dir, file), "utf-8");
|
|
4645
4904
|
const result = await adapter2.lint(content, { filePath: file });
|
|
4646
4905
|
if (result?.findings) {
|
|
4647
4906
|
for (const f of result.findings) {
|
|
@@ -4665,8 +4924,8 @@ var init_lint2 = __esm({
|
|
|
4665
4924
|
|
|
4666
4925
|
// ../core/src/studio-api/routes/render.ts
|
|
4667
4926
|
import { streamSSE } from "hono/streaming";
|
|
4668
|
-
import { existsSync as existsSync9, readFileSync as
|
|
4669
|
-
import { join as
|
|
4927
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, mkdirSync as mkdirSync5, unlinkSync, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
|
|
4928
|
+
import { join as join9 } from "path";
|
|
4670
4929
|
function registerRenderRoutes(api, adapter2) {
|
|
4671
4930
|
const renderJobs = /* @__PURE__ */ new Map();
|
|
4672
4931
|
const TTL_MS = 3e5;
|
|
@@ -4703,7 +4962,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4703
4962
|
const rendersDir = adapter2.rendersDir(project);
|
|
4704
4963
|
if (!existsSync9(rendersDir)) mkdirSync5(rendersDir, { recursive: true });
|
|
4705
4964
|
const ext = format === "webm" ? ".webm" : ".mp4";
|
|
4706
|
-
const outputPath =
|
|
4965
|
+
const outputPath = join9(rendersDir, `${jobId}${ext}`);
|
|
4707
4966
|
const jobState = adapter2.startRender({
|
|
4708
4967
|
project,
|
|
4709
4968
|
outputPath,
|
|
@@ -4764,7 +5023,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4764
5023
|
const isWebm = job.outputPath.endsWith(".webm");
|
|
4765
5024
|
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
4766
5025
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
4767
|
-
const content =
|
|
5026
|
+
const content = readFileSync9(job.outputPath);
|
|
4768
5027
|
return new Response(content, {
|
|
4769
5028
|
headers: {
|
|
4770
5029
|
"Content-Type": contentType,
|
|
@@ -4783,7 +5042,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4783
5042
|
const isWebm = job.outputPath.endsWith(".webm");
|
|
4784
5043
|
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
4785
5044
|
const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
|
|
4786
|
-
const content =
|
|
5045
|
+
const content = readFileSync9(job.outputPath);
|
|
4787
5046
|
return new Response(content, {
|
|
4788
5047
|
headers: {
|
|
4789
5048
|
"Content-Type": contentType,
|
|
@@ -4797,7 +5056,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4797
5056
|
if (state.id === jobId && state.outputPath) {
|
|
4798
5057
|
const dir = state.outputPath.replace(/\/[^/]+$/, "");
|
|
4799
5058
|
for (const ext of [".mp4", ".webm", ".meta.json"]) {
|
|
4800
|
-
const fp =
|
|
5059
|
+
const fp = join9(dir, `${jobId}${ext}`);
|
|
4801
5060
|
if (existsSync9(fp)) unlinkSync(fp);
|
|
4802
5061
|
}
|
|
4803
5062
|
break;
|
|
@@ -4806,21 +5065,41 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4806
5065
|
renderJobs.delete(jobId);
|
|
4807
5066
|
return c2.json({ deleted: true });
|
|
4808
5067
|
});
|
|
5068
|
+
api.get("/projects/:id/renders/file/*", async (c2) => {
|
|
5069
|
+
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
5070
|
+
if (!project) return c2.json({ error: "not found" }, 404);
|
|
5071
|
+
const filename = c2.req.path.split("/renders/file/")[1];
|
|
5072
|
+
if (!filename) return c2.json({ error: "missing filename" }, 400);
|
|
5073
|
+
const rendersDir = adapter2.rendersDir(project);
|
|
5074
|
+
const fp = join9(rendersDir, filename);
|
|
5075
|
+
if (!existsSync9(fp)) return c2.json({ error: "not found" }, 404);
|
|
5076
|
+
const isWebm = fp.endsWith(".webm");
|
|
5077
|
+
const contentType = isWebm ? "video/webm" : "video/mp4";
|
|
5078
|
+
const content = readFileSync9(fp);
|
|
5079
|
+
return new Response(content, {
|
|
5080
|
+
headers: {
|
|
5081
|
+
"Content-Type": contentType,
|
|
5082
|
+
"Content-Disposition": `inline; filename="${filename}"`,
|
|
5083
|
+
"Accept-Ranges": "bytes",
|
|
5084
|
+
"Content-Length": String(content.length)
|
|
5085
|
+
}
|
|
5086
|
+
});
|
|
5087
|
+
});
|
|
4809
5088
|
api.get("/projects/:id/renders", async (c2) => {
|
|
4810
5089
|
const project = await adapter2.resolveProject(c2.req.param("id"));
|
|
4811
5090
|
if (!project) return c2.json({ error: "not found" }, 404);
|
|
4812
5091
|
const rendersDir = adapter2.rendersDir(project);
|
|
4813
5092
|
if (!existsSync9(rendersDir)) return c2.json({ renders: [] });
|
|
4814
|
-
const files =
|
|
4815
|
-
const fp =
|
|
5093
|
+
const files = readdirSync5(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm")).map((f) => {
|
|
5094
|
+
const fp = join9(rendersDir, f);
|
|
4816
5095
|
const stat = statSync2(fp);
|
|
4817
5096
|
const rid = f.replace(/\.(mp4|webm)$/, "");
|
|
4818
|
-
const metaPath =
|
|
5097
|
+
const metaPath = join9(rendersDir, `${rid}.meta.json`);
|
|
4819
5098
|
let status = "complete";
|
|
4820
5099
|
let durationMs;
|
|
4821
5100
|
if (existsSync9(metaPath)) {
|
|
4822
5101
|
try {
|
|
4823
|
-
const meta = JSON.parse(
|
|
5102
|
+
const meta = JSON.parse(readFileSync9(metaPath, "utf-8"));
|
|
4824
5103
|
if (meta.status === "failed") status = "failed";
|
|
4825
5104
|
if (meta.durationMs) durationMs = meta.durationMs;
|
|
4826
5105
|
} catch {
|
|
@@ -4841,7 +5120,7 @@ function registerRenderRoutes(api, adapter2) {
|
|
|
4841
5120
|
id: file.id,
|
|
4842
5121
|
status: file.status,
|
|
4843
5122
|
progress: 100,
|
|
4844
|
-
outputPath:
|
|
5123
|
+
outputPath: join9(rendersDir, file.filename),
|
|
4845
5124
|
createdAt: file.createdAt
|
|
4846
5125
|
});
|
|
4847
5126
|
}
|
|
@@ -4856,8 +5135,8 @@ var init_render = __esm({
|
|
|
4856
5135
|
});
|
|
4857
5136
|
|
|
4858
5137
|
// ../core/src/studio-api/routes/thumbnail.ts
|
|
4859
|
-
import { existsSync as existsSync10, readFileSync as
|
|
4860
|
-
import { join as
|
|
5138
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
5139
|
+
import { join as join10 } from "path";
|
|
4861
5140
|
function registerThumbnailRoutes(api, adapter2) {
|
|
4862
5141
|
api.get("/projects/:id/thumbnail/*", async (c2) => {
|
|
4863
5142
|
if (!adapter2.generateThumbnail) {
|
|
@@ -4876,9 +5155,9 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
4876
5155
|
let compW = vpWidth || 1920;
|
|
4877
5156
|
let compH = vpHeight || 1080;
|
|
4878
5157
|
if (!vpWidth) {
|
|
4879
|
-
const htmlFile =
|
|
5158
|
+
const htmlFile = join10(project.dir, compPath);
|
|
4880
5159
|
if (existsSync10(htmlFile)) {
|
|
4881
|
-
const html =
|
|
5160
|
+
const html = readFileSync10(htmlFile, "utf-8");
|
|
4882
5161
|
const wMatch = html.match(/data-width=["'](\d+)["']/);
|
|
4883
5162
|
const hMatch = html.match(/data-height=["'](\d+)["']/);
|
|
4884
5163
|
if (wMatch?.[1]) compW = parseInt(wMatch[1]);
|
|
@@ -4886,11 +5165,11 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
4886
5165
|
}
|
|
4887
5166
|
}
|
|
4888
5167
|
const previewUrl = compPath === "index.html" ? `http://${c2.req.header("host")}/api/projects/${project.id}/preview` : `http://${c2.req.header("host")}/api/projects/${project.id}/preview/comp/${compPath}`;
|
|
4889
|
-
const cacheDir =
|
|
5168
|
+
const cacheDir = join10(project.dir, ".thumbnails");
|
|
4890
5169
|
const cacheKey = `${compPath.replace(/\//g, "_")}_${seekTime.toFixed(2)}.jpg`;
|
|
4891
|
-
const cachePath =
|
|
5170
|
+
const cachePath = join10(cacheDir, cacheKey);
|
|
4892
5171
|
if (existsSync10(cachePath)) {
|
|
4893
|
-
return new Response(new Uint8Array(
|
|
5172
|
+
return new Response(new Uint8Array(readFileSync10(cachePath)), {
|
|
4894
5173
|
headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
|
|
4895
5174
|
});
|
|
4896
5175
|
}
|
|
@@ -4907,7 +5186,7 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
4907
5186
|
return c2.json({ error: "Thumbnail generation returned null" }, 500);
|
|
4908
5187
|
}
|
|
4909
5188
|
if (!existsSync10(cacheDir)) mkdirSync6(cacheDir, { recursive: true });
|
|
4910
|
-
|
|
5189
|
+
writeFileSync4(cachePath, buffer);
|
|
4911
5190
|
return new Response(new Uint8Array(buffer), {
|
|
4912
5191
|
headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
|
|
4913
5192
|
});
|
|
@@ -4971,7 +5250,7 @@ __export(manager_exports2, {
|
|
|
4971
5250
|
import { execSync } from "child_process";
|
|
4972
5251
|
import { existsSync as existsSync11, rmSync as rmSync3 } from "fs";
|
|
4973
5252
|
import { homedir as homedir4 } from "os";
|
|
4974
|
-
import { join as
|
|
5253
|
+
import { join as join11 } from "path";
|
|
4975
5254
|
import { Browser, detectBrowserPlatform, getInstalledBrowsers, install } from "@puppeteer/browsers";
|
|
4976
5255
|
function setBrowserPath(path) {
|
|
4977
5256
|
_browserPathOverride = path;
|
|
@@ -5056,7 +5335,7 @@ var init_manager2 = __esm({
|
|
|
5056
5335
|
"src/browser/manager.ts"() {
|
|
5057
5336
|
"use strict";
|
|
5058
5337
|
CHROME_VERSION = "131.0.6778.85";
|
|
5059
|
-
CACHE_DIR =
|
|
5338
|
+
CACHE_DIR = join11(homedir4(), ".cache", "hyperframes", "chrome");
|
|
5060
5339
|
SYSTEM_CHROME_PATHS = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : [
|
|
5061
5340
|
"/usr/bin/google-chrome",
|
|
5062
5341
|
"/usr/bin/google-chrome-stable",
|
|
@@ -5174,8 +5453,8 @@ var init_config2 = __esm({
|
|
|
5174
5453
|
});
|
|
5175
5454
|
|
|
5176
5455
|
// ../engine/src/services/browserManager.ts
|
|
5177
|
-
import { existsSync as existsSync12, readdirSync as
|
|
5178
|
-
import { join as
|
|
5456
|
+
import { existsSync as existsSync12, readdirSync as readdirSync6 } from "fs";
|
|
5457
|
+
import { join as join12 } from "path";
|
|
5179
5458
|
import { homedir as homedir5 } from "os";
|
|
5180
5459
|
async function getPuppeteer() {
|
|
5181
5460
|
if (_puppeteer) return _puppeteer;
|
|
@@ -5196,16 +5475,16 @@ function resolveHeadlessShellPath(config) {
|
|
|
5196
5475
|
if (process.env.PRODUCER_HEADLESS_SHELL_PATH) {
|
|
5197
5476
|
return process.env.PRODUCER_HEADLESS_SHELL_PATH;
|
|
5198
5477
|
}
|
|
5199
|
-
const baseDir =
|
|
5478
|
+
const baseDir = join12(homedir5(), ".cache", "puppeteer", "chrome-headless-shell");
|
|
5200
5479
|
if (!existsSync12(baseDir)) return void 0;
|
|
5201
5480
|
try {
|
|
5202
|
-
const versions =
|
|
5481
|
+
const versions = readdirSync6(baseDir).sort().reverse();
|
|
5203
5482
|
for (const version of versions) {
|
|
5204
5483
|
const candidates = [
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5484
|
+
join12(baseDir, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
|
|
5485
|
+
join12(baseDir, version, "chrome-headless-shell-mac-arm64", "chrome-headless-shell"),
|
|
5486
|
+
join12(baseDir, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"),
|
|
5487
|
+
join12(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
|
|
5209
5488
|
];
|
|
5210
5489
|
for (const binary of candidates) {
|
|
5211
5490
|
if (existsSync12(binary)) return binary;
|
|
@@ -5990,6 +6269,69 @@ var init_gsap = __esm({
|
|
|
5990
6269
|
}
|
|
5991
6270
|
});
|
|
5992
6271
|
|
|
6272
|
+
// ../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/bidi.js
|
|
6273
|
+
var init_bidi = __esm({
|
|
6274
|
+
"../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/bidi.js"() {
|
|
6275
|
+
"use strict";
|
|
6276
|
+
}
|
|
6277
|
+
});
|
|
6278
|
+
|
|
6279
|
+
// ../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/analysis.js
|
|
6280
|
+
var arabicScriptRe, combiningMarkRe, decimalDigitRe;
|
|
6281
|
+
var init_analysis = __esm({
|
|
6282
|
+
"../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/analysis.js"() {
|
|
6283
|
+
"use strict";
|
|
6284
|
+
arabicScriptRe = new RegExp("\\p{Script=Arabic}", "u");
|
|
6285
|
+
combiningMarkRe = new RegExp("\\p{M}", "u");
|
|
6286
|
+
decimalDigitRe = new RegExp("\\p{Nd}", "u");
|
|
6287
|
+
}
|
|
6288
|
+
});
|
|
6289
|
+
|
|
6290
|
+
// ../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/measurement.js
|
|
6291
|
+
var emojiPresentationRe;
|
|
6292
|
+
var init_measurement = __esm({
|
|
6293
|
+
"../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/measurement.js"() {
|
|
6294
|
+
"use strict";
|
|
6295
|
+
init_analysis();
|
|
6296
|
+
emojiPresentationRe = new RegExp("\\p{Emoji_Presentation}", "u");
|
|
6297
|
+
}
|
|
6298
|
+
});
|
|
6299
|
+
|
|
6300
|
+
// ../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/line-break.js
|
|
6301
|
+
var init_line_break = __esm({
|
|
6302
|
+
"../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/line-break.js"() {
|
|
6303
|
+
"use strict";
|
|
6304
|
+
init_measurement();
|
|
6305
|
+
}
|
|
6306
|
+
});
|
|
6307
|
+
|
|
6308
|
+
// ../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/layout.js
|
|
6309
|
+
var init_layout = __esm({
|
|
6310
|
+
"../../node_modules/.bun/@chenglou+pretext@0.0.3/node_modules/@chenglou/pretext/dist/layout.js"() {
|
|
6311
|
+
"use strict";
|
|
6312
|
+
init_bidi();
|
|
6313
|
+
init_analysis();
|
|
6314
|
+
init_measurement();
|
|
6315
|
+
init_line_break();
|
|
6316
|
+
}
|
|
6317
|
+
});
|
|
6318
|
+
|
|
6319
|
+
// ../core/src/text/fitTextFontSize.ts
|
|
6320
|
+
var init_fitTextFontSize = __esm({
|
|
6321
|
+
"../core/src/text/fitTextFontSize.ts"() {
|
|
6322
|
+
"use strict";
|
|
6323
|
+
init_layout();
|
|
6324
|
+
}
|
|
6325
|
+
});
|
|
6326
|
+
|
|
6327
|
+
// ../core/src/text/index.ts
|
|
6328
|
+
var init_text = __esm({
|
|
6329
|
+
"../core/src/text/index.ts"() {
|
|
6330
|
+
"use strict";
|
|
6331
|
+
init_fitTextFontSize();
|
|
6332
|
+
}
|
|
6333
|
+
});
|
|
6334
|
+
|
|
5993
6335
|
// ../core/src/index.ts
|
|
5994
6336
|
var init_src = __esm({
|
|
5995
6337
|
"../core/src/index.ts"() {
|
|
@@ -6007,6 +6349,7 @@ var init_src = __esm({
|
|
|
6007
6349
|
init_hyperframesRuntime_engine();
|
|
6008
6350
|
init_parityContract();
|
|
6009
6351
|
init_gsap();
|
|
6352
|
+
init_text();
|
|
6010
6353
|
}
|
|
6011
6354
|
});
|
|
6012
6355
|
|
|
@@ -6174,8 +6517,8 @@ var init_screenshotService = __esm({
|
|
|
6174
6517
|
});
|
|
6175
6518
|
|
|
6176
6519
|
// ../engine/src/services/frameCapture.ts
|
|
6177
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as
|
|
6178
|
-
import { join as
|
|
6520
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
6521
|
+
import { join as join13 } from "path";
|
|
6179
6522
|
async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
|
|
6180
6523
|
if (!existsSync13(outputDir)) mkdirSync7(outputDir, { recursive: true });
|
|
6181
6524
|
const headlessShell = resolveHeadlessShellPath(config);
|
|
@@ -6336,13 +6679,13 @@ async function initializeSession(session) {
|
|
|
6336
6679
|
}
|
|
6337
6680
|
async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
|
|
6338
6681
|
try {
|
|
6339
|
-
const diagnosticsDir =
|
|
6682
|
+
const diagnosticsDir = join13(session.outputDir, "diagnostics");
|
|
6340
6683
|
if (!existsSync13(diagnosticsDir)) mkdirSync7(diagnosticsDir, { recursive: true });
|
|
6341
|
-
const base =
|
|
6684
|
+
const base = join13(diagnosticsDir, `frame-error-${frameIndex}`);
|
|
6342
6685
|
await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
|
|
6343
6686
|
const html = await session.page.content();
|
|
6344
|
-
|
|
6345
|
-
|
|
6687
|
+
writeFileSync5(`${base}.html`, html, "utf-8");
|
|
6688
|
+
writeFileSync5(
|
|
6346
6689
|
`${base}.json`,
|
|
6347
6690
|
JSON.stringify(
|
|
6348
6691
|
{
|
|
@@ -6436,8 +6779,8 @@ async function captureFrame(session, frameIndex, time) {
|
|
|
6436
6779
|
);
|
|
6437
6780
|
const ext = options.format === "png" ? "png" : "jpg";
|
|
6438
6781
|
const frameName = `frame_${String(frameIndex).padStart(6, "0")}.${ext}`;
|
|
6439
|
-
const framePath =
|
|
6440
|
-
|
|
6782
|
+
const framePath = join13(outputDir, frameName);
|
|
6783
|
+
writeFileSync5(framePath, buffer);
|
|
6441
6784
|
return { frameIndex, time: quantizedTime, path: framePath, captureTimeMs };
|
|
6442
6785
|
}
|
|
6443
6786
|
async function captureFrameToBuffer(session, frameIndex, time) {
|
|
@@ -6497,7 +6840,7 @@ var init_frameCapture = __esm({
|
|
|
6497
6840
|
// ../engine/src/utils/gpuEncoder.ts
|
|
6498
6841
|
import { spawn } from "child_process";
|
|
6499
6842
|
async function detectGpuEncoder() {
|
|
6500
|
-
return new Promise((
|
|
6843
|
+
return new Promise((resolve22) => {
|
|
6501
6844
|
const ffmpeg = spawn("ffmpeg", ["-encoders"], {
|
|
6502
6845
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6503
6846
|
});
|
|
@@ -6506,13 +6849,13 @@ async function detectGpuEncoder() {
|
|
|
6506
6849
|
stdout2 += data.toString();
|
|
6507
6850
|
});
|
|
6508
6851
|
ffmpeg.on("close", () => {
|
|
6509
|
-
if (stdout2.includes("h264_nvenc"))
|
|
6510
|
-
else if (stdout2.includes("h264_videotoolbox"))
|
|
6511
|
-
else if (stdout2.includes("h264_vaapi"))
|
|
6512
|
-
else if (stdout2.includes("h264_qsv"))
|
|
6513
|
-
else
|
|
6852
|
+
if (stdout2.includes("h264_nvenc")) resolve22("nvenc");
|
|
6853
|
+
else if (stdout2.includes("h264_videotoolbox")) resolve22("videotoolbox");
|
|
6854
|
+
else if (stdout2.includes("h264_vaapi")) resolve22("vaapi");
|
|
6855
|
+
else if (stdout2.includes("h264_qsv")) resolve22("qsv");
|
|
6856
|
+
else resolve22(null);
|
|
6514
6857
|
});
|
|
6515
|
-
ffmpeg.on("error", () =>
|
|
6858
|
+
ffmpeg.on("error", () => resolve22(null));
|
|
6516
6859
|
});
|
|
6517
6860
|
}
|
|
6518
6861
|
async function getCachedGpuEncoder() {
|
|
@@ -6551,7 +6894,7 @@ async function runFfmpeg(args, opts) {
|
|
|
6551
6894
|
const signal = opts?.signal;
|
|
6552
6895
|
const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
|
|
6553
6896
|
const onStderr = opts?.onStderr;
|
|
6554
|
-
return new Promise((
|
|
6897
|
+
return new Promise((resolve22) => {
|
|
6555
6898
|
const ffmpeg = spawn2("ffmpeg", args);
|
|
6556
6899
|
let stderr = "";
|
|
6557
6900
|
const onAbort = () => {
|
|
@@ -6577,7 +6920,7 @@ async function runFfmpeg(args, opts) {
|
|
|
6577
6920
|
ffmpeg.on("close", (code) => {
|
|
6578
6921
|
clearTimeout(timer);
|
|
6579
6922
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6580
|
-
|
|
6923
|
+
resolve22({
|
|
6581
6924
|
success: !signal?.aborted && code === 0,
|
|
6582
6925
|
exitCode: code,
|
|
6583
6926
|
stderr,
|
|
@@ -6587,7 +6930,7 @@ async function runFfmpeg(args, opts) {
|
|
|
6587
6930
|
ffmpeg.on("error", (err) => {
|
|
6588
6931
|
clearTimeout(timer);
|
|
6589
6932
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6590
|
-
|
|
6933
|
+
resolve22({
|
|
6591
6934
|
success: false,
|
|
6592
6935
|
exitCode: null,
|
|
6593
6936
|
stderr: err.message,
|
|
@@ -6606,8 +6949,8 @@ var init_runFfmpeg = __esm({
|
|
|
6606
6949
|
|
|
6607
6950
|
// ../engine/src/services/chunkEncoder.ts
|
|
6608
6951
|
import { spawn as spawn3 } from "child_process";
|
|
6609
|
-
import { copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as
|
|
6610
|
-
import { join as
|
|
6952
|
+
import { copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
6953
|
+
import { join as join14, dirname as dirname4 } from "path";
|
|
6611
6954
|
function getEncoderPreset(quality, format = "mp4") {
|
|
6612
6955
|
const base = ENCODER_PRESETS[quality];
|
|
6613
6956
|
if (format === "webm") {
|
|
@@ -6690,7 +7033,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6690
7033
|
const startTime = Date.now();
|
|
6691
7034
|
const outputDir = dirname4(outputPath);
|
|
6692
7035
|
if (!existsSync14(outputDir)) mkdirSync8(outputDir, { recursive: true });
|
|
6693
|
-
const files =
|
|
7036
|
+
const files = readdirSync7(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i));
|
|
6694
7037
|
const frameCount = files.length;
|
|
6695
7038
|
if (frameCount === 0) {
|
|
6696
7039
|
return {
|
|
@@ -6706,10 +7049,10 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6706
7049
|
if (options.useGpu) {
|
|
6707
7050
|
gpuEncoder = await getCachedGpuEncoder();
|
|
6708
7051
|
}
|
|
6709
|
-
const inputPath =
|
|
7052
|
+
const inputPath = join14(framesDir, framePattern);
|
|
6710
7053
|
const inputArgs = ["-framerate", String(options.fps), "-i", inputPath];
|
|
6711
7054
|
const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
|
|
6712
|
-
return new Promise((
|
|
7055
|
+
return new Promise((resolve22) => {
|
|
6713
7056
|
const ffmpeg = spawn3("ffmpeg", args);
|
|
6714
7057
|
let stderr = "";
|
|
6715
7058
|
const onAbort = () => {
|
|
@@ -6734,7 +7077,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6734
7077
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6735
7078
|
const durationMs = Date.now() - startTime;
|
|
6736
7079
|
if (signal?.aborted) {
|
|
6737
|
-
|
|
7080
|
+
resolve22({
|
|
6738
7081
|
success: false,
|
|
6739
7082
|
outputPath,
|
|
6740
7083
|
durationMs,
|
|
@@ -6745,7 +7088,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6745
7088
|
return;
|
|
6746
7089
|
}
|
|
6747
7090
|
if (code !== 0) {
|
|
6748
|
-
|
|
7091
|
+
resolve22({
|
|
6749
7092
|
success: false,
|
|
6750
7093
|
outputPath,
|
|
6751
7094
|
durationMs,
|
|
@@ -6756,12 +7099,12 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6756
7099
|
return;
|
|
6757
7100
|
}
|
|
6758
7101
|
const fileSize = existsSync14(outputPath) ? statSync3(outputPath).size : 0;
|
|
6759
|
-
|
|
7102
|
+
resolve22({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
|
|
6760
7103
|
});
|
|
6761
7104
|
ffmpeg.on("error", (err) => {
|
|
6762
7105
|
clearTimeout(timer);
|
|
6763
7106
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6764
|
-
|
|
7107
|
+
resolve22({
|
|
6765
7108
|
success: false,
|
|
6766
7109
|
outputPath,
|
|
6767
7110
|
durationMs: Date.now() - startTime,
|
|
@@ -6774,7 +7117,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
6774
7117
|
}
|
|
6775
7118
|
async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, options, chunkSizeFrames, signal) {
|
|
6776
7119
|
const start = Date.now();
|
|
6777
|
-
const files =
|
|
7120
|
+
const files = readdirSync7(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i)).sort();
|
|
6778
7121
|
if (files.length === 0) {
|
|
6779
7122
|
return {
|
|
6780
7123
|
success: false,
|
|
@@ -6787,7 +7130,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
6787
7130
|
}
|
|
6788
7131
|
const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
|
|
6789
7132
|
const chunkCount = Math.ceil(files.length / chunkSize);
|
|
6790
|
-
const chunkDir =
|
|
7133
|
+
const chunkDir = join14(dirname4(outputPath), "chunk-encode");
|
|
6791
7134
|
if (!existsSync14(chunkDir)) mkdirSync8(chunkDir, { recursive: true });
|
|
6792
7135
|
const chunkPaths = [];
|
|
6793
7136
|
for (let i = 0; i < chunkCount; i++) {
|
|
@@ -6804,8 +7147,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
6804
7147
|
const startNumber = i * chunkSize;
|
|
6805
7148
|
const framesInChunk = Math.min(chunkSize, files.length - startNumber);
|
|
6806
7149
|
const ext = outputPath.endsWith(".webm") ? ".webm" : ".mp4";
|
|
6807
|
-
const chunkPath =
|
|
6808
|
-
const inputPath =
|
|
7150
|
+
const chunkPath = join14(chunkDir, `chunk_${String(i).padStart(4, "0")}${ext}`);
|
|
7151
|
+
const inputPath = join14(framesDir, framePattern);
|
|
6809
7152
|
const inputArgs = [
|
|
6810
7153
|
"-framerate",
|
|
6811
7154
|
String(options.fps),
|
|
@@ -6819,18 +7162,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
6819
7162
|
let gpuEncoder = null;
|
|
6820
7163
|
if (options.useGpu) gpuEncoder = await getCachedGpuEncoder();
|
|
6821
7164
|
const args = buildEncoderArgs(options, inputArgs, chunkPath, gpuEncoder);
|
|
6822
|
-
const chunkResult = await new Promise((
|
|
7165
|
+
const chunkResult = await new Promise((resolve22) => {
|
|
6823
7166
|
const ffmpeg = spawn3("ffmpeg", args);
|
|
6824
7167
|
let stderr = "";
|
|
6825
7168
|
ffmpeg.stderr.on("data", (d) => {
|
|
6826
7169
|
stderr += d.toString();
|
|
6827
7170
|
});
|
|
6828
7171
|
ffmpeg.on("close", (code) => {
|
|
6829
|
-
if (code === 0)
|
|
6830
|
-
else
|
|
7172
|
+
if (code === 0) resolve22({ success: true });
|
|
7173
|
+
else resolve22({ success: false, error: `Chunk ${i} encode failed: ${stderr.slice(-400)}` });
|
|
6831
7174
|
});
|
|
6832
7175
|
ffmpeg.on("error", (err) => {
|
|
6833
|
-
|
|
7176
|
+
resolve22({ success: false, error: `Chunk ${i} encode error: ${err.message}` });
|
|
6834
7177
|
});
|
|
6835
7178
|
});
|
|
6836
7179
|
if (!chunkResult.success) {
|
|
@@ -6845,9 +7188,9 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
6845
7188
|
}
|
|
6846
7189
|
chunkPaths.push(chunkPath);
|
|
6847
7190
|
}
|
|
6848
|
-
const concatListPath =
|
|
7191
|
+
const concatListPath = join14(chunkDir, "concat-list.txt");
|
|
6849
7192
|
const concatInput = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
|
|
6850
|
-
|
|
7193
|
+
writeFileSync6(concatListPath, concatInput, "utf-8");
|
|
6851
7194
|
const concatArgs = [
|
|
6852
7195
|
"-f",
|
|
6853
7196
|
"concat",
|
|
@@ -6860,18 +7203,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
6860
7203
|
"-y",
|
|
6861
7204
|
outputPath
|
|
6862
7205
|
];
|
|
6863
|
-
const concatResult = await new Promise((
|
|
7206
|
+
const concatResult = await new Promise((resolve22) => {
|
|
6864
7207
|
const ffmpeg = spawn3("ffmpeg", concatArgs);
|
|
6865
7208
|
let stderr = "";
|
|
6866
7209
|
ffmpeg.stderr.on("data", (d) => {
|
|
6867
7210
|
stderr += d.toString();
|
|
6868
7211
|
});
|
|
6869
7212
|
ffmpeg.on("close", (code) => {
|
|
6870
|
-
if (code === 0)
|
|
6871
|
-
else
|
|
7213
|
+
if (code === 0) resolve22({ success: true });
|
|
7214
|
+
else resolve22({ success: false, error: `Chunk concat failed: ${stderr.slice(-400)}` });
|
|
6872
7215
|
});
|
|
6873
7216
|
ffmpeg.on("error", (err) => {
|
|
6874
|
-
|
|
7217
|
+
resolve22({ success: false, error: `Chunk concat error: ${err.message}` });
|
|
6875
7218
|
});
|
|
6876
7219
|
});
|
|
6877
7220
|
if (!concatResult.success) {
|
|
@@ -6976,16 +7319,16 @@ function createFrameReorderBuffer(startFrame, endFrame) {
|
|
|
6976
7319
|
}
|
|
6977
7320
|
};
|
|
6978
7321
|
return {
|
|
6979
|
-
waitForFrame: (frame) => new Promise((
|
|
6980
|
-
waiters.push({ frame, resolve:
|
|
7322
|
+
waitForFrame: (frame) => new Promise((resolve22) => {
|
|
7323
|
+
waiters.push({ frame, resolve: resolve22 });
|
|
6981
7324
|
resolveWaiters();
|
|
6982
7325
|
}),
|
|
6983
7326
|
advanceTo: (frame) => {
|
|
6984
7327
|
nextFrame = frame;
|
|
6985
7328
|
resolveWaiters();
|
|
6986
7329
|
},
|
|
6987
|
-
waitForAllDone: () => new Promise((
|
|
6988
|
-
waiters.push({ frame: endFrame, resolve:
|
|
7330
|
+
waitForAllDone: () => new Promise((resolve22) => {
|
|
7331
|
+
waiters.push({ frame: endFrame, resolve: resolve22 });
|
|
6989
7332
|
resolveWaiters();
|
|
6990
7333
|
})
|
|
6991
7334
|
};
|
|
@@ -7085,7 +7428,7 @@ async function spawnStreamingEncoder(outputPath, options, signal, config) {
|
|
|
7085
7428
|
let stderr = "";
|
|
7086
7429
|
let exitCode = null;
|
|
7087
7430
|
let exitPromiseResolve = null;
|
|
7088
|
-
const exitPromise = new Promise((
|
|
7431
|
+
const exitPromise = new Promise((resolve22) => exitPromiseResolve = resolve22);
|
|
7089
7432
|
ffmpeg.stderr?.on("data", (data) => {
|
|
7090
7433
|
stderr += data.toString();
|
|
7091
7434
|
});
|
|
@@ -7129,8 +7472,8 @@ Process error: ${err.message}`;
|
|
|
7129
7472
|
clearTimeout(timer);
|
|
7130
7473
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
7131
7474
|
if (ffmpeg.stdin && !ffmpeg.stdin.destroyed) {
|
|
7132
|
-
await new Promise((
|
|
7133
|
-
ffmpeg.stdin.end(() =>
|
|
7475
|
+
await new Promise((resolve22) => {
|
|
7476
|
+
ffmpeg.stdin.end(() => resolve22());
|
|
7134
7477
|
});
|
|
7135
7478
|
}
|
|
7136
7479
|
await exitPromise;
|
|
@@ -11020,8 +11363,8 @@ var init_custom_element_registry = __esm({
|
|
|
11020
11363
|
} : (element) => element.localName === localName;
|
|
11021
11364
|
registry.set(localName, { Class, check });
|
|
11022
11365
|
if (waiting.has(localName)) {
|
|
11023
|
-
for (const
|
|
11024
|
-
|
|
11366
|
+
for (const resolve22 of waiting.get(localName))
|
|
11367
|
+
resolve22(Class);
|
|
11025
11368
|
waiting.delete(localName);
|
|
11026
11369
|
}
|
|
11027
11370
|
ownerDocument.querySelectorAll(
|
|
@@ -11061,13 +11404,13 @@ var init_custom_element_registry = __esm({
|
|
|
11061
11404
|
*/
|
|
11062
11405
|
whenDefined(localName) {
|
|
11063
11406
|
const { registry, waiting } = this;
|
|
11064
|
-
return new Promise((
|
|
11407
|
+
return new Promise((resolve22) => {
|
|
11065
11408
|
if (registry.has(localName))
|
|
11066
|
-
|
|
11409
|
+
resolve22(registry.get(localName).Class);
|
|
11067
11410
|
else {
|
|
11068
11411
|
if (!waiting.has(localName))
|
|
11069
11412
|
waiting.set(localName, []);
|
|
11070
|
-
waiting.get(localName).push(
|
|
11413
|
+
waiting.get(localName).push(resolve22);
|
|
11071
11414
|
}
|
|
11072
11415
|
});
|
|
11073
11416
|
}
|
|
@@ -13754,7 +14097,7 @@ var init_matches = __esm({
|
|
|
13754
14097
|
|
|
13755
14098
|
// ../../node_modules/.bun/linkedom@0.18.12/node_modules/linkedom/esm/interface/text.js
|
|
13756
14099
|
var Text3;
|
|
13757
|
-
var
|
|
14100
|
+
var init_text2 = __esm({
|
|
13758
14101
|
"../../node_modules/.bun/linkedom@0.18.12/node_modules/linkedom/esm/interface/text.js"() {
|
|
13759
14102
|
"use strict";
|
|
13760
14103
|
init_constants2();
|
|
@@ -13807,7 +14150,7 @@ var init_parent_node = __esm({
|
|
|
13807
14150
|
init_node3();
|
|
13808
14151
|
init_utils();
|
|
13809
14152
|
init_node2();
|
|
13810
|
-
|
|
14153
|
+
init_text2();
|
|
13811
14154
|
init_node_list();
|
|
13812
14155
|
init_mutation_observer();
|
|
13813
14156
|
init_custom_element_registry();
|
|
@@ -14593,7 +14936,7 @@ var init_element = __esm({
|
|
|
14593
14936
|
init_shadow_root();
|
|
14594
14937
|
init_node_list();
|
|
14595
14938
|
init_attr();
|
|
14596
|
-
|
|
14939
|
+
init_text2();
|
|
14597
14940
|
init_text_escaper();
|
|
14598
14941
|
attributesHandler = {
|
|
14599
14942
|
get(target, key2) {
|
|
@@ -15157,7 +15500,7 @@ var init_facades = __esm({
|
|
|
15157
15500
|
init_element();
|
|
15158
15501
|
init_node2();
|
|
15159
15502
|
init_shadow_root();
|
|
15160
|
-
|
|
15503
|
+
init_text2();
|
|
15161
15504
|
init_element2();
|
|
15162
15505
|
init_object();
|
|
15163
15506
|
illegalConstructor = () => {
|
|
@@ -19290,7 +19633,7 @@ var init_document = __esm({
|
|
|
19290
19633
|
init_named_node_map();
|
|
19291
19634
|
init_node_list();
|
|
19292
19635
|
init_range();
|
|
19293
|
-
|
|
19636
|
+
init_text2();
|
|
19294
19637
|
init_tree_walker();
|
|
19295
19638
|
query = (method, ownerDocument, selectors) => {
|
|
19296
19639
|
let { [NEXT]: next, [END]: end } = ownerDocument;
|
|
@@ -19721,7 +20064,7 @@ var init_parse_json = __esm({
|
|
|
19721
20064
|
init_cdata_section();
|
|
19722
20065
|
init_comment();
|
|
19723
20066
|
init_document_type();
|
|
19724
|
-
|
|
20067
|
+
init_text2();
|
|
19725
20068
|
init_document2();
|
|
19726
20069
|
init_element3();
|
|
19727
20070
|
init_element2();
|
|
@@ -19770,7 +20113,7 @@ var init_esm10 = __esm({
|
|
|
19770
20113
|
// ../engine/src/utils/ffprobe.ts
|
|
19771
20114
|
import { spawn as spawn5 } from "child_process";
|
|
19772
20115
|
function runFfprobe(args) {
|
|
19773
|
-
return new Promise((
|
|
20116
|
+
return new Promise((resolve22, reject) => {
|
|
19774
20117
|
const proc = spawn5("ffprobe", args);
|
|
19775
20118
|
let stdout2 = "";
|
|
19776
20119
|
let stderr = "";
|
|
@@ -19784,7 +20127,7 @@ function runFfprobe(args) {
|
|
|
19784
20127
|
if (code !== 0) {
|
|
19785
20128
|
reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
|
|
19786
20129
|
} else {
|
|
19787
|
-
|
|
20130
|
+
resolve22(stdout2);
|
|
19788
20131
|
}
|
|
19789
20132
|
});
|
|
19790
20133
|
proc.on("error", (err) => {
|
|
@@ -19949,13 +20292,13 @@ var init_ffprobe = __esm({
|
|
|
19949
20292
|
// ../engine/src/utils/urlDownloader.ts
|
|
19950
20293
|
import { createWriteStream as createWriteStream2, existsSync as existsSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
19951
20294
|
import { createHash } from "crypto";
|
|
19952
|
-
import { join as
|
|
20295
|
+
import { join as join15, extname as extname2 } from "path";
|
|
19953
20296
|
import { Readable } from "stream";
|
|
19954
20297
|
import { finished } from "stream/promises";
|
|
19955
20298
|
function getFilenameFromUrl(url) {
|
|
19956
20299
|
const hash = createHash("md5").update(url).digest("hex").slice(0, 12);
|
|
19957
20300
|
const urlObj = new URL(url);
|
|
19958
|
-
const ext =
|
|
20301
|
+
const ext = extname2(urlObj.pathname) || ".mp4";
|
|
19959
20302
|
return `download_${hash}${ext}`;
|
|
19960
20303
|
}
|
|
19961
20304
|
async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
@@ -19971,7 +20314,7 @@ async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
|
19971
20314
|
mkdirSync10(destDir, { recursive: true });
|
|
19972
20315
|
}
|
|
19973
20316
|
const filename = getFilenameFromUrl(url);
|
|
19974
|
-
const localPath =
|
|
20317
|
+
const localPath = join15(destDir, filename);
|
|
19975
20318
|
if (existsSync16(localPath)) {
|
|
19976
20319
|
downloadPathCache.set(url, localPath);
|
|
19977
20320
|
return localPath;
|
|
@@ -20020,8 +20363,8 @@ var init_urlDownloader = __esm({
|
|
|
20020
20363
|
|
|
20021
20364
|
// ../engine/src/services/videoFrameExtractor.ts
|
|
20022
20365
|
import { spawn as spawn6 } from "child_process";
|
|
20023
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readdirSync as
|
|
20024
|
-
import { join as
|
|
20366
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readdirSync as readdirSync8, rmSync as rmSync4 } from "fs";
|
|
20367
|
+
import { join as join16 } from "path";
|
|
20025
20368
|
function parseVideoElements(html) {
|
|
20026
20369
|
const videos = [];
|
|
20027
20370
|
const { document: document2 } = parseHTML(html);
|
|
@@ -20066,11 +20409,11 @@ function parseVideoElements(html) {
|
|
|
20066
20409
|
async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config) {
|
|
20067
20410
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
20068
20411
|
const { fps, outputDir, quality = 95, format = "jpg" } = options;
|
|
20069
|
-
const videoOutputDir =
|
|
20412
|
+
const videoOutputDir = join16(outputDir, videoId);
|
|
20070
20413
|
if (!existsSync17(videoOutputDir)) mkdirSync11(videoOutputDir, { recursive: true });
|
|
20071
20414
|
const metadata = await extractVideoMetadata(videoPath);
|
|
20072
20415
|
const framePattern = `frame_%05d.${format}`;
|
|
20073
|
-
const outputPattern =
|
|
20416
|
+
const outputPattern = join16(videoOutputDir, framePattern);
|
|
20074
20417
|
const args = [
|
|
20075
20418
|
"-ss",
|
|
20076
20419
|
String(startTime),
|
|
@@ -20085,7 +20428,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
20085
20428
|
];
|
|
20086
20429
|
if (format === "png") args.push("-compression_level", "6");
|
|
20087
20430
|
args.push("-y", outputPattern);
|
|
20088
|
-
return new Promise((
|
|
20431
|
+
return new Promise((resolve22, reject) => {
|
|
20089
20432
|
const ffmpeg = spawn6("ffmpeg", args);
|
|
20090
20433
|
let stderr = "";
|
|
20091
20434
|
const onAbort = () => {
|
|
@@ -20116,11 +20459,11 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
20116
20459
|
return;
|
|
20117
20460
|
}
|
|
20118
20461
|
const framePaths = /* @__PURE__ */ new Map();
|
|
20119
|
-
const files =
|
|
20462
|
+
const files = readdirSync8(videoOutputDir).filter((f) => f.startsWith("frame_") && f.endsWith(`.${format}`)).sort();
|
|
20120
20463
|
files.forEach((file, index) => {
|
|
20121
|
-
framePaths.set(index,
|
|
20464
|
+
framePaths.set(index, join16(videoOutputDir, file));
|
|
20122
20465
|
});
|
|
20123
|
-
|
|
20466
|
+
resolve22({
|
|
20124
20467
|
videoId,
|
|
20125
20468
|
srcPath: videoPath,
|
|
20126
20469
|
outputDir: videoOutputDir,
|
|
@@ -20155,10 +20498,10 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config) {
|
|
|
20155
20498
|
try {
|
|
20156
20499
|
let videoPath = video.src;
|
|
20157
20500
|
if (!videoPath.startsWith("/") && !isHttpUrl(videoPath)) {
|
|
20158
|
-
videoPath =
|
|
20501
|
+
videoPath = join16(baseDir, videoPath);
|
|
20159
20502
|
}
|
|
20160
20503
|
if (isHttpUrl(videoPath)) {
|
|
20161
|
-
const downloadDir =
|
|
20504
|
+
const downloadDir = join16(options.outputDir, "_downloads");
|
|
20162
20505
|
mkdirSync11(downloadDir, { recursive: true });
|
|
20163
20506
|
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
20164
20507
|
}
|
|
@@ -20419,7 +20762,7 @@ var init_videoFrameInjector = __esm({
|
|
|
20419
20762
|
|
|
20420
20763
|
// ../engine/src/services/audioMixer.ts
|
|
20421
20764
|
import { existsSync as existsSync18, mkdirSync as mkdirSync12, rmSync as rmSync5 } from "fs";
|
|
20422
|
-
import { join as
|
|
20765
|
+
import { join as join17, dirname as dirname6 } from "path";
|
|
20423
20766
|
function parseAudioElements(html) {
|
|
20424
20767
|
const elements = [];
|
|
20425
20768
|
const { document: document2 } = parseHTML(html);
|
|
@@ -20647,7 +20990,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
20647
20990
|
try {
|
|
20648
20991
|
let srcPath = element.src;
|
|
20649
20992
|
if (!srcPath.startsWith("/") && !isHttpUrl(srcPath)) {
|
|
20650
|
-
srcPath =
|
|
20993
|
+
srcPath = join17(baseDir, srcPath);
|
|
20651
20994
|
}
|
|
20652
20995
|
if (isHttpUrl(srcPath)) {
|
|
20653
20996
|
try {
|
|
@@ -20670,7 +21013,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
20670
21013
|
}
|
|
20671
21014
|
let audioSrcPath = srcPath;
|
|
20672
21015
|
if (element.type === "video") {
|
|
20673
|
-
const extractedPath =
|
|
21016
|
+
const extractedPath = join17(workDir, `${element.id}-extracted.wav`);
|
|
20674
21017
|
const extractResult = await extractAudioFromVideo(
|
|
20675
21018
|
srcPath,
|
|
20676
21019
|
extractedPath,
|
|
@@ -20687,7 +21030,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
20687
21030
|
}
|
|
20688
21031
|
audioSrcPath = extractedPath;
|
|
20689
21032
|
} else {
|
|
20690
|
-
const trimmedPath =
|
|
21033
|
+
const trimmedPath = join17(workDir, `${element.id}-trimmed.wav`);
|
|
20691
21034
|
const prepResult = await prepareAudioTrack(
|
|
20692
21035
|
srcPath,
|
|
20693
21036
|
trimmedPath,
|
|
@@ -20740,9 +21083,9 @@ var init_audioMixer = __esm({
|
|
|
20740
21083
|
|
|
20741
21084
|
// ../engine/src/services/parallelCoordinator.ts
|
|
20742
21085
|
import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
|
|
20743
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as
|
|
21086
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as readdirSync9 } from "fs";
|
|
20744
21087
|
import { copyFile, rename } from "fs/promises";
|
|
20745
|
-
import { join as
|
|
21088
|
+
import { join as join18 } from "path";
|
|
20746
21089
|
function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
20747
21090
|
const effectiveMaxWorkers = (() => {
|
|
20748
21091
|
const concurrency = config?.concurrency ?? DEFAULT_CONFIG2.concurrency;
|
|
@@ -20785,7 +21128,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
|
|
|
20785
21128
|
workerId: i,
|
|
20786
21129
|
startFrame,
|
|
20787
21130
|
endFrame,
|
|
20788
|
-
outputDir:
|
|
21131
|
+
outputDir: join18(workDir, `worker-${i}`)
|
|
20789
21132
|
});
|
|
20790
21133
|
}
|
|
20791
21134
|
return tasks;
|
|
@@ -20890,10 +21233,10 @@ async function mergeWorkerFrames(workDir, tasks, outputDir) {
|
|
|
20890
21233
|
if (!existsSync19(task.outputDir)) {
|
|
20891
21234
|
continue;
|
|
20892
21235
|
}
|
|
20893
|
-
const files =
|
|
21236
|
+
const files = readdirSync9(task.outputDir).filter((f) => f.startsWith("frame_") && (f.endsWith(".jpg") || f.endsWith(".png"))).sort();
|
|
20894
21237
|
const copyTasks = files.map(async (file) => {
|
|
20895
|
-
const sourcePath =
|
|
20896
|
-
const targetPath =
|
|
21238
|
+
const sourcePath = join18(task.outputDir, file);
|
|
21239
|
+
const targetPath = join18(outputDir, file);
|
|
20897
21240
|
try {
|
|
20898
21241
|
await rename(sourcePath, targetPath);
|
|
20899
21242
|
} catch {
|
|
@@ -20930,8 +21273,8 @@ var init_parallelCoordinator = __esm({
|
|
|
20930
21273
|
// ../engine/src/services/fileServer.ts
|
|
20931
21274
|
import { Hono as Hono2 } from "hono";
|
|
20932
21275
|
import { serve } from "@hono/node-server";
|
|
20933
|
-
import { readFileSync as
|
|
20934
|
-
import { join as
|
|
21276
|
+
import { readFileSync as readFileSync11, existsSync as existsSync20, statSync as statSync5 } from "fs";
|
|
21277
|
+
import { join as join19, extname as extname3 } from "path";
|
|
20935
21278
|
function stripEmbeddedRuntimeScripts(html) {
|
|
20936
21279
|
if (!html) return html;
|
|
20937
21280
|
const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
@@ -21000,32 +21343,32 @@ function createFileServer(options) {
|
|
|
21000
21343
|
let requestPath = c2.req.path;
|
|
21001
21344
|
if (requestPath === "/") requestPath = "/index.html";
|
|
21002
21345
|
const relativePath = requestPath.replace(/^\//, "");
|
|
21003
|
-
const compiledPath = compiledDir ?
|
|
21346
|
+
const compiledPath = compiledDir ? join19(compiledDir, relativePath) : null;
|
|
21004
21347
|
const hasCompiledFile = Boolean(
|
|
21005
21348
|
compiledPath && existsSync20(compiledPath) && statSync5(compiledPath).isFile()
|
|
21006
21349
|
);
|
|
21007
|
-
const filePath = hasCompiledFile ? compiledPath :
|
|
21350
|
+
const filePath = hasCompiledFile ? compiledPath : join19(projectDir, relativePath);
|
|
21008
21351
|
if (!existsSync20(filePath) || !statSync5(filePath).isFile()) {
|
|
21009
21352
|
return c2.text("Not found", 404);
|
|
21010
21353
|
}
|
|
21011
|
-
const ext =
|
|
21354
|
+
const ext = extname3(filePath).toLowerCase();
|
|
21012
21355
|
const contentType = MIME_TYPES2[ext] || "application/octet-stream";
|
|
21013
21356
|
if (ext === ".html") {
|
|
21014
|
-
const rawHtml =
|
|
21357
|
+
const rawHtml = readFileSync11(filePath, "utf-8");
|
|
21015
21358
|
const html = relativePath === "index.html" ? injectScriptsIntoHtml(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
|
|
21016
21359
|
return c2.text(html, 200, { "Content-Type": contentType });
|
|
21017
21360
|
}
|
|
21018
|
-
const content =
|
|
21361
|
+
const content = readFileSync11(filePath);
|
|
21019
21362
|
return new Response(content, {
|
|
21020
21363
|
status: 200,
|
|
21021
21364
|
headers: { "Content-Type": contentType }
|
|
21022
21365
|
});
|
|
21023
21366
|
});
|
|
21024
|
-
return new Promise((
|
|
21367
|
+
return new Promise((resolve22) => {
|
|
21025
21368
|
const server = serve({ fetch: app.fetch, port }, (info) => {
|
|
21026
21369
|
const actualPort = info.port;
|
|
21027
21370
|
const url = `http://localhost:${actualPort}`;
|
|
21028
|
-
|
|
21371
|
+
resolve22({
|
|
21029
21372
|
url,
|
|
21030
21373
|
port: actualPort,
|
|
21031
21374
|
close: () => server.close()
|
|
@@ -21219,8 +21562,8 @@ var init_staticGuard = __esm({
|
|
|
21219
21562
|
});
|
|
21220
21563
|
|
|
21221
21564
|
// ../core/src/compiler/htmlBundler.ts
|
|
21222
|
-
import { readFileSync as
|
|
21223
|
-
import { join as
|
|
21565
|
+
import { readFileSync as readFileSync12, existsSync as existsSync21 } from "fs";
|
|
21566
|
+
import { join as join20, resolve as resolve7, isAbsolute, sep as sep2 } from "path";
|
|
21224
21567
|
import * as cheerio from "cheerio";
|
|
21225
21568
|
import { transformSync } from "esbuild";
|
|
21226
21569
|
function safePath(projectDir, relativePath) {
|
|
@@ -21284,7 +21627,7 @@ function isRelativeUrl(url) {
|
|
|
21284
21627
|
function safeReadFile(filePath) {
|
|
21285
21628
|
if (!existsSync21(filePath)) return null;
|
|
21286
21629
|
try {
|
|
21287
|
-
return
|
|
21630
|
+
return readFileSync12(filePath, "utf-8");
|
|
21288
21631
|
} catch {
|
|
21289
21632
|
return null;
|
|
21290
21633
|
}
|
|
@@ -21292,7 +21635,7 @@ function safeReadFile(filePath) {
|
|
|
21292
21635
|
function safeReadFileBuffer(filePath) {
|
|
21293
21636
|
if (!existsSync21(filePath)) return null;
|
|
21294
21637
|
try {
|
|
21295
|
-
return
|
|
21638
|
+
return readFileSync12(filePath);
|
|
21296
21639
|
} catch {
|
|
21297
21640
|
return null;
|
|
21298
21641
|
}
|
|
@@ -21479,9 +21822,9 @@ function stripJsCommentsParserSafe(source) {
|
|
|
21479
21822
|
}
|
|
21480
21823
|
}
|
|
21481
21824
|
async function bundleToSingleHtml(projectDir, options) {
|
|
21482
|
-
const indexPath =
|
|
21825
|
+
const indexPath = join20(projectDir, "index.html");
|
|
21483
21826
|
if (!existsSync21(indexPath)) throw new Error("index.html not found in project directory");
|
|
21484
|
-
const rawHtml =
|
|
21827
|
+
const rawHtml = readFileSync12(indexPath, "utf-8");
|
|
21485
21828
|
const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
|
|
21486
21829
|
const staticGuard = validateHyperframeHtmlContract(compiled);
|
|
21487
21830
|
if (!staticGuard.isValid) {
|
|
@@ -21586,6 +21929,64 @@ async function bundleToSingleHtml(projectDir, options) {
|
|
|
21586
21929
|
}
|
|
21587
21930
|
$(hostEl).removeAttr("data-composition-src");
|
|
21588
21931
|
});
|
|
21932
|
+
$("template[id]").each((_2, templateEl) => {
|
|
21933
|
+
const templateId = $(templateEl).attr("id") || "";
|
|
21934
|
+
const match = templateId.match(/^(.+)-template$/);
|
|
21935
|
+
if (!match) return;
|
|
21936
|
+
const compId = match[1];
|
|
21937
|
+
const hostSelector = `[data-composition-id="${compId}"]:not([data-composition-src])`;
|
|
21938
|
+
const $candidates = $(hostSelector).filter((__, el) => $(el).parents().length > 0);
|
|
21939
|
+
const $host = $candidates.first();
|
|
21940
|
+
if ($host.length === 0) return;
|
|
21941
|
+
if ($host.children().length > 0) return;
|
|
21942
|
+
const templateHtml = $(templateEl).html() || "";
|
|
21943
|
+
const $inner = cheerio.load(templateHtml, { xml: false });
|
|
21944
|
+
const $innerRoot = $inner(`[data-composition-id="${compId}"]`).first();
|
|
21945
|
+
if ($innerRoot.length > 0) {
|
|
21946
|
+
$innerRoot.find("style").each((__, styleEl) => {
|
|
21947
|
+
compStyleChunks.push($inner(styleEl).html() || "");
|
|
21948
|
+
$inner(styleEl).remove();
|
|
21949
|
+
});
|
|
21950
|
+
$innerRoot.find("script").each((__, scriptEl) => {
|
|
21951
|
+
const externalSrc = ($inner(scriptEl).attr("src") || "").trim();
|
|
21952
|
+
if (externalSrc) {
|
|
21953
|
+
if (!compExternalScriptSrcs.includes(externalSrc)) {
|
|
21954
|
+
compExternalScriptSrcs.push(externalSrc);
|
|
21955
|
+
}
|
|
21956
|
+
} else {
|
|
21957
|
+
compScriptChunks.push(
|
|
21958
|
+
`(function(){ try { ${$inner(scriptEl).html() || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`
|
|
21959
|
+
);
|
|
21960
|
+
}
|
|
21961
|
+
$inner(scriptEl).remove();
|
|
21962
|
+
});
|
|
21963
|
+
const innerW = $innerRoot.attr("data-width");
|
|
21964
|
+
const innerH = $innerRoot.attr("data-height");
|
|
21965
|
+
if (innerW && !$host.attr("data-width")) $host.attr("data-width", innerW);
|
|
21966
|
+
if (innerH && !$host.attr("data-height")) $host.attr("data-height", innerH);
|
|
21967
|
+
$host.html($innerRoot.html() || "");
|
|
21968
|
+
} else {
|
|
21969
|
+
$inner("style").each((__, styleEl) => {
|
|
21970
|
+
compStyleChunks.push($inner(styleEl).html() || "");
|
|
21971
|
+
$inner(styleEl).remove();
|
|
21972
|
+
});
|
|
21973
|
+
$inner("script").each((__, scriptEl) => {
|
|
21974
|
+
const externalSrc = ($inner(scriptEl).attr("src") || "").trim();
|
|
21975
|
+
if (externalSrc) {
|
|
21976
|
+
if (!compExternalScriptSrcs.includes(externalSrc)) {
|
|
21977
|
+
compExternalScriptSrcs.push(externalSrc);
|
|
21978
|
+
}
|
|
21979
|
+
} else {
|
|
21980
|
+
compScriptChunks.push(
|
|
21981
|
+
`(function(){ try { ${$inner(scriptEl).html() || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`
|
|
21982
|
+
);
|
|
21983
|
+
}
|
|
21984
|
+
$inner(scriptEl).remove();
|
|
21985
|
+
});
|
|
21986
|
+
$host.html($inner.html() || "");
|
|
21987
|
+
}
|
|
21988
|
+
$(templateEl).remove();
|
|
21989
|
+
});
|
|
21589
21990
|
for (const extSrc of compExternalScriptSrcs) {
|
|
21590
21991
|
if (!$(`script[src="${extSrc}"]`).length) {
|
|
21591
21992
|
$("body").append(`<script src="${extSrc}"></script>`);
|
|
@@ -21651,7 +22052,7 @@ var init_compiler = __esm({
|
|
|
21651
22052
|
|
|
21652
22053
|
// ../producer/src/services/hyperframeRuntimeLoader.ts
|
|
21653
22054
|
import { createHash as createHash2 } from "crypto";
|
|
21654
|
-
import { existsSync as existsSync22, readFileSync as
|
|
22055
|
+
import { existsSync as existsSync22, readFileSync as readFileSync13 } from "fs";
|
|
21655
22056
|
import { dirname as dirname7, resolve as resolve8 } from "path";
|
|
21656
22057
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21657
22058
|
function resolveHyperframeManifestPath() {
|
|
@@ -21680,7 +22081,7 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
21680
22081
|
`[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
|
|
21681
22082
|
);
|
|
21682
22083
|
}
|
|
21683
|
-
const manifestRaw =
|
|
22084
|
+
const manifestRaw = readFileSync13(manifestPath, "utf8");
|
|
21684
22085
|
const manifest = JSON.parse(manifestRaw);
|
|
21685
22086
|
const runtimeFileName = manifest.artifacts?.iife;
|
|
21686
22087
|
if (!runtimeFileName || !manifest.sha256) {
|
|
@@ -21692,7 +22093,7 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
21692
22093
|
if (!existsSync22(runtimePath)) {
|
|
21693
22094
|
throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
|
|
21694
22095
|
}
|
|
21695
|
-
const runtimeSource =
|
|
22096
|
+
const runtimeSource = readFileSync13(runtimePath, "utf8");
|
|
21696
22097
|
const runtimeSha = createHash2("sha256").update(runtimeSource, "utf8").digest("hex");
|
|
21697
22098
|
if (runtimeSha !== manifest.sha256) {
|
|
21698
22099
|
throw new Error(
|
|
@@ -21731,8 +22132,8 @@ var init_hyperframeRuntimeLoader = __esm({
|
|
|
21731
22132
|
// ../producer/src/services/fileServer.ts
|
|
21732
22133
|
import { Hono as Hono3 } from "hono";
|
|
21733
22134
|
import { serve as serve2 } from "@hono/node-server";
|
|
21734
|
-
import { readFileSync as
|
|
21735
|
-
import { join as
|
|
22135
|
+
import { readFileSync as readFileSync14, existsSync as existsSync23, statSync as statSync6 } from "fs";
|
|
22136
|
+
import { join as join21, extname as extname4 } from "path";
|
|
21736
22137
|
function stripEmbeddedRuntimeScripts3(html) {
|
|
21737
22138
|
if (!html) return html;
|
|
21738
22139
|
const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
@@ -21801,33 +22202,33 @@ function createFileServer2(options) {
|
|
|
21801
22202
|
let requestPath = c2.req.path;
|
|
21802
22203
|
if (requestPath === "/") requestPath = "/index.html";
|
|
21803
22204
|
const relativePath = requestPath.replace(/^\//, "");
|
|
21804
|
-
const compiledPath = compiledDir ?
|
|
22205
|
+
const compiledPath = compiledDir ? join21(compiledDir, relativePath) : null;
|
|
21805
22206
|
const hasCompiledFile = Boolean(
|
|
21806
22207
|
compiledPath && existsSync23(compiledPath) && statSync6(compiledPath).isFile()
|
|
21807
22208
|
);
|
|
21808
|
-
const filePath = hasCompiledFile ? compiledPath :
|
|
22209
|
+
const filePath = hasCompiledFile ? compiledPath : join21(projectDir, relativePath);
|
|
21809
22210
|
if (!existsSync23(filePath) || !statSync6(filePath).isFile()) {
|
|
21810
22211
|
return c2.text("Not found", 404);
|
|
21811
22212
|
}
|
|
21812
|
-
const ext =
|
|
22213
|
+
const ext = extname4(filePath).toLowerCase();
|
|
21813
22214
|
const contentType = MIME_TYPES3[ext] || "application/octet-stream";
|
|
21814
22215
|
if (ext === ".html") {
|
|
21815
|
-
const rawHtml =
|
|
22216
|
+
const rawHtml = readFileSync14(filePath, "utf-8");
|
|
21816
22217
|
const isIndex = relativePath === "index.html";
|
|
21817
22218
|
const html = isIndex ? injectScriptsIntoHtml2(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
|
|
21818
22219
|
return c2.text(html, 200, { "Content-Type": contentType });
|
|
21819
22220
|
}
|
|
21820
|
-
const content =
|
|
22221
|
+
const content = readFileSync14(filePath);
|
|
21821
22222
|
return new Response(content, {
|
|
21822
22223
|
status: 200,
|
|
21823
22224
|
headers: { "Content-Type": contentType }
|
|
21824
22225
|
});
|
|
21825
22226
|
});
|
|
21826
|
-
return new Promise((
|
|
22227
|
+
return new Promise((resolve22) => {
|
|
21827
22228
|
const server = serve2({ fetch: app.fetch, port }, (info) => {
|
|
21828
22229
|
const actualPort = info.port;
|
|
21829
22230
|
const url = `http://localhost:${actualPort}`;
|
|
21830
|
-
|
|
22231
|
+
resolve22({
|
|
21831
22232
|
url,
|
|
21832
22233
|
port: actualPort,
|
|
21833
22234
|
close: () => server.close()
|
|
@@ -22269,8 +22670,8 @@ var init_deterministicFonts = __esm({
|
|
|
22269
22670
|
});
|
|
22270
22671
|
|
|
22271
22672
|
// ../producer/src/services/htmlCompiler.ts
|
|
22272
|
-
import { readFileSync as
|
|
22273
|
-
import { join as
|
|
22673
|
+
import { readFileSync as readFileSync15, existsSync as existsSync24, mkdirSync as mkdirSync14 } from "fs";
|
|
22674
|
+
import { join as join22, dirname as dirname8, resolve as resolve9 } from "path";
|
|
22274
22675
|
function dedupeElementsById(elements) {
|
|
22275
22676
|
const deduped = /* @__PURE__ */ new Map();
|
|
22276
22677
|
for (const element of elements) {
|
|
@@ -22288,7 +22689,7 @@ async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagNa
|
|
|
22288
22689
|
return { duration: 0, resolvedPath: src };
|
|
22289
22690
|
}
|
|
22290
22691
|
} else if (!filePath.startsWith("/")) {
|
|
22291
|
-
filePath =
|
|
22692
|
+
filePath = join22(baseDir, filePath);
|
|
22292
22693
|
}
|
|
22293
22694
|
if (!existsSync24(filePath)) {
|
|
22294
22695
|
return { duration: 0, resolvedPath: filePath };
|
|
@@ -22361,7 +22762,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
22361
22762
|
if (!existsSync24(filePath)) {
|
|
22362
22763
|
continue;
|
|
22363
22764
|
}
|
|
22364
|
-
const rawSubHtml =
|
|
22765
|
+
const rawSubHtml = readFileSync15(filePath, "utf-8");
|
|
22365
22766
|
const nestedVisited = new Set(visited);
|
|
22366
22767
|
nestedVisited.add(filePath);
|
|
22367
22768
|
workItems.push({ srcPath, absoluteStart, absoluteEnd, filePath, rawSubHtml, nestedVisited });
|
|
@@ -22556,7 +22957,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
22556
22957
|
if (!compHtml) {
|
|
22557
22958
|
const filePath = resolve9(projectDir, srcPath);
|
|
22558
22959
|
if (existsSync24(filePath)) {
|
|
22559
|
-
compHtml =
|
|
22960
|
+
compHtml = readFileSync15(filePath, "utf-8");
|
|
22560
22961
|
}
|
|
22561
22962
|
}
|
|
22562
22963
|
if (!compHtml) {
|
|
@@ -22676,7 +23077,7 @@ ${html}
|
|
|
22676
23077
|
</html>`;
|
|
22677
23078
|
}
|
|
22678
23079
|
async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
22679
|
-
const rawHtml =
|
|
23080
|
+
const rawHtml = readFileSync15(htmlPath, "utf-8");
|
|
22680
23081
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
22681
23082
|
rawHtml,
|
|
22682
23083
|
projectDir,
|
|
@@ -22905,12 +23306,12 @@ import {
|
|
|
22905
23306
|
existsSync as existsSync25,
|
|
22906
23307
|
mkdirSync as mkdirSync15,
|
|
22907
23308
|
rmSync as rmSync6,
|
|
22908
|
-
readFileSync as
|
|
22909
|
-
writeFileSync as
|
|
23309
|
+
readFileSync as readFileSync16,
|
|
23310
|
+
writeFileSync as writeFileSync7,
|
|
22910
23311
|
copyFileSync as copyFileSync2,
|
|
22911
23312
|
appendFileSync
|
|
22912
23313
|
} from "fs";
|
|
22913
|
-
import { join as
|
|
23314
|
+
import { join as join23, dirname as dirname9, resolve as resolve10 } from "path";
|
|
22914
23315
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
22915
23316
|
import { freemem as freemem2 } from "os";
|
|
22916
23317
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -22966,13 +23367,13 @@ function installDebugLogger(logPath, log = defaultLogger) {
|
|
|
22966
23367
|
};
|
|
22967
23368
|
}
|
|
22968
23369
|
function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
22969
|
-
const compileDir =
|
|
23370
|
+
const compileDir = join23(workDir, "compiled");
|
|
22970
23371
|
mkdirSync15(compileDir, { recursive: true });
|
|
22971
|
-
|
|
23372
|
+
writeFileSync7(join23(compileDir, "index.html"), compiled.html, "utf-8");
|
|
22972
23373
|
for (const [srcPath, html] of compiled.subCompositions) {
|
|
22973
|
-
const outPath =
|
|
23374
|
+
const outPath = join23(compileDir, srcPath);
|
|
22974
23375
|
mkdirSync15(dirname9(outPath), { recursive: true });
|
|
22975
|
-
|
|
23376
|
+
writeFileSync7(outPath, html, "utf-8");
|
|
22976
23377
|
}
|
|
22977
23378
|
if (includeSummary) {
|
|
22978
23379
|
const summary = {
|
|
@@ -22995,7 +23396,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
22995
23396
|
})),
|
|
22996
23397
|
subCompositions: Array.from(compiled.subCompositions.keys())
|
|
22997
23398
|
};
|
|
22998
|
-
|
|
23399
|
+
writeFileSync7(join23(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
|
|
22999
23400
|
}
|
|
23000
23401
|
}
|
|
23001
23402
|
function createRenderJob(config) {
|
|
@@ -23040,8 +23441,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
23040
23441
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
23041
23442
|
const moduleDir = dirname9(fileURLToPath3(import.meta.url));
|
|
23042
23443
|
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve10(process.env.PRODUCER_RENDERS_DIR, "..") : resolve10(moduleDir, "../..");
|
|
23043
|
-
const debugDir =
|
|
23044
|
-
const workDir = job.config.debug ?
|
|
23444
|
+
const debugDir = join23(producerRoot, ".debug");
|
|
23445
|
+
const workDir = job.config.debug ? join23(debugDir, job.id) : join23(dirname9(outputPath), `work-${job.id}`);
|
|
23045
23446
|
const pipelineStart = Date.now();
|
|
23046
23447
|
const log = job.config.logger ?? defaultLogger;
|
|
23047
23448
|
let fileServer = null;
|
|
@@ -23049,7 +23450,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23049
23450
|
let lastBrowserConsole = [];
|
|
23050
23451
|
let restoreLogger = null;
|
|
23051
23452
|
const perfStages = {};
|
|
23052
|
-
const perfOutputPath =
|
|
23453
|
+
const perfOutputPath = join23(workDir, "perf-summary.json");
|
|
23053
23454
|
const cfg = { ...job.config.producerConfig ?? resolveConfig() };
|
|
23054
23455
|
const outputFormat = job.config.format ?? "mp4";
|
|
23055
23456
|
const isWebm = outputFormat === "webm";
|
|
@@ -23069,26 +23470,26 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23069
23470
|
assertNotAborted();
|
|
23070
23471
|
if (!existsSync25(workDir)) mkdirSync15(workDir, { recursive: true });
|
|
23071
23472
|
if (job.config.debug) {
|
|
23072
|
-
const logPath =
|
|
23473
|
+
const logPath = join23(workDir, "render.log");
|
|
23073
23474
|
restoreLogger = installDebugLogger(logPath, log);
|
|
23074
23475
|
}
|
|
23075
23476
|
const entryFile = job.config.entryFile || "index.html";
|
|
23076
|
-
let htmlPath =
|
|
23477
|
+
let htmlPath = join23(projectDir, entryFile);
|
|
23077
23478
|
if (!existsSync25(htmlPath)) {
|
|
23078
23479
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
23079
23480
|
}
|
|
23080
23481
|
assertNotAborted();
|
|
23081
|
-
const rawEntry =
|
|
23482
|
+
const rawEntry = readFileSync16(htmlPath, "utf-8");
|
|
23082
23483
|
if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
|
|
23083
|
-
const wrapperPath =
|
|
23084
|
-
const projectIndexPath =
|
|
23484
|
+
const wrapperPath = join23(workDir, "standalone-entry.html");
|
|
23485
|
+
const projectIndexPath = join23(projectDir, "index.html");
|
|
23085
23486
|
if (!existsSync25(projectIndexPath)) {
|
|
23086
23487
|
throw new Error(
|
|
23087
23488
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
23088
23489
|
);
|
|
23089
23490
|
}
|
|
23090
23491
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
23091
|
-
|
|
23492
|
+
readFileSync16(projectIndexPath, "utf-8"),
|
|
23092
23493
|
entryFile
|
|
23093
23494
|
);
|
|
23094
23495
|
if (!standaloneHtml) {
|
|
@@ -23096,7 +23497,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23096
23497
|
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
23097
23498
|
);
|
|
23098
23499
|
}
|
|
23099
|
-
|
|
23500
|
+
writeFileSync7(wrapperPath, standaloneHtml, "utf-8");
|
|
23100
23501
|
htmlPath = wrapperPath;
|
|
23101
23502
|
log.info("Extracted standalone entry from index.html host context", {
|
|
23102
23503
|
entryFile
|
|
@@ -23105,7 +23506,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23105
23506
|
const stage1Start = Date.now();
|
|
23106
23507
|
updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress);
|
|
23107
23508
|
const compileStart = Date.now();
|
|
23108
|
-
let compiled = await compileForRender(projectDir, htmlPath,
|
|
23509
|
+
let compiled = await compileForRender(projectDir, htmlPath, join23(workDir, "downloads"));
|
|
23109
23510
|
assertNotAborted();
|
|
23110
23511
|
perfStages.compileOnlyMs = Date.now() - compileStart;
|
|
23111
23512
|
writeCompiledArtifacts(compiled, workDir, Boolean(job.config.debug));
|
|
@@ -23134,7 +23535,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23134
23535
|
reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
|
|
23135
23536
|
fileServer = await createFileServer2({
|
|
23136
23537
|
projectDir,
|
|
23137
|
-
compiledDir:
|
|
23538
|
+
compiledDir: join23(workDir, "compiled"),
|
|
23138
23539
|
port: 0
|
|
23139
23540
|
});
|
|
23140
23541
|
assertNotAborted();
|
|
@@ -23147,7 +23548,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23147
23548
|
};
|
|
23148
23549
|
probeSession = await createCaptureSession(
|
|
23149
23550
|
fileServer.url,
|
|
23150
|
-
|
|
23551
|
+
join23(workDir, "probe"),
|
|
23151
23552
|
captureOpts,
|
|
23152
23553
|
null,
|
|
23153
23554
|
cfg
|
|
@@ -23179,7 +23580,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23179
23580
|
compiled,
|
|
23180
23581
|
resolutions,
|
|
23181
23582
|
projectDir,
|
|
23182
|
-
|
|
23583
|
+
join23(workDir, "downloads")
|
|
23183
23584
|
);
|
|
23184
23585
|
assertNotAborted();
|
|
23185
23586
|
composition.videos = compiled.videos;
|
|
@@ -23276,7 +23677,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23276
23677
|
const extractionResult = await extractAllVideoFrames(
|
|
23277
23678
|
composition.videos,
|
|
23278
23679
|
projectDir,
|
|
23279
|
-
{ fps: job.config.fps, outputDir:
|
|
23680
|
+
{ fps: job.config.fps, outputDir: join23(workDir, "video-frames") },
|
|
23280
23681
|
abortSignal
|
|
23281
23682
|
);
|
|
23282
23683
|
assertNotAborted();
|
|
@@ -23308,13 +23709,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23308
23709
|
}
|
|
23309
23710
|
const stage3Start = Date.now();
|
|
23310
23711
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
23311
|
-
const audioOutputPath =
|
|
23712
|
+
const audioOutputPath = join23(workDir, "audio.aac");
|
|
23312
23713
|
let hasAudio = false;
|
|
23313
23714
|
if (composition.audios.length > 0) {
|
|
23314
23715
|
const audioResult = await processCompositionAudio(
|
|
23315
23716
|
composition.audios,
|
|
23316
23717
|
projectDir,
|
|
23317
|
-
|
|
23718
|
+
join23(workDir, "audio-work"),
|
|
23318
23719
|
audioOutputPath,
|
|
23319
23720
|
job.duration,
|
|
23320
23721
|
abortSignal
|
|
@@ -23330,12 +23731,12 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23330
23731
|
if (!fileServer) {
|
|
23331
23732
|
fileServer = await createFileServer2({
|
|
23332
23733
|
projectDir,
|
|
23333
|
-
compiledDir:
|
|
23734
|
+
compiledDir: join23(workDir, "compiled"),
|
|
23334
23735
|
port: 0
|
|
23335
23736
|
});
|
|
23336
23737
|
assertNotAborted();
|
|
23337
23738
|
}
|
|
23338
|
-
const framesDir =
|
|
23739
|
+
const framesDir = join23(workDir, "captured-frames");
|
|
23339
23740
|
if (!existsSync25(framesDir)) mkdirSync15(framesDir, { recursive: true });
|
|
23340
23741
|
const captureOptions = {
|
|
23341
23742
|
width,
|
|
@@ -23346,7 +23747,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23346
23747
|
};
|
|
23347
23748
|
const workerCount = calculateOptimalWorkers(job.totalFrames, job.config.workers, cfg);
|
|
23348
23749
|
const videoExt = isWebm ? ".webm" : ".mp4";
|
|
23349
|
-
const videoOnlyPath =
|
|
23750
|
+
const videoOnlyPath = join23(workDir, `video-only${videoExt}`);
|
|
23350
23751
|
const preset = getEncoderPreset(job.config.quality, outputFormat);
|
|
23351
23752
|
job.framesRendered = 0;
|
|
23352
23753
|
let streamingEncoder = null;
|
|
@@ -23615,7 +24016,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23615
24016
|
job.perfSummary = perfSummary;
|
|
23616
24017
|
if (job.config.debug) {
|
|
23617
24018
|
try {
|
|
23618
|
-
|
|
24019
|
+
writeFileSync7(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
|
|
23619
24020
|
} catch (err) {
|
|
23620
24021
|
log.debug("Failed to write perf summary", {
|
|
23621
24022
|
perfOutputPath,
|
|
@@ -23625,7 +24026,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
23625
24026
|
}
|
|
23626
24027
|
if (job.config.debug) {
|
|
23627
24028
|
if (existsSync25(outputPath)) {
|
|
23628
|
-
const debugOutput =
|
|
24029
|
+
const debugOutput = join23(workDir, isWebm ? "output.webm" : "output.mp4");
|
|
23629
24030
|
copyFileSync2(outputPath, debugOutput);
|
|
23630
24031
|
}
|
|
23631
24032
|
} else {
|
|
@@ -23762,8 +24163,8 @@ var init_config3 = __esm({
|
|
|
23762
24163
|
});
|
|
23763
24164
|
|
|
23764
24165
|
// ../producer/src/services/hyperframeLint.ts
|
|
23765
|
-
import { existsSync as existsSync26, readFileSync as
|
|
23766
|
-
import { resolve as resolve11, join as
|
|
24166
|
+
import { existsSync as existsSync26, readFileSync as readFileSync17, statSync as statSync7 } from "fs";
|
|
24167
|
+
import { resolve as resolve11, join as join24 } from "path";
|
|
23767
24168
|
function isStringRecord(value) {
|
|
23768
24169
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23769
24170
|
return false;
|
|
@@ -23805,13 +24206,13 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
23805
24206
|
if (existsSync26(absoluteEntryPath) && statSync7(absoluteEntryPath).isFile()) {
|
|
23806
24207
|
return {
|
|
23807
24208
|
entryFile,
|
|
23808
|
-
html:
|
|
24209
|
+
html: readFileSync17(absoluteEntryPath, "utf-8"),
|
|
23809
24210
|
source: "projectDir"
|
|
23810
24211
|
};
|
|
23811
24212
|
}
|
|
23812
24213
|
}
|
|
23813
24214
|
return {
|
|
23814
|
-
error: `No HTML entry file found in project directory: ${
|
|
24215
|
+
error: `No HTML entry file found in project directory: ${join24(absProjectDir, preferredEntryFile || "index.html")}`
|
|
23815
24216
|
};
|
|
23816
24217
|
}
|
|
23817
24218
|
function prepareHyperframeLintBody(body) {
|
|
@@ -23858,11 +24259,11 @@ var init_hyperframeLint = __esm({
|
|
|
23858
24259
|
});
|
|
23859
24260
|
|
|
23860
24261
|
// ../producer/src/utils/paths.ts
|
|
23861
|
-
import { resolve as resolve12, basename, join as
|
|
24262
|
+
import { resolve as resolve12, basename, join as join25 } from "path";
|
|
23862
24263
|
function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
|
|
23863
24264
|
const absoluteProjectDir = resolve12(projectDir);
|
|
23864
24265
|
const projectName = basename(absoluteProjectDir);
|
|
23865
|
-
const resolvedOutputPath = outputPath ??
|
|
24266
|
+
const resolvedOutputPath = outputPath ?? join25(rendersDir, `${projectName}.mp4`);
|
|
23866
24267
|
const absoluteOutputPath = resolve12(resolvedOutputPath);
|
|
23867
24268
|
return { absoluteProjectDir, absoluteOutputPath };
|
|
23868
24269
|
}
|
|
@@ -23880,11 +24281,11 @@ import {
|
|
|
23880
24281
|
mkdirSync as mkdirSync16,
|
|
23881
24282
|
statSync as statSync8,
|
|
23882
24283
|
mkdtempSync,
|
|
23883
|
-
writeFileSync as
|
|
24284
|
+
writeFileSync as writeFileSync8,
|
|
23884
24285
|
rmSync as rmSync7,
|
|
23885
24286
|
createReadStream
|
|
23886
24287
|
} from "fs";
|
|
23887
|
-
import { resolve as resolve13, dirname as dirname10, join as
|
|
24288
|
+
import { resolve as resolve13, dirname as dirname10, join as join26 } from "path";
|
|
23888
24289
|
import { tmpdir } from "os";
|
|
23889
24290
|
import { parseArgs as parseArgs2 } from "util";
|
|
23890
24291
|
import crypto from "crypto";
|
|
@@ -23936,8 +24337,8 @@ async function prepareRenderBody(body) {
|
|
|
23936
24337
|
}
|
|
23937
24338
|
}
|
|
23938
24339
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir();
|
|
23939
|
-
const tempProjectDir = mkdtempSync(
|
|
23940
|
-
|
|
24340
|
+
const tempProjectDir = mkdtempSync(join26(tempRoot, "producer-project-"));
|
|
24341
|
+
writeFileSync8(join26(tempProjectDir, "index.html"), htmlContent, "utf-8");
|
|
23941
24342
|
return {
|
|
23942
24343
|
prepared: {
|
|
23943
24344
|
input: {
|
|
@@ -24390,8 +24791,8 @@ __export(studioServer_exports, {
|
|
|
24390
24791
|
});
|
|
24391
24792
|
import { Hono as Hono5 } from "hono";
|
|
24392
24793
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
24393
|
-
import { existsSync as existsSync28, readFileSync as
|
|
24394
|
-
import { resolve as resolve14, join as
|
|
24794
|
+
import { existsSync as existsSync28, readFileSync as readFileSync18, writeFileSync as writeFileSync9, statSync as statSync9 } from "fs";
|
|
24795
|
+
import { resolve as resolve14, join as join27, basename as basename2 } from "path";
|
|
24395
24796
|
function resolveDistDir() {
|
|
24396
24797
|
const builtPath = resolve14(__dirname, "studio");
|
|
24397
24798
|
if (existsSync28(resolve14(builtPath, "index.html"))) return builtPath;
|
|
@@ -24472,7 +24873,7 @@ function createStudioServer(options) {
|
|
|
24472
24873
|
return lintHyperframeHtml2(html, opts);
|
|
24473
24874
|
},
|
|
24474
24875
|
runtimeUrl: "/api/runtime.js",
|
|
24475
|
-
rendersDir: () =>
|
|
24876
|
+
rendersDir: () => join27(projectDir, "renders"),
|
|
24476
24877
|
startRender(opts) {
|
|
24477
24878
|
const state = {
|
|
24478
24879
|
id: opts.jobId,
|
|
@@ -24505,7 +24906,7 @@ function createStudioServer(options) {
|
|
|
24505
24906
|
state.status = "complete";
|
|
24506
24907
|
state.progress = 100;
|
|
24507
24908
|
const metaPath = opts.outputPath.replace(/\.(mp4|webm)$/, ".meta.json");
|
|
24508
|
-
|
|
24909
|
+
writeFileSync9(
|
|
24509
24910
|
metaPath,
|
|
24510
24911
|
JSON.stringify({ status: "complete", durationMs: Date.now() - startTime })
|
|
24511
24912
|
);
|
|
@@ -24514,7 +24915,7 @@ function createStudioServer(options) {
|
|
|
24514
24915
|
state.error = err instanceof Error ? err.message : String(err);
|
|
24515
24916
|
try {
|
|
24516
24917
|
const metaPath = opts.outputPath.replace(/\.(mp4|webm)$/, ".meta.json");
|
|
24517
|
-
|
|
24918
|
+
writeFileSync9(metaPath, JSON.stringify({ status: "failed" }));
|
|
24518
24919
|
} catch {
|
|
24519
24920
|
}
|
|
24520
24921
|
}
|
|
@@ -24555,7 +24956,7 @@ function createStudioServer(options) {
|
|
|
24555
24956
|
const app = new Hono5();
|
|
24556
24957
|
app.get("/api/runtime.js", (c2) => {
|
|
24557
24958
|
if (!existsSync28(runtimePath)) return c2.text("runtime not built", 404);
|
|
24558
|
-
return c2.body(
|
|
24959
|
+
return c2.body(readFileSync18(runtimePath, "utf-8"), 200, {
|
|
24559
24960
|
"Content-Type": "text/javascript",
|
|
24560
24961
|
"Cache-Control": "no-store"
|
|
24561
24962
|
});
|
|
@@ -24588,7 +24989,7 @@ function createStudioServer(options) {
|
|
|
24588
24989
|
app.get("/assets/*", (c2) => {
|
|
24589
24990
|
const filePath = resolve14(studioDir, c2.req.path.slice(1));
|
|
24590
24991
|
if (!existsSync28(filePath) || !statSync9(filePath).isFile()) return c2.text("not found", 404);
|
|
24591
|
-
const content =
|
|
24992
|
+
const content = readFileSync18(filePath);
|
|
24592
24993
|
return new Response(content, {
|
|
24593
24994
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
24594
24995
|
});
|
|
@@ -24596,7 +24997,7 @@ function createStudioServer(options) {
|
|
|
24596
24997
|
app.get("/icons/*", (c2) => {
|
|
24597
24998
|
const filePath = resolve14(studioDir, c2.req.path.slice(1));
|
|
24598
24999
|
if (!existsSync28(filePath) || !statSync9(filePath).isFile()) return c2.text("not found", 404);
|
|
24599
|
-
const content =
|
|
25000
|
+
const content = readFileSync18(filePath);
|
|
24600
25001
|
return new Response(content, {
|
|
24601
25002
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
24602
25003
|
});
|
|
@@ -24606,7 +25007,7 @@ function createStudioServer(options) {
|
|
|
24606
25007
|
if (!existsSync28(indexPath)) {
|
|
24607
25008
|
return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
|
|
24608
25009
|
}
|
|
24609
|
-
return c2.html(
|
|
25010
|
+
return c2.html(readFileSync18(indexPath, "utf-8"));
|
|
24610
25011
|
});
|
|
24611
25012
|
return { app, watcher };
|
|
24612
25013
|
}
|
|
@@ -24628,7 +25029,7 @@ __export(dev_exports, {
|
|
|
24628
25029
|
});
|
|
24629
25030
|
import { spawn as spawn7 } from "child_process";
|
|
24630
25031
|
import { existsSync as existsSync29, lstatSync, symlinkSync, unlinkSync as unlinkSync2, readlinkSync, mkdirSync as mkdirSync17 } from "fs";
|
|
24631
|
-
import { resolve as resolve15, dirname as dirname11, basename as basename3, join as
|
|
25032
|
+
import { resolve as resolve15, dirname as dirname11, basename as basename3, join as join28 } from "path";
|
|
24632
25033
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
24633
25034
|
import { createRequire } from "module";
|
|
24634
25035
|
async function serveWithPortFallback(fetch3, startPort, maxAttempts = 10) {
|
|
@@ -24667,9 +25068,9 @@ async function serveWithPortFallback(fetch3, startPort, maxAttempts = 10) {
|
|
|
24667
25068
|
async function runDevMode(dir, projectName) {
|
|
24668
25069
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
24669
25070
|
const repoRoot = resolve15(dirname11(thisFile), "..", "..", "..", "..");
|
|
24670
|
-
const projectsDir =
|
|
25071
|
+
const projectsDir = join28(repoRoot, "packages", "studio", "data", "projects");
|
|
24671
25072
|
const pName = projectName ?? basename3(dir);
|
|
24672
|
-
const symlinkPath =
|
|
25073
|
+
const symlinkPath = join28(projectsDir, pName);
|
|
24673
25074
|
mkdirSync17(projectsDir, { recursive: true });
|
|
24674
25075
|
let createdSymlink = false;
|
|
24675
25076
|
if (dir !== symlinkPath) {
|
|
@@ -24693,7 +25094,7 @@ async function runDevMode(dir, projectName) {
|
|
|
24693
25094
|
Wt2(c.bold("hyperframes dev"));
|
|
24694
25095
|
const s = be();
|
|
24695
25096
|
s.start("Starting studio...");
|
|
24696
|
-
const studioPkgDir =
|
|
25097
|
+
const studioPkgDir = join28(repoRoot, "packages", "studio");
|
|
24697
25098
|
const child = spawn7("pnpm", ["exec", "vite"], {
|
|
24698
25099
|
cwd: studioPkgDir,
|
|
24699
25100
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -24732,13 +25133,13 @@ async function runDevMode(dir, projectName) {
|
|
|
24732
25133
|
}
|
|
24733
25134
|
});
|
|
24734
25135
|
}
|
|
24735
|
-
return new Promise((
|
|
24736
|
-
child.on("close", () =>
|
|
25136
|
+
return new Promise((resolve22) => {
|
|
25137
|
+
child.on("close", () => resolve22());
|
|
24737
25138
|
});
|
|
24738
25139
|
}
|
|
24739
25140
|
function hasLocalStudio(dir) {
|
|
24740
25141
|
try {
|
|
24741
|
-
const req = createRequire(
|
|
25142
|
+
const req = createRequire(join28(dir, "package.json"));
|
|
24742
25143
|
req.resolve("@hyperframes/studio/package.json");
|
|
24743
25144
|
return true;
|
|
24744
25145
|
} catch {
|
|
@@ -24746,11 +25147,11 @@ function hasLocalStudio(dir) {
|
|
|
24746
25147
|
}
|
|
24747
25148
|
}
|
|
24748
25149
|
async function runLocalStudioMode(dir, projectName) {
|
|
24749
|
-
const req = createRequire(
|
|
25150
|
+
const req = createRequire(join28(dir, "package.json"));
|
|
24750
25151
|
const studioPkgPath = dirname11(req.resolve("@hyperframes/studio/package.json"));
|
|
24751
25152
|
const pName = projectName ?? basename3(dir);
|
|
24752
|
-
const projectsDir =
|
|
24753
|
-
const symlinkPath =
|
|
25153
|
+
const projectsDir = join28(studioPkgPath, "data", "projects");
|
|
25154
|
+
const symlinkPath = join28(projectsDir, pName);
|
|
24754
25155
|
mkdirSync17(projectsDir, { recursive: true });
|
|
24755
25156
|
let createdSymlink = false;
|
|
24756
25157
|
if (dir !== symlinkPath) {
|
|
@@ -24803,8 +25204,8 @@ async function runLocalStudioMode(dir, projectName) {
|
|
|
24803
25204
|
}
|
|
24804
25205
|
});
|
|
24805
25206
|
}
|
|
24806
|
-
return new Promise((
|
|
24807
|
-
child.on("close", () =>
|
|
25207
|
+
return new Promise((resolve22) => {
|
|
25208
|
+
child.on("close", () => resolve22());
|
|
24808
25209
|
});
|
|
24809
25210
|
}
|
|
24810
25211
|
async function runEmbeddedMode(dir, startPort, projectName) {
|
|
@@ -24867,7 +25268,7 @@ var init_dev = __esm({
|
|
|
24867
25268
|
const startPort = parseInt(args.port ?? "3002", 10);
|
|
24868
25269
|
const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
|
|
24869
25270
|
const projectName = isImplicitCwd ? basename3(process.env.PWD ?? dir) : basename3(dir);
|
|
24870
|
-
const indexPath =
|
|
25271
|
+
const indexPath = join28(dir, "index.html");
|
|
24871
25272
|
if (existsSync29(indexPath)) {
|
|
24872
25273
|
const project = { dir, name: projectName, indexPath };
|
|
24873
25274
|
const lintResult = lintProject(project);
|
|
@@ -25023,7 +25424,7 @@ __export(render_exports, {
|
|
|
25023
25424
|
});
|
|
25024
25425
|
import { existsSync as existsSync31, mkdirSync as mkdirSync18, statSync as statSync11 } from "fs";
|
|
25025
25426
|
import { cpus as cpus3, freemem as freemem3 } from "os";
|
|
25026
|
-
import { resolve as resolve17, dirname as dirname12, join as
|
|
25427
|
+
import { resolve as resolve17, dirname as dirname12, join as join29 } from "path";
|
|
25027
25428
|
function defaultWorkerCount() {
|
|
25028
25429
|
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT / 2), 4));
|
|
25029
25430
|
}
|
|
@@ -25240,7 +25641,7 @@ Examples:
|
|
|
25240
25641
|
const now = /* @__PURE__ */ new Date();
|
|
25241
25642
|
const datePart = now.toISOString().slice(0, 10);
|
|
25242
25643
|
const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
|
|
25243
|
-
const outputPath = args.output ? resolve17(args.output) :
|
|
25644
|
+
const outputPath = args.output ? resolve17(args.output) : join29(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
|
|
25244
25645
|
mkdirSync18(dirname12(outputPath), { recursive: true });
|
|
25245
25646
|
const useDocker = args.docker ?? false;
|
|
25246
25647
|
const useGpu = args.gpu ?? false;
|
|
@@ -25350,17 +25751,17 @@ __export(transcribe_exports, {
|
|
|
25350
25751
|
transcribe: () => transcribe
|
|
25351
25752
|
});
|
|
25352
25753
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
25353
|
-
import { existsSync as existsSync32, readFileSync as
|
|
25354
|
-
import { join as
|
|
25754
|
+
import { existsSync as existsSync32, readFileSync as readFileSync19, mkdirSync as mkdirSync19, unlinkSync as unlinkSync3 } from "fs";
|
|
25755
|
+
import { join as join30, extname as extname5 } from "path";
|
|
25355
25756
|
import { tmpdir as tmpdir2 } from "os";
|
|
25356
25757
|
function isAudioFile(filePath) {
|
|
25357
|
-
return AUDIO_EXTENSIONS.has(
|
|
25758
|
+
return AUDIO_EXTENSIONS.has(extname5(filePath).toLowerCase());
|
|
25358
25759
|
}
|
|
25359
25760
|
function isVideoFile(filePath) {
|
|
25360
|
-
return VIDEO_EXTENSIONS.has(
|
|
25761
|
+
return VIDEO_EXTENSIONS.has(extname5(filePath).toLowerCase());
|
|
25361
25762
|
}
|
|
25362
25763
|
function extractAudio(videoPath) {
|
|
25363
|
-
const wavPath =
|
|
25764
|
+
const wavPath = join30(tmpdir2(), `hyperframes-audio-${Date.now()}.wav`);
|
|
25364
25765
|
execFileSync3(
|
|
25365
25766
|
"ffmpeg",
|
|
25366
25767
|
["-i", videoPath, "-vn", "-ar", "16000", "-ac", "1", "-f", "wav", "-y", wavPath],
|
|
@@ -25383,10 +25784,10 @@ function isWav16kMono(filePath) {
|
|
|
25383
25784
|
}
|
|
25384
25785
|
}
|
|
25385
25786
|
function prepareAudio(audioPath) {
|
|
25386
|
-
if (
|
|
25787
|
+
if (extname5(audioPath).toLowerCase() === ".wav" && isWav16kMono(audioPath)) {
|
|
25387
25788
|
return audioPath;
|
|
25388
25789
|
}
|
|
25389
|
-
const wavPath =
|
|
25790
|
+
const wavPath = join30(tmpdir2(), `hyperframes-audio-${Date.now()}.wav`);
|
|
25390
25791
|
execFileSync3(
|
|
25391
25792
|
"ffmpeg",
|
|
25392
25793
|
["-i", audioPath, "-ar", "16000", "-ac", "1", "-f", "wav", "-y", wavPath],
|
|
@@ -25403,7 +25804,7 @@ async function transcribe(inputPath, outputDir, options) {
|
|
|
25403
25804
|
onProgress: options?.onProgress
|
|
25404
25805
|
});
|
|
25405
25806
|
let wavPath;
|
|
25406
|
-
const ext =
|
|
25807
|
+
const ext = extname5(inputPath).toLowerCase();
|
|
25407
25808
|
if (isAudioFile(inputPath)) {
|
|
25408
25809
|
options?.onProgress?.("Preparing audio...");
|
|
25409
25810
|
wavPath = prepareAudio(inputPath);
|
|
@@ -25419,28 +25820,28 @@ async function transcribe(inputPath, outputDir, options) {
|
|
|
25419
25820
|
throw new Error(`Unsupported file type: ${ext}`);
|
|
25420
25821
|
}
|
|
25421
25822
|
options?.onProgress?.("Transcribing...");
|
|
25422
|
-
const outputBase =
|
|
25823
|
+
const outputBase = join30(outputDir, "transcript");
|
|
25423
25824
|
mkdirSync19(outputDir, { recursive: true });
|
|
25424
|
-
|
|
25425
|
-
|
|
25426
|
-
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25436
|
-
|
|
25437
|
-
|
|
25438
|
-
);
|
|
25825
|
+
const whisperArgs = [
|
|
25826
|
+
"--model",
|
|
25827
|
+
modelPath,
|
|
25828
|
+
"--output-json-full",
|
|
25829
|
+
"--output-file",
|
|
25830
|
+
outputBase,
|
|
25831
|
+
"--dtw",
|
|
25832
|
+
model,
|
|
25833
|
+
"--suppress-nst"
|
|
25834
|
+
];
|
|
25835
|
+
if (options?.language) {
|
|
25836
|
+
whisperArgs.push("--language", options.language);
|
|
25837
|
+
}
|
|
25838
|
+
whisperArgs.push(wavPath);
|
|
25839
|
+
execFileSync3(whisper.executablePath, whisperArgs, { stdio: "ignore", timeout: 3e5 });
|
|
25439
25840
|
const transcriptPath = `${outputBase}.json`;
|
|
25440
25841
|
if (!existsSync32(transcriptPath)) {
|
|
25441
25842
|
throw new Error("Whisper did not produce output. Check the input file.");
|
|
25442
25843
|
}
|
|
25443
|
-
const transcript = JSON.parse(
|
|
25844
|
+
const transcript = JSON.parse(readFileSync19(transcriptPath, "utf-8"));
|
|
25444
25845
|
const segments = transcript.transcription ?? [];
|
|
25445
25846
|
let wordCount = 0;
|
|
25446
25847
|
let maxEnd = 0;
|
|
@@ -25483,11 +25884,11 @@ import {
|
|
|
25483
25884
|
mkdirSync as mkdirSync20,
|
|
25484
25885
|
copyFileSync as copyFileSync3,
|
|
25485
25886
|
cpSync as cpSync2,
|
|
25486
|
-
writeFileSync as
|
|
25487
|
-
readFileSync as
|
|
25488
|
-
readdirSync as
|
|
25887
|
+
writeFileSync as writeFileSync10,
|
|
25888
|
+
readFileSync as readFileSync20,
|
|
25889
|
+
readdirSync as readdirSync10
|
|
25489
25890
|
} from "fs";
|
|
25490
|
-
import { resolve as resolve18, basename as basename5, join as
|
|
25891
|
+
import { resolve as resolve18, basename as basename5, join as join31, dirname as dirname13 } from "path";
|
|
25491
25892
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
25492
25893
|
import { execFileSync as execFileSync4, spawn as spawn8 } from "child_process";
|
|
25493
25894
|
async function installSkills(interactive) {
|
|
@@ -25622,9 +26023,9 @@ function getBundledSkillsDir() {
|
|
|
25622
26023
|
return resolveAssetDir(["..", "..", "..", "..", "skills"], ["skills"]);
|
|
25623
26024
|
}
|
|
25624
26025
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
25625
|
-
const htmlFiles =
|
|
26026
|
+
const htmlFiles = readdirSync10(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join31(e.parentPath ?? e.path, e.name));
|
|
25626
26027
|
for (const file of htmlFiles) {
|
|
25627
|
-
let content =
|
|
26028
|
+
let content = readFileSync20(file, "utf-8");
|
|
25628
26029
|
if (videoFilename) {
|
|
25629
26030
|
content = content.replaceAll("__VIDEO_SRC__", videoFilename);
|
|
25630
26031
|
} else {
|
|
@@ -25635,49 +26036,14 @@ function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
|
25635
26036
|
}
|
|
25636
26037
|
const dur = durationSeconds ? String(Math.round(durationSeconds * 100) / 100) : "10";
|
|
25637
26038
|
content = content.replaceAll("__VIDEO_DURATION__", dur);
|
|
25638
|
-
|
|
26039
|
+
writeFileSync10(file, content, "utf-8");
|
|
25639
26040
|
}
|
|
25640
26041
|
}
|
|
25641
|
-
function patchTranscript(dir, transcriptPath) {
|
|
25642
|
-
const
|
|
25643
|
-
const words =
|
|
25644
|
-
for (const seg of raw.transcription ?? []) {
|
|
25645
|
-
for (const token of seg.tokens ?? []) {
|
|
25646
|
-
const text = (token.text ?? "").trim();
|
|
25647
|
-
if (!text || text.startsWith("[_") || text.startsWith("[BLANK")) continue;
|
|
25648
|
-
const isPunctuation = /^[.,!?;:'")\]}>…–—-]+$/.test(text);
|
|
25649
|
-
const lastWord = words[words.length - 1];
|
|
25650
|
-
if (isPunctuation && lastWord) {
|
|
25651
|
-
lastWord.text += text;
|
|
25652
|
-
lastWord.end = Math.round((token.offsets?.to ?? 0) / 1e3 * 1e3) / 1e3;
|
|
25653
|
-
continue;
|
|
25654
|
-
}
|
|
25655
|
-
words.push({
|
|
25656
|
-
text,
|
|
25657
|
-
start: Math.round((token.offsets?.from ?? 0) / 1e3 * 1e3) / 1e3,
|
|
25658
|
-
end: Math.round((token.offsets?.to ?? 0) / 1e3 * 1e3) / 1e3
|
|
25659
|
-
});
|
|
25660
|
-
}
|
|
25661
|
-
}
|
|
26042
|
+
async function patchTranscript(dir, transcriptPath) {
|
|
26043
|
+
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
26044
|
+
const { words } = loadTranscript2(transcriptPath);
|
|
25662
26045
|
if (words.length === 0) return;
|
|
25663
|
-
|
|
25664
|
-
const htmlFiles = readdirSync9(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join30(e.parentPath ?? e.path, e.name));
|
|
25665
|
-
for (const file of htmlFiles) {
|
|
25666
|
-
let content = readFileSync19(file, "utf-8");
|
|
25667
|
-
const scriptBlocks = content.match(/<script>[\s\S]*?<\/script>/g) ?? [];
|
|
25668
|
-
let scriptMatch = null;
|
|
25669
|
-
let transcriptMatch = null;
|
|
25670
|
-
for (const block of scriptBlocks) {
|
|
25671
|
-
scriptMatch = scriptMatch ?? block.match(/const script = \[[\s\S]*?\];/);
|
|
25672
|
-
transcriptMatch = transcriptMatch ?? block.match(/const TRANSCRIPT = \[[\s\S]*?\];/);
|
|
25673
|
-
}
|
|
25674
|
-
const match = scriptMatch ?? transcriptMatch;
|
|
25675
|
-
if (match) {
|
|
25676
|
-
const varName = scriptMatch ? "script" : "TRANSCRIPT";
|
|
25677
|
-
content = content.replace(match[0], `const ${varName} = ${wordsJson};`);
|
|
25678
|
-
writeFileSync9(file, content, "utf-8");
|
|
25679
|
-
}
|
|
25680
|
-
}
|
|
26046
|
+
patchCaptionHtml2(dir, words);
|
|
25681
26047
|
}
|
|
25682
26048
|
async function handleVideoFile(videoPath, destDir, interactive) {
|
|
25683
26049
|
const probed = probeVideo(videoPath);
|
|
@@ -25766,7 +26132,7 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
25766
26132
|
const templateDir = getStaticTemplateDir(templateId);
|
|
25767
26133
|
cpSync2(templateDir, destDir, { recursive: true });
|
|
25768
26134
|
patchVideoSrc(destDir, localVideoName, durationSeconds);
|
|
25769
|
-
|
|
26135
|
+
writeFileSync10(
|
|
25770
26136
|
resolve18(destDir, "meta.json"),
|
|
25771
26137
|
JSON.stringify(
|
|
25772
26138
|
{
|
|
@@ -25781,8 +26147,8 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
25781
26147
|
);
|
|
25782
26148
|
const sharedDir = getSharedTemplateDir();
|
|
25783
26149
|
if (existsSync33(sharedDir)) {
|
|
25784
|
-
for (const entry of
|
|
25785
|
-
const src =
|
|
26150
|
+
for (const entry of readdirSync10(sharedDir, { withFileTypes: true })) {
|
|
26151
|
+
const src = join31(sharedDir, entry.name);
|
|
25786
26152
|
const dest = resolve18(destDir, entry.name);
|
|
25787
26153
|
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
25788
26154
|
copyFileSync3(src, dest);
|
|
@@ -25793,7 +26159,7 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
25793
26159
|
if (existsSync33(skillsSrcDir)) {
|
|
25794
26160
|
const projectSkills = ["hyperframes-compose", "hyperframes-captions"];
|
|
25795
26161
|
for (const skill of projectSkills) {
|
|
25796
|
-
const src =
|
|
26162
|
+
const src = join31(skillsSrcDir, skill);
|
|
25797
26163
|
if (existsSync33(src)) {
|
|
25798
26164
|
const dest = resolve18(destDir, ".claude", "skills", skill);
|
|
25799
26165
|
mkdirSync20(dest, { recursive: true });
|
|
@@ -25890,6 +26256,14 @@ Examples:
|
|
|
25890
26256
|
type: "boolean",
|
|
25891
26257
|
description: "Skip whisper transcription"
|
|
25892
26258
|
},
|
|
26259
|
+
model: {
|
|
26260
|
+
type: "string",
|
|
26261
|
+
description: "Whisper model for transcription (e.g. tiny.en, base.en, small.en, medium.en, large)"
|
|
26262
|
+
},
|
|
26263
|
+
language: {
|
|
26264
|
+
type: "string",
|
|
26265
|
+
description: "Language code for transcription (e.g. en, es, ja). Filters out non-target speech."
|
|
26266
|
+
},
|
|
25893
26267
|
"non-interactive": {
|
|
25894
26268
|
type: "boolean",
|
|
25895
26269
|
description: "Disable interactive prompts (for CI/agents)"
|
|
@@ -25902,6 +26276,8 @@ Examples:
|
|
|
25902
26276
|
const skipSkills = args["skip-skills"] === true;
|
|
25903
26277
|
const skipTranscribe = args["skip-transcribe"] === true;
|
|
25904
26278
|
const nonInteractive = args["non-interactive"] === true;
|
|
26279
|
+
const modelFlag = args.model;
|
|
26280
|
+
const languageFlag = args.language;
|
|
25905
26281
|
const interactive = !nonInteractive && process.stdout.isTTY === true;
|
|
25906
26282
|
if (!interactive) {
|
|
25907
26283
|
const resolvedTemplate = templateFlag ?? "blank";
|
|
@@ -25913,7 +26289,7 @@ Examples:
|
|
|
25913
26289
|
const templateId2 = resolvedTemplate;
|
|
25914
26290
|
const name2 = args.name ?? "my-video";
|
|
25915
26291
|
const destDir2 = resolve18(name2);
|
|
25916
|
-
if (existsSync33(destDir2) &&
|
|
26292
|
+
if (existsSync33(destDir2) && readdirSync10(destDir2).length > 0) {
|
|
25917
26293
|
console.error(c.error(`Directory already exists and is not empty: ${name2}`));
|
|
25918
26294
|
process.exit(1);
|
|
25919
26295
|
}
|
|
@@ -25949,10 +26325,13 @@ Examples:
|
|
|
25949
26325
|
try {
|
|
25950
26326
|
const { ensureWhisper: ensureWhisper2, ensureModel: ensureModel2 } = await Promise.resolve().then(() => (init_manager(), manager_exports));
|
|
25951
26327
|
await ensureWhisper2();
|
|
25952
|
-
await ensureModel2();
|
|
26328
|
+
await ensureModel2(modelFlag);
|
|
25953
26329
|
console.log("Transcribing...");
|
|
25954
26330
|
const { transcribe: runTranscribe } = await Promise.resolve().then(() => (init_transcribe(), transcribe_exports));
|
|
25955
|
-
const result = await runTranscribe(sourceFilePath2, destDir2
|
|
26331
|
+
const result = await runTranscribe(sourceFilePath2, destDir2, {
|
|
26332
|
+
model: modelFlag,
|
|
26333
|
+
language: languageFlag
|
|
26334
|
+
});
|
|
25956
26335
|
console.log(
|
|
25957
26336
|
`Transcribed: ${result.wordCount} words (${result.durationSeconds.toFixed(1)}s)`
|
|
25958
26337
|
);
|
|
@@ -25965,13 +26344,13 @@ Examples:
|
|
|
25965
26344
|
trackInitTemplate(templateId2);
|
|
25966
26345
|
const transcriptFile2 = resolve18(destDir2, "transcript.json");
|
|
25967
26346
|
if (existsSync33(transcriptFile2)) {
|
|
25968
|
-
patchTranscript(destDir2, transcriptFile2);
|
|
26347
|
+
await patchTranscript(destDir2, transcriptFile2);
|
|
25969
26348
|
}
|
|
25970
26349
|
if (!skipSkills) {
|
|
25971
26350
|
await installSkills(false);
|
|
25972
26351
|
}
|
|
25973
26352
|
console.log(c.success(`Created ${c.accent(name2 + "/")}`));
|
|
25974
|
-
for (const f of
|
|
26353
|
+
for (const f of readdirSync10(destDir2).filter((f2) => !f2.startsWith("."))) {
|
|
25975
26354
|
console.log(` ${c.accent(f)}`);
|
|
25976
26355
|
}
|
|
25977
26356
|
console.log();
|
|
@@ -26012,7 +26391,7 @@ Examples:
|
|
|
26012
26391
|
name = nameResult;
|
|
26013
26392
|
}
|
|
26014
26393
|
const destDir = resolve18(name);
|
|
26015
|
-
if (existsSync33(destDir) &&
|
|
26394
|
+
if (existsSync33(destDir) && readdirSync10(destDir).length > 0) {
|
|
26016
26395
|
const overwrite = await Rt({
|
|
26017
26396
|
message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
|
|
26018
26397
|
initialValue: false
|
|
@@ -26105,12 +26484,14 @@ Examples:
|
|
|
26105
26484
|
await ensureWhisper2({
|
|
26106
26485
|
onProgress: (msg) => spin.message(msg)
|
|
26107
26486
|
});
|
|
26108
|
-
await ensureModel2(
|
|
26487
|
+
await ensureModel2(modelFlag, {
|
|
26109
26488
|
onProgress: (msg) => spin.message(msg)
|
|
26110
26489
|
});
|
|
26111
26490
|
spin.message("Transcribing audio...");
|
|
26112
26491
|
const { transcribe: runTranscribe } = await Promise.resolve().then(() => (init_transcribe(), transcribe_exports));
|
|
26113
26492
|
const transcribeResult = await runTranscribe(sourceFilePath, destDir, {
|
|
26493
|
+
model: modelFlag,
|
|
26494
|
+
language: languageFlag,
|
|
26114
26495
|
onProgress: (msg) => spin.message(msg)
|
|
26115
26496
|
});
|
|
26116
26497
|
spin.stop(
|
|
@@ -26155,7 +26536,7 @@ Examples:
|
|
|
26155
26536
|
if (!skipSkills) {
|
|
26156
26537
|
await installSkills(true);
|
|
26157
26538
|
}
|
|
26158
|
-
const files =
|
|
26539
|
+
const files = readdirSync10(destDir);
|
|
26159
26540
|
Vt2(files.map((f) => c.accent(f)).join("\n"), c.success(`Created ${name}/`));
|
|
26160
26541
|
R2.message(
|
|
26161
26542
|
`${c.dim("Tip:")} Open this project with ${c.accent("Claude Code")}, ${c.accent("Cursor")}, or your preferred AI agent.
|
|
@@ -26178,40 +26559,87 @@ var init_lint3 = __esm({
|
|
|
26178
26559
|
"use strict";
|
|
26179
26560
|
init_dist();
|
|
26180
26561
|
init_colors();
|
|
26181
|
-
init_project();
|
|
26182
|
-
init_lintProject();
|
|
26183
26562
|
init_lintFormat();
|
|
26563
|
+
init_lintProject();
|
|
26564
|
+
init_project();
|
|
26184
26565
|
init_updateCheck();
|
|
26185
26566
|
lint_default = defineCommand({
|
|
26186
|
-
meta: {
|
|
26567
|
+
meta: {
|
|
26568
|
+
name: "lint",
|
|
26569
|
+
description: "Validate a composition for common mistakes"
|
|
26570
|
+
},
|
|
26187
26571
|
args: {
|
|
26188
|
-
dir: {
|
|
26189
|
-
|
|
26572
|
+
dir: {
|
|
26573
|
+
type: "positional",
|
|
26574
|
+
description: "Project directory",
|
|
26575
|
+
required: false
|
|
26576
|
+
},
|
|
26577
|
+
json: {
|
|
26578
|
+
type: "boolean",
|
|
26579
|
+
description: "Output findings as JSON",
|
|
26580
|
+
default: false
|
|
26581
|
+
},
|
|
26582
|
+
verbose: {
|
|
26583
|
+
type: "boolean",
|
|
26584
|
+
description: "Show info-level findings (hidden by default)",
|
|
26585
|
+
default: false
|
|
26586
|
+
}
|
|
26190
26587
|
},
|
|
26191
26588
|
async run({ args }) {
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
|
|
26207
|
-
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
26589
|
+
try {
|
|
26590
|
+
const project = resolveProject(args.dir);
|
|
26591
|
+
const lintResult = lintProject(project);
|
|
26592
|
+
if (args.json) {
|
|
26593
|
+
const allFindings = lintResult.results.flatMap((r) => r.result.findings);
|
|
26594
|
+
const combined = {
|
|
26595
|
+
ok: lintResult.totalErrors === 0,
|
|
26596
|
+
errorCount: lintResult.totalErrors,
|
|
26597
|
+
warningCount: lintResult.totalWarnings,
|
|
26598
|
+
infoCount: lintResult.totalInfos,
|
|
26599
|
+
findings: args.verbose ? allFindings : allFindings.filter((f) => f.severity !== "info"),
|
|
26600
|
+
filesScanned: lintResult.results.length
|
|
26601
|
+
};
|
|
26602
|
+
console.log(JSON.stringify(withMeta(combined), null, 2));
|
|
26603
|
+
process.exit(combined.ok ? 0 : 1);
|
|
26604
|
+
}
|
|
26605
|
+
const fileCount = lintResult.results.length;
|
|
26606
|
+
const fileLabel = fileCount === 1 ? lintResult.results[0]?.file ?? "index.html" : `${fileCount} files`;
|
|
26607
|
+
console.log(`${c.accent("\u25C6")} Linting ${c.accent(`${project.name}/${fileLabel}`)}`);
|
|
26608
|
+
console.log();
|
|
26609
|
+
if (lintResult.totalErrors === 0 && lintResult.totalWarnings === 0) {
|
|
26610
|
+
console.log(`${c.success("\u25C7")} ${c.success("0 errors, 0 warnings")}`);
|
|
26611
|
+
return;
|
|
26612
|
+
}
|
|
26613
|
+
const lines = formatLintFindings(lintResult, {
|
|
26614
|
+
showElementId: true,
|
|
26615
|
+
showSummary: true,
|
|
26616
|
+
verbose: args.verbose
|
|
26617
|
+
});
|
|
26618
|
+
for (const line of lines) console.log(line);
|
|
26619
|
+
process.exit(lintResult.totalErrors > 0 ? 1 : 0);
|
|
26620
|
+
} catch (err) {
|
|
26621
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
26622
|
+
if (args.json) {
|
|
26623
|
+
console.log(
|
|
26624
|
+
JSON.stringify(
|
|
26625
|
+
withMeta({
|
|
26626
|
+
ok: false,
|
|
26627
|
+
error: message,
|
|
26628
|
+
findings: [],
|
|
26629
|
+
errorCount: 0,
|
|
26630
|
+
warningCount: 0,
|
|
26631
|
+
infoCount: 0,
|
|
26632
|
+
filesScanned: 0
|
|
26633
|
+
}),
|
|
26634
|
+
null,
|
|
26635
|
+
2
|
|
26636
|
+
)
|
|
26637
|
+
);
|
|
26638
|
+
process.exit(1);
|
|
26639
|
+
}
|
|
26640
|
+
console.error(message);
|
|
26641
|
+
process.exit(1);
|
|
26211
26642
|
}
|
|
26212
|
-
const lines = formatLintFindings(lintResult, { showElementId: true, showSummary: true });
|
|
26213
|
-
for (const line of lines) console.log(line);
|
|
26214
|
-
process.exit(lintResult.totalErrors > 0 ? 1 : 0);
|
|
26215
26643
|
}
|
|
26216
26644
|
});
|
|
26217
26645
|
}
|
|
@@ -26235,12 +26663,12 @@ var info_exports = {};
|
|
|
26235
26663
|
__export(info_exports, {
|
|
26236
26664
|
default: () => info_default
|
|
26237
26665
|
});
|
|
26238
|
-
import { readFileSync as
|
|
26239
|
-
import { join as
|
|
26666
|
+
import { readFileSync as readFileSync21, readdirSync as readdirSync11, statSync as statSync12 } from "fs";
|
|
26667
|
+
import { join as join32 } from "path";
|
|
26240
26668
|
function totalSize(dir) {
|
|
26241
26669
|
let total = 0;
|
|
26242
|
-
for (const entry of
|
|
26243
|
-
const path =
|
|
26670
|
+
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
26671
|
+
const path = join32(dir, entry.name);
|
|
26244
26672
|
if (entry.isDirectory()) {
|
|
26245
26673
|
total += totalSize(path);
|
|
26246
26674
|
} else {
|
|
@@ -26268,7 +26696,7 @@ var init_info = __esm({
|
|
|
26268
26696
|
},
|
|
26269
26697
|
async run({ args }) {
|
|
26270
26698
|
const project = resolveProject(args.dir);
|
|
26271
|
-
const html =
|
|
26699
|
+
const html = readFileSync21(project.indexPath, "utf-8");
|
|
26272
26700
|
ensureDOMParser();
|
|
26273
26701
|
const parsed = parseHtml(html);
|
|
26274
26702
|
const tracks = new Set(parsed.elements.map((el) => el.zIndex));
|
|
@@ -26319,7 +26747,7 @@ var compositions_exports = {};
|
|
|
26319
26747
|
__export(compositions_exports, {
|
|
26320
26748
|
default: () => compositions_default
|
|
26321
26749
|
});
|
|
26322
|
-
import { readFileSync as
|
|
26750
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
26323
26751
|
function parseCompositions(html) {
|
|
26324
26752
|
const parser = new DOMParser();
|
|
26325
26753
|
const doc = parser.parseFromString(html, "text/html");
|
|
@@ -26376,7 +26804,7 @@ var init_compositions = __esm({
|
|
|
26376
26804
|
},
|
|
26377
26805
|
async run({ args }) {
|
|
26378
26806
|
const project = resolveProject(args.dir);
|
|
26379
|
-
const html =
|
|
26807
|
+
const html = readFileSync22(project.indexPath, "utf-8");
|
|
26380
26808
|
ensureDOMParser();
|
|
26381
26809
|
const compositions = parseCompositions(html);
|
|
26382
26810
|
if (compositions.length === 0) {
|
|
@@ -26413,7 +26841,7 @@ __export(benchmark_exports, {
|
|
|
26413
26841
|
default: () => benchmark_default
|
|
26414
26842
|
});
|
|
26415
26843
|
import { existsSync as existsSync34, statSync as statSync13 } from "fs";
|
|
26416
|
-
import { resolve as resolve19, join as
|
|
26844
|
+
import { resolve as resolve19, join as join33 } from "path";
|
|
26417
26845
|
var DEFAULT_CONFIGS, benchmark_default;
|
|
26418
26846
|
var init_benchmark = __esm({
|
|
26419
26847
|
"src/commands/benchmark.ts"() {
|
|
@@ -26483,7 +26911,7 @@ var init_benchmark = __esm({
|
|
|
26483
26911
|
s?.start(`Benchmarking ${config.label}...`);
|
|
26484
26912
|
for (let i = 0; i < runsPerConfig; i++) {
|
|
26485
26913
|
s?.message(`${config.label} \u2014 run ${i + 1}/${runsPerConfig}`);
|
|
26486
|
-
const outputPath =
|
|
26914
|
+
const outputPath = join33(
|
|
26487
26915
|
benchDir,
|
|
26488
26916
|
`${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i}.mp4`
|
|
26489
26917
|
);
|
|
@@ -26701,20 +27129,151 @@ Run ${c.accent("hyperframes browser --help")} for usage.`
|
|
|
26701
27129
|
}
|
|
26702
27130
|
});
|
|
26703
27131
|
|
|
27132
|
+
// src/commands/transcribe.ts
|
|
27133
|
+
var transcribe_exports2 = {};
|
|
27134
|
+
__export(transcribe_exports2, {
|
|
27135
|
+
default: () => transcribe_default
|
|
27136
|
+
});
|
|
27137
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync11 } from "fs";
|
|
27138
|
+
import { resolve as resolve20, join as join34, extname as extname6 } from "path";
|
|
27139
|
+
async function importTranscript(inputPath, dir, json) {
|
|
27140
|
+
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
27141
|
+
const { words, format } = loadTranscript2(inputPath);
|
|
27142
|
+
if (words.length === 0) {
|
|
27143
|
+
console.error(c.error("No words found in transcript."));
|
|
27144
|
+
process.exit(1);
|
|
27145
|
+
}
|
|
27146
|
+
const outPath = join34(dir, "transcript.json");
|
|
27147
|
+
writeFileSync11(outPath, JSON.stringify(words, null, 2));
|
|
27148
|
+
patchCaptionHtml2(dir, words);
|
|
27149
|
+
if (json) {
|
|
27150
|
+
console.log(
|
|
27151
|
+
JSON.stringify({ ok: true, format, wordCount: words.length, transcriptPath: outPath })
|
|
27152
|
+
);
|
|
27153
|
+
} else {
|
|
27154
|
+
console.log(
|
|
27155
|
+
`${c.success("\u25C7")} Imported ${c.accent(String(words.length))} words from ${c.accent(format)} format \u2192 ${c.accent("transcript.json")}`
|
|
27156
|
+
);
|
|
27157
|
+
}
|
|
27158
|
+
}
|
|
27159
|
+
async function transcribeAudio(inputPath, dir, opts) {
|
|
27160
|
+
const { transcribe: transcribe2 } = await Promise.resolve().then(() => (init_transcribe(), transcribe_exports));
|
|
27161
|
+
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
27162
|
+
const model = opts.model ?? DEFAULT_MODEL;
|
|
27163
|
+
const spin = opts.json ? null : be();
|
|
27164
|
+
spin?.start(`Transcribing with ${c.accent(model)}...`);
|
|
27165
|
+
try {
|
|
27166
|
+
const result = await transcribe2(inputPath, dir, {
|
|
27167
|
+
model,
|
|
27168
|
+
language: opts.language,
|
|
27169
|
+
onProgress: spin ? (msg) => spin.message(msg) : void 0
|
|
27170
|
+
});
|
|
27171
|
+
const { words } = loadTranscript2(result.transcriptPath);
|
|
27172
|
+
writeFileSync11(result.transcriptPath, JSON.stringify(words, null, 2));
|
|
27173
|
+
patchCaptionHtml2(dir, words);
|
|
27174
|
+
if (opts.json) {
|
|
27175
|
+
console.log(
|
|
27176
|
+
JSON.stringify({
|
|
27177
|
+
ok: true,
|
|
27178
|
+
model,
|
|
27179
|
+
wordCount: words.length,
|
|
27180
|
+
durationSeconds: result.durationSeconds,
|
|
27181
|
+
transcriptPath: result.transcriptPath
|
|
27182
|
+
})
|
|
27183
|
+
);
|
|
27184
|
+
} else {
|
|
27185
|
+
spin.stop(
|
|
27186
|
+
c.success(
|
|
27187
|
+
`Transcribed ${c.accent(String(words.length))} words (${result.durationSeconds.toFixed(1)}s)`
|
|
27188
|
+
)
|
|
27189
|
+
);
|
|
27190
|
+
}
|
|
27191
|
+
} catch (err) {
|
|
27192
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
27193
|
+
if (opts.json) {
|
|
27194
|
+
console.log(JSON.stringify({ ok: false, error: message }));
|
|
27195
|
+
} else {
|
|
27196
|
+
spin.stop(c.error(`Transcription failed: ${message}`));
|
|
27197
|
+
}
|
|
27198
|
+
process.exit(1);
|
|
27199
|
+
}
|
|
27200
|
+
}
|
|
27201
|
+
var transcribe_default;
|
|
27202
|
+
var init_transcribe2 = __esm({
|
|
27203
|
+
"src/commands/transcribe.ts"() {
|
|
27204
|
+
"use strict";
|
|
27205
|
+
init_dist();
|
|
27206
|
+
init_dist3();
|
|
27207
|
+
init_colors();
|
|
27208
|
+
init_manager();
|
|
27209
|
+
transcribe_default = defineCommand({
|
|
27210
|
+
meta: {
|
|
27211
|
+
name: "transcribe",
|
|
27212
|
+
description: "Transcribe audio/video to word-level timestamps, or import an existing transcript"
|
|
27213
|
+
},
|
|
27214
|
+
args: {
|
|
27215
|
+
input: {
|
|
27216
|
+
type: "positional",
|
|
27217
|
+
description: "Audio/video file to transcribe, or transcript file to import (.json, .srt, .vtt)",
|
|
27218
|
+
required: true
|
|
27219
|
+
},
|
|
27220
|
+
dir: {
|
|
27221
|
+
type: "string",
|
|
27222
|
+
description: "Project directory (default: current directory)",
|
|
27223
|
+
alias: "d"
|
|
27224
|
+
},
|
|
27225
|
+
model: {
|
|
27226
|
+
type: "string",
|
|
27227
|
+
description: `Whisper model (default: ${DEFAULT_MODEL}). Options: tiny.en, base.en, small.en, medium.en, large-v3`,
|
|
27228
|
+
alias: "m"
|
|
27229
|
+
},
|
|
27230
|
+
language: {
|
|
27231
|
+
type: "string",
|
|
27232
|
+
description: "Language code (e.g. en, es, ja). Filters out non-target language speech.",
|
|
27233
|
+
alias: "l"
|
|
27234
|
+
},
|
|
27235
|
+
json: {
|
|
27236
|
+
type: "boolean",
|
|
27237
|
+
description: "Output result as JSON",
|
|
27238
|
+
default: false
|
|
27239
|
+
}
|
|
27240
|
+
},
|
|
27241
|
+
async run({ args }) {
|
|
27242
|
+
const inputPath = resolve20(args.input);
|
|
27243
|
+
if (!existsSync35(inputPath)) {
|
|
27244
|
+
console.error(c.error(`File not found: ${args.input}`));
|
|
27245
|
+
process.exit(1);
|
|
27246
|
+
}
|
|
27247
|
+
const dir = resolve20(args.dir ?? ".");
|
|
27248
|
+
const ext = extname6(inputPath).toLowerCase();
|
|
27249
|
+
const isImport = ext === ".json" || ext === ".srt" || ext === ".vtt";
|
|
27250
|
+
if (isImport) {
|
|
27251
|
+
return importTranscript(inputPath, dir, args.json);
|
|
27252
|
+
}
|
|
27253
|
+
return transcribeAudio(inputPath, dir, {
|
|
27254
|
+
model: args.model,
|
|
27255
|
+
language: args.language,
|
|
27256
|
+
json: args.json
|
|
27257
|
+
});
|
|
27258
|
+
}
|
|
27259
|
+
});
|
|
27260
|
+
}
|
|
27261
|
+
});
|
|
27262
|
+
|
|
26704
27263
|
// src/commands/docs.ts
|
|
26705
27264
|
var docs_exports = {};
|
|
26706
27265
|
__export(docs_exports, {
|
|
26707
27266
|
default: () => docs_default
|
|
26708
27267
|
});
|
|
26709
|
-
import { readFileSync as
|
|
26710
|
-
import { resolve as
|
|
27268
|
+
import { readFileSync as readFileSync23, existsSync as existsSync36 } from "fs";
|
|
27269
|
+
import { resolve as resolve21, dirname as dirname14, join as join35 } from "path";
|
|
26711
27270
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
26712
27271
|
function docsDir() {
|
|
26713
27272
|
const thisFile = fileURLToPath6(import.meta.url);
|
|
26714
27273
|
const dir = dirname14(thisFile);
|
|
26715
|
-
const devPath =
|
|
26716
|
-
const builtPath =
|
|
26717
|
-
return
|
|
27274
|
+
const devPath = resolve21(dir, "..", "docs");
|
|
27275
|
+
const builtPath = resolve21(dir, "docs");
|
|
27276
|
+
return existsSync36(devPath) ? devPath : builtPath;
|
|
26718
27277
|
}
|
|
26719
27278
|
function formatInlineCode(line) {
|
|
26720
27279
|
return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
|
|
@@ -26805,12 +27364,12 @@ var init_docs = __esm({
|
|
|
26805
27364
|
}
|
|
26806
27365
|
process.exit(1);
|
|
26807
27366
|
}
|
|
26808
|
-
const filePath =
|
|
26809
|
-
if (!
|
|
27367
|
+
const filePath = join35(docsDir(), entry.file);
|
|
27368
|
+
if (!existsSync36(filePath)) {
|
|
26810
27369
|
console.error(c.error(`Doc file not found: ${filePath}`));
|
|
26811
27370
|
process.exit(1);
|
|
26812
27371
|
}
|
|
26813
|
-
const content =
|
|
27372
|
+
const content = readFileSync23(filePath, "utf-8");
|
|
26814
27373
|
console.log();
|
|
26815
27374
|
renderMarkdown(content);
|
|
26816
27375
|
}
|
|
@@ -27211,6 +27770,7 @@ var subCommands = {
|
|
|
27211
27770
|
benchmark: () => Promise.resolve().then(() => (init_benchmark(), benchmark_exports)).then((m) => m.default),
|
|
27212
27771
|
browser: () => Promise.resolve().then(() => (init_browser(), browser_exports)).then((m) => m.default),
|
|
27213
27772
|
skills: () => Promise.resolve().then(() => (init_install_skills(), install_skills_exports)).then((m) => m.default),
|
|
27773
|
+
transcribe: () => Promise.resolve().then(() => (init_transcribe2(), transcribe_exports2)).then((m) => m.default),
|
|
27214
27774
|
docs: () => Promise.resolve().then(() => (init_docs(), docs_exports)).then((m) => m.default),
|
|
27215
27775
|
doctor: () => Promise.resolve().then(() => (init_doctor(), doctor_exports)).then((m) => m.default),
|
|
27216
27776
|
upgrade: () => Promise.resolve().then(() => (init_upgrade(), upgrade_exports)).then((m) => m.default),
|