Sign inSign up

awkto/music-ripper

By awkto

•Updated about 1 month ago

YouTube Playlist to MP3 Ripper with web interface

Image
0

1.9K

awkto/music-ripper repository overview

⁠YouTube Playlist to MP3 Ripper

Download YouTube playlists as MP3 files with a beautiful web interface or CLI.

⁠Features

  • Web Interface: Modern, responsive UI for easy playlist management
  • CLI Tool: Command-line script for automated workflows
  • Background Processing: Download multiple playlists simultaneously
  • ZIP Downloads: Package completed playlists for easy distribution
  • Auto Sanitization: Clean filenames (remove special chars, spaces to hyphens)
  • M3U Playlists: Auto-generated playlist files
  • Metadata: Embedded thumbnails and track information
  • Docker Support: Run anywhere with containerization

⁠Quick Start

⁠Option 1: Docker Hub (Easiest)

Pull and run the pre-built image:

# Using Docker
docker run -d -p 5000:5000 \
  -v ./downloads:/app/downloads \
  --name music-ripper \
  awkto/music-ripper:latest

# Using Podman
podman run -d -p 5000:5000 \
  -v ./downloads:/app/downloads \
  --name music-ripper \
  docker.io/awkto/music-ripper:latest

# Access the web interface at http://localhost:5000
⁠Option 2: Docker Compose (Build from Source)
# Clone the repository
git clone https://github.com/awkto/music-ripper.git
cd music-ripper

# Start with Docker Compose
docker-compose up -d

# Or with Podman
podman-compose up -d

# Access the web interface at http://localhost:5000
⁠Option 3: Local Installation
# Clone the repository
git clone https://github.com/awkto/music-ripper.git
cd music-ripper

# Install dependencies
pip install -r requirements.txt

# Start the web server
python3 app.py

# Access the web interface at http://localhost:5000
⁠Option 4: CLI Only
# Install dependencies
pip install yt-dlp

# Run with pure playlist URL
python3 playlist_ripper.py 'https://www.youtube.com/playlist?list=PLxxxx'

# Run with mixed video+playlist URL
python3 playlist_ripper.py 'https://www.youtube.com/watch?v=xxxxx&list=PLxxxx'

# With custom folder name
python3 playlist_ripper.py '<youtube_playlist_url>' 'MyPlaylist'

Note: Both pure playlist URLs and mixed video+playlist URLs are supported. The tool will always download the entire playlist.

⁠Requirements

  • Python 3.x
  • yt-dlp
  • ffmpeg (for audio conversion and thumbnail embedding)
  • Flask (for web interface)
  • Docker (optional, for containerized deployment)

⁠Web Interface Features

  • Dashboard: View all download jobs with real-time status updates
  • Progress Tracking: Monitor download progress for active jobs
  • ZIP Downloads: Package and download completed playlists
  • Job Management: Delete old downloads to free up space
  • Responsive Design: Works on desktop, tablet, and mobile
  • Auto-refresh: Automatically updates while downloads are in progress
  • Flexible URLs: Accepts both pure playlist URLs and mixed video+playlist URLs

⁠Output Structure

music-ripper/
├── app.py                    # Web application
├── playlist_ripper.py        # CLI tool
├── templates/
│   └── index.html           # Web interface
├── downloads/               # Web downloads directory
│   ├── PlaylistName/
│   │   ├── Artist-SongTitle.mp3
│   │   └── ...
│   └── PlaylistName.m3u
└── BoruBiro/                # CLI downloads (if used)
    ├── Artist-SongTitle.mp3
    └── ...

⁠API Endpoints

The web application exposes the following REST API:

  • GET / - Web interface
  • POST /api/download - Start a new download job
  • GET /api/jobs - List all jobs
  • GET /api/jobs/<job_id> - Get job status
  • GET /api/download/<job_id>/zip - Download completed playlist as ZIP
  • DELETE /api/jobs/<job_id> - Delete a job and its files

⁠Authentication (optional)

Authentication is off by default — set RIPPER_PASSWORD to turn it on. This is used by the Android share app⁠ so you can share YouTube links to your server without exposing it fully.

Env varPurpose
RIPPER_PASSWORDEnables auth. Password for the web login (/login). Unset = fully open (original behaviour).
SECRET_KEYFlask session-signing key. Set a fixed random value so logins survive restarts; otherwise a random one is generated each boot.
TOKENS_FILEWhere API tokens are persisted. Defaults to downloads/.tokens.json (inside the mounted volume, so it survives restarts).

When enabled:

  • The web UI (pages + browse/stream/delete/zip APIs) requires a login session established at /login.
  • Manage API tokens on the /settings page (behind login): add short, human-typable tokens for the mobile app, or auto-generate one.
  • POST /api/download and GET /api/jobs[/<id>] accept either a logged-in session or a valid X-API-Token header. Tokens are scoped: they can only queue downloads and read job progress — not manage tokens or browse files.
docker run -d -p 5010:5000 \
  -e RIPPER_PASSWORD='your-web-password' \
  -e SECRET_KEY='long-random-string' \
  -v /path/to/downloads:/app/downloads \
  --name awkto-mp3 awkto/music-ripper:latest

⁠How It Works

⁠CLI Mode
  1. Fetches playlist information from YouTube
  2. Creates a subdirectory named after the playlist (sanitized)
  3. Downloads all videos and converts to MP3 format
  4. Embeds thumbnails and metadata
  5. Sanitizes all filenames (removes special chars, spaces become hyphens)
  6. Creates an M3U playlist file in the parent directory
⁠Web Mode
  1. User submits playlist URL through web interface
  2. Job is created and queued for background processing
  3. Downloads are processed in separate threads
  4. Real-time status updates via polling
  5. Completed playlists can be downloaded as ZIP files
  6. Jobs can be deleted to clean up space

⁠Docker Configuration

The application uses Docker Compose with volume mounting for persistent downloads:

services:
  music-ripper:
    ports:
      - "5000:5000"
    volumes:
      - ./downloads:/app/downloads  # Persist downloads on host

⁠Development

# Create virtual environment
python3 -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Run in development mode
python3 app.py

⁠Troubleshooting

⁠Downloads fail with "Video unavailable"
  • Some videos may be region-locked or removed
  • Try updating yt-dlp: pip install --upgrade yt-dlp
⁠Web interface doesn't start
  • Check if port 5000 is already in use
  • Ensure Flask is installed: pip install flask
⁠Docker build fails
  • Ensure Docker and Docker Compose are installed
  • Check available disk space

⁠License

MIT

⁠HTTP API

Everything the UI does is a plain JSON API, so it can be scripted. When auth is enabled (RIPPER_PASSWORD set), pass X-API-Token: <token> (or Authorization: Bearer <token>) on the endpoints below; tokens are managed in Settings.

⁠Playlists & downloads
# List a playlist's tracks (metadata only, nothing downloaded)
curl -X POST /api/playlist/preview -H 'Content-Type: application/json' \
  -d '{"playlist_url": "https://www.youtube.com/playlist?list=PLxxxx"}'
# -> {"playlist_title": "...", "track_count": 43, "tracks": [{"id", "title", "uploader", "duration", "index"}]}

# Download the whole playlist
curl -X POST /api/download -d '{"playlist_url": "...", "custom_name": "MyFolder"}'

# Download only hand-picked tracks (ids from the preview)
curl -X POST /api/download -d '{"playlist_url": "...", "video_ids": ["lLZvJ_rtZO8", "x4OI91W2jFE"]}'

# Job progress
curl /api/jobs            # all jobs
curl /api/jobs/<job_id>   # one job
⁠Playlist sync

A sync target binds a downloads folder to a playlist URL. Syncing diffs the folder against the live playlist: new tracks are downloaded, tracks removed from the playlist are deleted. Only files the app downloaded itself (tracked in downloads/.ripper.db by YouTube video ID) are ever deleted — anything else in the folder is reported as unmanaged and never touched.

# Manage targets
curl /api/sync/targets
curl -X POST /api/sync/targets -d '{"playlist_url": "...", "directory": "MyFolder", "name": "optional"}'
curl -X DELETE /api/sync/targets/<id>

# Dry run: what would change? (downloads/deletes nothing)
curl -X POST /api/sync/targets/<id>/preview
# -> {"to_add": [...], "to_remove": [...], "unchanged_count": N, "unmanaged": [...]}

# Reconcile (runs as a job; poll /api/jobs/<job_id>)
curl -X POST /api/sync/targets/<id>/apply -d '{"delete_removed": true}'
⁠Files
curl /api/browse                     # top-level folders/files
curl /api/browse/<dir>               # files in a folder (with tracked video_id where known)
curl /api/download/file/<dir>/<file> # fetch one MP3
curl /api/stream/<dir>/<file>        # stream inline
curl -X DELETE /api/browse/file/<dir>/<file>  # delete (also untracks + un-archives it)
curl /api/download/directory/<dir>   # folder as ZIP

Tag summary

Content type

Image

Digest

sha256:a448c6dc0…

Size

330.1 MB

Last updated

about 1 month ago

docker pull awkto/music-ripper