Files
jarri-spooty/README.md
T

6.5 KiB

Spooty Hardened

Self-hosted Spotify playlist and track downloader built with NestJS + Angular.

Spooty does not download audio from Spotify itself.
It retrieves metadata from Spotify and locates matching audio on YouTube.

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

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

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:

sudo mkdir -p /etc/tokens

sudo tee /etc/tokens/spotify.env > /dev/null <<'EOF'
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
EOF

sudo chown root:$USER /etc/tokens/spotify.env
sudo chmod 640 /etc/tokens/spotify.env

Never commit this file.


Docker Run

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=3 \
  -v "$PWD/downloads:/spooty/backend/downloads" \
  spootyfy-hardened: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

Docker Compose

services:
  spooty:
    image: spootyfy-hardened:local
    container_name: spooty
    restart: unless-stopped

    ports:
      - "3000:3000"

    env_file:
      - /etc/tokens/spotify.env

    environment:
      SPOTIFY_REDIRECT_URI: "http://127.0.0.1:3000/api/spotify/callback"
      AUTH_ENABLED: "true"
      SPOOTY_AUTH_TOKEN: "change_this_token"

      YT_SEARCH_DELAY_MS: "7000"
      YT_DOWNLOADS_PER_MINUTE: "3"

    volumes:
      - ./downloads:/spooty/backend/downloads

Build From Source

Requirements

  • Node.js 20.20.0
  • Docker
  • ffmpeg
  • Python3
  • Redis

Build

npm install
npm run build

Run

npm run start

Environment Variables

Variable Default Description
DB_PATH ./config/db.sqlite SQLite database path
DOWNLOADS_PATH ./downloads Download directory
FORMAT mp3 Output format
REDIS_HOST localhost Redis host
REDIS_PORT 6379 Redis port
SPOTIFY_CLIENT_ID unset Spotify client ID
SPOTIFY_CLIENT_SECRET unset Spotify client secret
SPOTIFY_REDIRECT_URI unset OAuth callback URI
AUTH_ENABLED false Enable frontend token auth
SPOOTY_AUTH_TOKEN unset Frontend auth token
YT_SEARCH_DELAY_MS 5000 Delay between YouTube searches
YT_DOWNLOADS_PER_MINUTE 3 Download pacing
YT_COOKIES unset Browser cookie extraction
YT_COOKIES_FILE unset Netscape cookies.txt path

YouTube Cookies

YouTube may throttle or block requests without cookies.

Two supported methods exist.


Browser Cookies (Native Install Only)

YT_COOKIES=firefox

Supported:

  • firefox
  • chrome
  • chromium
  • edge
  • brave
  • opera

This does NOT work reliably in Docker.


Export a Netscape-format cookies.txt.

Mount into container:

-v /path/to/cookies.txt:/spooty/config/cookies.txt

Then:

-e YT_COOKIES_FILE=/spooty/config/cookies.txt

Large Playlist Support

The hardened branch fixes several Spotify API limitations:

  • Proper playlist pagination
  • 100 track playlists

  • OAuth-authenticated playlist access
  • Queue stability improvements

Verified working with playlists containing 300+ tracks.


Queue Pacing

Aggressive YouTube access can trigger:

  • HTTP 302 loops
  • CAPTCHA
  • temporary throttling
  • incomplete downloads

Recommended safe pacing:

-e YT_SEARCH_DELAY_MS=7000
-e YT_DOWNLOADS_PER_MINUTE=3

Security Notes

Never commit:

  • Spotify secrets
  • OAuth tokens
  • cookies.txt
  • downloaded music
  • local databases

Recommended .gitignore additions:

downloads/
config/
*.sqlite
cookies.txt
.env
.env.local

Hardened Branch Changelog

Unreleased Hardened Changes

Spotify

  • Added Spotify OAuth login flow
  • Added persistent Spotify user token storage
  • Added authenticated playlist retrieval
  • Fixed large playlist pagination
  • Fixed playlist truncation at 100 tracks
  • Added playlist retrieval debugging
  • Added OAuth status endpoint

YouTube

  • Added configurable search pacing
  • Added configurable download pacing
  • Reduced aggressive YouTube request bursts
  • Improved queue throttling behavior

Backend

  • Added safer queue pacing logic
  • Added explicit search processor throttling
  • Improved Docker runtime stability
  • Improved logging around Spotify retrieval
  • Added auth bootstrap flow support

Frontend

  • Added Spotify login integration
  • Added frontend auth token bootstrap
  • Fixed playlist progress handling for large playlists
  • Improved playlist rendering stability

Security

  • Moved secrets to external env files
  • Improved Docker secret handling recommendations
  • Added security documentation
  • Prevented accidental secret inclusion in repository

Development Notes

Recommended local test run:

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=test-token \
  -e YT_SEARCH_DELAY_MS=7000 \
  -e YT_DOWNLOADS_PER_MINUTE=3 \
  -v "$PWD/downloads:/spooty/backend/downloads" \
  spootyfy-hardened:local

License

MIT