spot-downloader converts Spotify playlists to local M4A audio files by:
- PHASE 1: Fetching track metadata from Spotify (title, artist, album, cover, etc.)
- PHASE 2: Matching each track on YouTube Music
- PHASE 3: Downloading audio
- PHASE 4: Downloading lyrics
- PHASE 5: Embedding metadata and lyrics
The result is a collection of properly tagged M4A files ready for any music player.
- Download entire Spotify playlists or Liked Songs
- Sync mode: download new tracks and detect playlist changes
- Export: generate M3U playlists or folder copies for portability (includes LRC lyrics files)
- M4A audio (128 kbps free, 256 kbps with YouTube Premium)
- Full metadata embedding (title, artist, album, cover art, lyrics, etc.)
- Automatic lyrics fetching from multiple providers
- Multi-threaded matching and downloads (configurable separately)
- Hard link architecture: shared tracks stored once, linked to multiple playlists
- Resume interrupted downloads
- Python 3.11+
- FFmpeg
- Spotify Developer credentials
- Deno (required for some protected videos)
- Optional: YouTube Music cookies for Premium quality and age-restricted content
| Platform | Status | Notes |
|---|---|---|
| macOS | Partially tested | Edge cases not tested |
| Linux | Not tested | Tests coming soon |
| Windows | Not tested | Tests coming soon |
| Docker | In development | Cross-platform solution coming soon |
Hard Links & Symlinks:
The application uses hard links to prevent file duplication across playlists. On Windows, this may require running Command Prompt or PowerShell as Administrator.
Windows users must install FFmpeg manually and add it to PATH (installation guide).
This project was primarily developed and tested on macOS. If you encounter issues on Linux or Windows, please:
- Open an issue with details
- Include your OS version and Python version
- Share the full error message
- Pleace do not share private informations
Your feedback helps improve cross-platform compatibility!
- Download the latest release from Releases
- Extract the archive
- Install:
cd spot_downloader-1.0.1
pip install -e .git clone https://github.com/Verryx-02/spot_downloader.git
cd spot-downloader
pip install -e .Rename the config.yaml.example file in config.yaml.
- Go to Spotify Developer Dashboard
- Create a new application
- Choose an app name and description. (you can invent them)
- Set the Website to
http://localhostand the Redirect URIs tohttp://127.0.0.1:8888/callback - Copy the Client ID and Client Secret to your
config.yamlfile - Do NOT share or post these IDs with anyone, EVER.
Cookies are useful for two reasons:
| Benefit | Description |
|---|---|
| Higher quality | 256 kbps instead of 128 kbps (requires YouTube Premium) |
| Age-restricted content | Access videos that require sign-in to confirm age |
To set up cookies:
- Install a browser extension like this one
- Log in to YouTube Music
- Export cookies to a file
- Set
cookie_fileinconfig.yamlto the path of your cookies file
spot --url "https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M"spot --likedThis will open your browser for Spotify authentication (required to access your Liked Songs).
Sync mode downloads new tracks and detects changes in playlists:
# Sync a specific playlist
spot --url "https://open.spotify.com/playlist/..." --sync
# Sync ALL known playlists and Liked Songs
spot --sync
# Sync ALL playlists only (no Spotify login required)
spot --sync --no-likedExport your downloaded music for use on other devices:
# Export all playlists as M3U files
spot --export-all
# Export a specific playlist
spot --export "My Playlist"
# Export as folder copies (actual files, not links)
spot --export-all --copy-files
spot --export "My Playlist" --copy-filesLRC lyrics files are automatically included in the export if available.
You can run each phase independently if you want:
# PHASE 1: Fetch Spotify metadata
spot --1 --url "https://open.spotify.com/playlist/..."
spot --1 --liked
# PHASE 2: Match tracks on YouTube Music
spot --2
spot --2 --force-rematch # Retry failed matches
# PHASE 3: Download audio
spot --3
# PHASE 4: Fetch lyrics
spot --4
# PHASE 5: Embed metadata and lyrics
spot --5Sometimes the matching algorithm may match the Spotify song with the wrong YouTube song.
This is a problem with any matching algorithm. (You can see a formal explanation here)
If you notice that the audio track isn't the same as the one you can listen to on Spotify, you can replace it using the --replace function.
Find the link to the song you want on Spotify and run the command:
spot --replace <path of the song to replace> <youtube_link of the correct song>spot [OPTIONS]
Input Sources:
--url <spotify-url> Spotify playlist URL to download
--liked Download Liked Songs
Sync Options:
--sync Sync mode: new tracks + change detection
--no-liked Skip Liked Songs in sync mode
Export Options:
--export-all Export all playlists as M3U
--export <playlist-name> Export specific playlist as M3U
--copy-files Export as folder copies instead of M3U
Phase Selection:
--1 PHASE 1: Fetch Spotify metadata
--2 PHASE 2: Match on YouTube Music
--3 PHASE 3: Download audio
--4 PHASE 4: Fetch lyrics
--5 PHASE 5: Embed metadata and lyrics
Advanced Options:
--replace <file> <url> Replace audio in M4A file
--cookie-file <path> Path to cookies.txt
--force-rematch Retry failed YouTube matches
Info:
--version Show version and exit
--help Show this message and exit
- Audio files are stored once in
tracks/ - Playlist directories contain hard links to the master files
- If a song appears in 10 playlists, it uses disk space only once
- Deleting a playlist link doesn't delete the master file
- LRC lyrics files follow the same architecture
| Location | Format |
|---|---|
tracks/ |
{title}-{artist}.m4a |
tracks/ |
{title}-{artist}.lrc (if synced lyrics available) |
Playlists/ |
{position:05d}-{title}-{artist}.m4a |
Playlists/ |
{position:05d}-{title}-{artist}.lrc (if synced lyrics available) |
Position uses 5-digit padding (00001-99999) to support Spotify's maximum of 10,000 tracks per playlist.
| File | Description |
|---|---|
log_full.log |
Complete log of all events |
log_errors.log |
Only errors and critical issues |
download_failures.log |
Tracks whose audio download failed |
lyrics_failures.log |
Tracks whose lyrics could not be retrieved |
match_close_alternatives.log |
Tracks with multiple similar YouTube matches |
This error occurs when YouTube requires JavaScript to unlock video formats:
- Install Deno
- Re-run the song download
This video is age-restricted:
- Set up a cookie file (see YouTube Cookies section)
- Make sure you're logged into YouTube in your browser
- Re-export fresh cookies
YouTube is temporarily blocking requests:
- Wait 10-30 minutes before retrying
- Try running without cookies (you will have low audio quality)
- Verify
client_idandclient_secretinconfig.yaml - Check that your Spotify app is properly configured
- Without cookies, YouTube limits quality to 128 kbps
- Use a cookie file with YouTube Premium for 256 kbps
spot_downloader
├── Asset
│ ├── banner.png
│ ├── Demo_phase1_2.mov
│ └── support_me_on_kofi_beige.png
├── config.yaml.example
├── LICENSE
├── pyproject.toml
├── README.md
└── spot_downloader
├── __init__.py
├── cli.py # CLI entry point
├── core
│ ├── __init__.py
│ ├── config.py # Configuration loading
│ ├── database.py # SQLite database
│ ├── exceptions.py # Custom exceptions
│ ├── file_manager.py # Hard link architecture
│ ├── logger.py # Multi-file logging
│ └── progress.py # Progress bars for all phases
├── download
│ ├── __init__.py
│ ├── downloader.py # PHASE 3: Audio download
│ ├── embed_phase.py # PHASE 5: Metadata embedding
│ ├── lyrics_phase.py # PHASE 4: Lyrics fetching
│ ├── lyrics.py # Lyrics providers
│ └── metadata.py # M4A metadata handling
├── spotify
│ ├── __init__.py
│ ├── client.py # Spotify API client
│ ├── fetcher.py # PHASE 1: Metadata fetching
│ └── models.py # Track, Playlist dataclasses
├── utils
│ ├── __init__.py
│ └── replace.py # --replace functionality
└── youtube
├── __init__.py
├── matcher.py # PHASE 2: YouTube matching
└── models.py # MatchResult dataclass
8 directories, 33 filesThe following metadata is embedded in each M4A file:
| Tag | Source |
|---|---|
| Title | Spotify |
| Artist | Spotify |
| Album | Spotify |
| Album Artist | Spotify |
| Release Date | Spotify |
| Genre | Spotify (from artist) |
| Track Number | Spotify |
| Disc Number | Spotify |
| Cover Art | Spotify (downloaded) |
| Lyrics | Genius, AZLyrics, MusixMatch, Synced |
| Explicit | Spotify |
| Copyright | Spotify |
| Publisher | Spotify |
| ISRC | Spotify |
| Spotify URL | Spotify (custom tag) |
| Package | Purpose |
|---|---|
| spotipy | Spotify API client |
| ytmusicapi | YouTube Music search |
| yt-dlp | YouTube download |
| mutagen | M4A metadata |
| rapidfuzz | Fuzzy string matching |
| rich-click | CLI framework with colors |
| rich | Progress bars and formatting |
| pyyaml | Config parsing |
| syncedlyrics | Timestamped lyrics |
| requests | HTTP requests |
| beautifulsoup4 | HTML parsing |
