@hyperframes/engine 0.8.146 → 0.8.147
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.
|
@@ -20,6 +20,8 @@ export type { CaptureOptions, CaptureResult, CaptureBufferResult, CapturePerfSum
|
|
|
20
20
|
* `heldVideoTime` (motion-blur samples only) is the output frame's time: a video on screen at
|
|
21
21
|
* both times shows its frame for `heldVideoTime`, so footage does not smear. */
|
|
22
22
|
export type BeforeCaptureHook = (page: Page, time: number, heldVideoTime?: number) => Promise<void>;
|
|
23
|
+
/** The per-frame wait a capture is in, worded for the stall error a user reads. */
|
|
24
|
+
type CaptureStep = "seeking the timeline" | "decoding CSS background images" | "injecting video frames" | "waiting for page seek handlers" | "loading color-grading LUTs" | "compositing the page" | "capturing the frame";
|
|
23
25
|
export interface CaptureSession {
|
|
24
26
|
browser: Browser;
|
|
25
27
|
/** Exact ownership token for this browser acquisition. */
|
|
@@ -202,6 +204,7 @@ export interface CaptureSession {
|
|
|
202
204
|
* retries via screenshot.
|
|
203
205
|
*/
|
|
204
206
|
deFrameTimeouts?: number;
|
|
207
|
+
captureStep?: CaptureStep;
|
|
205
208
|
/**
|
|
206
209
|
* drawElement init passed every gate but stopped before verification +
|
|
207
210
|
* canvas injection: the session has no video-frame injector yet (probe
|
|
@@ -2040,7 +2040,9 @@ async function prepareFrameForCapture(session, frameIndex, time, seekOptions, he
|
|
|
2040
2040
|
assertVfxIntact(session);
|
|
2041
2041
|
const quantizedTime = quantizeSeekTime(time, fpsToNumber(options.fps), seekOptions?.subFrameDivisions);
|
|
2042
2042
|
const seekStart = Date.now();
|
|
2043
|
+
session.captureStep = "seeking the timeline";
|
|
2043
2044
|
const hasPendingComposite = await seekPageTimeline(page, quantizedTime, seekOptions);
|
|
2045
|
+
session.captureStep = "decoding CSS background images";
|
|
2044
2046
|
await decodeDynamicCssBackgroundImages(page);
|
|
2045
2047
|
const seekMs = Date.now() - seekStart;
|
|
2046
2048
|
// Before-capture hook (e.g. video frame injection) — runs before
|
|
@@ -2048,9 +2050,12 @@ async function prepareFrameForCapture(session, frameIndex, time, seekOptions, he
|
|
|
2048
2050
|
// replacements for <video> elements.
|
|
2049
2051
|
const beforeCaptureStart = Date.now();
|
|
2050
2052
|
if (session.onBeforeCapture) {
|
|
2053
|
+
session.captureStep = "injecting video frames";
|
|
2051
2054
|
await session.onBeforeCapture(page, quantizedTime, heldVideoTime);
|
|
2052
2055
|
}
|
|
2056
|
+
session.captureStep = "waiting for page seek handlers";
|
|
2053
2057
|
await waitForPendingSeekCompletion(page);
|
|
2058
|
+
session.captureStep = "loading color-grading LUTs";
|
|
2054
2059
|
await page.evaluate(async () => {
|
|
2055
2060
|
const runtime = window.__hf?.colorGrading;
|
|
2056
2061
|
await runtime?.waitForActiveLuts?.();
|
|
@@ -2063,6 +2068,7 @@ async function prepareFrameForCapture(session, frameIndex, time, seekOptions, he
|
|
|
2063
2068
|
if (hasPendingComposite &&
|
|
2064
2069
|
session.captureMode !== "beginframe" &&
|
|
2065
2070
|
session.captureMode !== "drawelement") {
|
|
2071
|
+
session.captureStep = "compositing the page";
|
|
2066
2072
|
await page.evaluate(async () => {
|
|
2067
2073
|
const w = window;
|
|
2068
2074
|
if (typeof w.__hf_page_composite_prepare === "function") {
|
|
@@ -2082,6 +2088,7 @@ async function prepareFrameForCapture(session, frameIndex, time, seekOptions, he
|
|
|
2082
2088
|
}
|
|
2083
2089
|
});
|
|
2084
2090
|
}
|
|
2091
|
+
session.captureStep = "capturing the frame";
|
|
2085
2092
|
return { quantizedTime, seekMs, beforeCaptureMs };
|
|
2086
2093
|
}
|
|
2087
2094
|
// ── Static-frame dedup (default-on, opt-out HF_STATIC_DEDUP=false) ─────────────
|
|
@@ -3095,12 +3102,14 @@ async function captureAccumulatedFrame(session, frameIndex, absFrameIndex, plan)
|
|
|
3095
3102
|
const fps = fpsToNumber(session.options.fps);
|
|
3096
3103
|
const frameTime = quantizeSeekTime(absFrameIndex / fps, fps);
|
|
3097
3104
|
const eventfulSeekStart = Date.now();
|
|
3105
|
+
session.captureStep = "seeking the timeline";
|
|
3098
3106
|
await seekPageTimeline(session.page, frameTime, undefined);
|
|
3099
3107
|
const totals = { seekMs: Date.now() - eventfulSeekStart, beforeCaptureMs: 0, screenshotMs: 0 };
|
|
3100
3108
|
if (session.onBeforeCapture) {
|
|
3101
3109
|
// The injector copies a video's own style only when its frame changes: inject at frameTime
|
|
3102
3110
|
// first so the held frame carries frameTime's style, not the first sample's.
|
|
3103
3111
|
const injectStart = Date.now();
|
|
3112
|
+
session.captureStep = "injecting video frames";
|
|
3104
3113
|
await session.onBeforeCapture(session.page, frameTime, frameTime);
|
|
3105
3114
|
totals.beforeCaptureMs += Date.now() - injectStart;
|
|
3106
3115
|
}
|
|
@@ -3125,6 +3134,7 @@ async function captureAccumulatedFrame(session, frameIndex, absFrameIndex, plan)
|
|
|
3125
3134
|
accumulator.add(sample.buffer);
|
|
3126
3135
|
}
|
|
3127
3136
|
const restoreSeekStart = Date.now();
|
|
3137
|
+
session.captureStep = "seeking the timeline";
|
|
3128
3138
|
await seekPageTimeline(session.page, frameTime, { suppressEvents: true });
|
|
3129
3139
|
totals.seekMs += Date.now() - restoreSeekStart;
|
|
3130
3140
|
const blended = accumulator.finish();
|
|
@@ -3396,6 +3406,7 @@ export async function captureFramesBatchPipelined(session, frameIndices, times)
|
|
|
3396
3406
|
const startTime = Date.now();
|
|
3397
3407
|
const fps = fpsToNumber(options.fps);
|
|
3398
3408
|
const quantized = times.map((t) => quantizeTimeToFrame(t, fps));
|
|
3409
|
+
session.captureStep = "capturing the frame";
|
|
3399
3410
|
const { encodeResults, failedAt, error } = await produceDrawElementFrameBatch(page, quantized, options.width, options.height, options.quality ?? 80);
|
|
3400
3411
|
// A later batch failure abandons this prefix. Its asynchronous encodes can
|
|
3401
3412
|
// still reject while session cleanup shuts the worker down.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/engine",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.147",
|
|
4
4
|
"description": "Seekable web page to video rendering engine (Puppeteer + FFmpeg)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"linkedom": "^0.18.12",
|
|
47
47
|
"puppeteer": "^25.8.0",
|
|
48
48
|
"puppeteer-core": "^25.8.0",
|
|
49
|
-
"@hyperframes/core": "^0.8.
|
|
50
|
-
"@hyperframes/parsers": "^0.8.
|
|
49
|
+
"@hyperframes/core": "^0.8.147",
|
|
50
|
+
"@hyperframes/parsers": "^0.8.147"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^25.0.10",
|