convert_video
Convert video files between MP4, AVI, MOV, WMV, MKV, WEBM, and M4V formats with customizable quality, resolution, bitrate, and frame rate settings for precise output control.
Instructions
将视频文件转换为指定格式。支持MP4、AVI、MOV、WMV、FLV、MKV、WEBM、M4V等主流格式之间的相互转换。
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| audioBitrate | No | 音频码率,单位kbps(可选) | |
| frameRate | No | 输出帧率,单位fps(可选) | |
| inputPath | Yes | 输入视频文件的完整路径 | |
| outputFormat | Yes | 目标输出格式 | |
| outputPath | No | 输出文件路径(可选,如果不指定则自动生成) | |
| overwrite | No | 是否覆盖已存在的输出文件(默认false) | |
| quality | No | 视频质量预设(可选) | |
| resolution | No | 输出分辨率,格式为"宽度x高度",如"1920x1080"(可选) | |
| videoBitrate | No | 视频码率,单位kbps(可选) |
Implementation Reference
- src/tools/convert.ts:62-211 (handler)Main handler for 'convert_video' tool: validates inputs, builds options, invokes FFmpeg service for conversion, processes progress and returns comprehensive results with file info.export async function handleConvertVideo(args: ConvertVideoArgs): Promise<any> { const ffmpegService = FFmpegService.getInstance(); const validator = ValidatorService.getInstance(); try { // 验证输入文件 const inputValidation = await validator.validateVideoFile(args.inputPath); if (!inputValidation.isValid) { return { success: false, error: `输入文件验证失败: ${inputValidation.error}` }; } // 验证输出格式 const formatValidation = validator.validateOutputFormat(args.outputFormat); if (!formatValidation.isValid) { return { success: false, error: formatValidation.error }; } // 生成输出路径(如果未指定) let outputPath = args.outputPath; if (!outputPath) { const inputDir = path.dirname(args.inputPath); const inputName = path.parse(args.inputPath).name; outputPath = path.join(inputDir, `${inputName}_converted.${args.outputFormat}`); } // 验证输出路径 const outputValidation = await validator.validateOutputPath(outputPath, args.overwrite || false); if (!outputValidation.isValid) { return { success: false, error: outputValidation.error }; } // 构建转换选项 const conversionOptions: any = { outputFormat: args.outputFormat as VideoFormat, overwrite: args.overwrite || false }; // 添加可选参数 if (args.quality) { conversionOptions.quality = args.quality; } if (args.resolution) { const resolutionValidation = validator.validateResolution(args.resolution); if (!resolutionValidation.isValid) { return { success: false, error: resolutionValidation.error }; } conversionOptions.resolution = { width: resolutionValidation.width!, height: resolutionValidation.height! }; } if (args.videoBitrate) { const bitrateValidation = validator.validateBitrate(args.videoBitrate, 'video'); if (!bitrateValidation.isValid) { return { success: false, error: bitrateValidation.error }; } conversionOptions.videoBitrate = args.videoBitrate; } if (args.audioBitrate) { const bitrateValidation = validator.validateBitrate(args.audioBitrate, 'audio'); if (!bitrateValidation.isValid) { return { success: false, error: bitrateValidation.error }; } conversionOptions.audioBitrate = args.audioBitrate; } if (args.frameRate) { const frameRateValidation = validator.validateFrameRate(args.frameRate); if (!frameRateValidation.isValid) { return { success: false, error: frameRateValidation.error }; } conversionOptions.frameRate = args.frameRate; } // 获取输入文件信息 const inputInfo = await ffmpegService.getVideoInfo(args.inputPath); // 执行转换 console.log(`开始转换视频: ${args.inputPath} -> ${outputPath}`); const result = await ffmpegService.convertVideo( args.inputPath, outputPath, conversionOptions, (progress) => { console.log(`转换进度: ${progress.progress}% (${progress.status})`); } ); // 获取输出文件信息 const outputInfo = await ffmpegService.getVideoInfo(result); return { success: true, message: '视频转换完成', data: { inputPath: args.inputPath, outputPath: result, inputInfo: { format: inputInfo.format, size: `${(inputInfo.size / (1024 * 1024)).toFixed(2)} MB`, duration: `${Math.round(inputInfo.duration)} 秒`, resolution: inputInfo.video ? `${inputInfo.video.width}x${inputInfo.video.height}` : '未知', videoCodec: inputInfo.video?.codec || '未知', audioCodec: inputInfo.audio?.codec || '未知' }, outputInfo: { format: outputInfo.format, size: `${(outputInfo.size / (1024 * 1024)).toFixed(2)} MB`, duration: `${Math.round(outputInfo.duration)} 秒`, resolution: outputInfo.video ? `${outputInfo.video.width}x${outputInfo.video.height}` : '未知', videoCodec: outputInfo.video?.codec || '未知', audioCodec: outputInfo.audio?.codec || '未知' }, conversionOptions } }; } catch (error: any) { console.error('视频转换失败:', error); return { success: false, error: `转换失败: ${error.message}` }; } }
- src/tools/convert.ts:10-56 (schema)Tool definition including input schema for 'convert_video' with detailed properties, enums for formats/quality, and required fields.export const convertVideoTool: Tool = { name: 'convert_video', description: '将视频文件转换为指定格式。支持MP4、AVI、MOV、WMV、FLV、MKV、WEBM、M4V等主流格式之间的相互转换。', inputSchema: { type: 'object', properties: { inputPath: { type: 'string', description: '输入视频文件的完整路径' }, outputFormat: { type: 'string', enum: ['mp4', 'avi', 'mov', 'wmv', 'mkv', 'webm', 'm4v'], description: '目标输出格式' }, outputPath: { type: 'string', description: '输出文件路径(可选,如果不指定则自动生成)' }, quality: { type: 'string', enum: ['low', 'medium', 'high', 'ultra'], description: '视频质量预设(可选)' }, resolution: { type: 'string', description: '输出分辨率,格式为"宽度x高度",如"1920x1080"(可选)' }, videoBitrate: { type: 'number', description: '视频码率,单位kbps(可选)' }, audioBitrate: { type: 'number', description: '音频码率,单位kbps(可选)' }, frameRate: { type: 'number', description: '输出帧率,单位fps(可选)' }, overwrite: { type: 'boolean', description: '是否覆盖已存在的输出文件(默认false)' } }, required: ['inputPath', 'outputFormat'] }
- src/tools/index.ts:22-26 (registration)Registration of tool handlers mapping 'convert_video' to its handleConvertVideo function.export const toolHandlers = { convert_video: handleConvertVideo, get_video_info: handleGetVideoInfo, batch_convert: handleBatchConvert };
- src/tools/index.ts:15-19 (registration)Export of tools array including convertVideoTool for MCP registration.export const tools = [ convertVideoTool, getVideoInfoTool, batchConvertTool ];
- src/services/ffmpeg.ts:102-208 (helper)Core FFmpeg conversion helper method invoked by the tool handler, handling actual video transcoding with progress callbacks and comprehensive format support.async convertVideo( inputPath: string, outputPath: string, options: ConversionOptions, onProgress?: (progress: ConversionProgress) => void ): Promise<string> { const taskId = this.generateTaskId(); // 检查输入文件是否存在 try { await fs.access(inputPath); } catch { throw new Error(`输入文件不存在: ${inputPath}`); } // 确保输出目录存在 const outputDir = path.dirname(outputPath); await fs.mkdir(outputDir, { recursive: true }); // 检查输出文件是否已存在 if (!options.overwrite) { try { await fs.access(outputPath); throw new Error(`输出文件已存在: ${outputPath}`); } catch (err: any) { if (err.code !== 'ENOENT') { throw err; } } } return new Promise((resolve, reject) => { const progress: ConversionProgress = { taskId, inputPath, outputPath, progress: 0, status: 'pending', startTime: new Date(), }; this.activeConversions.set(taskId, progress); let command = ffmpeg(inputPath); // 应用转换选项 command = this.applyConversionOptions(command, options); command .output(outputPath) .on('start', (commandLine) => { console.log('开始转换:', commandLine); progress.status = 'processing'; onProgress?.(progress); }) .on('progress', (progressInfo) => { progress.progress = Math.round(progressInfo.percent || 0); onProgress?.(progress); }) .on('end', async () => { try { // 验证输出文件是否完整 await this.validateOutputFile(outputPath); progress.status = 'completed'; progress.progress = 100; progress.endTime = new Date(); this.activeConversions.delete(taskId); onProgress?.(progress); resolve(outputPath); } catch (validationError: any) { progress.status = 'failed'; progress.error = `输出文件验证失败: ${validationError.message}`; progress.endTime = new Date(); this.activeConversions.delete(taskId); onProgress?.(progress); reject(new Error(`转换完成但文件验证失败: ${validationError.message}`)); } }) .on('error', (err) => { progress.status = 'failed'; progress.error = err.message; progress.endTime = new Date(); this.activeConversions.delete(taskId); onProgress?.(progress); // 提供更详细的错误信息 let errorMessage = `视频转换失败: ${err.message}`; // 针对常见错误提供解决建议 if (err.message.includes('moov atom not found')) { errorMessage += '\n建议: 输入文件可能损坏或格式不完整,请检查源文件'; } else if (err.message.includes('Invalid data found')) { errorMessage += '\n建议: 输入文件格式可能不受支持或文件已损坏'; } else if (err.message.includes('No such file or directory')) { errorMessage += '\n建议: 请检查输入文件路径是否正确'; } else if (err.message.includes('Permission denied')) { errorMessage += '\n建议: 请检查文件权限或确保输出目录可写'; } else if (err.message.includes('codec not found')) { errorMessage += '\n建议: 缺少必要的编解码器,请检查FFmpeg安装'; } reject(new Error(errorMessage)); }) .run(); }); }