Skip to main content

Download and label albums and playlists from YouTube using yt-dlp

Project description

youtube-multi-dl

License

youtube-multi-dl makes it super easy to download and label music from YouTube. It handles single songs, playlists, and single-song albums (it splits them by chapters).

It tags them (title, artist, album, track number) so they're correctly grouped and ordered in whatever music player you use, cleans up the song titles, and gives files clean names. Output is Opus by default (excellent quality at small sizes, and copied straight from YouTube's stream without re-encoding when possible) or mp3.

It's a wrapper around the amazing yt-dlp, built to be simple to use from a shell, a script, or an AI agent — it prints a single JSON object describing what it did. It's built to be as simple as possible:

# Download and label tracks 1-10 of this playlist by "Star Band de Dakar"
youtube-multi-dl "https://www.youtube.com/watch?v=PlnanwD_vS0&index=1&list=PLcOYKKFxnwAdGh4NCgpXq_FNQoZKL6xWM" -a "Star Band de Dakar" -p "1-10"

# Download "Nilsson Schmilsson" from a single vid, split it by chapters, and label each song
youtube-multi-dl eTYushgUR00 -a "Harry Nilsson" --album "Nilsson Schmilsson"

# Download this Lucinda Williams album from a list of single-song URLs/IDs
youtube-multi-dl vWyXoGUdj4U 9R_dkP2duog qAJ8OCqe0v4 qWJCu3d6EX0 dPr0Iyh0z60 4VMUjcQ2ggs haUHiHVTvtg IOCPe_ff2RE ihuPM-xiCqY pjYxBxGSNnY HrSEeNE_Uzw cpP11qYuhg8 -a "Lucinda Williams" --album "Sweet Old World"

# Download this Pharoah Sanders album from a single vid, split it by chapters, and label each song; youtube-multi-dl guesses at the album name from the video metadata
youtube-multi-dl SDeuYY3Hi_I -a "Pharoah Sanders"

Installation

pip install youtube-multi-dl

Deps

youtube-multi-dl needs two system binaries, plus yt-dlp (installed automatically with yt-dlp[default], which also pulls in yt-dlp-ejs):

  • FFmpeg (which includes ffprobe) — to convert and split audio.
    • macOS: brew install ffmpeg · Ubuntu: sudo apt install ffmpeg
  • A JavaScript runtime — modern YouTube requires one for extraction. Deno is recommended (Node also works).

If a required binary is missing, youtube-multi-dl tells you (with an error.code of NO_FFMPEG or NO_JS_RUNTIME) instead of failing cryptically.

It's worth noting that yt-dlp occasionally "breaks", for example because YouTube changes something that prevents it from properly downloading videos. In these cases fixes appear quickly. If youtube-multi-dl suddenly stops working, try running pip install --upgrade "yt-dlp[default]" to upgrade yt-dlp.

What it does that plain yt-dlp doesn't

yt-dlp can extract audio, embed metadata, and split by chapters on its own. youtube-multi-dl is a focused convenience layer on top of it that adds:

  • Custom-timestamp splitting. Split a single "full album" video at boundaries you supply in a JSON/CSV --chapters-file — useful when the tracklist is only in the description and there are no real YouTube chapters. yt-dlp can only split by chapters that already exist.
  • Albums from a list of single-song URLs, tagged with a shared album and sequential track numbers, in one command.
  • Opinionated one-liner defaults: makes an album folder, cleans song titles (strips the artist/album out), tags everything, and gives files clean, ordered names — from just -a "Artist".
  • Automatic mode detection: you don't tell it whether the URL is a playlist, a chaptered video, or single songs — it figures it out. (It also auto-splits a single video that does have chapters, with no extra flag.)
  • Agent-friendly output: one JSON object on stdout, a stable error/exit-code contract, and idempotent re-runs.

Usage

youtube-multi-dl creates a folder named after the album and downloads tagged tracks into it, inside your current directory (or -o path). Run youtube-multi-dl -h for the full help.

It prints one JSON object to stdout (logs go to stderr), so youtube-multi-dl … 2>/dev/null | jq gives you clean JSON. Re-runs are idempotent — tracks already present (matched by an embedded youtube_video_id tag) are skipped. Exit code is 0 on success, 2 if some tracks failed, 1 on a fatal error.

Required Arguments

  • url: URL or ID of a YouTube playlist, a video with chapters, or one or more single-song URLs
  • -a ARTIST, --artist ARTIST

Optional Arguments

  • --album ALBUM (required for single-song URLs; otherwise defaults to the playlist/video title)
  • -p, --playlist-items PLAYLIST_ITEMS: playlist items to download; e.g. "1,3-5,7-9,11,12"
  • -t, --track-numbers TRACK_NUMBERS: track numbers to assign; must be the same length as the items
  • -s, --strip-patterns STRIP_PATTERNS [...]: extra regex patterns to remove from title(s)
  • --no-strip-meta: don't strip artist/album names from title(s)
  • -f, --audio-format {opus,mp3}: default opus. Opus is copied from YouTube's stream without re-encoding when possible; mp3 is for maximum device compatibility.
  • -q, --audio-quality QUALITY: a bitrate like 160K, or 09 VBR for mp3. Omit for opus to avoid re-encoding (recommended).
  • --chapters-file CHAPTERS_FILE: JSON or CSV file of chapters used to split a single video at custom timestamps; see these examples
  • -o, --output-path OUTPUT_PATH: directory in which the album directory is created
  • --force: re-download tracks even if they're already present

File names and tags

Files are named cleanly and in order, like 01 - Gotta Get Up.opus — the artist/album is stripped out of both the title tag (what your player shows) and the filename. The source video is not lost: it's stored in a youtube_video_id tag on each file (yt-dlp also embeds the source URL), which is how re-runs know what's already been downloaded.

Use with AI agents

Because the CLI is non-interactive and emits schema-stable JSON, an agent can drive it directly — e.g. "download this album to ~/Music", a YouTube URL, or a CSV of albums to fetch one by one. This repo ships a skill that teaches an agent the workflows, the output schema, and the error codes.

Claude Code — copy or symlink the skill into your skills directory:

# user-wide (or use a project's .claude/skills/ instead)
mkdir -p ~/.claude/skills
ln -s "$(pwd)/skills/youtube-multi-dl" ~/.claude/skills/youtube-multi-dl

The skill is plain Markdown, so it works with other agent harnesses (e.g. Codex) too — point your tool at skills/youtube-multi-dl/SKILL.md.

Contributing

Fork the repo and submit a PR. Create an issue if something is broken!

Development

This project uses uv for packaging and development. Run uv sync to set things up:

uv sync

By default this creates a virtual environment at .venv in the repo root (the standard location uv uses) and installs all dependencies into it, including dev tools (ruff, pyright). pyrightconfig.json points pyright at this .venv, so type checking can resolve yt-dlp, mutagen, and the other deps without any extra configuration.

Run tools inside the environment with uv run (e.g. uv run pyright), or activate it with source .venv/bin/activate.

See main.py in the root of the repo? This script makes it easy to test the package. It ensures youtube-multi-dl can be invoked from the command line, without going through the shim created when the package is installed.

For example, from the root of the repo, just run uv run main.py SDeuYY3Hi_I -a "Pharoah Sanders".

Run cd .git/hooks && ln -s -f ../../pre-push to add the pre-push hook to ensure you can't push anything that doesn't pass ruff and pyright checks.

Style

Uses ruff for formatting, linting, and import sorting.

  • uv run ruff format . to format source files in place
  • uv run ruff check . to lint (add --fix to auto-fix)
  • uv run pyright to type-check
  • uv run pytest to type-check

Wish List

Some single-song albums aren't divided into chapters, like this one. But if you look at the description, it clearly has metadata about the songs in the album. Can we find and parse this metadata so youtube-multi-dl can split videos like this into individual songs, the way it does for videos with chapters?

Release/Deploy to PyPI

uv build
uv publish

License

This code is licensed under the MIT License.

Thanks

To the maintainers of yt-dlp, Mutagen and FFmpeg, and to anyone who doesn't want every last song to disappear behind ads or a paywall.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

youtube_multi_dl-2.0.0.tar.gz (66.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

youtube_multi_dl-2.0.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file youtube_multi_dl-2.0.0.tar.gz.

File metadata

  • Download URL: youtube_multi_dl-2.0.0.tar.gz
  • Upload date:
  • Size: 66.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for youtube_multi_dl-2.0.0.tar.gz
Algorithm Hash digest
SHA256 b27a67aefd68f724690ee3bbb88e38924ad3e27520c01b085207013c4a685ca2
MD5 8821123d0e355d13ab4d648d72d3596d
BLAKE2b-256 dda11fd9ba3710ce2347939faeeb2d8e86d5a4995d5847ab21ae5567b49bb105

See more details on using hashes here.

File details

Details for the file youtube_multi_dl-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: youtube_multi_dl-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for youtube_multi_dl-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7facb154a845e1148075f8187f195712f2294e0eadae8164bea0f94c787009b2
MD5 a0cb8a131f502a0c9e67b230232950f9
BLAKE2b-256 c5f1b9ce494aef2fab13b04904c5702eaaeeb42e283eada40f1e5bb7be6cad80

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page