hyperframes 0.4.32 → 0.4.33
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.4.
|
|
57
|
+
VERSION = true ? "0.4.33" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -9478,9 +9478,12 @@ function readCache(path2) {
|
|
|
9478
9478
|
}
|
|
9479
9479
|
}
|
|
9480
9480
|
function writeCache(path2, data) {
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9481
|
+
try {
|
|
9482
|
+
mkdirSync(dirname3(path2), { recursive: true });
|
|
9483
|
+
const entry = { fetchedAt: Date.now(), data };
|
|
9484
|
+
writeFileSync(path2, JSON.stringify(entry), "utf-8");
|
|
9485
|
+
} catch {
|
|
9486
|
+
}
|
|
9484
9487
|
}
|
|
9485
9488
|
async function fetchJson(url) {
|
|
9486
9489
|
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
@@ -28885,6 +28888,7 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
28885
28888
|
const effectiveCoresPerWorker = config?.coresPerWorker ?? DEFAULT_CONFIG2.coresPerWorker;
|
|
28886
28889
|
const effectiveMinParallelFrames = config?.minParallelFrames ?? DEFAULT_CONFIG2.minParallelFrames;
|
|
28887
28890
|
const effectiveLargeRenderThreshold = config?.largeRenderThreshold ?? DEFAULT_CONFIG2.largeRenderThreshold;
|
|
28891
|
+
const captureCostMultiplier = Math.max(1, config?.captureCostMultiplier ?? 1);
|
|
28888
28892
|
if (requested !== void 0) {
|
|
28889
28893
|
return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
|
|
28890
28894
|
}
|
|
@@ -28897,8 +28901,14 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
28897
28901
|
const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
|
|
28898
28902
|
const minWorkersForJob = totalFrames >= effectiveMinParallelFrames ? 2 : MIN_WORKERS;
|
|
28899
28903
|
let finalWorkers = Math.max(minWorkersForJob, Math.min(effectiveMaxWorkers, optimal));
|
|
28900
|
-
|
|
28901
|
-
|
|
28904
|
+
const weightedFrames = totalFrames * captureCostMultiplier;
|
|
28905
|
+
const contentionThreshold = Math.max(
|
|
28906
|
+
effectiveMinParallelFrames,
|
|
28907
|
+
Math.floor(effectiveLargeRenderThreshold / 3)
|
|
28908
|
+
);
|
|
28909
|
+
if (totalFrames >= effectiveLargeRenderThreshold || weightedFrames >= contentionThreshold) {
|
|
28910
|
+
const weightedCoresPerWorker = effectiveCoresPerWorker * captureCostMultiplier;
|
|
28911
|
+
const cpuScaledMax = Math.max(MIN_WORKERS, Math.floor(cpuCount / weightedCoresPerWorker));
|
|
28902
28912
|
if (finalWorkers > cpuScaledMax) {
|
|
28903
28913
|
finalWorkers = cpuScaledMax;
|
|
28904
28914
|
}
|
|
@@ -32649,6 +32659,17 @@ function detectRenderModeHints(html) {
|
|
|
32649
32659
|
reasons
|
|
32650
32660
|
};
|
|
32651
32661
|
}
|
|
32662
|
+
function detectShaderTransitionUsage(html) {
|
|
32663
|
+
let scriptMatch;
|
|
32664
|
+
const scriptPattern = new RegExp(INLINE_SCRIPT_PATTERN.source, INLINE_SCRIPT_PATTERN.flags);
|
|
32665
|
+
while ((scriptMatch = scriptPattern.exec(html)) !== null) {
|
|
32666
|
+
const attrs = scriptMatch[1] || "";
|
|
32667
|
+
if (/\bsrc\s*=/i.test(attrs)) continue;
|
|
32668
|
+
const content = stripJsComments(stripCompilerMountBootstrap(scriptMatch[2] || ""));
|
|
32669
|
+
if (SHADER_TRANSITION_USAGE_PATTERN.test(content)) return true;
|
|
32670
|
+
}
|
|
32671
|
+
return false;
|
|
32672
|
+
}
|
|
32652
32673
|
async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
|
|
32653
32674
|
let filePath = src;
|
|
32654
32675
|
if (isHttpUrl(src)) {
|
|
@@ -33185,6 +33206,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33185
33206
|
"$1"
|
|
33186
33207
|
);
|
|
33187
33208
|
const renderModeHints = detectRenderModeHints(sanitizedHtml);
|
|
33209
|
+
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
33188
33210
|
const coalescedHtml = await injectDeterministicFontFaces(
|
|
33189
33211
|
coalesceHeadStylesAndBodyScripts2(promoteCssImportsToLinkTags(sanitizedHtml))
|
|
33190
33212
|
);
|
|
@@ -33232,7 +33254,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
|
33232
33254
|
width,
|
|
33233
33255
|
height,
|
|
33234
33256
|
staticDuration,
|
|
33235
|
-
renderModeHints
|
|
33257
|
+
renderModeHints,
|
|
33258
|
+
hasShaderTransitions
|
|
33236
33259
|
};
|
|
33237
33260
|
}
|
|
33238
33261
|
async function discoverMediaFromBrowser(page) {
|
|
@@ -33334,10 +33357,11 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
33334
33357
|
audios,
|
|
33335
33358
|
images,
|
|
33336
33359
|
unresolvedCompositions: remaining,
|
|
33337
|
-
renderModeHints: compiled.renderModeHints
|
|
33360
|
+
renderModeHints: compiled.renderModeHints,
|
|
33361
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33338
33362
|
};
|
|
33339
33363
|
}
|
|
33340
|
-
var INLINE_SCRIPT_PATTERN, COMPILER_MOUNT_BLOCK_START, COMPILER_MOUNT_BLOCK_END;
|
|
33364
|
+
var INLINE_SCRIPT_PATTERN, COMPILER_MOUNT_BLOCK_START, COMPILER_MOUNT_BLOCK_END, SHADER_TRANSITION_USAGE_PATTERN;
|
|
33341
33365
|
var init_htmlCompiler2 = __esm({
|
|
33342
33366
|
"../producer/src/services/htmlCompiler.ts"() {
|
|
33343
33367
|
"use strict";
|
|
@@ -33352,6 +33376,7 @@ var init_htmlCompiler2 = __esm({
|
|
|
33352
33376
|
INLINE_SCRIPT_PATTERN = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
33353
33377
|
COMPILER_MOUNT_BLOCK_START = "/* __HF_COMPILER_MOUNT_START__ */";
|
|
33354
33378
|
COMPILER_MOUNT_BLOCK_END = "/* __HF_COMPILER_MOUNT_END__ */";
|
|
33379
|
+
SHADER_TRANSITION_USAGE_PATTERN = /\b(?:(?:window|globalThis)\s*\.\s*)?HyperShader\s*\.\s*init\s*\(|\b__hf\s*\.\s*transitions\s*=/;
|
|
33355
33380
|
}
|
|
33356
33381
|
});
|
|
33357
33382
|
|
|
@@ -33655,7 +33680,8 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
33655
33680
|
mediaStart: a.mediaStart
|
|
33656
33681
|
})),
|
|
33657
33682
|
subCompositions: Array.from(compiled.subCompositions.keys()),
|
|
33658
|
-
renderModeHints: compiled.renderModeHints
|
|
33683
|
+
renderModeHints: compiled.renderModeHints,
|
|
33684
|
+
hasShaderTransitions: compiled.hasShaderTransitions
|
|
33659
33685
|
};
|
|
33660
33686
|
writeFileSync14(join35(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
|
|
33661
33687
|
}
|
|
@@ -33668,6 +33694,287 @@ function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
|
|
|
33668
33694
|
reasons: compiled.renderModeHints.reasons.map((reason) => reason.message)
|
|
33669
33695
|
});
|
|
33670
33696
|
}
|
|
33697
|
+
function resolveRenderWorkerCount(totalFrames, requestedWorkers, cfg, compiled, composition, log2 = defaultLogger, measuredCaptureCost) {
|
|
33698
|
+
const captureCost = combineCaptureCostEstimates(
|
|
33699
|
+
estimateCaptureCostMultiplier(compiled, composition),
|
|
33700
|
+
measuredCaptureCost
|
|
33701
|
+
);
|
|
33702
|
+
const workerCount = calculateOptimalWorkers(totalFrames, requestedWorkers, {
|
|
33703
|
+
...cfg,
|
|
33704
|
+
captureCostMultiplier: captureCost.multiplier
|
|
33705
|
+
});
|
|
33706
|
+
if (requestedWorkers !== void 0 || captureCost.multiplier <= 1) {
|
|
33707
|
+
return workerCount;
|
|
33708
|
+
}
|
|
33709
|
+
const baselineWorkers = calculateOptimalWorkers(totalFrames, void 0, cfg);
|
|
33710
|
+
if (workerCount < baselineWorkers) {
|
|
33711
|
+
log2.warn(
|
|
33712
|
+
"[Render] Reduced auto worker count for high-cost capture workload to avoid Chrome compositor starvation.",
|
|
33713
|
+
{
|
|
33714
|
+
from: baselineWorkers,
|
|
33715
|
+
to: workerCount,
|
|
33716
|
+
costMultiplier: captureCost.multiplier,
|
|
33717
|
+
reasons: captureCost.reasons
|
|
33718
|
+
}
|
|
33719
|
+
);
|
|
33720
|
+
}
|
|
33721
|
+
return workerCount;
|
|
33722
|
+
}
|
|
33723
|
+
function estimateCaptureCostMultiplier(compiled, composition) {
|
|
33724
|
+
let multiplier = 1;
|
|
33725
|
+
const reasons = [];
|
|
33726
|
+
if (compiled.hasShaderTransitions) {
|
|
33727
|
+
multiplier += 2;
|
|
33728
|
+
reasons.push("shader-transitions");
|
|
33729
|
+
}
|
|
33730
|
+
const reasonCodes = new Set(compiled.renderModeHints.reasons.map((reason) => reason.code));
|
|
33731
|
+
if (reasonCodes.has("requestAnimationFrame")) {
|
|
33732
|
+
multiplier += 1;
|
|
33733
|
+
reasons.push("requestAnimationFrame");
|
|
33734
|
+
}
|
|
33735
|
+
if (reasonCodes.has("iframe")) {
|
|
33736
|
+
multiplier += 0.5;
|
|
33737
|
+
reasons.push("iframe");
|
|
33738
|
+
}
|
|
33739
|
+
if (composition.videos.length > 0) {
|
|
33740
|
+
multiplier += Math.min(2, composition.videos.length * 0.75);
|
|
33741
|
+
reasons.push(`${composition.videos.length} video${composition.videos.length === 1 ? "" : "s"}`);
|
|
33742
|
+
}
|
|
33743
|
+
if (composition.audios.length > 0) {
|
|
33744
|
+
multiplier += Math.min(1, composition.audios.length * 0.75);
|
|
33745
|
+
reasons.push(`${composition.audios.length} audio${composition.audios.length === 1 ? "" : "s"}`);
|
|
33746
|
+
}
|
|
33747
|
+
return {
|
|
33748
|
+
multiplier: Math.round(multiplier * 100) / 100,
|
|
33749
|
+
reasons
|
|
33750
|
+
};
|
|
33751
|
+
}
|
|
33752
|
+
function combineCaptureCostEstimates(staticCost, measuredCost) {
|
|
33753
|
+
if (!measuredCost || measuredCost.multiplier <= 1) return staticCost;
|
|
33754
|
+
if (staticCost.multiplier >= measuredCost.multiplier) {
|
|
33755
|
+
return {
|
|
33756
|
+
multiplier: staticCost.multiplier,
|
|
33757
|
+
reasons: [...staticCost.reasons, ...measuredCost.reasons],
|
|
33758
|
+
p95Ms: measuredCost.p95Ms
|
|
33759
|
+
};
|
|
33760
|
+
}
|
|
33761
|
+
return {
|
|
33762
|
+
multiplier: measuredCost.multiplier,
|
|
33763
|
+
reasons: [...measuredCost.reasons, ...staticCost.reasons],
|
|
33764
|
+
p95Ms: measuredCost.p95Ms
|
|
33765
|
+
};
|
|
33766
|
+
}
|
|
33767
|
+
function createCaptureCalibrationConfig(cfg) {
|
|
33768
|
+
return {
|
|
33769
|
+
...cfg,
|
|
33770
|
+
protocolTimeout: Math.min(cfg.protocolTimeout, CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS)
|
|
33771
|
+
};
|
|
33772
|
+
}
|
|
33773
|
+
function estimateMeasuredCaptureCostMultiplier(samples) {
|
|
33774
|
+
if (samples.length === 0) {
|
|
33775
|
+
return { multiplier: 1, reasons: [] };
|
|
33776
|
+
}
|
|
33777
|
+
const sorted = [...samples].sort((a, b) => a.captureTimeMs - b.captureTimeMs);
|
|
33778
|
+
const p95Index = Math.max(0, Math.ceil(sorted.length * 0.95) - 1);
|
|
33779
|
+
const p95Sample = sorted[p95Index] ?? sorted[sorted.length - 1];
|
|
33780
|
+
if (!p95Sample) {
|
|
33781
|
+
return { multiplier: 1, reasons: [] };
|
|
33782
|
+
}
|
|
33783
|
+
const p95Ms = Math.round(p95Sample.captureTimeMs);
|
|
33784
|
+
const multiplier = Math.min(
|
|
33785
|
+
MAX_MEASURED_CAPTURE_COST_MULTIPLIER,
|
|
33786
|
+
Math.max(1, Math.round(p95Ms / CAPTURE_CALIBRATION_TARGET_MS * 100) / 100)
|
|
33787
|
+
);
|
|
33788
|
+
return {
|
|
33789
|
+
multiplier,
|
|
33790
|
+
reasons: multiplier > 1 ? [`calibration-p95=${p95Ms}ms`] : [],
|
|
33791
|
+
p95Ms
|
|
33792
|
+
};
|
|
33793
|
+
}
|
|
33794
|
+
function selectCaptureCalibrationFrames(totalFrames) {
|
|
33795
|
+
if (totalFrames <= 0) return [];
|
|
33796
|
+
const lastFrame = totalFrames - 1;
|
|
33797
|
+
const candidates = [
|
|
33798
|
+
0,
|
|
33799
|
+
Math.floor(totalFrames * 0.25),
|
|
33800
|
+
Math.floor(totalFrames * 0.5),
|
|
33801
|
+
Math.floor(totalFrames * 0.75),
|
|
33802
|
+
lastFrame
|
|
33803
|
+
];
|
|
33804
|
+
return Array.from(
|
|
33805
|
+
new Set(candidates.map((frame) => Math.max(0, Math.min(lastFrame, frame))))
|
|
33806
|
+
).sort((a, b) => a - b);
|
|
33807
|
+
}
|
|
33808
|
+
function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
33809
|
+
const ranges = [];
|
|
33810
|
+
let rangeStart = null;
|
|
33811
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
33812
|
+
const framePath = join35(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
|
|
33813
|
+
const missing = !existsSync33(framePath);
|
|
33814
|
+
if (missing && rangeStart === null) {
|
|
33815
|
+
rangeStart = frameIndex;
|
|
33816
|
+
} else if (!missing && rangeStart !== null) {
|
|
33817
|
+
ranges.push({ startFrame: rangeStart, endFrame: frameIndex });
|
|
33818
|
+
rangeStart = null;
|
|
33819
|
+
}
|
|
33820
|
+
}
|
|
33821
|
+
if (rangeStart !== null) {
|
|
33822
|
+
ranges.push({ startFrame: rangeStart, endFrame: totalFrames });
|
|
33823
|
+
}
|
|
33824
|
+
return ranges;
|
|
33825
|
+
}
|
|
33826
|
+
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt) {
|
|
33827
|
+
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
33828
|
+
const batches = [];
|
|
33829
|
+
for (let i2 = 0; i2 < ranges.length; i2 += workersPerBatch) {
|
|
33830
|
+
const batchIndex = batches.length;
|
|
33831
|
+
const batch = ranges.slice(i2, i2 + workersPerBatch).map((range, workerId) => ({
|
|
33832
|
+
workerId,
|
|
33833
|
+
startFrame: range.startFrame,
|
|
33834
|
+
endFrame: range.endFrame,
|
|
33835
|
+
outputDir: join35(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`)
|
|
33836
|
+
}));
|
|
33837
|
+
batches.push(batch);
|
|
33838
|
+
}
|
|
33839
|
+
return batches;
|
|
33840
|
+
}
|
|
33841
|
+
function getNextRetryWorkerCount(currentWorkers) {
|
|
33842
|
+
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
33843
|
+
}
|
|
33844
|
+
function isRecoverableParallelCaptureError(error) {
|
|
33845
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33846
|
+
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(
|
|
33847
|
+
message
|
|
33848
|
+
);
|
|
33849
|
+
}
|
|
33850
|
+
function shouldFallbackToScreenshotAfterCalibrationError(error) {
|
|
33851
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33852
|
+
return /HeadlessExperimental\.beginFrame timed out|beginFrame probe timeout|Another frame is pending|Frame still pending|Protocol error.*HeadlessExperimental\.beginFrame/i.test(
|
|
33853
|
+
message
|
|
33854
|
+
);
|
|
33855
|
+
}
|
|
33856
|
+
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
33857
|
+
let captured = 0;
|
|
33858
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
33859
|
+
const framePath = join35(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
|
|
33860
|
+
if (existsSync33(framePath)) captured++;
|
|
33861
|
+
}
|
|
33862
|
+
return captured;
|
|
33863
|
+
}
|
|
33864
|
+
function countFrameRanges(ranges) {
|
|
33865
|
+
return ranges.reduce((sum, range) => sum + (range.endFrame - range.startFrame), 0);
|
|
33866
|
+
}
|
|
33867
|
+
async function measureCaptureCostFromSession(session, totalFrames, fps) {
|
|
33868
|
+
const sampledFrames = selectCaptureCalibrationFrames(totalFrames);
|
|
33869
|
+
const samples = [];
|
|
33870
|
+
for (const frameIndex of sampledFrames) {
|
|
33871
|
+
const time = frameIndex / fps;
|
|
33872
|
+
const startedAt = Date.now();
|
|
33873
|
+
const result = await captureFrameToBuffer(session, frameIndex, time);
|
|
33874
|
+
samples.push({
|
|
33875
|
+
frameIndex,
|
|
33876
|
+
captureTimeMs: result.captureTimeMs || Date.now() - startedAt
|
|
33877
|
+
});
|
|
33878
|
+
}
|
|
33879
|
+
return {
|
|
33880
|
+
estimate: estimateMeasuredCaptureCostMultiplier(samples),
|
|
33881
|
+
samples
|
|
33882
|
+
};
|
|
33883
|
+
}
|
|
33884
|
+
async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
33885
|
+
const attempts = [];
|
|
33886
|
+
let currentWorkers = options.initialWorkerCount;
|
|
33887
|
+
let missingRanges = null;
|
|
33888
|
+
let attempt = 0;
|
|
33889
|
+
while (true) {
|
|
33890
|
+
const frameCount = missingRanges ? countFrameRanges(missingRanges) : options.totalFrames;
|
|
33891
|
+
attempts.push({
|
|
33892
|
+
attempt,
|
|
33893
|
+
workers: currentWorkers,
|
|
33894
|
+
frameCount,
|
|
33895
|
+
reason: attempt === 0 ? "initial" : "retry"
|
|
33896
|
+
});
|
|
33897
|
+
const attemptWorkDir = join35(options.workDir, `capture-attempt-${attempt}`);
|
|
33898
|
+
const batches = missingRanges ? buildMissingFrameRetryBatches(missingRanges, currentWorkers, attemptWorkDir, attempt) : [distributeFrames(options.totalFrames, currentWorkers, attemptWorkDir)];
|
|
33899
|
+
try {
|
|
33900
|
+
for (const tasks of batches) {
|
|
33901
|
+
const capturedBeforeBatch = countCapturedFrames(
|
|
33902
|
+
options.totalFrames,
|
|
33903
|
+
options.framesDir,
|
|
33904
|
+
options.frameExt
|
|
33905
|
+
);
|
|
33906
|
+
try {
|
|
33907
|
+
await executeParallelCapture(
|
|
33908
|
+
options.serverUrl,
|
|
33909
|
+
attemptWorkDir,
|
|
33910
|
+
tasks,
|
|
33911
|
+
options.captureOptions,
|
|
33912
|
+
options.createBeforeCaptureHook,
|
|
33913
|
+
options.abortSignal,
|
|
33914
|
+
options.onProgress ? (progress) => {
|
|
33915
|
+
options.onProgress?.({
|
|
33916
|
+
...progress,
|
|
33917
|
+
totalFrames: options.totalFrames,
|
|
33918
|
+
capturedFrames: Math.min(
|
|
33919
|
+
options.totalFrames,
|
|
33920
|
+
capturedBeforeBatch + progress.capturedFrames
|
|
33921
|
+
)
|
|
33922
|
+
});
|
|
33923
|
+
} : void 0,
|
|
33924
|
+
void 0,
|
|
33925
|
+
options.cfg
|
|
33926
|
+
);
|
|
33927
|
+
} finally {
|
|
33928
|
+
await mergeWorkerFrames(attemptWorkDir, tasks, options.framesDir);
|
|
33929
|
+
}
|
|
33930
|
+
}
|
|
33931
|
+
const remaining = findMissingFrameRanges(
|
|
33932
|
+
options.totalFrames,
|
|
33933
|
+
options.framesDir,
|
|
33934
|
+
options.frameExt
|
|
33935
|
+
);
|
|
33936
|
+
if (remaining.length === 0) {
|
|
33937
|
+
return attempts;
|
|
33938
|
+
}
|
|
33939
|
+
if (!options.allowRetry || currentWorkers <= 1) {
|
|
33940
|
+
throw new Error(
|
|
33941
|
+
`[Render] Capture completed but ${countFrameRanges(remaining)} frame(s) are missing`
|
|
33942
|
+
);
|
|
33943
|
+
}
|
|
33944
|
+
const nextWorkers = getNextRetryWorkerCount(currentWorkers);
|
|
33945
|
+
options.log.warn("[Render] Retrying missing captured frames with fewer workers.", {
|
|
33946
|
+
fromWorkers: currentWorkers,
|
|
33947
|
+
toWorkers: nextWorkers,
|
|
33948
|
+
missingFrames: countFrameRanges(remaining)
|
|
33949
|
+
});
|
|
33950
|
+
currentWorkers = nextWorkers;
|
|
33951
|
+
missingRanges = remaining;
|
|
33952
|
+
attempt++;
|
|
33953
|
+
} catch (error) {
|
|
33954
|
+
const remaining = findMissingFrameRanges(
|
|
33955
|
+
options.totalFrames,
|
|
33956
|
+
options.framesDir,
|
|
33957
|
+
options.frameExt
|
|
33958
|
+
);
|
|
33959
|
+
if (remaining.length === 0) {
|
|
33960
|
+
return attempts;
|
|
33961
|
+
}
|
|
33962
|
+
if (!options.allowRetry || currentWorkers <= 1 || !isRecoverableParallelCaptureError(error)) {
|
|
33963
|
+
throw error;
|
|
33964
|
+
}
|
|
33965
|
+
const nextWorkers = getNextRetryWorkerCount(currentWorkers);
|
|
33966
|
+
options.log.warn("[Render] Parallel capture timed out; retrying missing frames.", {
|
|
33967
|
+
fromWorkers: currentWorkers,
|
|
33968
|
+
toWorkers: nextWorkers,
|
|
33969
|
+
missingFrames: countFrameRanges(remaining),
|
|
33970
|
+
error: error instanceof Error ? error.message : String(error)
|
|
33971
|
+
});
|
|
33972
|
+
currentWorkers = nextWorkers;
|
|
33973
|
+
missingRanges = remaining;
|
|
33974
|
+
attempt++;
|
|
33975
|
+
}
|
|
33976
|
+
}
|
|
33977
|
+
}
|
|
33671
33978
|
function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, width, height, log2, sourceTransfer, targetTransfer) {
|
|
33672
33979
|
const frameDir = hdrFrameDirs.get(el.id);
|
|
33673
33980
|
const startTime = hdrStartTimes.get(el.id);
|
|
@@ -34294,7 +34601,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34294
34601
|
let extractionResult = null;
|
|
34295
34602
|
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
34296
34603
|
const videoTransfers = /* @__PURE__ */ new Map();
|
|
34297
|
-
if (job.config.
|
|
34604
|
+
if (job.config.hdrMode !== "force-sdr" && composition.videos.length > 0) {
|
|
34298
34605
|
await Promise.all(
|
|
34299
34606
|
composition.videos.map(async (v) => {
|
|
34300
34607
|
let videoPath = v.src;
|
|
@@ -34315,7 +34622,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34315
34622
|
const imageTransfers = /* @__PURE__ */ new Map();
|
|
34316
34623
|
const hdrImageSrcPaths = /* @__PURE__ */ new Map();
|
|
34317
34624
|
const imageColorSpaces = [];
|
|
34318
|
-
if (job.config.
|
|
34625
|
+
if (job.config.hdrMode !== "force-sdr" && composition.images.length > 0) {
|
|
34319
34626
|
const probed = await Promise.all(
|
|
34320
34627
|
composition.images.map(async (img) => {
|
|
34321
34628
|
let imgPath = img.src;
|
|
@@ -34372,28 +34679,53 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34372
34679
|
perfStages.videoExtractMs = Date.now() - stage2Start;
|
|
34373
34680
|
}
|
|
34374
34681
|
let effectiveHdr;
|
|
34375
|
-
|
|
34682
|
+
let forcedHdrWithoutSources = false;
|
|
34683
|
+
{
|
|
34684
|
+
const hdrMode = job.config.hdrMode ?? "auto";
|
|
34376
34685
|
const videoColorSpaces = (extractionResult?.extracted ?? []).map(
|
|
34377
34686
|
(ext) => ext.metadata.colorSpace
|
|
34378
34687
|
);
|
|
34379
34688
|
const allColorSpaces = [...videoColorSpaces, ...imageColorSpaces];
|
|
34380
|
-
|
|
34381
|
-
|
|
34382
|
-
|
|
34689
|
+
const info = allColorSpaces.length > 0 ? analyzeCompositionHdr(allColorSpaces) : null;
|
|
34690
|
+
if (hdrMode === "force-sdr") {
|
|
34691
|
+
effectiveHdr = void 0;
|
|
34692
|
+
} else if (hdrMode === "force-hdr") {
|
|
34693
|
+
if (info?.hasHdr && info.dominantTransfer) {
|
|
34694
|
+
effectiveHdr = { transfer: info.dominantTransfer };
|
|
34695
|
+
} else {
|
|
34696
|
+
effectiveHdr = { transfer: "hlg" };
|
|
34697
|
+
forcedHdrWithoutSources = true;
|
|
34698
|
+
}
|
|
34699
|
+
} else {
|
|
34700
|
+
if (info?.hasHdr && info.dominantTransfer) {
|
|
34383
34701
|
effectiveHdr = { transfer: info.dominantTransfer };
|
|
34384
34702
|
}
|
|
34385
34703
|
}
|
|
34386
34704
|
}
|
|
34387
34705
|
if (effectiveHdr && outputFormat !== "mp4") {
|
|
34706
|
+
const hdrSourceReason = forcedHdrWithoutSources ? "HDR was forced without detected HDR sources" : "HDR source detected";
|
|
34388
34707
|
log2.warn(
|
|
34389
|
-
`[Render]
|
|
34708
|
+
`[Render] ${hdrSourceReason}, but format is "${outputFormat}" \u2014 falling back to SDR. HDR + alpha is not supported. Use --format mp4 for HDR10 output.`
|
|
34390
34709
|
);
|
|
34391
34710
|
effectiveHdr = void 0;
|
|
34392
34711
|
}
|
|
34393
|
-
|
|
34394
|
-
|
|
34395
|
-
|
|
34396
|
-
|
|
34712
|
+
{
|
|
34713
|
+
const hdrMode = job.config.hdrMode ?? "auto";
|
|
34714
|
+
if (forcedHdrWithoutSources) {
|
|
34715
|
+
log2.warn(
|
|
34716
|
+
"[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."
|
|
34717
|
+
);
|
|
34718
|
+
}
|
|
34719
|
+
if (effectiveHdr) {
|
|
34720
|
+
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)";
|
|
34721
|
+
log2.info(
|
|
34722
|
+
`[Render] HDR ${reason} \u2014 output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
34723
|
+
);
|
|
34724
|
+
} else if (hdrMode === "force-sdr") {
|
|
34725
|
+
log2.info("[Render] SDR forced by --sdr flag");
|
|
34726
|
+
} else {
|
|
34727
|
+
log2.info("[Render] No HDR sources detected \u2014 rendering SDR");
|
|
34728
|
+
}
|
|
34397
34729
|
}
|
|
34398
34730
|
const stage3Start = Date.now();
|
|
34399
34731
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
@@ -34440,7 +34772,101 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34440
34772
|
...captureOptions,
|
|
34441
34773
|
skipReadinessVideoIds: Array.from(nativeHdrVideoIds)
|
|
34442
34774
|
});
|
|
34443
|
-
|
|
34775
|
+
let captureCalibration;
|
|
34776
|
+
let switchedToScreenshotAfterCalibration = false;
|
|
34777
|
+
if (job.config.workers === void 0 && totalFrames >= 60) {
|
|
34778
|
+
const calibrationDir = join35(workDir, "capture-calibration");
|
|
34779
|
+
const calibrationCfg = createCaptureCalibrationConfig(cfg);
|
|
34780
|
+
const videoInjector = createVideoFrameInjector(frameLookup);
|
|
34781
|
+
let calibrationSession = null;
|
|
34782
|
+
try {
|
|
34783
|
+
calibrationSession = await createCaptureSession(
|
|
34784
|
+
fileServer.url,
|
|
34785
|
+
calibrationDir,
|
|
34786
|
+
buildHdrCaptureOptions(),
|
|
34787
|
+
videoInjector,
|
|
34788
|
+
calibrationCfg
|
|
34789
|
+
);
|
|
34790
|
+
if (!calibrationSession.isInitialized) {
|
|
34791
|
+
await initializeSession(calibrationSession);
|
|
34792
|
+
}
|
|
34793
|
+
assertNotAborted();
|
|
34794
|
+
captureCalibration = await measureCaptureCostFromSession(
|
|
34795
|
+
calibrationSession,
|
|
34796
|
+
totalFrames,
|
|
34797
|
+
job.config.fps
|
|
34798
|
+
);
|
|
34799
|
+
if (captureCalibration.estimate.multiplier > 1) {
|
|
34800
|
+
log2.warn("[Render] Measured slow frame capture during auto-worker calibration.", {
|
|
34801
|
+
multiplier: captureCalibration.estimate.multiplier,
|
|
34802
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
34803
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex)
|
|
34804
|
+
});
|
|
34805
|
+
} else {
|
|
34806
|
+
log2.debug("[Render] Auto-worker calibration kept baseline capture cost.", {
|
|
34807
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
34808
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex)
|
|
34809
|
+
});
|
|
34810
|
+
}
|
|
34811
|
+
} catch (error) {
|
|
34812
|
+
const shouldFallbackToScreenshot = !cfg.forceScreenshot && shouldFallbackToScreenshotAfterCalibrationError(error);
|
|
34813
|
+
if (shouldFallbackToScreenshot) {
|
|
34814
|
+
cfg.forceScreenshot = true;
|
|
34815
|
+
switchedToScreenshotAfterCalibration = true;
|
|
34816
|
+
if (probeSession) {
|
|
34817
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
34818
|
+
await closeCaptureSession(probeSession).catch(() => {
|
|
34819
|
+
});
|
|
34820
|
+
probeSession = null;
|
|
34821
|
+
}
|
|
34822
|
+
}
|
|
34823
|
+
captureCalibration = {
|
|
34824
|
+
estimate: {
|
|
34825
|
+
multiplier: MAX_MEASURED_CAPTURE_COST_MULTIPLIER,
|
|
34826
|
+
reasons: shouldFallbackToScreenshot ? ["calibration-beginframe-timeout", "screenshot-fallback"] : ["calibration-failed"]
|
|
34827
|
+
},
|
|
34828
|
+
samples: []
|
|
34829
|
+
};
|
|
34830
|
+
if (shouldFallbackToScreenshot) {
|
|
34831
|
+
log2.warn(
|
|
34832
|
+
"[Render] BeginFrame auto-worker calibration timed out; falling back to screenshot capture mode.",
|
|
34833
|
+
{
|
|
34834
|
+
protocolTimeout: calibrationCfg.protocolTimeout,
|
|
34835
|
+
error: error instanceof Error ? error.message : String(error)
|
|
34836
|
+
}
|
|
34837
|
+
);
|
|
34838
|
+
} else {
|
|
34839
|
+
log2.warn("[Render] Auto-worker calibration failed; using conservative worker budget.", {
|
|
34840
|
+
protocolTimeout: calibrationCfg.protocolTimeout,
|
|
34841
|
+
error: error instanceof Error ? error.message : String(error)
|
|
34842
|
+
});
|
|
34843
|
+
}
|
|
34844
|
+
} finally {
|
|
34845
|
+
if (calibrationSession) {
|
|
34846
|
+
lastBrowserConsole = calibrationSession.browserConsoleBuffer;
|
|
34847
|
+
await closeCaptureSession(calibrationSession).catch(() => {
|
|
34848
|
+
});
|
|
34849
|
+
}
|
|
34850
|
+
}
|
|
34851
|
+
}
|
|
34852
|
+
let workerCount = resolveRenderWorkerCount(
|
|
34853
|
+
totalFrames,
|
|
34854
|
+
job.config.workers,
|
|
34855
|
+
cfg,
|
|
34856
|
+
compiled,
|
|
34857
|
+
composition,
|
|
34858
|
+
log2,
|
|
34859
|
+
captureCalibration?.estimate
|
|
34860
|
+
);
|
|
34861
|
+
if (switchedToScreenshotAfterCalibration && workerCount > 1) {
|
|
34862
|
+
workerCount = 1;
|
|
34863
|
+
}
|
|
34864
|
+
if (workerCount > 1 && probeSession) {
|
|
34865
|
+
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
34866
|
+
await closeCaptureSession(probeSession);
|
|
34867
|
+
probeSession = null;
|
|
34868
|
+
}
|
|
34869
|
+
const captureAttempts = [];
|
|
34444
34870
|
const FORMAT_EXT2 = {
|
|
34445
34871
|
mp4: ".mp4",
|
|
34446
34872
|
webm: ".webm",
|
|
@@ -35032,15 +35458,18 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35032
35458
|
perfStages.encodeMs = encodeResult.durationMs;
|
|
35033
35459
|
} else {
|
|
35034
35460
|
if (workerCount > 1) {
|
|
35035
|
-
const
|
|
35036
|
-
|
|
35037
|
-
fileServer.url,
|
|
35461
|
+
const attempts = await executeDiskCaptureWithAdaptiveRetry({
|
|
35462
|
+
serverUrl: fileServer.url,
|
|
35038
35463
|
workDir,
|
|
35039
|
-
|
|
35040
|
-
|
|
35041
|
-
|
|
35464
|
+
framesDir,
|
|
35465
|
+
totalFrames: job.totalFrames,
|
|
35466
|
+
initialWorkerCount: workerCount,
|
|
35467
|
+
allowRetry: job.config.workers === void 0,
|
|
35468
|
+
frameExt: needsAlpha ? "png" : "jpg",
|
|
35469
|
+
captureOptions: buildHdrCaptureOptions(),
|
|
35470
|
+
createBeforeCaptureHook: () => createVideoFrameInjector(frameLookup),
|
|
35042
35471
|
abortSignal,
|
|
35043
|
-
(progress) => {
|
|
35472
|
+
onProgress: (progress) => {
|
|
35044
35473
|
job.framesRendered = progress.capturedFrames;
|
|
35045
35474
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
35046
35475
|
const progressPct = 25 + frameProgress * 45;
|
|
@@ -35048,16 +35477,20 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35048
35477
|
updateJobStatus(
|
|
35049
35478
|
job,
|
|
35050
35479
|
"rendering",
|
|
35051
|
-
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${
|
|
35480
|
+
`Capturing frame ${progress.capturedFrames}/${progress.totalFrames} (${progress.activeWorkers} workers)`,
|
|
35052
35481
|
Math.round(progressPct),
|
|
35053
35482
|
onProgress
|
|
35054
35483
|
);
|
|
35055
35484
|
}
|
|
35056
35485
|
},
|
|
35057
|
-
|
|
35058
|
-
|
|
35059
|
-
);
|
|
35060
|
-
|
|
35486
|
+
cfg,
|
|
35487
|
+
log: log2
|
|
35488
|
+
});
|
|
35489
|
+
captureAttempts.push(...attempts);
|
|
35490
|
+
const lastAttempt = attempts[attempts.length - 1];
|
|
35491
|
+
if (lastAttempt) {
|
|
35492
|
+
workerCount = lastAttempt.workers;
|
|
35493
|
+
}
|
|
35061
35494
|
if (probeSession) {
|
|
35062
35495
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
35063
35496
|
await closeCaptureSession(probeSession);
|
|
@@ -35227,6 +35660,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35227
35660
|
stages: perfStages,
|
|
35228
35661
|
videoExtractBreakdown: extractionResult?.phaseBreakdown,
|
|
35229
35662
|
tmpPeakBytes,
|
|
35663
|
+
captureCalibration: captureCalibration ? {
|
|
35664
|
+
sampledFrames: captureCalibration.samples.map((sample) => sample.frameIndex),
|
|
35665
|
+
p95Ms: captureCalibration.estimate.p95Ms,
|
|
35666
|
+
multiplier: captureCalibration.estimate.multiplier,
|
|
35667
|
+
reasons: captureCalibration.estimate.reasons
|
|
35668
|
+
} : void 0,
|
|
35669
|
+
captureAttempts: captureAttempts.length > 0 ? captureAttempts : void 0,
|
|
35230
35670
|
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0,
|
|
35231
35671
|
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0,
|
|
35232
35672
|
peakRssMb: Math.round(peakRssBytes / (1024 * 1024)),
|
|
@@ -35342,7 +35782,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35342
35782
|
clearInterval(memSamplerInterval);
|
|
35343
35783
|
}
|
|
35344
35784
|
}
|
|
35345
|
-
var RenderCancelledError, BROWSER_MEDIA_EPSILON;
|
|
35785
|
+
var RenderCancelledError, BROWSER_MEDIA_EPSILON, CAPTURE_CALIBRATION_TARGET_MS, MAX_MEASURED_CAPTURE_COST_MULTIPLIER, CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS;
|
|
35346
35786
|
var init_renderOrchestrator = __esm({
|
|
35347
35787
|
"../producer/src/services/renderOrchestrator.ts"() {
|
|
35348
35788
|
"use strict";
|
|
@@ -35363,6 +35803,9 @@ var init_renderOrchestrator = __esm({
|
|
|
35363
35803
|
}
|
|
35364
35804
|
};
|
|
35365
35805
|
BROWSER_MEDIA_EPSILON = 1e-4;
|
|
35806
|
+
CAPTURE_CALIBRATION_TARGET_MS = 600;
|
|
35807
|
+
MAX_MEASURED_CAPTURE_COST_MULTIPLIER = 8;
|
|
35808
|
+
CAPTURE_CALIBRATION_PROTOCOL_TIMEOUT_MS = 3e4;
|
|
35366
35809
|
}
|
|
35367
35810
|
});
|
|
35368
35811
|
|
|
@@ -38384,13 +38827,13 @@ function buildDockerRunArgs(input) {
|
|
|
38384
38827
|
options.quality,
|
|
38385
38828
|
"--format",
|
|
38386
38829
|
options.format,
|
|
38387
|
-
"--workers",
|
|
38388
|
-
String(options.workers),
|
|
38830
|
+
...options.workers != null ? ["--workers", String(options.workers)] : [],
|
|
38389
38831
|
...options.crf != null ? ["--crf", String(options.crf)] : [],
|
|
38390
38832
|
...options.videoBitrate ? ["--video-bitrate", options.videoBitrate] : [],
|
|
38391
38833
|
...options.quiet ? ["--quiet"] : [],
|
|
38392
38834
|
...options.gpu ? ["--gpu"] : [],
|
|
38393
|
-
...options.hdr ? ["--hdr"] : []
|
|
38835
|
+
...options.hdrMode === "force-hdr" ? ["--hdr"] : [],
|
|
38836
|
+
...options.hdrMode === "force-sdr" ? ["--sdr"] : []
|
|
38394
38837
|
];
|
|
38395
38838
|
}
|
|
38396
38839
|
var init_dockerRunArgs = __esm({
|
|
@@ -38446,9 +38889,6 @@ import { mkdirSync as mkdirSync24, readFileSync as readFileSync29, statSync as s
|
|
|
38446
38889
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
|
|
38447
38890
|
import { resolve as resolve28, dirname as dirname16, join as join43, basename as basename9 } from "path";
|
|
38448
38891
|
import { execFileSync as execFileSync5, spawn as spawn11 } from "child_process";
|
|
38449
|
-
function defaultWorkerCount() {
|
|
38450
|
-
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
38451
|
-
}
|
|
38452
38892
|
function dockerImageTag(version) {
|
|
38453
38893
|
return `${DOCKER_IMAGE_PREFIX}:${version}`;
|
|
38454
38894
|
}
|
|
@@ -38540,7 +38980,7 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
38540
38980
|
format: options.format,
|
|
38541
38981
|
workers: options.workers,
|
|
38542
38982
|
gpu: options.gpu,
|
|
38543
|
-
|
|
38983
|
+
hdrMode: options.hdrMode,
|
|
38544
38984
|
crf: options.crf,
|
|
38545
38985
|
videoBitrate: options.videoBitrate,
|
|
38546
38986
|
quiet: options.quiet
|
|
@@ -38589,7 +39029,7 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
38589
39029
|
format: options.format,
|
|
38590
39030
|
workers: options.workers,
|
|
38591
39031
|
useGpu: options.gpu,
|
|
38592
|
-
|
|
39032
|
+
hdrMode: options.hdrMode,
|
|
38593
39033
|
crf: options.crf,
|
|
38594
39034
|
videoBitrate: options.videoBitrate
|
|
38595
39035
|
});
|
|
@@ -38636,7 +39076,7 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
38636
39076
|
durationMs: elapsedMs,
|
|
38637
39077
|
fps: options.fps,
|
|
38638
39078
|
quality: options.quality,
|
|
38639
|
-
workers: options.workers,
|
|
39079
|
+
workers: options.workers ?? perf?.workers,
|
|
38640
39080
|
docker,
|
|
38641
39081
|
gpu: options.gpu,
|
|
38642
39082
|
compositionDurationMs,
|
|
@@ -38702,7 +39142,7 @@ var init_render2 = __esm({
|
|
|
38702
39142
|
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
|
38703
39143
|
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
|
38704
39144
|
["Parallel rendering with 6 workers", "hyperframes render --workers 6 --output fast.mp4"],
|
|
38705
|
-
["HDR output (
|
|
39145
|
+
["HDR output (auto-detected)", "hyperframes render --output hdr-output.mp4"]
|
|
38706
39146
|
];
|
|
38707
39147
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
38708
39148
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
@@ -38754,7 +39194,12 @@ var init_render2 = __esm({
|
|
|
38754
39194
|
},
|
|
38755
39195
|
hdr: {
|
|
38756
39196
|
type: "boolean",
|
|
38757
|
-
description: "
|
|
39197
|
+
description: "Force HDR output even if no HDR sources are detected",
|
|
39198
|
+
default: false
|
|
39199
|
+
},
|
|
39200
|
+
sdr: {
|
|
39201
|
+
type: "boolean",
|
|
39202
|
+
description: "Force SDR output even if HDR sources are detected",
|
|
38758
39203
|
default: false
|
|
38759
39204
|
},
|
|
38760
39205
|
crf: {
|
|
@@ -38860,9 +39305,8 @@ var init_render2 = __esm({
|
|
|
38860
39305
|
);
|
|
38861
39306
|
process.exit(1);
|
|
38862
39307
|
}
|
|
38863
|
-
const workerCount = workers ?? defaultWorkerCount();
|
|
38864
39308
|
if (!quiet) {
|
|
38865
|
-
const workerLabel =
|
|
39309
|
+
const workerLabel = workers != null ? `${workers} workers` : `auto workers (${CPU_CORE_COUNT} cores detected)`;
|
|
38866
39310
|
console.log("");
|
|
38867
39311
|
console.log(
|
|
38868
39312
|
c.accent("\u25C6") + " Rendering " + c.accent(project.name) + c.dim(" \u2192 " + outputPath)
|
|
@@ -38930,14 +39374,18 @@ var init_render2 = __esm({
|
|
|
38930
39374
|
console.log("");
|
|
38931
39375
|
}
|
|
38932
39376
|
}
|
|
39377
|
+
if (args.hdr && args.sdr) {
|
|
39378
|
+
console.error("Error: --hdr and --sdr are mutually exclusive.");
|
|
39379
|
+
process.exit(1);
|
|
39380
|
+
}
|
|
38933
39381
|
if (useDocker) {
|
|
38934
39382
|
await renderDocker(project.dir, outputPath, {
|
|
38935
39383
|
fps,
|
|
38936
39384
|
quality,
|
|
38937
39385
|
format,
|
|
38938
|
-
workers
|
|
39386
|
+
workers,
|
|
38939
39387
|
gpu: useGpu,
|
|
38940
|
-
|
|
39388
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
38941
39389
|
crf,
|
|
38942
39390
|
videoBitrate,
|
|
38943
39391
|
quiet
|
|
@@ -38947,9 +39395,9 @@ var init_render2 = __esm({
|
|
|
38947
39395
|
fps,
|
|
38948
39396
|
quality,
|
|
38949
39397
|
format,
|
|
38950
|
-
workers
|
|
39398
|
+
workers,
|
|
38951
39399
|
gpu: useGpu,
|
|
38952
|
-
|
|
39400
|
+
hdrMode: args.sdr ? "force-sdr" : args.hdr ? "force-hdr" : "auto",
|
|
38953
39401
|
crf,
|
|
38954
39402
|
videoBitrate,
|
|
38955
39403
|
quiet,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperframes",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
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"
|