A powerful CLI tool to download, analyze, and intelligently organize your entire Spotify music library using AI.
- Batch Download System: Downloads your Spotify liked songs in configurable batches (default: 50 songs) - dramatically faster than single-playlist downloads
- Auto-Resume: Interrupted? No problem. Just run again and it continues exactly where it stopped
- Incremental Downloads: After the first run, only downloads newly liked songs - saves time and bandwidth
- Smart File Scanning: Automatically scans output directory for existing files and skips re-downloading them
- State Tracking: Automatic progress saving after each batch
- Intelligent App Rotation: Automatically detects rate limits and switches to different Spotify apps
- Auto Rate Limit Recovery: When an app hits rate limit, system marks it, switches to another, and auto-recovers after cooldown
- Smart Caching: Leverages snapshots to avoid unnecessary API calls
- Consolidated Error Logging: All failed/not found tracks saved to a single timestamped error file per download session
- Genre Extraction: Scans your MP3 library and extracts all unique genres from ID3 tags
- Comprehensive Scanning: Recursively analyzes entire directory structures
- Metadata Parsing: Reads title, artist, album, genre, and year from MP3 files
- LLM Genre Consolidation: Uses OpenAI's Structured Outputs to intelligently consolidate hundreds of genres into optimal major categories
- Flexible Category Count: AI determines the best number of categories for your collection (no artificial limits)
- Complete Genre Mappings: Every genre mapped to a category with detailed reasoning
- Guaranteed Valid JSON: Structured Outputs ensures reliable, parseable responses
- Smart Classification: AI determines the best genre folder for each song based on metadata
- โก Async Processing: Concurrent API requests for 10-20x faster organization (5100 files in ~8 minutes vs 90 minutes)
- Configurable Concurrency: Control speed vs API load (default: 50 concurrent requests)
- Decision Caching: Remembers previous classifications to minimize API costs
- Fallback Logic: Built-in genre matching when LLM is unavailable
- Automatic Folder Creation: Creates organized genre-based folder structures
- Dry Run Mode: Preview all changes before moving files
- Duplicate Handling: Automatically renames duplicates instead of overwriting
- Safe File Operations: Move operations with error handling and traceback
- Rich Terminal UI: Color-coded output with progress bars
- Real-time Progress: Live spotdl output during downloads
- Detailed Statistics: See batch completion, failed downloads, and category distributions
- Interactive Prompts: User-friendly confirmations and setup wizard
- Python 3.11+ (Download)
- uv - Fast Python package manager (Install)
- Spotify Developer Account (Create App)
- OpenAI API Key (Get Key)
- Clone the repository
git clone <repository-url>
cd spotify-download
- Install dependencies with uv
uv sync
This installs all required packages:
spotdl- Downloads music from Spotify/YouTubespotipy- Spotify Web API clientclick- CLI frameworkmutagen- MP3 metadata handlingopenai- OpenAI API clientpython-dotenv- Environment variable managementrich- Terminal UI formatting
- Run the setup wizard
uv run spotify-dl setup
This interactive wizard will guide you through:
- Entering your Spotify Client ID and Secret
- Entering your OpenAI API key
- Optionally setting default download and organization paths
- Go to Spotify Developer Dashboard
- Log in with your Spotify account
- Click "Create App"
- Fill in:
- App Name: "Spotify Downloader" (or any name)
- App Description: "Personal music downloader"
- Redirect URI:
https://httpbin.org/anything(Important!)
- Accept terms and create
- Click "Settings" to view your Client ID and Client Secret
- Copy both values for the setup wizard
Pro Tip: Create 3-5 separate apps for app rotation to avoid rate limits (see Advanced Setup below)
- Go to OpenAI Platform
- Sign up or log in
- Click "Create new secret key"
- Name it (e.g., "Spotify Organizer")
- Copy the key immediately (you won't see it again!)
- Add credits to your account if needed
Create a .env file in the project root:
# Spotify API Credentials
SPOTIFY_CLIENT_ID=your_client_id_here
SPOTIFY_CLIENT_SECRET=your_client_secret_here
SPOTIFY_REDIRECT_URI=https://httpbin.org/anything
# OpenAI API Key
OPENAI_API_KEY=your_openai_api_key_here
# Optional: Default Paths
DEFAULT_DOWNLOAD_PATH=/Volumes/ExternalHDD/music-downloads
DEFAULT_ORGANIZE_PATH=/Volumes/ExternalHDD/music-organized
# First time download - fetches all your liked songs
uv run spotify-dl download --output ~/Music/Spotify
# Check download progress
uv run spotify-dl status
# If interrupted, resume from where you left off
uv run spotify-dl download
# Later downloads - only fetches NEW songs you've liked since last time
uv run spotify-dl download
What happens during download:
- Opens browser for Spotify authentication (first time only)
- Fetches your liked songs (or checks for new ones)
- Creates a timestamped error log file (
.spotdl_errors_YYYYMMDD_HHMMSS.txt) - Creates temporary playlists in batches of 50 songs
- Downloads each batch using spotdl with live streaming output
- Scans output directory for existing files and skips them automatically
- Appends any failed/not found tracks to the consolidated error file with batch context
- Deletes temporary playlist
- Saves progress after each batch
- Repeats until all songs are downloaded
- Removes error file if no errors occurred
Download Options:
# Custom batch size (smaller for slower connections, larger for faster)
uv run spotify-dl download --batch-size 25
# Download ALL songs again (ignore incremental mode)
uv run spotify-dl download --full
# Disable file scanning (faster but may re-download existing files)
# Recommended for incremental mode where we already know which tracks are new
uv run spotify-dl download --no-scan
# Combine options for maximum speed in incremental mode
uv run spotify-dl download --no-scan
# Full mode without scanning (faster but less safe)
uv run spotify-dl download --full --no-scan
# Start fresh (ignore resume state)
uv run spotify-dl download --no-resume
# Specify output directory
uv run spotify-dl download --output /path/to/music
Test Your Setup:
# Download just 10 songs to test your configuration
uv run spotify-dl test-download --size 10
# Test a specific batch
uv run spotify-dl test-download --batch 2 --size 5
# Scan your downloaded music and extract all unique genres
uv run spotify-dl analyze ~/Music/Spotify
# Optionally specify custom output file
uv run spotify-dl analyze ~/Music/Spotify --output data/my_genres.txt
Output: Creates data/unique_genres.txt containing all unique genres found in your MP3 files.
# Use OpenAI Structured Outputs to create optimal major genre categories
# AI determines the best number based on your collection's diversity
uv run spotify-dl consolidate-genres
# Use GPT-4 for better categorization (more expensive but more accurate)
uv run spotify-dl consolidate-genres --model gpt-4o
# Custom input/output files
uv run spotify-dl consolidate-genres \
--input data/my_genres.txt \
--output data/my_categories.json
Output: Creates data/major_genre_categories.json with AI-recommended major categories, complete mappings, and reasoning:
{
"timestamp": "2025-10-13 14:30:00",
"status": "success",
"major_categories": [
"Rock",
"Pop",
"Electronic",
"HipHop",
"Jazz",
"Classical",
"Country",
"Folk",
"RnB",
"Metal",
"Alternative",
"Reggae",
"World",
"Blues"
],
"consolidation_notes": "Created 14 major categories based on distinct musical traditions...",
"genre_mappings": [
{
"original_genre": "indie rock",
"major_category": "Alternative",
"reasoning": "Indie rock is a subgenre of alternative rock with DIY ethos"
},
{
"original_genre": "deep house",
"major_category": "Electronic",
"reasoning": "Deep house is an electronic dance music subgenre characterized by..."
}
],
"total_categories": 14,
"total_genres_mapped": 228
}
Key Features:
- ๐ฏ Adaptive: AI determines optimal number of categories (no forced count)
- ๐ Complete Mappings: See exactly where each genre goes and why
- โ Guaranteed Valid: Structured Outputs ensures proper JSON format
- ๐ Detailed Reasoning: Understand the AI's consolidation strategy
Pro Tip: Review and edit this file before organizing - you can rename or add categories!
# Create genre folders based on AI categories
uv run spotify-dl create-folders ~/Music/Organized
# Use custom categories file
uv run spotify-dl create-folders ~/Music/Organized \
--categories data/my_categories.json
Result: Creates folder structure like:
~/Music/Organized/
โโโ Rock/
โโโ Pop/
โโโ Electronic/
โโโ HipHop/
โโโ Jazz/
โโโ Classical/
โโโ Country/
โโโ Folk/
โโโ RnB/
โโโ Metal/
โโโ Alternative/
โโโ Reggae/
โโโ World/
โโโ Blues/
โโโ Indie/
# DRY RUN: Preview where files will be moved (HIGHLY RECOMMENDED)
uv run spotify-dl organize ~/Music/Spotify ~/Music/Organized --dry-run
# If preview looks good, organize for real (uses async processing - FAST!)
uv run spotify-dl organize ~/Music/Spotify ~/Music/Organized
# High performance mode (100 concurrent API requests)
uv run spotify-dl organize ~/Music/Spotify ~/Music/Organized --concurrency 100
# Conservative mode (25 concurrent if concerned about API limits)
uv run spotify-dl organize ~/Music/Spotify ~/Music/Organized --concurrency 25
# Use custom categories
uv run spotify-dl organize ~/Music/Spotify ~/Music/Organized \
--categories data/my_categories.json
What happens during organization:
- Scans all MP3 files in source directory
- Extracts metadata (title, artist, album, genre, year)
- Processes files concurrently (default: 50 at a time):
- First tries quick genre matching
- If no match, asks OpenAI which folder it belongs in
- Caches the decision for future runs
- All running in parallel for maximum speed
- Moves files to appropriate genre folders
- Shows detailed statistics including cache hit rate and performance metrics
โก Performance: With async processing, 5100 files complete in ~8 minutes (vs 90 minutes before)!
--dry-run first! This moves files, so preview before committing.
# Check current download status
uv run spotify-dl status
# Clear download state and start fresh
uv run spotify-dl clear-state
# Force clear without confirmation
uv run spotify-dl clear-state --confirm
# View app rotation statistics and rate-limited apps
uv run spotify-dl rotation-stats
# Reset app usage counters and clear rate limits
uv run spotify-dl reset-rotation
# Force reset without confirmation
uv run spotify-dl reset-rotation --confirm
Example rotation-stats output:
App Rotation Statistics
Last used: Secondary App
Usage stats:
Primary App: 15 batches
Secondary App: 16 batches
Tertiary App: 14 batches
Rate-Limited Apps:
Primary App: 45 minutes remaining
โ System will auto-switch to available apps
If you're downloading large libraries (1000+ songs), set up multiple Spotify apps to avoid rate limits:
-
Create multiple Spotify apps (3-5 recommended):
- Go to Spotify Developer Dashboard
- Create 3-5 separate apps
- For each app, copy the Client ID and Client Secret
-
Create
spotify_apps.json:cp spotify_apps.example.json spotify_apps.json -
Edit
spotify_apps.jsonwith your app credentials:{ "apps": [ { "name": "Primary App", "client_id": "your_client_id_1", "client_secret": "your_client_secret_1" }, { "name": "Secondary App", "client_id": "your_client_id_2", "client_secret": "your_client_secret_2" }, { "name": "Tertiary App", "client_id": "your_client_id_3", "client_secret": "your_client_secret_3" } ] }
Benefits:
- ๐ Automatic rotation between apps per batch
- ๐ Extended rate limits (each app has separate quota)
- ๐ก๏ธ Prevents API throttling
- โก No manual switching required
- ๐ฏ Smart rotation prevents using same app 3x in a row
- ๐ Auto rate limit detection: Detects when an app hits limits during download
- โป๏ธ Automatic retry: Switches to different app and retries the batch
- โฐ Cooldown management: Rate-limited apps auto-recover after 1 hour
- ๐ Tracking: See which apps are rate-limited with
rotation-stats
How it works:
- Each batch uses a different app
- System tracks usage and balances load
- Automatically avoids overusing any single app
- Detects rate limits in real-time from spotdl output
- Marks rate-limited apps and excludes them from rotation
- Automatically retries failed batches with a different app (up to 3 attempts)
- Auto-recovery: Apps return to rotation after 1 hour cooldown
- View statistics with
spotify-dl rotation-stats
| Command | Description | Options |
|---|---|---|
download |
Download all liked songs from Spotify | --output, --batch-size, --no-resume, --full, --no-scan |
test-download |
Test download with small batch | --batch, --size |
status |
Show download progress and state | None |
clear-state |
Clear download state and start fresh | --confirm |
| Command | Description | Options |
|---|---|---|
analyze |
Extract unique genres from MP3 files | --output |
consolidate-genres |
Create major categories with AI (Structured Outputs) | --input, --output, --model |
create-folders |
Create genre folder structure | --categories |
organize |
Organize music into genre folders (async, fast!) | --categories, --dry-run, --concurrency |
| Command | Description | Options |
|---|---|---|
setup |
Interactive setup wizard | None |
rotation-stats |
Show app rotation statistics | None |
reset-rotation |
Reset app usage counters | --confirm |
spotify-download/
โโโ src/ # Source code
โ โโโ cli.py # CLI interface & commands
โ โโโ config.py # Configuration & environment variables
โ โโโ spotify_client.py # Spotify API wrapper (spotipy)
โ โโโ downloader.py # Batch download logic
โ โโโ app_rotator.py # Multi-app rotation system
โ โโโ spotdl_config_manager.py # spotdl configuration management
โ โโโ state_manager.py # Download state persistence
โ โโโ analyzer.py # MP3 metadata & genre analysis
โ โโโ genre_consolidator.py # LLM-based genre consolidation
โ โโโ organizer.py # Music organization & file moving
โโโ data/ # Generated data (auto-created)
โ โโโ unique_genres.txt # Extracted genres from analyze
โ โโโ major_genre_categories.json # AI-generated categories
โ โโโ liked_tracks.json # Snapshot of liked songs
โ โโโ download_state.json # Download progress tracking
โโโ spotify_apps.json # Multi-app credentials (create this)
โโโ spotify_apps.example.json # Template for spotify_apps.json
โโโ .app_rotation_state.json # Rotation state (auto-generated)
โโโ .env # Environment variables (create this)
โโโ env.example # Template for .env
โโโ pyproject.toml # Project dependencies (uv)
โโโ uv.lock # Locked dependencies
โโโ README.md # This file
| Variable | Required | Default | Description |
|---|---|---|---|
SPOTIFY_CLIENT_ID |
Yes | - | Your Spotify app client ID |
SPOTIFY_CLIENT_SECRET |
Yes | - | Your Spotify app client secret |
SPOTIFY_REDIRECT_URI |
No | https://httpbin.org/anything |
OAuth redirect URI |
OPENAI_API_KEY |
Yes* | - | OpenAI API key (*for AI features only) |
DEFAULT_DOWNLOAD_PATH |
No | - | Default directory for downloads |
DEFAULT_ORGANIZE_PATH |
No | - | Default directory for organized music |
| File | Purpose | Auto-Created |
|---|---|---|
data/download_state.json |
Tracks download progress and completed batches | Yes |
data/liked_tracks.json |
Snapshot of your liked songs | Yes |
.app_rotation_state.json |
App rotation usage statistics | Yes |
.spotify_cache |
Spotify OAuth token cache | Yes |
data/unique_genres.txt |
Extracted genres from analyze command | Yes |
data/major_genre_categories.json |
AI-generated major categories | Yes |
.spotdl_errors_YYYYMMDD_HHMMSS.txt |
Consolidated error log with timestamp (in download directory) | Yes |
โ Do:
- Use external HDD for large libraries to save SSD space
- Start with
test-downloadto verify setup - Run
statusperiodically to check progress - Set up app rotation for libraries with 1000+ songs
- Let it run in the background - state is saved automatically
- Use incremental mode (default) for regular updates - only downloads NEW songs
- Add
--no-scanin incremental mode for faster downloads (we know which tracks are new) - Keep scanning enabled in
--fullmode for safety (or use--no-scanfor speed)
โ Don't:
- Don't worry about interruptions - just run again to resume
- Don't use
--fullevery time - incremental mode is much faster - Don't delete
data/download_state.jsonunless you want to start over - Don't use very large batch sizes (>200) - smaller is more reliable
- Scanning enabled (default): Slower but safer, scans all files in directory
- Scanning disabled (
--no-scan): Faster, trusts incremental mode tracking - Best practice: Use
--no-scanfor incremental, keep it for--fullmode
โ Do:
- Always run with
--dry-runfirst - Review
data/major_genre_categories.jsonbefore organizing - Keep backups of your original music files
- Edit category names to your preference before organizing
- Use descriptive paths for better organization
โ Don't:
- Don't organize without testing on a small sample first
- Don't skip the dry run - files will be moved!
- Don't delete the categories JSON - it's needed for organization
- โก Async Organization: Use concurrent processing for 10-20x faster organization
- 5100 files: ~8 minutes with async vs ~90 minutes sequential
- Adjust concurrency with
--concurrencyflag (default: 50) - Higher = faster but more API load (max: 100)
- Lower = more conservative (min: 10)
- LLM Caching: Decision cache reduces API calls by ~90% on re-runs
- Quick Matching: Simple genre matching happens before LLM calls
- Incremental Mode: After first download, only fetches new songs
- App Rotation: Distributes load across multiple Spotify apps
- Batch Processing: Smaller batches = more reliable downloads
Real-world example: Organizing 5100 files
- First run (no cache): ~8 minutes with 50 concurrent requests
- Second run (90% cached): ~1 minute
- With 100 concurrent: ~5 minutes first run
"uv: command not found"
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or with pip
pip install uv
"Python 3.11 required"
# Install Python 3.11+ from python.org
# Or use pyenv
pyenv install 3.11
pyenv global 3.11
"uv sync fails"
# Clear cache and retry
rm -rf .venv
uv sync
"Spotify credentials not set"
- Ensure
.envfile exists - Verify
SPOTIFY_CLIENT_IDandSPOTIFY_CLIENT_SECRETare set - Check credentials are from developer.spotify.com/dashboard
"Invalid redirect URI"
- In Spotify app settings, add
https://httpbin.org/anythingto Redirect URIs - Must match exactly (including https://)
"OpenAI authentication failed"
- Verify
OPENAI_API_KEYin.env - Check API key has credits at platform.openai.com
- Ensure key hasn't expired
"spotdl command not found"
# Reinstall dependencies
uv sync
"Download is very slow"
- Reduce batch size:
--batch-size 25 - Check internet connection
- Some songs may take longer to find on YouTube
- Consider app rotation for better reliability
"Rate limit exceeded"
- With app rotation: System automatically detects and switches apps - just let it run!
- Without app rotation: Set up multiple apps (see Advanced Configuration)
- Rate-limited apps are automatically excluded for 1 hour
- Check which apps are rate-limited:
spotify-dl rotation-stats - System will retry with different apps automatically (up to 3 attempts per batch)
"Some songs fail to download"
- Normal - not all Spotify songs are on YouTube
- Duplicates are NOT errors: Songs already downloaded are automatically skipped (shown as "duplicate")
- Actual errors are logged to a single timestamped file:
.spotdl_errors_YYYYMMDD_HHMMSS.txt - Each batch's errors are appended with batch number and timestamp
- If no errors occur, the error file is automatically deleted
- Check error file to see which specific tracks failed and in which batch
- Failed batches are tracked in state
- Re-run to retry failed batches
"LLM consolidation issues"
- Invalid JSON: Now virtually impossible - Structured Outputs guarantees valid format
- Poor quality categories: Try
gpt-4oinstead ofgpt-4o-mini:--model gpt-4o - API errors: Check OpenAI API key has credits
- Model refusal: Check the output for refusal messages (rare but possible)
"Files not moving"
- Check source and destination paths exist
- Ensure you have write permissions
- Remove
--dry-runflag to actually move files
"Genre not detected"
- Some MP3s may lack genre tags
- Tool tries to extract from filename
- Falls back to "Unknown" or first category
# Setup
uv run spotify-dl setup
# Create app rotation (optional but recommended)
cp spotify_apps.example.json spotify_apps.json
# Edit with 3-5 app credentials
# Download to external HDD
uv run spotify-dl download --output /Volumes/ExternalHDD/spotify-music
# If interrupted, resume:
uv run spotify-dl download
# Check progress
uv run spotify-dl status
# Initial download (downloads all)
uv run spotify-dl download --output ~/Music/Spotify
# Weekly updates (only downloads new songs!)
uv run spotify-dl download
# This is fast - only fetches songs liked since last run
# Step 1: Analyze existing music
uv run spotify-dl analyze ~/Music/UnsortedMusic
# Step 2: Generate categories with AI
uv run spotify-dl consolidate-genres
# Step 3: Review and edit data/major_genre_categories.json if needed
# Step 4: Create folders
uv run spotify-dl create-folders ~/Music/SortedMusic
# Step 5: Preview organization
uv run spotify-dl organize ~/Music/UnsortedMusic ~/Music/SortedMusic --dry-run
# Step 6: Organize for real
uv run spotify-dl organize ~/Music/UnsortedMusic ~/Music/SortedMusic
# 1. Initial setup
uv run spotify-dl setup
# 2. Download all liked songs
uv run spotify-dl download --output ~/Music/Raw
# 3. Analyze the music
uv run spotify-dl analyze ~/Music/Raw
# 4. Create AI categories
uv run spotify-dl consolidate-genres
# 5. Create folder structure
uv run spotify-dl create-folders ~/Music/Organized
# 6. Organize (dry run first!)
uv run spotify-dl organize ~/Music/Raw ~/Music/Organized --dry-run
# 7. Actually organize
uv run spotify-dl organize ~/Music/Raw ~/Music/Organized
# 8. Future updates - just download new songs
uv run spotify-dl download # Fast incremental update!
Contributions are welcome! Areas for improvement:
- Additional music sources beyond Spotify
- Support for playlist downloads
- Custom genre mapping rules
- Parallel download processing
- Web UI interface
- Additional metadata sources
MIT License - feel free to use, modify, and distribute!
- spotdl - Excellent Spotify/YouTube downloader
- spotipy - Spotify Web API wrapper
- OpenAI - AI-powered genre classification
- Rich - Beautiful terminal formatting
- Click - CLI framework
- Mutagen - MP3 metadata handling
- uv - Fast Python package manager
Having issues? Check:
- This README's troubleshooting section
- Verify all prerequisites are installed
- Ensure API credentials are valid
- Run with
--helpon any command for detailed options
Planned features:
- Support for multiple Spotify users
- Playlist-specific downloads
- Advanced filtering (by genre, year, artist)
- Music quality selection (bitrate)
- Duplicate detection across library
- Smart playlist generation
- Web dashboard for monitoring
- Docker container support
Made with โค๏ธ for music lovers who like organized libraries
Happy Listening! ๐ต