-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathscreencast.ts
More file actions
121 lines (106 loc) · 3.63 KB
/
screencast.ts
File metadata and controls
121 lines (106 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import {zod} from '../third_party/index.js';
import type {ScreenRecorder, VideoFormat} from '../third_party/index.js';
import {ensureExtension} from '../utils/files.js';
import {ToolCategory} from './categories.js';
import {definePageTool} from './ToolDefinition.js';
async function generateTempFilePath(): Promise<string> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'chrome-devtools-mcp-'));
return path.join(dir, `screencast.mp4`);
}
const supportedExtensions: Array<`.${string}`> = ['.webm', '.mp4'];
export const startScreencast = definePageTool(args => ({
name: 'screencast_start',
description: `Starts recording a screencast (video) of the selected page in specified format.`,
annotations: {
category: ToolCategory.DEBUGGING,
readOnlyHint: false,
conditions: ['screencast'],
},
schema: {
filePath: zod
.string()
.optional()
.describe(
`Output file path (${supportedExtensions.join(',')} are supported). Uses mkdtemp to generate a unique path if not provided.`,
),
},
handler: async (request, response, context) => {
if (request.params.filePath) {
context.validatePath(request.params.filePath);
}
if (context.getScreenRecorder() !== null) {
response.appendResponseLine(
'Error: a screencast recording is already in progress. Use screencast_stop to stop it before starting a new one.',
);
return;
}
const filePath = request.params.filePath ?? (await generateTempFilePath());
let enforcedExtension = '.mp4' as `.${string}`;
let format: VideoFormat = 'mp4';
for (const supportedExtension of supportedExtensions) {
if (filePath.endsWith(supportedExtension)) {
enforcedExtension = supportedExtension;
format = supportedExtension.substring(1) as VideoFormat;
break;
}
}
const resolvedPath = ensureExtension(
path.resolve(filePath),
enforcedExtension,
) as `${string}.webm`;
const page = request.page;
let recorder: ScreenRecorder;
try {
recorder = await page.pptrPage.screencast({
path: resolvedPath,
format: format,
ffmpegPath: args?.experimentalFfmpegPath,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes('ENOENT') && message.includes('ffmpeg')) {
throw new Error(
'ffmpeg is required for screencast recording but was not found. ' +
'Install ffmpeg (https://ffmpeg.org/) and ensure it is available in your PATH.',
);
}
throw err;
}
context.setScreenRecorder({recorder, filePath: resolvedPath});
response.appendResponseLine(
`Screencast recording started. The recording will be saved to ${resolvedPath}. Use ${stopScreencast.name} to stop recording.`,
);
},
}));
export const stopScreencast = definePageTool({
name: 'screencast_stop',
description: 'Stops the active screencast recording on the selected page.',
annotations: {
category: ToolCategory.DEBUGGING,
readOnlyHint: false,
conditions: ['screencast'],
},
schema: {},
handler: async (_request, response, context) => {
const data = context.getScreenRecorder();
if (!data) {
return;
}
try {
await data.recorder.stop();
response.appendResponseLine(
`The screencast recording has been stopped and saved to ${data.filePath}.`,
);
} finally {
context.setScreenRecorder(null);
}
},
});