Live Link ( Hopefully its Online :)) : https://song-snatch.vercel.app/
SongSnatch is a robust, scalable, and resilient ecosystem designed to convert Spotify metadata into high-quality offline MP3s. It features a FastAPI backend, a Typer-powered CLI, and a distributed microservices architecture built for speed and reliability.
By leveraging a producer-consumer pattern, SongSnatch can handle high-concurrency download jobs across multiple workers, ensuring a seamless experience whether you're using it as a local tool or a deployed cloud service.
SongSnatch is built to handle the volatility of the web with multiple layers of redundancy:
If a YouTube download is blocked (due to anti-bot measures, 403 errors, or regional restrictions), the worker automatically switches to JioSaavn. It fetches high-quality audio directly from JioSaavn's CDN, ensuring your downloads never fail.
To ensure lightning-fast performance, we implement a multi-level caching strategy:
- Search Caching: YouTube and JioSaavn results are cached for 30 days.
- Job Status store: The entire job lifecycle is managed in Redis, allowing for seamless and fast operations .
Failed download jobs are never lost. We utilize RabbitMQ Dead-Letter Queues (DLQ) to catch and store problematic messages, allowing for easy debugging and inspection without disrupting the main processing pipeline.
/v1/spotify/findβ Accepts Spotify URL, fetches metadata, downloads from YouTube./v1/song/listβ Lists downloaded songs./v1/song/download/{song_name}β Serves MP3 file for download or playback.
spotify-find <url>β Downloads from Spotify track/album/playlist link.list-songsβ Lists locally saved MP3s.download-song <name>β Prints absolute path to downloaded MP3.install-completionβ Enables shell autocomplete for CLI.
- Python 3.9+
ffmpeginstalled and in PATH- YouTube-dl / yt-dlp compatible backend for downloading
- Optional:
.envfile for environment variables like port
We use uv for fast package management.
cd worker
uv syncuv run main.pyDefault: http://localhost:8000
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/spotify/find?url=... |
Download audio from Spotify URL |
| GET | /v1/song/list |
List all downloaded songs |
| GET | /v1/song/download/{song_name} |
Stream or download specific MP3 |
cd worker
uv syncuv run cli.py spotify-find "<spotify-url>"
uv run cli.py list-songs
uv run cli.py download-song "song1.mp3"Set the port in .env:
Enter the spotify dev account credentials
WORKER_PORT=8000
SPOTIFY_CLIENT_ID=clientid_here
SPOTIFY_CLIENT_SECRET=secret_here
uv run cli.py spotify-find "https://open.spotify.com/track/xyz"Or via API:
curl -X POST "http://localhost:8000/v1/spotify/find?url=https://open.spotify.com/track/xyz"This project uses a containerized microservices architecture with separate producer and worker services:
- β Node.js Producer (API server) β queues download jobs via RabbitMQ
- β Python Worker (FastAPI + background consumer) β downloads songs from Spotify/YouTube
- β Redis β persistent job state storage
- β RabbitMQ β message queue for job distribution
- β Prometheus β metrics collection from services
- β Grafana β visualization dashboards
- β JioSaavn Fallback β Automatically attempts to download from JioSaavn if YouTube is blocked or fails
- β Dead-Letter Queue (DLQ) β Handles failed RabbitMQ messages for better resilience
Prerequisites:
1. Create .env file with Spotify credentials:
cp .env.example .env
# Edit .env and add:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret2. Start all services:
docker compose up --buildThis starts:
- Producer API: http://localhost:8000 (Node.js Express)
- Worker API: http://localhost:8080 (Python FastAPI)
- RabbitMQ UI: http://localhost:15672 (guest/guest)
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3100 (admin/admin)
| Method | Route | Service | Description |
|---|---|---|---|
POST |
/v1/find?url=<spotify_url> |
Producer | Queue a Spotify download job |
GET |
/v1/status?jobId=<job_id> |
Producer | Check job status in Redis |
GET |
/v1/download/:song_name |
Producer | Download a completed MP3 file |
GET |
/v1/song/list |
Worker | List all downloaded songs |
GET |
/metrics |
Both | Prometheus metrics endpoint |
Both services export Prometheus metrics:
Producer Metrics (http://localhost:8000/metrics):
producer_jobs_created_totalβ Total jobs queued (with status label)producer_job_status_checks_totalβ Total status requestsproducer_redis_connectedβ Redis connection status (1/0)producer_rabbit_connectedβ RabbitMQ connection status (1/0)
Worker Metrics (http://localhost:8080/metrics):
worker_jobs_processed_totalβ Total jobs completed/failed (with status label)worker_songs_downloaded_totalβ Total songs downloaded (with status label)worker_download_duration_secondsβ Download time histogramworker_active_downloadsβ Currently active downloadsworker_redis_connectedβ Redis connection status (1/0)worker_rabbit_connectedβ RabbitMQ connection status (1/0)
View Metrics:
- Prometheus UI: http://localhost:9090 β Query metrics directly
- Grafana: http://localhost:3100 β Create custom dashboards
- Data source is auto-configured to Prometheus
curl -X POST "http://localhost:8000/v1/find?url=https://open.spotify.com/album/6AyUVv7MnxxTuijp4WmrhO"Response:
{
"message": "Job created",
"jobId": "0197fe57-6334-767e-aae2-0dd415799d46"
}curl "http://localhost:8000/v1/status?jobId=0197fe57-6334-767e-aae2-0dd415799d46"Response:
{
"data": {
"jobId": "0197fe57-6334-767e-aae2-0dd415799d46",
"status": "completed",
"songs": ["Muse - Unintended.mp3"],
"completedAt": "2026-02-05T12:34:56.789Z"
}
}curl "http://localhost:8000/v1/download/Muse%20-%20Unintended.mp3" --output song.mp3docker compose downRemove volumes:
docker compose down -vdocker run -it --rm -d \
--name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:3-managementdocker run -d \
--name redis \
-p 6379:6379 \
redis:latestcd producer
npm install
npm startcd worker
uv sync
uv run consumer.pycd worker
uv run main.py