YtDlpTool.ts•3.09 kB
import { MCPTool } from "mcp-framework";
import { z } from "zod";
import { exec } from "child_process";
import { promisify } from "util";
import path from "path";
const execPromise = promisify(exec);
interface YtDlpInput {
url: string;
}
class YtDlpTool extends MCPTool<YtDlpInput> {
name = "yt_dlp";
description = "使用yt-dlp下载视频或获取视频信息";
schema = {
url: {
type: z.string(),
description: "要下载或获取信息的视频URL",
},
};
async execute(input: YtDlpInput) {
// 使用正确的绝对路径
const ytDlpPath = "F:\\Github\\yt-mcp-server\\yt-dlp.exe";
try {
console.log(`yt-dlp.exe路径: ${ytDlpPath}`);
// 处理URL,移除可能导致命令行解析错误的参数
const cleanUrl = this.cleanUrl(input.url);
// 构建命令行参数,使用双引号包裹URL,并添加下载路径
const downloadPath = "H:\\B站视频";
const command = `${ytDlpPath} -P "${downloadPath}" "${cleanUrl}"`;
console.log(`执行命令: ${command}`);
const { stdout, stderr } = await execPromise(command);
// yt-dlp有时会将警告信息输出到stderr,但这不一定是错误
if (stderr) {
console.log(`yt-dlp警告或信息: ${stderr}`);
}
// 即使有stderr输出,如果stdout也有内容,我们仍然认为命令执行成功
if (stdout) {
return {
content: [
{
type: "text",
text: `使用yt-dlp路径: ${ytDlpPath}\n\n${stdout}`
}
]
};
} else {
// 如果没有stdout输出,则视为错误
return {
isError: true,
content: [
{
type: "text",
text: `使用yt-dlp路径: ${ytDlpPath}\n\n执行yt-dlp时出错: ${stderr}`
}
]
};
}
} catch (error) {
console.error(`执行yt-dlp时出错:`, error);
// 返回符合MCP协议的错误
return {
isError: true,
content: [
{
type: "text",
text: `使用yt-dlp路径: ${ytDlpPath}\n\n执行yt-dlp时出错: ${error instanceof Error ? error.message : String(error)}`
}
]
};
}
}
// 清理URL,移除可能导致命令行解析错误的参数
private cleanUrl(url: string): string {
// 提取基本URL,移除查询参数
const urlObj = new URL(url);
const baseUrl = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;
// 只保留必要的查询参数(例如视频ID)
// 对于B站,通常只需要保留BV号即可
if (url.includes('bilibili.com')) {
// B站URL通常格式为 https://www.bilibili.com/video/BV1p2Q6YsEGk/
// 我们只需要保留到BV号的部分
const bvMatch = baseUrl.match(/(\/video\/[A-Za-z0-9]+)/);
if (bvMatch) {
return `https://www.bilibili.com${bvMatch[0]}`;
}
}
return baseUrl;
}
}
export default YtDlpTool;