hyperframes 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +44 -31
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.6.
|
|
57
|
+
VERSION = true ? "0.6.15" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -34012,18 +34012,19 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
34012
34012
|
}
|
|
34013
34013
|
return finalWorkers;
|
|
34014
34014
|
}
|
|
34015
|
-
function distributeFrames(totalFrames, workerCount, workDir) {
|
|
34015
|
+
function distributeFrames(totalFrames, workerCount, workDir, rangeStart = 0) {
|
|
34016
34016
|
const tasks = [];
|
|
34017
34017
|
const framesPerWorker = Math.ceil(totalFrames / workerCount);
|
|
34018
34018
|
for (let i2 = 0; i2 < workerCount; i2++) {
|
|
34019
|
-
const startFrame = i2 * framesPerWorker;
|
|
34020
|
-
const endFrame = Math.min((i2 + 1) * framesPerWorker, totalFrames);
|
|
34021
|
-
if (startFrame >= totalFrames) break;
|
|
34019
|
+
const startFrame = rangeStart + i2 * framesPerWorker;
|
|
34020
|
+
const endFrame = Math.min(rangeStart + (i2 + 1) * framesPerWorker, rangeStart + totalFrames);
|
|
34021
|
+
if (startFrame >= rangeStart + totalFrames) break;
|
|
34022
34022
|
tasks.push({
|
|
34023
34023
|
workerId: i2,
|
|
34024
34024
|
startFrame,
|
|
34025
34025
|
endFrame,
|
|
34026
|
-
outputDir: join30(workDir, `worker-${i2}`)
|
|
34026
|
+
outputDir: join30(workDir, `worker-${i2}`),
|
|
34027
|
+
outputFrameOffset: rangeStart
|
|
34027
34028
|
});
|
|
34028
34029
|
}
|
|
34029
34030
|
return tasks;
|
|
@@ -34043,16 +34044,18 @@ async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCa
|
|
|
34043
34044
|
config
|
|
34044
34045
|
);
|
|
34045
34046
|
await initializeSession(session);
|
|
34047
|
+
const outputOffset = task.outputFrameOffset ?? 0;
|
|
34046
34048
|
for (let i2 = task.startFrame; i2 < task.endFrame; i2++) {
|
|
34047
34049
|
if (signal?.aborted) {
|
|
34048
34050
|
throw new Error("Parallel worker cancelled");
|
|
34049
34051
|
}
|
|
34050
34052
|
const time = i2 * captureOptions.fps.den / captureOptions.fps.num;
|
|
34053
|
+
const fileFrameIdx = i2 - outputOffset;
|
|
34051
34054
|
if (onFrameBuffer) {
|
|
34052
|
-
const { buffer } = await captureFrameToBuffer(session,
|
|
34055
|
+
const { buffer } = await captureFrameToBuffer(session, fileFrameIdx, time);
|
|
34053
34056
|
await onFrameBuffer(i2, buffer);
|
|
34054
34057
|
} else {
|
|
34055
|
-
await captureFrame(session,
|
|
34058
|
+
await captureFrame(session, fileFrameIdx, time);
|
|
34056
34059
|
}
|
|
34057
34060
|
framesCaptured++;
|
|
34058
34061
|
if (onFrameCaptured) onFrameCaptured(task.workerId, i2);
|
|
@@ -39073,17 +39076,18 @@ async function runCaptureStage(input) {
|
|
|
39073
39076
|
let { workerCount, probeSession } = input;
|
|
39074
39077
|
let lastBrowserConsole = [];
|
|
39075
39078
|
const captureCfg = cfg.forceScreenshot === forceScreenshot ? cfg : { ...cfg, forceScreenshot };
|
|
39076
|
-
if (frameRange !== void 0 && workerCount > 1) {
|
|
39077
|
-
throw new Error(
|
|
39078
|
-
`[captureStage] frameRange capture requires workerCount === 1 (received workerCount=${workerCount}). Distributed chunk workers fan out at the activity layer; reduce workerCount to 1 when passing frameRange.`
|
|
39079
|
-
);
|
|
39080
|
-
}
|
|
39081
39079
|
if (frameRange !== void 0) {
|
|
39082
39080
|
if (!Number.isFinite(frameRange.startFrame) || !Number.isFinite(frameRange.endFrame) || frameRange.startFrame < 0 || frameRange.endFrame <= frameRange.startFrame) {
|
|
39083
39081
|
throw new Error(
|
|
39084
39082
|
`[captureStage] invalid frameRange: ${JSON.stringify(frameRange)}. Expected non-negative startFrame strictly less than endFrame.`
|
|
39085
39083
|
);
|
|
39086
39084
|
}
|
|
39085
|
+
const rangeFrames = frameRange.endFrame - frameRange.startFrame;
|
|
39086
|
+
if (rangeFrames !== totalFrames) {
|
|
39087
|
+
throw new Error(
|
|
39088
|
+
`[captureStage] frameRange size (${rangeFrames}) must equal totalFrames (${totalFrames}). Received frameRange=${JSON.stringify(frameRange)}.`
|
|
39089
|
+
);
|
|
39090
|
+
}
|
|
39087
39091
|
}
|
|
39088
39092
|
if (workerCount > 1) {
|
|
39089
39093
|
const attempts = await executeDiskCaptureWithAdaptiveRetry({
|
|
@@ -39097,6 +39101,7 @@ async function runCaptureStage(input) {
|
|
|
39097
39101
|
captureOptions: buildCaptureOptions(),
|
|
39098
39102
|
createBeforeCaptureHook: createRenderVideoFrameInjector,
|
|
39099
39103
|
abortSignal,
|
|
39104
|
+
frameRangeStart: frameRange?.startFrame,
|
|
39100
39105
|
onProgress: (progress) => {
|
|
39101
39106
|
job.framesRendered = progress.capturedFrames;
|
|
39102
39107
|
const frameProgress = progress.capturedFrames / progress.totalFrames;
|
|
@@ -40986,16 +40991,17 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
40986
40991
|
}
|
|
40987
40992
|
return ranges;
|
|
40988
40993
|
}
|
|
40989
|
-
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt) {
|
|
40994
|
+
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0) {
|
|
40990
40995
|
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
40991
40996
|
const batches = [];
|
|
40992
40997
|
for (let i2 = 0; i2 < ranges.length; i2 += workersPerBatch) {
|
|
40993
40998
|
const batchIndex = batches.length;
|
|
40994
40999
|
const batch = ranges.slice(i2, i2 + workersPerBatch).map((range, workerId) => ({
|
|
40995
41000
|
workerId,
|
|
40996
|
-
startFrame: range.startFrame,
|
|
40997
|
-
endFrame: range.endFrame,
|
|
40998
|
-
outputDir: join49(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`)
|
|
41001
|
+
startFrame: rangeStart + range.startFrame,
|
|
41002
|
+
endFrame: rangeStart + range.endFrame,
|
|
41003
|
+
outputDir: join49(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`),
|
|
41004
|
+
outputFrameOffset: rangeStart
|
|
40999
41005
|
}));
|
|
41000
41006
|
batches.push(batch);
|
|
41001
41007
|
}
|
|
@@ -41026,6 +41032,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
41026
41032
|
let currentWorkers = options.initialWorkerCount;
|
|
41027
41033
|
let missingRanges = null;
|
|
41028
41034
|
let attempt = 0;
|
|
41035
|
+
const rangeStart = options.frameRangeStart ?? 0;
|
|
41029
41036
|
while (true) {
|
|
41030
41037
|
const frameCount = missingRanges ? countFrameRanges(missingRanges) : options.totalFrames;
|
|
41031
41038
|
attempts.push({
|
|
@@ -41035,7 +41042,13 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
41035
41042
|
reason: attempt === 0 ? "initial" : "retry"
|
|
41036
41043
|
});
|
|
41037
41044
|
const attemptWorkDir = join49(options.workDir, `capture-attempt-${attempt}`);
|
|
41038
|
-
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
41045
|
+
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
41046
|
+
missingRanges,
|
|
41047
|
+
currentWorkers,
|
|
41048
|
+
attemptWorkDir,
|
|
41049
|
+
attempt,
|
|
41050
|
+
rangeStart
|
|
41051
|
+
) : [distributeFrames(options.totalFrames, currentWorkers, attemptWorkDir, rangeStart)];
|
|
41039
41052
|
try {
|
|
41040
41053
|
for (const tasks of batches) {
|
|
41041
41054
|
const capturedBeforeBatch = countCapturedFrames(
|
|
@@ -43467,6 +43480,14 @@ async function plan(projectDir, config, planDir) {
|
|
|
43467
43480
|
height,
|
|
43468
43481
|
format: config.format
|
|
43469
43482
|
};
|
|
43483
|
+
try {
|
|
43484
|
+
rmSync11(workDir, { recursive: true, force: true });
|
|
43485
|
+
} catch (err) {
|
|
43486
|
+
log2.warn("[plan] failed to remove temp work dir", {
|
|
43487
|
+
workDir,
|
|
43488
|
+
error: err instanceof Error ? err.message : String(err)
|
|
43489
|
+
});
|
|
43490
|
+
}
|
|
43470
43491
|
const freezeResult = await freezePlan({
|
|
43471
43492
|
planDir,
|
|
43472
43493
|
composition: compositionJson,
|
|
@@ -43480,14 +43501,6 @@ async function plan(projectDir, config, planDir) {
|
|
|
43480
43501
|
hasAudio: audioResult.hasAudio
|
|
43481
43502
|
});
|
|
43482
43503
|
const planHash = freezeResult.planHash;
|
|
43483
|
-
try {
|
|
43484
|
-
rmSync11(workDir, { recursive: true, force: true });
|
|
43485
|
-
} catch (err) {
|
|
43486
|
-
log2.warn("[plan] failed to remove temp work dir", {
|
|
43487
|
-
workDir,
|
|
43488
|
-
error: err instanceof Error ? err.message : String(err)
|
|
43489
|
-
});
|
|
43490
|
-
}
|
|
43491
43504
|
const sizeLimitBytes = config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
43492
43505
|
const planDirBytes = measurePlanDirBytes(planDir);
|
|
43493
43506
|
if (planDirBytes > sizeLimitBytes) {
|
|
@@ -43775,6 +43788,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
43775
43788
|
session = await createCaptureSession(fileServer.url, framesDir, captureOptions, null, cfg);
|
|
43776
43789
|
await assertSwiftShader(session.page, readWebGlVendorInfoFromCanvas);
|
|
43777
43790
|
await initializeSession(session);
|
|
43791
|
+
const chunkWorkerCount = calculateOptimalWorkers(framesInChunk, void 0, cfg);
|
|
43778
43792
|
await runCaptureStage({
|
|
43779
43793
|
fileServer,
|
|
43780
43794
|
workDir,
|
|
@@ -43784,11 +43798,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
43784
43798
|
cfg,
|
|
43785
43799
|
forceScreenshot: encoder.forceScreenshot,
|
|
43786
43800
|
log: log2,
|
|
43787
|
-
workerCount:
|
|
43788
|
-
//
|
|
43789
|
-
//
|
|
43790
|
-
//
|
|
43791
|
-
// so we MUST clear our own reference here to avoid a double-close.
|
|
43801
|
+
workerCount: chunkWorkerCount,
|
|
43802
|
+
// The parallel branch closes this session and spins up its own
|
|
43803
|
+
// worker sessions, wasting the ~3-5s of pre-warmed setup. Worth a
|
|
43804
|
+
// follow-up to skip pre-warmup when the resolved workerCount > 1.
|
|
43792
43805
|
probeSession: session,
|
|
43793
43806
|
needsAlpha: plan2.dimensions.format !== "mp4",
|
|
43794
43807
|
captureAttempts: [],
|