@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/index.js
CHANGED
|
@@ -98670,7 +98670,8 @@ var DEFAULT_CONFIG = {
|
|
|
98670
98670
|
forceScreenshot: false,
|
|
98671
98671
|
enableChunkedEncode: false,
|
|
98672
98672
|
chunkSizeFrames: 360,
|
|
98673
|
-
enableStreamingEncode:
|
|
98673
|
+
enableStreamingEncode: true,
|
|
98674
|
+
streamingEncodeMaxDurationSeconds: 240,
|
|
98674
98675
|
ffmpegEncodeTimeout: 6e5,
|
|
98675
98676
|
ffmpegProcessTimeout: 3e5,
|
|
98676
98677
|
ffmpegStreamingTimeout: 6e5,
|
|
@@ -98732,6 +98733,13 @@ function resolveConfig(overrides) {
|
|
|
98732
98733
|
"PRODUCER_ENABLE_STREAMING_ENCODE",
|
|
98733
98734
|
DEFAULT_CONFIG.enableStreamingEncode
|
|
98734
98735
|
),
|
|
98736
|
+
streamingEncodeMaxDurationSeconds: Math.max(
|
|
98737
|
+
0,
|
|
98738
|
+
envNum(
|
|
98739
|
+
"PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS",
|
|
98740
|
+
DEFAULT_CONFIG.streamingEncodeMaxDurationSeconds
|
|
98741
|
+
)
|
|
98742
|
+
),
|
|
98735
98743
|
ffmpegEncodeTimeout: envNum("FFMPEG_ENCODE_TIMEOUT_MS", DEFAULT_CONFIG.ffmpegEncodeTimeout),
|
|
98736
98744
|
ffmpegProcessTimeout: envNum("FFMPEG_PROCESS_TIMEOUT_MS", DEFAULT_CONFIG.ffmpegProcessTimeout),
|
|
98737
98745
|
ffmpegStreamingTimeout: envNum(
|
|
@@ -101695,6 +101703,23 @@ async function applyVideoMetadataHints(page, hints) {
|
|
|
101695
101703
|
[...hints]
|
|
101696
101704
|
);
|
|
101697
101705
|
}
|
|
101706
|
+
async function waitForOptionalTailwindReady(page, timeoutMs) {
|
|
101707
|
+
const hasTailwindReady = await page.evaluate(
|
|
101708
|
+
`(() => { const ready = window.__tailwindReady; return !!ready && typeof ready.then === "function"; })()`
|
|
101709
|
+
);
|
|
101710
|
+
if (!hasTailwindReady) return;
|
|
101711
|
+
const ready = await Promise.race([
|
|
101712
|
+
page.evaluate(
|
|
101713
|
+
`Promise.resolve(window.__tailwindReady).then(() => true, () => false)`
|
|
101714
|
+
),
|
|
101715
|
+
new Promise((resolve14) => setTimeout(() => resolve14(false), timeoutMs))
|
|
101716
|
+
]);
|
|
101717
|
+
if (!ready) {
|
|
101718
|
+
throw new Error(
|
|
101719
|
+
`[FrameCapture] window.__tailwindReady not resolved after ${timeoutMs}ms. Tailwind browser runtime must finish before frame capture starts.`
|
|
101720
|
+
);
|
|
101721
|
+
}
|
|
101722
|
+
}
|
|
101698
101723
|
async function initializeSession(session) {
|
|
101699
101724
|
const { page, serverUrl } = session;
|
|
101700
101725
|
page.on("console", (msg) => {
|
|
@@ -101751,6 +101776,7 @@ async function initializeSession(session) {
|
|
|
101751
101776
|
);
|
|
101752
101777
|
}
|
|
101753
101778
|
await page.evaluate(`document.fonts?.ready`);
|
|
101779
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout2);
|
|
101754
101780
|
if (session.options.format === "png") {
|
|
101755
101781
|
await initTransparentBackground(session.page);
|
|
101756
101782
|
}
|
|
@@ -101816,6 +101842,7 @@ async function initializeSession(session) {
|
|
|
101816
101842
|
await new Promise((r) => setTimeout(r, 100));
|
|
101817
101843
|
}
|
|
101818
101844
|
await page.evaluate(`document.fonts?.ready`);
|
|
101845
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout);
|
|
101819
101846
|
warmupRunning = false;
|
|
101820
101847
|
session.beginFrameTimeTicks = (warmupTicks + 10) * session.beginFrameIntervalMs;
|
|
101821
101848
|
if (session.options.format === "png") {
|
|
@@ -111470,6 +111497,27 @@ function createRenderJob(config2) {
|
|
|
111470
111497
|
function normalizeCompositionSrcPath(srcPath) {
|
|
111471
111498
|
return srcPath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
111472
111499
|
}
|
|
111500
|
+
function createStandaloneEntryRenderClone(root, host) {
|
|
111501
|
+
const hostClone = host.cloneNode(true);
|
|
111502
|
+
hostClone.setAttribute("data-start", "0");
|
|
111503
|
+
if (root === host) return hostClone;
|
|
111504
|
+
const rootClone = root.cloneNode(false);
|
|
111505
|
+
rootClone.appendChild(hostClone);
|
|
111506
|
+
return rootClone;
|
|
111507
|
+
}
|
|
111508
|
+
function replaceBodyWithRenderClone(body, renderClone) {
|
|
111509
|
+
while (body.firstChild) {
|
|
111510
|
+
body.removeChild(body.firstChild);
|
|
111511
|
+
}
|
|
111512
|
+
body.appendChild(renderClone);
|
|
111513
|
+
}
|
|
111514
|
+
function shouldUseStreamingEncode(cfg, outputFormat, workerCount, durationSeconds) {
|
|
111515
|
+
if (!cfg.enableStreamingEncode) return false;
|
|
111516
|
+
if (outputFormat === "png-sequence") return false;
|
|
111517
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return false;
|
|
111518
|
+
if (durationSeconds > cfg.streamingEncodeMaxDurationSeconds) return false;
|
|
111519
|
+
return workerCount === 1;
|
|
111520
|
+
}
|
|
111473
111521
|
function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
111474
111522
|
const normalizedEntryFile = normalizeCompositionSrcPath(entryFile);
|
|
111475
111523
|
const { document: document2 } = parseHTML(indexHtml);
|
|
@@ -111484,16 +111532,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
111484
111532
|
(candidate) => candidate.hasAttribute("data-composition-id")
|
|
111485
111533
|
) ?? null;
|
|
111486
111534
|
if (!root) return null;
|
|
111487
|
-
const
|
|
111488
|
-
|
|
111489
|
-
body.innerHTML = "";
|
|
111490
|
-
if (root === host) {
|
|
111491
|
-
body.appendChild(hostClone);
|
|
111492
|
-
return document2.toString();
|
|
111493
|
-
}
|
|
111494
|
-
const rootClone = root.cloneNode(false);
|
|
111495
|
-
rootClone.appendChild(hostClone);
|
|
111496
|
-
body.appendChild(rootClone);
|
|
111535
|
+
const renderClone = createStandaloneEntryRenderClone(root, host);
|
|
111536
|
+
replaceBodyWithRenderClone(body, renderClone);
|
|
111497
111537
|
return document2.toString();
|
|
111498
111538
|
}
|
|
111499
111539
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
@@ -111525,7 +111565,6 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
111525
111565
|
}
|
|
111526
111566
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
|
111527
111567
|
const chunkedEncodeSize = cfg.chunkSizeFrames;
|
|
111528
|
-
const enableStreamingEncode = cfg.enableStreamingEncode && !isPngSequence;
|
|
111529
111568
|
let peakRssBytes = 0;
|
|
111530
111569
|
let peakHeapUsedBytes = 0;
|
|
111531
111570
|
const sampleMemory = () => {
|
|
@@ -112094,6 +112133,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
112094
112133
|
await closeCaptureSession(probeSession);
|
|
112095
112134
|
probeSession = null;
|
|
112096
112135
|
}
|
|
112136
|
+
let useStreamingEncode = shouldUseStreamingEncode(cfg, outputFormat, workerCount, job.duration);
|
|
112137
|
+
log.info("streaming-encode gate", {
|
|
112138
|
+
enabled: useStreamingEncode,
|
|
112139
|
+
configFlag: cfg.enableStreamingEncode,
|
|
112140
|
+
outputFormat,
|
|
112141
|
+
workerCount,
|
|
112142
|
+
durationSeconds: job.duration,
|
|
112143
|
+
maxDurationSeconds: cfg.streamingEncodeMaxDurationSeconds
|
|
112144
|
+
});
|
|
112097
112145
|
const captureAttempts = [];
|
|
112098
112146
|
const FORMAT_EXT = {
|
|
112099
112147
|
mp4: ".mp4",
|
|
@@ -112633,28 +112681,47 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
112633
112681
|
} else {
|
|
112634
112682
|
let streamingEncoder = null;
|
|
112635
112683
|
let streamingEncoderClosed = false;
|
|
112636
|
-
if (
|
|
112637
|
-
|
|
112638
|
-
|
|
112639
|
-
|
|
112640
|
-
|
|
112641
|
-
|
|
112642
|
-
|
|
112643
|
-
|
|
112644
|
-
|
|
112645
|
-
|
|
112646
|
-
|
|
112647
|
-
|
|
112648
|
-
|
|
112649
|
-
|
|
112650
|
-
|
|
112651
|
-
|
|
112652
|
-
|
|
112653
|
-
|
|
112654
|
-
|
|
112684
|
+
if (useStreamingEncode) {
|
|
112685
|
+
try {
|
|
112686
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
112687
|
+
videoOnlyPath,
|
|
112688
|
+
{
|
|
112689
|
+
fps: job.config.fps,
|
|
112690
|
+
width,
|
|
112691
|
+
height,
|
|
112692
|
+
codec: preset.codec,
|
|
112693
|
+
preset: preset.preset,
|
|
112694
|
+
quality: effectiveQuality,
|
|
112695
|
+
bitrate: effectiveBitrate,
|
|
112696
|
+
pixelFormat: preset.pixelFormat,
|
|
112697
|
+
useGpu: job.config.useGpu,
|
|
112698
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
112699
|
+
hdr: preset.hdr
|
|
112700
|
+
},
|
|
112701
|
+
abortSignal
|
|
112702
|
+
);
|
|
112703
|
+
assertNotAborted();
|
|
112704
|
+
} catch (err) {
|
|
112705
|
+
if (abortSignal?.aborted) {
|
|
112706
|
+
if (streamingEncoder && !streamingEncoderClosed) {
|
|
112707
|
+
await streamingEncoder.close().catch(() => {
|
|
112708
|
+
});
|
|
112709
|
+
streamingEncoderClosed = true;
|
|
112710
|
+
}
|
|
112711
|
+
throw err;
|
|
112712
|
+
}
|
|
112713
|
+
useStreamingEncode = false;
|
|
112714
|
+
streamingEncoder = null;
|
|
112715
|
+
log.warn("[Render] Streaming encoder spawn failed; falling back to disk-frame encode.", {
|
|
112716
|
+
error: err instanceof Error ? err.message : String(err),
|
|
112717
|
+
outputFormat,
|
|
112718
|
+
workerCount,
|
|
112719
|
+
durationSeconds: job.duration
|
|
112720
|
+
});
|
|
112721
|
+
}
|
|
112655
112722
|
}
|
|
112656
112723
|
try {
|
|
112657
|
-
if (
|
|
112724
|
+
if (useStreamingEncode && streamingEncoder) {
|
|
112658
112725
|
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
112659
112726
|
const currentEncoder = streamingEncoder;
|
|
112660
112727
|
if (workerCount > 1) {
|