@hyperframes/producer 0.8.184 → 0.8.186
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 +52 -20
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +67 -67
- package/dist/index.js +64 -24
- package/dist/public-server.js +26 -8
- package/dist/renderRequest.d.ts +1 -0
- package/dist/services/distributed/chunkFrames.d.ts +1 -1
- package/dist/services/htmlCompiler.d.ts +3 -2
- package/dist/services/render/motionBlurRoute.d.ts +11 -1
- package/dist/services/render/stages/compileStage.d.ts +2 -0
- package/dist/services/renderOrchestrator.d.ts +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -6004,7 +6004,10 @@ function stripCompilerMountBootstrap(source) {
|
|
|
6004
6004
|
""
|
|
6005
6005
|
);
|
|
6006
6006
|
}
|
|
6007
|
-
function
|
|
6007
|
+
function runtimeRoot(document2) {
|
|
6008
|
+
return document2.querySelector('[data-composition-id][data-root="true"]') ?? document2.querySelector("[data-composition-id]");
|
|
6009
|
+
}
|
|
6010
|
+
function detectRenderModeHints(html, pageMotionBlur) {
|
|
6008
6011
|
const reasons = [];
|
|
6009
6012
|
const { document: document2 } = parseHTML4(html);
|
|
6010
6013
|
if (document2.querySelector("canvas[layoutsubtree]")) {
|
|
@@ -6012,6 +6015,11 @@ function detectRenderModeHints(html) {
|
|
|
6012
6015
|
code: "htmlInCanvas",
|
|
6013
6016
|
message: "Detected html-in-canvas API (layoutsubtree canvas). Chrome does not support concurrent drawElementImage across multiple workers; render is pinned to a single worker."
|
|
6014
6017
|
});
|
|
6018
|
+
} else if (pageMotionBlur !== "off" && runtimeRoot(document2)?.getAttribute("data-motion-blur") === "on") {
|
|
6019
|
+
reasons.push({
|
|
6020
|
+
code: "captureMotionBlur",
|
|
6021
|
+
message: 'Detected capture motion blur (data-motion-blur="on"), which draws each frame with html-in-canvas; routed through screenshot capture mode.'
|
|
6022
|
+
});
|
|
6015
6023
|
}
|
|
6016
6024
|
if (document2.querySelector("iframe")) {
|
|
6017
6025
|
reasons.push({
|
|
@@ -7043,7 +7051,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7043
7051
|
/(<(?:video|audio)\b[^>]*?)\s+preload\s*=\s*["']none["']/gi,
|
|
7044
7052
|
"$1"
|
|
7045
7053
|
);
|
|
7046
|
-
const renderModeHints = detectRenderModeHints(sanitizedHtml);
|
|
7054
|
+
const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
|
|
7047
7055
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
7048
7056
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
7049
7057
|
const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
|
|
@@ -7528,6 +7536,7 @@ async function runCompileStage(input) {
|
|
|
7528
7536
|
abortSignal,
|
|
7529
7537
|
variables: input.variables,
|
|
7530
7538
|
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7539
|
+
pageMotionBlur: input.pageMotionBlur,
|
|
7531
7540
|
animatedGifCacheDir: cfg.extractCacheDir ? join14(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7532
7541
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7533
7542
|
});
|
|
@@ -7824,6 +7833,7 @@ async function runProbeStage(input) {
|
|
|
7824
7833
|
variables: job.config.variables,
|
|
7825
7834
|
deviceScaleFactor,
|
|
7826
7835
|
motionBlur,
|
|
7836
|
+
pageMotionBlur: job.config.pageMotionBlur,
|
|
7827
7837
|
requiresWebGpu: compositionRequiresWebGpu(compiled.html),
|
|
7828
7838
|
subTimelineWaitMemo
|
|
7829
7839
|
};
|
|
@@ -10771,7 +10781,6 @@ async function enforceAacTruePeak(input) {
|
|
|
10771
10781
|
try {
|
|
10772
10782
|
scratchDir = mkdtempSync3(join25(dirname9(input.audioPath), ".true-peak-"));
|
|
10773
10783
|
const correctedPath = join25(scratchDir, "audio.m4a");
|
|
10774
|
-
const correctionTargetDbfs = AAC_DELIVERY_TRUE_PEAK_DBFS - AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB;
|
|
10775
10784
|
const measurements = [];
|
|
10776
10785
|
let attenuationDb = 0;
|
|
10777
10786
|
let measuredPath = input.audioPath;
|
|
@@ -10789,7 +10798,8 @@ async function enforceAacTruePeak(input) {
|
|
|
10789
10798
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
10790
10799
|
}
|
|
10791
10800
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
10792
|
-
|
|
10801
|
+
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
10802
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
|
|
10793
10803
|
const result = await input.encode(
|
|
10794
10804
|
input.audioPath,
|
|
10795
10805
|
correctedPath,
|
|
@@ -11084,6 +11094,10 @@ function assertMotionBlurSupported(motionBlur, route) {
|
|
|
11084
11094
|
`[MotionBlur] sub-frame motion blur is not supported on the "${route}" capture route: ${reason}`
|
|
11085
11095
|
);
|
|
11086
11096
|
}
|
|
11097
|
+
function pageMotionBlurForRoute(route) {
|
|
11098
|
+
if (route.hasHdrContent) return "hdr-render";
|
|
11099
|
+
return route.useLayeredComposite ? "layered-composite" : void 0;
|
|
11100
|
+
}
|
|
11087
11101
|
|
|
11088
11102
|
// src/services/render/outputScaleRoute.ts
|
|
11089
11103
|
var UNSUPPORTED_REASON2 = {
|
|
@@ -12157,7 +12171,8 @@ async function executeRenderPipeline(input) {
|
|
|
12157
12171
|
needsAlpha,
|
|
12158
12172
|
log,
|
|
12159
12173
|
assertNotAborted,
|
|
12160
|
-
variables: job.config.variables
|
|
12174
|
+
variables: job.config.variables,
|
|
12175
|
+
pageMotionBlur: job.config.pageMotionBlur
|
|
12161
12176
|
})
|
|
12162
12177
|
);
|
|
12163
12178
|
let compiled = compileResult.compiled;
|
|
@@ -12461,14 +12476,17 @@ async function executeRenderPipeline(input) {
|
|
|
12461
12476
|
variables: job.config.variables,
|
|
12462
12477
|
deviceScaleFactor,
|
|
12463
12478
|
motionBlur: job.config.motionBlur,
|
|
12479
|
+
pageMotionBlur: job.config.pageMotionBlur,
|
|
12464
12480
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {}
|
|
12465
12481
|
};
|
|
12466
12482
|
resolvedCaptureBeyondViewport = captureOptions.captureBeyondViewport ?? resolvedCaptureBeyondViewport;
|
|
12467
12483
|
if (resolvedCaptureBeyondViewport !== void 0) {
|
|
12468
12484
|
updateCaptureObservability({ captureBeyondViewport: resolvedCaptureBeyondViewport });
|
|
12469
12485
|
}
|
|
12486
|
+
let routePageMotionBlur;
|
|
12470
12487
|
const buildCaptureOptions = () => ({
|
|
12471
12488
|
...captureOptions,
|
|
12489
|
+
...routePageMotionBlur ? { pageMotionBlur: routePageMotionBlur } : {},
|
|
12472
12490
|
videoMetadataHints,
|
|
12473
12491
|
skipReadinessVideoIds: videoReadinessSkipIds,
|
|
12474
12492
|
subTimelineWaitMemo,
|
|
@@ -12502,6 +12520,7 @@ async function executeRenderPipeline(input) {
|
|
|
12502
12520
|
const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
|
|
12503
12521
|
const hasHdrContent = Boolean(effectiveHdr && nativeHdrIds.size > 0);
|
|
12504
12522
|
if (hasHdrContent) assertOutputScaleSupported(deviceScaleFactor, "hdr_layered");
|
|
12523
|
+
routePageMotionBlur = pageMotionBlurForRoute({ hasHdrContent, useLayeredComposite: false });
|
|
12505
12524
|
const deSingleMinFramesRaw = process.env.HF_DE_SINGLE_MIN_FRAMES;
|
|
12506
12525
|
const deSingleMinFramesNum = deSingleMinFramesRaw === void 0 || deSingleMinFramesRaw.trim() === "" ? 900 : Number(deSingleMinFramesRaw);
|
|
12507
12526
|
const deSingleMinFrames = Number.isFinite(deSingleMinFramesNum) ? deSingleMinFramesNum : 900;
|
|
@@ -12904,6 +12923,7 @@ async function executeRenderPipeline(input) {
|
|
|
12904
12923
|
hasShaderTransitions: compiled.hasShaderTransitions && !isGif,
|
|
12905
12924
|
isPngSequence
|
|
12906
12925
|
});
|
|
12926
|
+
routePageMotionBlur = pageMotionBlurForRoute({ hasHdrContent, useLayeredComposite });
|
|
12907
12927
|
const inversionFallback = resolveInversionRetryPlan({
|
|
12908
12928
|
deWorkerInversion,
|
|
12909
12929
|
preInversionWorkerCount: preRoutingWorkerCount,
|
|
@@ -14100,7 +14120,8 @@ function buildSyntheticRenderJob(input) {
|
|
|
14100
14120
|
hdrMode: input.hdrMode,
|
|
14101
14121
|
strictness: input.strictness,
|
|
14102
14122
|
producerConfig: input.producerConfig,
|
|
14103
|
-
variables: input.variables
|
|
14123
|
+
variables: input.variables,
|
|
14124
|
+
pageMotionBlur: "off"
|
|
14104
14125
|
};
|
|
14105
14126
|
return createRenderJob(renderConfig);
|
|
14106
14127
|
}
|
|
@@ -14145,7 +14166,14 @@ var RenderChunkValidationError = class extends Error {
|
|
|
14145
14166
|
}
|
|
14146
14167
|
};
|
|
14147
14168
|
function createChunkVideoFrameInjectorFactory(frameLookup) {
|
|
14148
|
-
return () => createVideoFrameInjector2(frameLookup);
|
|
14169
|
+
if (!(frameLookup instanceof Promise)) return () => createVideoFrameInjector2(frameLookup);
|
|
14170
|
+
return () => {
|
|
14171
|
+
let hook;
|
|
14172
|
+
return async (page, time, heldVideoTime) => {
|
|
14173
|
+
hook ??= createVideoFrameInjector2(await frameLookup);
|
|
14174
|
+
await hook?.(page, time, heldVideoTime);
|
|
14175
|
+
};
|
|
14176
|
+
};
|
|
14149
14177
|
}
|
|
14150
14178
|
function frameNumberFromFileName(name) {
|
|
14151
14179
|
const match = /(\d+)(?=\.[^.]+$)/.exec(name);
|
|
@@ -14217,6 +14245,7 @@ function buildChunkCaptureOptions(input) {
|
|
|
14217
14245
|
skipReadinessVideoIds: input.skipReadinessVideoIds,
|
|
14218
14246
|
videoMetadataHints: input.videoMetadataHints,
|
|
14219
14247
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
14248
|
+
pageMotionBlur: "distributed-render",
|
|
14220
14249
|
// A fixed warmup count keeps `beginFrameTimeTicks` host-independent.
|
|
14221
14250
|
lockWarmupTicks: true,
|
|
14222
14251
|
requiresWebGpu: compositionRequiresWebGpu3(input.html)
|
|
@@ -15879,7 +15908,8 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
15879
15908
|
// Distributed renders must not capture host-specific system fonts —
|
|
15880
15909
|
// the Lambda/worker filesystem won't have the same fonts installed.
|
|
15881
15910
|
allowSystemFontCapture: false,
|
|
15882
|
-
variables: config.variables
|
|
15911
|
+
variables: config.variables,
|
|
15912
|
+
pageMotionBlur: job.config.pageMotionBlur
|
|
15883
15913
|
});
|
|
15884
15914
|
logPlanStage(log, "compile", stageStarted);
|
|
15885
15915
|
let compiled = compileResult.compiled;
|
|
@@ -16590,6 +16620,7 @@ function assertRequestOptionScalars(options) {
|
|
|
16590
16620
|
}
|
|
16591
16621
|
assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
|
|
16592
16622
|
assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
|
|
16623
|
+
assertOptionalEnum(options, "pageMotionBlur", ["off"]);
|
|
16593
16624
|
if (options.videoFrameFormat !== void 0 && !isVideoFrameFormat2(options.videoFrameFormat)) {
|
|
16594
16625
|
throw new Error("Render request videoFrameFormat is invalid");
|
|
16595
16626
|
}
|
|
@@ -17166,9 +17197,6 @@ function validateOutputResolutionOverride(body) {
|
|
|
17166
17197
|
if (body.outputResolution.trim().length > 0 && normalized === void 0) {
|
|
17167
17198
|
return `Invalid outputResolution "${body.outputResolution}". Must be one of: landscape, portrait, landscape-4k, portrait-4k, square, square-4k (aliases: 1080p, 4k, \u2026).`;
|
|
17168
17199
|
}
|
|
17169
|
-
if (normalized !== void 0 && (body.format === "webm" || body.format === "mov")) {
|
|
17170
|
-
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.`;
|
|
17171
|
-
}
|
|
17172
17200
|
return void 0;
|
|
17173
17201
|
}
|
|
17174
17202
|
function prepareProjectDirectory(projectDir, options) {
|
|
@@ -19155,6 +19183,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19155
19183
|
const captureAttempts = [];
|
|
19156
19184
|
let forceScreenshotForChunk = encoder.forceScreenshot;
|
|
19157
19185
|
let shownBoxes;
|
|
19186
|
+
let extraction;
|
|
19158
19187
|
try {
|
|
19159
19188
|
if (chunkWorkerCount === 1 || !forceScreenshotForChunk) {
|
|
19160
19189
|
const bootStarted = Date.now();
|
|
@@ -19211,7 +19240,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19211
19240
|
session = null;
|
|
19212
19241
|
}
|
|
19213
19242
|
}
|
|
19214
|
-
const deferredVideoFrames = planVideos ?
|
|
19243
|
+
const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
|
|
19215
19244
|
planDir,
|
|
19216
19245
|
planVideos,
|
|
19217
19246
|
frames: chunkFrameNumbers,
|
|
@@ -19220,19 +19249,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19220
19249
|
cfg,
|
|
19221
19250
|
remoteSources,
|
|
19222
19251
|
shownBoxes
|
|
19223
|
-
}) : [];
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
|
|
19227
|
-
|
|
19228
|
-
|
|
19229
|
-
|
|
19230
|
-
|
|
19231
|
-
|
|
19232
|
-
|
|
19233
|
-
|
|
19234
|
-
|
|
19252
|
+
}) : Promise.resolve([]);
|
|
19253
|
+
extraction = deferredVideoFrames;
|
|
19254
|
+
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
|
|
19255
|
+
(deferred) => createFrameLookupTable5(
|
|
19256
|
+
planVideos.videos,
|
|
19257
|
+
[
|
|
19258
|
+
...rebuildExtractedFramesFromPlanDir(
|
|
19259
|
+
planDir,
|
|
19260
|
+
planVideos.extracted.filter((video) => !video.deferredRange),
|
|
19261
|
+
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
19262
|
+
),
|
|
19263
|
+
...deferred
|
|
19264
|
+
],
|
|
19265
|
+
resolveRenderFpsConfig(job.config.fps).value
|
|
19266
|
+
)
|
|
19235
19267
|
) : null;
|
|
19268
|
+
videoFrameLookup?.catch(() => {
|
|
19269
|
+
});
|
|
19236
19270
|
const createChunkVideoFrameInjector = createChunkVideoFrameInjectorFactory(videoFrameLookup);
|
|
19237
19271
|
const captureStarted = Date.now();
|
|
19238
19272
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
@@ -19315,7 +19349,11 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19315
19349
|
}
|
|
19316
19350
|
);
|
|
19317
19351
|
}
|
|
19352
|
+
}).catch(async (captureError) => {
|
|
19353
|
+
await deferredVideoFrames;
|
|
19354
|
+
throw captureError;
|
|
19318
19355
|
});
|
|
19356
|
+
await deferredVideoFrames;
|
|
19319
19357
|
captureStageMs = Date.now() - captureStarted;
|
|
19320
19358
|
applyRenderWarningPolicy(
|
|
19321
19359
|
job,
|
|
@@ -19394,6 +19432,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19394
19432
|
}
|
|
19395
19433
|
encodeStageMs = Date.now() - encodeStarted;
|
|
19396
19434
|
} finally {
|
|
19435
|
+
await extraction?.catch(() => {
|
|
19436
|
+
});
|
|
19397
19437
|
if (session) {
|
|
19398
19438
|
try {
|
|
19399
19439
|
await closeCaptureSession11(session);
|
package/dist/public-server.js
CHANGED
|
@@ -6023,7 +6023,10 @@ function stripCompilerMountBootstrap(source) {
|
|
|
6023
6023
|
""
|
|
6024
6024
|
);
|
|
6025
6025
|
}
|
|
6026
|
-
function
|
|
6026
|
+
function runtimeRoot(document2) {
|
|
6027
|
+
return document2.querySelector('[data-composition-id][data-root="true"]') ?? document2.querySelector("[data-composition-id]");
|
|
6028
|
+
}
|
|
6029
|
+
function detectRenderModeHints(html, pageMotionBlur) {
|
|
6027
6030
|
const reasons = [];
|
|
6028
6031
|
const { document: document2 } = parseHTML4(html);
|
|
6029
6032
|
if (document2.querySelector("canvas[layoutsubtree]")) {
|
|
@@ -6031,6 +6034,11 @@ function detectRenderModeHints(html) {
|
|
|
6031
6034
|
code: "htmlInCanvas",
|
|
6032
6035
|
message: "Detected html-in-canvas API (layoutsubtree canvas). Chrome does not support concurrent drawElementImage across multiple workers; render is pinned to a single worker."
|
|
6033
6036
|
});
|
|
6037
|
+
} else if (pageMotionBlur !== "off" && runtimeRoot(document2)?.getAttribute("data-motion-blur") === "on") {
|
|
6038
|
+
reasons.push({
|
|
6039
|
+
code: "captureMotionBlur",
|
|
6040
|
+
message: 'Detected capture motion blur (data-motion-blur="on"), which draws each frame with html-in-canvas; routed through screenshot capture mode.'
|
|
6041
|
+
});
|
|
6034
6042
|
}
|
|
6035
6043
|
if (document2.querySelector("iframe")) {
|
|
6036
6044
|
reasons.push({
|
|
@@ -7062,7 +7070,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7062
7070
|
/(<(?:video|audio)\b[^>]*?)\s+preload\s*=\s*["']none["']/gi,
|
|
7063
7071
|
"$1"
|
|
7064
7072
|
);
|
|
7065
|
-
const renderModeHints = detectRenderModeHints(sanitizedHtml);
|
|
7073
|
+
const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
|
|
7066
7074
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
7067
7075
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
7068
7076
|
const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
|
|
@@ -7547,6 +7555,7 @@ async function runCompileStage(input) {
|
|
|
7547
7555
|
abortSignal,
|
|
7548
7556
|
variables: input.variables,
|
|
7549
7557
|
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7558
|
+
pageMotionBlur: input.pageMotionBlur,
|
|
7550
7559
|
animatedGifCacheDir: cfg.extractCacheDir ? join14(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7551
7560
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7552
7561
|
});
|
|
@@ -7843,6 +7852,7 @@ async function runProbeStage(input) {
|
|
|
7843
7852
|
variables: job.config.variables,
|
|
7844
7853
|
deviceScaleFactor,
|
|
7845
7854
|
motionBlur,
|
|
7855
|
+
pageMotionBlur: job.config.pageMotionBlur,
|
|
7846
7856
|
requiresWebGpu: compositionRequiresWebGpu(compiled.html),
|
|
7847
7857
|
subTimelineWaitMemo
|
|
7848
7858
|
};
|
|
@@ -10744,7 +10754,6 @@ async function enforceAacTruePeak(input) {
|
|
|
10744
10754
|
try {
|
|
10745
10755
|
scratchDir = mkdtempSync3(join25(dirname9(input.audioPath), ".true-peak-"));
|
|
10746
10756
|
const correctedPath = join25(scratchDir, "audio.m4a");
|
|
10747
|
-
const correctionTargetDbfs = AAC_DELIVERY_TRUE_PEAK_DBFS - AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB;
|
|
10748
10757
|
const measurements = [];
|
|
10749
10758
|
let attenuationDb = 0;
|
|
10750
10759
|
let measuredPath = input.audioPath;
|
|
@@ -10762,7 +10771,8 @@ async function enforceAacTruePeak(input) {
|
|
|
10762
10771
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
10763
10772
|
}
|
|
10764
10773
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
10765
|
-
|
|
10774
|
+
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
10775
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
|
|
10766
10776
|
const result = await input.encode(
|
|
10767
10777
|
input.audioPath,
|
|
10768
10778
|
correctedPath,
|
|
@@ -11057,6 +11067,10 @@ function assertMotionBlurSupported(motionBlur, route) {
|
|
|
11057
11067
|
`[MotionBlur] sub-frame motion blur is not supported on the "${route}" capture route: ${reason}`
|
|
11058
11068
|
);
|
|
11059
11069
|
}
|
|
11070
|
+
function pageMotionBlurForRoute(route) {
|
|
11071
|
+
if (route.hasHdrContent) return "hdr-render";
|
|
11072
|
+
return route.useLayeredComposite ? "layered-composite" : void 0;
|
|
11073
|
+
}
|
|
11060
11074
|
|
|
11061
11075
|
// src/services/render/outputScaleRoute.ts
|
|
11062
11076
|
var UNSUPPORTED_REASON2 = {
|
|
@@ -12130,7 +12144,8 @@ async function executeRenderPipeline(input) {
|
|
|
12130
12144
|
needsAlpha,
|
|
12131
12145
|
log,
|
|
12132
12146
|
assertNotAborted,
|
|
12133
|
-
variables: job.config.variables
|
|
12147
|
+
variables: job.config.variables,
|
|
12148
|
+
pageMotionBlur: job.config.pageMotionBlur
|
|
12134
12149
|
})
|
|
12135
12150
|
);
|
|
12136
12151
|
let compiled = compileResult.compiled;
|
|
@@ -12434,14 +12449,17 @@ async function executeRenderPipeline(input) {
|
|
|
12434
12449
|
variables: job.config.variables,
|
|
12435
12450
|
deviceScaleFactor,
|
|
12436
12451
|
motionBlur: job.config.motionBlur,
|
|
12452
|
+
pageMotionBlur: job.config.pageMotionBlur,
|
|
12437
12453
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {}
|
|
12438
12454
|
};
|
|
12439
12455
|
resolvedCaptureBeyondViewport = captureOptions.captureBeyondViewport ?? resolvedCaptureBeyondViewport;
|
|
12440
12456
|
if (resolvedCaptureBeyondViewport !== void 0) {
|
|
12441
12457
|
updateCaptureObservability({ captureBeyondViewport: resolvedCaptureBeyondViewport });
|
|
12442
12458
|
}
|
|
12459
|
+
let routePageMotionBlur;
|
|
12443
12460
|
const buildCaptureOptions = () => ({
|
|
12444
12461
|
...captureOptions,
|
|
12462
|
+
...routePageMotionBlur ? { pageMotionBlur: routePageMotionBlur } : {},
|
|
12445
12463
|
videoMetadataHints,
|
|
12446
12464
|
skipReadinessVideoIds: videoReadinessSkipIds,
|
|
12447
12465
|
subTimelineWaitMemo,
|
|
@@ -12475,6 +12493,7 @@ async function executeRenderPipeline(input) {
|
|
|
12475
12493
|
const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
|
|
12476
12494
|
const hasHdrContent = Boolean(effectiveHdr && nativeHdrIds.size > 0);
|
|
12477
12495
|
if (hasHdrContent) assertOutputScaleSupported(deviceScaleFactor, "hdr_layered");
|
|
12496
|
+
routePageMotionBlur = pageMotionBlurForRoute({ hasHdrContent, useLayeredComposite: false });
|
|
12478
12497
|
const deSingleMinFramesRaw = process.env.HF_DE_SINGLE_MIN_FRAMES;
|
|
12479
12498
|
const deSingleMinFramesNum = deSingleMinFramesRaw === void 0 || deSingleMinFramesRaw.trim() === "" ? 900 : Number(deSingleMinFramesRaw);
|
|
12480
12499
|
const deSingleMinFrames = Number.isFinite(deSingleMinFramesNum) ? deSingleMinFramesNum : 900;
|
|
@@ -12877,6 +12896,7 @@ async function executeRenderPipeline(input) {
|
|
|
12877
12896
|
hasShaderTransitions: compiled.hasShaderTransitions && !isGif,
|
|
12878
12897
|
isPngSequence
|
|
12879
12898
|
});
|
|
12899
|
+
routePageMotionBlur = pageMotionBlurForRoute({ hasHdrContent, useLayeredComposite });
|
|
12880
12900
|
const inversionFallback = resolveInversionRetryPlan({
|
|
12881
12901
|
deWorkerInversion,
|
|
12882
12902
|
preInversionWorkerCount: preRoutingWorkerCount,
|
|
@@ -14234,6 +14254,7 @@ function assertRequestOptionScalars(options) {
|
|
|
14234
14254
|
}
|
|
14235
14255
|
assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
|
|
14236
14256
|
assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
|
|
14257
|
+
assertOptionalEnum(options, "pageMotionBlur", ["off"]);
|
|
14237
14258
|
if (options.videoFrameFormat !== void 0 && !isVideoFrameFormat2(options.videoFrameFormat)) {
|
|
14238
14259
|
throw new Error("Render request videoFrameFormat is invalid");
|
|
14239
14260
|
}
|
|
@@ -14473,9 +14494,6 @@ function validateOutputResolutionOverride(body) {
|
|
|
14473
14494
|
if (body.outputResolution.trim().length > 0 && normalized === void 0) {
|
|
14474
14495
|
return `Invalid outputResolution "${body.outputResolution}". Must be one of: landscape, portrait, landscape-4k, portrait-4k, square, square-4k (aliases: 1080p, 4k, \u2026).`;
|
|
14475
14496
|
}
|
|
14476
|
-
if (normalized !== void 0 && (body.format === "webm" || body.format === "mov")) {
|
|
14477
|
-
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.`;
|
|
14478
|
-
}
|
|
14479
14497
|
return void 0;
|
|
14480
14498
|
}
|
|
14481
14499
|
function prepareProjectDirectory(projectDir, options) {
|
package/dist/renderRequest.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface RenderRequestOptions {
|
|
|
38
38
|
videoBitrate?: string;
|
|
39
39
|
videoFrameFormat?: VideoFrameFormat;
|
|
40
40
|
hdrMode?: RenderConfig["hdrMode"];
|
|
41
|
+
pageMotionBlur?: RenderConfig["pageMotionBlur"];
|
|
41
42
|
variables?: Record<string, unknown>;
|
|
42
43
|
outputResolution?: CanvasResolution;
|
|
43
44
|
outputResolutionAspectAgnostic?: boolean;
|
|
@@ -17,7 +17,7 @@ export declare class RenderChunkValidationError extends Error {
|
|
|
17
17
|
constructor(code: RenderChunkValidationCode, message: string);
|
|
18
18
|
}
|
|
19
19
|
/** One injector per browser session: a hook reused on a fresh page can skip its first injection. */
|
|
20
|
-
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | null): () => BeforeCaptureHook | null;
|
|
20
|
+
export declare function createChunkVideoFrameInjectorFactory(frameLookup: FrameLookupTable | Promise<FrameLookupTable> | null): () => BeforeCaptureHook | null;
|
|
21
21
|
/**
|
|
22
22
|
* Rebuilds `ExtractedFrames[]` from `<planDir>/video-frames/<videoId>/`. `framePaths` keys are 0-based
|
|
23
23
|
* frame indexes (what `getFrameAtTime` reads); any other key drops every video's first-paint frame.
|
|
@@ -26,7 +26,7 @@ export interface CompiledComposition {
|
|
|
26
26
|
hasAncestorBackgroundImage: boolean;
|
|
27
27
|
}
|
|
28
28
|
export declare function injectSdkPositionEditsRenderScript(html: string): string;
|
|
29
|
-
export type RenderModeHintCode = "iframe" | "requestAnimationFrame" | "htmlInCanvas";
|
|
29
|
+
export type RenderModeHintCode = "iframe" | "requestAnimationFrame" | "htmlInCanvas" | "captureMotionBlur";
|
|
30
30
|
export interface RenderModeHint {
|
|
31
31
|
code: RenderModeHintCode;
|
|
32
32
|
message: string;
|
|
@@ -35,7 +35,7 @@ export interface RenderModeHints {
|
|
|
35
35
|
recommendScreenshot: boolean;
|
|
36
36
|
reasons: RenderModeHint[];
|
|
37
37
|
}
|
|
38
|
-
export declare function detectRenderModeHints(html: string): RenderModeHints;
|
|
38
|
+
export declare function detectRenderModeHints(html: string, pageMotionBlur?: "off"): RenderModeHints;
|
|
39
39
|
export declare function detectThreeDTransformUsage(html: string): boolean;
|
|
40
40
|
/**
|
|
41
41
|
* Background-image signals on ancestors of the composition root.
|
|
@@ -191,6 +191,7 @@ export interface CompileForRenderOptions {
|
|
|
191
191
|
* `window.__hfVariables` injection covers script reads, not var() in CSS.
|
|
192
192
|
*/
|
|
193
193
|
variables?: Record<string, unknown>;
|
|
194
|
+
pageMotionBlur?: "off";
|
|
194
195
|
}
|
|
195
196
|
/**
|
|
196
197
|
* Compile an HTML composition project into a single self-contained HTML string
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* requested on one. There is nothing to reject, and threading it through the frozen plan
|
|
17
17
|
* is a feature rather than a fix.
|
|
18
18
|
*/
|
|
19
|
-
import type { MotionBlurOptions } from "@hyperframes/engine";
|
|
19
|
+
import type { MotionBlurOptions, PageMotionBlurOff } from "@hyperframes/engine";
|
|
20
20
|
import type { CapturePlan } from "./capturePlan.js";
|
|
21
21
|
/**
|
|
22
22
|
* Throw a named error when motion blur was asked for on a route that cannot deliver it.
|
|
@@ -25,3 +25,13 @@ import type { CapturePlan } from "./capturePlan.js";
|
|
|
25
25
|
* is a render that accepts `motionBlur` and returns frames with no blur in them.
|
|
26
26
|
*/
|
|
27
27
|
export declare function assertMotionBlurSupported(motionBlur: MotionBlurOptions | undefined, route: CapturePlan["kind"]): void;
|
|
28
|
+
/**
|
|
29
|
+
* Why the page's own capture blur (`data-motion-blur="on"`) must stand down on this route, or
|
|
30
|
+
* undefined when it can run. The layered compositor screenshots each DOM layer after hiding the
|
|
31
|
+
* others; blur draws the whole composition into a canvas that those hides do not reach, so a
|
|
32
|
+
* layer comes out wrong (HDR: backgrounds and overlays lost).
|
|
33
|
+
*/
|
|
34
|
+
export declare function pageMotionBlurForRoute(route: {
|
|
35
|
+
hasHdrContent: boolean;
|
|
36
|
+
useLayeredComposite: boolean;
|
|
37
|
+
}): PageMotionBlurOff | undefined;
|
|
@@ -80,6 +80,8 @@ export interface CompileStageInput {
|
|
|
80
80
|
variables?: Record<string, unknown>;
|
|
81
81
|
/** Absolute project paths not on disk, readable at these URLs instead. */
|
|
82
82
|
urlByAbsentLocalPath?: ReadonlyMap<string, string>;
|
|
83
|
+
/** See RenderConfig.pageMotionBlur; decides whether the page renders through screenshots. */
|
|
84
|
+
pageMotionBlur?: "off";
|
|
83
85
|
}
|
|
84
86
|
export interface CompileStageResult {
|
|
85
87
|
compiled: CompiledComposition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.186",
|
|
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/
|
|
42
|
-
"@hyperframes/studio-server": "0.8.
|
|
38
|
+
"@hyperframes/engine": "0.8.186",
|
|
39
|
+
"@hyperframes/lint": "0.8.186",
|
|
40
|
+
"@hyperframes/parsers": "0.8.186",
|
|
41
|
+
"@hyperframes/core": "0.8.186",
|
|
42
|
+
"@hyperframes/studio-server": "0.8.186"
|
|
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.186"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|