hyperframes 0.5.0-alpha.6 → 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 +500 -52
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.5.0-alpha.
|
|
57
|
+
VERSION = true ? "0.5.0-alpha.7" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -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) });
|
|
@@ -29126,6 +29129,7 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
29126
29129
|
const effectiveCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG2.coresPerWorker;
|
|
29127
29130
|
const effectiveMinParallelFrames = config?.minParallelFrames ?? DEFAULT_CONFIG2.minParallelFrames;
|
|
29128
29131
|
const effectiveLargeRenderThreshold = config?.largeRenderThreshold ?? DEFAULT_CONFIG2.largeRenderThreshold;
|
|
29132
|
+
const captureCostMultiplier = Math.max(1, config?.captureCostMultiplier ?? 1);
|
|
29129
29133
|
if (requested !== void 0) {
|
|
29130
29134
|
return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
|
|
29131
29135
|
}
|
|
@@ -29138,8 +29142,14 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
29138
29142
|
const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
|
|
29139
29143
|
const minWorkersForJob = totalFrames >= effectiveMinParallelFrames ? 2 : MIN_WORKERS;
|
|
29140
29144
|
let finalWorkers = Math.max(minWorkersForJob, Math.min(effectiveMaxWorkers, optimal));
|
|
29141
|
-
|
|
29142
|
-
|
|
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));
|
|
29143
29153
|
if (finalWorkers > cpuScaledMax) {
|
|
29144
29154
|
finalWorkers = cpuScaledMax;
|
|
29145
29155
|
}
|
|
@@ -32899,6 +32909,17 @@ function detectRenderModeHints(html) {
|
|
|
32899
32909
|
reasons
|
|
32900
32910
|
};
|
|
32901
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
|
+
}
|
|
32902
32923
|
async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
|
|
32903
32924
|
let filePath = src;
|
|
32904
32925
|
if (isHttpUrl(src)) {
|
|
@@ -33435,6 +33456,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33435
33456
|
"$1"
|
|
33436
33457
|
);
|
|
33437
33458
|
const renderModeHints = detectRenderModeHints(sanitizedHtml);
|
|
33459
|
+
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
33438
33460
|
const coalescedHtml = await injectDeterministicFontFaces(
|
|
33439
33461
|
coalesceHeadStylesAndBodyScripts2(promoteCssImportsToLinkTags(sanitizedHtml))
|
|
33440
33462
|
);
|
|
@@ -33482,7 +33504,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33482
33504
|
width,
|
|
33483
33505
|
height,
|
|
33484
33506
|
staticDuration,
|
|
33485
|
-
renderModeHints
|
|
33507
|
+
renderModeHints,
|
|
33508
|
+
hasShaderTransitions
|
|
33486
33509
|
};
|
|
33487
33510
|
}
|
|
33488
33511
|
async function discoverMediaFromBrowser(page) {
|
|
@@ -33584,10 +33607,11 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
33584
33607
|
audios,
|
|
33585
33608
|
images,
|
|
33586
33609
|
unresolvedCompositions: remaining,
|
|
33587
|
-
renderModeHints: compiled.renderModeHints
|
|
33610
|
+
renderModeHints: compiled.renderModeHints,
|
|
33611
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33588
33612
|
};
|
|
33589
33613
|
}
|
|
33590
|
-
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;
|
|
33591
33615
|
var init_htmlCompiler2 = __esm({
|
|
33592
33616
|
"../producer/src/services/htmlCompiler.ts"() {
|
|
33593
33617
|
"use strict";
|
|
@@ -33602,6 +33626,7 @@ var init_htmlCompiler2 = __esm({
|
|
|
33602
33626
|
INLINE_SCRIPT_PATTERN = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
33603
33627
|
COMPILER_MOUNT_BLOCK_START = "/* __HF_COMPILER_MOUNT_START__ */";
|
|
33604
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*=/;
|
|
33605
33630
|
}
|
|
33606
33631
|
});
|
|
33607
33632
|
|
|
@@ -33905,7 +33930,8 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
33905
33930
|
mediaStart: a.mediaStart
|
|
33906
33931
|
})),
|
|
33907
33932
|
subCompositions: Array.from(compiled.subCompositions.keys()),
|
|
33908
|
-
renderModeHints: compiled.renderModeHints
|
|
33933
|
+
renderModeHints: compiled.renderModeHints,
|
|
33934
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33909
33935
|
};
|
|
33910
33936
|
writeFileSync14(join36(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
|
|
33911
33937
|
}
|
|
@@ -33918,6 +33944,287 @@ function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
|
|
|
33918
33944
|
reasons: compiled.renderModeHints.reasons.map((reason) => reason.message)
|
|
33919
33945
|
});
|
|
33920
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
|
+
}
|
|
33921
34228
|
function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, width, height, log2, sourceTransfer, targetTransfer) {
|
|
33922
34229
|
const frameDir = hdrFrameDirs.get(el.id);
|
|
33923
34230
|
const startTime = hdrStartTimes.get(el.id);
|
|
@@ -34544,7 +34851,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34544
34851
|
let extractionResult = null;
|
|
34545
34852
|
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
34546
34853
|
const videoTransfers = /* @__PURE__ */ new Map();
|
|
34547
|
-
if (job.config.
|
|
34854
|
+
if (job.config.hdrMode !== "force-sdr" && composition.videos.length > 0) {
|
|
34548
34855
|
await Promise.all(
|
|
34549
34856
|
composition.videos.map(async (v) => {
|
|
34550
34857
|
let videoPath = v.src;
|
|
@@ -34565,7 +34872,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34565
34872
|
const imageTransfers = /* @__PURE__ */ new Map();
|
|
34566
34873
|
const hdrImageSrcPaths = /* @__PURE__ */ new Map();
|
|
34567
34874
|
const imageColorSpaces = [];
|
|
34568
|
-
if (job.config.
|
|
34875
|
+
if (job.config.hdrMode !== "force-sdr" && composition.images.length > 0) {
|
|
34569
34876
|
const probed = await Promise.all(
|
|
34570
34877
|
composition.images.map(async (img) => {
|
|
34571
34878
|
let imgPath = img.src;
|
|
@@ -34622,28 +34929,53 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34622
34929
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
34623
34930
|
}
|
|
34624
34931
|
let effectiveHdr;
|
|
34625
|
-
|
|
34932
|
+
let forcedHdrWithoutSources = false;
|
|
34933
|
+
{
|
|
34934
|
+
const hdrMode = job.config.hdrMode ?? "auto";
|
|
34626
34935
|
const videoColorSpaces = (extractionResult?.extracted ?? []).map(
|
|
34627
34936
|
(ext) => ext.metadata.colorSpace
|
|
34628
34937
|
);
|
|
34629
34938
|
const allColorSpaces = [...videoColorSpaces, ...imageColorSpaces];
|
|
34630
|
-
|
|
34631
|
-
|
|
34632
|
-
|
|
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) {
|
|
34633
34951
|
effectiveHdr = { transfer: info.dominantTransfer };
|
|
34634
34952
|
}
|
|
34635
34953
|
}
|
|
34636
34954
|
}
|
|
34637
34955
|
if (effectiveHdr && outputFormat !== "mp4") {
|
|
34956
|
+
const hdrSourceReason = forcedHdrWithoutSources ? "HDR was forced without detected HDR sources" : "HDR source detected";
|
|
34638
34957
|
log2.warn(
|
|
34639
|
-
`[Render]
|
|
34958
|
+
`[Render] ${hdrSourceReason}, but format is "${outputFormat}" \u2014 falling back to SDR. HDR + alpha is not supported. Use --format mp4 for HDR10 output.`
|
|
34640
34959
|
);
|
|
34641
34960
|
effectiveHdr = void 0;
|
|
34642
34961
|
}
|
|
34643
|
-
|
|
34644
|
-
|
|
34645
|
-
|
|
34646
|
-
|
|
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
|
+
}
|
|
34647
34979
|
}
|
|
34648
34980
|
const stage3Start = Date.now();
|
|
34649
34981
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
@@ -34690,7 +35022,101 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34690
35022
|
...captureOptions,
|
|
34691
35023
|
skipReadinessVideoIds: Array.from(nativeHdrVideoIds)
|
|
34692
35024
|
});
|
|
34693
|
-
|
|
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 = [];
|
|
34694
35120
|
const FORMAT_EXT2 = {
|
|
34695
35121
|
mp4: ".mp4",
|
|
34696
35122
|
webm: ".webm",
|
|
@@ -35282,15 +35708,18 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35282
35708
|
perfStages.encodeMs = encodeResult.durationMs;
|
|
35283
35709
|
} else {
|
|
35284
35710
|
if (workerCount > 1) {
|
|
35285
|
-
const
|
|
35286
|
-
|
|
35287
|
-
fileServer.url,
|
|
35711
|
+
const attempts = await executeDiskCaptureWithAdaptiveRetry({
|
|
35712
|
+
serverUrl: fileServer.url,
|
|
35288
35713
|
workDir,
|
|
35289
|
-
|
|
35290
|
-
|
|
35291
|
-
|
|
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),
|
|
35292
35721
|
abortSignal,
|
|
35293
|
-
(progress) => {
|
|
35722
|
+
onProgress: (progress) => {
|
|
35294
35723
|
job.framesRendered = progress.capturedFrames;
|
|
35295
35724
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
35296
35725
|
const progressPct = 25 + frameProgress * 45;
|
|
@@ -35298,16 +35727,20 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35298
35727
|
updateJobStatus(
|
|
35299
35728
|
job,
|
|
35300
35729
|
"rendering",
|
|
35301
|
-
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${
|
|
35730
|
+
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${progress.activeWorkers} workers)`,
|
|
35302
35731
|
Math.round(progressPct),
|
|
35303
35732
|
onProgress
|
|
35304
35733
|
);
|
|
35305
35734
|
}
|
|
35306
35735
|
},
|
|
35307
|
-
|
|
35308
|
-
|
|
35309
|
-
);
|
|
35310
|
-
|
|
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
|
+
}
|
|
35311
35744
|
if (probeSession) {
|
|
35312
35745
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
35313
35746
|
await closeCaptureSession(probeSession);
|
|
@@ -35477,6 +35910,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35477
35910
|
stages: perfStages,
|
|
35478
35911
|
videoExtractBreakdown: extractionResult?.phaseBreakdown,
|
|
35479
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,
|
|
35480
35920
|
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0,
|
|
35481
35921
|
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0,
|
|
35482
35922
|
peakRssMb: Math.round(peakRssBytes / (1024 * 1024)),
|
|
@@ -35592,7 +36032,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35592
36032
|
clearInterval(memSamplerInterval);
|
|
35593
36033
|
}
|
|
35594
36034
|
}
|
|
35595
|
-
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;
|
|
35596
36036
|
var init_renderOrchestrator = __esm({
|
|
35597
36037
|
"../producer/src/services/renderOrchestrator.ts"() {
|
|
35598
36038
|
"use strict";
|
|
@@ -35613,6 +36053,9 @@ var init_renderOrchestrator = __esm({
|
|
|
35613
36053
|
}
|
|
35614
36054
|
};
|
|
35615
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;
|
|
35616
36059
|
}
|
|
35617
36060
|
});
|
|
35618
36061
|
|
|
@@ -38645,13 +39088,13 @@ function buildDockerRunArgs(input) {
|
|
|
38645
39088
|
options.quality,
|
|
38646
39089
|
"--format",
|
|
38647
39090
|
options.format,
|
|
38648
|
-
"--workers",
|
|
38649
|
-
String(options.workers),
|
|
39091
|
+
...options.workers != null ? ["--workers", String(options.workers)] : [],
|
|
38650
39092
|
...options.crf != null ? ["--crf", String(options.crf)] : [],
|
|
38651
39093
|
...options.videoBitrate ? ["--video-bitrate", options.videoBitrate] : [],
|
|
38652
39094
|
...options.quiet ? ["--quiet"] : [],
|
|
38653
39095
|
...options.gpu ? ["--gpu"] : [],
|
|
38654
|
-
...options.hdr ? ["--hdr"] : []
|
|
39096
|
+
...options.hdrMode === "force-hdr" ? ["--hdr"] : [],
|
|
39097
|
+
...options.hdrMode === "force-sdr" ? ["--sdr"] : []
|
|
38655
39098
|
];
|
|
38656
39099
|
}
|
|
38657
39100
|
var init_dockerRunArgs = __esm({
|
|
@@ -38707,9 +39150,6 @@ import { mkdirSync as mkdirSync24, readFileSync as readFileSync29, statSync as s
|
|
|
38707
39150
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
|
|
38708
39151
|
import { resolve as resolve28, dirname as dirname16, join as join44, basename as basename9 } from "path";
|
|
38709
39152
|
import { execFileSync as execFileSync6, spawn as spawn11 } from "child_process";
|
|
38710
|
-
function defaultWorkerCount() {
|
|
38711
|
-
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
38712
|
-
}
|
|
38713
39153
|
function dockerImageTag(version) {
|
|
38714
39154
|
return `${DOCKER_IMAGE_PREFIX}:${version}`;
|
|
38715
39155
|
}
|
|
@@ -38801,7 +39241,7 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
38801
39241
|
format: options.format,
|
|
38802
39242
|
workers: options.workers,
|
|
38803
39243
|
gpu: options.gpu,
|
|
38804
|
-
|
|
39244
|
+
hdrMode: options.hdrMode,
|
|
38805
39245
|
crf: options.crf,
|
|
38806
39246
|
videoBitrate: options.videoBitrate,
|
|
38807
39247
|
quiet: options.quiet
|
|
@@ -38850,7 +39290,7 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
38850
39290
|
format: options.format,
|
|
38851
39291
|
workers: options.workers,
|
|
38852
39292
|
useGpu: options.gpu,
|
|
38853
|
-
|
|
39293
|
+
hdrMode: options.hdrMode,
|
|
38854
39294
|
crf: options.crf,
|
|
38855
39295
|
videoBitrate: options.videoBitrate
|
|
38856
39296
|
});
|
|
@@ -38897,7 +39337,7 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
38897
39337
|
durationMs: elapsedMs,
|
|
38898
39338
|
fps: options.fps,
|
|
38899
39339
|
quality: options.quality,
|
|
38900
|
-
workers: options.workers,
|
|
39340
|
+
workers: options.workers ?? perf?.workers,
|
|
38901
39341
|
docker,
|
|
38902
39342
|
gpu: options.gpu,
|
|
38903
39343
|
compositionDurationMs,
|
|
@@ -38963,7 +39403,7 @@ var init_render2 = __esm({
|
|
|
38963
39403
|
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
|
38964
39404
|
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
|
38965
39405
|
["Parallel rendering with 6 workers", "hyperframes render --workers 6 --output fast.mp4"],
|
|
38966
|
-
["HDR output (
|
|
39406
|
+
["HDR output (auto-detected)", "hyperframes render --output hdr-output.mp4"]
|
|
38967
39407
|
];
|
|
38968
39408
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
38969
39409
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
@@ -39015,7 +39455,12 @@ var init_render2 = __esm({
|
|
|
39015
39455
|
},
|
|
39016
39456
|
hdr: {
|
|
39017
39457
|
type: "boolean",
|
|
39018
|
-
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",
|
|
39019
39464
|
default: false
|
|
39020
39465
|
},
|
|
39021
39466
|
crf: {
|
|
@@ -39121,9 +39566,8 @@ var init_render2 = __esm({
|
|
|
39121
39566
|
);
|
|
39122
39567
|
process.exit(1);
|
|
39123
39568
|
}
|
|
39124
|
-
const workerCount = workers ?? defaultWorkerCount();
|
|
39125
39569
|
if (!quiet) {
|
|
39126
|
-
const workerLabel =
|
|
39570
|
+
const workerLabel = workers != null ? `${workers} workers` : `auto workers (${CPU_CORE_COUNT} cores detected)`;
|
|
39127
39571
|
console.log("");
|
|
39128
39572
|
console.log(
|
|
39129
39573
|
c.accent("\u25C6") + " Rendering " + c.accent(project.name) + c.dim(" \u2192 " + outputPath)
|
|
@@ -39191,14 +39635,18 @@ var init_render2 = __esm({
|
|
|
39191
39635
|
console.log("");
|
|
39192
39636
|
}
|
|
39193
39637
|
}
|
|
39638
|
+
if (args.hdr && args.sdr) {
|
|
39639
|
+
console.error("Error: --hdr and --sdr are mutually exclusive.");
|
|
39640
|
+
process.exit(1);
|
|
39641
|
+
}
|
|
39194
39642
|
if (useDocker) {
|
|
39195
39643
|
await renderDocker(project.dir, outputPath, {
|
|
39196
39644
|
fps,
|
|
39197
39645
|
quality,
|
|
39198
39646
|
format,
|
|
39199
|
-
workers
|
|
39647
|
+
workers,
|
|
39200
39648
|
gpu: useGpu,
|
|
39201
|
-
|
|
39649
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
39202
39650
|
crf,
|
|
39203
39651
|
videoBitrate,
|
|
39204
39652
|
quiet
|
|
@@ -39208,9 +39656,9 @@ var init_render2 = __esm({
|
|
|
39208
39656
|
fps,
|
|
39209
39657
|
quality,
|
|
39210
39658
|
format,
|
|
39211
|
-
workers
|
|
39659
|
+
workers,
|
|
39212
39660
|
gpu: useGpu,
|
|
39213
|
-
|
|
39661
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
39214
39662
|
crf,
|
|
39215
39663
|
videoBitrate,
|
|
39216
39664
|
quiet,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperframes",
|
|
3
|
-
"version": "0.5.0-alpha.
|
|
3
|
+
"version": "0.5.0-alpha.7",
|
|
4
4
|
"description": "HyperFrames CLI — create, preview, and render HTML video compositions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vitest": "^3.2.4"
|
|
56
56
|
},
|
|
57
57
|
"optionalDependencies": {
|
|
58
|
-
"@google/genai": "^1.50.
|
|
58
|
+
"@google/genai": "^1.50.1"
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=22"
|