@walkrstudio/recorder 0.2.0
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/cdp.d.ts +14 -0
- package/dist/cdp.d.ts.map +1 -0
- package/dist/cdp.js +86 -0
- package/dist/cdp.js.map +1 -0
- package/dist/chromium.d.ts +12 -0
- package/dist/chromium.d.ts.map +1 -0
- package/dist/chromium.js +75 -0
- package/dist/chromium.js.map +1 -0
- package/dist/embed.d.ts +3 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +548 -0
- package/dist/embed.js.map +1 -0
- package/dist/encoder.d.ts +6 -0
- package/dist/encoder.d.ts.map +1 -0
- package/dist/encoder.js +121 -0
- package/dist/encoder.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/realtime-recorder.d.ts +4 -0
- package/dist/realtime-recorder.d.ts.map +1 -0
- package/dist/realtime-recorder.js +109 -0
- package/dist/realtime-recorder.js.map +1 -0
- package/dist/recorder.d.ts +4 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +180 -0
- package/dist/recorder.js.map +1 -0
- package/dist/recording-session.d.ts +26 -0
- package/dist/recording-session.d.ts.map +1 -0
- package/dist/recording-session.js +88 -0
- package/dist/recording-session.js.map +1 -0
- package/dist/static-server.d.ts +9 -0
- package/dist/static-server.d.ts.map +1 -0
- package/dist/static-server.js +212 -0
- package/dist/static-server.js.map +1 -0
- package/dist/streaming-encoder.d.ts +26 -0
- package/dist/streaming-encoder.d.ts.map +1 -0
- package/dist/streaming-encoder.js +89 -0
- package/dist/streaming-encoder.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +34 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Walkthrough } from "@walkrstudio/core";
|
|
2
|
+
import type { RecordOptions, RecordResult } from "./types.js";
|
|
3
|
+
export declare function recordRealtimeWalkthrough(walkthrough: Walkthrough, options?: RecordOptions): Promise<RecordResult>;
|
|
4
|
+
//# sourceMappingURL=realtime-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-recorder.d.ts","sourceRoot":"","sources":["../src/realtime-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAYrD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA+HvB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { encodeFrames } from "./encoder.js";
|
|
3
|
+
import { createRecordingSession, DEFAULT_FPS, DEFAULT_HEIGHT, DEFAULT_WIDTH, getDefaultOutput, isFiniteNumber, waitForConsoleMessage, } from "./recording-session.js";
|
|
4
|
+
import { StreamingEncoder } from "./streaming-encoder.js";
|
|
5
|
+
export async function recordRealtimeWalkthrough(walkthrough, options = {}) {
|
|
6
|
+
const width = isFiniteNumber(options.width)
|
|
7
|
+
? Math.max(1, Math.round(options.width))
|
|
8
|
+
: DEFAULT_WIDTH;
|
|
9
|
+
const height = isFiniteNumber(options.height)
|
|
10
|
+
? Math.max(1, Math.round(options.height))
|
|
11
|
+
: DEFAULT_HEIGHT;
|
|
12
|
+
const fps = isFiniteNumber(options.fps) ? Math.max(1, Math.round(options.fps)) : DEFAULT_FPS;
|
|
13
|
+
const format = options.format ?? "mp4";
|
|
14
|
+
const isEmbed = format === "embed";
|
|
15
|
+
const outputPath = path.resolve(options.output ?? getDefaultOutput(format));
|
|
16
|
+
const totalDurationMs = walkthrough.steps.reduce((sum, step) => sum + Math.max(0, step.duration), 0);
|
|
17
|
+
const expectedFrames = Math.ceil(totalDurationMs / (1000 / fps));
|
|
18
|
+
const session = await createRecordingSession(walkthrough, { width, height });
|
|
19
|
+
try {
|
|
20
|
+
const { cdp, server } = session;
|
|
21
|
+
// No browser API patching — real-time mode uses wall clock
|
|
22
|
+
// Set up console listener before navigating
|
|
23
|
+
const readyPromise = waitForConsoleMessage(cdp, "__WALKR_RECORD_READY__");
|
|
24
|
+
// Navigate to studio in record mode
|
|
25
|
+
await cdp.send("Page.navigate", {
|
|
26
|
+
url: `${server.url}?mode=record`,
|
|
27
|
+
});
|
|
28
|
+
await readyPromise;
|
|
29
|
+
// Set up completion listener
|
|
30
|
+
const completePromise = new Promise((resolve) => {
|
|
31
|
+
const handler = (params) => {
|
|
32
|
+
const event = params;
|
|
33
|
+
const text = event.args?.map((a) => a.value ?? "").join(" ") ?? "";
|
|
34
|
+
if (text.includes("__WALKR_RECORD_COMPLETE__")) {
|
|
35
|
+
cdp.off("Runtime.consoleAPICalled", handler);
|
|
36
|
+
resolve();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
cdp.on("Runtime.consoleAPICalled", handler);
|
|
40
|
+
});
|
|
41
|
+
// For embed format, buffer frames. For video formats, stream to encoder.
|
|
42
|
+
const frames = [];
|
|
43
|
+
let encoder = null;
|
|
44
|
+
let frameCount = 0;
|
|
45
|
+
if (!isEmbed) {
|
|
46
|
+
encoder = new StreamingEncoder({
|
|
47
|
+
format: format,
|
|
48
|
+
fps,
|
|
49
|
+
width,
|
|
50
|
+
height,
|
|
51
|
+
outputPath,
|
|
52
|
+
expectedFrames,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Start screencast — push-based frame delivery
|
|
56
|
+
await cdp.send("Page.startScreencast", {
|
|
57
|
+
format: "jpeg",
|
|
58
|
+
quality: 90,
|
|
59
|
+
maxWidth: width,
|
|
60
|
+
maxHeight: height,
|
|
61
|
+
everyNthFrame: 1,
|
|
62
|
+
});
|
|
63
|
+
// Handle incoming screencast frames with serialized writes
|
|
64
|
+
let writeChain = Promise.resolve();
|
|
65
|
+
cdp.on("Page.screencastFrame", (params) => {
|
|
66
|
+
const event = params;
|
|
67
|
+
// Ack immediately so Chrome keeps sending frames
|
|
68
|
+
void cdp.send("Page.screencastFrameAck", { sessionId: event.sessionId });
|
|
69
|
+
const frameBuffer = Buffer.from(event.data, "base64");
|
|
70
|
+
if (isEmbed) {
|
|
71
|
+
frames.push(frameBuffer);
|
|
72
|
+
}
|
|
73
|
+
else if (encoder) {
|
|
74
|
+
const enc = encoder;
|
|
75
|
+
writeChain = writeChain.then(() => enc.write(frameBuffer));
|
|
76
|
+
}
|
|
77
|
+
frameCount++;
|
|
78
|
+
options.onProgress?.(Math.min(100, (frameCount / expectedFrames) * 100));
|
|
79
|
+
});
|
|
80
|
+
// Trigger playback
|
|
81
|
+
await cdp.send("Runtime.evaluate", {
|
|
82
|
+
expression: "window.__walkrPlay()",
|
|
83
|
+
awaitPromise: false,
|
|
84
|
+
});
|
|
85
|
+
// Wait for walkthrough completion
|
|
86
|
+
await completePromise;
|
|
87
|
+
// Stop screencast and drain pending writes
|
|
88
|
+
await cdp.send("Page.stopScreencast");
|
|
89
|
+
await writeChain;
|
|
90
|
+
// Finalize
|
|
91
|
+
if (isEmbed) {
|
|
92
|
+
return await encodeFrames(frames, {
|
|
93
|
+
...options,
|
|
94
|
+
width,
|
|
95
|
+
height,
|
|
96
|
+
fps,
|
|
97
|
+
walkthrough,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (encoder) {
|
|
101
|
+
return await encoder.finish();
|
|
102
|
+
}
|
|
103
|
+
throw new Error("Unexpected state: no encoder and not embed format");
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
session.cleanup();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=realtime-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-recorder.js","sourceRoot":"","sources":["../src/realtime-recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Walkthrough } from "@walkrstudio/core";
|
|
2
|
+
import type { RecordOptions, RecordResult } from "./types.js";
|
|
3
|
+
export declare function recordWalkthrough(walkthrough: Walkthrough, options?: RecordOptions): Promise<RecordResult>;
|
|
4
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAarD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAwMvB"}
|
package/dist/recorder.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { encodeFrames } from "./encoder.js";
|
|
3
|
+
import { recordRealtimeWalkthrough } from "./realtime-recorder.js";
|
|
4
|
+
import { createRecordingSession, DEFAULT_FPS, DEFAULT_HEIGHT, DEFAULT_WIDTH, getDefaultOutput, isFiniteNumber, waitForConsoleMessage, } from "./recording-session.js";
|
|
5
|
+
import { StreamingEncoder } from "./streaming-encoder.js";
|
|
6
|
+
export async function recordWalkthrough(walkthrough, options = {}) {
|
|
7
|
+
if (options.realtime) {
|
|
8
|
+
return recordRealtimeWalkthrough(walkthrough, options);
|
|
9
|
+
}
|
|
10
|
+
const width = isFiniteNumber(options.width)
|
|
11
|
+
? Math.max(1, Math.round(options.width))
|
|
12
|
+
: DEFAULT_WIDTH;
|
|
13
|
+
const height = isFiniteNumber(options.height)
|
|
14
|
+
? Math.max(1, Math.round(options.height))
|
|
15
|
+
: DEFAULT_HEIGHT;
|
|
16
|
+
const fps = isFiniteNumber(options.fps) ? Math.max(1, Math.round(options.fps)) : DEFAULT_FPS;
|
|
17
|
+
const frameIntervalMs = 1000 / fps;
|
|
18
|
+
const format = options.format ?? "mp4";
|
|
19
|
+
const isEmbed = format === "embed";
|
|
20
|
+
const outputPath = path.resolve(options.output ?? getDefaultOutput(format));
|
|
21
|
+
const session = await createRecordingSession(walkthrough, { width, height });
|
|
22
|
+
try {
|
|
23
|
+
const { cdp, server } = session;
|
|
24
|
+
// Patch browser APIs for virtual time compatibility.
|
|
25
|
+
// - WebSocket: block connections (pending WebSockets prevent virtual time advance)
|
|
26
|
+
// - requestAnimationFrame: replace with setTimeout (rAF doesn't fire during
|
|
27
|
+
// virtual time, but setTimeout does — the engine uses rAF for step timing)
|
|
28
|
+
await cdp.send("Page.addScriptToEvaluateOnNewDocument", {
|
|
29
|
+
source: `
|
|
30
|
+
window.WebSocket = class extends EventTarget {
|
|
31
|
+
static CONNECTING = 0;
|
|
32
|
+
static OPEN = 1;
|
|
33
|
+
static CLOSING = 2;
|
|
34
|
+
static CLOSED = 3;
|
|
35
|
+
CONNECTING = 0;
|
|
36
|
+
OPEN = 1;
|
|
37
|
+
CLOSING = 2;
|
|
38
|
+
CLOSED = 3;
|
|
39
|
+
readyState = 3;
|
|
40
|
+
bufferedAmount = 0;
|
|
41
|
+
extensions = '';
|
|
42
|
+
protocol = '';
|
|
43
|
+
binaryType = 'blob';
|
|
44
|
+
url = '';
|
|
45
|
+
constructor(url) {
|
|
46
|
+
super();
|
|
47
|
+
this.url = typeof url === 'string' ? url : '';
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
this.dispatchEvent(new CloseEvent('error'));
|
|
50
|
+
this.dispatchEvent(new CloseEvent('close', { code: 1006 }));
|
|
51
|
+
}, 0);
|
|
52
|
+
}
|
|
53
|
+
send() {}
|
|
54
|
+
close() {}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Replace rAF with setTimeout so callbacks fire during virtual time.
|
|
58
|
+
// Chrome's virtual time advances setTimeout but not rAF.
|
|
59
|
+
// The delay matches the frame capture interval so exactly 1 rAF
|
|
60
|
+
// callback fires per frame budget — minimal overhead with best
|
|
61
|
+
// timing accuracy. setTimeout(0) doesn't work because
|
|
62
|
+
// maxVirtualTimeTaskStarvationCount batches same-time tasks.
|
|
63
|
+
window.requestAnimationFrame = function(cb) {
|
|
64
|
+
return setTimeout(() => cb(performance.now()), ${Math.round(frameIntervalMs)});
|
|
65
|
+
};
|
|
66
|
+
window.cancelAnimationFrame = function(id) {
|
|
67
|
+
clearTimeout(id);
|
|
68
|
+
};
|
|
69
|
+
`,
|
|
70
|
+
worldName: "",
|
|
71
|
+
runImmediately: true,
|
|
72
|
+
});
|
|
73
|
+
// Pause virtual time before navigation
|
|
74
|
+
await cdp.send("Emulation.setVirtualTimePolicy", {
|
|
75
|
+
policy: "pause",
|
|
76
|
+
});
|
|
77
|
+
// Set up console listener before navigating
|
|
78
|
+
const readyPromise = waitForConsoleMessage(cdp, "__WALKR_RECORD_READY__");
|
|
79
|
+
// Navigate to studio in record mode
|
|
80
|
+
await cdp.send("Page.navigate", {
|
|
81
|
+
url: `${server.url}?mode=record`,
|
|
82
|
+
});
|
|
83
|
+
// Grant load budget — let page load, fetch walkthrough.json, load iframe
|
|
84
|
+
await cdp.send("Emulation.setVirtualTimePolicy", {
|
|
85
|
+
policy: "pauseIfNetworkFetchesPending",
|
|
86
|
+
budget: 30000,
|
|
87
|
+
maxVirtualTimeTaskStarvationCount: 100000,
|
|
88
|
+
waitForNavigation: true,
|
|
89
|
+
});
|
|
90
|
+
// Wait for __WALKR_RECORD_READY__
|
|
91
|
+
await readyPromise;
|
|
92
|
+
// Trigger playback
|
|
93
|
+
let isComplete = false;
|
|
94
|
+
cdp.on("Runtime.consoleAPICalled", (params) => {
|
|
95
|
+
const event = params;
|
|
96
|
+
const text = event.args?.map((a) => a.value ?? "").join(" ") ?? "";
|
|
97
|
+
if (text.includes("__WALKR_RECORD_COMPLETE__")) {
|
|
98
|
+
isComplete = true;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
await cdp.send("Runtime.evaluate", {
|
|
102
|
+
expression: "window.__walkrPlay()",
|
|
103
|
+
awaitPromise: false,
|
|
104
|
+
});
|
|
105
|
+
// Advance time in chunks until STEPPING fires (iframe loaded, first step executing)
|
|
106
|
+
await waitForConsoleMessage(cdp, "__WALKR_RECORD_STEPPING__");
|
|
107
|
+
// Pause virtual time to cancel the remaining load budget, then drain
|
|
108
|
+
// any stale virtualTimeBudgetExpired event from the cancelled budget.
|
|
109
|
+
await cdp.send("Emulation.setVirtualTimePolicy", { policy: "pause" });
|
|
110
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
111
|
+
// Frame capture loop using virtual time
|
|
112
|
+
const totalDurationMs = walkthrough.steps.reduce((sum, step) => sum + Math.max(0, step.duration), 0);
|
|
113
|
+
const expectedFrames = Math.ceil(totalDurationMs / frameIntervalMs);
|
|
114
|
+
// For embed format, buffer all frames. For video formats, stream to encoder.
|
|
115
|
+
const frames = [];
|
|
116
|
+
let encoder = null;
|
|
117
|
+
if (!isEmbed) {
|
|
118
|
+
encoder = new StreamingEncoder({
|
|
119
|
+
format: format,
|
|
120
|
+
fps,
|
|
121
|
+
width,
|
|
122
|
+
height,
|
|
123
|
+
outputPath,
|
|
124
|
+
expectedFrames,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
let capturedCount = 0;
|
|
128
|
+
while (!isComplete && capturedCount < expectedFrames) {
|
|
129
|
+
// Register listener BEFORE send — the response and event may arrive
|
|
130
|
+
// in the same TCP packet, so the event could be dispatched before
|
|
131
|
+
// the send promise resolves.
|
|
132
|
+
const budgetExpired = cdp.once("Emulation.virtualTimeBudgetExpired");
|
|
133
|
+
await cdp.send("Emulation.setVirtualTimePolicy", {
|
|
134
|
+
policy: "pauseIfNetworkFetchesPending",
|
|
135
|
+
budget: frameIntervalMs,
|
|
136
|
+
maxVirtualTimeTaskStarvationCount: 100000,
|
|
137
|
+
});
|
|
138
|
+
await budgetExpired;
|
|
139
|
+
// Switch to clean pause so the compositor can produce a frame.
|
|
140
|
+
// With pauseIfNetworkFetchesPending the compositor may block on
|
|
141
|
+
// pending requests, causing captureScreenshot to hang.
|
|
142
|
+
await cdp.send("Emulation.setVirtualTimePolicy", { policy: "pause" });
|
|
143
|
+
// Capture screenshot
|
|
144
|
+
const result = (await cdp.send("Page.captureScreenshot", {
|
|
145
|
+
format: "jpeg",
|
|
146
|
+
quality: 90,
|
|
147
|
+
}));
|
|
148
|
+
const frameBuffer = Buffer.from(result.data, "base64");
|
|
149
|
+
if (isEmbed) {
|
|
150
|
+
frames.push(frameBuffer);
|
|
151
|
+
}
|
|
152
|
+
else if (encoder) {
|
|
153
|
+
await encoder.write(frameBuffer);
|
|
154
|
+
}
|
|
155
|
+
capturedCount++;
|
|
156
|
+
// Report progress
|
|
157
|
+
options.onProgress?.(Math.min(100, (capturedCount / expectedFrames) * 100));
|
|
158
|
+
}
|
|
159
|
+
// Finalize
|
|
160
|
+
if (isEmbed) {
|
|
161
|
+
// Trim to exact expected frame count — virtual time rounding may produce extras
|
|
162
|
+
const finalFrames = frames.slice(0, expectedFrames);
|
|
163
|
+
return await encodeFrames(finalFrames, {
|
|
164
|
+
...options,
|
|
165
|
+
width,
|
|
166
|
+
height,
|
|
167
|
+
fps,
|
|
168
|
+
walkthrough,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (encoder) {
|
|
172
|
+
return await encoder.finish();
|
|
173
|
+
}
|
|
174
|
+
throw new Error("Unexpected state: no encoder and not embed format");
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
session.cleanup();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Walkthrough } from "@walkrstudio/core";
|
|
2
|
+
import { CDPClient } from "./cdp.js";
|
|
3
|
+
import type { RecordOptions } from "./types.js";
|
|
4
|
+
interface RecordingSession {
|
|
5
|
+
server: {
|
|
6
|
+
url: string;
|
|
7
|
+
close: () => void;
|
|
8
|
+
};
|
|
9
|
+
chromium: {
|
|
10
|
+
close: () => void;
|
|
11
|
+
};
|
|
12
|
+
cdp: CDPClient;
|
|
13
|
+
cleanup: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_WIDTH = 1920;
|
|
16
|
+
export declare const DEFAULT_HEIGHT = 1080;
|
|
17
|
+
export declare const DEFAULT_FPS = 30;
|
|
18
|
+
export declare const isFiniteNumber: (value: unknown) => value is number;
|
|
19
|
+
export declare const getDefaultOutput: (format: RecordOptions["format"]) => string;
|
|
20
|
+
export declare function createRecordingSession(walkthrough: Walkthrough, opts: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}): Promise<RecordingSession>;
|
|
24
|
+
export declare function waitForConsoleMessage(cdp: CDPClient, message: string): Promise<void>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=recording-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-session.d.ts","sourceRoot":"","sources":["../src/recording-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,UAAU,gBAAgB;IACxB,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAC3C,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAChC,GAAG,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACJ,CAAC;AAEtD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,aAAa,CAAC,QAAQ,CAAC,KAAG,MAKlE,CAAC;AAoBF,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,gBAAgB,CAAC,CAwC3B;AAED,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB1F"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CDPClient } from "./cdp.js";
|
|
2
|
+
import { launchChromium } from "./chromium.js";
|
|
3
|
+
import { startStaticServer } from "./static-server.js";
|
|
4
|
+
export const DEFAULT_WIDTH = 1920;
|
|
5
|
+
export const DEFAULT_HEIGHT = 1080;
|
|
6
|
+
export const DEFAULT_FPS = 30;
|
|
7
|
+
export const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
8
|
+
export const getDefaultOutput = (format) => {
|
|
9
|
+
if (format === "gif")
|
|
10
|
+
return "output.gif";
|
|
11
|
+
if (format === "webm")
|
|
12
|
+
return "output.webm";
|
|
13
|
+
if (format === "embed")
|
|
14
|
+
return "output.html";
|
|
15
|
+
return "output.mp4";
|
|
16
|
+
};
|
|
17
|
+
async function getPageWsUrl(browserWsUrl) {
|
|
18
|
+
const url = new URL(browserWsUrl);
|
|
19
|
+
const httpUrl = `http://${url.host}/json/list`;
|
|
20
|
+
const res = await fetch(httpUrl);
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`Failed to list CDP targets: ${res.status}`);
|
|
23
|
+
}
|
|
24
|
+
const targets = (await res.json());
|
|
25
|
+
const page = targets.find((t) => t.type === "page");
|
|
26
|
+
if (!page?.webSocketDebuggerUrl) {
|
|
27
|
+
throw new Error("No page target found in Chromium");
|
|
28
|
+
}
|
|
29
|
+
return page.webSocketDebuggerUrl;
|
|
30
|
+
}
|
|
31
|
+
export async function createRecordingSession(walkthrough, opts) {
|
|
32
|
+
const server = await startStaticServer(walkthrough);
|
|
33
|
+
let chromium = null;
|
|
34
|
+
let cdp = null;
|
|
35
|
+
try {
|
|
36
|
+
const browser = await launchChromium({ width: opts.width, height: opts.height });
|
|
37
|
+
chromium = browser;
|
|
38
|
+
const pageWsUrl = await getPageWsUrl(browser.wsUrl);
|
|
39
|
+
cdp = await CDPClient.connect(pageWsUrl);
|
|
40
|
+
await cdp.send("Page.enable");
|
|
41
|
+
await cdp.send("Runtime.enable");
|
|
42
|
+
await cdp.send("Emulation.setDeviceMetricsOverride", {
|
|
43
|
+
width: opts.width,
|
|
44
|
+
height: opts.height,
|
|
45
|
+
deviceScaleFactor: 1,
|
|
46
|
+
mobile: false,
|
|
47
|
+
});
|
|
48
|
+
const sessionCdp = cdp;
|
|
49
|
+
const sessionChromium = chromium;
|
|
50
|
+
return {
|
|
51
|
+
server,
|
|
52
|
+
chromium,
|
|
53
|
+
cdp,
|
|
54
|
+
cleanup() {
|
|
55
|
+
sessionCdp.close();
|
|
56
|
+
sessionChromium.close();
|
|
57
|
+
server.close();
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
cdp?.close();
|
|
63
|
+
chromium?.close();
|
|
64
|
+
server.close();
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function waitForConsoleMessage(cdp, message) {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const timeout = setTimeout(() => {
|
|
71
|
+
cdp.off("Runtime.consoleAPICalled", handler);
|
|
72
|
+
reject(new Error(`Timed out waiting for console message: ${message}`));
|
|
73
|
+
}, 30_000);
|
|
74
|
+
const handler = (params) => {
|
|
75
|
+
const event = params;
|
|
76
|
+
if (event.type !== "log")
|
|
77
|
+
return;
|
|
78
|
+
const text = event.args?.map((a) => a.value ?? "").join(" ") ?? "";
|
|
79
|
+
if (text.includes(message)) {
|
|
80
|
+
clearTimeout(timeout);
|
|
81
|
+
cdp.off("Runtime.consoleAPICalled", handler);
|
|
82
|
+
resolve();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
cdp.on("Runtime.consoleAPICalled", handler);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=recording-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-session.js","sourceRoot":"","sources":["../src/recording-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Walkthrough } from "@walkrstudio/core";
|
|
2
|
+
interface StudioServer {
|
|
3
|
+
port: number;
|
|
4
|
+
url: string;
|
|
5
|
+
close: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function startStaticServer(walkthrough: Walkthrough): Promise<StudioServer>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=static-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.d.ts","sourceRoot":"","sources":["../src/static-server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAoKD,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAuFvF"}
|