hyperframes 0.5.0-alpha.5 → 0.5.0-alpha.7
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 +534 -72
- package/dist/studio/assets/index-0Zt0t13W.css +1 -0
- package/dist/studio/assets/index-CDSQavT7.js +105 -0
- package/dist/studio/index.html +2 -2
- package/package.json +2 -2
- package/dist/studio/assets/index-Ba6SZOXW.js +0 -105
- package/dist/studio/assets/index-BpcIkyVP.css +0 -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.5.0-alpha.
|
|
57
|
+
VERSION = true ? "0.5.0-alpha.7" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -5438,7 +5438,7 @@ var init_captions = __esm({
|
|
|
5438
5438
|
const hasHardKill = /\.set\s*\([^,]+,\s*\{[^}]*(?:visibility\s*:\s*["']hidden["']|opacity\s*:\s*0)/.test(
|
|
5439
5439
|
content
|
|
5440
5440
|
);
|
|
5441
|
-
const hasCaptionLoop = /forEach|\.forEach\s*\(/.test(content) && /
|
|
5441
|
+
const hasCaptionLoop = /forEach|\.forEach\s*\(/.test(content) && /karaoke|caption[-_]?(?:group|word|line|block)|cg-/.test(content);
|
|
5442
5442
|
if (hasCaptionLoop && hasExitTween && !hasHardKill) {
|
|
5443
5443
|
findings.push({
|
|
5444
5444
|
code: "caption_exit_missing_hard_kill",
|
|
@@ -9490,9 +9490,12 @@ function readCache(path2) {
|
|
|
9490
9490
|
}
|
|
9491
9491
|
}
|
|
9492
9492
|
function writeCache(path2, data) {
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9493
|
+
try {
|
|
9494
|
+
mkdirSync(dirname3(path2), { recursive: true });
|
|
9495
|
+
const entry = { fetchedAt: Date.now(), data };
|
|
9496
|
+
writeFileSync(path2, JSON.stringify(entry), "utf-8");
|
|
9497
|
+
} catch {
|
|
9498
|
+
}
|
|
9496
9499
|
}
|
|
9497
9500
|
async function fetchJson(url) {
|
|
9498
9501
|
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
@@ -25024,6 +25027,8 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
25024
25027
|
const vpWidth = parseInt(url.searchParams.get("w") || "0") || 0;
|
|
25025
25028
|
const vpHeight = parseInt(url.searchParams.get("h") || "0") || 0;
|
|
25026
25029
|
const selector = url.searchParams.get("selector") || void 0;
|
|
25030
|
+
const rawSelectorIndex = Number.parseInt(url.searchParams.get("selectorIndex") || "0", 10);
|
|
25031
|
+
const selectorIndex = Number.isFinite(rawSelectorIndex) && rawSelectorIndex > 0 ? rawSelectorIndex : void 0;
|
|
25027
25032
|
const urlVersion = url.searchParams.get("v") || "";
|
|
25028
25033
|
let compW = vpWidth || 1920;
|
|
25029
25034
|
let compH = vpHeight || 1080;
|
|
@@ -25041,7 +25046,7 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
25041
25046
|
}
|
|
25042
25047
|
const previewUrl = compPath === "index.html" ? `http://${c2.req.header("host")}/api/projects/${project.id}/preview` : `http://${c2.req.header("host")}/api/projects/${project.id}/preview/comp/${compPath}`;
|
|
25043
25048
|
const cacheDir = join17(project.dir, ".thumbnails");
|
|
25044
|
-
const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}` : "";
|
|
25049
|
+
const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}_${selectorIndex ?? 0}` : "";
|
|
25045
25050
|
const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
|
|
25046
25051
|
const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.jpg`;
|
|
25047
25052
|
const cachePath2 = join17(cacheDir, cacheKey);
|
|
@@ -25058,7 +25063,8 @@ function registerThumbnailRoutes(api, adapter2) {
|
|
|
25058
25063
|
width: compW,
|
|
25059
25064
|
height: compH,
|
|
25060
25065
|
previewUrl,
|
|
25061
|
-
selector
|
|
25066
|
+
selector,
|
|
25067
|
+
selectorIndex
|
|
25062
25068
|
});
|
|
25063
25069
|
if (!buffer) {
|
|
25064
25070
|
return c2.json({ error: "Thumbnail generation returned null" }, 500);
|
|
@@ -29123,6 +29129,7 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
29123
29129
|
const effectiveCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG2.coresPerWorker;
|
|
29124
29130
|
const effectiveMinParallelFrames = config?.minParallelFrames ?? DEFAULT_CONFIG2.minParallelFrames;
|
|
29125
29131
|
const effectiveLargeRenderThreshold = config?.largeRenderThreshold ?? DEFAULT_CONFIG2.largeRenderThreshold;
|
|
29132
|
+
const captureCostMultiplier = Math.max(1, config?.captureCostMultiplier ?? 1);
|
|
29126
29133
|
if (requested !== void 0) {
|
|
29127
29134
|
return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
|
|
29128
29135
|
}
|
|
@@ -29135,8 +29142,14 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
29135
29142
|
const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
|
|
29136
29143
|
const minWorkersForJob = totalFrames >= effectiveMinParallelFrames ? 2 : MIN_WORKERS;
|
|
29137
29144
|
let finalWorkers = Math.max(minWorkersForJob, Math.min(effectiveMaxWorkers, optimal));
|
|
29138
|
-
|
|
29139
|
-
|
|
29145
|
+
const weightedFrames = totalFrames * captureCostMultiplier;
|
|
29146
|
+
const contentionThreshold = Math.max(
|
|
29147
|
+
effectiveMinParallelFrames,
|
|
29148
|
+
Math.floor(effectiveLargeRenderThreshold / 3)
|
|
29149
|
+
);
|
|
29150
|
+
if (totalFrames >= effectiveLargeRenderThreshold || weightedFrames >= contentionThreshold) {
|
|
29151
|
+
const weightedCoresPerWorker = effectiveCoresPerWorker * captureCostMultiplier;
|
|
29152
|
+
const cpuScaledMax = Math.max(MIN_WORKERS, Math.floor(cpuCount / weightedCoresPerWorker));
|
|
29140
29153
|
if (finalWorkers > cpuScaledMax) {
|
|
29141
29154
|
finalWorkers = cpuScaledMax;
|
|
29142
29155
|
}
|
|
@@ -32896,6 +32909,17 @@ function detectRenderModeHints(html) {
|
|
|
32896
32909
|
reasons
|
|
32897
32910
|
};
|
|
32898
32911
|
}
|
|
32912
|
+
function detectShaderTransitionUsage(html) {
|
|
32913
|
+
let scriptMatch;
|
|
32914
|
+
const scriptPattern = new RegExp(INLINE_SCRIPT_PATTERN.source, INLINE_SCRIPT_PATTERN.flags);
|
|
32915
|
+
while ((scriptMatch = scriptPattern.exec(html)) !== null) {
|
|
32916
|
+
const attrs = scriptMatch[1] || "";
|
|
32917
|
+
if (/\bsrc\s*=/i.test(attrs)) continue;
|
|
32918
|
+
const content = stripJsComments(stripCompilerMountBootstrap(scriptMatch[2] || ""));
|
|
32919
|
+
if (SHADER_TRANSITION_USAGE_PATTERN.test(content)) return true;
|
|
32920
|
+
}
|
|
32921
|
+
return false;
|
|
32922
|
+
}
|
|
32899
32923
|
async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
|
|
32900
32924
|
let filePath = src;
|
|
32901
32925
|
if (isHttpUrl(src)) {
|
|
@@ -33432,6 +33456,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33432
33456
|
"$1"
|
|
33433
33457
|
);
|
|
33434
33458
|
const renderModeHints = detectRenderModeHints(sanitizedHtml);
|
|
33459
|
+
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
33435
33460
|
const coalescedHtml = await injectDeterministicFontFaces(
|
|
33436
33461
|
coalesceHeadStylesAndBodyScripts2(promoteCssImportsToLinkTags(sanitizedHtml))
|
|
33437
33462
|
);
|
|
@@ -33479,7 +33504,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33479
33504
|
width,
|
|
33480
33505
|
height,
|
|
33481
33506
|
staticDuration,
|
|
33482
|
-
renderModeHints
|
|
33507
|
+
renderModeHints,
|
|
33508
|
+
hasShaderTransitions
|
|
33483
33509
|
};
|
|
33484
33510
|
}
|
|
33485
33511
|
async function discoverMediaFromBrowser(page) {
|
|
@@ -33581,10 +33607,11 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
33581
33607
|
audios,
|
|
33582
33608
|
images,
|
|
33583
33609
|
unresolvedCompositions: remaining,
|
|
33584
|
-
renderModeHints: compiled.renderModeHints
|
|
33610
|
+
renderModeHints: compiled.renderModeHints,
|
|
33611
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33585
33612
|
};
|
|
33586
33613
|
}
|
|
33587
|
-
var INLINE_SCRIPT_PATTERN, COMPILER_MOUNT_BLOCK_START, COMPILER_MOUNT_BLOCK_END;
|
|
33614
|
+
var INLINE_SCRIPT_PATTERN, COMPILER_MOUNT_BLOCK_START, COMPILER_MOUNT_BLOCK_END, SHADER_TRANSITION_USAGE_PATTERN;
|
|
33588
33615
|
var init_htmlCompiler2 = __esm({
|
|
33589
33616
|
"../producer/src/services/htmlCompiler.ts"() {
|
|
33590
33617
|
"use strict";
|
|
@@ -33599,6 +33626,7 @@ var init_htmlCompiler2 = __esm({
|
|
|
33599
33626
|
INLINE_SCRIPT_PATTERN = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
33600
33627
|
COMPILER_MOUNT_BLOCK_START = "/* __HF_COMPILER_MOUNT_START__ */";
|
|
33601
33628
|
COMPILER_MOUNT_BLOCK_END = "/* __HF_COMPILER_MOUNT_END__ */";
|
|
33629
|
+
SHADER_TRANSITION_USAGE_PATTERN = /\b(?:(?:window|globalThis)\s*\.\s*)?HyperShader\s*\.\s*init\s*\(|\b__hf\s*\.\s*transitions\s*=/;
|
|
33602
33630
|
}
|
|
33603
33631
|
});
|
|
33604
33632
|
|
|
@@ -33902,7 +33930,8 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
33902
33930
|
mediaStart: a.mediaStart
|
|
33903
33931
|
})),
|
|
33904
33932
|
subCompositions: Array.from(compiled.subCompositions.keys()),
|
|
33905
|
-
renderModeHints: compiled.renderModeHints
|
|
33933
|
+
renderModeHints: compiled.renderModeHints,
|
|
33934
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33906
33935
|
};
|
|
33907
33936
|
writeFileSync14(join36(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
|
|
33908
33937
|
}
|
|
@@ -33915,6 +33944,287 @@ function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
|
|
|
33915
33944
|
reasons: compiled.renderModeHints.reasons.map((reason) => reason.message)
|
|
33916
33945
|
});
|
|
33917
33946
|
}
|
|
33947
|
+
function resolveRenderWorkerCount(totalFrames, requestedWorkers, cfg, compiled, composition, log2 = defaultLogger, measuredCaptureCost) {
|
|
33948
|
+
const captureCost = combineCaptureCostEstimates(
|
|
33949
|
+
estimateCaptureCostMultiplier(compiled, composition),
|
|
33950
|
+
measuredCaptureCost
|
|
33951
|
+
);
|
|
33952
|
+
const workerCount = calculateOptimalWorkers(totalFrames, requestedWorkers, {
|
|
33953
|
+
...cfg,
|
|
33954
|
+
captureCostMultiplier: captureCost.multiplier
|
|
33955
|
+
});
|
|
33956
|
+
if (requestedWorkers !== void 0 || captureCost.multiplier <= 1) {
|
|
33957
|
+
return workerCount;
|
|
33958
|
+
}
|
|
33959
|
+
const baselineWorkers = calculateOptimalWorkers(totalFrames, void 0, cfg);
|
|
33960
|
+
if (workerCount < baselineWorkers) {
|
|
33961
|
+
log2.warn(
|
|
33962
|
+
"[Render] Reduced auto worker count for high-cost capture workload to avoid Chrome compositor starvation.",
|
|
33963
|
+
{
|
|
33964
|
+
from: baselineWorkers,
|
|
33965
|
+
to: workerCount,
|
|
33966
|
+
costMultiplier: captureCost.multiplier,
|
|
33967
|
+
reasons: captureCost.reasons
|
|
33968
|
+
}
|
|
33969
|
+
);
|
|
33970
|
+
}
|
|
33971
|
+
return workerCount;
|
|
33972
|
+
}
|
|
33973
|
+
function estimateCaptureCostMultiplier(compiled, composition) {
|
|
33974
|
+
let multiplier = 1;
|
|
33975
|
+
const reasons = [];
|
|
33976
|
+
if (compiled.hasShaderTransitions) {
|
|
33977
|
+
multiplier += 2;
|
|
33978
|
+
reasons.push("shader-transitions");
|
|
33979
|
+
}
|
|
33980
|
+
const reasonCodes = new Set(compiled.renderModeHints.reasons.map((reason) => reason.code));
|
|
33981
|
+
if (reasonCodes.has("requestAnimationFrame")) {
|
|
33982
|
+
multiplier += 1;
|
|
33983
|
+
reasons.push("requestAnimationFrame");
|
|
33984
|
+
}
|
|
33985
|
+
if (reasonCodes.has("iframe")) {
|
|
33986
|
+
multiplier += 0.5;
|
|
33987
|
+
reasons.push("iframe");
|
|
33988
|
+
}
|
|
33989
|
+
if (composition.videos.length > 0) {
|
|
33990
|
+
multiplier += Math.min(2, composition.videos.length * 0.75);
|
|
33991
|
+
reasons.push(`${composition.videos.length} video${composition.videos.length === 1 ? "" : "s"}`);
|
|
33992
|
+
}
|
|
33993
|
+
if (composition.audios.length > 0) {
|
|
33994
|
+
multiplier += Math.min(1, composition.audios.length * 0.75);
|
|
33995
|
+
reasons.push(`${composition.audios.length} audio${composition.audios.length === 1 ? "" : "s"}`);
|
|
33996
|
+
}
|
|
33997
|
+
return {
|
|
33998
|
+
multiplier: Math.round(multiplier * 100) / 100,
|
|
33999
|
+
reasons
|
|
34000
|
+
};
|
|
34001
|
+
}
|
|
34002
|
+
function combineCaptureCostEstimates(staticCost, measuredCost) {
|
|
34003
|
+
if (!measuredCost || measuredCost.multiplier <= 1) return staticCost;
|
|
34004
|
+
if (staticCost.multiplier >= measuredCost.multiplier) {
|
|
34005
|
+
return {
|
|
34006
|
+
multiplier: staticCost.multiplier,
|
|
34007
|
+
reasons: [...staticCost.reasons, ...measuredCost.reasons],
|
|
34008
|
+
p95Ms: measuredCost.p95Ms
|
|
34009
|
+
};
|
|
34010
|
+
}
|
|
34011
|
+
return {
|
|
34012
|
+
multiplier: measuredCost.multiplier,
|
|
34013
|
+
reasons: [...measuredCost.reasons, ...staticCost.reasons],
|
|
34014
|
+
p95Ms: measuredCost.p95Ms
|
|
34015
|
+
};
|
|
34016
|
+
}
|
|
34017
|
+
function createCaptureCalibrationConfig(cfg) {
|
|
34018
|
+
return {
|
|
34019
|
+
...cfg,
|
|
34020
|
+
protocolTimeout: Math.min(cfg.protocolTimeout, CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS)
|
|
34021
|
+
};
|
|
34022
|
+
}
|
|
34023
|
+
function estimateMeasuredCaptureCostMultiplier(samples) {
|
|
34024
|
+
if (samples.length === 0) {
|
|
34025
|
+
return { multiplier: 1, reasons: [] };
|
|
34026
|
+
}
|
|
34027
|
+
const sorted = [...samples].sort((a, b) => a.captureTimeMs - b.captureTimeMs);
|
|
34028
|
+
const p95Index = Math.max(0, Math.ceil(sorted.length * 0.95) - 1);
|
|
34029
|
+
const p95Sample = sorted[p95Index] ?? sorted[sorted.length - 1];
|
|
34030
|
+
if (!p95Sample) {
|
|
34031
|
+
return { multiplier: 1, reasons: [] };
|
|
34032
|
+
}
|
|
34033
|
+
const p95Ms = Math.round(p95Sample.captureTimeMs);
|
|
34034
|
+
const multiplier = Math.min(
|
|
34035
|
+
MAX_MEASURED_CAPTURE_COST_MULTIPLIER,
|
|
34036
|
+
Math.max(1, Math.round(p95Ms / CAPTURE_CALIBRATION_TARGET_MS * 100) / 100)
|
|
34037
|
+
);
|
|
34038
|
+
return {
|
|
34039
|
+
multiplier,
|
|
34040
|
+
reasons: multiplier > 1 ? [`calibration-p95=${p95Ms}ms`] : [],
|
|
34041
|
+
p95Ms
|
|
34042
|
+
};
|
|
34043
|
+
}
|
|
34044
|
+
function selectCaptureCalibrationFrames(totalFrames) {
|
|
34045
|
+
if (totalFrames <= 0) return [];
|
|
34046
|
+
const lastFrame = totalFrames - 1;
|
|
34047
|
+
const candidates = [
|
|
34048
|
+
0,
|
|
34049
|
+
Math.floor(totalFrames * 0.25),
|
|
34050
|
+
Math.floor(totalFrames * 0.5),
|
|
34051
|
+
Math.floor(totalFrames * 0.75),
|
|
34052
|
+
lastFrame
|
|
34053
|
+
];
|
|
34054
|
+
return Array.from(
|
|
34055
|
+
new Set(candidates.map((frame) => Math.max(0, Math.min(lastFrame, frame))))
|
|
34056
|
+
).sort((a, b) => a - b);
|
|
34057
|
+
}
|
|
34058
|
+
function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
34059
|
+
const ranges = [];
|
|
34060
|
+
let rangeStart = null;
|
|
34061
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
34062
|
+
const framePath = join36(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
|
|
34063
|
+
const missing = !existsSync34(framePath);
|
|
34064
|
+
if (missing && rangeStart === null) {
|
|
34065
|
+
rangeStart = frameIndex;
|
|
34066
|
+
} else if (!missing && rangeStart !== null) {
|
|
34067
|
+
ranges.push({ startFrame: rangeStart, endFrame: frameIndex });
|
|
34068
|
+
rangeStart = null;
|
|
34069
|
+
}
|
|
34070
|
+
}
|
|
34071
|
+
if (rangeStart !== null) {
|
|
34072
|
+
ranges.push({ startFrame: rangeStart, endFrame: totalFrames });
|
|
34073
|
+
}
|
|
34074
|
+
return ranges;
|
|
34075
|
+
}
|
|
34076
|
+
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt) {
|
|
34077
|
+
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
34078
|
+
const batches = [];
|
|
34079
|
+
for (let i2 = 0; i2 < ranges.length; i2 += workersPerBatch) {
|
|
34080
|
+
const batchIndex = batches.length;
|
|
34081
|
+
const batch = ranges.slice(i2, i2 + workersPerBatch).map((range, workerId) => ({
|
|
34082
|
+
workerId,
|
|
34083
|
+
startFrame: range.startFrame,
|
|
34084
|
+
endFrame: range.endFrame,
|
|
34085
|
+
outputDir: join36(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`)
|
|
34086
|
+
}));
|
|
34087
|
+
batches.push(batch);
|
|
34088
|
+
}
|
|
34089
|
+
return batches;
|
|
34090
|
+
}
|
|
34091
|
+
function getNextRetryWorkerCount(currentWorkers) {
|
|
34092
|
+
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
34093
|
+
}
|
|
34094
|
+
function isRecoverableParallelCaptureError(error) {
|
|
34095
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34096
|
+
return message.includes("[Parallel] Capture failed") && /Runtime\.callFunctionOn timed out|HeadlessExperimental\.beginFrame timed out|Waiting failed|timeout exceeded|timed out|Navigation timeout|Protocol error|Target closed/i.test(
|
|
34097
|
+
message
|
|
34098
|
+
);
|
|
34099
|
+
}
|
|
34100
|
+
function shouldFallbackToScreenshotAfterCalibrationError(error) {
|
|
34101
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34102
|
+
return /HeadlessExperimental\.beginFrame timed out|beginFrame probe timeout|Another frame is pending|Frame still pending|Protocol error.*HeadlessExperimental\.beginFrame/i.test(
|
|
34103
|
+
message
|
|
34104
|
+
);
|
|
34105
|
+
}
|
|
34106
|
+
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
34107
|
+
let captured = 0;
|
|
34108
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
34109
|
+
const framePath = join36(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
|
|
34110
|
+
if (existsSync34(framePath)) captured++;
|
|
34111
|
+
}
|
|
34112
|
+
return captured;
|
|
34113
|
+
}
|
|
34114
|
+
function countFrameRanges(ranges) {
|
|
34115
|
+
return ranges.reduce((sum, range) => sum + (range.endFrame - range.startFrame), 0);
|
|
34116
|
+
}
|
|
34117
|
+
async function measureCaptureCostFromSession(session, totalFrames, fps) {
|
|
34118
|
+
const sampledFrames = selectCaptureCalibrationFrames(totalFrames);
|
|
34119
|
+
const samples = [];
|
|
34120
|
+
for (const frameIndex of sampledFrames) {
|
|
34121
|
+
const time = frameIndex / fps;
|
|
34122
|
+
const startedAt = Date.now();
|
|
34123
|
+
const result = await captureFrameToBuffer(session, frameIndex, time);
|
|
34124
|
+
samples.push({
|
|
34125
|
+
frameIndex,
|
|
34126
|
+
captureTimeMs: result.captureTimeMs || Date.now() - startedAt
|
|
34127
|
+
});
|
|
34128
|
+
}
|
|
34129
|
+
return {
|
|
34130
|
+
estimate: estimateMeasuredCaptureCostMultiplier(samples),
|
|
34131
|
+
samples
|
|
34132
|
+
};
|
|
34133
|
+
}
|
|
34134
|
+
async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
34135
|
+
const attempts = [];
|
|
34136
|
+
let currentWorkers = options.initialWorkerCount;
|
|
34137
|
+
let missingRanges = null;
|
|
34138
|
+
let attempt = 0;
|
|
34139
|
+
while (true) {
|
|
34140
|
+
const frameCount = missingRanges ? countFrameRanges(missingRanges) : options.totalFrames;
|
|
34141
|
+
attempts.push({
|
|
34142
|
+
attempt,
|
|
34143
|
+
workers: currentWorkers,
|
|
34144
|
+
frameCount,
|
|
34145
|
+
reason: attempt === 0 ? "initial" : "retry"
|
|
34146
|
+
});
|
|
34147
|
+
const attemptWorkDir = join36(options.workDir, `capture-attempt-${attempt}`);
|
|
34148
|
+
const batches = missingRanges ? buildMissingFrameRetryBatches(missingRanges, currentWorkers, attemptWorkDir, attempt) : [distributeFrames(options.totalFrames, currentWorkers, attemptWorkDir)];
|
|
34149
|
+
try {
|
|
34150
|
+
for (const tasks of batches) {
|
|
34151
|
+
const capturedBeforeBatch = countCapturedFrames(
|
|
34152
|
+
options.totalFrames,
|
|
34153
|
+
options.framesDir,
|
|
34154
|
+
options.frameExt
|
|
34155
|
+
);
|
|
34156
|
+
try {
|
|
34157
|
+
await executeParallelCapture(
|
|
34158
|
+
options.serverUrl,
|
|
34159
|
+
attemptWorkDir,
|
|
34160
|
+
tasks,
|
|
34161
|
+
options.captureOptions,
|
|
34162
|
+
options.createBeforeCaptureHook,
|
|
34163
|
+
options.abortSignal,
|
|
34164
|
+
options.onProgress ? (progress) => {
|
|
34165
|
+
options.onProgress?.({
|
|
34166
|
+
...progress,
|
|
34167
|
+
totalFrames: options.totalFrames,
|
|
34168
|
+
capturedFrames: Math.min(
|
|
34169
|
+
options.totalFrames,
|
|
34170
|
+
capturedBeforeBatch + progress.capturedFrames
|
|
34171
|
+
)
|
|
34172
|
+
});
|
|
34173
|
+
} : void 0,
|
|
34174
|
+
void 0,
|
|
34175
|
+
options.cfg
|
|
34176
|
+
);
|
|
34177
|
+
} finally {
|
|
34178
|
+
await mergeWorkerFrames(attemptWorkDir, tasks, options.framesDir);
|
|
34179
|
+
}
|
|
34180
|
+
}
|
|
34181
|
+
const remaining = findMissingFrameRanges(
|
|
34182
|
+
options.totalFrames,
|
|
34183
|
+
options.framesDir,
|
|
34184
|
+
options.frameExt
|
|
34185
|
+
);
|
|
34186
|
+
if (remaining.length === 0) {
|
|
34187
|
+
return attempts;
|
|
34188
|
+
}
|
|
34189
|
+
if (!options.allowRetry || currentWorkers <= 1) {
|
|
34190
|
+
throw new Error(
|
|
34191
|
+
`[Render] Capture completed but ${countFrameRanges(remaining)} frame(s) are missing`
|
|
34192
|
+
);
|
|
34193
|
+
}
|
|
34194
|
+
const nextWorkers = getNextRetryWorkerCount(currentWorkers);
|
|
34195
|
+
options.log.warn("[Render] Retrying missing captured frames with fewer workers.", {
|
|
34196
|
+
fromWorkers: currentWorkers,
|
|
34197
|
+
toWorkers: nextWorkers,
|
|
34198
|
+
missingFrames: countFrameRanges(remaining)
|
|
34199
|
+
});
|
|
34200
|
+
currentWorkers = nextWorkers;
|
|
34201
|
+
missingRanges = remaining;
|
|
34202
|
+
attempt++;
|
|
34203
|
+
} catch (error) {
|
|
34204
|
+
const remaining = findMissingFrameRanges(
|
|
34205
|
+
options.totalFrames,
|
|
34206
|
+
options.framesDir,
|
|
34207
|
+
options.frameExt
|
|
34208
|
+
);
|
|
34209
|
+
if (remaining.length === 0) {
|
|
34210
|
+
return attempts;
|
|
34211
|
+
}
|
|
34212
|
+
if (!options.allowRetry || currentWorkers <= 1 || !isRecoverableParallelCaptureError(error)) {
|
|
34213
|
+
throw error;
|
|
34214
|
+
}
|
|
34215
|
+
const nextWorkers = getNextRetryWorkerCount(currentWorkers);
|
|
34216
|
+
options.log.warn("[Render] Parallel capture timed out; retrying missing frames.", {
|
|
34217
|
+
fromWorkers: currentWorkers,
|
|
34218
|
+
toWorkers: nextWorkers,
|
|
34219
|
+
missingFrames: countFrameRanges(remaining),
|
|
34220
|
+
error: error instanceof Error ? error.message : String(error)
|
|
34221
|
+
});
|
|
34222
|
+
currentWorkers = nextWorkers;
|
|
34223
|
+
missingRanges = remaining;
|
|
34224
|
+
attempt++;
|
|
34225
|
+
}
|
|
34226
|
+
}
|
|
34227
|
+
}
|
|
33918
34228
|
function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, width, height, log2, sourceTransfer, targetTransfer) {
|
|
33919
34229
|
const frameDir = hdrFrameDirs.get(el.id);
|
|
33920
34230
|
const startTime = hdrStartTimes.get(el.id);
|
|
@@ -34541,7 +34851,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34541
34851
|
let extractionResult = null;
|
|
34542
34852
|
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
34543
34853
|
const videoTransfers = /* @__PURE__ */ new Map();
|
|
34544
|
-
if (job.config.
|
|
34854
|
+
if (job.config.hdrMode !== "force-sdr" && composition.videos.length > 0) {
|
|
34545
34855
|
await Promise.all(
|
|
34546
34856
|
composition.videos.map(async (v) => {
|
|
34547
34857
|
let videoPath = v.src;
|
|
@@ -34562,7 +34872,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34562
34872
|
const imageTransfers = /* @__PURE__ */ new Map();
|
|
34563
34873
|
const hdrImageSrcPaths = /* @__PURE__ */ new Map();
|
|
34564
34874
|
const imageColorSpaces = [];
|
|
34565
|
-
if (job.config.
|
|
34875
|
+
if (job.config.hdrMode !== "force-sdr" && composition.images.length > 0) {
|
|
34566
34876
|
const probed = await Promise.all(
|
|
34567
34877
|
composition.images.map(async (img) => {
|
|
34568
34878
|
let imgPath = img.src;
|
|
@@ -34619,28 +34929,53 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34619
34929
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
34620
34930
|
}
|
|
34621
34931
|
let effectiveHdr;
|
|
34622
|
-
|
|
34932
|
+
let forcedHdrWithoutSources = false;
|
|
34933
|
+
{
|
|
34934
|
+
const hdrMode = job.config.hdrMode ?? "auto";
|
|
34623
34935
|
const videoColorSpaces = (extractionResult?.extracted ?? []).map(
|
|
34624
34936
|
(ext) => ext.metadata.colorSpace
|
|
34625
34937
|
);
|
|
34626
34938
|
const allColorSpaces = [...videoColorSpaces, ...imageColorSpaces];
|
|
34627
|
-
|
|
34628
|
-
|
|
34629
|
-
|
|
34939
|
+
const info = allColorSpaces.length > 0 ? analyzeCompositionHdr(allColorSpaces) : null;
|
|
34940
|
+
if (hdrMode === "force-sdr") {
|
|
34941
|
+
effectiveHdr = void 0;
|
|
34942
|
+
} else if (hdrMode === "force-hdr") {
|
|
34943
|
+
if (info?.hasHdr && info.dominantTransfer) {
|
|
34944
|
+
effectiveHdr = { transfer: info.dominantTransfer };
|
|
34945
|
+
} else {
|
|
34946
|
+
effectiveHdr = { transfer: "hlg" };
|
|
34947
|
+
forcedHdrWithoutSources = true;
|
|
34948
|
+
}
|
|
34949
|
+
} else {
|
|
34950
|
+
if (info?.hasHdr && info.dominantTransfer) {
|
|
34630
34951
|
effectiveHdr = { transfer: info.dominantTransfer };
|
|
34631
34952
|
}
|
|
34632
34953
|
}
|
|
34633
34954
|
}
|
|
34634
34955
|
if (effectiveHdr && outputFormat !== "mp4") {
|
|
34956
|
+
const hdrSourceReason = forcedHdrWithoutSources ? "HDR was forced without detected HDR sources" : "HDR source detected";
|
|
34635
34957
|
log2.warn(
|
|
34636
|
-
`[Render]
|
|
34958
|
+
`[Render] ${hdrSourceReason}, but format is "${outputFormat}" \u2014 falling back to SDR. HDR + alpha is not supported. Use --format mp4 for HDR10 output.`
|
|
34637
34959
|
);
|
|
34638
34960
|
effectiveHdr = void 0;
|
|
34639
34961
|
}
|
|
34640
|
-
|
|
34641
|
-
|
|
34642
|
-
|
|
34643
|
-
|
|
34962
|
+
{
|
|
34963
|
+
const hdrMode = job.config.hdrMode ?? "auto";
|
|
34964
|
+
if (forcedHdrWithoutSources) {
|
|
34965
|
+
log2.warn(
|
|
34966
|
+
"[Render] HDR forced by --hdr flag, but no HDR sources were detected \u2014 defaulting to HLG. SDR-only compositions may look perceptually wrong on HDR displays."
|
|
34967
|
+
);
|
|
34968
|
+
}
|
|
34969
|
+
if (effectiveHdr) {
|
|
34970
|
+
const reason = hdrMode === "force-hdr" ? forcedHdrWithoutSources ? "forced by --hdr flag (no HDR sources detected \u2014 defaulting to HLG)" : "forced by --hdr flag" : "auto-detected from source(s)";
|
|
34971
|
+
log2.info(
|
|
34972
|
+
`[Render] HDR ${reason} \u2014 output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
34973
|
+
);
|
|
34974
|
+
} else if (hdrMode === "force-sdr") {
|
|
34975
|
+
log2.info("[Render] SDR forced by --sdr flag");
|
|
34976
|
+
} else {
|
|
34977
|
+
log2.info("[Render] No HDR sources detected \u2014 rendering SDR");
|
|
34978
|
+
}
|
|
34644
34979
|
}
|
|
34645
34980
|
const stage3Start = Date.now();
|
|
34646
34981
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
@@ -34687,7 +35022,101 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34687
35022
|
...captureOptions,
|
|
34688
35023
|
skipReadinessVideoIds: Array.from(nativeHdrVideoIds)
|
|
34689
35024
|
});
|
|
34690
|
-
|
|
35025
|
+
let captureCalibration;
|
|
35026
|
+
let switchedToScreenshotAfterCalibration = false;
|
|
35027
|
+
if (job.config.workers === void 0 && totalFrames >= 60) {
|
|
35028
|
+
const calibrationDir = join36(workDir, "capture-calibration");
|
|
35029
|
+
const calibrationCfg = createCaptureCalibrationConfig(cfg);
|
|
35030
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
35031
|
+
let calibrationSession = null;
|
|
35032
|
+
try {
|
|
35033
|
+
calibrationSession = await createCaptureSession(
|
|
35034
|
+
fileServer.url,
|
|
35035
|
+
calibrationDir,
|
|
35036
|
+
buildHdrCaptureOptions(),
|
|
35037
|
+
videoInjector,
|
|
35038
|
+
calibrationCfg
|
|
35039
|
+
);
|
|
35040
|
+
if (!calibrationSession.isInitialized) {
|
|
35041
|
+
await initializeSession(calibrationSession);
|
|
35042
|
+
}
|
|
35043
|
+
assertNotAborted();
|
|
35044
|
+
captureCalibration = await measureCaptureCostFromSession(
|
|
35045
|
+
calibrationSession,
|
|
35046
|
+
totalFrames,
|
|
35047
|
+
job.config.fps
|
|
35048
|
+
);
|
|
35049
|
+
if (captureCalibration.estimate.multiplier > 1) {
|
|
35050
|
+
log2.warn("[Render] Measured slow frame capture during auto-worker calibration.", {
|
|
35051
|
+
multiplier: captureCalibration.estimate.multiplier,
|
|
35052
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
35053
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex)
|
|
35054
|
+
});
|
|
35055
|
+
} else {
|
|
35056
|
+
log2.debug("[Render] Auto-worker calibration kept baseline capture cost.", {
|
|
35057
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
35058
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex)
|
|
35059
|
+
});
|
|
35060
|
+
}
|
|
35061
|
+
} catch (error) {
|
|
35062
|
+
const shouldFallbackToScreenshot = !cfg.forceScreenshot && shouldFallbackToScreenshotAfterCalibrationError(error);
|
|
35063
|
+
if (shouldFallbackToScreenshot) {
|
|
35064
|
+
cfg.forceScreenshot = true;
|
|
35065
|
+
switchedToScreenshotAfterCalibration = true;
|
|
35066
|
+
if (probeSession) {
|
|
35067
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
35068
|
+
await closeCaptureSession(probeSession).catch(() => {
|
|
35069
|
+
});
|
|
35070
|
+
probeSession = null;
|
|
35071
|
+
}
|
|
35072
|
+
}
|
|
35073
|
+
captureCalibration = {
|
|
35074
|
+
estimate: {
|
|
35075
|
+
multiplier: MAX_MEASURED_CAPTURE_COST_MULTIPLIER,
|
|
35076
|
+
reasons: shouldFallbackToScreenshot ? ["calibration-beginframe-timeout", "screenshot-fallback"] : ["calibration-failed"]
|
|
35077
|
+
},
|
|
35078
|
+
samples: []
|
|
35079
|
+
};
|
|
35080
|
+
if (shouldFallbackToScreenshot) {
|
|
35081
|
+
log2.warn(
|
|
35082
|
+
"[Render] BeginFrame auto-worker calibration timed out; falling back to screenshot capture mode.",
|
|
35083
|
+
{
|
|
35084
|
+
protocolTimeout: calibrationCfg.protocolTimeout,
|
|
35085
|
+
error: error instanceof Error ? error.message : String(error)
|
|
35086
|
+
}
|
|
35087
|
+
);
|
|
35088
|
+
} else {
|
|
35089
|
+
log2.warn("[Render] Auto-worker calibration failed; using conservative worker budget.", {
|
|
35090
|
+
protocolTimeout: calibrationCfg.protocolTimeout,
|
|
35091
|
+
error: error instanceof Error ? error.message : String(error)
|
|
35092
|
+
});
|
|
35093
|
+
}
|
|
35094
|
+
} finally {
|
|
35095
|
+
if (calibrationSession) {
|
|
35096
|
+
lastBrowserConsole = calibrationSession.browserConsoleBuffer;
|
|
35097
|
+
await closeCaptureSession(calibrationSession).catch(() => {
|
|
35098
|
+
});
|
|
35099
|
+
}
|
|
35100
|
+
}
|
|
35101
|
+
}
|
|
35102
|
+
let workerCount = resolveRenderWorkerCount(
|
|
35103
|
+
totalFrames,
|
|
35104
|
+
job.config.workers,
|
|
35105
|
+
cfg,
|
|
35106
|
+
compiled,
|
|
35107
|
+
composition,
|
|
35108
|
+
log2,
|
|
35109
|
+
captureCalibration?.estimate
|
|
35110
|
+
);
|
|
35111
|
+
if (switchedToScreenshotAfterCalibration && workerCount > 1) {
|
|
35112
|
+
workerCount = 1;
|
|
35113
|
+
}
|
|
35114
|
+
if (workerCount > 1 && probeSession) {
|
|
35115
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
35116
|
+
await closeCaptureSession(probeSession);
|
|
35117
|
+
probeSession = null;
|
|
35118
|
+
}
|
|
35119
|
+
const captureAttempts = [];
|
|
34691
35120
|
const FORMAT_EXT2 = {
|
|
34692
35121
|
mp4: ".mp4",
|
|
34693
35122
|
webm: ".webm",
|
|
@@ -35279,15 +35708,18 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35279
35708
|
perfStages.encodeMs = encodeResult.durationMs;
|
|
35280
35709
|
} else {
|
|
35281
35710
|
if (workerCount > 1) {
|
|
35282
|
-
const
|
|
35283
|
-
|
|
35284
|
-
fileServer.url,
|
|
35711
|
+
const attempts = await executeDiskCaptureWithAdaptiveRetry({
|
|
35712
|
+
serverUrl: fileServer.url,
|
|
35285
35713
|
workDir,
|
|
35286
|
-
|
|
35287
|
-
|
|
35288
|
-
|
|
35714
|
+
framesDir,
|
|
35715
|
+
totalFrames: job.totalFrames,
|
|
35716
|
+
initialWorkerCount: workerCount,
|
|
35717
|
+
allowRetry: job.config.workers === void 0,
|
|
35718
|
+
frameExt: needsAlpha ? "png" : "jpg",
|
|
35719
|
+
captureOptions: buildHdrCaptureOptions(),
|
|
35720
|
+
createBeforeCaptureHook: () => createVideoFrameInjector(frameLookup),
|
|
35289
35721
|
abortSignal,
|
|
35290
|
-
(progress) => {
|
|
35722
|
+
onProgress: (progress) => {
|
|
35291
35723
|
job.framesRendered = progress.capturedFrames;
|
|
35292
35724
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
35293
35725
|
const progressPct = 25 + frameProgress * 45;
|
|
@@ -35295,16 +35727,20 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35295
35727
|
updateJobStatus(
|
|
35296
35728
|
job,
|
|
35297
35729
|
"rendering",
|
|
35298
|
-
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${
|
|
35730
|
+
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${progress.activeWorkers} workers)`,
|
|
35299
35731
|
Math.round(progressPct),
|
|
35300
35732
|
onProgress
|
|
35301
35733
|
);
|
|
35302
35734
|
}
|
|
35303
35735
|
},
|
|
35304
|
-
|
|
35305
|
-
|
|
35306
|
-
);
|
|
35307
|
-
|
|
35736
|
+
cfg,
|
|
35737
|
+
log: log2
|
|
35738
|
+
});
|
|
35739
|
+
captureAttempts.push(...attempts);
|
|
35740
|
+
const lastAttempt = attempts[attempts.length - 1];
|
|
35741
|
+
if (lastAttempt) {
|
|
35742
|
+
workerCount = lastAttempt.workers;
|
|
35743
|
+
}
|
|
35308
35744
|
if (probeSession) {
|
|
35309
35745
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
35310
35746
|
await closeCaptureSession(probeSession);
|
|
@@ -35474,6 +35910,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35474
35910
|
stages: perfStages,
|
|
35475
35911
|
videoExtractBreakdown: extractionResult?.phaseBreakdown,
|
|
35476
35912
|
tmpPeakBytes,
|
|
35913
|
+
captureCalibration: captureCalibration ? {
|
|
35914
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex),
|
|
35915
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
35916
|
+
multiplier: captureCalibration.estimate.multiplier,
|
|
35917
|
+
reasons: captureCalibration.estimate.reasons
|
|
35918
|
+
} : void 0,
|
|
35919
|
+
captureAttempts: captureAttempts.length > 0 ? captureAttempts : void 0,
|
|
35477
35920
|
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0,
|
|
35478
35921
|
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0,
|
|
35479
35922
|
peakRssMb: Math.round(peakRssBytes / (1024 * 1024)),
|
|
@@ -35589,7 +36032,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35589
36032
|
clearInterval(memSamplerInterval);
|
|
35590
36033
|
}
|
|
35591
36034
|
}
|
|
35592
|
-
var RenderCancelledError, BROWSER_MEDIA_EPSILON;
|
|
36035
|
+
var RenderCancelledError, BROWSER_MEDIA_EPSILON, CAPTURE_CALIBRATION_TARGET_MS, MAX_MEASURED_CAPTURE_COST_MULTIPLIER, CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS;
|
|
35593
36036
|
var init_renderOrchestrator = __esm({
|
|
35594
36037
|
"../producer/src/services/renderOrchestrator.ts"() {
|
|
35595
36038
|
"use strict";
|
|
@@ -35610,6 +36053,9 @@ var init_renderOrchestrator = __esm({
|
|
|
35610
36053
|
}
|
|
35611
36054
|
};
|
|
35612
36055
|
BROWSER_MEDIA_EPSILON = 1e-4;
|
|
36056
|
+
CAPTURE_CALIBRATION_TARGET_MS = 600;
|
|
36057
|
+
MAX_MEASURED_CAPTURE_COST_MULTIPLIER = 8;
|
|
36058
|
+
CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS = 3e4;
|
|
35613
36059
|
}
|
|
35614
36060
|
});
|
|
35615
36061
|
|
|
@@ -36488,23 +36934,34 @@ function createStudioServer(options) {
|
|
|
36488
36934
|
await new Promise((r2) => setTimeout(r2, 200));
|
|
36489
36935
|
let clip;
|
|
36490
36936
|
if (opts.selector) {
|
|
36491
|
-
clip = await page.evaluate(
|
|
36492
|
-
|
|
36493
|
-
|
|
36494
|
-
|
|
36495
|
-
|
|
36496
|
-
|
|
36497
|
-
|
|
36498
|
-
|
|
36499
|
-
|
|
36500
|
-
|
|
36501
|
-
|
|
36502
|
-
|
|
36503
|
-
|
|
36504
|
-
|
|
36505
|
-
|
|
36506
|
-
|
|
36507
|
-
|
|
36937
|
+
clip = await page.evaluate(
|
|
36938
|
+
(selector, selectorIndex) => {
|
|
36939
|
+
const matches2 = Array.from(document.querySelectorAll(selector)).filter(
|
|
36940
|
+
(el2) => el2 instanceof HTMLElement
|
|
36941
|
+
);
|
|
36942
|
+
const safeIndex = Math.max(
|
|
36943
|
+
0,
|
|
36944
|
+
Math.min(matches2.length - 1, Math.floor(selectorIndex ?? 0))
|
|
36945
|
+
);
|
|
36946
|
+
const el = matches2[safeIndex] ?? null;
|
|
36947
|
+
if (!(el instanceof HTMLElement)) return void 0;
|
|
36948
|
+
const rect = el.getBoundingClientRect();
|
|
36949
|
+
if (rect.width < 4 || rect.height < 4) return void 0;
|
|
36950
|
+
const pad = 8;
|
|
36951
|
+
const x4 = Math.max(0, rect.left - pad);
|
|
36952
|
+
const y2 = Math.max(0, rect.top - pad);
|
|
36953
|
+
const maxWidth = window.innerWidth - x4;
|
|
36954
|
+
const maxHeight = window.innerHeight - y2;
|
|
36955
|
+
return {
|
|
36956
|
+
x: x4,
|
|
36957
|
+
y: y2,
|
|
36958
|
+
width: Math.max(1, Math.min(rect.width + pad * 2, maxWidth)),
|
|
36959
|
+
height: Math.max(1, Math.min(rect.height + pad * 2, maxHeight))
|
|
36960
|
+
};
|
|
36961
|
+
},
|
|
36962
|
+
opts.selector,
|
|
36963
|
+
opts.selectorIndex
|
|
36964
|
+
);
|
|
36508
36965
|
}
|
|
36509
36966
|
const screenshot = await page.screenshot({
|
|
36510
36967
|
type: "jpeg",
|
|
@@ -38631,13 +39088,13 @@ function buildDockerRunArgs(input) {
|
|
|
38631
39088
|
options.quality,
|
|
38632
39089
|
"--format",
|
|
38633
39090
|
options.format,
|
|
38634
|
-
"--workers",
|
|
38635
|
-
String(options.workers),
|
|
39091
|
+
...options.workers != null ? ["--workers", String(options.workers)] : [],
|
|
38636
39092
|
...options.crf != null ? ["--crf", String(options.crf)] : [],
|
|
38637
39093
|
...options.videoBitrate ? ["--video-bitrate", options.videoBitrate] : [],
|
|
38638
39094
|
...options.quiet ? ["--quiet"] : [],
|
|
38639
39095
|
...options.gpu ? ["--gpu"] : [],
|
|
38640
|
-
...options.hdr ? ["--hdr"] : []
|
|
39096
|
+
...options.hdrMode === "force-hdr" ? ["--hdr"] : [],
|
|
39097
|
+
...options.hdrMode === "force-sdr" ? ["--sdr"] : []
|
|
38641
39098
|
];
|
|
38642
39099
|
}
|
|
38643
39100
|
var init_dockerRunArgs = __esm({
|
|
@@ -38693,9 +39150,6 @@ import { mkdirSync as mkdirSync24, readFileSync as readFileSync29, statSync as s
|
|
|
38693
39150
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
|
|
38694
39151
|
import { resolve as resolve28, dirname as dirname16, join as join44, basename as basename9 } from "path";
|
|
38695
39152
|
import { execFileSync as execFileSync6, spawn as spawn11 } from "child_process";
|
|
38696
|
-
function defaultWorkerCount() {
|
|
38697
|
-
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
38698
|
-
}
|
|
38699
39153
|
function dockerImageTag(version) {
|
|
38700
39154
|
return `${DOCKER_IMAGE_PREFIX}:${version}`;
|
|
38701
39155
|
}
|
|
@@ -38787,7 +39241,7 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
38787
39241
|
format: options.format,
|
|
38788
39242
|
workers: options.workers,
|
|
38789
39243
|
gpu: options.gpu,
|
|
38790
|
-
|
|
39244
|
+
hdrMode: options.hdrMode,
|
|
38791
39245
|
crf: options.crf,
|
|
38792
39246
|
videoBitrate: options.videoBitrate,
|
|
38793
39247
|
quiet: options.quiet
|
|
@@ -38836,7 +39290,7 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
38836
39290
|
format: options.format,
|
|
38837
39291
|
workers: options.workers,
|
|
38838
39292
|
useGpu: options.gpu,
|
|
38839
|
-
|
|
39293
|
+
hdrMode: options.hdrMode,
|
|
38840
39294
|
crf: options.crf,
|
|
38841
39295
|
videoBitrate: options.videoBitrate
|
|
38842
39296
|
});
|
|
@@ -38883,7 +39337,7 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
38883
39337
|
durationMs: elapsedMs,
|
|
38884
39338
|
fps: options.fps,
|
|
38885
39339
|
quality: options.quality,
|
|
38886
|
-
workers: options.workers,
|
|
39340
|
+
workers: options.workers ?? perf?.workers,
|
|
38887
39341
|
docker,
|
|
38888
39342
|
gpu: options.gpu,
|
|
38889
39343
|
compositionDurationMs,
|
|
@@ -38949,7 +39403,7 @@ var init_render2 = __esm({
|
|
|
38949
39403
|
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
|
38950
39404
|
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
|
38951
39405
|
["Parallel rendering with 6 workers", "hyperframes render --workers 6 --output fast.mp4"],
|
|
38952
|
-
["HDR output (
|
|
39406
|
+
["HDR output (auto-detected)", "hyperframes render --output hdr-output.mp4"]
|
|
38953
39407
|
];
|
|
38954
39408
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
38955
39409
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
@@ -39001,7 +39455,12 @@ var init_render2 = __esm({
|
|
|
39001
39455
|
},
|
|
39002
39456
|
hdr: {
|
|
39003
39457
|
type: "boolean",
|
|
39004
|
-
description: "
|
|
39458
|
+
description: "Force HDR output even if no HDR sources are detected",
|
|
39459
|
+
default: false
|
|
39460
|
+
},
|
|
39461
|
+
sdr: {
|
|
39462
|
+
type: "boolean",
|
|
39463
|
+
description: "Force SDR output even if HDR sources are detected",
|
|
39005
39464
|
default: false
|
|
39006
39465
|
},
|
|
39007
39466
|
crf: {
|
|
@@ -39107,9 +39566,8 @@ var init_render2 = __esm({
|
|
|
39107
39566
|
);
|
|
39108
39567
|
process.exit(1);
|
|
39109
39568
|
}
|
|
39110
|
-
const workerCount = workers ?? defaultWorkerCount();
|
|
39111
39569
|
if (!quiet) {
|
|
39112
|
-
const workerLabel =
|
|
39570
|
+
const workerLabel = workers != null ? `${workers} workers` : `auto workers (${CPU_CORE_COUNT} cores detected)`;
|
|
39113
39571
|
console.log("");
|
|
39114
39572
|
console.log(
|
|
39115
39573
|
c.accent("\u25C6") + " Rendering " + c.accent(project.name) + c.dim(" \u2192 " + outputPath)
|
|
@@ -39177,14 +39635,18 @@ var init_render2 = __esm({
|
|
|
39177
39635
|
console.log("");
|
|
39178
39636
|
}
|
|
39179
39637
|
}
|
|
39638
|
+
if (args.hdr && args.sdr) {
|
|
39639
|
+
console.error("Error: --hdr and --sdr are mutually exclusive.");
|
|
39640
|
+
process.exit(1);
|
|
39641
|
+
}
|
|
39180
39642
|
if (useDocker) {
|
|
39181
39643
|
await renderDocker(project.dir, outputPath, {
|
|
39182
39644
|
fps,
|
|
39183
39645
|
quality,
|
|
39184
39646
|
format,
|
|
39185
|
-
workers
|
|
39647
|
+
workers,
|
|
39186
39648
|
gpu: useGpu,
|
|
39187
|
-
|
|
39649
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
39188
39650
|
crf,
|
|
39189
39651
|
videoBitrate,
|
|
39190
39652
|
quiet
|
|
@@ -39194,9 +39656,9 @@ var init_render2 = __esm({
|
|
|
39194
39656
|
fps,
|
|
39195
39657
|
quality,
|
|
39196
39658
|
format,
|
|
39197
|
-
workers
|
|
39659
|
+
workers,
|
|
39198
39660
|
gpu: useGpu,
|
|
39199
|
-
|
|
39661
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
39200
39662
|
crf,
|
|
39201
39663
|
videoBitrate,
|
|
39202
39664
|
quiet,
|