A self-hosted web application for converting video files between formats and extracting audio from video files. Built with Python/Flask and powered by FFmpeg.
- Features
- Quick Start
- Installing FFmpeg
- GPU Acceleration
- Configuration
- Supported Formats
- Project Structure
- Security
- License
- Video Format Conversion — Convert between popular video formats (MP4, AVI, MKV, MOV, WMV, FLV, WebM)
- Audio Extraction — Extract audio tracks from video files (MP3, AAC, WAV, FLAC, OGG)
- Social Media Downloader — Download media from YouTube, Instagram, TikTok, X/Twitter, and Spotify with mode selection (video/audio), quality presets, and abort support
- Media Library Page — Browse
converted/anddownloads/in a simple web file browser with selectable multi-file download - Resolution Scaling — Upscale or downscale video (480p, 720p, 1080p, 1440p, 4K) with high-quality Lanczos filtering
- GPU Acceleration — Automatically uses hardware encoding (NVIDIA NVENC, AMD AMF, Intel QSV, VA-API) when available, with seamless CPU fallback
- Real-time Progress — Live progress bar with speed and ETA during conversion
- Abort Support — Cancel in-progress conversions with automatic cleanup
- No File Size Limits — Upload files of any size
- Automatic Cleanup — Temporary uploads and converted files are automatically deleted after 24 hours
- Persistent Downloads — Downloaded media is stored under
downloads/<service>/and is never auto-deleted - Dark/Light Mode — Modern UI with dark mode as default and easy toggle
- Docker Ready — Run with GPU support via Docker Compose in one command
- Self-Contained — Runs in a Python virtual environment with minimal dependencies
🐳 Docker (recommended)
git clone https://github.com/M1XZG/media-converter.git
cd media-converter
docker compose up -dOpen http://localhost:5000. GPU acceleration works automatically if you have an NVIDIA GPU and the NVIDIA Container Toolkit installed.
Driver compatibility: the image is based on
nvidia/cuda:12.6.3. The NVIDIA Container Toolkit will refuse to start a CUDA image that is newer than your host driver supports, so if GPU startup fails with acuda>=...requirement error, either update your NVIDIA driver or pin theFROMline in theDockerfileto a CUDA version your driver supports.
Without GPU support:
Remove the gpus: all line (and the NVIDIA_* environment variables) from docker-compose.yml, or run directly:
docker build -t media-converter .
docker run -d -p 5000:5000 --name media-converter media-converter📥 Downloader-only instance
You can split the app so a public instance only exposes the downloader and can't be used to convert large video files on your server. Set the ENABLE_CONVERTER feature flag to false (both halves are enabled by default):
git clone https://github.com/M1XZG/media-converter.git
cd media-converter
ENABLE_CONVERTER=false docker compose up -dOr run directly without the converter:
docker build -t media-converter .
docker run -d -p 5000:5000 \
-e ENABLE_CONVERTER=false \
-e ENABLE_FILE_MANAGER=false \
-v "$(pwd)/downloads:/app/downloads" \
--name media-downloader media-converterWhen the converter is disabled, its UI and its HTTP routes are turned off (disabled endpoints return 403). To run a converter-only instance instead, set ENABLE_DOWNLOADER=false. For a public server, also set ENABLE_FILE_MANAGER=false so visitors can't browse what others have downloaded. See Configuration for the full list of deployment combinations.
🪟 Windows
Prerequisites: Python 3.10+ and FFmpeg on your PATH.
git clone https://github.com/M1XZG/media-converter.git
cd media-converter
setup.bat🐧 Linux / macOS
Prerequisites: Python 3.10+ and FFmpeg on your PATH.
git clone https://github.com/M1XZG/media-converter.git
cd media-converter
chmod +x setup.sh
./setup.sh⚙️ Manual Setup
Prerequisites: Python 3.10+ and FFmpeg on your PATH.
# Create and activate virtual environment
python -m venv venv
# Windows:
venv\Scripts\activate
# Linux/macOS:
source venv/bin/activate
# Install dependencies and run
pip install -r requirements.txt
python app.pyThen open your browser to http://localhost:5000
Media Library page: http://localhost:5000/files
Downloader support: YouTube, Instagram, TikTok, X/Twitter, Spotify
FFmpeg must be installed and available on your system PATH (not required for Docker — it's included in the image). yt-dlp is installed automatically via pip from requirements and is used for YouTube, Instagram, TikTok, and X/Twitter downloads. spotdl is installed automatically via pip from requirements and is used for Spotify downloads. Because Spotify streams are DRM-protected, spotdl reads the track metadata and fetches the matching audio from YouTube, so Spotify links are always saved as audio.
Public posts generally work best. Some Instagram or X/Twitter links may require authentication or cookies depending on upstream site restrictions.
Windows
# Via winget
winget install FFmpeg
# Or via Chocolatey
choco install ffmpegmacOS
brew install ffmpegUbuntu / Debian
sudo apt update && sudo apt install ffmpegThe application automatically detects and uses GPU hardware encoders when available. On startup, the console and the web UI will show whether GPU acceleration is active.
GPU encoding is used for MP4, MKV, and MOV output. Other formats and audio extraction use CPU encoding.
Supported GPU encoders
| GPU Vendor | Encoder | Requirements |
|---|---|---|
| NVIDIA | NVENC | NVIDIA GPU + driver 470+, FFmpeg built with --enable-nvenc |
| AMD | AMF | AMD GPU + Adrenalin driver, FFmpeg built with --enable-amf |
| Intel | QSV | Intel iGPU/dGPU + media driver, FFmpeg built with --enable-libmfx or --enable-libvpl |
| Linux (generic) | VA-API | VA-API capable GPU + libva, FFmpeg built with --enable-vaapi |
When upscaling, quality settings are automatically increased (lower QP/CRF, slower presets) and a post-processing filter chain is applied (sharpening + denoising) for the best possible output.
Environment variables can be set in a .env file or exported:
| Variable | Default | Description |
|---|---|---|
FLASK_HOST |
0.0.0.0 |
Host to bind to |
FLASK_PORT |
5000 |
Port to listen on |
MAX_CONTENT_LENGTH |
0 (unlimited) |
Max upload size in bytes (0 = no limit) |
CLEANUP_HOURS |
24 |
Hours before uploaded/converted temp files are auto-deleted |
DOWNLOADS_CLEANUP_MINUTES |
30 |
Minutes before downloaded files are auto-deleted (0/off/disabled = keep forever) |
ENABLE_CONVERTER |
true |
Enable the video converter / audio extractor / GIF maker |
ENABLE_DOWNLOADER |
true |
Enable the social-media downloader |
ENABLE_FILE_MANAGER |
true |
Enable the "Media Library" file browser at /files (list/download/delete) |
Cleanup applies to uploads/ and converted/ (after CLEANUP_HOURS) and to
downloads/ (after DOWNLOADS_CLEANUP_MINUTES). The downloads timer effectively
starts when a download finishes — files still being written keep a fresh
timestamp and are never removed mid-download. Set DOWNLOADS_CLEANUP_MINUTES=0
to keep downloaded files indefinitely.
Out of the box both halves are enabled and the app works exactly as before. You can split the functionality using the two feature flags above — useful for hosting a public, downloader-only instance that can't be used to convert large video files on your server:
| Deployment | Settings |
|---|---|
| Full app (default) | ENABLE_CONVERTER=true, ENABLE_DOWNLOADER=true |
| Downloader only | ENABLE_CONVERTER=false, ENABLE_DOWNLOADER=true |
| Converter only | ENABLE_CONVERTER=true, ENABLE_DOWNLOADER=false |
When a half is disabled, both its UI and its HTTP routes are turned off — the
disabled endpoints return 403, so the feature can't be triggered by hand-crafted
requests. If both flags are set to false, the app falls back to full functionality.
The Media Library at /files lets anyone browse, download, and delete
every file on the server. On a public instance you usually don't want visitors
to see what others have downloaded, so set:
ENABLE_FILE_MANAGER=false
When disabled, the /files page and all /files/* endpoints return 403,
the "Media Library" link is removed from the UI, and no browser file retrieval is
offered anywhere (downloaded files are saved to the server only). Converter output
is still delivered through its own /download/<id> link.
Example — spin up a standalone downloader on another server:
# docker-compose.yml (public downloader-only instance)
services:
media-downloader:
build: .
ports:
- "5000:5000"
environment:
- ENABLE_CONVERTER=false
- ENABLE_DOWNLOADER=true
- ENABLE_FILE_MANAGER=false
volumes:
- ./downloads:/app/downloads
restart: unless-stoppedVideo output formats
| Format | Extension |
|---|---|
| MP4 | .mp4 |
| AVI | .avi |
| MKV | .mkv |
| MOV | .mov |
| WMV | .wmv |
| FLV | .flv |
| WebM | .webm |
Audio output formats
| Format | Extension |
|---|---|
| MP3 | .mp3 |
| AAC | .aac |
| WAV | .wav |
| FLAC | .flac |
| OGG | .ogg |
Supported input formats
MP4, AVI, MKV, MOV, WMV, FLV, WebM, M4V, MPEG, MPG, 3GP, OGV, TS, VOB
media-converter/
├── app.py # Flask application
├── cleanup.py # File cleanup utility
├── requirements.txt # Python dependencies
├── Dockerfile # Docker image definition
├── docker-compose.yml # Docker Compose with GPU support
├── .dockerignore # Docker build exclusions
├── setup.bat # Windows setup script
├── setup.sh # Linux/macOS setup script
├── .gitignore
├── README.md
├── templates/
│ ├── index.html # Main web UI
│ └── files.html # Simple media library browser
├── uploads/ # Temporary upload storage (auto-created)
├── converted/ # Temporary converted file storage (auto-created)
└── downloads/
├── youtube/ # Persistent YouTube downloads (not auto-cleaned)
├── instagram/ # Persistent Instagram downloads (not auto-cleaned)
├── tiktok/ # Persistent TikTok downloads (not auto-cleaned)
├── twitter/ # Persistent X/Twitter downloads (not auto-cleaned)
└── spotify/ # Persistent Spotify downloads (not auto-cleaned)
Media Converter is designed as a self-hosted tool for a trusted network (your own machine or LAN). Please read this before exposing it to the internet.
- There is no authentication. Anyone who can reach the port can upload, convert, download media, browse the library, and delete files. Do not expose the app directly to the public internet. If you need remote access, put it behind a reverse proxy (nginx, Caddy, Traefik) that enforces authentication and TLS, or restrict access with a VPN or firewall rules.
- It binds to
0.0.0.0by default so it is reachable from other machines on your network. SetFLASK_HOST=127.0.0.1to restrict it to the local machine only. - Uploads are unlimited by default (
MAX_CONTENT_LENGTH=0). On a shared or exposed host this allows disk-fill denial of service. SetMAX_CONTENT_LENGTH(in bytes) to cap upload size, for exampleMAX_CONTENT_LENGTH=2147483648for a 2 GB limit. - The
downloads/folder is never auto-cleaned. Downloaded media persists until you delete it, so monitor disk usage if you use the downloader heavily. - FFmpeg and yt-dlp process untrusted media and URLs. Keep them up to date so you pick
up upstream security fixes (
pip install -U -r requirements.txt, and update FFmpeg via your package manager or the Docker base image).
To report a security vulnerability, please use GitHub's private vulnerability reporting (see SECURITY.md). Do not open a public issue for security problems.
MIT License