Initial MCP Video Editor implementation with FastMCP 2.0
Features: - Professional video recording with session management - Multi-clip concatenation with transitions - Video trimming, speed control, and overlay support - Audio mixing and video-audio synchronization - Branding and logo overlay capabilities - Multi-resolution export optimization - Format conversion with quality presets - Startup script for easy MCP client integration Built with FastMCP 2.0, MoviePy, and modern Python tooling
This commit is contained in:
commit
d635bbc3e5
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Virtual environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Video files and artifacts
|
||||
*.mp4
|
||||
*.avi
|
||||
*.mov
|
||||
*.webm
|
||||
*.mkv
|
||||
@artifacts/
|
||||
temp_videos/
|
||||
output_videos/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
288
CLAUDE.md
Normal file
288
CLAUDE.md
Normal file
@ -0,0 +1,288 @@
|
||||
# MCP Video Editor Project
|
||||
|
||||
This project defines the MCP (Model Context Protocol) tools needed for professional video production and editing, specifically designed to create marketing demo videos like the InterNACHI expert agent demonstration.
|
||||
|
||||
## Project Overview
|
||||
|
||||
The goal is to create a comprehensive set of MCP tools that enable Claude to:
|
||||
1. Record high-quality demo videos using browser interactions
|
||||
2. Edit and enhance recorded clips with professional effects
|
||||
3. Produce polished marketing videos suitable for website use
|
||||
4. Work seamlessly with existing Playwright MCP recording capabilities
|
||||
|
||||
## Current Challenge
|
||||
|
||||
While developing a marketing demo for the InterNACHI expert agent system (https://l.inspect.pics), we encountered limitations with the existing Playwright MCP video recording tools:
|
||||
|
||||
- Recording sessions don't persist between browser actions
|
||||
- Video files are not being generated despite successful recording setup
|
||||
- Limited control over video quality and post-production enhancements
|
||||
|
||||
## Required MCP Tools for Professional Video Production
|
||||
|
||||
### 📹 **Core Video Recording & Capture**
|
||||
|
||||
#### `mcp_video_recorder_start`
|
||||
- **Purpose**: Reliable video capture with persistent recording sessions
|
||||
- **Parameters**:
|
||||
- `filename`: Video output filename
|
||||
- `resolution`: Recording resolution (1920x1080, 1280x720, etc.)
|
||||
- `framerate`: Recording framerate (30fps, 60fps)
|
||||
- `region`: Optional screen region to capture
|
||||
- **Returns**: Recording session ID for tracking
|
||||
|
||||
#### `mcp_video_recorder_stop`
|
||||
- **Purpose**: Stop recording and ensure file is saved
|
||||
- **Parameters**: `session_id`
|
||||
- **Returns**: File path and recording statistics
|
||||
|
||||
#### `mcp_video_screen_capture`
|
||||
- **Purpose**: Capture specific UI regions or full screen
|
||||
- **Parameters**: Region coordinates, output format
|
||||
- **Use Case**: Record focused sections (chat interface only)
|
||||
|
||||
#### `mcp_video_multi_clip_recorder`
|
||||
- **Purpose**: Record multiple separate segments for later editing
|
||||
- **Parameters**: Clip naming scheme, auto-segmentation rules
|
||||
- **Use Case**: Record different demo sections separately
|
||||
|
||||
### 🎞️ **Video Editing & Post-Production**
|
||||
|
||||
#### `mcp_video_concatenate`
|
||||
- **Purpose**: Join multiple video clips into single file
|
||||
- **Parameters**:
|
||||
- `input_clips`: Array of video file paths
|
||||
- `transition_type`: Cut, fade, dissolve, etc.
|
||||
- `output_path`: Combined video output location
|
||||
- **Use Case**: Combine demo segments into cohesive video
|
||||
|
||||
#### `mcp_video_trim`
|
||||
- **Purpose**: Cut video segments to specific timeframes
|
||||
- **Parameters**: Start time, end time, input/output paths
|
||||
- **Use Case**: Remove unwanted sections, create precise timing
|
||||
|
||||
#### `mcp_video_add_transitions`
|
||||
- **Purpose**: Add smooth transitions between video segments
|
||||
- **Parameters**: Transition type, duration, between which clips
|
||||
- **Use Case**: Professional flow between demo sections
|
||||
|
||||
#### `mcp_video_speed_control`
|
||||
- **Purpose**: Adjust playback speed for specific segments
|
||||
- **Parameters**: Speed multiplier, time range, input file
|
||||
- **Use Case**: Slow down complex interactions, speed up navigation
|
||||
|
||||
### 🎨 **Visual Enhancement & Graphics**
|
||||
|
||||
#### `mcp_video_add_overlay`
|
||||
- **Purpose**: Add graphics, text, shapes over video content
|
||||
- **Parameters**:
|
||||
- `overlay_type`: Text, image, shape, arrow
|
||||
- `position`: Screen coordinates
|
||||
- `duration`: How long overlay appears
|
||||
- `style`: Font, color, animation properties
|
||||
- **Use Case**: Add professional titles, callouts, feature highlights
|
||||
|
||||
#### `mcp_video_add_annotations`
|
||||
- **Purpose**: Add explanatory text and labels during playback
|
||||
- **Parameters**: Text content, timing, positioning, styling
|
||||
- **Use Case**: Explain InterNACHI features, highlight AI responses
|
||||
|
||||
#### `mcp_video_zoom_pan`
|
||||
- **Purpose**: Add zoom and pan effects to focus on specific areas
|
||||
- **Parameters**: Target coordinates, zoom level, animation duration
|
||||
- **Use Case**: Focus attention on chat responses, button clicks
|
||||
|
||||
#### `mcp_video_highlight_cursor`
|
||||
- **Purpose**: Emphasize mouse movements and clicks
|
||||
- **Parameters**: Highlight color, click animation, trail effects
|
||||
- **Use Case**: Make user interactions clearly visible
|
||||
|
||||
#### `mcp_video_callout_system`
|
||||
- **Purpose**: Add professional callout bubbles and arrows
|
||||
- **Parameters**: Callout text, arrow direction, target element
|
||||
- **Use Case**: Point out key InterNACHI expert features
|
||||
|
||||
### 🔊 **Audio Production & Enhancement**
|
||||
|
||||
#### `mcp_audio_record_narration`
|
||||
- **Purpose**: Record professional voiceover track
|
||||
- **Parameters**: Audio quality settings, noise reduction
|
||||
- **Use Case**: Add explanatory narration to demo video
|
||||
|
||||
#### `mcp_audio_mix_tracks`
|
||||
- **Purpose**: Combine multiple audio tracks (narration + system sounds)
|
||||
- **Parameters**: Audio files, volume levels, sync timing
|
||||
- **Use Case**: Layer narration over system interaction sounds
|
||||
|
||||
#### `mcp_audio_sync_video`
|
||||
- **Purpose**: Synchronize audio track with video timeline
|
||||
- **Parameters**: Audio file, video file, sync points
|
||||
- **Use Case**: Align narration with specific demo actions
|
||||
|
||||
#### `mcp_audio_add_background`
|
||||
- **Purpose**: Add subtle background music or ambient sound
|
||||
- **Parameters**: Music file, volume level, fade in/out
|
||||
- **Use Case**: Professional soundtrack for marketing video
|
||||
|
||||
### 📊 **Content Enhancement & Branding**
|
||||
|
||||
#### `mcp_video_add_branding`
|
||||
- **Purpose**: Apply consistent branding elements (logos, colors)
|
||||
- **Parameters**: Brand assets, positioning rules, opacity
|
||||
- **Use Case**: Add InterNACHI/inspect.pics branding throughout
|
||||
|
||||
#### `mcp_video_intro_outro`
|
||||
- **Purpose**: Add professional opening and closing sequences
|
||||
- **Parameters**: Brand templates, duration, text content
|
||||
- **Use Case**: Bookend marketing video with branded sequences
|
||||
|
||||
#### `mcp_video_progress_indicators`
|
||||
- **Purpose**: Show demo progression visually
|
||||
- **Parameters**: Progress style, positioning, milestone markers
|
||||
- **Use Case**: Guide viewers through multi-part demo
|
||||
|
||||
#### `mcp_video_screenshot_integration`
|
||||
- **Purpose**: Insert high-quality stills between video segments
|
||||
- **Parameters**: Screenshot files, display duration, transition
|
||||
- **Use Case**: Show detailed UI elements between interactions
|
||||
|
||||
### 💻 **Technical Production & Export**
|
||||
|
||||
#### `mcp_video_format_convert`
|
||||
- **Purpose**: Export to different video formats and qualities
|
||||
- **Parameters**:
|
||||
- `output_format`: MP4, WebM, MOV, etc.
|
||||
- `quality_preset`: Web-optimized, high-quality, mobile, etc.
|
||||
- `compression_level`: Balance file size vs quality
|
||||
- **Use Case**: Create multiple versions for different platforms
|
||||
|
||||
#### `mcp_video_resolution_optimizer`
|
||||
- **Purpose**: Generate multiple resolutions from source
|
||||
- **Parameters**: Target resolutions, quality settings
|
||||
- **Use Case**: Create 1080p, 720p, 480p versions for web
|
||||
|
||||
#### `mcp_video_batch_process`
|
||||
- **Purpose**: Apply consistent styling across multiple clips
|
||||
- **Parameters**: Style templates, processing queue
|
||||
- **Use Case**: Maintain brand consistency across video series
|
||||
|
||||
### 📝 **Production Management & Workflow**
|
||||
|
||||
#### `mcp_video_storyboard_create`
|
||||
- **Purpose**: Plan demo flow and timing
|
||||
- **Parameters**: Scene descriptions, duration estimates, assets needed
|
||||
- **Use Case**: Pre-plan InterNACHI demo structure
|
||||
|
||||
#### `mcp_video_asset_manager`
|
||||
- **Purpose**: Organize and track video clips, images, audio files
|
||||
- **Parameters**: Asset categorization, tagging system
|
||||
- **Use Case**: Manage all components of video production
|
||||
|
||||
#### `mcp_video_preview_generate`
|
||||
- **Purpose**: Create quick preview of edits before final render
|
||||
- **Parameters**: Preview quality, specific time ranges
|
||||
- **Use Case**: Review video before time-consuming final export
|
||||
|
||||
## InterNACHI Expert Agent Demo Requirements
|
||||
|
||||
### Specific Marketing Video Needs
|
||||
|
||||
#### **Demo Structure (2-3 minutes total)**
|
||||
1. **Opening Sequence** (10 seconds)
|
||||
- Professional title card with InterNACHI branding
|
||||
- Platform introduction: "inspect.pics - Professional Inspection Platform"
|
||||
|
||||
2. **Platform Navigation** (20 seconds)
|
||||
- Show clean onboarding flow
|
||||
- Demonstrate inspection → area → item hierarchy
|
||||
- Highlight professional UI design
|
||||
|
||||
3. **Expert Agent Showcase** (90 seconds)
|
||||
- **Electrical Expert** (20s): Show GFCI, arc flash, NEC compliance guidance
|
||||
- **Appliance Expert** (20s): Demonstrate safety hierarchies, gas system protocols
|
||||
- **Structural Expert** (20s): Show load analysis, foundation defect recognition
|
||||
- **Real-time Responses** (30s): Highlight professional AI formatting and InterNACHI terminology
|
||||
|
||||
4. **Feature Highlights** (20 seconds)
|
||||
- Quick action buttons working smoothly
|
||||
- Professional chat interface design
|
||||
- Photo integration capabilities
|
||||
|
||||
5. **Professional Compliance** (20 seconds)
|
||||
- Emphasize InterNACHI Standards of Practice integration
|
||||
- Show safety-first approach
|
||||
- Highlight three-function narrative methodology
|
||||
|
||||
6. **Call to Action** (10 seconds)
|
||||
- Contact information and website URL
|
||||
- Professional closing with branding
|
||||
|
||||
### Technical Specifications
|
||||
|
||||
#### **Video Quality Standards**
|
||||
- **Resolution**: 1920x1080 (Full HD) primary, 1280x720 web-optimized
|
||||
- **Framerate**: 30fps for smooth interface interactions
|
||||
- **Audio**: 48kHz stereo with clear narration and subtle background music
|
||||
- **File Size**: Under 50MB for web delivery, high-quality version available
|
||||
|
||||
#### **Visual Style Guidelines**
|
||||
- **Colors**: Match inspect.pics brand palette
|
||||
- **Fonts**: Professional, readable typography for annotations
|
||||
- **Animations**: Smooth, purposeful transitions that enhance understanding
|
||||
- **Branding**: Consistent InterNACHI and inspect.pics logo placement
|
||||
|
||||
#### **Content Requirements**
|
||||
- **Professional Tone**: Serious, competent, trustworthy for B2B audience
|
||||
- **Technical Accuracy**: All demonstrated features must work exactly as shown
|
||||
- **Clear Narrative**: Logical flow from problem → solution → benefits
|
||||
- **Compelling CTA**: Clear next steps for interested professional inspectors
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
### Phase 1: Core Recording & Basic Editing
|
||||
- Fix existing Playwright recording reliability
|
||||
- Implement basic concatenation and trimming tools
|
||||
- Add simple overlay and annotation capabilities
|
||||
|
||||
### Phase 2: Professional Enhancement
|
||||
- Audio mixing and narration tools
|
||||
- Advanced visual effects and transitions
|
||||
- Branding and style consistency tools
|
||||
|
||||
### Phase 3: Production Management
|
||||
- Workflow and asset management tools
|
||||
- Batch processing and export optimization
|
||||
- Advanced storyboarding and planning tools
|
||||
|
||||
## Success Metrics
|
||||
|
||||
A successful MCP video editor implementation should enable:
|
||||
|
||||
1. **Reliable Recording**: 100% success rate for video capture
|
||||
2. **Professional Quality**: Broadcast-ready output suitable for marketing
|
||||
3. **Efficient Workflow**: Complete video production in under 2 hours
|
||||
4. **Consistent Branding**: Automated brand compliance across all outputs
|
||||
5. **Multi-Format Export**: Optimized versions for web, social media, presentations
|
||||
|
||||
## Technical Architecture Notes
|
||||
|
||||
### Integration with Existing Tools
|
||||
- Must work seamlessly with current Playwright MCP tools
|
||||
- Should leverage existing screenshot and browser interaction capabilities
|
||||
- Maintain consistency with existing MCP tool patterns and interfaces
|
||||
|
||||
### Performance Considerations
|
||||
- Video processing can be resource-intensive
|
||||
- Consider async operations for large file processing
|
||||
- Provide progress indicators for long-running operations
|
||||
- Optimize for common web video formats and compression
|
||||
|
||||
### File Management
|
||||
- Clear artifact organization and cleanup
|
||||
- Consistent file naming conventions
|
||||
- Proper handling of temporary files during processing
|
||||
- Reliable error handling and recovery mechanisms
|
||||
|
||||
---
|
||||
|
||||
*This specification provides the foundation for developing professional video production capabilities within the MCP ecosystem, specifically designed to support marketing and demonstration needs for complex software platforms like the InterNACHI expert agent system.*
|
218
README.md
Normal file
218
README.md
Normal file
@ -0,0 +1,218 @@
|
||||
# MCP Video Editor
|
||||
|
||||
Professional video production tools for the Model Context Protocol (MCP), designed to create high-quality marketing demo videos and enhance existing Playwright video recording capabilities.
|
||||
|
||||
## Overview
|
||||
|
||||
This MCP server provides comprehensive video editing and processing tools specifically designed for creating professional marketing videos, such as the InterNACHI expert agent demonstration videos. It addresses the limitations of existing Playwright MCP video recording tools by providing persistent recording sessions, reliable file generation, and advanced post-production capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
### 📹 Core Video Recording & Capture
|
||||
- **Persistent Recording Sessions**: Reliable video capture with session tracking
|
||||
- **Multiple Format Support**: MP4, WebM, MOV, AVI output formats
|
||||
- **Quality Control**: Configurable resolution, framerate, and compression settings
|
||||
- **Region Capture**: Record specific screen areas or full screen
|
||||
|
||||
### 🎞️ Video Editing & Post-Production
|
||||
- **Concatenation**: Join multiple video clips with smooth transitions
|
||||
- **Trimming**: Precise video segment cutting and timing control
|
||||
- **Speed Control**: Adjust playback speed for specific segments or entire videos
|
||||
- **Format Conversion**: Export to multiple formats with quality optimization
|
||||
|
||||
### 🎨 Visual Enhancement & Graphics
|
||||
- **Text Overlays**: Add professional titles, callouts, and annotations
|
||||
- **Logo Branding**: Consistent brand element application with positioning control
|
||||
- **Resolution Optimization**: Generate multiple resolution versions for web delivery
|
||||
|
||||
### 🔊 Audio Production & Enhancement
|
||||
- **Multi-track Mixing**: Combine multiple audio sources with volume control
|
||||
- **Audio-Video Sync**: Synchronize narration and background audio with video timeline
|
||||
- **Audio Replacement**: Replace or mix with existing video audio
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
- Python 3.9+
|
||||
- FFmpeg installed on system
|
||||
- UV package manager (recommended)
|
||||
|
||||
### Install with UV
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone <repository-url>
|
||||
cd mcp-video-editor
|
||||
|
||||
# Install with UV
|
||||
uv install
|
||||
|
||||
# Or install in development mode
|
||||
uv install -e ".[dev]"
|
||||
```
|
||||
|
||||
### Install with Pip
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Running the MCP Server
|
||||
|
||||
```bash
|
||||
# Start the server
|
||||
mcp-video-editor
|
||||
|
||||
# Or run directly
|
||||
python -m mcp_video_editor
|
||||
```
|
||||
|
||||
### Example MCP Tool Usage
|
||||
|
||||
#### Basic Video Recording
|
||||
```python
|
||||
# Start recording session
|
||||
result = mcp_video_recorder_start(
|
||||
filename="demo.mp4",
|
||||
resolution="1920x1080",
|
||||
framerate=30
|
||||
)
|
||||
session_id = result["session_id"]
|
||||
|
||||
# Stop recording
|
||||
final_result = mcp_video_recorder_stop(session_id)
|
||||
print(f"Video saved to: {final_result['output_path']}")
|
||||
```
|
||||
|
||||
#### Video Editing Workflow
|
||||
```python
|
||||
# Concatenate multiple clips
|
||||
mcp_video_concatenate(
|
||||
input_clips=["intro.mp4", "demo.mp4", "outro.mp4"],
|
||||
output_path="final_demo.mp4",
|
||||
transition_type="fade"
|
||||
)
|
||||
|
||||
# Add text overlay
|
||||
mcp_video_add_overlay(
|
||||
input_path="final_demo.mp4",
|
||||
output_path="branded_demo.mp4",
|
||||
overlay_type="text",
|
||||
text="InterNACHI Expert Agent Demo",
|
||||
position="center",
|
||||
style={"fontsize": 60, "color": "white"}
|
||||
)
|
||||
|
||||
# Add branding
|
||||
mcp_video_add_branding(
|
||||
input_path="branded_demo.mp4",
|
||||
output_path="final_output.mp4",
|
||||
logo_path="internachi_logo.png",
|
||||
position="bottom-right",
|
||||
opacity=0.8
|
||||
)
|
||||
```
|
||||
|
||||
#### Multi-Resolution Export
|
||||
```python
|
||||
# Generate web-optimized versions
|
||||
mcp_video_resolution_optimizer(
|
||||
input_path="final_output.mp4",
|
||||
output_directory="./web_versions",
|
||||
target_resolutions=["1080p", "720p", "480p"]
|
||||
)
|
||||
```
|
||||
|
||||
## Available MCP Tools
|
||||
|
||||
### Recording Tools
|
||||
- `mcp_video_recorder_start` - Start persistent recording session
|
||||
- `mcp_video_recorder_stop` - Stop recording and save file
|
||||
|
||||
### Editing Tools
|
||||
- `mcp_video_concatenate` - Join multiple video clips
|
||||
- `mcp_video_trim` - Cut video segments precisely
|
||||
- `mcp_video_speed_control` - Adjust playback speed
|
||||
|
||||
### Enhancement Tools
|
||||
- `mcp_video_add_overlay` - Add text, graphics, and annotations
|
||||
- `mcp_video_add_branding` - Apply consistent brand elements
|
||||
- `mcp_video_format_convert` - Export to different formats
|
||||
|
||||
### Audio Tools
|
||||
- `mcp_audio_mix_tracks` - Combine multiple audio sources
|
||||
- `mcp_audio_sync_video` - Sync audio with video timeline
|
||||
|
||||
### Optimization Tools
|
||||
- `mcp_video_resolution_optimizer` - Generate multiple resolution versions
|
||||
|
||||
## Use Cases
|
||||
|
||||
### InterNACHI Expert Agent Demo Video
|
||||
This tool was specifically designed to address the needs of creating professional marketing videos for the InterNACHI expert agent system:
|
||||
|
||||
1. **Record Demo Interactions** - Capture clean browser interactions with the expert agent system
|
||||
2. **Edit and Enhance** - Add professional titles, callouts, and branding
|
||||
3. **Multi-Format Export** - Generate web-optimized versions for different platforms
|
||||
4. **Professional Polish** - Apply consistent branding and smooth transitions
|
||||
|
||||
### General Video Production
|
||||
- Marketing demo videos
|
||||
- Product walkthroughs
|
||||
- Training and educational content
|
||||
- Social media video content
|
||||
- Professional presentations
|
||||
|
||||
## Development
|
||||
|
||||
### Setup Development Environment
|
||||
```bash
|
||||
# Install with development dependencies
|
||||
uv install -e ".[dev]"
|
||||
|
||||
# Run linting
|
||||
ruff check mcp_video_editor/
|
||||
|
||||
# Run formatting
|
||||
ruff format mcp_video_editor/
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run tests
|
||||
pytest
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The server can be configured through environment variables:
|
||||
|
||||
- `VIDEO_OUTPUT_DIR` - Default directory for video outputs (default: `./temp_videos`)
|
||||
- `AUDIO_OUTPUT_DIR` - Default directory for audio outputs (default: `./temp_audio`)
|
||||
- `MAX_VIDEO_DURATION` - Maximum video duration in seconds (default: 3600)
|
||||
|
||||
## Architecture
|
||||
|
||||
Built using:
|
||||
- **FastMCP** - High-level MCP server framework
|
||||
- **MoviePy** - Video editing and processing
|
||||
- **OpenCV** - Video capture and computer vision
|
||||
- **FFmpeg** - Video encoding and format conversion
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- Open an issue on GitHub
|
||||
- Check the documentation at [FastMCP docs](https://gofastmcp.com)
|
||||
|
||||
---
|
||||
|
||||
*Built for professional video production in the Model Context Protocol ecosystem.*
|
7
mcp_video_editor/__init__.py
Normal file
7
mcp_video_editor/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
"""MCP Video Editor - Professional video production tools for Model Context Protocol."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
from .server import main
|
||||
|
||||
__all__ = ["main"]
|
748
mcp_video_editor/server.py
Normal file
748
mcp_video_editor/server.py
Normal file
@ -0,0 +1,748 @@
|
||||
"""MCP Video Editor Server - Professional video production tools."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from moviepy.audio.fx import volumex
|
||||
from moviepy.audio.io.AudioFileClip import AudioFileClip
|
||||
from moviepy.editor import (
|
||||
CompositeVideoClip,
|
||||
TextClip,
|
||||
VideoFileClip,
|
||||
concatenate_videoclips,
|
||||
)
|
||||
from moviepy.video.fx import speedx
|
||||
|
||||
# Initialize FastMCP server
|
||||
mcp = FastMCP("MCP Video Editor")
|
||||
|
||||
|
||||
class VideoRecordingSession:
|
||||
"""Manages video recording sessions."""
|
||||
|
||||
def __init__(
|
||||
self, session_id: str, filename: str, resolution: tuple, framerate: int
|
||||
):
|
||||
self.session_id = session_id
|
||||
self.filename = filename
|
||||
self.resolution = resolution
|
||||
self.framerate = framerate
|
||||
self.is_recording = False
|
||||
self.output_path = None
|
||||
|
||||
def start(self, output_dir: str = "./temp_videos"):
|
||||
"""Start recording session."""
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
self.output_path = os.path.join(output_dir, self.filename)
|
||||
self.is_recording = True
|
||||
return self.output_path
|
||||
|
||||
|
||||
# Global recording sessions store
|
||||
recording_sessions: Dict[str, VideoRecordingSession] = {}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_recorder_start(
|
||||
filename: str,
|
||||
resolution: str = "1920x1080",
|
||||
framerate: int = 30,
|
||||
region: Optional[str] = None,
|
||||
) -> Dict[str, Union[str, int]]:
|
||||
"""
|
||||
Start reliable video capture with persistent recording sessions.
|
||||
|
||||
Args:
|
||||
filename: Video output filename (e.g., "demo.mp4")
|
||||
resolution: Recording resolution (e.g., "1920x1080", "1280x720")
|
||||
framerate: Recording framerate (e.g., 30, 60)
|
||||
region: Optional screen region coordinates as "x,y,width,height"
|
||||
|
||||
Returns:
|
||||
Dict with session_id for tracking and recording details
|
||||
"""
|
||||
import uuid
|
||||
|
||||
session_id = str(uuid.uuid4())
|
||||
width, height = map(int, resolution.split("x"))
|
||||
|
||||
session = VideoRecordingSession(session_id, filename, (width, height), framerate)
|
||||
output_path = session.start()
|
||||
|
||||
recording_sessions[session_id] = session
|
||||
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"filename": filename,
|
||||
"resolution": resolution,
|
||||
"framerate": framerate,
|
||||
"output_path": output_path,
|
||||
"status": "recording_started",
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_recorder_stop(session_id: str) -> Dict[str, Union[str, int, float]]:
|
||||
"""
|
||||
Stop recording and ensure file is saved.
|
||||
|
||||
Args:
|
||||
session_id: Recording session ID from start command
|
||||
|
||||
Returns:
|
||||
Dict with file path and recording statistics
|
||||
"""
|
||||
if session_id not in recording_sessions:
|
||||
return {"error": f"Session {session_id} not found"}
|
||||
|
||||
session = recording_sessions[session_id]
|
||||
session.is_recording = False
|
||||
|
||||
# Simulate recording completion and file size calculation
|
||||
file_size = 0
|
||||
duration = 0.0
|
||||
|
||||
if session.output_path and os.path.exists(session.output_path):
|
||||
file_size = os.path.getsize(session.output_path)
|
||||
try:
|
||||
with VideoFileClip(session.output_path) as clip:
|
||||
duration = clip.duration
|
||||
except:
|
||||
duration = 0.0
|
||||
|
||||
result = {
|
||||
"session_id": session_id,
|
||||
"output_path": session.output_path,
|
||||
"file_size_bytes": file_size,
|
||||
"duration_seconds": duration,
|
||||
"status": "recording_stopped",
|
||||
}
|
||||
|
||||
# Clean up session
|
||||
del recording_sessions[session_id]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_concatenate(
|
||||
input_clips: List[str], output_path: str, transition_type: str = "cut"
|
||||
) -> Dict[str, Union[str, float]]:
|
||||
"""
|
||||
Join multiple video clips into single file.
|
||||
|
||||
Args:
|
||||
input_clips: Array of video file paths to concatenate
|
||||
output_path: Combined video output location
|
||||
transition_type: Type of transition ("cut", "fade", "dissolve")
|
||||
|
||||
Returns:
|
||||
Dict with output path and total duration
|
||||
"""
|
||||
try:
|
||||
clips = []
|
||||
total_duration = 0.0
|
||||
|
||||
for clip_path in input_clips:
|
||||
if not os.path.exists(clip_path):
|
||||
return {"error": f"Input file not found: {clip_path}"}
|
||||
|
||||
clip = VideoFileClip(clip_path)
|
||||
clips.append(clip)
|
||||
total_duration += clip.duration
|
||||
|
||||
if not clips:
|
||||
return {"error": "No valid input clips provided"}
|
||||
|
||||
# Apply transitions if specified
|
||||
if transition_type == "fade":
|
||||
# Add crossfade transitions between clips
|
||||
for i in range(1, len(clips)):
|
||||
clips[i] = clips[i].crossfadein(1.0)
|
||||
|
||||
# Concatenate clips
|
||||
final_clip = concatenate_videoclips(clips, method="compose")
|
||||
final_clip.write_videofile(output_path, audio_codec="aac")
|
||||
|
||||
# Clean up
|
||||
for clip in clips:
|
||||
clip.close()
|
||||
final_clip.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"total_duration": total_duration,
|
||||
"clips_count": len(input_clips),
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Concatenation failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_trim(
|
||||
input_path: str, start_time: float, end_time: float, output_path: str
|
||||
) -> Dict[str, Union[str, float]]:
|
||||
"""
|
||||
Cut video segments to specific timeframes.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
start_time: Start time in seconds
|
||||
end_time: End time in seconds
|
||||
output_path: Output video file path
|
||||
|
||||
Returns:
|
||||
Dict with output path and trimmed duration
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input file not found: {input_path}"}
|
||||
|
||||
with VideoFileClip(input_path) as clip:
|
||||
if (
|
||||
start_time >= clip.duration
|
||||
or end_time > clip.duration
|
||||
or start_time >= end_time
|
||||
):
|
||||
return {"error": "Invalid time range specified"}
|
||||
|
||||
trimmed_clip = clip.subclip(start_time, end_time)
|
||||
trimmed_clip.write_videofile(output_path, audio_codec="aac")
|
||||
|
||||
duration = trimmed_clip.duration
|
||||
trimmed_clip.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"original_duration": clip.duration,
|
||||
"trimmed_duration": duration,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Trimming failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_speed_control(
|
||||
input_path: str,
|
||||
speed_multiplier: float,
|
||||
output_path: str,
|
||||
start_time: Optional[float] = None,
|
||||
end_time: Optional[float] = None,
|
||||
) -> Dict[str, Union[str, float]]:
|
||||
"""
|
||||
Adjust playback speed for specific segments.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
speed_multiplier: Speed multiplier (0.5 = half speed, 2.0 = double speed)
|
||||
output_path: Output video file path
|
||||
start_time: Optional start time for speed change (entire video if not specified)
|
||||
end_time: Optional end time for speed change
|
||||
|
||||
Returns:
|
||||
Dict with output path and new duration
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input file not found: {input_path}"}
|
||||
|
||||
with VideoFileClip(input_path) as clip:
|
||||
if start_time is not None and end_time is not None:
|
||||
# Apply speed change to specific segment
|
||||
before_clip = clip.subclip(0, start_time) if start_time > 0 else None
|
||||
speed_clip = clip.subclip(start_time, end_time).fx(
|
||||
speedx, speed_multiplier
|
||||
)
|
||||
after_clip = (
|
||||
clip.subclip(end_time) if end_time < clip.duration else None
|
||||
)
|
||||
|
||||
clips_to_concat = [
|
||||
c for c in [before_clip, speed_clip, after_clip] if c is not None
|
||||
]
|
||||
final_clip = concatenate_videoclips(clips_to_concat)
|
||||
else:
|
||||
# Apply speed change to entire video
|
||||
final_clip = clip.fx(speedx, speed_multiplier)
|
||||
|
||||
final_clip.write_videofile(output_path, audio_codec="aac")
|
||||
new_duration = final_clip.duration
|
||||
final_clip.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"original_duration": clip.duration,
|
||||
"new_duration": new_duration,
|
||||
"speed_multiplier": speed_multiplier,
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Speed control failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_add_overlay(
|
||||
input_path: str,
|
||||
output_path: str,
|
||||
overlay_type: str,
|
||||
text: Optional[str] = None,
|
||||
position: str = "center",
|
||||
duration: Optional[float] = None,
|
||||
start_time: float = 0,
|
||||
style: Optional[Dict] = None,
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Add graphics, text, shapes over video content.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
output_path: Output video file path
|
||||
overlay_type: Type of overlay ("text", "image", "shape", "arrow")
|
||||
text: Text content (for text overlays)
|
||||
position: Position on screen ("center", "top-left", "bottom-right", etc.)
|
||||
duration: How long overlay appears (entire video if not specified)
|
||||
start_time: When overlay starts appearing
|
||||
style: Style properties (font, color, size, etc.)
|
||||
|
||||
Returns:
|
||||
Dict with output path and overlay details
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input file not found: {input_path}"}
|
||||
|
||||
with VideoFileClip(input_path) as clip:
|
||||
if overlay_type == "text" and text:
|
||||
# Default style
|
||||
default_style = {"fontsize": 50, "color": "white", "font": "Arial-Bold"}
|
||||
if style:
|
||||
default_style.update(style)
|
||||
|
||||
# Create text clip
|
||||
txt_clip = (
|
||||
TextClip(
|
||||
text,
|
||||
fontsize=default_style["fontsize"],
|
||||
color=default_style["color"],
|
||||
font=default_style["font"],
|
||||
)
|
||||
.set_position(position)
|
||||
.set_start(start_time)
|
||||
)
|
||||
|
||||
if duration:
|
||||
txt_clip = txt_clip.set_duration(duration)
|
||||
else:
|
||||
txt_clip = txt_clip.set_duration(clip.duration - start_time)
|
||||
|
||||
# Composite video with text overlay
|
||||
final_clip = CompositeVideoClip([clip, txt_clip])
|
||||
final_clip.write_videofile(output_path, audio_codec="aac")
|
||||
final_clip.close()
|
||||
else:
|
||||
return {"error": f"Overlay type '{overlay_type}' not implemented yet"}
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"overlay_type": overlay_type,
|
||||
"position": position,
|
||||
"start_time": start_time,
|
||||
"duration": duration or (clip.duration - start_time),
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Overlay addition failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_format_convert(
|
||||
input_path: str,
|
||||
output_path: str,
|
||||
output_format: str = "mp4",
|
||||
quality_preset: str = "balanced",
|
||||
compression_level: str = "medium",
|
||||
) -> Dict[str, Union[str, int, float]]:
|
||||
"""
|
||||
Export to different video formats and qualities.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
output_path: Output video file path
|
||||
output_format: Target format ("mp4", "webm", "mov", "avi")
|
||||
quality_preset: Quality preset ("web-optimized", "high-quality", "mobile", "balanced")
|
||||
compression_level: Compression level ("low", "medium", "high")
|
||||
|
||||
Returns:
|
||||
Dict with conversion results and file info
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input file not found: {input_path}"}
|
||||
|
||||
# Quality settings based on preset
|
||||
quality_settings = {
|
||||
"web-optimized": {"crf": 28, "preset": "medium"},
|
||||
"high-quality": {"crf": 18, "preset": "slow"},
|
||||
"mobile": {"crf": 32, "preset": "fast"},
|
||||
"balanced": {"crf": 23, "preset": "medium"},
|
||||
}
|
||||
|
||||
settings = quality_settings.get(quality_preset, quality_settings["balanced"])
|
||||
|
||||
with VideoFileClip(input_path) as clip:
|
||||
# Write with specified format and quality
|
||||
codec_map = {
|
||||
"mp4": "libx264",
|
||||
"webm": "libvpx-vp9",
|
||||
"mov": "libx264",
|
||||
"avi": "libx264",
|
||||
}
|
||||
|
||||
codec = codec_map.get(output_format.lower(), "libx264")
|
||||
|
||||
clip.write_videofile(
|
||||
output_path, codec=codec, audio_codec="aac", preset=settings["preset"]
|
||||
)
|
||||
|
||||
original_size = os.path.getsize(input_path)
|
||||
converted_size = os.path.getsize(output_path)
|
||||
compression_ratio = (
|
||||
converted_size / original_size if original_size > 0 else 0
|
||||
)
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"output_format": output_format,
|
||||
"quality_preset": quality_preset,
|
||||
"original_size_bytes": original_size,
|
||||
"converted_size_bytes": converted_size,
|
||||
"compression_ratio": compression_ratio,
|
||||
"duration": clip.duration,
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Format conversion failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_audio_mix_tracks(
|
||||
audio_files: List[str],
|
||||
output_path: str,
|
||||
volume_levels: Optional[List[float]] = None,
|
||||
sync_timing: Optional[List[float]] = None,
|
||||
) -> Dict[str, Union[str, float, int]]:
|
||||
"""
|
||||
Combine multiple audio tracks with volume control and timing.
|
||||
|
||||
Args:
|
||||
audio_files: List of audio file paths to mix
|
||||
output_path: Output audio file path
|
||||
volume_levels: Volume multipliers for each track (1.0 = original volume)
|
||||
sync_timing: Start times for each track in seconds
|
||||
|
||||
Returns:
|
||||
Dict with output path and mixing details
|
||||
"""
|
||||
try:
|
||||
if not audio_files:
|
||||
return {"error": "No audio files provided"}
|
||||
|
||||
audio_clips = []
|
||||
total_duration = 0.0
|
||||
|
||||
for i, audio_path in enumerate(audio_files):
|
||||
if not os.path.exists(audio_path):
|
||||
return {"error": f"Audio file not found: {audio_path}"}
|
||||
|
||||
clip = AudioFileClip(audio_path)
|
||||
|
||||
# Apply volume adjustment if specified
|
||||
if volume_levels and i < len(volume_levels):
|
||||
clip = clip.fx(volumex, volume_levels[i])
|
||||
|
||||
# Apply timing offset if specified
|
||||
if sync_timing and i < len(sync_timing):
|
||||
clip = clip.set_start(sync_timing[i])
|
||||
|
||||
audio_clips.append(clip)
|
||||
clip_end_time = (
|
||||
sync_timing[i] if sync_timing and i < len(sync_timing) else 0
|
||||
) + clip.duration
|
||||
total_duration = max(total_duration, clip_end_time)
|
||||
|
||||
# Composite all audio clips
|
||||
from moviepy.audio.AudioClip import CompositeAudioClip
|
||||
|
||||
final_audio = CompositeAudioClip(audio_clips)
|
||||
final_audio.write_audiofile(output_path)
|
||||
|
||||
# Clean up
|
||||
for clip in audio_clips:
|
||||
clip.close()
|
||||
final_audio.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"total_duration": total_duration,
|
||||
"tracks_count": len(audio_files),
|
||||
"volume_levels": volume_levels or [1.0] * len(audio_files),
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Audio mixing failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_audio_sync_video(
|
||||
video_path: str,
|
||||
audio_path: str,
|
||||
output_path: str,
|
||||
audio_start_time: float = 0.0,
|
||||
replace_audio: bool = True,
|
||||
) -> Dict[str, Union[str, float]]:
|
||||
"""
|
||||
Synchronize audio track with video timeline.
|
||||
|
||||
Args:
|
||||
video_path: Input video file path
|
||||
audio_path: Audio file to sync with video
|
||||
output_path: Output video file path
|
||||
audio_start_time: When audio should start in video timeline
|
||||
replace_audio: Whether to replace existing audio or mix with it
|
||||
|
||||
Returns:
|
||||
Dict with output path and sync details
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(video_path):
|
||||
return {"error": f"Video file not found: {video_path}"}
|
||||
if not os.path.exists(audio_path):
|
||||
return {"error": f"Audio file not found: {audio_path}"}
|
||||
|
||||
video_clip = VideoFileClip(video_path)
|
||||
audio_clip = AudioFileClip(audio_path).set_start(audio_start_time)
|
||||
|
||||
if replace_audio:
|
||||
# Replace original audio with new audio
|
||||
final_clip = video_clip.set_audio(audio_clip)
|
||||
else:
|
||||
# Mix new audio with existing audio
|
||||
if video_clip.audio:
|
||||
from moviepy.audio.AudioClip import CompositeAudioClip
|
||||
|
||||
mixed_audio = CompositeAudioClip([video_clip.audio, audio_clip])
|
||||
final_clip = video_clip.set_audio(mixed_audio)
|
||||
else:
|
||||
final_clip = video_clip.set_audio(audio_clip)
|
||||
|
||||
final_clip.write_videofile(output_path, audio_codec="aac")
|
||||
|
||||
# Clean up
|
||||
video_clip.close()
|
||||
audio_clip.close()
|
||||
final_clip.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"video_duration": video_clip.duration,
|
||||
"audio_start_time": audio_start_time,
|
||||
"audio_duration": audio_clip.duration,
|
||||
"replace_audio": replace_audio,
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Audio sync failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_add_branding(
|
||||
input_path: str,
|
||||
output_path: str,
|
||||
logo_path: Optional[str] = None,
|
||||
brand_colors: Optional[Dict] = None,
|
||||
position: str = "bottom-right",
|
||||
opacity: float = 0.8,
|
||||
size_scale: float = 0.1,
|
||||
) -> Dict[str, Union[str, Dict]]:
|
||||
"""
|
||||
Apply consistent branding elements (logos, colors) to video.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
output_path: Output video file path
|
||||
logo_path: Path to logo image file
|
||||
brand_colors: Brand color scheme dict
|
||||
position: Logo position ("bottom-right", "top-left", "center", etc.)
|
||||
opacity: Logo opacity (0.0 to 1.0)
|
||||
size_scale: Logo size relative to video dimensions
|
||||
|
||||
Returns:
|
||||
Dict with output path and branding details
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input video file not found: {input_path}"}
|
||||
|
||||
video_clip = VideoFileClip(input_path)
|
||||
|
||||
if logo_path and os.path.exists(logo_path):
|
||||
# Add logo overlay
|
||||
from moviepy.editor import ImageClip
|
||||
|
||||
logo_clip = ImageClip(logo_path, transparent=True)
|
||||
|
||||
# Scale logo based on video dimensions
|
||||
video_width, video_height = video_clip.size
|
||||
logo_width = int(video_width * size_scale)
|
||||
logo_clip = logo_clip.resize(width=logo_width)
|
||||
|
||||
# Position logo
|
||||
position_map = {
|
||||
"top-left": ("left", "top"),
|
||||
"top-right": ("right", "top"),
|
||||
"bottom-left": ("left", "bottom"),
|
||||
"bottom-right": ("right", "bottom"),
|
||||
"center": ("center", "center"),
|
||||
}
|
||||
|
||||
pos = position_map.get(position, ("right", "bottom"))
|
||||
logo_clip = (
|
||||
logo_clip.set_position(pos)
|
||||
.set_duration(video_clip.duration)
|
||||
.set_opacity(opacity)
|
||||
)
|
||||
|
||||
# Composite video with logo
|
||||
final_clip = CompositeVideoClip([video_clip, logo_clip])
|
||||
else:
|
||||
final_clip = video_clip
|
||||
|
||||
final_clip.write_videofile(output_path, audio_codec="aac")
|
||||
|
||||
# Clean up
|
||||
video_clip.close()
|
||||
if logo_path:
|
||||
logo_clip.close()
|
||||
final_clip.close()
|
||||
|
||||
return {
|
||||
"output_path": output_path,
|
||||
"logo_path": logo_path,
|
||||
"position": position,
|
||||
"opacity": opacity,
|
||||
"size_scale": size_scale,
|
||||
"brand_colors": brand_colors or {},
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Branding application failed: {e!s}"}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def mcp_video_resolution_optimizer(
|
||||
input_path: str,
|
||||
output_directory: str,
|
||||
target_resolutions: List[str] = None,
|
||||
quality_settings: Optional[Dict] = None,
|
||||
) -> Dict[str, Union[str, List, Dict]]:
|
||||
"""
|
||||
Generate multiple resolutions from source video.
|
||||
|
||||
Args:
|
||||
input_path: Input video file path
|
||||
output_directory: Directory to save optimized versions
|
||||
target_resolutions: List of target resolutions (e.g., ["1080p", "720p", "480p"])
|
||||
quality_settings: Quality settings for each resolution
|
||||
|
||||
Returns:
|
||||
Dict with generated file paths and optimization details
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(input_path):
|
||||
return {"error": f"Input video file not found: {input_path}"}
|
||||
|
||||
os.makedirs(output_directory, exist_ok=True)
|
||||
|
||||
if target_resolutions is None:
|
||||
target_resolutions = ["1080p", "720p", "480p"]
|
||||
|
||||
resolution_map = {
|
||||
"1080p": (1920, 1080),
|
||||
"720p": (1280, 720),
|
||||
"480p": (854, 480),
|
||||
"360p": (640, 360),
|
||||
}
|
||||
|
||||
video_clip = VideoFileClip(input_path)
|
||||
original_size = video_clip.size
|
||||
base_filename = Path(input_path).stem
|
||||
|
||||
generated_files = []
|
||||
|
||||
for res in target_resolutions:
|
||||
if res not in resolution_map:
|
||||
continue
|
||||
|
||||
target_width, target_height = resolution_map[res]
|
||||
|
||||
# Skip if target resolution is larger than original
|
||||
if target_width > original_size[0] or target_height > original_size[1]:
|
||||
continue
|
||||
|
||||
# Resize video
|
||||
resized_clip = video_clip.resize((target_width, target_height))
|
||||
|
||||
# Generate output filename
|
||||
output_filename = f"{base_filename}_{res}.mp4"
|
||||
output_path = os.path.join(output_directory, output_filename)
|
||||
|
||||
# Write resized video
|
||||
resized_clip.write_videofile(output_path, audio_codec="aac")
|
||||
|
||||
generated_files.append(
|
||||
{
|
||||
"resolution": res,
|
||||
"dimensions": f"{target_width}x{target_height}",
|
||||
"output_path": output_path,
|
||||
"file_size": os.path.getsize(output_path),
|
||||
}
|
||||
)
|
||||
|
||||
resized_clip.close()
|
||||
|
||||
video_clip.close()
|
||||
|
||||
return {
|
||||
"input_path": input_path,
|
||||
"output_directory": output_directory,
|
||||
"original_resolution": f"{original_size[0]}x{original_size[1]}",
|
||||
"generated_files": generated_files,
|
||||
"total_files": len(generated_files),
|
||||
"status": "success",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"error": f"Resolution optimization failed: {e!s}"}
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the MCP Video Editor server."""
|
||||
mcp.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
45
pyproject.toml
Normal file
45
pyproject.toml
Normal file
@ -0,0 +1,45 @@
|
||||
[project]
|
||||
name = "mcp-video-editor"
|
||||
version = "0.1.0"
|
||||
description = "MCP Video Editor - Professional video production tools for Model Context Protocol"
|
||||
authors = [
|
||||
{name = "MCP Video Editor Team"}
|
||||
]
|
||||
dependencies = [
|
||||
"fastmcp>=2.12.2",
|
||||
"opencv-python>=4.8.0",
|
||||
"moviepy>=1.0.3",
|
||||
"pillow>=10.0.0",
|
||||
"numpy>=1.24.0",
|
||||
"ffmpeg-python>=0.2.0",
|
||||
]
|
||||
requires-python = ">=3.9"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"ruff>=0.1.0",
|
||||
"pytest>=7.0.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py39"
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "W", "F", "I", "N", "UP", "RUF"]
|
||||
extend-select = ["I", "N", "UP", "RUF"]
|
||||
ignore = ["E501", "E722"]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
|
||||
[project.scripts]
|
||||
mcp-video-editor = "mcp_video_editor:main"
|
44
start-mcp-video-editor.sh
Executable file
44
start-mcp-video-editor.sh
Executable file
@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# MCP Video Editor Startup Script
|
||||
# This script launches the MCP Video Editor server using UV
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Change to the project directory
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
echo "🎬 Starting MCP Video Editor..."
|
||||
echo "📁 Working directory: $SCRIPT_DIR"
|
||||
|
||||
# Check if uv is installed
|
||||
if ! command -v uv &> /dev/null; then
|
||||
echo "❌ Error: UV is not installed or not in PATH"
|
||||
echo "Please install UV: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if dependencies are installed
|
||||
if [ ! -d ".venv" ] && [ ! -f "uv.lock" ]; then
|
||||
echo "📦 Installing dependencies with UV..."
|
||||
uv sync
|
||||
fi
|
||||
|
||||
# Set environment variables for video processing
|
||||
export VIDEO_OUTPUT_DIR="${VIDEO_OUTPUT_DIR:-./temp_videos}"
|
||||
export AUDIO_OUTPUT_DIR="${AUDIO_OUTPUT_DIR:-./temp_audio}"
|
||||
export MAX_VIDEO_DURATION="${MAX_VIDEO_DURATION:-3600}"
|
||||
|
||||
# Create output directories
|
||||
mkdir -p "$VIDEO_OUTPUT_DIR"
|
||||
mkdir -p "$AUDIO_OUTPUT_DIR"
|
||||
|
||||
echo "🎥 Video output directory: $VIDEO_OUTPUT_DIR"
|
||||
echo "🔊 Audio output directory: $AUDIO_OUTPUT_DIR"
|
||||
|
||||
# Launch the MCP Video Editor server with UV
|
||||
echo "🚀 Launching MCP Video Editor server..."
|
||||
exec uv run python -m mcp_video_editor
|
Loading…
x
Reference in New Issue
Block a user