Files
jarri-spooty/README.md
T

241 lines
5.0 KiB
Markdown

# Jarri Spooty
Self-hosted Spotify playlist and track downloader built with NestJS + Angular.
Jarri Spooty does not download audio from Spotify itself.
It retrieves metadata from Spotify and locates matching audio on YouTube.
![Jarri Spooty Demo](docs/images/jarri-spooty-demo.png)
Deterministic Spotify metadata ingestion with duration-aware YouTube candidate scoring, paced queue execution, hardened Docker deployment, and local archival workflows.
This hardened branch focuses on:
- Large playlist support (>100 tracks)
- Spotify OAuth login flow
- Better YouTube pacing and throttling resistance
- Improved Docker deployment
- Safer credential handling
- More resilient cover-art embedding
- Better queue stability
- Deterministic YouTube fallback handling
- Explicit yt-dlp CLI execution
- Automatic failed-candidate rejection
- Improved age-gated video handling
- Persistent SQLite state across container restarts
- Deterministic Docker config persistence
- Improved operational observability
- Reduced unused dependency surface
- Hardened filename, cover-art, subprocess, and websocket boundaries
---
# Features
- Download Spotify playlists
- Download individual Spotify tracks
- Playlist auto-subscription support
- Automatic YouTube matching
- MP3 tagging and embedded cover art
- Docker deployment
- Queue-based download system
- Spotify OAuth integration
- Large playlist pagination support
- Download pacing controls
- YouTube cookie support
- Automatic YouTube retry/fallback handling
- Failed YouTube candidate rejection memory
- Deterministic yt-dlp error classification
- Hardened cover-art validation and embedding
- Explicit client-facing websocket payload shaping
---
# Important Notice
Use this software responsibly.
Only download music you legally own or are permitted to access.
The maintainers are not responsible for misuse.
---
# Supported URLs
- Spotify playlists
- Spotify tracks
Example:
https://open.spotify.com/playlist/...
https://open.spotify.com/track/...
---
# Quick Start (Recommended)
## 1. Create Spotify Developer App
Go to:
https://developer.spotify.com/dashboard
Create an application.
Add this Redirect URI:
http://127.0.0.1:3000/api/spotify/callback
Copy:
- Client ID
- Client Secret
---
## 2. Store Credentials Outside Repository
Create a secure env file:
~~~bash
sudo mkdir -p /etc/tokens
sudo tee /etc/tokens/spotify.env > /dev/null <<'EOT'
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
EOT
sudo chown root:$USER /etc/tokens/spotify.env
sudo chmod 640 /etc/tokens/spotify.env
~~~
Never commit this file.
---
## 3. Export YouTube Cookies (Recommended)
YouTube increasingly rate-limits or age-gates anonymous downloads.
Export a Netscape-format `cookies.txt` from a logged-in browser session.
Recommended storage:
~~~bash
sudo cp cookies.txt /etc/tokens/youtube.cookies.txt
sudo chown root:$USER /etc/tokens/youtube.cookies.txt
sudo chmod 640 /etc/tokens/youtube.cookies.txt
~~~
---
# Docker Run
~~~bash
docker run --rm -p 3000:3000 \
--env-file /etc/tokens/spotify.env \
-e SPOTIFY_REDIRECT_URI='http://127.0.0.1:3000/api/spotify/callback' \
-e AUTH_ENABLED=true \
-e SPOOTY_AUTH_TOKEN=change_this_token \
-e YT_SEARCH_DELAY_MS=7000 \
-e YT_DOWNLOADS_PER_MINUTE=6 \
-e YT_COOKIES_FILE=/spooty/config/youtube.cookies.txt \
-v "$PWD/downloads:/spooty/backend/downloads" \
-v "$PWD/spooty-config:/spooty/backend/config" \
-v "/etc/tokens/youtube.cookies.txt:/spooty/config/youtube.cookies.txt:ro" \
jarri-spooty:local
~~~
Open:
http://127.0.0.1:3000/?token=change_this_token
Then:
1. Click "Connect Spotify"
2. Login to Spotify
3. Approve access
4. Paste playlist URL
5. Download
---
# Deterministic YouTube Fallback Handling
The hardened branch now uses direct `yt-dlp` CLI execution rather than relying entirely on wrapper abstractions.
This provides:
- Explicit stderr visibility
- Better Docker compatibility
- Deterministic retry handling
- Automatic failed-candidate rejection
- Better age-gated video handling
- Improved operational observability
If a YouTube candidate fails:
1. The failed URL is recorded
2. The candidate is rejected
3. A new YouTube search is performed
4. The next-best valid candidate is attempted automatically
This prevents infinite retry loops against dead or restricted videos.
---
# Queue Pacing
Aggressive YouTube access can trigger:
- HTTP 302 loops
- CAPTCHA
- temporary throttling
- incomplete downloads
Recommended safe pacing:
~~~bash
-e YT_SEARCH_DELAY_MS=7000
-e YT_DOWNLOADS_PER_MINUTE=6
~~~
The hardened branch also includes additional internal pacing and retry coordination to reduce:
- repeated failed candidate loops
- aggressive retry bursts
- queue collisions
- YouTube anti-bot triggers
---
# Security Notes
Never commit:
- Spotify secrets
- OAuth tokens
- cookies.txt
- downloaded music
- local databases
- spooty-config/
Recommended `.gitignore` additions:
~~~gitignore
downloads/
config/
spooty-config/
*.sqlite
cookies.txt
.env
.env.local
~~~
---
# License
MIT