@t3lnet/sceneforge 1.0.25 → 1.0.27
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/cli/commands/record-demo.js +7 -3
- package/cli/utils/cdp-recorder.js +16 -12
- package/package.json +1 -1
|
@@ -182,6 +182,7 @@ export async function runRecordDemoCommand(argv) {
|
|
|
182
182
|
const hqVideo = hasFlag(args, "--hq-video");
|
|
183
183
|
const hqFormat = getFlagValue(args, "--hq-format") ?? "jpeg";
|
|
184
184
|
const hqQuality = parseInt(getFlagValue(args, "--hq-quality") ?? "100", 10);
|
|
185
|
+
const hqFps = parseInt(getFlagValue(args, "--hq-fps") ?? "30", 10);
|
|
185
186
|
|
|
186
187
|
if (!baseUrl) {
|
|
187
188
|
console.error("[error] --base-url is required");
|
|
@@ -291,8 +292,9 @@ export async function runRecordDemoCommand(argv) {
|
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
// Stop CDP recorder before closing context
|
|
295
|
+
let cdpStopInfo = null;
|
|
294
296
|
if (cdpRecorder) {
|
|
295
|
-
await cdpRecorder.stop();
|
|
297
|
+
cdpStopInfo = await cdpRecorder.stop();
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
await context.close();
|
|
@@ -303,10 +305,12 @@ export async function runRecordDemoCommand(argv) {
|
|
|
303
305
|
const finalVideoPath = path.join(outputPaths.videosDir, `${definition.name}.webm`);
|
|
304
306
|
try {
|
|
305
307
|
if (!demoError && result?.success) {
|
|
306
|
-
await cdpRecorder.assembleVideo(finalVideoPath);
|
|
308
|
+
await cdpRecorder.assembleVideo(finalVideoPath, { fps: hqFps });
|
|
307
309
|
console.log(`[record] High-quality video saved: ${finalVideoPath}`);
|
|
308
310
|
if (result.scriptPath) {
|
|
309
|
-
|
|
311
|
+
// Use the actual first frame time for alignment, not when we started the recorder
|
|
312
|
+
const actualVideoStartTime = cdpStopInfo?.firstFrameTime || videoRecordingStartTime;
|
|
313
|
+
await alignScriptToVideo(result.scriptPath, finalVideoPath, actualVideoStartTime);
|
|
310
314
|
}
|
|
311
315
|
}
|
|
312
316
|
} finally {
|
|
@@ -55,6 +55,7 @@ export function createCDPRecorder(page, options) {
|
|
|
55
55
|
let frameTimestamps = [];
|
|
56
56
|
let isRecording = false;
|
|
57
57
|
let startTime = null;
|
|
58
|
+
let firstFrameTime = null; // Wall clock time when first frame was received
|
|
58
59
|
let writeQueue = Promise.resolve();
|
|
59
60
|
let nextFrameNumber = 0;
|
|
60
61
|
|
|
@@ -64,6 +65,11 @@ export function createCDPRecorder(page, options) {
|
|
|
64
65
|
const { data, metadata, sessionId } = params;
|
|
65
66
|
frameCount += 1;
|
|
66
67
|
|
|
68
|
+
// Track when we receive the first frame (wall clock time)
|
|
69
|
+
if (firstFrameTime === null) {
|
|
70
|
+
firstFrameTime = Date.now();
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
// Acknowledge the frame immediately to continue receiving
|
|
68
74
|
cdpSession.send("Page.screencastFrameAck", { sessionId }).catch(() => {
|
|
69
75
|
// Session may be closed
|
|
@@ -156,15 +162,21 @@ export function createCDPRecorder(page, options) {
|
|
|
156
162
|
// Wait for all pending frame writes to complete
|
|
157
163
|
await writeQueue;
|
|
158
164
|
|
|
165
|
+
const delayToFirstFrame = firstFrameTime ? firstFrameTime - startTime : 0;
|
|
159
166
|
console.log(
|
|
160
167
|
`[cdp-recorder] Captured ${frameTimestamps.length} frames in ${(duration / 1000).toFixed(2)}s`
|
|
161
168
|
);
|
|
169
|
+
if (delayToFirstFrame > 100) {
|
|
170
|
+
console.log(`[cdp-recorder] First frame delay: ${delayToFirstFrame}ms`);
|
|
171
|
+
}
|
|
162
172
|
|
|
163
173
|
return {
|
|
164
174
|
frameCount: frameTimestamps.length,
|
|
165
175
|
duration,
|
|
166
176
|
framesDir,
|
|
167
177
|
frameTimestamps,
|
|
178
|
+
firstFrameTime, // Wall clock time when first frame was captured
|
|
179
|
+
startTime, // Wall clock time when recording was started
|
|
168
180
|
};
|
|
169
181
|
},
|
|
170
182
|
|
|
@@ -183,17 +195,8 @@ export function createCDPRecorder(page, options) {
|
|
|
183
195
|
throw new Error("No frames captured - cannot assemble video");
|
|
184
196
|
}
|
|
185
197
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
if (!fps && frameTimestamps.length > 1) {
|
|
189
|
-
const firstTimestamp = frameTimestamps[0].timestamp;
|
|
190
|
-
const lastTimestamp = frameTimestamps[frameTimestamps.length - 1].timestamp;
|
|
191
|
-
const durationSec = lastTimestamp - firstTimestamp;
|
|
192
|
-
if (durationSec > 0) {
|
|
193
|
-
fps = Math.round(frameTimestamps.length / durationSec);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
fps = fps || 30; // Default to 30fps
|
|
198
|
+
// Use provided fps or default to 30
|
|
199
|
+
const fps = fpsOverride || 30;
|
|
197
200
|
|
|
198
201
|
console.log(`[cdp-recorder] Assembling video at ${fps} fps`);
|
|
199
202
|
|
|
@@ -259,5 +262,6 @@ High-Quality Recording Options:
|
|
|
259
262
|
--hq-video Use CDP-based high-quality recording instead of Playwright
|
|
260
263
|
Captures frames at maximum quality and assembles with FFmpeg
|
|
261
264
|
--hq-format <format> Frame format: jpeg (default) or png (lossless, larger files)
|
|
262
|
-
--hq-quality <1-100> JPEG quality (default: 100, ignored for PNG)
|
|
265
|
+
--hq-quality <1-100> JPEG quality (default: 100, ignored for PNG)
|
|
266
|
+
--hq-fps <fps> Output framerate (default: 30)`;
|
|
263
267
|
}
|