@hyperframes/producer 0.4.40 → 0.4.41
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/public-server.js
CHANGED
|
@@ -101459,7 +101459,8 @@ var DEFAULT_CONFIG = {
|
|
|
101459
101459
|
forceScreenshot: false,
|
|
101460
101460
|
enableChunkedEncode: false,
|
|
101461
101461
|
chunkSizeFrames: 360,
|
|
101462
|
-
enableStreamingEncode:
|
|
101462
|
+
enableStreamingEncode: true,
|
|
101463
|
+
streamingEncodeMaxDurationSeconds: 240,
|
|
101463
101464
|
ffmpegEncodeTimeout: 6e5,
|
|
101464
101465
|
ffmpegProcessTimeout: 3e5,
|
|
101465
101466
|
ffmpegStreamingTimeout: 6e5,
|
|
@@ -101521,6 +101522,13 @@ function resolveConfig(overrides) {
|
|
|
101521
101522
|
"PRODUCER_ENABLE_STREAMING_ENCODE",
|
|
101522
101523
|
DEFAULT_CONFIG.enableStreamingEncode
|
|
101523
101524
|
),
|
|
101525
|
+
streamingEncodeMaxDurationSeconds: Math.max(
|
|
101526
|
+
0,
|
|
101527
|
+
envNum(
|
|
101528
|
+
"PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS",
|
|
101529
|
+
DEFAULT_CONFIG.streamingEncodeMaxDurationSeconds
|
|
101530
|
+
)
|
|
101531
|
+
),
|
|
101524
101532
|
ffmpegEncodeTimeout: envNum("FFMPEG_ENCODE_TIMEOUT_MS", DEFAULT_CONFIG.ffmpegEncodeTimeout),
|
|
101525
101533
|
ffmpegProcessTimeout: envNum("FFMPEG_PROCESS_TIMEOUT_MS", DEFAULT_CONFIG.ffmpegProcessTimeout),
|
|
101526
101534
|
ffmpegStreamingTimeout: envNum(
|
|
@@ -104484,6 +104492,23 @@ async function applyVideoMetadataHints(page, hints) {
|
|
|
104484
104492
|
[...hints]
|
|
104485
104493
|
);
|
|
104486
104494
|
}
|
|
104495
|
+
async function waitForOptionalTailwindReady(page, timeoutMs) {
|
|
104496
|
+
const hasTailwindReady = await page.evaluate(
|
|
104497
|
+
`(() => { const ready = window.__tailwindReady; return !!ready && typeof ready.then === "function"; })()`
|
|
104498
|
+
);
|
|
104499
|
+
if (!hasTailwindReady) return;
|
|
104500
|
+
const ready = await Promise.race([
|
|
104501
|
+
page.evaluate(
|
|
104502
|
+
`Promise.resolve(window.__tailwindReady).then(() => true, () => false)`
|
|
104503
|
+
),
|
|
104504
|
+
new Promise((resolve14) => setTimeout(() => resolve14(false), timeoutMs))
|
|
104505
|
+
]);
|
|
104506
|
+
if (!ready) {
|
|
104507
|
+
throw new Error(
|
|
104508
|
+
`[FrameCapture] window.__tailwindReady not resolved after ${timeoutMs}ms. Tailwind browser runtime must finish before frame capture starts.`
|
|
104509
|
+
);
|
|
104510
|
+
}
|
|
104511
|
+
}
|
|
104487
104512
|
async function initializeSession(session) {
|
|
104488
104513
|
const { page, serverUrl } = session;
|
|
104489
104514
|
page.on("console", (msg) => {
|
|
@@ -104540,6 +104565,7 @@ async function initializeSession(session) {
|
|
|
104540
104565
|
);
|
|
104541
104566
|
}
|
|
104542
104567
|
await page.evaluate(`document.fonts?.ready`);
|
|
104568
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout2);
|
|
104543
104569
|
if (session.options.format === "png") {
|
|
104544
104570
|
await initTransparentBackground(session.page);
|
|
104545
104571
|
}
|
|
@@ -104605,6 +104631,7 @@ async function initializeSession(session) {
|
|
|
104605
104631
|
await new Promise((r) => setTimeout(r, 100));
|
|
104606
104632
|
}
|
|
104607
104633
|
await page.evaluate(`document.fonts?.ready`);
|
|
104634
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout);
|
|
104608
104635
|
warmupRunning = false;
|
|
104609
104636
|
session.beginFrameTimeTicks = (warmupTicks + 10) * session.beginFrameIntervalMs;
|
|
104610
104637
|
if (session.options.format === "png") {
|
|
@@ -111635,6 +111662,27 @@ function createRenderJob(config2) {
|
|
|
111635
111662
|
function normalizeCompositionSrcPath(srcPath) {
|
|
111636
111663
|
return srcPath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
111637
111664
|
}
|
|
111665
|
+
function createStandaloneEntryRenderClone(root, host) {
|
|
111666
|
+
const hostClone = host.cloneNode(true);
|
|
111667
|
+
hostClone.setAttribute("data-start", "0");
|
|
111668
|
+
if (root === host) return hostClone;
|
|
111669
|
+
const rootClone = root.cloneNode(false);
|
|
111670
|
+
rootClone.appendChild(hostClone);
|
|
111671
|
+
return rootClone;
|
|
111672
|
+
}
|
|
111673
|
+
function replaceBodyWithRenderClone(body, renderClone) {
|
|
111674
|
+
while (body.firstChild) {
|
|
111675
|
+
body.removeChild(body.firstChild);
|
|
111676
|
+
}
|
|
111677
|
+
body.appendChild(renderClone);
|
|
111678
|
+
}
|
|
111679
|
+
function shouldUseStreamingEncode(cfg, outputFormat, workerCount, durationSeconds) {
|
|
111680
|
+
if (!cfg.enableStreamingEncode) return false;
|
|
111681
|
+
if (outputFormat === "png-sequence") return false;
|
|
111682
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return false;
|
|
111683
|
+
if (durationSeconds > cfg.streamingEncodeMaxDurationSeconds) return false;
|
|
111684
|
+
return workerCount === 1;
|
|
111685
|
+
}
|
|
111638
111686
|
function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
111639
111687
|
const normalizedEntryFile = normalizeCompositionSrcPath(entryFile);
|
|
111640
111688
|
const { document: document2 } = parseHTML(indexHtml);
|
|
@@ -111649,16 +111697,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
111649
111697
|
(candidate) => candidate.hasAttribute("data-composition-id")
|
|
111650
111698
|
) ?? null;
|
|
111651
111699
|
if (!root) return null;
|
|
111652
|
-
const
|
|
111653
|
-
|
|
111654
|
-
body.innerHTML = "";
|
|
111655
|
-
if (root === host) {
|
|
111656
|
-
body.appendChild(hostClone);
|
|
111657
|
-
return document2.toString();
|
|
111658
|
-
}
|
|
111659
|
-
const rootClone = root.cloneNode(false);
|
|
111660
|
-
rootClone.appendChild(hostClone);
|
|
111661
|
-
body.appendChild(rootClone);
|
|
111700
|
+
const renderClone = createStandaloneEntryRenderClone(root, host);
|
|
111701
|
+
replaceBodyWithRenderClone(body, renderClone);
|
|
111662
111702
|
return document2.toString();
|
|
111663
111703
|
}
|
|
111664
111704
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
@@ -111690,7 +111730,6 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
111690
111730
|
}
|
|
111691
111731
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
|
111692
111732
|
const chunkedEncodeSize = cfg.chunkSizeFrames;
|
|
111693
|
-
const enableStreamingEncode = cfg.enableStreamingEncode && !isPngSequence;
|
|
111694
111733
|
let peakRssBytes = 0;
|
|
111695
111734
|
let peakHeapUsedBytes = 0;
|
|
111696
111735
|
const sampleMemory = () => {
|
|
@@ -112259,6 +112298,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
112259
112298
|
await closeCaptureSession(probeSession);
|
|
112260
112299
|
probeSession = null;
|
|
112261
112300
|
}
|
|
112301
|
+
let useStreamingEncode = shouldUseStreamingEncode(cfg, outputFormat, workerCount, job.duration);
|
|
112302
|
+
log.info("streaming-encode gate", {
|
|
112303
|
+
enabled: useStreamingEncode,
|
|
112304
|
+
configFlag: cfg.enableStreamingEncode,
|
|
112305
|
+
outputFormat,
|
|
112306
|
+
workerCount,
|
|
112307
|
+
durationSeconds: job.duration,
|
|
112308
|
+
maxDurationSeconds: cfg.streamingEncodeMaxDurationSeconds
|
|
112309
|
+
});
|
|
112262
112310
|
const captureAttempts = [];
|
|
112263
112311
|
const FORMAT_EXT = {
|
|
112264
112312
|
mp4: ".mp4",
|
|
@@ -112798,28 +112846,47 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
112798
112846
|
} else {
|
|
112799
112847
|
let streamingEncoder = null;
|
|
112800
112848
|
let streamingEncoderClosed = false;
|
|
112801
|
-
if (
|
|
112802
|
-
|
|
112803
|
-
|
|
112804
|
-
|
|
112805
|
-
|
|
112806
|
-
|
|
112807
|
-
|
|
112808
|
-
|
|
112809
|
-
|
|
112810
|
-
|
|
112811
|
-
|
|
112812
|
-
|
|
112813
|
-
|
|
112814
|
-
|
|
112815
|
-
|
|
112816
|
-
|
|
112817
|
-
|
|
112818
|
-
|
|
112819
|
-
|
|
112849
|
+
if (useStreamingEncode) {
|
|
112850
|
+
try {
|
|
112851
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
112852
|
+
videoOnlyPath,
|
|
112853
|
+
{
|
|
112854
|
+
fps: job.config.fps,
|
|
112855
|
+
width,
|
|
112856
|
+
height,
|
|
112857
|
+
codec: preset.codec,
|
|
112858
|
+
preset: preset.preset,
|
|
112859
|
+
quality: effectiveQuality,
|
|
112860
|
+
bitrate: effectiveBitrate,
|
|
112861
|
+
pixelFormat: preset.pixelFormat,
|
|
112862
|
+
useGpu: job.config.useGpu,
|
|
112863
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
112864
|
+
hdr: preset.hdr
|
|
112865
|
+
},
|
|
112866
|
+
abortSignal
|
|
112867
|
+
);
|
|
112868
|
+
assertNotAborted();
|
|
112869
|
+
} catch (err) {
|
|
112870
|
+
if (abortSignal?.aborted) {
|
|
112871
|
+
if (streamingEncoder && !streamingEncoderClosed) {
|
|
112872
|
+
await streamingEncoder.close().catch(() => {
|
|
112873
|
+
});
|
|
112874
|
+
streamingEncoderClosed = true;
|
|
112875
|
+
}
|
|
112876
|
+
throw err;
|
|
112877
|
+
}
|
|
112878
|
+
useStreamingEncode = false;
|
|
112879
|
+
streamingEncoder = null;
|
|
112880
|
+
log.warn("[Render] Streaming encoder spawn failed; falling back to disk-frame encode.", {
|
|
112881
|
+
error: err instanceof Error ? err.message : String(err),
|
|
112882
|
+
outputFormat,
|
|
112883
|
+
workerCount,
|
|
112884
|
+
durationSeconds: job.duration
|
|
112885
|
+
});
|
|
112886
|
+
}
|
|
112820
112887
|
}
|
|
112821
112888
|
try {
|
|
112822
|
-
if (
|
|
112889
|
+
if (useStreamingEncode && streamingEncoder) {
|
|
112823
112890
|
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
112824
112891
|
const currentEncoder = streamingEncoder;
|
|
112825
112892
|
if (workerCount > 1) {
|