Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
548e3f6
chore: upgrades to
ruiconti Jan 24, 2025
c559345
chore: maintain CSS output file name in vite@6.0.1
ruiconti Jan 24, 2025
61ae54e
Merge branch 'master' into rui/fix-worker-b64-inlined-source
Juice10 Feb 4, 2025
aacb7c4
Create cuddly-dolphins-approve.md
Juice10 Feb 4, 2025
5ac9a19
Merge branch 'master' into rui/fix-worker-b64-inlined-source
Juice10 Nov 20, 2025
d2e46d5
Merge branch 'master' into rui/fix-worker-b64-inlined-source
Juice10 Nov 20, 2025
fcea902
Merge branch 'master' into rui/fix-worker-b64-inlined-source
Juice10 Nov 20, 2025
a98ad5a
build(rrvideo): upgrade playwright from 1.32.1 to 1.56.1
Juice10 Nov 21, 2025
985e586
chore: add empty changeset
Juice10 Nov 21, 2025
88f9d83
build(rrvideo): upgrade playwright from 1.32.1 to 1.56.1
Juice10 Nov 21, 2025
51eb746
chore: add empty changeset
Juice10 Nov 21, 2025
d6c8c32
debug(rrvideo): add comprehensive logging to video transformation pro…
Juice10 Nov 21, 2025
2eb0631
Apply formatting changes
Juice10 Nov 21, 2025
9f71c1a
ci(rrvideo): install playwright browsers and improve test output visi…
Juice10 Nov 21, 2025
39d367e
fix(rrvideo): prevent autoplay and manually start playback after even…
Juice10 Nov 21, 2025
193f1bf
fix(rrvideo): add timeout and error handling to replay process
Juice10 Nov 21, 2025
564c8b0
Apply formatting changes
Juice10 Nov 21, 2025
7c9296c
fix(rrvideo): add error handling and restructure replayer initialization
Juice10 Nov 21, 2025
2ad7690
Merge branch 'juice10/rrvideo-test-update' into juice10/pr-1637-tests
Juice10 Nov 21, 2025
05cf0dc
Initial plan
Copilot Jan 2, 2026
de8a48c
Calculate timeout based on event duration and playback speed
Copilot Jan 2, 2026
b494d77
Apply formatting changes
Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/cuddly-dolphins-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@rrweb/all": patch
"@rrweb/packer": patch
"@rrweb/record": patch
"rrweb-snapshot": patch
"rrweb": patch
"@rrweb/web-extension": patch
---

Drop base64 inlined worker source from all bundles
2 changes: 2 additions & 0 deletions .changeset/eight-years-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
3 changes: 3 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
- name: Build Project
run: NODE_OPTIONS='--max-old-space-size=4096' yarn build:all

- name: Install Playwright browsers
run: cd packages/rrvideo && yarn playwright install chromium

- name: Check types
run: yarn check-types

Expand Down
2 changes: 1 addition & 1 deletion packages/all/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
],
"devDependencies": {
"puppeteer": "^20.9.0",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"typescript": "^5.4.5"
Expand Down
2 changes: 1 addition & 1 deletion packages/packer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"package.json"
],
"devDependencies": {
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"typescript": "^5.4.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"devDependencies": {
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"devDependencies": {
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/rrweb-plugin-console-record/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"devDependencies": {
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"puppeteer": "^20.9.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/rrweb-plugin-console-replay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.19",
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"devDependencies": {
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@rrweb/rrweb-plugin-sequential-id-record": "^2.0.0-alpha.19",
"rrweb": "^2.0.0-alpha.19",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/record/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
],
"devDependencies": {
"puppeteer": "^20.9.0",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"typescript": "^5.4.5"
Expand Down
2 changes: 1 addition & 1 deletion packages/replay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
],
"devDependencies": {
"puppeteer": "^20.9.0",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"typescript": "^5.4.5"
Expand Down
2 changes: 1 addition & 1 deletion packages/rrdom-nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"compare-versions": "^4.1.3",
"eslint": "^8.15.0",
"puppeteer": "^9.1.1",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1",
"vitest": "^1.4.0",
"typescript": "^5.4.5"
Expand Down
2 changes: 1 addition & 1 deletion packages/rrdom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"eslint": "^8.15.0",
"puppeteer": "^17.1.3",
"typescript": "^5.4.5",
"vite": "^5.3.1",
"vite": "^6.0.1",
"vite-plugin-dts": "^3.9.1"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/rrvideo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"@open-tech-world/cli-progress-bar": "^2.0.2",
"fs-extra": "^11.1.1",
"minimist": "^1.2.5",
"playwright": "^1.32.1",
"playwright": "^1.56.1",
"rrweb-player": "^2.0.0-alpha.19"
}
}
112 changes: 90 additions & 22 deletions packages/rrvideo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,29 @@ function getHtml(events: Array<eventWithTime>, config?: RRvideoConfig): string {
)};
/*-->*/
const userConfig = ${JSON.stringify(config?.rrwebPlayer || {})};
window.replayer = new rrwebPlayer.Player({
target: document.body,
width: userConfig.width,
height: userConfig.height,
props: {
...userConfig,
events,
showController: false,
},
});
window.replayer.addEventListener('finish', () => window.onReplayFinish());
window.replayer.addEventListener('ui-update-progress', (payload)=> window.onReplayProgressUpdate
(payload));
window.replayer.addEventListener('resize',()=>document.querySelector('.replayer-wrapper').style.transform = 'scale(${
(config?.resolutionRatio ?? 1) * MaxScaleValue
}) translate(-50%, -50%)');
try {
window.replayer = new rrwebPlayer({
target: document.body,
props: {
width: userConfig.width,
height: userConfig.height,
...userConfig,
events,
showController: false,
autoPlay: false,
},
});
window.replayer.addEventListener('finish', () => window.onReplayFinish());
window.replayer.addEventListener('ui-update-progress', (payload)=> window.onReplayProgressUpdate(payload));
window.replayer.addEventListener('resize',()=>document.querySelector('.replayer-wrapper').style.transform = 'scale(${
(config?.resolutionRatio ?? 1) * MaxScaleValue
}) translate(-50%, -50%)');
// Start playback after event listeners are attached
window.replayer.play();
} catch (error) {
console.error('Error initializing replayer:', error);
window.onReplayFinish();
}
</script>
</body>
</html>
Expand Down Expand Up @@ -105,18 +112,21 @@ export async function transformToVideo(options: RRvideoConfig) {
Object.assign(config, options);
if (config.resolutionRatio > 1) config.resolutionRatio = 1; // The max value is 1.

console.log('[DEBUG] Starting transformToVideo');
const eventsPath = path.isAbsolute(config.input)
? config.input
: path.resolve(process.cwd(), config.input);
const outputPath = path.isAbsolute(config.output)
? config.output
: path.resolve(process.cwd(), config.output);
console.log('[DEBUG] Reading events from:', eventsPath);
const events = JSON.parse(
fs.readFileSync(eventsPath, 'utf-8'),
) as eventWithTime[];

// Make the browser viewport fit the player size.
const maxViewport = getMaxViewport(events);
console.log('[DEBUG] Max viewport:', maxViewport);
// Use the scaling method to improve the video quality.
const scaledViewport = {
width: Math.round(
Expand All @@ -127,18 +137,39 @@ export async function transformToVideo(options: RRvideoConfig) {
),
};
Object.assign(config.rrwebPlayer, scaledViewport);
console.log('[DEBUG] Launching browser with headless:', config.headless);
const browser = await chromium.launch({
headless: config.headless,
});
console.log('[DEBUG] Browser launched successfully');
console.log(
'[DEBUG] Creating browser context with viewport:',
scaledViewport,
);
const context = await browser.newContext({
viewport: scaledViewport,
recordVideo: {
dir: defaultVideoDir,
size: scaledViewport,
},
});
console.log('[DEBUG] Browser context created');
const page = await context.newPage();
console.log('[DEBUG] New page created');
await page.goto('about:blank');
console.log('[DEBUG] Navigated to about:blank');
console.log('[DEBUG] Exposing functions to page');

// Listen to console messages from the page
page.on('console', (msg) => {
console.log('[PAGE CONSOLE]', msg.type(), msg.text());
});

// Listen to page errors
page.on('pageerror', (error) => {
console.error('[PAGE ERROR]', error.message);
});

await page.exposeFunction(
'onReplayProgressUpdate',
(data: { payload: number }) => {
Expand All @@ -147,20 +178,56 @@ export async function transformToVideo(options: RRvideoConfig) {
);

// Wait for the replay to finish
await new Promise<void>(
(resolve) =>
void page
.exposeFunction('onReplayFinish', () => resolve())
.then(() => page.setContent(getHtml(events, config))),
);
console.log('[DEBUG] Starting replay');
await new Promise<void>((resolve, reject) => {
// Calculate timeout based on event duration and playback speed
const timestamps = events.map((e) => e.timestamp);
const minTimestamp = Math.min(...timestamps);
const maxTimestamp = Math.max(...timestamps);
const eventDuration = maxTimestamp - minTimestamp;
const playbackSpeed = config.rrwebPlayer.speed ?? 1;
// Calculate timeout: event duration / playback speed + 2 minute buffer
const timeoutDuration = Math.ceil(eventDuration / playbackSpeed) + 120000;
console.log(
`[DEBUG] Event duration: ${eventDuration}ms, playback speed: ${playbackSpeed}x, timeout: ${timeoutDuration}ms`,
);

const timeout = setTimeout(() => {
console.error('[DEBUG] Replay timeout - finish event never fired');
reject(new Error('Replay timeout'));
}, timeoutDuration);

void page
.exposeFunction('onReplayFinish', () => {
console.log('[DEBUG] Replay finished');
clearTimeout(timeout);
resolve();
})
.then(() => {
console.log('[DEBUG] Setting page content');
return page.setContent(getHtml(events, config));
})
.then(() => {
console.log('[DEBUG] Page content set successfully');
})
.catch((err) => {
console.error('[DEBUG] Error setting page content:', err);
clearTimeout(timeout);
reject(err);
});
});
console.log('[DEBUG] Getting video path');
const videoPath = (await page.video()?.path()) || '';
console.log('[DEBUG] Video path:', videoPath);
const cleanFiles = async (videoPath: string) => {
await fs.remove(videoPath);
if ((await fs.readdir(defaultVideoDir)).length === 0) {
await fs.remove(defaultVideoDir);
}
};
console.log('[DEBUG] Closing context');
await context.close();
console.log('[DEBUG] Moving video file to output path:', outputPath);
await Promise.all([
fs
.move(videoPath, outputPath, { overwrite: true })
Expand All @@ -173,5 +240,6 @@ export async function transformToVideo(options: RRvideoConfig) {
.finally(() => void cleanFiles(videoPath)),
browser.close(),
]);
console.log('[DEBUG] Video transformation complete');
return outputPath;
}
14 changes: 9 additions & 5 deletions packages/rrvideo/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ describe('should be able to run cli', () => {
await fs.remove(path.resolve(__dirname, './generated'));
});

const execOptions = { timeout: 60_000 } as const;
const execOptionsWithOutput = { ...execOptions, stdio: 'inherit' } as const;

it('should throw error without input path', () => {
expect(() => {
execSync('node ./build/cli.js', { stdio: 'pipe' });
execSync('node ./build/cli.js', { ...execOptions, stdio: 'pipe' });
}).toThrowError(/.*please pass --input to your rrweb events file.*/);
});

it('should generate a video without output path', () => {
execSync('node ./build/cli.js --input ./test/generated/example.json', {
stdio: 'pipe',
});
execSync(
'node ./build/cli.js --input ./test/generated/example.json',
execOptionsWithOutput,
);
const outputFile = path.resolve(__dirname, '../rrvideo-output.webm');
expect(fs.existsSync(outputFile)).toBe(true);
fs.removeSync(outputFile);
Expand All @@ -37,7 +41,7 @@ describe('should be able to run cli', () => {
const outputFile = path.resolve(__dirname, './generated/output.webm');
execSync(
`node ./build/cli.js --input ./test/generated/example.json --output ${outputFile}`,
{ stdio: 'pipe' },
execOptionsWithOutput,
);
expect(fs.existsSync(outputFile)).toBe(true);
fs.removeSync(outputFile);
Expand Down
Loading