Skip to content
/ yubal Public

Self-hosted YouTube Music downloader with automatic metadata tagging and playlist sync

License

Notifications You must be signed in to change notification settings

guillevc/yubal

Repository files navigation

yubal

Self-hosted YouTube Music downloader with automatic metadata tagging and playlist sync.

Paste a link, get a properly tagged and organized library. Subscribe to playlists to keep them synced. Albums sorted by artist and year. No duplicates. Media server ready.

CI Release Docker codecov

yubal demo

Important

Upgrading from v0.3? Folder and file names now preserve unicode characters (BjorkBjörk), which may create duplicates alongside existing ASCII-named items.

To keep the previous ASCII behavior, set YUBAL_ASCII_FILENAMES=true. Otherwise, check your library and merge any duplicates after upgrading.

📖 How It Works

Downloading music is easy. Organizing it is the hard part.

yubal takes a YouTube Music URL and produces a clean, tagged music library:

data/
├── Pink Floyd/
│   └── 1973 - The Dark Side of the Moon/
│       ├── 01 - Speak to Me.opus
│       ├── 01 - Speak to Me.lrc
│       ├── 02 - Breathe.opus
│       ├── 02 - Breathe.lrc
│       └── cover.jpg
│
├── Radiohead/
│   └── 1997 - OK Computer/
│       ├── 01 - Airbag.opus
│       ├── 01 - Airbag.lrc
│       ├── 02 - Paranoid Android.opus
│       ├── 02 - Paranoid Android.lrc
│       └── cover.jpg
│
└── _Playlists/
    ├── My Favorites [n2g-XhDv].m3u
    └── My Favorites [n2g-XhDv].jpg

When downloading a playlist, each track goes to its album folder—the M3U file just references them:

#EXTM3U
#EXTINF:239,Pink Floyd - Breathe
../Pink Floyd/1973 - The Dark Side of the Moon/02 - Breathe.opus
#EXTINF:386,Radiohead - Paranoid Android
../Radiohead/1997 - OK Computer/02 - Paranoid Android.opus

✨ Features

  • Web UI — Real-time progress, job queue, responsive design
  • Albums, playlists & tracks — Paste any YouTube Music link, get organized files
  • Scheduled sync — Subscribe to playlists; new tracks download automatically
  • Smart deduplication — Same track across 10 playlists? Stored once, referenced everywhere
  • Reliable downloads — Automatic retry on failures, graceful cancellation
  • Automatic lyrics — Synced .lrc files downloaded alongside tracks when available
  • ReplayGain tagging — Track and album ReplayGain/R128 tags for consistent playback volume
  • Format options — Native opus (best quality), mp3, or m4a (direct download when available, transcoded otherwise)
  • Media server ready — Tested with Navidrome, Jellyfin, and Gonic
  • CLI — Download and inspect metadata from the terminal

🚀 Quick Start

# compose.yaml
services:
  yubal:
    image: ghcr.io/guillevc/yubal:latest
    container_name: yubal
    user: 1000:1000
    ports:
      - 8000:8000
    environment:
      YUBAL_SCHEDULER_CRON: "0 0 * * *"
      YUBAL_DOWNLOAD_UGC: false
      YUBAL_TZ: UTC
    volumes:
      - ./data:/app/data
      - ./config:/app/config
    restart: unless-stopped

Tip

Volume permissions: The container runs as UID:GID 1000:1000 by default. If your host user has a different UID, either:

  • Change user: to match your UID:GID (run id to check), or
  • Set ownership on the volume directories: chown 1000:1000 -R data config
docker compose up -d
# Open http://localhost:8000

⚙️ Configuration

Variable Description Default (Docker)
YUBAL_AUDIO_FORMAT opus, mp3, or m4a opus
YUBAL_AUDIO_QUALITY Transcode quality (0=best, 10=worst) 0
YUBAL_SCHEDULER_ENABLED Enable automatic scheduled sync true
YUBAL_SCHEDULER_CRON Cron schedule for auto-sync 0 0 * * *
YUBAL_FETCH_LYRICS Fetch lyrics from lrclib.net true
YUBAL_DOWNLOAD_UGC Download user-generated content to _Unofficial/ false
YUBAL_REPLAYGAIN Apply ReplayGain tags to downloads true
YUBAL_TZ Timezone (IANA format) UTC
All options
Variable Description Default (Docker)
YUBAL_HOST Server bind address 127.0.0.1
YUBAL_PORT Server port 8000
YUBAL_DATA Music library output /app/data
YUBAL_CONFIG Config directory /app/config
YUBAL_LOG_LEVEL DEBUG, INFO, WARNING, ERROR INFO
YUBAL_ASCII_FILENAMES Transliterate unicode to ASCII false
YUBAL_CORS_ORIGINS Allowed CORS origins ["*"]
YUBAL_TEMP Temp directory System temp

🔌 Media Server Integration

Tested with Navidrome, Jellyfin, and Gonic. Artists link correctly, even on tracks with multiple artists.

Server Artist linking Playlists
Navidrome ✅ Works out of the box
Jellyfin ⚙️ Enable "Use non-standard artists tags" in library settings
Gonic ⚙️ Set GONIC_MULTI_VALUE_ARTIST=multi

✅ Supported · ⚙️ Requires configuration · ❌ Not supported

Detailed setup guides

Navidrome

No configuration required. Optionally, make imported playlists public:

ND_DEFAULTPLAYLISTPUBLICVISIBILITY=true

See Navidrome docs.

Jellyfin

For multi-artist support:

  1. Dashboard → Libraries → Music Library → Manage Library
  2. Check Use non-standard artists tags
  3. Save and rescan

Gonic

For artist linking:

GONIC_MULTI_VALUE_ARTIST=multi
GONIC_MULTI_VALUE_ALBUM_ARTIST=multi

M3U playlists are not supported (pending PR).

🍪 Cookies (Optional)

Need age-restricted content, private playlists, or Premium quality? Add your cookies:

  1. Export https://www.youtube.com/ cookies with a browser extension (yt-dlp guide)
  2. Place at config/ytdlp/cookies.txt or upload via the web UI

Caution

Cookie usage may trigger stricter rate limiting and could put your account at risk. See #3 and yt-dlp wiki.

🗺️ What's Coming

  • Playlist support with M3U generation (v0.2.0)
  • Single track downloads (v0.3.0)
  • Automatic lyrics (.lrc) (v0.3.0)
  • Auto-sync playlists (v0.4.0)
  • UGC tracks (user-generated content, remixes, unofficial tracks) (v0.5.0)
  • Flat folder mode
  • Browser extension
  • Post-download webhooks
  • New music automatic discovery

💜 Support

If yubal is useful to you, consider supporting its development:

ko-fi Sponsor

A ⭐ also helps others discover yubal!

📈 Star History

Star History Chart

🙏 Acknowledgments

Built with yt-dlp and ytmusicapi.

Thanks to everyone who's starred, shared, reported bugs, suggested features, or supported the project 💝

License

MIT


For personal archiving only. Comply with YouTube's Terms of Service and applicable copyright laws.