hyperframes 0.4.10 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +894 -236
- package/dist/docs/rendering.md +2 -0
- package/dist/skills/hyperframes/references/tts.md +19 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.4.
|
|
57
|
+
VERSION = true ? "0.4.11" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -3476,9 +3476,25 @@ var init_media = __esm({
|
|
|
3476
3476
|
// video_nested_in_timed_element
|
|
3477
3477
|
({ source, tags }) => {
|
|
3478
3478
|
const findings = [];
|
|
3479
|
+
const voidElements3 = /* @__PURE__ */ new Set([
|
|
3480
|
+
"area",
|
|
3481
|
+
"base",
|
|
3482
|
+
"br",
|
|
3483
|
+
"col",
|
|
3484
|
+
"embed",
|
|
3485
|
+
"hr",
|
|
3486
|
+
"img",
|
|
3487
|
+
"input",
|
|
3488
|
+
"link",
|
|
3489
|
+
"meta",
|
|
3490
|
+
"source",
|
|
3491
|
+
"track",
|
|
3492
|
+
"wbr"
|
|
3493
|
+
]);
|
|
3479
3494
|
const timedTagPositions = [];
|
|
3480
3495
|
for (const tag of tags) {
|
|
3481
3496
|
if (tag.name === "video" || tag.name === "audio") continue;
|
|
3497
|
+
if (voidElements3.has(tag.name)) continue;
|
|
3482
3498
|
if (readAttr(tag.raw, "data-composition-id")) continue;
|
|
3483
3499
|
if (readAttr(tag.raw, "data-start")) {
|
|
3484
3500
|
timedTagPositions.push({
|
|
@@ -3646,7 +3662,9 @@ function extractGsapWindows(script) {
|
|
|
3646
3662
|
let index = 0;
|
|
3647
3663
|
while ((match = methodPattern.exec(script)) !== null && index < parsed.animations.length) {
|
|
3648
3664
|
const raw = match[0];
|
|
3649
|
-
const
|
|
3665
|
+
const args = match[2] ?? "";
|
|
3666
|
+
if (!/^\s*["']/.test(args)) continue;
|
|
3667
|
+
const meta = parseGsapWindowMeta(match[1] ?? "", args);
|
|
3650
3668
|
const animation = parsed.animations[index];
|
|
3651
3669
|
index += 1;
|
|
3652
3670
|
if (!animation) continue;
|
|
@@ -20997,6 +21015,12 @@ async function createCaptureSession(serverUrl, outputDir, options, onBeforeCaptu
|
|
|
20997
21015
|
);
|
|
20998
21016
|
const { browser, captureMode } = await acquireBrowser(chromeArgs, config);
|
|
20999
21017
|
const page = await browser.newPage();
|
|
21018
|
+
await page.evaluateOnNewDocument(() => {
|
|
21019
|
+
const w = window;
|
|
21020
|
+
if (typeof w.__name !== "function") {
|
|
21021
|
+
w.__name = (fn, _name) => fn;
|
|
21022
|
+
}
|
|
21023
|
+
});
|
|
21000
21024
|
const browserVersion = await browser.version();
|
|
21001
21025
|
const expectedMajor = config?.expectedChromiumMajor;
|
|
21002
21026
|
if (Number.isFinite(expectedMajor)) {
|
|
@@ -21101,9 +21125,10 @@ async function initializeSession(session) {
|
|
|
21101
21125
|
`[FrameCapture] window.__hf not ready after ${pageReadyTimeout2}ms. Page must expose window.__hf = { duration, seek }.`
|
|
21102
21126
|
);
|
|
21103
21127
|
}
|
|
21128
|
+
const skipIdsLiteral = JSON.stringify(session.options.skipReadinessVideoIds ?? []);
|
|
21104
21129
|
const videosReady = await pollPageExpression(
|
|
21105
21130
|
page,
|
|
21106
|
-
`
|
|
21131
|
+
`(() => { const skip = new Set(${skipIdsLiteral}); const vids = Array.from(document.querySelectorAll("video")).filter(v => !skip.has(v.id)); return vids.length === 0 || vids.every(v => v.readyState >= 1); })()`,
|
|
21107
21132
|
pageReadyTimeout2
|
|
21108
21133
|
);
|
|
21109
21134
|
if (!videosReady) {
|
|
@@ -21163,10 +21188,11 @@ async function initializeSession(session) {
|
|
|
21163
21188
|
`[FrameCapture] window.__hf not ready after ${pageReadyTimeout}ms. Page must expose window.__hf = { duration, seek }.`
|
|
21164
21189
|
);
|
|
21165
21190
|
}
|
|
21191
|
+
const beginframeSkipIdsLiteral = JSON.stringify(session.options.skipReadinessVideoIds ?? []);
|
|
21166
21192
|
const videoDeadline = Date.now() + (session.config?.playerReadyTimeout ?? DEFAULT_CONFIG2.playerReadyTimeout);
|
|
21167
21193
|
while (Date.now() < videoDeadline) {
|
|
21168
21194
|
const videosReady = await page.evaluate(
|
|
21169
|
-
`
|
|
21195
|
+
`(() => { const skip = new Set(${beginframeSkipIdsLiteral}); const vids = Array.from(document.querySelectorAll("video")).filter(v => !skip.has(v.id)); return vids.length === 0 || vids.every(v => v.readyState >= 1); })()`
|
|
21170
21196
|
);
|
|
21171
21197
|
if (videosReady) break;
|
|
21172
21198
|
await new Promise((r2) => setTimeout(r2, 100));
|
|
@@ -22184,6 +22210,8 @@ var init_streamingEncoder = __esm({
|
|
|
22184
22210
|
|
|
22185
22211
|
// ../engine/src/utils/ffprobe.ts
|
|
22186
22212
|
import { spawn as spawn6 } from "child_process";
|
|
22213
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
22214
|
+
import { extname as extname4 } from "path";
|
|
22187
22215
|
function runFfprobe(args) {
|
|
22188
22216
|
return new Promise((resolve35, reject) => {
|
|
22189
22217
|
const proc = spawn6("ffprobe", args);
|
|
@@ -22220,6 +22248,67 @@ function parseProbeJson(stdout2) {
|
|
|
22220
22248
|
);
|
|
22221
22249
|
}
|
|
22222
22250
|
}
|
|
22251
|
+
function crc32(buf) {
|
|
22252
|
+
let crc = 4294967295;
|
|
22253
|
+
for (let i2 = 0; i2 < buf.length; i2++) {
|
|
22254
|
+
crc ^= buf[i2] ?? 0;
|
|
22255
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
22256
|
+
const mask = -(crc & 1);
|
|
22257
|
+
crc = crc >>> 1 ^ 3988292384 & mask;
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
return (crc ^ 4294967295) >>> 0;
|
|
22261
|
+
}
|
|
22262
|
+
function extractPngMetadataFromBuffer(buf) {
|
|
22263
|
+
if (buf.length < 8 || buf[0] !== 137 || buf[1] !== 80 || buf[2] !== 78 || buf[3] !== 71 || buf[4] !== 13 || buf[5] !== 10 || buf[6] !== 26 || buf[7] !== 10) {
|
|
22264
|
+
return null;
|
|
22265
|
+
}
|
|
22266
|
+
let width = 0;
|
|
22267
|
+
let height = 0;
|
|
22268
|
+
let seenIdat = false;
|
|
22269
|
+
let pos = 8;
|
|
22270
|
+
while (pos + 12 <= buf.length) {
|
|
22271
|
+
const chunkLen = buf.readUInt32BE(pos);
|
|
22272
|
+
const chunkType = buf.toString("ascii", pos + 4, pos + 8);
|
|
22273
|
+
if (pos + 12 + chunkLen > buf.length) return null;
|
|
22274
|
+
const chunkData = buf.subarray(pos + 8, pos + 8 + chunkLen);
|
|
22275
|
+
const chunkCrc = buf.readUInt32BE(pos + 8 + chunkLen);
|
|
22276
|
+
const chunkBytes = Buffer.concat([Buffer.from(chunkType, "ascii"), chunkData]);
|
|
22277
|
+
if (crc32(chunkBytes) !== chunkCrc) return null;
|
|
22278
|
+
if (chunkType === "IHDR" && chunkLen >= 8) {
|
|
22279
|
+
width = buf.readUInt32BE(pos + 8);
|
|
22280
|
+
height = buf.readUInt32BE(pos + 12);
|
|
22281
|
+
}
|
|
22282
|
+
if (chunkType === "IDAT") {
|
|
22283
|
+
seenIdat = true;
|
|
22284
|
+
}
|
|
22285
|
+
if (chunkType === "cICP" && chunkLen === 4 && !seenIdat) {
|
|
22286
|
+
const primariesCode = chunkData[0] ?? 0;
|
|
22287
|
+
const transferCode = chunkData[1] ?? 0;
|
|
22288
|
+
const matrixCode = chunkData[2] ?? 0;
|
|
22289
|
+
return {
|
|
22290
|
+
width,
|
|
22291
|
+
height,
|
|
22292
|
+
colorSpace: {
|
|
22293
|
+
colorPrimaries: primariesCode === 9 ? "bt2020" : primariesCode === 1 ? "bt709" : `unknown-${primariesCode}`,
|
|
22294
|
+
colorTransfer: transferCode === 16 ? "smpte2084" : transferCode === 18 ? "arib-std-b67" : transferCode === 1 ? "bt709" : `unknown-${transferCode}`,
|
|
22295
|
+
colorSpace: matrixCode === 9 ? "bt2020nc" : matrixCode === 0 ? "gbr" : `unknown-${matrixCode}`
|
|
22296
|
+
}
|
|
22297
|
+
};
|
|
22298
|
+
}
|
|
22299
|
+
if (chunkType === "IEND") break;
|
|
22300
|
+
pos += 12 + chunkLen;
|
|
22301
|
+
}
|
|
22302
|
+
return width > 0 && height > 0 ? { width, height, colorSpace: null } : null;
|
|
22303
|
+
}
|
|
22304
|
+
function extractStillImageMetadata(filePath) {
|
|
22305
|
+
if (extname4(filePath).toLowerCase() !== ".png") return null;
|
|
22306
|
+
try {
|
|
22307
|
+
return extractPngMetadataFromBuffer(readFileSync14(filePath));
|
|
22308
|
+
} catch {
|
|
22309
|
+
return null;
|
|
22310
|
+
}
|
|
22311
|
+
}
|
|
22223
22312
|
function parseFrameRate(frameRateStr) {
|
|
22224
22313
|
if (!frameRateStr) return 0;
|
|
22225
22314
|
const parts = frameRateStr.split("/");
|
|
@@ -22234,18 +22323,38 @@ async function extractVideoMetadata(filePath) {
|
|
|
22234
22323
|
const cached2 = videoMetadataCache.get(filePath);
|
|
22235
22324
|
if (cached2) return cached2;
|
|
22236
22325
|
const probePromise = (async () => {
|
|
22237
|
-
const
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22326
|
+
const stillImageMeta = extractStillImageMetadata(filePath);
|
|
22327
|
+
let output = null;
|
|
22328
|
+
try {
|
|
22329
|
+
const stdout2 = await runFfprobe([
|
|
22330
|
+
"-v",
|
|
22331
|
+
"quiet",
|
|
22332
|
+
"-print_format",
|
|
22333
|
+
"json",
|
|
22334
|
+
"-show_format",
|
|
22335
|
+
"-show_streams",
|
|
22336
|
+
filePath
|
|
22337
|
+
]);
|
|
22338
|
+
output = parseProbeJson(stdout2);
|
|
22339
|
+
} catch (error) {
|
|
22340
|
+
if (!stillImageMeta) throw error;
|
|
22341
|
+
}
|
|
22342
|
+
const videoStream = output?.streams.find((s2) => s2.codec_type === "video");
|
|
22343
|
+
if (!videoStream) {
|
|
22344
|
+
if (stillImageMeta) {
|
|
22345
|
+
return {
|
|
22346
|
+
durationSeconds: 0,
|
|
22347
|
+
width: stillImageMeta.width,
|
|
22348
|
+
height: stillImageMeta.height,
|
|
22349
|
+
fps: 0,
|
|
22350
|
+
videoCodec: "png",
|
|
22351
|
+
hasAudio: false,
|
|
22352
|
+
isVFR: false,
|
|
22353
|
+
colorSpace: stillImageMeta.colorSpace
|
|
22354
|
+
};
|
|
22355
|
+
}
|
|
22356
|
+
throw new Error("[FFmpeg] No video stream found");
|
|
22357
|
+
}
|
|
22249
22358
|
const rFps = parseFrameRate(videoStream.r_frame_rate);
|
|
22250
22359
|
const avgFps = parseFrameRate(videoStream.avg_frame_rate);
|
|
22251
22360
|
const fps = avgFps || rFps;
|
|
@@ -22253,16 +22362,17 @@ async function extractVideoMetadata(filePath) {
|
|
|
22253
22362
|
const colorTransfer = videoStream.color_transfer || "";
|
|
22254
22363
|
const colorPrimaries = videoStream.color_primaries || "";
|
|
22255
22364
|
const colorSpaceVal = videoStream.color_space || "";
|
|
22256
|
-
const
|
|
22365
|
+
const ffprobeColorSpace = colorTransfer || colorPrimaries || colorSpaceVal ? { colorTransfer, colorPrimaries, colorSpace: colorSpaceVal } : null;
|
|
22366
|
+
const colorSpace = ffprobeColorSpace ?? stillImageMeta?.colorSpace ?? null;
|
|
22257
22367
|
return {
|
|
22258
|
-
durationSeconds: output
|
|
22259
|
-
width: videoStream.width || 0,
|
|
22260
|
-
height: videoStream.height || 0,
|
|
22368
|
+
durationSeconds: output?.format.duration ? parseFloat(output.format.duration) : 0,
|
|
22369
|
+
width: videoStream.width || stillImageMeta?.width || 0,
|
|
22370
|
+
height: videoStream.height || stillImageMeta?.height || 0,
|
|
22261
22371
|
fps,
|
|
22262
22372
|
videoCodec: videoStream.codec_name || "unknown",
|
|
22263
|
-
hasAudio: output
|
|
22373
|
+
hasAudio: output?.streams.some((s2) => s2.codec_type === "audio") ?? false,
|
|
22264
22374
|
isVFR,
|
|
22265
|
-
colorSpace
|
|
22375
|
+
colorSpace
|
|
22266
22376
|
};
|
|
22267
22377
|
})();
|
|
22268
22378
|
videoMetadataCache.set(filePath, probePromise);
|
|
@@ -22369,13 +22479,13 @@ var init_ffprobe = __esm({
|
|
|
22369
22479
|
// ../engine/src/utils/urlDownloader.ts
|
|
22370
22480
|
import { createWriteStream as createWriteStream2, existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
|
|
22371
22481
|
import { createHash } from "crypto";
|
|
22372
|
-
import { join as join20, extname as
|
|
22482
|
+
import { join as join20, extname as extname5 } from "path";
|
|
22373
22483
|
import { Readable } from "stream";
|
|
22374
22484
|
import { finished } from "stream/promises";
|
|
22375
22485
|
function getFilenameFromUrl(url) {
|
|
22376
22486
|
const hash2 = createHash("md5").update(url).digest("hex").slice(0, 12);
|
|
22377
22487
|
const urlObj = new URL(url);
|
|
22378
|
-
const ext =
|
|
22488
|
+
const ext = extname5(urlObj.pathname) || ".mp4";
|
|
22379
22489
|
return `download_${hash2}${ext}`;
|
|
22380
22490
|
}
|
|
22381
22491
|
async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
@@ -22479,6 +22589,34 @@ function parseVideoElements(html) {
|
|
|
22479
22589
|
}
|
|
22480
22590
|
return videos;
|
|
22481
22591
|
}
|
|
22592
|
+
function parseImageElements(html) {
|
|
22593
|
+
const images = [];
|
|
22594
|
+
const { document: document2 } = parseHTML(html);
|
|
22595
|
+
const imgEls = document2.querySelectorAll("img[src]");
|
|
22596
|
+
let autoIdCounter = 0;
|
|
22597
|
+
for (const el of imgEls) {
|
|
22598
|
+
const src = el.getAttribute("src");
|
|
22599
|
+
if (!src) continue;
|
|
22600
|
+
const id = el.getAttribute("id") || `hf-img-${autoIdCounter++}`;
|
|
22601
|
+
if (!el.getAttribute("id")) {
|
|
22602
|
+
el.setAttribute("id", id);
|
|
22603
|
+
}
|
|
22604
|
+
const startAttr = el.getAttribute("data-start");
|
|
22605
|
+
const endAttr = el.getAttribute("data-end");
|
|
22606
|
+
const durationAttr = el.getAttribute("data-duration");
|
|
22607
|
+
const start = startAttr ? parseFloat(startAttr) : 0;
|
|
22608
|
+
let end = 0;
|
|
22609
|
+
if (endAttr) {
|
|
22610
|
+
end = parseFloat(endAttr);
|
|
22611
|
+
} else if (durationAttr) {
|
|
22612
|
+
end = start + parseFloat(durationAttr);
|
|
22613
|
+
} else {
|
|
22614
|
+
end = Infinity;
|
|
22615
|
+
}
|
|
22616
|
+
images.push({ id, src, start, end });
|
|
22617
|
+
}
|
|
22618
|
+
return images;
|
|
22619
|
+
}
|
|
22482
22620
|
async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config) {
|
|
22483
22621
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
22484
22622
|
const { fps, outputDir, quality = 95, format = "jpg" } = options;
|
|
@@ -22966,8 +23104,8 @@ async function queryVideoElementBounds(page, videoIds) {
|
|
|
22966
23104
|
});
|
|
22967
23105
|
}, videoIds);
|
|
22968
23106
|
}
|
|
22969
|
-
async function queryElementStacking(page,
|
|
22970
|
-
const hdrIds = Array.from(
|
|
23107
|
+
async function queryElementStacking(page, nativeHdrIds) {
|
|
23108
|
+
const hdrIds = Array.from(nativeHdrIds);
|
|
22971
23109
|
return page.evaluate((hdrIdList) => {
|
|
22972
23110
|
const hdrSet = new Set(hdrIdList);
|
|
22973
23111
|
const elements = document.querySelectorAll("[data-start]");
|
|
@@ -23096,7 +23234,12 @@ async function queryElementStacking(page, nativeHdrVideoIds) {
|
|
|
23096
23234
|
// affine blit can apply rotation/scale/translate properly. For DOM
|
|
23097
23235
|
// elements, the element-level transform is sufficient for reference.
|
|
23098
23236
|
transform: isHdrEl ? getViewportMatrix(el) : style.transform || "none",
|
|
23099
|
-
borderRadius: isHdrEl ? getEffectiveBorderRadius(el) : [0, 0, 0, 0]
|
|
23237
|
+
borderRadius: isHdrEl ? getEffectiveBorderRadius(el) : [0, 0, 0, 0],
|
|
23238
|
+
// `getComputedStyle` returns "" when the property doesn't apply (e.g.
|
|
23239
|
+
// for non-replaced elements); normalize to the CSS defaults so callers
|
|
23240
|
+
// can rely on a populated value.
|
|
23241
|
+
objectFit: style.objectFit || "fill",
|
|
23242
|
+
objectPosition: style.objectPosition || "50% 50%"
|
|
23100
23243
|
});
|
|
23101
23244
|
}
|
|
23102
23245
|
return results;
|
|
@@ -23624,8 +23767,8 @@ var init_parallelCoordinator = __esm({
|
|
|
23624
23767
|
// ../engine/src/services/fileServer.ts
|
|
23625
23768
|
import { Hono as Hono2 } from "hono";
|
|
23626
23769
|
import { serve } from "@hono/node-server";
|
|
23627
|
-
import { readFileSync as
|
|
23628
|
-
import { join as join24, extname as
|
|
23770
|
+
import { readFileSync as readFileSync15, existsSync as existsSync21, statSync as statSync6 } from "fs";
|
|
23771
|
+
import { join as join24, extname as extname6 } from "path";
|
|
23629
23772
|
function stripEmbeddedRuntimeScripts(html) {
|
|
23630
23773
|
if (!html) return html;
|
|
23631
23774
|
const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
@@ -23702,14 +23845,14 @@ function createFileServer(options) {
|
|
|
23702
23845
|
if (!existsSync21(filePath) || !statSync6(filePath).isFile()) {
|
|
23703
23846
|
return c2.text("Not found", 404);
|
|
23704
23847
|
}
|
|
23705
|
-
const ext =
|
|
23848
|
+
const ext = extname6(filePath).toLowerCase();
|
|
23706
23849
|
const contentType = MIME_TYPES2[ext] || "application/octet-stream";
|
|
23707
23850
|
if (ext === ".html") {
|
|
23708
|
-
const rawHtml =
|
|
23851
|
+
const rawHtml = readFileSync15(filePath, "utf-8");
|
|
23709
23852
|
const html = relativePath === "index.html" ? injectScriptsIntoHtml(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
|
|
23710
23853
|
return c2.text(html, 200, { "Content-Type": contentType });
|
|
23711
23854
|
}
|
|
23712
|
-
const content =
|
|
23855
|
+
const content = readFileSync15(filePath);
|
|
23713
23856
|
return new Response(content, {
|
|
23714
23857
|
status: 200,
|
|
23715
23858
|
headers: { "Content-Type": contentType }
|
|
@@ -24103,6 +24246,125 @@ function blitRgb48leAffine(canvas, source, matrix, srcW, srcH, canvasW, canvasH,
|
|
|
24103
24246
|
}
|
|
24104
24247
|
}
|
|
24105
24248
|
}
|
|
24249
|
+
function parseObjectPositionAxis(value, axis) {
|
|
24250
|
+
const lower2 = value.trim().toLowerCase();
|
|
24251
|
+
if (lower2 === "left" || lower2 === "top") return 0;
|
|
24252
|
+
if (lower2 === "right" || lower2 === "bottom") return 1;
|
|
24253
|
+
if (lower2 === "center" || lower2 === "") return 0.5;
|
|
24254
|
+
if (lower2.endsWith("%")) {
|
|
24255
|
+
const pct = parseFloat(lower2) / 100;
|
|
24256
|
+
return Number.isFinite(pct) ? Math.max(0, Math.min(1, pct)) : 0.5;
|
|
24257
|
+
}
|
|
24258
|
+
if (axis === "x" || axis === "y") return 0.5;
|
|
24259
|
+
return 0.5;
|
|
24260
|
+
}
|
|
24261
|
+
function parseObjectPosition(css) {
|
|
24262
|
+
if (!css || !css.trim()) return { x: 0.5, y: 0.5 };
|
|
24263
|
+
const tokens = css.trim().split(/\s+/);
|
|
24264
|
+
if (tokens.length === 1) {
|
|
24265
|
+
const single = tokens[0] ?? "";
|
|
24266
|
+
const v = parseObjectPositionAxis(single, "x");
|
|
24267
|
+
return { x: v, y: 0.5 };
|
|
24268
|
+
}
|
|
24269
|
+
return {
|
|
24270
|
+
x: parseObjectPositionAxis(tokens[0] ?? "", "x"),
|
|
24271
|
+
y: parseObjectPositionAxis(tokens[1] ?? "", "y")
|
|
24272
|
+
};
|
|
24273
|
+
}
|
|
24274
|
+
function computeObjectFitRect(srcW, srcH, dstW, dstH, fit, pos) {
|
|
24275
|
+
let renderedW = dstW;
|
|
24276
|
+
let renderedH = dstH;
|
|
24277
|
+
if (fit === "fill") {
|
|
24278
|
+
return { dx: 0, dy: 0, dw: dstW, dh: dstH };
|
|
24279
|
+
}
|
|
24280
|
+
if (fit === "none") {
|
|
24281
|
+
renderedW = srcW;
|
|
24282
|
+
renderedH = srcH;
|
|
24283
|
+
} else if (fit === "scale-down") {
|
|
24284
|
+
const scale = Math.min(dstW / srcW, dstH / srcH, 1);
|
|
24285
|
+
renderedW = srcW * scale;
|
|
24286
|
+
renderedH = srcH * scale;
|
|
24287
|
+
} else if (fit === "cover") {
|
|
24288
|
+
const scale = Math.max(dstW / srcW, dstH / srcH);
|
|
24289
|
+
renderedW = srcW * scale;
|
|
24290
|
+
renderedH = srcH * scale;
|
|
24291
|
+
} else {
|
|
24292
|
+
const scale = Math.min(dstW / srcW, dstH / srcH);
|
|
24293
|
+
renderedW = srcW * scale;
|
|
24294
|
+
renderedH = srcH * scale;
|
|
24295
|
+
}
|
|
24296
|
+
const dx = (dstW - renderedW) * pos.x;
|
|
24297
|
+
const dy = (dstH - renderedH) * pos.y;
|
|
24298
|
+
return { dx, dy, dw: renderedW, dh: renderedH };
|
|
24299
|
+
}
|
|
24300
|
+
function resampleRgb48leObjectFit(source, srcW, srcH, dstW, dstH, fit = "fill", objectPosition) {
|
|
24301
|
+
if (srcW <= 0 || srcH <= 0 || dstW <= 0 || dstH <= 0) {
|
|
24302
|
+
return source;
|
|
24303
|
+
}
|
|
24304
|
+
if (fit === "fill" && srcW === dstW && srcH === dstH) {
|
|
24305
|
+
return source;
|
|
24306
|
+
}
|
|
24307
|
+
const pos = parseObjectPosition(objectPosition);
|
|
24308
|
+
const rect = computeObjectFitRect(srcW, srcH, dstW, dstH, fit, pos);
|
|
24309
|
+
const dst = Buffer.alloc(dstW * dstH * 6);
|
|
24310
|
+
const stride = dstW * 6;
|
|
24311
|
+
const xMin = Math.max(0, Math.floor(rect.dx));
|
|
24312
|
+
const yMin = Math.max(0, Math.floor(rect.dy));
|
|
24313
|
+
const xMax = Math.min(dstW, Math.ceil(rect.dx + rect.dw));
|
|
24314
|
+
const yMax = Math.min(dstH, Math.ceil(rect.dy + rect.dh));
|
|
24315
|
+
if (rect.dw <= 0 || rect.dh <= 0) {
|
|
24316
|
+
return dst;
|
|
24317
|
+
}
|
|
24318
|
+
const invScaleX = srcW / rect.dw;
|
|
24319
|
+
const invScaleY = srcH / rect.dh;
|
|
24320
|
+
for (let dy = yMin; dy < yMax; dy++) {
|
|
24321
|
+
const rowOff = dy * stride;
|
|
24322
|
+
const sy = (dy + 0.5 - rect.dy) * invScaleY - 0.5;
|
|
24323
|
+
const syc = Math.max(0, Math.min(srcH - 1, sy));
|
|
24324
|
+
const y0 = Math.floor(syc);
|
|
24325
|
+
const y1 = Math.min(y0 + 1, srcH - 1);
|
|
24326
|
+
const fy = syc - y0;
|
|
24327
|
+
const ify = 1 - fy;
|
|
24328
|
+
for (let dx = xMin; dx < xMax; dx++) {
|
|
24329
|
+
const sx = (dx + 0.5 - rect.dx) * invScaleX - 0.5;
|
|
24330
|
+
const sxc = Math.max(0, Math.min(srcW - 1, sx));
|
|
24331
|
+
const x0 = Math.floor(sxc);
|
|
24332
|
+
const x1 = Math.min(x0 + 1, srcW - 1);
|
|
24333
|
+
const fx = sxc - x0;
|
|
24334
|
+
const ifx = 1 - fx;
|
|
24335
|
+
const off00 = (y0 * srcW + x0) * 6;
|
|
24336
|
+
const off10 = (y0 * srcW + x1) * 6;
|
|
24337
|
+
const off01 = (y1 * srcW + x0) * 6;
|
|
24338
|
+
const off11 = (y1 * srcW + x1) * 6;
|
|
24339
|
+
const w00 = ifx * ify;
|
|
24340
|
+
const w10 = fx * ify;
|
|
24341
|
+
const w01 = ifx * fy;
|
|
24342
|
+
const w11 = fx * fy;
|
|
24343
|
+
const r2 = source.readUInt16LE(off00) * w00 + source.readUInt16LE(off10) * w10 + source.readUInt16LE(off01) * w01 + source.readUInt16LE(off11) * w11;
|
|
24344
|
+
const g = source.readUInt16LE(off00 + 2) * w00 + source.readUInt16LE(off10 + 2) * w10 + source.readUInt16LE(off01 + 2) * w01 + source.readUInt16LE(off11 + 2) * w11;
|
|
24345
|
+
const b = source.readUInt16LE(off00 + 4) * w00 + source.readUInt16LE(off10 + 4) * w10 + source.readUInt16LE(off01 + 4) * w01 + source.readUInt16LE(off11 + 4) * w11;
|
|
24346
|
+
const dstOff = rowOff + dx * 6;
|
|
24347
|
+
dst.writeUInt16LE(Math.round(r2), dstOff);
|
|
24348
|
+
dst.writeUInt16LE(Math.round(g), dstOff + 2);
|
|
24349
|
+
dst.writeUInt16LE(Math.round(b), dstOff + 4);
|
|
24350
|
+
}
|
|
24351
|
+
}
|
|
24352
|
+
return dst;
|
|
24353
|
+
}
|
|
24354
|
+
function normalizeObjectFit(value) {
|
|
24355
|
+
switch ((value ?? "").trim().toLowerCase()) {
|
|
24356
|
+
case "cover":
|
|
24357
|
+
return "cover";
|
|
24358
|
+
case "contain":
|
|
24359
|
+
return "contain";
|
|
24360
|
+
case "none":
|
|
24361
|
+
return "none";
|
|
24362
|
+
case "scale-down":
|
|
24363
|
+
return "scale-down";
|
|
24364
|
+
default:
|
|
24365
|
+
return "fill";
|
|
24366
|
+
}
|
|
24367
|
+
}
|
|
24106
24368
|
function parseTransformMatrix(css) {
|
|
24107
24369
|
if (!css || css === "none") return null;
|
|
24108
24370
|
const match = css.match(
|
|
@@ -25035,6 +25297,7 @@ var init_hdrCapture = __esm({
|
|
|
25035
25297
|
var src_exports = {};
|
|
25036
25298
|
__export(src_exports, {
|
|
25037
25299
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG2,
|
|
25300
|
+
DEFAULT_HDR10_MASTERING: () => DEFAULT_HDR10_MASTERING,
|
|
25038
25301
|
DOM_LAYER_MASK_STYLE_ID: () => DOM_LAYER_MASK_STYLE_ID,
|
|
25039
25302
|
ENABLE_BROWSER_POOL: () => ENABLE_BROWSER_POOL,
|
|
25040
25303
|
ENCODER_PRESETS: () => ENCODER_PRESETS,
|
|
@@ -25101,8 +25364,10 @@ __export(src_exports, {
|
|
|
25101
25364
|
linearToHdr: () => linearToHdr,
|
|
25102
25365
|
mergeWorkerFrames: () => mergeWorkerFrames,
|
|
25103
25366
|
muxVideoWithAudio: () => muxVideoWithAudio,
|
|
25367
|
+
normalizeObjectFit: () => normalizeObjectFit,
|
|
25104
25368
|
pageScreenshotCapture: () => pageScreenshotCapture,
|
|
25105
25369
|
parseAudioElements: () => parseAudioElements,
|
|
25370
|
+
parseImageElements: () => parseImageElements,
|
|
25106
25371
|
parseTransformMatrix: () => parseTransformMatrix,
|
|
25107
25372
|
parseVideoElements: () => parseVideoElements,
|
|
25108
25373
|
prepareCaptureSessionForReuse: () => prepareCaptureSessionForReuse,
|
|
@@ -25112,6 +25377,7 @@ __export(src_exports, {
|
|
|
25112
25377
|
queryVideoElementBounds: () => queryVideoElementBounds,
|
|
25113
25378
|
releaseBrowser: () => releaseBrowser,
|
|
25114
25379
|
removeDomLayerMask: () => removeDomLayerMask,
|
|
25380
|
+
resampleRgb48leObjectFit: () => resampleRgb48leObjectFit,
|
|
25115
25381
|
resolveConfig: () => resolveConfig,
|
|
25116
25382
|
resolveHeadlessShellPath: () => resolveHeadlessShellPath,
|
|
25117
25383
|
roundedRectAlpha: () => roundedRectAlpha,
|
|
@@ -25231,7 +25497,7 @@ var init_staticGuard = __esm({
|
|
|
25231
25497
|
});
|
|
25232
25498
|
|
|
25233
25499
|
// ../core/src/compiler/htmlBundler.ts
|
|
25234
|
-
import { readFileSync as
|
|
25500
|
+
import { readFileSync as readFileSync16, existsSync as existsSync23 } from "fs";
|
|
25235
25501
|
import { join as join26, resolve as resolve11, isAbsolute as isAbsolute2, sep as sep2 } from "path";
|
|
25236
25502
|
import { transformSync } from "esbuild";
|
|
25237
25503
|
function parseHTMLContent(html) {
|
|
@@ -25302,7 +25568,7 @@ function isRelativeUrl(url) {
|
|
|
25302
25568
|
function safeReadFile(filePath) {
|
|
25303
25569
|
if (!existsSync23(filePath)) return null;
|
|
25304
25570
|
try {
|
|
25305
|
-
return
|
|
25571
|
+
return readFileSync16(filePath, "utf-8");
|
|
25306
25572
|
} catch {
|
|
25307
25573
|
return null;
|
|
25308
25574
|
}
|
|
@@ -25310,7 +25576,7 @@ function safeReadFile(filePath) {
|
|
|
25310
25576
|
function safeReadFileBuffer(filePath) {
|
|
25311
25577
|
if (!existsSync23(filePath)) return null;
|
|
25312
25578
|
try {
|
|
25313
|
-
return
|
|
25579
|
+
return readFileSync16(filePath);
|
|
25314
25580
|
} catch {
|
|
25315
25581
|
return null;
|
|
25316
25582
|
}
|
|
@@ -25503,7 +25769,7 @@ function stripJsCommentsParserSafe(source) {
|
|
|
25503
25769
|
async function bundleToSingleHtml(projectDir, options) {
|
|
25504
25770
|
const indexPath = join26(projectDir, "index.html");
|
|
25505
25771
|
if (!existsSync23(indexPath)) throw new Error("index.html not found in project directory");
|
|
25506
|
-
const rawHtml =
|
|
25772
|
+
const rawHtml = readFileSync16(indexPath, "utf-8");
|
|
25507
25773
|
const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
|
|
25508
25774
|
const staticGuard = validateHyperframeHtmlContract(compiled);
|
|
25509
25775
|
if (!staticGuard.isValid) {
|
|
@@ -25778,7 +26044,7 @@ var init_compiler = __esm({
|
|
|
25778
26044
|
|
|
25779
26045
|
// ../producer/src/services/hyperframeRuntimeLoader.ts
|
|
25780
26046
|
import { createHash as createHash2 } from "crypto";
|
|
25781
|
-
import { existsSync as existsSync24, readFileSync as
|
|
26047
|
+
import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
|
|
25782
26048
|
import { dirname as dirname8, resolve as resolve12 } from "path";
|
|
25783
26049
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
25784
26050
|
function resolveHyperframeManifestPath() {
|
|
@@ -25807,7 +26073,7 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
25807
26073
|
`[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
|
|
25808
26074
|
);
|
|
25809
26075
|
}
|
|
25810
|
-
const manifestRaw =
|
|
26076
|
+
const manifestRaw = readFileSync17(manifestPath, "utf8");
|
|
25811
26077
|
const manifest = JSON.parse(manifestRaw);
|
|
25812
26078
|
const runtimeFileName = manifest.artifacts?.iife;
|
|
25813
26079
|
if (!runtimeFileName || !manifest.sha256) {
|
|
@@ -25819,7 +26085,7 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
25819
26085
|
if (!existsSync24(runtimePath)) {
|
|
25820
26086
|
throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
|
|
25821
26087
|
}
|
|
25822
|
-
const runtimeSource =
|
|
26088
|
+
const runtimeSource = readFileSync17(runtimePath, "utf8");
|
|
25823
26089
|
const runtimeSha = createHash2("sha256").update(runtimeSource, "utf8").digest("hex");
|
|
25824
26090
|
if (runtimeSha !== manifest.sha256) {
|
|
25825
26091
|
throw new Error(
|
|
@@ -25858,8 +26124,8 @@ var init_hyperframeRuntimeLoader = __esm({
|
|
|
25858
26124
|
// ../producer/src/services/fileServer.ts
|
|
25859
26125
|
import { Hono as Hono3 } from "hono";
|
|
25860
26126
|
import { serve as serve2 } from "@hono/node-server";
|
|
25861
|
-
import { readFileSync as
|
|
25862
|
-
import { join as join27, extname as
|
|
26127
|
+
import { readFileSync as readFileSync18, existsSync as existsSync25, statSync as statSync7 } from "fs";
|
|
26128
|
+
import { join as join27, extname as extname7 } from "path";
|
|
25863
26129
|
function stripEmbeddedRuntimeScripts3(html) {
|
|
25864
26130
|
if (!html) return html;
|
|
25865
26131
|
const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
@@ -25953,10 +26219,10 @@ function createFileServer2(options) {
|
|
|
25953
26219
|
}
|
|
25954
26220
|
return c2.text("Not found", 404);
|
|
25955
26221
|
}
|
|
25956
|
-
const ext =
|
|
26222
|
+
const ext = extname7(filePath).toLowerCase();
|
|
25957
26223
|
const contentType = MIME_TYPES3[ext] || "application/octet-stream";
|
|
25958
26224
|
if (ext === ".html") {
|
|
25959
|
-
const rawHtml =
|
|
26225
|
+
const rawHtml = readFileSync18(filePath, "utf-8");
|
|
25960
26226
|
const isIndex = relativePath === "index.html";
|
|
25961
26227
|
let html = rawHtml;
|
|
25962
26228
|
if (preHeadScripts.length > 0) {
|
|
@@ -25965,7 +26231,7 @@ function createFileServer2(options) {
|
|
|
25965
26231
|
html = isIndex ? injectScriptsIntoHtml2(html, headScripts, bodyScripts, stripEmbeddedRuntime) : html;
|
|
25966
26232
|
return c2.text(html, 200, { "Content-Type": contentType });
|
|
25967
26233
|
}
|
|
25968
|
-
const content =
|
|
26234
|
+
const content = readFileSync18(filePath);
|
|
25969
26235
|
return new Response(content, {
|
|
25970
26236
|
status: 200,
|
|
25971
26237
|
headers: { "Content-Type": contentType }
|
|
@@ -26411,7 +26677,7 @@ var init_fontData_generated = __esm({
|
|
|
26411
26677
|
});
|
|
26412
26678
|
|
|
26413
26679
|
// ../producer/src/services/deterministicFonts.ts
|
|
26414
|
-
import { existsSync as existsSync26, mkdirSync as mkdirSync15, readFileSync as
|
|
26680
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync15, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
26415
26681
|
import { homedir as homedir7 } from "os";
|
|
26416
26682
|
import { join as join29 } from "path";
|
|
26417
26683
|
function normalizeFamilyName(family) {
|
|
@@ -26567,7 +26833,7 @@ async function fetchGoogleFont(familyName) {
|
|
|
26567
26833
|
continue;
|
|
26568
26834
|
}
|
|
26569
26835
|
}
|
|
26570
|
-
const fontBytes =
|
|
26836
|
+
const fontBytes = readFileSync19(cachePath2);
|
|
26571
26837
|
const dataUri = `data:font/woff2;base64,${fontBytes.toString("base64")}`;
|
|
26572
26838
|
faces.push({ weight, style, dataUri });
|
|
26573
26839
|
}
|
|
@@ -26749,7 +27015,7 @@ var init_deterministicFonts = __esm({
|
|
|
26749
27015
|
});
|
|
26750
27016
|
|
|
26751
27017
|
// ../producer/src/services/htmlCompiler.ts
|
|
26752
|
-
import { readFileSync as
|
|
27018
|
+
import { readFileSync as readFileSync20, existsSync as existsSync27, mkdirSync as mkdirSync16 } from "fs";
|
|
26753
27019
|
import { join as join30, dirname as dirname9, resolve as resolve14 } from "path";
|
|
26754
27020
|
import postcss from "postcss";
|
|
26755
27021
|
function dedupeElementsById(elements) {
|
|
@@ -26853,6 +27119,7 @@ async function compileHtmlFile(html, baseDir, downloadDir) {
|
|
|
26853
27119
|
async function parseSubCompositions(html, projectDir, downloadDir, parentOffset = 0, parentEnd = Infinity, visited = /* @__PURE__ */ new Set()) {
|
|
26854
27120
|
const videos = [];
|
|
26855
27121
|
const audios = [];
|
|
27122
|
+
const images = [];
|
|
26856
27123
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
26857
27124
|
const { document: document2 } = parseHTML(html);
|
|
26858
27125
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
@@ -26872,7 +27139,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
26872
27139
|
if (!existsSync27(filePath)) {
|
|
26873
27140
|
continue;
|
|
26874
27141
|
}
|
|
26875
|
-
const rawSubHtml =
|
|
27142
|
+
const rawSubHtml = readFileSync20(filePath, "utf-8");
|
|
26876
27143
|
const nestedVisited = new Set(visited);
|
|
26877
27144
|
nestedVisited.add(filePath);
|
|
26878
27145
|
workItems.push({ srcPath, absoluteStart, absoluteEnd, filePath, rawSubHtml, nestedVisited });
|
|
@@ -26894,12 +27161,14 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
26894
27161
|
);
|
|
26895
27162
|
const subVideos = parseVideoElements(compiledSub);
|
|
26896
27163
|
const subAudios = parseAudioElements(compiledSub);
|
|
27164
|
+
const subImages = parseImageElements(compiledSub);
|
|
26897
27165
|
return {
|
|
26898
27166
|
srcPath: item.srcPath,
|
|
26899
27167
|
compiledSub,
|
|
26900
27168
|
nested,
|
|
26901
27169
|
subVideos,
|
|
26902
27170
|
subAudios,
|
|
27171
|
+
subImages,
|
|
26903
27172
|
absoluteStart: item.absoluteStart,
|
|
26904
27173
|
absoluteEnd: item.absoluteEnd
|
|
26905
27174
|
};
|
|
@@ -26912,6 +27181,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
26912
27181
|
}
|
|
26913
27182
|
videos.push(...r2.nested.videos);
|
|
26914
27183
|
audios.push(...r2.nested.audios);
|
|
27184
|
+
images.push(...r2.nested.images);
|
|
26915
27185
|
for (const v of r2.subVideos) {
|
|
26916
27186
|
v.start += r2.absoluteStart;
|
|
26917
27187
|
v.end += r2.absoluteStart;
|
|
@@ -26932,10 +27202,20 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
26932
27202
|
audios.push(a);
|
|
26933
27203
|
}
|
|
26934
27204
|
}
|
|
26935
|
-
|
|
27205
|
+
for (const img of r2.subImages) {
|
|
27206
|
+
img.start += r2.absoluteStart;
|
|
27207
|
+
img.end += r2.absoluteStart;
|
|
27208
|
+
if (img.end > r2.absoluteEnd) {
|
|
27209
|
+
img.end = r2.absoluteEnd;
|
|
27210
|
+
}
|
|
27211
|
+
if (img.start < r2.absoluteEnd) {
|
|
27212
|
+
images.push(img);
|
|
27213
|
+
}
|
|
27214
|
+
}
|
|
27215
|
+
if (r2.subVideos.length > 0 || r2.subAudios.length > 0 || r2.subImages.length > 0 || r2.nested.videos.length > 0 || r2.nested.audios.length > 0 || r2.nested.images.length > 0) {
|
|
26936
27216
|
}
|
|
26937
27217
|
}
|
|
26938
|
-
return { videos, audios, subCompositions };
|
|
27218
|
+
return { videos, audios, images, subCompositions };
|
|
26939
27219
|
}
|
|
26940
27220
|
function promoteCssImportsToLinkTags(html) {
|
|
26941
27221
|
const { document: document2 } = parseHTML(html);
|
|
@@ -27067,7 +27347,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
27067
27347
|
if (!compHtml) {
|
|
27068
27348
|
const filePath = resolve14(projectDir, srcPath);
|
|
27069
27349
|
if (existsSync27(filePath)) {
|
|
27070
|
-
compHtml =
|
|
27350
|
+
compHtml = readFileSync20(filePath, "utf-8");
|
|
27071
27351
|
}
|
|
27072
27352
|
}
|
|
27073
27353
|
if (!compHtml) {
|
|
@@ -27235,7 +27515,9 @@ ${html}
|
|
|
27235
27515
|
</html>`;
|
|
27236
27516
|
}
|
|
27237
27517
|
async function inlineExternalScripts(html) {
|
|
27238
|
-
const
|
|
27518
|
+
const fullHtml = ensureFullDocument(html);
|
|
27519
|
+
const wrappedFragment = fullHtml !== html;
|
|
27520
|
+
const { document: document2 } = parseHTML(fullHtml);
|
|
27239
27521
|
const scripts = document2.querySelectorAll("script[src]");
|
|
27240
27522
|
const externalScripts = [];
|
|
27241
27523
|
for (const el of scripts) {
|
|
@@ -27254,20 +27536,20 @@ async function inlineExternalScripts(html) {
|
|
|
27254
27536
|
return { src, text: await response.text() };
|
|
27255
27537
|
})
|
|
27256
27538
|
);
|
|
27257
|
-
let result = html;
|
|
27258
27539
|
for (let i2 = 0; i2 < downloads.length; i2++) {
|
|
27259
27540
|
const download = downloads[i2];
|
|
27260
|
-
const { src } = externalScripts[i2];
|
|
27541
|
+
const { el, src } = externalScripts[i2];
|
|
27261
27542
|
if (download.status === "fulfilled") {
|
|
27262
|
-
const escapedSrc = src.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27263
|
-
const scriptTagRe = new RegExp(
|
|
27264
|
-
`<script\\b[^>]*\\bsrc=["']${escapedSrc}["'][^>]*>\\s*</script>`,
|
|
27265
|
-
"is"
|
|
27266
|
-
);
|
|
27267
27543
|
const safeText = download.value.text.replace(/<\/script/gi, "<\\/script");
|
|
27268
|
-
|
|
27544
|
+
const inlineScript = document2.createElement("script");
|
|
27545
|
+
for (const attr of Array.from(el.attributes)) {
|
|
27546
|
+
if (attr.name.toLowerCase() === "src") continue;
|
|
27547
|
+
inlineScript.setAttribute(attr.name, attr.value);
|
|
27548
|
+
}
|
|
27549
|
+
inlineScript.textContent = `/* inlined: ${src} */
|
|
27269
27550
|
${safeText}
|
|
27270
|
-
|
|
27551
|
+
`;
|
|
27552
|
+
el.replaceWith(inlineScript);
|
|
27271
27553
|
console.log(`[Compiler] Inlined CDN script: ${src}`);
|
|
27272
27554
|
} else {
|
|
27273
27555
|
console.warn(
|
|
@@ -27275,7 +27557,7 @@ ${safeText}
|
|
|
27275
27557
|
);
|
|
27276
27558
|
}
|
|
27277
27559
|
}
|
|
27278
|
-
return
|
|
27560
|
+
return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
|
|
27279
27561
|
}
|
|
27280
27562
|
function collectExternalAssets(html, projectDir) {
|
|
27281
27563
|
const absProjectDir = resolve14(projectDir);
|
|
@@ -27335,7 +27617,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
27335
27617
|
};
|
|
27336
27618
|
}
|
|
27337
27619
|
async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
27338
|
-
const rawHtml =
|
|
27620
|
+
const rawHtml = readFileSync20(htmlPath, "utf-8");
|
|
27339
27621
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
27340
27622
|
rawHtml,
|
|
27341
27623
|
projectDir,
|
|
@@ -27344,6 +27626,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
27344
27626
|
const {
|
|
27345
27627
|
videos: subVideos,
|
|
27346
27628
|
audios: subAudios,
|
|
27629
|
+
images: subImages,
|
|
27347
27630
|
subCompositions
|
|
27348
27631
|
} = await parseSubCompositions(compiledHtml, projectDir, downloadDir);
|
|
27349
27632
|
const fullHtml = ensureFullDocument(compiledHtml);
|
|
@@ -27360,8 +27643,10 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
27360
27643
|
const { html, externalAssets } = collectExternalAssets(assembledHtml, projectDir);
|
|
27361
27644
|
const mainVideos = parseVideoElements(html);
|
|
27362
27645
|
const mainAudios = parseAudioElements(html);
|
|
27646
|
+
const mainImages = parseImageElements(html);
|
|
27363
27647
|
const videos = dedupeElementsById([...mainVideos, ...subVideos]);
|
|
27364
27648
|
const audios = dedupeElementsById([...mainAudios, ...subAudios]);
|
|
27649
|
+
const images = dedupeElementsById([...mainImages, ...subImages]);
|
|
27365
27650
|
for (const video of videos) {
|
|
27366
27651
|
if (isHttpUrl(video.src)) continue;
|
|
27367
27652
|
const videoPath = resolve14(projectDir, video.src);
|
|
@@ -27392,6 +27677,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
27392
27677
|
subCompositions,
|
|
27393
27678
|
videos,
|
|
27394
27679
|
audios,
|
|
27680
|
+
images,
|
|
27395
27681
|
unresolvedCompositions,
|
|
27396
27682
|
externalAssets,
|
|
27397
27683
|
width,
|
|
@@ -27476,12 +27762,15 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
27476
27762
|
const {
|
|
27477
27763
|
videos: subVideos,
|
|
27478
27764
|
audios: subAudios,
|
|
27765
|
+
images: subImages,
|
|
27479
27766
|
subCompositions
|
|
27480
27767
|
} = await parseSubCompositions(html, projectDir, downloadDir);
|
|
27481
27768
|
const mainVideos = parseVideoElements(html);
|
|
27482
27769
|
const mainAudios = parseAudioElements(html);
|
|
27770
|
+
const mainImages = parseImageElements(html);
|
|
27483
27771
|
const videos = dedupeElementsById([...mainVideos, ...subVideos]);
|
|
27484
27772
|
const audios = dedupeElementsById([...mainAudios, ...subAudios]);
|
|
27773
|
+
const images = dedupeElementsById([...mainImages, ...subImages]);
|
|
27485
27774
|
const remaining = compiled.unresolvedCompositions.filter(
|
|
27486
27775
|
(c2) => !resolutions.some((r2) => r2.id === c2.id)
|
|
27487
27776
|
);
|
|
@@ -27491,6 +27780,7 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
27491
27780
|
subCompositions,
|
|
27492
27781
|
videos,
|
|
27493
27782
|
audios,
|
|
27783
|
+
images,
|
|
27494
27784
|
unresolvedCompositions: remaining,
|
|
27495
27785
|
renderModeHints: compiled.renderModeHints
|
|
27496
27786
|
};
|
|
@@ -27557,7 +27847,7 @@ import {
|
|
|
27557
27847
|
existsSync as existsSync28,
|
|
27558
27848
|
mkdirSync as mkdirSync17,
|
|
27559
27849
|
rmSync as rmSync6,
|
|
27560
|
-
readFileSync as
|
|
27850
|
+
readFileSync as readFileSync21,
|
|
27561
27851
|
readdirSync as readdirSync11,
|
|
27562
27852
|
writeFileSync as writeFileSync10,
|
|
27563
27853
|
copyFileSync as copyFileSync2,
|
|
@@ -27700,7 +27990,7 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, w
|
|
|
27700
27990
|
return;
|
|
27701
27991
|
}
|
|
27702
27992
|
try {
|
|
27703
|
-
const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(
|
|
27993
|
+
const { data: hdrRgb, width: srcW, height: srcH } = decodePngToRgb48le(readFileSync21(framePath));
|
|
27704
27994
|
if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
|
|
27705
27995
|
convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
|
|
27706
27996
|
}
|
|
@@ -27742,6 +28032,55 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, w
|
|
|
27742
28032
|
}
|
|
27743
28033
|
}
|
|
27744
28034
|
}
|
|
28035
|
+
function blitHdrImageLayer(canvas, el, hdrImageBuffers, width, height, log, sourceTransfer, targetTransfer) {
|
|
28036
|
+
const buf = hdrImageBuffers.get(el.id);
|
|
28037
|
+
if (!buf) {
|
|
28038
|
+
return;
|
|
28039
|
+
}
|
|
28040
|
+
try {
|
|
28041
|
+
let hdrRgb = buf.data;
|
|
28042
|
+
if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
|
|
28043
|
+
hdrRgb = Buffer.from(buf.data);
|
|
28044
|
+
convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
|
|
28045
|
+
}
|
|
28046
|
+
const viewportMatrix = parseTransformMatrix(el.transform);
|
|
28047
|
+
const br = el.borderRadius;
|
|
28048
|
+
const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0;
|
|
28049
|
+
const borderRadiusParam = hasBorderRadius ? br : void 0;
|
|
28050
|
+
if (viewportMatrix) {
|
|
28051
|
+
blitRgb48leAffine(
|
|
28052
|
+
canvas,
|
|
28053
|
+
hdrRgb,
|
|
28054
|
+
viewportMatrix,
|
|
28055
|
+
buf.width,
|
|
28056
|
+
buf.height,
|
|
28057
|
+
width,
|
|
28058
|
+
height,
|
|
28059
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
28060
|
+
borderRadiusParam
|
|
28061
|
+
);
|
|
28062
|
+
} else {
|
|
28063
|
+
blitRgb48leRegion(
|
|
28064
|
+
canvas,
|
|
28065
|
+
hdrRgb,
|
|
28066
|
+
el.x,
|
|
28067
|
+
el.y,
|
|
28068
|
+
buf.width,
|
|
28069
|
+
buf.height,
|
|
28070
|
+
width,
|
|
28071
|
+
height,
|
|
28072
|
+
el.opacity < 0.999 ? el.opacity : void 0,
|
|
28073
|
+
borderRadiusParam
|
|
28074
|
+
);
|
|
28075
|
+
}
|
|
28076
|
+
} catch (err) {
|
|
28077
|
+
if (log) {
|
|
28078
|
+
log.debug(`HDR image blit failed for ${el.id}`, {
|
|
28079
|
+
error: err instanceof Error ? err.message : String(err)
|
|
28080
|
+
});
|
|
28081
|
+
}
|
|
28082
|
+
}
|
|
28083
|
+
}
|
|
27745
28084
|
function createRenderJob(config) {
|
|
27746
28085
|
return {
|
|
27747
28086
|
id: randomUUID2(),
|
|
@@ -27793,6 +28132,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
27793
28132
|
let lastBrowserConsole = [];
|
|
27794
28133
|
let restoreLogger = null;
|
|
27795
28134
|
const perfStages = {};
|
|
28135
|
+
const hdrDiagnostics = {
|
|
28136
|
+
videoExtractionFailures: 0,
|
|
28137
|
+
imageDecodeFailures: 0
|
|
28138
|
+
};
|
|
27796
28139
|
const perfOutputPath = join31(workDir, "perf-summary.json");
|
|
27797
28140
|
const cfg = { ...job.config.producerConfig ?? resolveConfig() };
|
|
27798
28141
|
const outputFormat = job.config.format ?? "mp4";
|
|
@@ -27824,7 +28167,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
27824
28167
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
27825
28168
|
}
|
|
27826
28169
|
assertNotAborted();
|
|
27827
|
-
const rawEntry =
|
|
28170
|
+
const rawEntry = readFileSync21(htmlPath, "utf-8");
|
|
27828
28171
|
if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
|
|
27829
28172
|
const wrapperPath = join31(workDir, "standalone-entry.html");
|
|
27830
28173
|
const projectIndexPath = join31(projectDir, "index.html");
|
|
@@ -27834,7 +28177,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
27834
28177
|
);
|
|
27835
28178
|
}
|
|
27836
28179
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
27837
|
-
|
|
28180
|
+
readFileSync21(projectIndexPath, "utf-8"),
|
|
27838
28181
|
entryFile
|
|
27839
28182
|
);
|
|
27840
28183
|
if (!standaloneHtml) {
|
|
@@ -27869,6 +28212,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
27869
28212
|
duration: compiled.staticDuration,
|
|
27870
28213
|
videos: compiled.videos,
|
|
27871
28214
|
audios: compiled.audios,
|
|
28215
|
+
images: compiled.images,
|
|
27872
28216
|
width: compiled.width,
|
|
27873
28217
|
height: compiled.height
|
|
27874
28218
|
};
|
|
@@ -27933,6 +28277,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
27933
28277
|
assertNotAborted();
|
|
27934
28278
|
composition.videos = compiled.videos;
|
|
27935
28279
|
composition.audios = compiled.audios;
|
|
28280
|
+
composition.images = compiled.images;
|
|
27936
28281
|
writeCompiledArtifacts(compiled, workDir, Boolean(job.config.debug));
|
|
27937
28282
|
}
|
|
27938
28283
|
}
|
|
@@ -28088,6 +28433,30 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28088
28433
|
})
|
|
28089
28434
|
);
|
|
28090
28435
|
}
|
|
28436
|
+
const nativeHdrImageIds = /* @__PURE__ */ new Set();
|
|
28437
|
+
const imageTransfers = /* @__PURE__ */ new Map();
|
|
28438
|
+
const hdrImageSrcPaths = /* @__PURE__ */ new Map();
|
|
28439
|
+
const imageColorSpaces = [];
|
|
28440
|
+
if (job.config.hdr && composition.images.length > 0) {
|
|
28441
|
+
const probed = await Promise.all(
|
|
28442
|
+
composition.images.map(async (img) => {
|
|
28443
|
+
let imgPath = img.src;
|
|
28444
|
+
if (!imgPath.startsWith("/")) {
|
|
28445
|
+
const fromCompiled = existsSync28(join31(compiledDir, imgPath)) ? join31(compiledDir, imgPath) : join31(projectDir, imgPath);
|
|
28446
|
+
imgPath = fromCompiled;
|
|
28447
|
+
}
|
|
28448
|
+
if (!existsSync28(imgPath)) return null;
|
|
28449
|
+
const meta = await extractVideoMetadata(imgPath);
|
|
28450
|
+
if (isHdrColorSpace(meta.colorSpace)) {
|
|
28451
|
+
nativeHdrImageIds.add(img.id);
|
|
28452
|
+
imageTransfers.set(img.id, detectTransfer(meta.colorSpace));
|
|
28453
|
+
hdrImageSrcPaths.set(img.id, imgPath);
|
|
28454
|
+
}
|
|
28455
|
+
return meta.colorSpace;
|
|
28456
|
+
})
|
|
28457
|
+
);
|
|
28458
|
+
imageColorSpaces.push(...probed);
|
|
28459
|
+
}
|
|
28091
28460
|
if (composition.videos.length > 0) {
|
|
28092
28461
|
extractionResult = await extractAllVideoFrames(
|
|
28093
28462
|
composition.videos,
|
|
@@ -28125,21 +28494,22 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28125
28494
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
28126
28495
|
}
|
|
28127
28496
|
let effectiveHdr;
|
|
28128
|
-
if (job.config.hdr
|
|
28129
|
-
const
|
|
28130
|
-
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
28134
|
-
|
|
28135
|
-
|
|
28136
|
-
|
|
28137
|
-
|
|
28138
|
-
effectiveHdr = { transfer: firstTransfer };
|
|
28497
|
+
if (job.config.hdr) {
|
|
28498
|
+
const videoColorSpaces = (extractionResult?.extracted ?? []).map(
|
|
28499
|
+
(ext) => ext.metadata.colorSpace
|
|
28500
|
+
);
|
|
28501
|
+
const allColorSpaces = [...videoColorSpaces, ...imageColorSpaces];
|
|
28502
|
+
if (allColorSpaces.length > 0) {
|
|
28503
|
+
const info = analyzeCompositionHdr(allColorSpaces);
|
|
28504
|
+
if (info.hasHdr && info.dominantTransfer) {
|
|
28505
|
+
effectiveHdr = { transfer: info.dominantTransfer };
|
|
28506
|
+
}
|
|
28139
28507
|
}
|
|
28140
28508
|
}
|
|
28141
28509
|
if (effectiveHdr && outputFormat !== "mp4") {
|
|
28142
|
-
log.
|
|
28510
|
+
log.warn(
|
|
28511
|
+
`[Render] HDR source detected but format is ${outputFormat} \u2014 falling back to SDR. Use --format mp4 for HDR10 output.`
|
|
28512
|
+
);
|
|
28143
28513
|
effectiveHdr = void 0;
|
|
28144
28514
|
}
|
|
28145
28515
|
if (effectiveHdr) {
|
|
@@ -28192,12 +28562,14 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28192
28562
|
const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
28193
28563
|
const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
|
|
28194
28564
|
const videoOnlyPath = join31(workDir, `video-only${videoExt}`);
|
|
28195
|
-
const
|
|
28565
|
+
const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
|
|
28566
|
+
const hasHdrContent = effectiveHdr && nativeHdrIds.size > 0;
|
|
28196
28567
|
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
28197
28568
|
const preset = getEncoderPreset(job.config.quality, outputFormat, encoderHdr);
|
|
28198
28569
|
job.framesRendered = 0;
|
|
28199
28570
|
if (hasHdrContent) {
|
|
28200
28571
|
log.info("[Render] HDR layered composite: z-ordered DOM + native HLG video layers");
|
|
28572
|
+
cfg.forceScreenshot = true;
|
|
28201
28573
|
const hdrVideoIds = composition.videos.filter((v) => nativeHdrVideoIds.has(v.id)).map((v) => v.id);
|
|
28202
28574
|
const hdrVideoSrcPaths = /* @__PURE__ */ new Map();
|
|
28203
28575
|
for (const v of composition.videos) {
|
|
@@ -28213,7 +28585,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28213
28585
|
const domSession = await createCaptureSession(
|
|
28214
28586
|
fileServer.url,
|
|
28215
28587
|
framesDir,
|
|
28216
|
-
captureOptions,
|
|
28588
|
+
{ ...captureOptions, skipReadinessVideoIds: Array.from(nativeHdrVideoIds) },
|
|
28217
28589
|
createVideoFrameInjector(frameLookup),
|
|
28218
28590
|
cfg
|
|
28219
28591
|
);
|
|
@@ -28267,13 +28639,22 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28267
28639
|
);
|
|
28268
28640
|
assertNotAborted();
|
|
28269
28641
|
const hdrExtractionDims = /* @__PURE__ */ new Map();
|
|
28642
|
+
const hdrImageFitInfo = /* @__PURE__ */ new Map();
|
|
28270
28643
|
const hdrVideoStartTimes = /* @__PURE__ */ new Map();
|
|
28271
28644
|
for (const v of composition.videos) {
|
|
28272
28645
|
if (hdrVideoIds.includes(v.id)) {
|
|
28273
28646
|
hdrVideoStartTimes.set(v.id, v.start);
|
|
28274
28647
|
}
|
|
28275
28648
|
}
|
|
28276
|
-
const
|
|
28649
|
+
const hdrImageStartTimes = /* @__PURE__ */ new Map();
|
|
28650
|
+
for (const img of composition.images) {
|
|
28651
|
+
if (nativeHdrImageIds.has(img.id)) {
|
|
28652
|
+
hdrImageStartTimes.set(img.id, img.start);
|
|
28653
|
+
}
|
|
28654
|
+
}
|
|
28655
|
+
const uniqueStartTimes = [
|
|
28656
|
+
.../* @__PURE__ */ new Set([...hdrVideoStartTimes.values(), ...hdrImageStartTimes.values()])
|
|
28657
|
+
].sort((a, b) => a - b);
|
|
28277
28658
|
for (const seekTime of uniqueStartTimes) {
|
|
28278
28659
|
await domSession.page.evaluate((t3) => {
|
|
28279
28660
|
if (window.__hf && typeof window.__hf.seek === "function") window.__hf.seek(t3);
|
|
@@ -28281,11 +28662,17 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28281
28662
|
if (domSession.onBeforeCapture) {
|
|
28282
28663
|
await domSession.onBeforeCapture(domSession.page, seekTime);
|
|
28283
28664
|
}
|
|
28284
|
-
const stacking = await queryElementStacking(domSession.page,
|
|
28665
|
+
const stacking = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
28285
28666
|
for (const el of stacking) {
|
|
28286
28667
|
if (el.isHdr && el.layoutWidth > 0 && el.layoutHeight > 0 && !hdrExtractionDims.has(el.id)) {
|
|
28287
28668
|
hdrExtractionDims.set(el.id, { width: el.layoutWidth, height: el.layoutHeight });
|
|
28288
28669
|
}
|
|
28670
|
+
if (el.isHdr && nativeHdrImageIds.has(el.id) && !hdrImageFitInfo.has(el.id)) {
|
|
28671
|
+
hdrImageFitInfo.set(el.id, {
|
|
28672
|
+
fit: el.objectFit,
|
|
28673
|
+
position: el.objectPosition
|
|
28674
|
+
});
|
|
28675
|
+
}
|
|
28289
28676
|
}
|
|
28290
28677
|
}
|
|
28291
28678
|
const hdrFrameDirs = /* @__PURE__ */ new Map();
|
|
@@ -28316,14 +28703,59 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28316
28703
|
];
|
|
28317
28704
|
const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
|
|
28318
28705
|
if (!result.success) {
|
|
28319
|
-
|
|
28706
|
+
hdrDiagnostics.videoExtractionFailures += 1;
|
|
28707
|
+
log.error("HDR frame pre-extraction failed; aborting render", {
|
|
28320
28708
|
videoId,
|
|
28321
28709
|
srcPath,
|
|
28322
28710
|
stderr: result.stderr.slice(-400)
|
|
28323
28711
|
});
|
|
28712
|
+
throw new Error(
|
|
28713
|
+
`HDR frame extraction failed for video "${videoId}". Aborting render to avoid shipping black HDR layers.`
|
|
28714
|
+
);
|
|
28324
28715
|
}
|
|
28325
28716
|
hdrFrameDirs.set(videoId, frameDir);
|
|
28326
28717
|
}
|
|
28718
|
+
const hdrImageBuffers = /* @__PURE__ */ new Map();
|
|
28719
|
+
for (const [imageId, srcPath] of hdrImageSrcPaths) {
|
|
28720
|
+
try {
|
|
28721
|
+
const decoded = decodePngToRgb48le(readFileSync21(srcPath));
|
|
28722
|
+
const layout2 = hdrExtractionDims.get(imageId);
|
|
28723
|
+
const fitInfo = hdrImageFitInfo.get(imageId);
|
|
28724
|
+
if (layout2 && (layout2.width !== decoded.width || layout2.height !== decoded.height)) {
|
|
28725
|
+
const fit = normalizeObjectFit(fitInfo?.fit);
|
|
28726
|
+
const resampled = resampleRgb48leObjectFit(
|
|
28727
|
+
decoded.data,
|
|
28728
|
+
decoded.width,
|
|
28729
|
+
decoded.height,
|
|
28730
|
+
layout2.width,
|
|
28731
|
+
layout2.height,
|
|
28732
|
+
fit,
|
|
28733
|
+
fitInfo?.position
|
|
28734
|
+
);
|
|
28735
|
+
hdrImageBuffers.set(imageId, {
|
|
28736
|
+
data: resampled,
|
|
28737
|
+
width: layout2.width,
|
|
28738
|
+
height: layout2.height
|
|
28739
|
+
});
|
|
28740
|
+
} else {
|
|
28741
|
+
hdrImageBuffers.set(imageId, {
|
|
28742
|
+
data: Buffer.from(decoded.data),
|
|
28743
|
+
width: decoded.width,
|
|
28744
|
+
height: decoded.height
|
|
28745
|
+
});
|
|
28746
|
+
}
|
|
28747
|
+
} catch (err) {
|
|
28748
|
+
hdrDiagnostics.imageDecodeFailures += 1;
|
|
28749
|
+
log.error("HDR image decode failed; aborting render", {
|
|
28750
|
+
imageId,
|
|
28751
|
+
srcPath,
|
|
28752
|
+
error: err instanceof Error ? err.message : String(err)
|
|
28753
|
+
});
|
|
28754
|
+
throw new Error(
|
|
28755
|
+
`HDR image decode failed for image "${imageId}". Aborting render to avoid shipping missing HDR image layers.`
|
|
28756
|
+
);
|
|
28757
|
+
}
|
|
28758
|
+
}
|
|
28327
28759
|
assertNotAborted();
|
|
28328
28760
|
try {
|
|
28329
28761
|
let countNonZeroAlpha2 = function(rgba) {
|
|
@@ -28381,38 +28813,67 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28381
28813
|
const layer = layers[layerIdx];
|
|
28382
28814
|
if (layer.type === "hdr") {
|
|
28383
28815
|
const before2 = shouldLog ? countNonZeroRgb482(canvas) : 0;
|
|
28384
|
-
|
|
28385
|
-
|
|
28386
|
-
|
|
28387
|
-
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
28391
|
-
|
|
28392
|
-
|
|
28393
|
-
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28816
|
+
const isHdrImage = nativeHdrImageIds.has(layer.element.id);
|
|
28817
|
+
if (isHdrImage) {
|
|
28818
|
+
blitHdrImageLayer(
|
|
28819
|
+
canvas,
|
|
28820
|
+
layer.element,
|
|
28821
|
+
hdrImageBuffers,
|
|
28822
|
+
width,
|
|
28823
|
+
height,
|
|
28824
|
+
log,
|
|
28825
|
+
imageTransfers.get(layer.element.id),
|
|
28826
|
+
effectiveHdr?.transfer
|
|
28827
|
+
);
|
|
28828
|
+
} else {
|
|
28829
|
+
blitHdrVideoLayer(
|
|
28830
|
+
canvas,
|
|
28831
|
+
layer.element,
|
|
28832
|
+
time,
|
|
28833
|
+
job.config.fps,
|
|
28834
|
+
hdrFrameDirs,
|
|
28835
|
+
hdrVideoStartTimes,
|
|
28836
|
+
width,
|
|
28837
|
+
height,
|
|
28838
|
+
log,
|
|
28839
|
+
videoTransfers.get(layer.element.id),
|
|
28840
|
+
effectiveHdr?.transfer
|
|
28841
|
+
);
|
|
28842
|
+
}
|
|
28397
28843
|
if (shouldLog) {
|
|
28398
28844
|
const after2 = countNonZeroRgb482(canvas);
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28845
|
+
if (isHdrImage) {
|
|
28846
|
+
const buf = hdrImageBuffers.get(layer.element.id);
|
|
28847
|
+
log.info("[diag] hdr layer blit", {
|
|
28848
|
+
frame: debugFrameIndex,
|
|
28849
|
+
layerIdx,
|
|
28850
|
+
id: layer.element.id,
|
|
28851
|
+
kind: "image",
|
|
28852
|
+
pixelsAdded: after2 - before2,
|
|
28853
|
+
totalNonZero: after2,
|
|
28854
|
+
bufferDecoded: !!buf,
|
|
28855
|
+
bufferDims: buf ? `${buf.width}x${buf.height}` : null
|
|
28856
|
+
});
|
|
28857
|
+
} else {
|
|
28858
|
+
const frameDir = hdrFrameDirs.get(layer.element.id);
|
|
28859
|
+
const startTime = hdrVideoStartTimes.get(layer.element.id) ?? 0;
|
|
28860
|
+
const localTime = time - startTime;
|
|
28861
|
+
const frameNum = Math.floor(localTime * job.config.fps) + 1;
|
|
28862
|
+
const expectedFrame = frameDir ? join31(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
|
|
28863
|
+
log.info("[diag] hdr layer blit", {
|
|
28864
|
+
frame: debugFrameIndex,
|
|
28865
|
+
layerIdx,
|
|
28866
|
+
id: layer.element.id,
|
|
28867
|
+
kind: "video",
|
|
28868
|
+
pixelsAdded: after2 - before2,
|
|
28869
|
+
totalNonZero: after2,
|
|
28870
|
+
startTime,
|
|
28871
|
+
localTime: localTime.toFixed(3),
|
|
28872
|
+
hdrFrameNum: frameNum,
|
|
28873
|
+
expectedFrame,
|
|
28874
|
+
expectedFrameExists: expectedFrame ? existsSync28(expectedFrame) : false
|
|
28875
|
+
});
|
|
28876
|
+
}
|
|
28416
28877
|
}
|
|
28417
28878
|
} else {
|
|
28418
28879
|
const allElementIds = fullStacking.map((e2) => e2.id);
|
|
@@ -28487,7 +28948,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28487
28948
|
if (beforeCaptureHook) {
|
|
28488
28949
|
await beforeCaptureHook(domSession.page, time);
|
|
28489
28950
|
}
|
|
28490
|
-
const stackingInfo = await queryElementStacking(domSession.page,
|
|
28951
|
+
const stackingInfo = await queryElementStacking(domSession.page, nativeHdrIds);
|
|
28491
28952
|
const activeTransition = transitionRanges.find(
|
|
28492
28953
|
(t3) => i2 >= t3.startFrame && i2 <= t3.endFrame
|
|
28493
28954
|
);
|
|
@@ -28519,22 +28980,35 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28519
28980
|
}
|
|
28520
28981
|
for (const el of stackingInfo) {
|
|
28521
28982
|
if (!el.isHdr || !sceneIds.has(el.id)) continue;
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
|
|
28525
|
-
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
|
|
28529
|
-
|
|
28530
|
-
|
|
28531
|
-
|
|
28532
|
-
|
|
28533
|
-
|
|
28534
|
-
|
|
28983
|
+
if (nativeHdrImageIds.has(el.id)) {
|
|
28984
|
+
blitHdrImageLayer(
|
|
28985
|
+
sceneBuf,
|
|
28986
|
+
el,
|
|
28987
|
+
hdrImageBuffers,
|
|
28988
|
+
width,
|
|
28989
|
+
height,
|
|
28990
|
+
log,
|
|
28991
|
+
imageTransfers.get(el.id),
|
|
28992
|
+
effectiveHdr?.transfer
|
|
28993
|
+
);
|
|
28994
|
+
} else {
|
|
28995
|
+
blitHdrVideoLayer(
|
|
28996
|
+
sceneBuf,
|
|
28997
|
+
el,
|
|
28998
|
+
time,
|
|
28999
|
+
job.config.fps,
|
|
29000
|
+
hdrFrameDirs,
|
|
29001
|
+
hdrVideoStartTimes,
|
|
29002
|
+
width,
|
|
29003
|
+
height,
|
|
29004
|
+
log,
|
|
29005
|
+
videoTransfers.get(el.id),
|
|
29006
|
+
effectiveHdr?.transfer
|
|
29007
|
+
);
|
|
29008
|
+
}
|
|
28535
29009
|
}
|
|
28536
29010
|
const showIds = Array.from(sceneIds);
|
|
28537
|
-
const hideIds = stackingInfo.map((e2) => e2.id).filter((id) => !sceneIds.has(id) ||
|
|
29011
|
+
const hideIds = stackingInfo.map((e2) => e2.id).filter((id) => !sceneIds.has(id) || nativeHdrIds.has(id));
|
|
28538
29012
|
await applyDomLayerMask(domSession.page, showIds, hideIds);
|
|
28539
29013
|
const domPng = await captureAlphaPng(domSession.page, width, height);
|
|
28540
29014
|
await removeDomLayerMask(domSession.page, hideIds);
|
|
@@ -28655,7 +29129,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28655
29129
|
fileServer.url,
|
|
28656
29130
|
workDir,
|
|
28657
29131
|
tasks,
|
|
28658
|
-
captureOptions,
|
|
29132
|
+
{ ...captureOptions, skipReadinessVideoIds: Array.from(nativeHdrVideoIds) },
|
|
28659
29133
|
() => createVideoFrameInjector(frameLookup),
|
|
28660
29134
|
abortSignal,
|
|
28661
29135
|
(progress) => {
|
|
@@ -28685,7 +29159,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28685
29159
|
const session = probeSession ?? await createCaptureSession(
|
|
28686
29160
|
fileServer.url,
|
|
28687
29161
|
framesDir,
|
|
28688
|
-
captureOptions,
|
|
29162
|
+
{ ...captureOptions, skipReadinessVideoIds: Array.from(nativeHdrVideoIds) },
|
|
28689
29163
|
videoInjector,
|
|
28690
29164
|
cfg
|
|
28691
29165
|
);
|
|
@@ -28736,7 +29210,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28736
29210
|
fileServer.url,
|
|
28737
29211
|
workDir,
|
|
28738
29212
|
tasks,
|
|
28739
|
-
captureOptions,
|
|
29213
|
+
{ ...captureOptions, skipReadinessVideoIds: Array.from(nativeHdrVideoIds) },
|
|
28740
29214
|
() => createVideoFrameInjector(frameLookup),
|
|
28741
29215
|
abortSignal,
|
|
28742
29216
|
(progress) => {
|
|
@@ -28767,7 +29241,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28767
29241
|
const session = probeSession ?? await createCaptureSession(
|
|
28768
29242
|
fileServer.url,
|
|
28769
29243
|
framesDir,
|
|
28770
|
-
captureOptions,
|
|
29244
|
+
{ ...captureOptions, skipReadinessVideoIds: Array.from(nativeHdrVideoIds) },
|
|
28771
29245
|
videoInjector,
|
|
28772
29246
|
cfg
|
|
28773
29247
|
);
|
|
@@ -28885,6 +29359,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28885
29359
|
videoCount: composition.videos.length,
|
|
28886
29360
|
audioCount: composition.audios.length,
|
|
28887
29361
|
stages: perfStages,
|
|
29362
|
+
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0,
|
|
28888
29363
|
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0
|
|
28889
29364
|
};
|
|
28890
29365
|
job.perfSummary = perfSummary;
|
|
@@ -28965,7 +29440,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
28965
29440
|
elapsedMs: elapsed,
|
|
28966
29441
|
freeMemoryMB: freeMemMB,
|
|
28967
29442
|
browserConsoleTail: lastBrowserConsole.length > 0 ? lastBrowserConsole.slice(-30) : void 0,
|
|
28968
|
-
perfStages: Object.keys(perfStages).length > 0 ? { ...perfStages } : void 0
|
|
29443
|
+
perfStages: Object.keys(perfStages).length > 0 ? { ...perfStages } : void 0,
|
|
29444
|
+
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0
|
|
28969
29445
|
};
|
|
28970
29446
|
if (fileServer) {
|
|
28971
29447
|
const fs4 = fileServer;
|
|
@@ -29042,7 +29518,7 @@ var init_config3 = __esm({
|
|
|
29042
29518
|
});
|
|
29043
29519
|
|
|
29044
29520
|
// ../producer/src/services/hyperframeLint.ts
|
|
29045
|
-
import { existsSync as existsSync29, readFileSync as
|
|
29521
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, statSync as statSync8 } from "fs";
|
|
29046
29522
|
import { resolve as resolve16, join as join32 } from "path";
|
|
29047
29523
|
function isStringRecord(value) {
|
|
29048
29524
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -29085,7 +29561,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
29085
29561
|
if (existsSync29(absoluteEntryPath) && statSync8(absoluteEntryPath).isFile()) {
|
|
29086
29562
|
return {
|
|
29087
29563
|
entryFile,
|
|
29088
|
-
html:
|
|
29564
|
+
html: readFileSync22(absoluteEntryPath, "utf-8"),
|
|
29089
29565
|
source: "projectDir"
|
|
29090
29566
|
};
|
|
29091
29567
|
}
|
|
@@ -29715,7 +30191,7 @@ __export(studioServer_exports, {
|
|
|
29715
30191
|
});
|
|
29716
30192
|
import { Hono as Hono5 } from "hono";
|
|
29717
30193
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
29718
|
-
import { existsSync as existsSync31, readFileSync as
|
|
30194
|
+
import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as writeFileSync12, statSync as statSync10 } from "fs";
|
|
29719
30195
|
import { resolve as resolve18, join as join34, basename as basename2 } from "path";
|
|
29720
30196
|
function resolveDistDir() {
|
|
29721
30197
|
const builtPath = resolve18(__dirname, "studio");
|
|
@@ -29889,7 +30365,7 @@ function createStudioServer(options) {
|
|
|
29889
30365
|
});
|
|
29890
30366
|
app.get("/api/runtime.js", (c2) => {
|
|
29891
30367
|
if (!existsSync31(runtimePath)) return c2.text("runtime not built", 404);
|
|
29892
|
-
return c2.body(
|
|
30368
|
+
return c2.body(readFileSync23(runtimePath, "utf-8"), 200, {
|
|
29893
30369
|
"Content-Type": "text/javascript",
|
|
29894
30370
|
"Cache-Control": "no-store"
|
|
29895
30371
|
});
|
|
@@ -29922,7 +30398,7 @@ function createStudioServer(options) {
|
|
|
29922
30398
|
app.get("/assets/*", (c2) => {
|
|
29923
30399
|
const filePath = resolve18(studioDir, c2.req.path.slice(1));
|
|
29924
30400
|
if (!existsSync31(filePath) || !statSync10(filePath).isFile()) return c2.text("not found", 404);
|
|
29925
|
-
const content =
|
|
30401
|
+
const content = readFileSync23(filePath);
|
|
29926
30402
|
return new Response(content, {
|
|
29927
30403
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
29928
30404
|
});
|
|
@@ -29930,7 +30406,7 @@ function createStudioServer(options) {
|
|
|
29930
30406
|
app.get("/icons/*", (c2) => {
|
|
29931
30407
|
const filePath = resolve18(studioDir, c2.req.path.slice(1));
|
|
29932
30408
|
if (!existsSync31(filePath) || !statSync10(filePath).isFile()) return c2.text("not found", 404);
|
|
29933
|
-
const content =
|
|
30409
|
+
const content = readFileSync23(filePath);
|
|
29934
30410
|
return new Response(content, {
|
|
29935
30411
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
29936
30412
|
});
|
|
@@ -29940,7 +30416,7 @@ function createStudioServer(options) {
|
|
|
29940
30416
|
if (!existsSync31(indexPath)) {
|
|
29941
30417
|
return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
|
|
29942
30418
|
}
|
|
29943
|
-
return c2.html(
|
|
30419
|
+
return c2.html(readFileSync23(indexPath, "utf-8"));
|
|
29944
30420
|
});
|
|
29945
30421
|
return { app, watcher };
|
|
29946
30422
|
}
|
|
@@ -30276,7 +30752,7 @@ import {
|
|
|
30276
30752
|
copyFileSync as copyFileSync3,
|
|
30277
30753
|
cpSync,
|
|
30278
30754
|
writeFileSync as writeFileSync13,
|
|
30279
|
-
readFileSync as
|
|
30755
|
+
readFileSync as readFileSync24,
|
|
30280
30756
|
readdirSync as readdirSync12
|
|
30281
30757
|
} from "fs";
|
|
30282
30758
|
import { resolve as resolve20, basename as basename4, join as join36, dirname as dirname13 } from "path";
|
|
@@ -30362,7 +30838,7 @@ function getSharedTemplateDir() {
|
|
|
30362
30838
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
30363
30839
|
const htmlFiles = readdirSync12(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join36(e2.parentPath ?? e2.path, e2.name));
|
|
30364
30840
|
for (const file of htmlFiles) {
|
|
30365
|
-
let content =
|
|
30841
|
+
let content = readFileSync24(file, "utf-8");
|
|
30366
30842
|
if (videoFilename) {
|
|
30367
30843
|
content = content.replaceAll("__VIDEO_SRC__", videoFilename);
|
|
30368
30844
|
} else {
|
|
@@ -31311,7 +31787,7 @@ __export(play_exports, {
|
|
|
31311
31787
|
default: () => play_default,
|
|
31312
31788
|
examples: () => examples5
|
|
31313
31789
|
});
|
|
31314
|
-
import { existsSync as existsSync36, readFileSync as
|
|
31790
|
+
import { existsSync as existsSync36, readFileSync as readFileSync25 } from "fs";
|
|
31315
31791
|
import { resolve as resolve24, dirname as dirname14 } from "path";
|
|
31316
31792
|
function commandDir() {
|
|
31317
31793
|
return dirname14(new URL(import.meta.url).pathname);
|
|
@@ -31427,13 +31903,13 @@ var init_play = __esm({
|
|
|
31427
31903
|
const { createAdaptorServer } = await import("@hono/node-server");
|
|
31428
31904
|
const app = new Hono6();
|
|
31429
31905
|
app.get("/player.js", (ctx) => {
|
|
31430
|
-
return ctx.body(
|
|
31906
|
+
return ctx.body(readFileSync25(playerPath, "utf-8"), 200, {
|
|
31431
31907
|
"Content-Type": "application/javascript",
|
|
31432
31908
|
"Cache-Control": "no-cache"
|
|
31433
31909
|
});
|
|
31434
31910
|
});
|
|
31435
31911
|
app.get("/runtime.js", (ctx) => {
|
|
31436
|
-
return ctx.body(
|
|
31912
|
+
return ctx.body(readFileSync25(runtimePath, "utf-8"), 200, {
|
|
31437
31913
|
"Content-Type": "application/javascript",
|
|
31438
31914
|
"Cache-Control": "no-cache"
|
|
31439
31915
|
});
|
|
@@ -31443,7 +31919,7 @@ var init_play = __esm({
|
|
|
31443
31919
|
const filePath = resolve24(project.dir, reqPath);
|
|
31444
31920
|
if (!filePath.startsWith(project.dir)) return ctx.text("Forbidden", 403);
|
|
31445
31921
|
if (!existsSync36(filePath)) return ctx.text("Not found", 404);
|
|
31446
|
-
const content =
|
|
31922
|
+
const content = readFileSync25(filePath, "utf-8");
|
|
31447
31923
|
if (filePath.endsWith(".html")) {
|
|
31448
31924
|
const injected = injectRuntime(content);
|
|
31449
31925
|
return ctx.html(injected);
|
|
@@ -31462,7 +31938,7 @@ var init_play = __esm({
|
|
|
31462
31938
|
mp3: "audio/mpeg",
|
|
31463
31939
|
wav: "audio/wav"
|
|
31464
31940
|
};
|
|
31465
|
-
return ctx.body(
|
|
31941
|
+
return ctx.body(readFileSync25(filePath), 200, {
|
|
31466
31942
|
"Content-Type": types3[ext] ?? "application/octet-stream"
|
|
31467
31943
|
});
|
|
31468
31944
|
});
|
|
@@ -31549,6 +32025,46 @@ var init_progress = __esm({
|
|
|
31549
32025
|
}
|
|
31550
32026
|
});
|
|
31551
32027
|
|
|
32028
|
+
// src/utils/dockerRunArgs.ts
|
|
32029
|
+
function buildDockerRunArgs(input) {
|
|
32030
|
+
const { imageTag, projectDir, outputDir, outputFilename, options } = input;
|
|
32031
|
+
return [
|
|
32032
|
+
"run",
|
|
32033
|
+
"--rm",
|
|
32034
|
+
"--platform",
|
|
32035
|
+
"linux/amd64",
|
|
32036
|
+
"--shm-size=2g",
|
|
32037
|
+
// GPU encoding requires host GPU passthrough.
|
|
32038
|
+
...options.gpu ? ["--gpus", "all"] : [],
|
|
32039
|
+
"-v",
|
|
32040
|
+
`${projectDir}:/project:ro`,
|
|
32041
|
+
"-v",
|
|
32042
|
+
`${outputDir}:/output`,
|
|
32043
|
+
imageTag,
|
|
32044
|
+
"/project",
|
|
32045
|
+
"--output",
|
|
32046
|
+
`/output/${outputFilename}`,
|
|
32047
|
+
"--fps",
|
|
32048
|
+
String(options.fps),
|
|
32049
|
+
"--quality",
|
|
32050
|
+
options.quality,
|
|
32051
|
+
"--format",
|
|
32052
|
+
options.format,
|
|
32053
|
+
"--workers",
|
|
32054
|
+
String(options.workers),
|
|
32055
|
+
...options.crf != null ? ["--crf", String(options.crf)] : [],
|
|
32056
|
+
...options.videoBitrate ? ["--video-bitrate", options.videoBitrate] : [],
|
|
32057
|
+
...options.quiet ? ["--quiet"] : [],
|
|
32058
|
+
...options.gpu ? ["--gpu"] : [],
|
|
32059
|
+
...options.hdr ? ["--hdr"] : []
|
|
32060
|
+
];
|
|
32061
|
+
}
|
|
32062
|
+
var init_dockerRunArgs = __esm({
|
|
32063
|
+
"src/utils/dockerRunArgs.ts"() {
|
|
32064
|
+
"use strict";
|
|
32065
|
+
}
|
|
32066
|
+
});
|
|
32067
|
+
|
|
31552
32068
|
// src/browser/ffmpeg.ts
|
|
31553
32069
|
var ffmpeg_exports = {};
|
|
31554
32070
|
__export(ffmpeg_exports, {
|
|
@@ -31592,7 +32108,7 @@ __export(render_exports, {
|
|
|
31592
32108
|
default: () => render_default,
|
|
31593
32109
|
examples: () => examples6
|
|
31594
32110
|
});
|
|
31595
|
-
import { mkdirSync as mkdirSync21, readFileSync as
|
|
32111
|
+
import { mkdirSync as mkdirSync21, readFileSync as readFileSync26, statSync as statSync12, writeFileSync as writeFileSync14, rmSync as rmSync8 } from "fs";
|
|
31596
32112
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir3 } from "os";
|
|
31597
32113
|
import { resolve as resolve25, dirname as dirname15, join as join37, basename as basename6 } from "path";
|
|
31598
32114
|
import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
|
|
@@ -31633,7 +32149,7 @@ function ensureDockerImage(version, quiet) {
|
|
|
31633
32149
|
const dockerfilePath = resolveDockerfilePath();
|
|
31634
32150
|
const tmpDir = join37(tmpdir3(), `hyperframes-docker-${Date.now()}`);
|
|
31635
32151
|
mkdirSync21(tmpDir, { recursive: true });
|
|
31636
|
-
writeFileSync14(join37(tmpDir, "Dockerfile"),
|
|
32152
|
+
writeFileSync14(join37(tmpDir, "Dockerfile"), readFileSync26(dockerfilePath));
|
|
31637
32153
|
try {
|
|
31638
32154
|
execFileSync5(
|
|
31639
32155
|
"docker",
|
|
@@ -31679,33 +32195,23 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
31679
32195
|
}
|
|
31680
32196
|
const outputDir = dirname15(outputPath);
|
|
31681
32197
|
const outputFilename = basename6(outputPath);
|
|
31682
|
-
const dockerArgs =
|
|
31683
|
-
"run",
|
|
31684
|
-
"--rm",
|
|
31685
|
-
"--platform",
|
|
31686
|
-
"linux/amd64",
|
|
31687
|
-
"--shm-size=2g",
|
|
31688
|
-
// GPU encoding requires host GPU passthrough
|
|
31689
|
-
...options.gpu ? ["--gpus", "all"] : [],
|
|
31690
|
-
"-v",
|
|
31691
|
-
`${resolve25(projectDir)}:/project:ro`,
|
|
31692
|
-
"-v",
|
|
31693
|
-
`${resolve25(outputDir)}:/output`,
|
|
32198
|
+
const dockerArgs = buildDockerRunArgs({
|
|
31694
32199
|
imageTag,
|
|
31695
|
-
|
|
31696
|
-
|
|
31697
|
-
|
|
31698
|
-
|
|
31699
|
-
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31703
|
-
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
32200
|
+
projectDir: resolve25(projectDir),
|
|
32201
|
+
outputDir: resolve25(outputDir),
|
|
32202
|
+
outputFilename,
|
|
32203
|
+
options: {
|
|
32204
|
+
fps: options.fps,
|
|
32205
|
+
quality: options.quality,
|
|
32206
|
+
format: options.format,
|
|
32207
|
+
workers: options.workers,
|
|
32208
|
+
gpu: options.gpu,
|
|
32209
|
+
hdr: options.hdr,
|
|
32210
|
+
crf: options.crf,
|
|
32211
|
+
videoBitrate: options.videoBitrate,
|
|
32212
|
+
quiet: options.quiet
|
|
32213
|
+
}
|
|
32214
|
+
});
|
|
31709
32215
|
if (!options.quiet) {
|
|
31710
32216
|
console.log(c.dim(" Running render in Docker container..."));
|
|
31711
32217
|
console.log("");
|
|
@@ -31749,7 +32255,9 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
31749
32255
|
format: options.format,
|
|
31750
32256
|
workers: options.workers,
|
|
31751
32257
|
useGpu: options.gpu,
|
|
31752
|
-
hdr: options.hdr
|
|
32258
|
+
hdr: options.hdr,
|
|
32259
|
+
crf: options.crf,
|
|
32260
|
+
videoBitrate: options.videoBitrate
|
|
31753
32261
|
});
|
|
31754
32262
|
const onProgress = options.quiet ? void 0 : (progressJob, message) => {
|
|
31755
32263
|
renderProgress(progressJob.progress, message);
|
|
@@ -31833,6 +32341,7 @@ var init_render2 = __esm({
|
|
|
31833
32341
|
init_system();
|
|
31834
32342
|
init_version();
|
|
31835
32343
|
init_env();
|
|
32344
|
+
init_dockerRunArgs();
|
|
31836
32345
|
examples6 = [
|
|
31837
32346
|
["Render to MP4", "hyperframes render --output output.mp4"],
|
|
31838
32347
|
["Render transparent overlay (ProRes)", "hyperframes render --format mov --output overlay.mov"],
|
|
@@ -31860,15 +32369,18 @@ var init_render2 = __esm({
|
|
|
31860
32369
|
},
|
|
31861
32370
|
output: {
|
|
31862
32371
|
type: "string",
|
|
32372
|
+
alias: "o",
|
|
31863
32373
|
description: "Output path (default: renders/<name>.mp4)"
|
|
31864
32374
|
},
|
|
31865
32375
|
fps: {
|
|
31866
32376
|
type: "string",
|
|
32377
|
+
alias: "f",
|
|
31867
32378
|
description: "Frame rate: 24, 30, 60",
|
|
31868
32379
|
default: "30"
|
|
31869
32380
|
},
|
|
31870
32381
|
quality: {
|
|
31871
32382
|
type: "string",
|
|
32383
|
+
alias: "q",
|
|
31872
32384
|
description: "Quality: draft, standard, high",
|
|
31873
32385
|
default: "standard"
|
|
31874
32386
|
},
|
|
@@ -31879,6 +32391,7 @@ var init_render2 = __esm({
|
|
|
31879
32391
|
},
|
|
31880
32392
|
workers: {
|
|
31881
32393
|
type: "string",
|
|
32394
|
+
alias: "w",
|
|
31882
32395
|
description: "Parallel render workers (number or 'auto'). Default: auto. Each worker launches a separate Chrome process (~256 MB RAM)."
|
|
31883
32396
|
},
|
|
31884
32397
|
docker: {
|
|
@@ -31891,6 +32404,14 @@ var init_render2 = __esm({
|
|
|
31891
32404
|
description: "Enable HDR: probe sources for PQ/HLG, output H.265 10-bit BT.2020",
|
|
31892
32405
|
default: false
|
|
31893
32406
|
},
|
|
32407
|
+
crf: {
|
|
32408
|
+
type: "string",
|
|
32409
|
+
description: "Override encoder CRF. Mutually exclusive with --video-bitrate."
|
|
32410
|
+
},
|
|
32411
|
+
"video-bitrate": {
|
|
32412
|
+
type: "string",
|
|
32413
|
+
description: "Target video bitrate such as 10M. Mutually exclusive with --crf."
|
|
32414
|
+
},
|
|
31894
32415
|
gpu: { type: "boolean", description: "Use GPU encoding", default: false },
|
|
31895
32416
|
quiet: {
|
|
31896
32417
|
type: "boolean",
|
|
@@ -31964,6 +32485,28 @@ var init_render2 = __esm({
|
|
|
31964
32485
|
const quiet = args.quiet ?? false;
|
|
31965
32486
|
const strictAll = args["strict-all"] ?? false;
|
|
31966
32487
|
const strictErrors = (args.strict ?? false) || strictAll;
|
|
32488
|
+
const crfRaw = args.crf;
|
|
32489
|
+
const videoBitrate = args["video-bitrate"]?.trim();
|
|
32490
|
+
if (crfRaw != null && videoBitrate) {
|
|
32491
|
+
errorBox("Conflicting encoder settings", "Use either --crf or --video-bitrate, not both.");
|
|
32492
|
+
process.exit(1);
|
|
32493
|
+
}
|
|
32494
|
+
let crf;
|
|
32495
|
+
if (crfRaw != null) {
|
|
32496
|
+
const parsed = Number(crfRaw);
|
|
32497
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
32498
|
+
errorBox("Invalid crf", `Got "${crfRaw}". Must be a non-negative integer.`);
|
|
32499
|
+
process.exit(1);
|
|
32500
|
+
}
|
|
32501
|
+
crf = parsed;
|
|
32502
|
+
}
|
|
32503
|
+
if (args["video-bitrate"] != null && !videoBitrate) {
|
|
32504
|
+
errorBox(
|
|
32505
|
+
"Invalid video-bitrate",
|
|
32506
|
+
`Got "${args["video-bitrate"]}". Must be a non-empty bitrate such as "10M".`
|
|
32507
|
+
);
|
|
32508
|
+
process.exit(1);
|
|
32509
|
+
}
|
|
31967
32510
|
const workerCount = workers ?? defaultWorkerCount();
|
|
31968
32511
|
if (!quiet) {
|
|
31969
32512
|
const workerLabel = args.workers != null ? `${workerCount} workers` : `${workerCount} workers (auto \u2014 ${CPU_CORE_COUNT} cores detected)`;
|
|
@@ -32042,6 +32585,8 @@ var init_render2 = __esm({
|
|
|
32042
32585
|
workers: workerCount,
|
|
32043
32586
|
gpu: useGpu,
|
|
32044
32587
|
hdr: args.hdr ?? false,
|
|
32588
|
+
crf,
|
|
32589
|
+
videoBitrate,
|
|
32045
32590
|
quiet
|
|
32046
32591
|
});
|
|
32047
32592
|
} else {
|
|
@@ -32052,6 +32597,8 @@ var init_render2 = __esm({
|
|
|
32052
32597
|
workers: workerCount,
|
|
32053
32598
|
gpu: useGpu,
|
|
32054
32599
|
hdr: args.hdr ?? false,
|
|
32600
|
+
crf,
|
|
32601
|
+
videoBitrate,
|
|
32055
32602
|
quiet,
|
|
32056
32603
|
browserPath
|
|
32057
32604
|
});
|
|
@@ -32279,7 +32826,7 @@ __export(info_exports, {
|
|
|
32279
32826
|
default: () => info_default,
|
|
32280
32827
|
examples: () => examples8
|
|
32281
32828
|
});
|
|
32282
|
-
import { readFileSync as
|
|
32829
|
+
import { readFileSync as readFileSync27, readdirSync as readdirSync13, statSync as statSync13 } from "fs";
|
|
32283
32830
|
import { join as join38 } from "path";
|
|
32284
32831
|
function totalSize(dir) {
|
|
32285
32832
|
let total = 0;
|
|
@@ -32316,7 +32863,7 @@ var init_info = __esm({
|
|
|
32316
32863
|
},
|
|
32317
32864
|
async run({ args }) {
|
|
32318
32865
|
const project = resolveProject(args.dir);
|
|
32319
|
-
const html =
|
|
32866
|
+
const html = readFileSync27(project.indexPath, "utf-8");
|
|
32320
32867
|
ensureDOMParser();
|
|
32321
32868
|
const parsed = parseHtml(html);
|
|
32322
32869
|
const tracks = new Set(parsed.elements.map((el) => el.zIndex));
|
|
@@ -32372,7 +32919,7 @@ __export(compositions_exports, {
|
|
|
32372
32919
|
default: () => compositions_default,
|
|
32373
32920
|
examples: () => examples9
|
|
32374
32921
|
});
|
|
32375
|
-
import { existsSync as existsSync37, readFileSync as
|
|
32922
|
+
import { existsSync as existsSync37, readFileSync as readFileSync28 } from "fs";
|
|
32376
32923
|
import { resolve as resolve26, dirname as dirname16 } from "path";
|
|
32377
32924
|
function parseCompositions(html, baseDir) {
|
|
32378
32925
|
const parser = new DOMParser();
|
|
@@ -32387,7 +32934,7 @@ function parseCompositions(html, baseDir) {
|
|
|
32387
32934
|
if (compositionSrc) {
|
|
32388
32935
|
const subPath = resolve26(baseDir, compositionSrc);
|
|
32389
32936
|
if (existsSync37(subPath)) {
|
|
32390
|
-
const subHtml =
|
|
32937
|
+
const subHtml = readFileSync28(subPath, "utf-8");
|
|
32391
32938
|
const subInfo = parseSubComposition(subHtml, id, width, height);
|
|
32392
32939
|
compositions.push({ ...subInfo, source: compositionSrc });
|
|
32393
32940
|
return;
|
|
@@ -32481,7 +33028,7 @@ var init_compositions = __esm({
|
|
|
32481
33028
|
},
|
|
32482
33029
|
async run({ args }) {
|
|
32483
33030
|
const project = resolveProject(args.dir);
|
|
32484
|
-
const html =
|
|
33031
|
+
const html = readFileSync28(project.indexPath, "utf-8");
|
|
32485
33032
|
ensureDOMParser();
|
|
32486
33033
|
const compositions = parseCompositions(html, dirname16(project.indexPath));
|
|
32487
33034
|
if (compositions.length === 0) {
|
|
@@ -32826,7 +33373,7 @@ __export(transcribe_exports2, {
|
|
|
32826
33373
|
examples: () => examples12
|
|
32827
33374
|
});
|
|
32828
33375
|
import { existsSync as existsSync39, writeFileSync as writeFileSync15 } from "fs";
|
|
32829
|
-
import { resolve as resolve28, join as join40, extname as
|
|
33376
|
+
import { resolve as resolve28, join as join40, extname as extname8 } from "path";
|
|
32830
33377
|
async function importTranscript(inputPath, dir, json) {
|
|
32831
33378
|
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
32832
33379
|
const { words, format } = loadTranscript2(inputPath);
|
|
@@ -32956,7 +33503,7 @@ var init_transcribe2 = __esm({
|
|
|
32956
33503
|
process.exit(1);
|
|
32957
33504
|
}
|
|
32958
33505
|
const dir = resolve28(args.dir ?? ".");
|
|
32959
|
-
const ext =
|
|
33506
|
+
const ext = extname8(inputPath).toLowerCase();
|
|
32960
33507
|
const isImport = ext === ".json" || ext === ".srt" || ext === ".vtt";
|
|
32961
33508
|
if (isImport) {
|
|
32962
33509
|
return importTranscript(inputPath, dir, args.json);
|
|
@@ -32975,6 +33522,13 @@ var init_transcribe2 = __esm({
|
|
|
32975
33522
|
import { existsSync as existsSync40, mkdirSync as mkdirSync22 } from "fs";
|
|
32976
33523
|
import { homedir as homedir8 } from "os";
|
|
32977
33524
|
import { join as join41 } from "path";
|
|
33525
|
+
function inferLangFromVoiceId(voiceId) {
|
|
33526
|
+
const first = voiceId.charAt(0).toLowerCase();
|
|
33527
|
+
return VOICE_PREFIX_LANG[first] ?? "en-us";
|
|
33528
|
+
}
|
|
33529
|
+
function isSupportedLang(value) {
|
|
33530
|
+
return SUPPORTED_LANGS.includes(value);
|
|
33531
|
+
}
|
|
32978
33532
|
async function ensureModel2(model = DEFAULT_MODEL2, options) {
|
|
32979
33533
|
const modelPath = join41(MODELS_DIR2, `${model}.onnx`);
|
|
32980
33534
|
if (existsSync40(modelPath)) return modelPath;
|
|
@@ -33003,7 +33557,7 @@ async function ensureVoices(options) {
|
|
|
33003
33557
|
}
|
|
33004
33558
|
return voicesPath;
|
|
33005
33559
|
}
|
|
33006
|
-
var CACHE_DIR3, MODELS_DIR2, VOICES_DIR, DEFAULT_MODEL2, MODEL_URLS, VOICES_URL, BUNDLED_VOICES, DEFAULT_VOICE;
|
|
33560
|
+
var CACHE_DIR3, MODELS_DIR2, VOICES_DIR, DEFAULT_MODEL2, MODEL_URLS, VOICES_URL, SUPPORTED_LANGS, VOICE_PREFIX_LANG, BUNDLED_VOICES, DEFAULT_VOICE;
|
|
33007
33561
|
var init_manager3 = __esm({
|
|
33008
33562
|
"src/tts/manager.ts"() {
|
|
33009
33563
|
"use strict";
|
|
@@ -33016,6 +33570,37 @@ var init_manager3 = __esm({
|
|
|
33016
33570
|
"kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
|
|
33017
33571
|
};
|
|
33018
33572
|
VOICES_URL = "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin";
|
|
33573
|
+
SUPPORTED_LANGS = [
|
|
33574
|
+
"en-us",
|
|
33575
|
+
"en-gb",
|
|
33576
|
+
"es",
|
|
33577
|
+
"fr-fr",
|
|
33578
|
+
"hi",
|
|
33579
|
+
"it",
|
|
33580
|
+
"pt-br",
|
|
33581
|
+
"ja",
|
|
33582
|
+
"zh"
|
|
33583
|
+
];
|
|
33584
|
+
VOICE_PREFIX_LANG = {
|
|
33585
|
+
a: "en-us",
|
|
33586
|
+
// American English
|
|
33587
|
+
b: "en-gb",
|
|
33588
|
+
// British English
|
|
33589
|
+
e: "es",
|
|
33590
|
+
// Spanish
|
|
33591
|
+
f: "fr-fr",
|
|
33592
|
+
// French
|
|
33593
|
+
h: "hi",
|
|
33594
|
+
// Hindi
|
|
33595
|
+
i: "it",
|
|
33596
|
+
// Italian
|
|
33597
|
+
j: "ja",
|
|
33598
|
+
// Japanese
|
|
33599
|
+
p: "pt-br",
|
|
33600
|
+
// Brazilian Portuguese
|
|
33601
|
+
z: "zh"
|
|
33602
|
+
// Mandarin
|
|
33603
|
+
};
|
|
33019
33604
|
BUNDLED_VOICES = [
|
|
33020
33605
|
{ id: "af_heart", label: "Heart", language: "en-US", gender: "female" },
|
|
33021
33606
|
{ id: "af_nova", label: "Nova", language: "en-US", gender: "female" },
|
|
@@ -33024,7 +33609,11 @@ var init_manager3 = __esm({
|
|
|
33024
33609
|
{ id: "am_michael", label: "Michael", language: "en-US", gender: "male" },
|
|
33025
33610
|
{ id: "bf_emma", label: "Emma", language: "en-GB", gender: "female" },
|
|
33026
33611
|
{ id: "bf_isabella", label: "Isabella", language: "en-GB", gender: "female" },
|
|
33027
|
-
{ id: "bm_george", label: "George", language: "en-GB", gender: "male" }
|
|
33612
|
+
{ id: "bm_george", label: "George", language: "en-GB", gender: "male" },
|
|
33613
|
+
{ id: "ef_dora", label: "Dora", language: "es", gender: "female" },
|
|
33614
|
+
{ id: "ff_siwis", label: "Siwis", language: "fr-FR", gender: "female" },
|
|
33615
|
+
{ id: "jf_alpha", label: "Alpha", language: "ja", gender: "female" },
|
|
33616
|
+
{ id: "zf_xiaobei", label: "Xiaobei", language: "zh", gender: "female" }
|
|
33028
33617
|
];
|
|
33029
33618
|
DEFAULT_VOICE = "af_heart";
|
|
33030
33619
|
}
|
|
@@ -33036,8 +33625,8 @@ __export(synthesize_exports, {
|
|
|
33036
33625
|
synthesize: () => synthesize
|
|
33037
33626
|
});
|
|
33038
33627
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
33039
|
-
import { existsSync as existsSync41, writeFileSync as writeFileSync16, mkdirSync as mkdirSync23 } from "fs";
|
|
33040
|
-
import { join as join42, dirname as dirname17 } from "path";
|
|
33628
|
+
import { existsSync as existsSync41, writeFileSync as writeFileSync16, mkdirSync as mkdirSync23, readdirSync as readdirSync14, unlinkSync as unlinkSync6 } from "fs";
|
|
33629
|
+
import { join as join42, dirname as dirname17, basename as basename7 } from "path";
|
|
33041
33630
|
import { homedir as homedir9 } from "os";
|
|
33042
33631
|
function findPython() {
|
|
33043
33632
|
for (const name of ["python3", "python"]) {
|
|
@@ -33076,12 +33665,25 @@ function ensureSynthScript() {
|
|
|
33076
33665
|
if (!existsSync41(SCRIPT_PATH)) {
|
|
33077
33666
|
mkdirSync23(SCRIPT_DIR, { recursive: true });
|
|
33078
33667
|
writeFileSync16(SCRIPT_PATH, SYNTH_SCRIPT);
|
|
33668
|
+
const currentName = basename7(SCRIPT_PATH);
|
|
33669
|
+
try {
|
|
33670
|
+
for (const entry of readdirSync14(SCRIPT_DIR)) {
|
|
33671
|
+
if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
|
|
33672
|
+
try {
|
|
33673
|
+
unlinkSync6(join42(SCRIPT_DIR, entry));
|
|
33674
|
+
} catch {
|
|
33675
|
+
}
|
|
33676
|
+
}
|
|
33677
|
+
}
|
|
33678
|
+
} catch {
|
|
33679
|
+
}
|
|
33079
33680
|
}
|
|
33080
33681
|
return SCRIPT_PATH;
|
|
33081
33682
|
}
|
|
33082
33683
|
async function synthesize(text, outputPath, options) {
|
|
33083
33684
|
const voice = options?.voice ?? DEFAULT_VOICE;
|
|
33084
33685
|
const speed = options?.speed ?? 1;
|
|
33686
|
+
const lang = options?.lang ?? inferLangFromVoiceId(voice);
|
|
33085
33687
|
options?.onProgress?.("Checking Python runtime...");
|
|
33086
33688
|
const python = findPython();
|
|
33087
33689
|
if (!python) {
|
|
@@ -33103,11 +33705,11 @@ async function synthesize(text, outputPath, options) {
|
|
|
33103
33705
|
]);
|
|
33104
33706
|
const scriptPath = ensureSynthScript();
|
|
33105
33707
|
mkdirSync23(dirname17(outputPath), { recursive: true });
|
|
33106
|
-
options?.onProgress?.(`Generating speech with voice ${voice}...`);
|
|
33708
|
+
options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
|
|
33107
33709
|
try {
|
|
33108
33710
|
const stdout2 = execFileSync6(
|
|
33109
33711
|
python,
|
|
33110
|
-
[scriptPath, modelPath, voicesPath, text, voice, String(speed), outputPath],
|
|
33712
|
+
[scriptPath, modelPath, voicesPath, text, voice, String(speed), outputPath, lang],
|
|
33111
33713
|
{
|
|
33112
33714
|
encoding: "utf-8",
|
|
33113
33715
|
timeout: 3e5,
|
|
@@ -33123,7 +33725,8 @@ async function synthesize(text, outputPath, options) {
|
|
|
33123
33725
|
return {
|
|
33124
33726
|
outputPath: result.outputPath,
|
|
33125
33727
|
sampleRate: result.sampleRate,
|
|
33126
|
-
durationSeconds: result.durationSeconds
|
|
33728
|
+
durationSeconds: result.durationSeconds,
|
|
33729
|
+
langApplied: result.langApplied
|
|
33127
33730
|
};
|
|
33128
33731
|
} catch (err) {
|
|
33129
33732
|
if (err instanceof SyntaxError && existsSync41(outputPath)) {
|
|
@@ -33146,7 +33749,7 @@ var init_synthesize = __esm({
|
|
|
33146
33749
|
"use strict";
|
|
33147
33750
|
init_manager3();
|
|
33148
33751
|
SYNTH_SCRIPT = `
|
|
33149
|
-
import sys, json
|
|
33752
|
+
import sys, json, inspect
|
|
33150
33753
|
|
|
33151
33754
|
model_path = sys.argv[1]
|
|
33152
33755
|
voices_path = sys.argv[2]
|
|
@@ -33154,12 +33757,19 @@ text = sys.argv[3]
|
|
|
33154
33757
|
voice = sys.argv[4]
|
|
33155
33758
|
speed = float(sys.argv[5])
|
|
33156
33759
|
output_path = sys.argv[6]
|
|
33760
|
+
lang = sys.argv[7] if len(sys.argv) > 7 else ""
|
|
33157
33761
|
|
|
33158
33762
|
import kokoro_onnx
|
|
33159
33763
|
import soundfile as sf
|
|
33160
33764
|
|
|
33161
33765
|
model = kokoro_onnx.Kokoro(model_path, voices_path)
|
|
33162
|
-
|
|
33766
|
+
|
|
33767
|
+
kwargs = {"voice": voice, "speed": speed}
|
|
33768
|
+
supports_lang = "lang" in inspect.signature(model.create).parameters
|
|
33769
|
+
if lang and supports_lang:
|
|
33770
|
+
kwargs["lang"] = lang
|
|
33771
|
+
|
|
33772
|
+
samples, sample_rate = model.create(text, **kwargs)
|
|
33163
33773
|
sf.write(output_path, samples, sample_rate)
|
|
33164
33774
|
|
|
33165
33775
|
duration = len(samples) / sample_rate
|
|
@@ -33167,10 +33777,11 @@ print(json.dumps({
|
|
|
33167
33777
|
"outputPath": output_path,
|
|
33168
33778
|
"sampleRate": sample_rate,
|
|
33169
33779
|
"durationSeconds": round(duration, 3),
|
|
33780
|
+
"langApplied": bool(lang and supports_lang),
|
|
33170
33781
|
}))
|
|
33171
33782
|
`;
|
|
33172
33783
|
SCRIPT_DIR = join42(homedir9(), ".cache", "hyperframes", "tts");
|
|
33173
|
-
SCRIPT_PATH = join42(SCRIPT_DIR, "synth.py");
|
|
33784
|
+
SCRIPT_PATH = join42(SCRIPT_DIR, "synth-v2.py");
|
|
33174
33785
|
}
|
|
33175
33786
|
});
|
|
33176
33787
|
|
|
@@ -33180,49 +33791,64 @@ __export(tts_exports, {
|
|
|
33180
33791
|
default: () => tts_default,
|
|
33181
33792
|
examples: () => examples13
|
|
33182
33793
|
});
|
|
33183
|
-
import { existsSync as existsSync42, readFileSync as
|
|
33184
|
-
import { resolve as resolve29, extname as
|
|
33794
|
+
import { existsSync as existsSync42, readFileSync as readFileSync29 } from "fs";
|
|
33795
|
+
import { resolve as resolve29, extname as extname9 } from "path";
|
|
33185
33796
|
function listVoices(json) {
|
|
33797
|
+
const rows = BUNDLED_VOICES.map((v) => ({ ...v, defaultLang: inferLangFromVoiceId(v.id) }));
|
|
33186
33798
|
if (json) {
|
|
33187
|
-
console.log(JSON.stringify(
|
|
33799
|
+
console.log(JSON.stringify(rows));
|
|
33188
33800
|
return;
|
|
33189
33801
|
}
|
|
33190
33802
|
console.log(`
|
|
33191
33803
|
${c.bold("Available voices")} (Kokoro-82M)
|
|
33192
33804
|
`);
|
|
33193
33805
|
console.log(
|
|
33194
|
-
` ${c.dim("ID")} ${c.dim("Name")} ${c.dim("Language")} ${c.dim("Gender")}`
|
|
33806
|
+
` ${c.dim("ID")} ${c.dim("Name")} ${c.dim("Language")} ${c.dim("Lang code")} ${c.dim("Gender")}`
|
|
33195
33807
|
);
|
|
33196
|
-
console.log(` ${c.dim("\u2500".repeat(
|
|
33197
|
-
for (const
|
|
33198
|
-
const id =
|
|
33199
|
-
const label2 =
|
|
33200
|
-
const lang =
|
|
33201
|
-
|
|
33808
|
+
console.log(` ${c.dim("\u2500".repeat(72))}`);
|
|
33809
|
+
for (const row of rows) {
|
|
33810
|
+
const id = row.id.padEnd(18);
|
|
33811
|
+
const label2 = row.label.padEnd(13);
|
|
33812
|
+
const lang = row.language.padEnd(10);
|
|
33813
|
+
const code = row.defaultLang.padEnd(10);
|
|
33814
|
+
console.log(` ${c.accent(id)} ${label2} ${lang} ${code} ${row.gender}`);
|
|
33202
33815
|
}
|
|
33203
33816
|
console.log(
|
|
33204
33817
|
`
|
|
33205
|
-
${c.dim("Use any Kokoro voice ID \u2014 see https://github.com/thewh1teagle/kokoro-onnx for all 54 voices")}
|
|
33818
|
+
${c.dim("Use any Kokoro voice ID \u2014 see https://github.com/thewh1teagle/kokoro-onnx for all 54 voices")}`
|
|
33819
|
+
);
|
|
33820
|
+
console.log(
|
|
33821
|
+
` ${c.dim("Override phonemizer with --lang <" + SUPPORTED_LANGS.join("|") + ">")}
|
|
33206
33822
|
`
|
|
33207
33823
|
);
|
|
33208
33824
|
}
|
|
33209
|
-
var examples13, voiceList, tts_default;
|
|
33825
|
+
var examples13, voiceList, langList, tts_default;
|
|
33210
33826
|
var init_tts = __esm({
|
|
33211
33827
|
"src/commands/tts.ts"() {
|
|
33212
33828
|
"use strict";
|
|
33213
33829
|
init_dist();
|
|
33214
33830
|
init_dist3();
|
|
33215
33831
|
init_colors();
|
|
33832
|
+
init_format();
|
|
33216
33833
|
init_manager3();
|
|
33217
33834
|
examples13 = [
|
|
33218
33835
|
["Generate speech from text", 'hyperframes tts "Welcome to HyperFrames"'],
|
|
33219
33836
|
["Choose a voice", 'hyperframes tts "Hello world" --voice am_adam'],
|
|
33220
33837
|
["Save to a specific file", 'hyperframes tts "Intro" --voice bf_emma --output narration.wav'],
|
|
33221
33838
|
["Adjust speech speed", 'hyperframes tts "Slow and clear" --speed 0.8'],
|
|
33839
|
+
[
|
|
33840
|
+
"Generate Spanish speech",
|
|
33841
|
+
'hyperframes tts "La reuni\xF3n empieza a las nueve" --voice ef_dora --output es.wav'
|
|
33842
|
+
],
|
|
33843
|
+
[
|
|
33844
|
+
"Override phonemizer language",
|
|
33845
|
+
'hyperframes tts "Ciao a tutti" --voice af_heart --lang it --output accented.wav'
|
|
33846
|
+
],
|
|
33222
33847
|
["Read text from a file", "hyperframes tts script.txt"],
|
|
33223
33848
|
["List available voices", "hyperframes tts --list"]
|
|
33224
33849
|
];
|
|
33225
33850
|
voiceList = BUNDLED_VOICES.map((v) => `${v.id} (${v.label})`).join(", ");
|
|
33851
|
+
langList = SUPPORTED_LANGS.join(", ");
|
|
33226
33852
|
tts_default = defineCommand({
|
|
33227
33853
|
meta: {
|
|
33228
33854
|
name: "tts",
|
|
@@ -33249,6 +33875,11 @@ var init_tts = __esm({
|
|
|
33249
33875
|
description: "Speech speed multiplier (default: 1.0)",
|
|
33250
33876
|
alias: "s"
|
|
33251
33877
|
},
|
|
33878
|
+
lang: {
|
|
33879
|
+
type: "string",
|
|
33880
|
+
description: `Phonemizer language (auto-detected from voice prefix when omitted). Options: ${langList}`,
|
|
33881
|
+
alias: "l"
|
|
33882
|
+
},
|
|
33252
33883
|
list: {
|
|
33253
33884
|
type: "boolean",
|
|
33254
33885
|
description: "List available voices and exit",
|
|
@@ -33270,8 +33901,8 @@ var init_tts = __esm({
|
|
|
33270
33901
|
}
|
|
33271
33902
|
let text;
|
|
33272
33903
|
const maybeFile = resolve29(args.input);
|
|
33273
|
-
if (existsSync42(maybeFile) &&
|
|
33274
|
-
text =
|
|
33904
|
+
if (existsSync42(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
|
|
33905
|
+
text = readFileSync29(maybeFile, "utf-8").trim();
|
|
33275
33906
|
if (!text) {
|
|
33276
33907
|
console.error(c.error("File is empty."));
|
|
33277
33908
|
process.exit(1);
|
|
@@ -33290,13 +33921,31 @@ var init_tts = __esm({
|
|
|
33290
33921
|
console.error(c.error("Speed must be a number between 0.1 and 3.0"));
|
|
33291
33922
|
process.exit(1);
|
|
33292
33923
|
}
|
|
33924
|
+
const inferredLang = inferLangFromVoiceId(voice);
|
|
33925
|
+
let lang = inferredLang;
|
|
33926
|
+
if (args.lang != null) {
|
|
33927
|
+
const requested = String(args.lang).toLowerCase();
|
|
33928
|
+
if (!isSupportedLang(requested)) {
|
|
33929
|
+
errorBox("Invalid --lang", `Got "${args.lang}". Must be one of: ${langList}.`);
|
|
33930
|
+
process.exit(1);
|
|
33931
|
+
}
|
|
33932
|
+
lang = requested;
|
|
33933
|
+
}
|
|
33934
|
+
if (!args.json && args.lang != null && lang !== inferredLang) {
|
|
33935
|
+
console.log(
|
|
33936
|
+
c.dim(
|
|
33937
|
+
` Note: voice "${voice}" is ${inferredLang}, rendering with --lang ${lang} instead.`
|
|
33938
|
+
)
|
|
33939
|
+
);
|
|
33940
|
+
}
|
|
33293
33941
|
const { synthesize: synthesize2 } = await Promise.resolve().then(() => (init_synthesize(), synthesize_exports));
|
|
33294
33942
|
const spin = args.json ? null : be();
|
|
33295
|
-
spin?.start(`Generating speech with ${c.accent(voice)}...`);
|
|
33943
|
+
spin?.start(`Generating speech with ${c.accent(voice)} (${lang})...`);
|
|
33296
33944
|
try {
|
|
33297
33945
|
const result = await synthesize2(text, output, {
|
|
33298
33946
|
voice,
|
|
33299
33947
|
speed,
|
|
33948
|
+
lang,
|
|
33300
33949
|
onProgress: spin ? (msg) => spin.message(msg) : void 0
|
|
33301
33950
|
});
|
|
33302
33951
|
if (args.json) {
|
|
@@ -33305,6 +33954,8 @@ var init_tts = __esm({
|
|
|
33305
33954
|
ok: true,
|
|
33306
33955
|
voice,
|
|
33307
33956
|
speed,
|
|
33957
|
+
lang,
|
|
33958
|
+
langApplied: result.langApplied,
|
|
33308
33959
|
durationSeconds: result.durationSeconds,
|
|
33309
33960
|
outputPath: result.outputPath
|
|
33310
33961
|
})
|
|
@@ -33315,6 +33966,13 @@ var init_tts = __esm({
|
|
|
33315
33966
|
`Generated ${c.accent(result.durationSeconds.toFixed(1) + "s")} of speech \u2192 ${c.accent(result.outputPath)}`
|
|
33316
33967
|
)
|
|
33317
33968
|
);
|
|
33969
|
+
if (args.lang != null && !result.langApplied) {
|
|
33970
|
+
console.log(
|
|
33971
|
+
c.dim(
|
|
33972
|
+
" Note: installed kokoro-onnx version does not support the --lang kwarg; phonemization used Kokoro's default."
|
|
33973
|
+
)
|
|
33974
|
+
);
|
|
33975
|
+
}
|
|
33318
33976
|
}
|
|
33319
33977
|
} catch (err) {
|
|
33320
33978
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -33336,7 +33994,7 @@ __export(docs_exports, {
|
|
|
33336
33994
|
default: () => docs_default,
|
|
33337
33995
|
examples: () => examples14
|
|
33338
33996
|
});
|
|
33339
|
-
import { readFileSync as
|
|
33997
|
+
import { readFileSync as readFileSync30, existsSync as existsSync43 } from "fs";
|
|
33340
33998
|
import { resolve as resolve30, dirname as dirname18, join as join43 } from "path";
|
|
33341
33999
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
33342
34000
|
function docsDir() {
|
|
@@ -33446,7 +34104,7 @@ var init_docs = __esm({
|
|
|
33446
34104
|
console.error(c.error(`Doc file not found: ${filePath}`));
|
|
33447
34105
|
process.exit(1);
|
|
33448
34106
|
}
|
|
33449
|
-
const content =
|
|
34107
|
+
const content = readFileSync30(filePath, "utf-8");
|
|
33450
34108
|
console.log();
|
|
33451
34109
|
renderMarkdown(content);
|
|
33452
34110
|
}
|
|
@@ -33872,7 +34530,7 @@ var validate_exports = {};
|
|
|
33872
34530
|
__export(validate_exports, {
|
|
33873
34531
|
default: () => validate_default
|
|
33874
34532
|
});
|
|
33875
|
-
import { existsSync as existsSync44, readFileSync as
|
|
34533
|
+
import { existsSync as existsSync44, readFileSync as readFileSync31 } from "fs";
|
|
33876
34534
|
import { resolve as resolve31, join as join44, dirname as dirname19 } from "path";
|
|
33877
34535
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
33878
34536
|
async function getCompositionDuration2(page) {
|
|
@@ -33929,7 +34587,7 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
33929
34587
|
"hyperframe.runtime.iife.js"
|
|
33930
34588
|
);
|
|
33931
34589
|
if (existsSync44(runtimePath)) {
|
|
33932
|
-
const runtimeSource =
|
|
34590
|
+
const runtimeSource = readFileSync31(runtimePath, "utf-8");
|
|
33933
34591
|
html = html.replace(
|
|
33934
34592
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
33935
34593
|
() => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
|
|
@@ -33947,7 +34605,7 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
33947
34605
|
const filePath = join44(projectDir, decodeURIComponent(url));
|
|
33948
34606
|
if (existsSync44(filePath)) {
|
|
33949
34607
|
res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
|
|
33950
|
-
res.end(
|
|
34608
|
+
res.end(readFileSync31(filePath));
|
|
33951
34609
|
return;
|
|
33952
34610
|
}
|
|
33953
34611
|
res.writeHead(404);
|
|
@@ -34138,7 +34796,7 @@ __export(snapshot_exports, {
|
|
|
34138
34796
|
examples: () => examples18
|
|
34139
34797
|
});
|
|
34140
34798
|
import { spawn as spawn11 } from "child_process";
|
|
34141
|
-
import { existsSync as existsSync45, mkdtempSync as mkdtempSync2, readFileSync as
|
|
34799
|
+
import { existsSync as existsSync45, mkdtempSync as mkdtempSync2, readFileSync as readFileSync32, mkdirSync as mkdirSync24, rmSync as rmSync9 } from "fs";
|
|
34142
34800
|
import { tmpdir as tmpdir4 } from "os";
|
|
34143
34801
|
import { resolve as resolve32, join as join45, dirname as dirname20, relative as relative4, isAbsolute as isAbsolute4 } from "path";
|
|
34144
34802
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
@@ -34183,7 +34841,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds) {
|
|
|
34183
34841
|
}
|
|
34184
34842
|
);
|
|
34185
34843
|
if (result.code !== 0 || result.timedOut || !existsSync45(outPath)) return null;
|
|
34186
|
-
return
|
|
34844
|
+
return readFileSync32(outPath);
|
|
34187
34845
|
} finally {
|
|
34188
34846
|
try {
|
|
34189
34847
|
rmSync9(tmp, { recursive: true, force: true });
|
|
@@ -34206,7 +34864,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
34206
34864
|
"hyperframe.runtime.iife.js"
|
|
34207
34865
|
);
|
|
34208
34866
|
if (existsSync45(runtimePath)) {
|
|
34209
|
-
const runtimeSource =
|
|
34867
|
+
const runtimeSource = readFileSync32(runtimePath, "utf-8");
|
|
34210
34868
|
html = html.replace(
|
|
34211
34869
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
34212
34870
|
() => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
|
|
@@ -34230,7 +34888,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
34230
34888
|
}
|
|
34231
34889
|
if (existsSync45(filePath)) {
|
|
34232
34890
|
res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
|
|
34233
|
-
res.end(
|
|
34891
|
+
res.end(readFileSync32(filePath));
|
|
34234
34892
|
return;
|
|
34235
34893
|
}
|
|
34236
34894
|
res.writeHead(404);
|
|
@@ -34476,7 +35134,7 @@ ${c.error("\u2717")} Snapshot failed: ${msg}`);
|
|
|
34476
35134
|
|
|
34477
35135
|
// src/capture/assetDownloader.ts
|
|
34478
35136
|
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync25 } from "fs";
|
|
34479
|
-
import { join as join46, extname as
|
|
35137
|
+
import { join as join46, extname as extname10 } from "path";
|
|
34480
35138
|
async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
|
|
34481
35139
|
const assetsDir = join46(outputDir, "assets");
|
|
34482
35140
|
mkdirSync25(assetsDir, { recursive: true });
|
|
@@ -34498,7 +35156,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
34498
35156
|
for (const icon of faviconLinks || []) {
|
|
34499
35157
|
if (!icon.href) continue;
|
|
34500
35158
|
try {
|
|
34501
|
-
const ext =
|
|
35159
|
+
const ext = extname10(new URL(icon.href).pathname) || ".ico";
|
|
34502
35160
|
const name = `favicon${ext}`;
|
|
34503
35161
|
const localPath = `assets/${name}`;
|
|
34504
35162
|
const buffer = await fetchBuffer(icon.href);
|
|
@@ -34540,7 +35198,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
34540
35198
|
const results = await Promise.allSettled(
|
|
34541
35199
|
batch.map(async ({ url, isPoster }) => {
|
|
34542
35200
|
const parsedUrl = new URL(url);
|
|
34543
|
-
const pathExt =
|
|
35201
|
+
const pathExt = extname10(parsedUrl.pathname);
|
|
34544
35202
|
const ext = pathExt && pathExt.length <= 5 ? pathExt : ".jpg";
|
|
34545
35203
|
const buffer = await fetchBuffer(url);
|
|
34546
35204
|
if (!buffer) return null;
|
|
@@ -34569,7 +35227,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
34569
35227
|
}
|
|
34570
35228
|
if (tokens.ogImage && !downloadedUrls.has(normalizeUrl(tokens.ogImage))) {
|
|
34571
35229
|
try {
|
|
34572
|
-
const ext =
|
|
35230
|
+
const ext = extname10(new URL(tokens.ogImage).pathname) || ".jpg";
|
|
34573
35231
|
const localPath = `assets/og-image${ext}`;
|
|
34574
35232
|
const buffer = await fetchBuffer(tokens.ogImage);
|
|
34575
35233
|
if (buffer && buffer.length > 5e3) {
|
|
@@ -35429,7 +36087,7 @@ var init_animationCataloger = __esm({
|
|
|
35429
36087
|
});
|
|
35430
36088
|
|
|
35431
36089
|
// src/capture/mediaCapture.ts
|
|
35432
|
-
import { mkdirSync as mkdirSync26, writeFileSync as writeFileSync18, readdirSync as
|
|
36090
|
+
import { mkdirSync as mkdirSync26, writeFileSync as writeFileSync18, readdirSync as readdirSync15, readFileSync as readFileSync33, statSync as statSync15 } from "fs";
|
|
35433
36091
|
import { join as join47 } from "path";
|
|
35434
36092
|
async function saveLottieAnimations(discoveredLotties, lottieDir) {
|
|
35435
36093
|
let savedCount = 0;
|
|
@@ -35493,10 +36151,10 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
|
|
|
35493
36151
|
const manifest = [];
|
|
35494
36152
|
const previewDir = join47(lottieDir, "previews");
|
|
35495
36153
|
mkdirSync26(previewDir, { recursive: true });
|
|
35496
|
-
for (const file of
|
|
36154
|
+
for (const file of readdirSync15(lottieDir)) {
|
|
35497
36155
|
if (!file.endsWith(".json")) continue;
|
|
35498
36156
|
try {
|
|
35499
|
-
const raw = JSON.parse(
|
|
36157
|
+
const raw = JSON.parse(readFileSync33(join47(lottieDir, file), "utf-8"));
|
|
35500
36158
|
const fr = raw.fr || 30;
|
|
35501
36159
|
const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
|
|
35502
36160
|
const previewName = file.replace(".json", "-preview.png");
|
|
@@ -35506,7 +36164,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
|
|
|
35506
36164
|
try {
|
|
35507
36165
|
previewPage = await chromeBrowser.newPage();
|
|
35508
36166
|
await previewPage.setViewport({ width: 400, height: 400 });
|
|
35509
|
-
const animData = JSON.parse(
|
|
36167
|
+
const animData = JSON.parse(readFileSync33(join47(lottieDir, file), "utf-8"));
|
|
35510
36168
|
const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
|
|
35511
36169
|
await previewPage.setContent(
|
|
35512
36170
|
`<!DOCTYPE html>
|
|
@@ -42624,7 +43282,7 @@ var require_node_domexception = __commonJS({
|
|
|
42624
43282
|
|
|
42625
43283
|
// ../../node_modules/.bun/fetch-blob@3.2.0/node_modules/fetch-blob/from.js
|
|
42626
43284
|
import { statSync as statSync16, createReadStream as createReadStream2, promises as fs2 } from "fs";
|
|
42627
|
-
import { basename as
|
|
43285
|
+
import { basename as basename8 } from "path";
|
|
42628
43286
|
var import_node_domexception, stat, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
42629
43287
|
var init_from = __esm({
|
|
42630
43288
|
"../../node_modules/.bun/fetch-blob@3.2.0/node_modules/fetch-blob/from.js"() {
|
|
@@ -42648,7 +43306,7 @@ var init_from = __esm({
|
|
|
42648
43306
|
size: stat3.size,
|
|
42649
43307
|
lastModified: stat3.mtimeMs,
|
|
42650
43308
|
start: 0
|
|
42651
|
-
})],
|
|
43309
|
+
})], basename8(path2), { type, lastModified: stat3.mtimeMs });
|
|
42652
43310
|
BlobDataItem = class _BlobDataItem {
|
|
42653
43311
|
#path;
|
|
42654
43312
|
#start;
|
|
@@ -76124,7 +76782,7 @@ ${underline2}`);
|
|
|
76124
76782
|
});
|
|
76125
76783
|
|
|
76126
76784
|
// src/capture/contentExtractor.ts
|
|
76127
|
-
import { readdirSync as
|
|
76785
|
+
import { readdirSync as readdirSync16, statSync as statSync17, readFileSync as readFileSync34 } from "fs";
|
|
76128
76786
|
import { join as join48 } from "path";
|
|
76129
76787
|
async function detectLibraries(page, capturedShaders) {
|
|
76130
76788
|
let detectedLibraries = [];
|
|
@@ -76245,7 +76903,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
|
|
|
76245
76903
|
try {
|
|
76246
76904
|
const { GoogleGenAI: GoogleGenAI2 } = await Promise.resolve().then(() => (init_node4(), node_exports));
|
|
76247
76905
|
const ai = new GoogleGenAI2({ apiKey: geminiKey });
|
|
76248
|
-
const imageFiles =
|
|
76906
|
+
const imageFiles = readdirSync16(join48(outputDir, "assets")).filter(
|
|
76249
76907
|
(f3) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f3)
|
|
76250
76908
|
);
|
|
76251
76909
|
const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
|
|
@@ -76257,7 +76915,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
|
|
|
76257
76915
|
const filePath = join48(outputDir, "assets", file);
|
|
76258
76916
|
const stat3 = statSync17(filePath);
|
|
76259
76917
|
if (stat3.size > 4e6) return { file, caption: "" };
|
|
76260
|
-
const buffer =
|
|
76918
|
+
const buffer = readFileSync34(filePath);
|
|
76261
76919
|
const base64 = buffer.toString("base64");
|
|
76262
76920
|
const ext = file.split(".").pop()?.toLowerCase() || "png";
|
|
76263
76921
|
const mimeType = ext === "jpg" ? "image/jpeg" : `image/${ext}`;
|
|
@@ -76305,7 +76963,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
76305
76963
|
const fontLines = [];
|
|
76306
76964
|
const assetsPath = join48(outputDir, "assets");
|
|
76307
76965
|
try {
|
|
76308
|
-
for (const file of
|
|
76966
|
+
for (const file of readdirSync16(assetsPath)) {
|
|
76309
76967
|
if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
|
|
76310
76968
|
const filePath = join48(assetsPath, file);
|
|
76311
76969
|
const stat3 = statSync17(filePath);
|
|
@@ -76337,7 +76995,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
76337
76995
|
}
|
|
76338
76996
|
try {
|
|
76339
76997
|
const svgsPath = join48(assetsPath, "svgs");
|
|
76340
|
-
for (const file of
|
|
76998
|
+
for (const file of readdirSync16(svgsPath)) {
|
|
76341
76999
|
if (!file.endsWith(".svg")) continue;
|
|
76342
77000
|
const svgMatch = tokens.svgs.find(
|
|
76343
77001
|
(s2) => s2.label && file.includes(
|
|
@@ -76352,7 +77010,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
76352
77010
|
}
|
|
76353
77011
|
try {
|
|
76354
77012
|
const fontsPath = join48(assetsPath, "fonts");
|
|
76355
|
-
for (const file of
|
|
77013
|
+
for (const file of readdirSync16(fontsPath)) {
|
|
76356
77014
|
fontLines.push(`fonts/${file} \u2014 font file`);
|
|
76357
77015
|
}
|
|
76358
77016
|
} catch {
|
|
@@ -76443,7 +77101,7 @@ var init_agentPromptGenerator = __esm({
|
|
|
76443
77101
|
});
|
|
76444
77102
|
|
|
76445
77103
|
// src/capture/scaffolding.ts
|
|
76446
|
-
import { existsSync as existsSync46, writeFileSync as writeFileSync20, readFileSync as
|
|
77104
|
+
import { existsSync as existsSync46, writeFileSync as writeFileSync20, readFileSync as readFileSync35 } from "fs";
|
|
76447
77105
|
import { join as join50, resolve as resolve33 } from "path";
|
|
76448
77106
|
function loadEnvFile(startDir) {
|
|
76449
77107
|
try {
|
|
@@ -76451,7 +77109,7 @@ function loadEnvFile(startDir) {
|
|
|
76451
77109
|
for (let i2 = 0; i2 < 5; i2++) {
|
|
76452
77110
|
const envPath = resolve33(dir, ".env");
|
|
76453
77111
|
try {
|
|
76454
|
-
const envContent =
|
|
77112
|
+
const envContent = readFileSync35(envPath, "utf-8");
|
|
76455
77113
|
for (const line of envContent.split("\n")) {
|
|
76456
77114
|
const trimmed = line.trim();
|
|
76457
77115
|
if (!trimmed || trimmed.startsWith("#")) continue;
|