- Added ghcr.io/xpltdco/media-rip:latest prominently in Quickstart - Added curl one-liner to grab docker-compose.yml - Added docker run alternative for users who don't want compose - Updated features: 9 built-in themes (was 3) |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| frontend | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Caddyfile | ||
| docker-compose.example.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
media.rip()
A self-hostable yt-dlp web frontend. Paste a URL, pick quality, download — with session isolation, real-time progress, and a cyberpunk default theme.
Features
- Paste & download — Any URL yt-dlp supports. Format picker with live quality extraction.
- Real-time progress — Server-Sent Events stream download progress to the browser instantly.
- Session isolation — Each browser gets its own download queue. No cross-talk.
- Playlist support — Collapsible parent/child jobs with per-video status tracking.
- 9 built-in themes — 5 dark (Cyberpunk, Dark, Midnight, Hacker, Neon) + 4 light (Light, Paper, Arctic, Solarized). Admin picks the pair, visitors toggle dark/light.
- Custom themes — Drop a CSS file into
/themesvolume. No rebuild needed. - Admin panel — Session management, storage info, manual purge, error logs. Protected by bcrypt auth.
- Cookie auth — Upload cookies.txt per session for paywalled/private content.
- Auto-purge — Configurable scheduled cleanup of old downloads and logs.
- Zero telemetry — No outbound requests. No CDN, no fonts, no analytics. CSP enforced.
- Mobile-friendly — Responsive layout with bottom tabs on small screens.
Quickstart
The Docker image is published to GitHub Container Registry:
ghcr.io/xpltdco/media-rip:latest
Pull and run with Docker Compose (recommended):
# Download the compose file
curl -O https://raw.githubusercontent.com/xpltdco/media-rip/master/docker-compose.yml
# Start the container
docker compose up -d
Or pull and run directly:
docker run -d \
--name mediarip \
-p 8080:8000 \
-v ./downloads:/downloads \
-v mediarip-data:/data \
--restart unless-stopped \
ghcr.io/xpltdco/media-rip:latest
Open http://localhost:8080 and paste a URL. On first run, you'll set an admin password.
Downloads are saved to ./downloads/. Everything else (database, sessions, logs) lives in a named Docker volume.
That's it. The defaults are production-ready: isolated sessions, admin panel with first-run setup wizard, 24h auto-purge, 3 concurrent downloads. Most users don't need to set any environment variables.
Docker Volumes
| Mount | Purpose | Required |
|---|---|---|
/downloads |
Downloaded media files | ✅ Bind mount recommended |
/data |
SQLite database, session cookies, error logs | ✅ Named volume recommended |
/themes |
Custom theme CSS overrides | Optional |
/app/config.yaml |
YAML config file | Optional |
Configuration
Everything works out of the box. The settings below are for operators who want to tune specific behavior.
Most Useful Settings
These are the knobs most operators actually touch — all shown commented out in docker-compose.yml:
| Variable | Default | When to change |
|---|---|---|
MEDIARIP__SESSION__MODE |
isolated |
Set to shared for family/team use, open to disable sessions entirely |
MEDIARIP__DOWNLOADS__MAX_CONCURRENT |
3 |
Increase for faster connections, decrease on low-spec hardware |
MEDIARIP__PURGE__MAX_AGE_MINUTES |
1440 |
Raise for longer retention, or set PURGE__ENABLED=false to keep forever |
MEDIARIP__ADMIN__PASSWORD |
(empty) | Pre-set to skip the first-run wizard (useful for automated deployments) |
All Settings
Full reference — click to expand
Core
| Variable | Default | Description |
|---|---|---|
MEDIARIP__SERVER__PORT |
8000 |
Internal server port |
MEDIARIP__SERVER__LOG_LEVEL |
info |
Log level (debug, info, warning, error) |
MEDIARIP__DOWNLOADS__MAX_CONCURRENT |
3 |
Maximum parallel downloads |
MEDIARIP__SESSION__MODE |
isolated |
isolated, shared, or open |
MEDIARIP__SESSION__TIMEOUT_HOURS |
72 |
Session cookie lifetime (hours) |
Admin
| Variable | Default | Description |
|---|---|---|
MEDIARIP__ADMIN__ENABLED |
true |
Enable admin panel |
MEDIARIP__ADMIN__USERNAME |
admin |
Admin username |
MEDIARIP__ADMIN__PASSWORD |
(empty) | Admin password (hashed on startup, never stored as plaintext) |
Purge
| Variable | Default | Description |
|---|---|---|
MEDIARIP__PURGE__ENABLED |
true |
Enable automatic cleanup of old downloads |
MEDIARIP__PURGE__MAX_AGE_MINUTES |
1440 |
Delete completed downloads older than this (minutes) |
MEDIARIP__PURGE__CRON |
* * * * * |
Purge check schedule (cron syntax) |
MEDIARIP__PURGE__PRIVACY_MODE |
false |
Aggressive cleanup — removes downloads + logs on schedule |
MEDIARIP__PURGE__PRIVACY_RETENTION_MINUTES |
1440 |
Retention period when privacy mode is enabled |
UI
| Variable | Default | Description |
|---|---|---|
MEDIARIP__UI__DEFAULT_THEME |
dark |
Default theme (dark, light, cyberpunk, or custom) |
MEDIARIP__UI__WELCOME_MESSAGE |
(built-in) | Header subtitle text shown to users |
yt-dlp
| Variable | Default | Description |
|---|---|---|
MEDIARIP__YTDLP__EXTRACTOR_ARGS |
{} |
JSON object of yt-dlp extractor args |
Note: Internal paths (
SERVER__DB_PATH,SERVER__DATA_DIR,DOWNLOADS__OUTPUT_DIR) are pre-configured in the Docker image. Only override these if you change the volume mount points.
Session Modes
- isolated (default): Each browser session has its own private download queue.
- shared: All sessions see all downloads. Good for household/team use.
- open: No session tracking at all. Everyone shares one queue.
Admin Panel
Enabled by default. On first run, you'll be prompted to set a password in the browser.
To pre-configure for automated deployments (skip the wizard), set the password via environment variable or .env file:
# docker-compose.yml
environment:
- MEDIARIP__ADMIN__PASSWORD=your-password-here
# Or in .env file
MEDIARIP__ADMIN__PASSWORD=your-password-here
The plaintext password is hashed on startup and cleared from memory — it's never stored or logged.
Troubleshooting: YouTube 403 Errors
YouTube downloads work out of the box — yt-dlp automatically selects the right player clients. If you do hit HTTP 403 errors, it's usually one of:
- VPN/datacenter IP — YouTube blocks many VPN exit IPs. Run the container on your direct internet connection instead.
- Private/age-restricted content — Upload a
cookies.txtfrom a logged-in browser session via the UI. - YouTube-side changes — As a last resort, you can override yt-dlp's player client selection:
MEDIARIP__YTDLP__EXTRACTOR_ARGS='{"youtube": {"player_client": ["web_safari"]}}'
Custom Themes
- Create a folder in your themes volume:
./themes/my-theme/ - Add
metadata.json:{ "name": "My Theme", "author": "You", "description": "A cool theme" } - Add
theme.csswith CSS variable overrides:[data-theme="my-theme"] { --color-bg: #1a1a2e; --color-accent: #e94560; /* See base.css for all 50+ tokens */ } - Restart the container. Your theme appears in the picker.
See the built-in themes in frontend/src/themes/ for fully commented examples.
Secure Deployment
For production with TLS, use the included Caddy reverse proxy:
cp docker-compose.example.yml docker-compose.yml
cp .env.example .env
# Edit .env with your domain and admin password
docker compose up -d
Caddy automatically provisions Let's Encrypt TLS certificates for your domain.
Development
Backend
cd backend
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install pytest pytest-asyncio pytest-anyio httpx ruff
.venv/bin/python -m pytest tests/ -v -m "not integration"
Frontend
cd frontend
npm install
npm run dev # Dev server with hot reload
npx vitest run # Run tests
npm run build # Production build
API
| Endpoint | Method | Description |
|---|---|---|
/api/health |
GET | Health check with version + uptime |
/api/config/public |
GET | Public configuration |
/api/downloads |
GET | List downloads for current session |
/api/downloads |
POST | Start a new download |
/api/downloads/{id} |
DELETE | Cancel/remove a download |
/api/formats |
GET | Extract available formats for a URL |
/api/events |
GET | SSE stream for real-time progress |
/api/cookies |
POST | Upload cookies.txt for authenticated downloads |
/api/cookies |
DELETE | Remove cookies.txt for current session |
/api/themes |
GET | List available custom themes |
/api/admin/* |
GET/POST | Admin endpoints (requires auth) |
Architecture
- Backend: Python 3.12 + FastAPI + aiosqlite + yt-dlp
- Frontend: Vue 3 + TypeScript + Pinia + Vite
- Transport: Server-Sent Events for real-time progress
- Database: SQLite with WAL mode
- Styling: CSS custom properties (no Tailwind, no component library)
- Container: Multi-stage build, non-root user, amd64 + arm64
License
MIT