2024-08-15 12:54:10 +03:00
|
|
|
import { spawn, type ChildProcessWithoutNullStreams } from 'child_process';
|
2024-08-16 15:51:33 +03:00
|
|
|
import path from 'node:path';
|
2024-08-15 12:54:10 +03:00
|
|
|
import type { Frameworks } from '@wdio/types';
|
|
|
|
|
|
|
|
function fileName(title: string) {
|
|
|
|
return encodeURIComponent(title.trim().replace(/\s+/g, '-'));
|
|
|
|
}
|
|
|
|
|
|
|
|
export class TestRecorder {
|
|
|
|
ffmpeg!: ChildProcessWithoutNullStreams;
|
|
|
|
|
|
|
|
constructor() {}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
this.ffmpeg?.kill('SIGINT');
|
|
|
|
}
|
|
|
|
|
|
|
|
start(test: Frameworks.Test, videoPath: string) {
|
2024-08-16 15:51:33 +03:00
|
|
|
if (!videoPath || !test) {
|
|
|
|
throw new Error('Cannot start recording without a test and path for the video file.');
|
2024-08-15 12:54:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (process.env.DISPLAY && process.env.DISPLAY.startsWith(':')) {
|
2024-08-16 15:51:33 +03:00
|
|
|
const parsedPath = path.join(
|
2024-08-15 12:54:10 +03:00
|
|
|
videoPath,
|
2024-08-16 15:51:33 +03:00
|
|
|
`${fileName(test.parent)}-${fileName(test.title)}.mp4`
|
|
|
|
);
|
2024-08-15 12:54:10 +03:00
|
|
|
|
|
|
|
this.ffmpeg = spawn('ffmpeg', [
|
|
|
|
'-f',
|
2024-08-16 15:51:33 +03:00
|
|
|
'x11grab',
|
2024-08-15 12:54:10 +03:00
|
|
|
'-video_size',
|
2024-08-16 15:51:33 +03:00
|
|
|
'1280x1024',
|
2024-08-15 12:54:10 +03:00
|
|
|
'-i',
|
2024-08-16 15:51:33 +03:00
|
|
|
process.env.DISPLAY,
|
2024-08-15 12:54:10 +03:00
|
|
|
'-loglevel',
|
2024-08-16 15:51:33 +03:00
|
|
|
'error',
|
|
|
|
'-y',
|
2024-08-15 12:54:10 +03:00
|
|
|
'-pix_fmt',
|
2024-08-16 15:51:33 +03:00
|
|
|
'yuv420p',
|
|
|
|
parsedPath
|
2024-08-15 12:54:10 +03:00
|
|
|
]);
|
|
|
|
|
2024-08-16 15:51:33 +03:00
|
|
|
function logBuffer(buffer: Buffer, prefix: string) {
|
2024-08-15 12:54:10 +03:00
|
|
|
const lines = buffer.toString().trim().split('\n');
|
|
|
|
lines.forEach(function (line) {
|
|
|
|
console.log(prefix + line);
|
|
|
|
});
|
2024-08-16 15:51:33 +03:00
|
|
|
}
|
2024-08-15 12:54:10 +03:00
|
|
|
|
|
|
|
this.ffmpeg.stdout.on('data', (data: Buffer) => {
|
|
|
|
logBuffer(data, '[ffmpeg:stdout] ');
|
|
|
|
});
|
|
|
|
|
|
|
|
this.ffmpeg.stderr.on('data', (data: Buffer) => {
|
|
|
|
logBuffer(data, '[ffmpeg:error] ');
|
|
|
|
});
|
|
|
|
|
2024-08-16 15:51:33 +03:00
|
|
|
this.ffmpeg.on('close', (code?: number, signal?: string) => {
|
|
|
|
if (code) {
|
2024-08-15 12:54:10 +03:00
|
|
|
console.log(`[ffmpeg:stdout] exited with code ${code}: ${videoPath}`);
|
|
|
|
}
|
2024-08-16 15:51:33 +03:00
|
|
|
if (signal) {
|
2024-08-15 12:54:10 +03:00
|
|
|
console.log(`[ffmpeg:stdout] received signal ${signal}: ${videoPath}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|