Skip to main content
Glama
bitscorp-mcp

MCP FFmpeg Video Processor

by bitscorp-mcp

resize-video

Resize video files to standard resolutions (360p, 480p, 720p, 1080p) using MCP FFmpeg Video Processor. Specify input video path and desired output resolutions for efficient conversion.

Instructions

Resize a video to one or more standard resolutions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
outputDirNoOptional directory to save the output files (defaults to a temporary directory)
resolutionsYesResolutions to convert the video to
videoPathYesPath to the video file to resize

Implementation Reference

  • The main execution logic for the resize-video tool. Handles input validation, file existence checks, user permission via notification, output directory setup, FFmpeg command construction and execution for each specified resolution, error handling, and formatted result reporting.
    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}` }] }; } }
  • Zod input schema defining parameters: videoPath (required string), resolutions (required array of '360p'|'480p'|'720p'|'1080p'), 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)") },
  • Registers the resize-video tool on the MCP server with name, description, input schema, and handler function.
    "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}` }] }; } } );
  • RESOLUTIONS constant mapping resolution strings to {width, height} objects, used exclusively by the resize-video handler.
    const RESOLUTIONS = { "360p": { width: 640, height: 360 }, "480p": { width: 854, height: 480 }, "720p": { width: 1280, height: 720 }, "1080p": { width: 1920, height: 1080 }
  • askPermission helper function used by resize-video (and others) to request user approval via desktop notification before executing FFmpeg.
    async function askPermission(action: string): Promise<boolean> { // Skip notification if DISABLE_NOTIFICATIONS is set if (process.env.DISABLE_NOTIFICATIONS === 'true') { console.log(`Auto-allowing action (notifications disabled): ${action}`); return true; } return new Promise((resolve) => { notifier.notify({ title: 'FFmpeg Processor Permission Request', message: `${action}`, wait: true, timeout: 60, actions: 'Allow', closeLabel: 'Deny' }, (err, response, metadata) => { if (err) { console.error('Error showing notification:', err); resolve(false); return; } const buttonPressed = metadata?.activationValue || response; resolve(buttonPressed !== 'Deny'); }); });

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bitscorp-mcp/mcp-ffmpeg'

If you have feedback or need assistance with the MCP directory API, please join our Discord server