This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
VideoEditorAPI (ShortsCreator) is an AI-powered video processing service that provides subtitle generation, karaoke effects, video editing, and audio mixing capabilities. Built with Flask, Whisper AI, and MoviePy.
# Start standard server
python app.py
# Start OPTIMIZED server (recommended for 4GB systems)
python app_optimized.py
# Or use startup script (sets up venv, installs deps)
./start.sh
# Run API tests
python test_api.py
# Run performance tests (optimized version)
python scripts/performance_test.py# Build and start with Docker Compose (optimized by default)
docker-compose up -d
# Build with optimized Dockerfile for 4GB systems
docker build -f Dockerfile.optimized -t videoeditorapi:optimized .
# Run optimized container directly
docker run -p 8080:8080 --memory=4g --cpus=2 videoeditorapi:optimized
# View logs
docker-compose logs -f
# Stop services
docker-compose down# Test API endpoints
python test_api.py
# Test specific functionality
python test_subtitle_generation.py
python test_google_drive.py
python test_enhanced_gdrive.py# Deploy to production
./deploy.sh
# Deploy to Digital Ocean
./deploy-to-do.sh
# Or use one-click installer
curl -fsSL https://raw.githubusercontent.com/jdportugal/VideoEditorAPI/main/install-ghcr.sh | sudo bash- Flask Application (
app.py): Main REST API server with async job processing - Video Service (
app/services/video_service.py): Handles video operations (subtitles, splitting, joining, music) - Subtitle Service (
app/services/subtitle_service.py): Whisper AI integration for speech recognition - Job Manager (
app/services/job_manager.py): Async job tracking and state management - Download Utils (
app/utils/download_utils.py): Enhanced file downloading with Google Drive support
- Async Processing: Jobs run in ThreadPoolExecutor, status tracked via JSON files
- Service Layer: Business logic encapsulated in dedicated service classes
- Job Chaining: Output from one job can be input to another via job_id reference
- Enhanced Google Drive: Multiple download strategies for robust file access
- API receives request → creates job → returns job_id
- Job runs async in background thread
- Job updates status/progress in jobs/{job_id}.json
- Client polls /job-status/{job_id} for updates
- Completed jobs provide download URLs
VideoService (app/services/video_service.py)
- Subtitle rendering with 4 modes: off, karaoke, popup, typewriter
- Video splitting with precise timing control
- Multi-video concatenation
- Background music integration with fade effects
- Uses MoviePy for video processing
SubtitleService (app/services/subtitle_service.py)
- Whisper AI integration for speech-to-text
- Word-level timestamp extraction
- Timing analysis and synchronization recommendations
- Multiple output formats: SRT, VTT, JSON
JobManager (app/services/job_manager.py)
- JSON-based job persistence
- Status tracking: pending → processing → completed/failed
- File verification before marking jobs complete
- Job cleanup utilities
- Environment-based config in
config.py - Default Whisper model: "base" (speed/accuracy balance)
- Max workers: 4 concurrent jobs
- Default port: 8080 (Digital Ocean compatible)
- Automatic directory creation: temp/, uploads/, jobs/, static/
app/
├── services/ # Business logic services
│ ├── video_service.py
│ ├── subtitle_service.py
│ └── job_manager.py
└── utils/
└── download_utils.py # Enhanced download with Google Drive
jobs/ # Job status tracking (JSON files)
temp/ # Temporary processing files
uploads/ # User uploaded files
static/ # Static output files
POST /generate-subtitles
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"model": "base" // Optional: tiny, base, small, medium, large
}POST /add-subtitles
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"subtitle_mode": "karaoke", // off, karaoke, popup, typewriter
"model": "base", // Optional: tiny, base, small, medium, large
"font_size": 60, // Optional: default 60
"font_color": "#FFFF00", // Optional: yellow default
"normal_color": "#FFFFFF", // Optional: white default for non-active words
"position": "bottom" // Optional: bottom, top, center
}POST /split-video
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"start_time": "00:01:30", // Format: HH:MM:SS or SS.SS
"end_time": "00:03:45" // Format: HH:MM:SS or SS.SS
}POST /join-videos
Content-Type: application/json
{
"video_urls": [
"https://drive.google.com/uc?id=FILE1_ID&export=download",
"https://drive.google.com/uc?id=FILE2_ID&export=download"
]
}POST /add-music
Content-Type: application/json
{
"video_url": "https://drive.google.com/uc?id=YOUR_VIDEO_ID&export=download",
"music_url": "https://drive.google.com/uc?id=YOUR_MUSIC_ID&export=download",
"volume": "0.3", // Music volume (0.0-1.0)
"fade_in": "2.0", // Optional: fade in duration in seconds
"fade_out": "3.0", // Optional: fade out duration in seconds
"loop_music": "true" // Optional: loop music to match video length
}POST /voice-filters
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"filter_type": "telephone" // telephone, radio, walkie_talkie
}GET /voice-filters/listPOST /video-filters
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"filter_type": "crt",
"custom_parameters": { // Optional: override default parameters
"scanline_intensity": 0.3,
"curvature": 0.02
}
}GET /video-filters/listGET /video-filters/parameters/{filter_type}POST /video-aspect-ratio
Content-Type: application/json
{
"url": "https://drive.google.com/uc?id=YOUR_FILE_ID&export=download",
"target_ratio": "9:16", // 16:9, 9:16, 4:3, 3:4, 1:1
"scale_mode": "fill", // fit, fill, stretch
"background_color": "#000000" // Optional: for fit mode
}GET /video-aspect-ratio/ratiosGET /video-aspect-ratio/modesPOST /add-voiceover
Content-Type: application/json
// For video input:
{
"video_url": "https://drive.google.com/uc?id=YOUR_VIDEO_ID&export=download",
"audio_url": "https://drive.google.com/uc?id=YOUR_AUDIO_ID&export=download",
"zoom_factor": 1.1 // Optional: zoom factor for animation (default 1.0 = no zoom)
}
// For image input (creates video with optional zoom effect):
{
"image_url": "https://drive.google.com/uc?id=YOUR_IMAGE_ID&export=download",
"audio_url": "https://drive.google.com/uc?id=YOUR_AUDIO_ID&export=download",
"zoom_factor": 1.15 // Optional: zoom factor for animation (default 1.0 = no zoom)
}GET /job-status/{job_id}Response:
{
"job_id": "uuid-here",
"status": "completed", // pending, processing, completed, failed
"progress": 100, // 0-100
"status_message": "✅ Processing complete",
"created_at": "2025-11-28T10:00:00",
"updated_at": "2025-11-28T10:02:30",
"output_path": "temp/uuid_output.mp4",
"error": null
}GET /download/{job_id}GET /download-subtitles/{job_id}GET /healthPOST /admin/cleanupResponse:
{
"message": "Cleanup completed",
"deleted_jobs": 45,
"deleted_temp_files": 23,
"deleted_uploads": 12,
"freed_space_mb": 1024.5
}{
"job_id": "uuid-here",
"status": "pending",
"message": "Job started successfully"
}{
"error": "Description of the error"
}- Core: Flask, Flask-CORS, requests
- AI: openai-whisper, torch, torchaudio
- Video: moviepy, ffmpeg-python, pillow
- Utils: python-dotenv, numpy<2.0.0
- Default font: "Luckiest Guy" at
/usr/share/fonts/truetype/luckiest-guy/LuckiestGuy-Regular.ttf - Fallback: "DejaVu-Sans-Bold"
- Ensure fonts are available in deployment environment
- Enhanced download with 6 fallback strategies
- Handles virus scan warnings and confirmation tokens
- Supports multiple URL formats and domains
- Automatic file verification to prevent HTML downloads
- Jobs run asynchronously with ThreadPoolExecutor
- Status persisted to JSON files in jobs/ directory
- Failed jobs include detailed error messages
- Automatic cleanup of partial files on failure
- Use
test_api.pyfor endpoint validation - Specific tests for Google Drive, subtitles, enhanced downloads
- Test with sample videos from sample-videos.com
- Monitor job status for completion/failure
- Whisper model "base" balances speed vs accuracy
- Video processing time: ~30-60s per minute of video
- Concurrent job limit: 4 workers
- Memory usage scales with video size and length
- Comprehensive error messages with context
- Automatic cleanup on failure
- Job status tracking for debugging
- File verification before completion
- Development: 5000 (Flask default)
- Production: 8080 (Digital Ocean App Platform)
- Configurable via PORT environment variable
-
OptimizedVideoService (
app/services/optimized_video_service.py)- Chunked video processing for 10+ minute videos
- Real-time memory monitoring with automatic cleanup
- FFmpeg integration for memory-efficient concatenation
- Adaptive chunk sizing based on system resources
-
OptimizedSubtitleService (
app/services/optimized_subtitle_service.py)- Dynamic Whisper model selection (tiny/base/small based on available RAM)
- Audio chunking for very long videos (20+ minutes)
- Model lifecycle management (load/unload to free memory)
- Memory-first processing approach
-
app_optimized.py - Resource-aware main application
- Single worker for 4GB systems (prevents resource contention)
- Real-time memory/CPU monitoring with warning thresholds
- Emergency cleanup when memory exceeds 95%
- Adaptive job acceptance based on system state
- Memory Management: 80% reduction in peak memory usage for long videos
- Chunked Processing: Videos >5min processed in 30-60s segments
- Model Selection: Automatic tiny/base/small model selection based on available RAM
- Resource Monitoring: Real-time tracking with automatic cleanup triggers
- Worker Optimization: 1 worker for 4GB systems vs 4 workers default
- Font/Encoding: Smaller fonts, ultrafast encoding preset for performance
- Memory Usage: 3.5GB → 2.8GB peak (20% reduction)
- Processing Speed: 0.3x → 0.8x realtime (167% improvement)
- Failure Rate: 60% → 5% for 10+ minute videos
- Throughput: 1 stable job vs 0 previously
# Use optimized version
python app_optimized.py
# Install additional monitoring dependencies
pip install psutil
# Monitor performance during processing
python scripts/performance_test.py
# Check real-time system status
curl http://localhost:8080/system-statusGET /system-status- Detailed memory/CPU metrics and worker statusGET /health- Enhanced health check with resource warnings- Enhanced job status includes memory usage during processing
Error: "Failed to download 1: Invalid URL '1': No scheme supplied"
Problem: Using invalid URL for music_url parameter (e.g., "1" instead of actual URL)
Solution: Provide a valid URL to a music file:
{
"video_url": "https://drive.google.com/uc?id=YOUR_VIDEO_ID&export=download",
"music_url": "https://drive.google.com/uc?id=YOUR_MUSIC_FILE_ID&export=download"
}Error: 404 Not Found when calling /add_subtitles
Problem: Wrong endpoint URL (underscore vs hyphen)
Solution: Use correct endpoint with hyphen:
# Wrong
POST /add_subtitles
# Correct
POST /add-subtitlesError: 400 Bad Request with color parameter errors
Problem: Invalid color format or parameter names
Solution: Use correct format and parameter names:
{
"font_color": "#FFFF00", // Correct: 6-digit hex with #
"normal_color": "#FFFFFF" // Correct: normal-color, not normal_color
}Error: Google Drive URLs return WAV files instead of video with filtered audio
Problem: File type detection failing for Google Drive URLs
Solution: This is now fixed in the current version. The system downloads the file first to detect the actual content type.
Error: 400 Bad Request when using unsupported scale mode
Problem: Using non-existent scale mode like "crop"
Solution: Use valid scale modes:
"fit"- Fit within target ratio with letterboxing"fill"- Fill target ratio (may crop content)"stretch"- Stretch to exact ratio
Error: Failed to download or HTML content instead of file
Solutions:
- Make file publicly accessible: Share → Anyone with link can view
- Use correct download URL format:
https://drive.google.com/uc?id=FILE_ID&export=download - Check file size: Files >25MB may require additional confirmation
- Verify file type: Ensure file is actually video/audio format
Error: Job remains in "processing" state indefinitely
Solutions:
- Check job status with details:
curl http://localhost:8080/job-status/YOUR_JOB_ID
- Check system resources:
curl http://localhost:8080/health
- Clean up if needed:
curl -X POST http://localhost:8080/admin/cleanup
Error: Jobs failing with memory-related errors
Solutions:
- Use optimized version:
python app_optimized.py
- Process shorter video segments
- Use smaller Whisper model:
"tiny"instead of"base" - Clean up temp files: Use
/admin/cleanupendpoint
Error: Subtitle rendering fails with font errors
Solution: Ensure fonts are available in deployment environment:
# Install required fonts (Ubuntu/Debian)
sudo apt-get install fonts-dejavu-core
# Or copy custom fonts to system directory
sudo cp fonts/LuckiestGuy-Regular.ttf /usr/share/fonts/truetype/
sudo fc-cache -fError: Cannot connect to API on port 8080
Solutions:
- Check container status:
docker ps
- Check port mapping:
docker-compose logs
- Rebuild if needed:
docker-compose down docker-compose up -d --build
- Google Drive: Use
uc?id=FILE_ID&export=downloadformat - Direct URLs: Ensure files are publicly accessible
- File Size: Keep files under reasonable limits for processing time
- Format Support: MP4 for video, MP3/WAV for audio
- Colors: Use 6-digit hex format with # prefix
- Times: Use HH:MM:SS or decimal seconds format
- Volumes: Use decimal values between 0.0 and 1.0
- Required Fields: Always include all required parameters
- Job Status: Regularly check job progress via
/job-status/{job_id} - System Health: Monitor
/healthendpoint for resource issues - Logs: Check Docker logs for detailed error messages
- Video Length: Process longer videos in segments when possible
- Model Selection: Use appropriate Whisper model for your accuracy needs
- Concurrent Jobs: Limit concurrent processing on constrained systems
- Cleanup: Regularly clean up temp files and completed jobs