@hyperframes/producer 0.8.145 → 0.8.146
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/distributed.js +44 -36
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +214 -68
- package/dist/index.js +60 -52
- package/dist/public-server.js +54 -46
- package/dist/services/htmlCompiler.d.ts +1 -0
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -292,7 +292,7 @@ function buildVirtualTimeShim(options) {
|
|
|
292
292
|
const seedRandomFromFrame = options.seedRandomFromFrame === true;
|
|
293
293
|
const seededRandomBlock = seedRandomFromFrame ? String.raw`
|
|
294
294
|
// Seeded Math.random / crypto.getRandomValues, keyed by virtual time.
|
|
295
|
-
// Mulberry32
|
|
295
|
+
// Mulberry32: single uint32 state, deterministic, fast.
|
|
296
296
|
var rngState = 0;
|
|
297
297
|
function mulberry32() {
|
|
298
298
|
rngState |= 0; rngState = (rngState + 0x6D2B79F5) | 0;
|
|
@@ -303,7 +303,7 @@ function buildVirtualTimeShim(options) {
|
|
|
303
303
|
}
|
|
304
304
|
function reseedRngFromTime(ms) {
|
|
305
305
|
var ms32 = Math.max(0, Math.floor(Number(ms) || 0)) | 0;
|
|
306
|
-
// Knuth's multiplicative hash + golden-ratio offset
|
|
306
|
+
// Knuth's multiplicative hash + golden-ratio offset gives a well-
|
|
307
307
|
// distributed seed even for frame 0 (otherwise rngState=0 degenerates
|
|
308
308
|
// the PRNG's first few outputs).
|
|
309
309
|
rngState = (Math.imul(ms32, -1640531527) + 0x9E3779B9) | 0;
|
|
@@ -545,7 +545,7 @@ function buildRenderModeScript(fps) {
|
|
|
545
545
|
};
|
|
546
546
|
window.__playerReady = true;
|
|
547
547
|
// Media-fallback player has no timeline to bind, so render-ready is immediate.
|
|
548
|
-
// init.ts defers __renderReady until the timeline is bound
|
|
548
|
+
// init.ts defers __renderReady until the timeline is bound (a different runtime).
|
|
549
549
|
window.__renderReady = true;
|
|
550
550
|
return true;
|
|
551
551
|
}
|
|
@@ -2136,7 +2136,7 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrVideoFrameSources, hdrStart
|
|
|
2136
2136
|
if (matrix && !isTranslationOnly) {
|
|
2137
2137
|
if (clipped && log) {
|
|
2138
2138
|
log.debug(
|
|
2139
|
-
`HDR clip rect on affine-transformed element ${el.id}
|
|
2139
|
+
`HDR clip rect on affine-transformed element ${el.id}: clip not applied (affine scissor not yet supported)`
|
|
2140
2140
|
);
|
|
2141
2141
|
}
|
|
2142
2142
|
blitRgb48leAffine(
|
|
@@ -2193,7 +2193,7 @@ function blitHdrImageLayer(canvas, el, hdrImageBuffers, hdrImageTransferCache, w
|
|
|
2193
2193
|
return;
|
|
2194
2194
|
}
|
|
2195
2195
|
if (el.clipRect && log) {
|
|
2196
|
-
log.debug(`HDR clip rect on image element ${el.id}
|
|
2196
|
+
log.debug(`HDR clip rect on image element ${el.id}: clip not yet supported for images`);
|
|
2197
2197
|
}
|
|
2198
2198
|
try {
|
|
2199
2199
|
if (hdrPerf) hdrPerf.hdrImageLayerBlits += 1;
|
|
@@ -4385,7 +4385,7 @@ import {
|
|
|
4385
4385
|
function formatDowngradeWarning(outputFormat, forcedHdrWithoutSources) {
|
|
4386
4386
|
const hdrSourceReason = forcedHdrWithoutSources ? "HDR was forced without detected HDR sources" : "HDR source detected";
|
|
4387
4387
|
const formatReason = outputFormat === "hls" ? "HLS output is SDR-only (H.264 in MPEG-TS)" : "HDR + alpha is not supported";
|
|
4388
|
-
return `[Render] ${hdrSourceReason}, but format is "${outputFormat}"
|
|
4388
|
+
return `[Render] ${hdrSourceReason}, but format is "${outputFormat}", so falling back to SDR. ${formatReason}. Use --format mp4 for HDR10 output.`;
|
|
4389
4389
|
}
|
|
4390
4390
|
function findRenderHdrAutoPromotionTrigger(input) {
|
|
4391
4391
|
for (const extracted of input.extractionResult?.extracted ?? []) {
|
|
@@ -4398,12 +4398,12 @@ function findRenderHdrAutoPromotionTrigger(input) {
|
|
|
4398
4398
|
function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources) {
|
|
4399
4399
|
if (forcedHdrWithoutSources) {
|
|
4400
4400
|
input.log.warn(
|
|
4401
|
-
"[Render] HDR forced by --hdr flag, but no HDR sources were detected
|
|
4401
|
+
"[Render] HDR forced by --hdr flag, but no HDR sources were detected, defaulting to HLG. SDR-only compositions may look perceptually wrong on HDR displays."
|
|
4402
4402
|
);
|
|
4403
4403
|
}
|
|
4404
4404
|
if (!effectiveHdr) {
|
|
4405
4405
|
input.log.info(
|
|
4406
|
-
hdrMode === "force-sdr" ? "[Render] SDR forced by --sdr flag" : "[Render] No HDR sources detected
|
|
4406
|
+
hdrMode === "force-sdr" ? "[Render] SDR forced by --sdr flag" : "[Render] No HDR sources detected, rendering SDR"
|
|
4407
4407
|
);
|
|
4408
4408
|
return;
|
|
4409
4409
|
}
|
|
@@ -4418,9 +4418,9 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
|
|
|
4418
4418
|
);
|
|
4419
4419
|
return;
|
|
4420
4420
|
}
|
|
4421
|
-
const reason = forcedHdrWithoutSources ? "forced by --hdr flag (no HDR sources detected
|
|
4421
|
+
const reason = forcedHdrWithoutSources ? "forced by --hdr flag (no HDR sources detected, defaulting to HLG)" : "forced by --hdr flag";
|
|
4422
4422
|
input.log.info(
|
|
4423
|
-
`[Render] HDR ${reason}
|
|
4423
|
+
`[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
|
|
4424
4424
|
);
|
|
4425
4425
|
}
|
|
4426
4426
|
function resolveEffectiveHdrMode(input) {
|
|
@@ -4516,14 +4516,14 @@ function resolveRenderWorkerCount(totalFrames, requestedWorkers, cfg, compiled,
|
|
|
4516
4516
|
const reasonCodes = new Set(compiled.renderModeHints.reasons.map((r) => r.code));
|
|
4517
4517
|
if (reasonCodes.has("htmlInCanvas")) {
|
|
4518
4518
|
log.warn(
|
|
4519
|
-
"[Render] html-in-canvas (drawElementImage) detected
|
|
4519
|
+
"[Render] html-in-canvas (drawElementImage) detected, pinning to 1 worker (Chrome concurrency limitation).",
|
|
4520
4520
|
{ requestedWorkers }
|
|
4521
4521
|
);
|
|
4522
4522
|
return 1;
|
|
4523
4523
|
}
|
|
4524
4524
|
if (cfg.lowMemoryMode && requestedWorkers === void 0) {
|
|
4525
4525
|
log.info(
|
|
4526
|
-
"[Render] Low-memory profile
|
|
4526
|
+
"[Render] Low-memory profile, pinning to 1 capture worker (auto-worker calibration skipped)."
|
|
4527
4527
|
);
|
|
4528
4528
|
return 1;
|
|
4529
4529
|
}
|
|
@@ -5158,7 +5158,7 @@ function assertVideoFrameCoverage(reports, threshold) {
|
|
|
5158
5158
|
const thresholdPct = (threshold * 100).toFixed(1);
|
|
5159
5159
|
const suffix = sorted.length > 1 ? ` (+${sorted.length - 1} more clip(s) below threshold)` : "";
|
|
5160
5160
|
throw new VideoFrameCoverageError(
|
|
5161
|
-
`Video "${worst.videoId}" captured ${worst.capturedFrames} of expected ${worst.expectedFrames} frames (coverage ${pct}%, threshold ${thresholdPct}%). check/snapshot may pass while the encoded MP4 renders this clip blank
|
|
5161
|
+
`Video "${worst.videoId}" captured ${worst.capturedFrames} of expected ${worst.expectedFrames} frames (coverage ${pct}%, threshold ${thresholdPct}%). check/snapshot may pass while the encoded MP4 renders this clip blank, so the render is aborted to prevent shipping a wrong MP4.${suffix} Set HF_VIDEO_COVERAGE_THRESHOLD=0 to disable this gate.`,
|
|
5162
5162
|
{ threshold, worst, failedReports: sorted }
|
|
5163
5163
|
);
|
|
5164
5164
|
}
|
|
@@ -6054,7 +6054,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6054
6054
|
if (tagName !== "video") {
|
|
6055
6055
|
if (error instanceof NotMediaPayloadError2) {
|
|
6056
6056
|
log?.warn(
|
|
6057
|
-
`[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file
|
|
6057
|
+
`[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file, so the element is dropped from the render. Point it at a rendered media file.`
|
|
6058
6058
|
);
|
|
6059
6059
|
}
|
|
6060
6060
|
return { duration: null, resolvedPath: filePath };
|
|
@@ -6143,7 +6143,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
6143
6143
|
if (r.maxDuration != null && shouldClampResolvedMediaDuration(r.tagName, r.duration, r.maxDuration)) {
|
|
6144
6144
|
clampList.push({ id: r.id, duration: r.maxDuration });
|
|
6145
6145
|
log?.warn(
|
|
6146
|
-
`[compile] Audio "${r.id}" (${r.src}) is ${r.maxDuration.toFixed(2)}s but its data-duration is ${r.duration.toFixed(2)}s
|
|
6146
|
+
`[compile] Audio "${r.id}" (${r.src}) is ${r.maxDuration.toFixed(2)}s but its data-duration is ${r.duration.toFixed(2)}s, so the slot is shortened to the media length. Set data-duration to ~${r.maxDuration.toFixed(2)}s, trim data-media-start, or use a longer/looping source if that isn't intended.`
|
|
6147
6147
|
);
|
|
6148
6148
|
}
|
|
6149
6149
|
}
|
|
@@ -6532,7 +6532,7 @@ ${safeText}
|
|
|
6532
6532
|
} else {
|
|
6533
6533
|
if (download.reason instanceof ScriptIntegrityError) throw download.reason;
|
|
6534
6534
|
defaultLogger.warn(
|
|
6535
|
-
`[Compiler] WARNING: Failed to download CDN script: ${src}
|
|
6535
|
+
`[Compiler] WARNING: Failed to download CDN script: ${src} (${download.reason}). The render may fail if this script is required (e.g. GSAP). Consider bundling it locally in your project.`
|
|
6536
6536
|
);
|
|
6537
6537
|
}
|
|
6538
6538
|
}
|
|
@@ -6584,7 +6584,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
6584
6584
|
}
|
|
6585
6585
|
if (externalAssets.size > 0) {
|
|
6586
6586
|
defaultLogger.info(
|
|
6587
|
-
`[Compiler] Found ${externalAssets.size} asset(s) outside project directory
|
|
6587
|
+
`[Compiler] Found ${externalAssets.size} asset(s) outside project directory, will copy to render output`
|
|
6588
6588
|
);
|
|
6589
6589
|
}
|
|
6590
6590
|
return {
|
|
@@ -6609,7 +6609,7 @@ async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLab
|
|
|
6609
6609
|
urlToLocal.set(url, localPath);
|
|
6610
6610
|
} catch (err) {
|
|
6611
6611
|
const identity = safeDownloadUrlIdentity(url);
|
|
6612
|
-
defaultLogger.warn(`[Compiler] ${warnLabel}
|
|
6612
|
+
defaultLogger.warn(`[Compiler] ${warnLabel}, using original URL as fallback.`, {
|
|
6613
6613
|
urlFingerprint: identity.urlFingerprint,
|
|
6614
6614
|
host: identity.host,
|
|
6615
6615
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -6702,7 +6702,7 @@ async function fetchExternalStylesheetCss(href) {
|
|
|
6702
6702
|
timeoutMs: 15e3
|
|
6703
6703
|
});
|
|
6704
6704
|
} catch (err) {
|
|
6705
|
-
defaultLogger.warn("[Compiler] External stylesheet fetch failed
|
|
6705
|
+
defaultLogger.warn("[Compiler] External stylesheet fetch failed, preserving link tag.", {
|
|
6706
6706
|
urlFingerprint: identity.urlFingerprint,
|
|
6707
6707
|
host: identity.host,
|
|
6708
6708
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -7346,12 +7346,15 @@ async function resolveCompositionDurations(page, unresolved) {
|
|
|
7346
7346
|
const resolved = [];
|
|
7347
7347
|
for (const id of compIds) {
|
|
7348
7348
|
const tl = timelines[id];
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7349
|
+
let dur = 0;
|
|
7350
|
+
try {
|
|
7351
|
+
dur = typeof tl?.duration === "function" ? Number(tl.duration()) : 0;
|
|
7352
|
+
} catch {
|
|
7353
|
+
dur = 0;
|
|
7354
|
+
}
|
|
7355
|
+
if (dur > 0) {
|
|
7356
|
+
resolved.push({ id, duration: dur, source: "__timelines" });
|
|
7357
|
+
continue;
|
|
7355
7358
|
}
|
|
7356
7359
|
const el = document.getElementById(id);
|
|
7357
7360
|
if (el) {
|
|
@@ -7447,28 +7450,28 @@ async function runCompileStage(input) {
|
|
|
7447
7450
|
cfg.useDrawElement = false;
|
|
7448
7451
|
deCompileGate = "3d";
|
|
7449
7452
|
log.info(
|
|
7450
|
-
"[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility)
|
|
7453
|
+
"[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7451
7454
|
);
|
|
7452
7455
|
}
|
|
7453
7456
|
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
|
|
7454
7457
|
cfg.useDrawElement = false;
|
|
7455
7458
|
deCompileGate = "mix_blend_mode";
|
|
7456
7459
|
log.info(
|
|
7457
|
-
"[Render] Fast capture: composition uses mix-blend-mode
|
|
7460
|
+
"[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7458
7461
|
);
|
|
7459
7462
|
}
|
|
7460
7463
|
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
|
|
7461
7464
|
cfg.useDrawElement = false;
|
|
7462
7465
|
deCompileGate = "ancestor_background_image";
|
|
7463
7466
|
log.info(
|
|
7464
|
-
"[Render] Fast capture: composition root's ancestors (body/html/wrapper) carry a background-image
|
|
7467
|
+
"[Render] Fast capture: composition root's ancestors (body/html/wrapper) carry a background-image, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
7465
7468
|
);
|
|
7466
7469
|
}
|
|
7467
7470
|
if (cfg.useDrawElement && process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE !== "true" && compiled.hasShaderTransitions) {
|
|
7468
7471
|
cfg.useDrawElement = false;
|
|
7469
7472
|
deCompileGate = "shader_transitions";
|
|
7470
7473
|
log.info(
|
|
7471
|
-
"[Render] Fast capture: composition uses shader transitions
|
|
7474
|
+
"[Render] Fast capture: composition uses shader transitions, so disabling drawElementImage so page-side compositing stays available."
|
|
7472
7475
|
);
|
|
7473
7476
|
}
|
|
7474
7477
|
if (!cfg.useDrawElement && cfg.pageSideCompositingAutoDisabled) {
|
|
@@ -7796,7 +7799,7 @@ async function runProbeStage(input) {
|
|
|
7796
7799
|
} else {
|
|
7797
7800
|
beginFrameStalled = true;
|
|
7798
7801
|
log.warn(
|
|
7799
|
-
"[Render] BeginFrame liveness probe timed out
|
|
7802
|
+
"[Render] BeginFrame liveness probe timed out: this composition stalls BeginFrame on this host (SwiftShader heavy-layer pattern). Relaunching the probe browser in screenshot capture mode; the render will use screenshot capture throughout.",
|
|
7800
7803
|
{ probeTimeoutMs }
|
|
7801
7804
|
);
|
|
7802
7805
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
@@ -8010,7 +8013,7 @@ async function runProbeStage(input) {
|
|
|
8010
8013
|
video.start = win.visibleStart;
|
|
8011
8014
|
video.end = win.visibleEnd;
|
|
8012
8015
|
log.info(
|
|
8013
|
-
`[Probe] Runtime video discovery: ${video.id} visible ${win.visibleStart.toFixed(2)}s
|
|
8016
|
+
`[Probe] Runtime video discovery: ${video.id} visible ${win.visibleStart.toFixed(2)}s to ${win.visibleEnd.toFixed(2)}s`
|
|
8014
8017
|
);
|
|
8015
8018
|
}
|
|
8016
8019
|
}
|
|
@@ -8058,7 +8061,7 @@ async function runProbeStage(input) {
|
|
|
8058
8061
|
});
|
|
8059
8062
|
if (!timelinesInfo.gsapLoaded) {
|
|
8060
8063
|
diagnostics.push(
|
|
8061
|
-
"GSAP is not loaded
|
|
8064
|
+
"GSAP is not loaded. The CDN script may have failed to download. Bundle GSAP locally in your project instead of using a CDN <script src>."
|
|
8062
8065
|
);
|
|
8063
8066
|
} else if (timelinesInfo.timelineKeys.length === 0) {
|
|
8064
8067
|
diagnostics.push(
|
|
@@ -8075,10 +8078,10 @@ async function runProbeStage(input) {
|
|
|
8075
8078
|
log.warn("Failed to gather browser diagnostics for zero-duration composition", {
|
|
8076
8079
|
error: err instanceof Error ? err.message : String(err)
|
|
8077
8080
|
});
|
|
8078
|
-
diagnostics.push("(Could not gather browser diagnostics
|
|
8081
|
+
diagnostics.push("(Could not gather browser diagnostics. The page may have crashed.)");
|
|
8079
8082
|
}
|
|
8080
8083
|
const hint = diagnostics.length > 0 ? "\n\nDiagnostics:\n - " + diagnostics.join("\n - ") : "\n\nCheck that GSAP timelines are registered on window.__timelines.";
|
|
8081
|
-
throw new Error("Composition duration is 0
|
|
8084
|
+
throw new Error("Composition duration is 0, so this would produce a black video." + hint);
|
|
8082
8085
|
}
|
|
8083
8086
|
if (probeSession) {
|
|
8084
8087
|
const failedRequests = probeSession.browserConsoleBuffer.filter(isActionableProbeFailure);
|
|
@@ -8159,6 +8162,7 @@ import {
|
|
|
8159
8162
|
detectTransfer,
|
|
8160
8163
|
extractAllVideoFrames,
|
|
8161
8164
|
extractMediaMetadata as extractMediaMetadata3,
|
|
8165
|
+
isVideoPastTimelineEnd,
|
|
8162
8166
|
isHdrColorSpace as isHdrColorSpace2,
|
|
8163
8167
|
resolveProjectRelativeSrc as resolveProjectRelativeSrc3,
|
|
8164
8168
|
runVideoExtractionWithRetry,
|
|
@@ -8373,6 +8377,9 @@ async function runExtractVideosStage(input) {
|
|
|
8373
8377
|
} = input;
|
|
8374
8378
|
const stage2Start = Date.now();
|
|
8375
8379
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
8380
|
+
composition.videos = composition.videos.filter(
|
|
8381
|
+
(video) => !isVideoPastTimelineEnd(video, composition.duration)
|
|
8382
|
+
);
|
|
8376
8383
|
let frameLookup = null;
|
|
8377
8384
|
let extractionResult = null;
|
|
8378
8385
|
let failureToEnforce = null;
|
|
@@ -8459,7 +8466,8 @@ async function runExtractVideosStage(input) {
|
|
|
8459
8466
|
timelineEnd: composition.duration,
|
|
8460
8467
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
8461
8468
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
8462
|
-
deferRangeExtraction
|
|
8469
|
+
deferRangeExtraction,
|
|
8470
|
+
contentKeyedDirs: [join16(compiledDir, REMOTE_MEDIA_SUBDIR)]
|
|
8463
8471
|
},
|
|
8464
8472
|
abortSignal,
|
|
8465
8473
|
cfg,
|
|
@@ -11085,7 +11093,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11085
11093
|
const madeProgress = captureAttemptMadeProgress(frameCount, remainingCount);
|
|
11086
11094
|
if (!madeProgress) {
|
|
11087
11095
|
options.log.warn(
|
|
11088
|
-
"[Render] Capture attempt made no forward progress; composition is likely structurally broken
|
|
11096
|
+
"[Render] Capture attempt made no forward progress; composition is likely structurally broken, not retrying.",
|
|
11089
11097
|
{ attempt, frameCount, remainingCount, workers: currentWorkers }
|
|
11090
11098
|
);
|
|
11091
11099
|
}
|
|
@@ -11154,7 +11162,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11154
11162
|
}
|
|
11155
11163
|
if (!madeProgress) {
|
|
11156
11164
|
options.log.warn(
|
|
11157
|
-
"[Render] Capture attempt made no forward progress; composition is likely structurally broken
|
|
11165
|
+
"[Render] Capture attempt made no forward progress; composition is likely structurally broken, not retrying.",
|
|
11158
11166
|
{ attempt, frameCount, remainingCount, workers: currentWorkers }
|
|
11159
11167
|
);
|
|
11160
11168
|
}
|
|
@@ -11617,7 +11625,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
11617
11625
|
execution.defer("remove workDir", () => {
|
|
11618
11626
|
if (job.config.debug) return;
|
|
11619
11627
|
if (job.status === "complete" && process.env.KEEP_TEMP === "1") {
|
|
11620
|
-
log.info("KEEP_TEMP=1
|
|
11628
|
+
log.info("KEEP_TEMP=1, leaving workDir on disk for inspection", { workDir });
|
|
11621
11629
|
return;
|
|
11622
11630
|
}
|
|
11623
11631
|
rmSync9(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
@@ -11854,7 +11862,7 @@ async function executeRenderPipeline(input) {
|
|
|
11854
11862
|
captureForceScreenshot = true;
|
|
11855
11863
|
updateCaptureObservability({ forceScreenshot: captureForceScreenshot });
|
|
11856
11864
|
log.info(
|
|
11857
|
-
"[Render] Low-memory render profile active
|
|
11865
|
+
"[Render] Low-memory render profile active: screenshot capture, auto-worker calibration skipped" + (job.config.workers === void 0 ? ", pinned to 1 worker" : "") + ". Override with --no-low-memory-mode or PRODUCER_LOW_MEMORY_MODE=false.",
|
|
11858
11866
|
{ totalMemMb: getSystemTotalMb(), thresholdMb: LOW_MEMORY_TOTAL_MB_THRESHOLD }
|
|
11859
11867
|
);
|
|
11860
11868
|
}
|
|
@@ -12422,7 +12430,7 @@ async function executeRenderPipeline(input) {
|
|
|
12422
12430
|
cfg.useDrawElement = false;
|
|
12423
12431
|
deClampReason = workerCount > 1 ? "parallel" : "disk_path";
|
|
12424
12432
|
log.info(
|
|
12425
|
-
"[Render] Fast capture: default-on drawElement disabled for this render
|
|
12433
|
+
"[Render] Fast capture: default-on drawElement disabled for this render: " + (workerCount > 1 ? "parallel capture" : "the disk capture path") + " has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override."
|
|
12426
12434
|
);
|
|
12427
12435
|
if (probeSession && probeSession.captureMode === "drawelement") {
|
|
12428
12436
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
@@ -12518,7 +12526,7 @@ async function executeRenderPipeline(input) {
|
|
|
12518
12526
|
});
|
|
12519
12527
|
updateCaptureObservability({ forceScreenshot: captureForceScreenshot });
|
|
12520
12528
|
log.info(
|
|
12521
|
-
`[Render] Page-side compositing enabled
|
|
12529
|
+
`[Render] Page-side compositing enabled, bypassing Node-side layered shader-blend path. Engine will capture one ${needsAlpha ? "RGBA PNG" : "opaque RGB"} screenshot per output frame.`
|
|
12522
12530
|
);
|
|
12523
12531
|
}
|
|
12524
12532
|
const useLayeredComposite = !usePageSideCompositingForTransitions && shouldUseLayeredComposite({
|
|
@@ -13015,7 +13023,7 @@ async function executeRenderPipeline(input) {
|
|
|
13015
13023
|
cfg.useDrawElement = false;
|
|
13016
13024
|
deClampReason = deClampReason ?? "disk_path";
|
|
13017
13025
|
log.info(
|
|
13018
|
-
"[Render] Fast capture: drawElement disabled for the disk fallback
|
|
13026
|
+
"[Render] Fast capture: drawElement disabled for the disk fallback: streaming encoder spawn failed and the disk path has no runtime self-verification."
|
|
13019
13027
|
);
|
|
13020
13028
|
if (probeSession && probeSession.captureMode === "drawelement") {
|
|
13021
13029
|
lastBrowserConsole = probeSession.browserConsoleBuffer;
|
|
@@ -13633,10 +13641,10 @@ function validateJsonSafeValue(value, field) {
|
|
|
13633
13641
|
walkVariables(value, field, /* @__PURE__ */ new WeakSet());
|
|
13634
13642
|
}
|
|
13635
13643
|
var LEAF_REJECTIONS = {
|
|
13636
|
-
undefined: "undefined leaves are silently dropped by JSON.stringify
|
|
13644
|
+
undefined: "undefined leaves are silently dropped by JSON.stringify. Use null if you mean an absent value",
|
|
13637
13645
|
function: "functions are not JSON-serializable",
|
|
13638
13646
|
symbol: "Symbols are not JSON-serializable",
|
|
13639
|
-
bigint: "BigInt values throw at JSON.stringify
|
|
13647
|
+
bigint: "BigInt values throw at JSON.stringify. Encode as a string if you need 64-bit integers"
|
|
13640
13648
|
};
|
|
13641
13649
|
function walkVariables(value, path, seen) {
|
|
13642
13650
|
const t = typeof value;
|
|
@@ -13657,7 +13665,7 @@ function walkVariables(value, path, seen) {
|
|
|
13657
13665
|
if (seen.has(value)) {
|
|
13658
13666
|
throw new InvalidConfigError(
|
|
13659
13667
|
path,
|
|
13660
|
-
"circular reference detected
|
|
13668
|
+
"circular reference detected: JSON.stringify cannot serialize cycles"
|
|
13661
13669
|
);
|
|
13662
13670
|
}
|
|
13663
13671
|
seen.add(value);
|
|
@@ -13997,7 +14005,7 @@ function validateOutputResolutionOverride(body) {
|
|
|
13997
14005
|
return `Invalid outputResolution "${body.outputResolution}". Must be one of: landscape, portrait, landscape-4k, portrait-4k, square, square-4k (aliases: 1080p, 4k, \u2026).`;
|
|
13998
14006
|
}
|
|
13999
14007
|
if (normalized !== void 0 && (body.format === "webm" || body.format === "mov")) {
|
|
14000
|
-
return `outputResolution is not supported with format "${body.format}"
|
|
14008
|
+
return `outputResolution is not supported with format "${body.format}": the alpha (webm/mov) capture path can't supersample. Use format "mp4", or omit outputResolution to render at the composition's native dimensions.`;
|
|
14001
14009
|
}
|
|
14002
14010
|
return void 0;
|
|
14003
14011
|
}
|
|
@@ -67,6 +67,7 @@ export declare function collectExternalAssets(html: string, projectDir: string):
|
|
|
67
67
|
html: string;
|
|
68
68
|
externalAssets: Map<string, string>;
|
|
69
69
|
};
|
|
70
|
+
export declare const REMOTE_MEDIA_SUBDIR = "_remote_media";
|
|
70
71
|
/**
|
|
71
72
|
* Download any remote `src` URLs on `<video>` / `<audio>` elements and their
|
|
72
73
|
* `<source>` children into a local subdirectory of `downloadDir`, rewrite the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.146",
|
|
4
4
|
"description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"postcss": "^8.4.0",
|
|
36
36
|
"puppeteer": "^25.2.1",
|
|
37
37
|
"puppeteer-core": "^25.2.1",
|
|
38
|
-
"@hyperframes/
|
|
39
|
-
"@hyperframes/
|
|
40
|
-
"@hyperframes/
|
|
41
|
-
"@hyperframes/parsers": "0.8.
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/lint": "0.8.146",
|
|
39
|
+
"@hyperframes/studio-server": "0.8.146",
|
|
40
|
+
"@hyperframes/core": "0.8.146",
|
|
41
|
+
"@hyperframes/parsers": "0.8.146",
|
|
42
|
+
"@hyperframes/engine": "0.8.146"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.0.10",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.11",
|
|
51
|
-
"@hyperframes/aws-lambda": "0.8.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.8.146"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|