ADR-0001: Python as the Implementation Language
Status: Accepted
Date: 2025-01-15
Context
We need to build a passive music downloader that: - Monitors Spotify and YouTube Music playlists - Downloads new tracks automatically - Embeds metadata and album artwork - Runs as a background service on a homelab server
The initial prototype was started as a Bun TypeScript project, but we need to decide on the final implementation language.
Technology Landscape
For Spotify integration:
- TypeScript: @spotify/web-api-ts-sdk, or direct HTTP with undici
- Python: spotipy (mature, well-maintained)
For YouTube Music integration:
- TypeScript: No official library; would require ytmusicapi as subprocess wrapper
- Python: ytmusicapi (official unofficial library, actively maintained)
For downloading:
- TypeScript: spotdl (Python), or yt-dlp as subprocess
- Python: yt-dlp as native library, spotdl for matching
For metadata tagging:
- TypeScript: node-id3, music-tag-native (NAPI), or FFmpeg subprocess
- Python: mutagen (mature, cross-format)
Decision
We will implement this tool in Python 3.11+ using uv for package management.
Rationale
1. Library Ecosystem Alignment
The music downloading space is dominated by Python. The three critical libraries for this project—ytmusicapi, spotipy, and yt-dlp—are all written in Python and work best as native libraries:
| Component | TypeScript Approach | Python Approach |
|---|---|---|
| YouTube Music | Subprocess wrapper (fragile) | ytmusicapi native |
| Spotify | HTTP SDK or manual HTTP | spotipy native |
| Download engine | spotdl subprocess or yt-dlp subprocess |
yt-dlp as library |
| Metadata | FFmpeg subprocess or NAPI wrapper | mutagen native |
Using Python means we can call these libraries directly, avoiding subprocess complexity, improving error handling, and gaining access to the full API surface.
2. spotdl as a Reference Implementation
spotdl (24k+ GitHub stars) is the de facto standard for Spotify-to-YouTube downloading. It solves the hardest problem in this space: matching Spotify tracks to YouTube videos. While we won't use spotdl directly (it brings heavy dependencies), studying its approach confirms that Python is the right ecosystem:
ytmusicapifor playlist accessyt-dlpfor downloading- Matching logic via metadata comparison
Our architecture follows the same patterns that have proven reliable in spotdl.
3. Type Safety via ty and Pydantic
Python's type system has matured significantly. We will enforce strict type hints throughout:
tyfor static type checking (fast, from Astral)Pydanticfor runtime validation of config and data models- All function signatures include full type annotations
ty is Astral's blazing-fast type checker written in Rust—10x to 100x faster than mypy. Combined with Pydantic for runtime validation, this provides safety comparable to TypeScript while retaining Python's ecosystem advantages.
4. uv as a Fast Package Manager
uv provides:
- Sub-second package resolution
- Deterministic builds
- Virtual environment management
- Same speed as Bun for JS/TS
Performance is not a concern—we're I/O bound on downloads anyway.
5. Simpler Deployment
- No Node.js runtime to install
- FFmpeg remains the only system dependency
- Virtual environment via
uv venvoruv run - Familiar Python deployment patterns for homelab servers
Consequences
Positive
- Native library access:
ytmusicapi,spotipy, andyt-dlpwork as first-class libraries - Proven patterns:
spotdldemonstrates this approach works at scale - Strong typing:
ty+Pydanticprovides comparable safety to TypeScript - Fast tooling:
uvmatches Bun's performance - Simpler deps: FFmpeg is the only system dependency
Negative
- Runtime overhead: Python has more startup overhead than a compiled Bun binary
- Two runtimes: If we ever add a frontend, we'd need Node.js anyway
- GIL considerations: Threading is limited (but we use async I/O anyway)
Neutral
- Language preference: Subjective; some prefer TypeScript's compile-time checking
- Ecosystem maturity: Python's async ecosystem is good but not as ergonomic as Node.js
Alternatives Considered
TypeScript (Bun)
Rejected because:
- ytmusicapi is Python-only; would require subprocess wrapper
- spotdl matching logic is Python; would need to reimplement
- Fewer options for metadata tagging (no mature mutagen equivalent)
Rust
Rejected because:
- ytmusicapi and spotipy are not available as Rust libraries
- Would require calling Python libraries via FFI or subprocess anyway
- Steeper learning curve for the specific problem domain
Go
Rejected for the same reasons as Rust, plus: - No mature music-specific libraries exist
References
- spotdl repository - Reference implementation
- ytmusicapi - YouTube Music API
- spotipy - Spotify API
- Pydantic - Runtime validation library
- ty - Fast static type checker
- uv - Python package manager