resize-video
Resize videos to standard resolutions like 360p, 480p, 720p, or 1080p for compatibility across devices and platforms.
Instructions
Resize a video to one or more standard resolutions
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| videoPath | Yes | Path to the video file to resize | |
| resolutions | Yes | Resolutions to convert the video to | |
| outputDir | No | Optional directory to save the output files (defaults to a temporary directory) |
Implementation Reference
- src/mcp-ffmpeg.ts:113-237 (handler)The core handler function that implements the resize-video tool logic. It resolves paths, checks existence and permissions, requests user permission, runs FFmpeg scale commands for each specified resolution, and returns formatted results or errors.async ({ videoPath, resolutions, outputDir }) => { try { // Resolve the absolute path const absVideoPath = path.resolve(videoPath); // Check if file exists try { await fs.access(absVideoPath); } catch (error) { return { isError: true, content: [{ type: "text" as const, text: `Error: Video file not found at ${absVideoPath}` }] }; } // Determine output directory let outputDirectory = outputDir ? path.resolve(outputDir) : await ensureDirectoriesExist(); // Check if output directory exists and is writable try { await fs.access(outputDirectory, fs.constants.W_OK); } catch (error) { return { isError: true, content: [{ type: "text" as const, text: `Error: Output directory ${outputDirectory} does not exist or is not writable` }] }; } // Format command for permission request const resolutionsStr = resolutions.join(', '); const permissionMessage = `Resize video ${path.basename(absVideoPath)} to ${resolutionsStr}`; // Ask for permission const permitted = await askPermission(permissionMessage); if (!permitted) { return { isError: true, content: [{ type: "text" as const, text: "Permission denied by user" }] }; } // Get video filename without extension const videoFilename = path.basename(absVideoPath, path.extname(absVideoPath)); // Define the type for our results type ResizeResult = { resolution: "360p" | "480p" | "720p" | "1080p"; outputPath: string; success: boolean; error?: string; }; // Process each resolution const results: ResizeResult[] = []; for (const resolution of resolutions) { const { width, height } = RESOLUTIONS[resolution as keyof typeof RESOLUTIONS]; const outputFilename = `${videoFilename}_${resolution}${path.extname(absVideoPath)}`; const outputPath = path.join(outputDirectory, outputFilename); // Build FFmpeg command const command = `ffmpeg -i "${absVideoPath}" -vf "scale=${width}:${height}" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k "${outputPath}"`; try { // Execute FFmpeg command const { stdout, stderr } = await execAsync(command); results.push({ resolution, outputPath, success: true }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); results.push({ resolution, outputPath, success: false, error: errorMessage }); } } // Format results const successCount = results.filter(r => r.success).length; const failCount = results.length - successCount; let resultText = `Processed ${results.length} resolutions (${successCount} successful, ${failCount} failed)\n\n`; results.forEach(result => { if (result.success) { resultText += `✅ ${result.resolution}: ${result.outputPath}\n`; } else { resultText += `❌ ${result.resolution}: Failed - ${result.error}\n`; } }); return { content: [{ type: "text" as const, text: resultText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { isError: true, content: [{ type: "text" as const, text: `Error resizing video: ${errorMessage}` }] }; } }
- src/mcp-ffmpeg.ts:108-112 (schema)Zod input schema defining parameters for the resize-video tool: videoPath (required string), resolutions (array of specific enums), outputDir (optional string).{ videoPath: z.string().describe("Path to the video file to resize"), resolutions: z.array(z.enum(["360p", "480p", "720p", "1080p"])).describe("Resolutions to convert the video to"), outputDir: z.string().optional().describe("Optional directory to save the output files (defaults to a temporary directory)") },
- src/mcp-ffmpeg.ts:105-238 (registration)Registration of the resize-video tool on the MCP server using server.tool(), providing name, description, input schema, and handler function.server.tool( "resize-video", "Resize a video to one or more standard resolutions", { videoPath: z.string().describe("Path to the video file to resize"), resolutions: z.array(z.enum(["360p", "480p", "720p", "1080p"])).describe("Resolutions to convert the video to"), outputDir: z.string().optional().describe("Optional directory to save the output files (defaults to a temporary directory)") }, async ({ videoPath, resolutions, outputDir }) => { try { // Resolve the absolute path const absVideoPath = path.resolve(videoPath); // Check if file exists try { await fs.access(absVideoPath); } catch (error) { return { isError: true, content: [{ type: "text" as const, text: `Error: Video file not found at ${absVideoPath}` }] }; } // Determine output directory let outputDirectory = outputDir ? path.resolve(outputDir) : await ensureDirectoriesExist(); // Check if output directory exists and is writable try { await fs.access(outputDirectory, fs.constants.W_OK); } catch (error) { return { isError: true, content: [{ type: "text" as const, text: `Error: Output directory ${outputDirectory} does not exist or is not writable` }] }; } // Format command for permission request const resolutionsStr = resolutions.join(', '); const permissionMessage = `Resize video ${path.basename(absVideoPath)} to ${resolutionsStr}`; // Ask for permission const permitted = await askPermission(permissionMessage); if (!permitted) { return { isError: true, content: [{ type: "text" as const, text: "Permission denied by user" }] }; } // Get video filename without extension const videoFilename = path.basename(absVideoPath, path.extname(absVideoPath)); // Define the type for our results type ResizeResult = { resolution: "360p" | "480p" | "720p" | "1080p"; outputPath: string; success: boolean; error?: string; }; // Process each resolution const results: ResizeResult[] = []; for (const resolution of resolutions) { const { width, height } = RESOLUTIONS[resolution as keyof typeof RESOLUTIONS]; const outputFilename = `${videoFilename}_${resolution}${path.extname(absVideoPath)}`; const outputPath = path.join(outputDirectory, outputFilename); // Build FFmpeg command const command = `ffmpeg -i "${absVideoPath}" -vf "scale=${width}:${height}" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k "${outputPath}"`; try { // Execute FFmpeg command const { stdout, stderr } = await execAsync(command); results.push({ resolution, outputPath, success: true }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); results.push({ resolution, outputPath, success: false, error: errorMessage }); } } // Format results const successCount = results.filter(r => r.success).length; const failCount = results.length - successCount; let resultText = `Processed ${results.length} resolutions (${successCount} successful, ${failCount} failed)\n\n`; results.forEach(result => { if (result.success) { resultText += `✅ ${result.resolution}: ${result.outputPath}\n`; } else { resultText += `❌ ${result.resolution}: Failed - ${result.error}\n`; } }); return { content: [{ type: "text" as const, text: resultText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { isError: true, content: [{ type: "text" as const, text: `Error resizing video: ${errorMessage}` }] }; } } );
- src/mcp-ffmpeg.ts:20-25 (helper)Helper constant mapping resolution names to width/height dimensions, used exclusively by the resize-video tool handler.const RESOLUTIONS = { "360p": { width: 640, height: 360 }, "480p": { width: 854, height: 480 }, "720p": { width: 1280, height: 720 }, "1080p": { width: 1920, height: 1080 } };