Full metadata Β· Synced lyrics Β· Album art Β· Genre tags Β· Smart audio matching
SonicBoard is a Google Colab notebook that turns any Spotify playlist or individual track into a clean, fully-tagged MP3 collection - downloaded as a ZIP directly to your computer. No CLI setup. No local Python environment. No subscriptions. Just paste, run, and receive.
| Feature | Details |
|---|---|
| π΅ Audio Quality | MP3 at 320 kbps via yt-dlp |
| π·οΈ Metadata | Title, Artist, Album Artist, Album, Year, Track β, Disc β, ISRC, Spotify URL, Cover Art, Genre |
| π Lyrics | Synced .lrc preferred; 4-source cascade: LRCLib β lyrics.ovh β Genius β syncedlyrics |
| π No Lyrics Fallback | Animated placeholder .lrc embedded into MP3 - works in any folder |
| π― Smart Audio Matching | Scores up to 8 candidates before downloading; rejects karaoke, instrumentals, covers, tributes automatically |
| π Audio Track Guard | bestaudio[acodec!=none] format filter prevents silent/background-only alternate streams |
| π§Ή Clean Audio | SponsorBlock removes intros, outros, self-promos; duration filter rejects extended mixes |
| πΌ Genre Tags | Spotify artist data with automatic Last.fm fallback (Spotify genres empty since 2025) |
| π URL Support | Paste a playlist URL or a single track URL β both work |
| π Output | Playlist Name/MP3/ and Playlist Name/LRC/ inside a single ZIP |
| βοΈ Platform | Google Colab - runs entirely in the cloud, no local install |
- Go to developer.spotify.com/dashboard and log in
- Click Create App
- Set Redirect URI to
https://open.spotify.com - Under APIs used, check β Web API β Save
- Copy your Client ID and Client Secret
β οΈ February 2026 API Note: Spotify now requires an active Premium subscription on the account that owns the API app. Dev Mode apps will return a403for non-Premium accounts.
Spotify has silently emptied most artist genre data since early 2025. SonicBoard uses Last.fm as a fallback to keep genre tags accurate.
- Go to last.fm/api/account/create
- Create a free API account
- Copy your API Key into Cell 2
If left blank, genre tags will only populate when Spotify returns data directly.
| Cell | Action |
|---|---|
| Cell 1 | Installs all dependencies (spotipy, yt-dlp, mutagen, ffmpeg, syncedlyrics, etc.) |
| Cell 2 | Paste your Spotify Client ID, Client Secret, and optional Last.fm API Key |
| Cell 3 | Paste a Spotify playlist URL or a single track URL |
| Cell 4 | Runs the full downloader β sit back |
Runtime β Run all and you're done. A ZIP file will download to your PC automatically when finished.
Playlist:
Playlist Name/
βββ MP3/
β βββ Blinding Lights - The Weeknd.mp3
β βββ Levitating - Dua Lipa.mp3
β βββ ...
βββ LRC/
βββ Blinding Lights - The Weeknd.lrc
βββ Levitating - Dua Lipa.lrc
βββ ...
Single track:
Song Title - Artist Name/
βββ MP3/
β βββ Song Title - Artist Name.mp3
βββ LRC/
βββ Song Title - Artist Name.lrc
Each .mp3 file is embedded with:
- ID3v2.3 tags: Title, Artist, Album Artist, Album, Year, Track Number, Disc Number
- ISRC (
TSRC) and Spotify URL (WOAF) - Album Art (
APIC) - fetched from Spotify's CDN - Inline Lyrics (
USLT) - lyrics always embedded inside the MP3 itself, so they work in any folder regardless of where the.lrcfile is - Genre (
TCON) - from Spotify artist data, with Last.fm fallback - Comment (
COMM) - download date and ISRC for reference
Spotify URL (playlist or track)
β
βΌ
Spotify API βββΊ Track metadata (title, artist, ISRC, cover, genreβ¦)
β β’ playlist/ β paginates all tracks via playlist_items()
β β’ track/ β fetches single track via sp.track()
β Both paths produce identical track dicts
βΌ
YouTube Smart Matching (yt-dlp)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Phase 1 β Candidate scoring β
β β’ Fetch up to 8 candidates via extract_flat β
β β’ Score each: title words + artist name match β
β β’ Hard -100 penalty: karaoke / instrumental / β
β cover / tribute / backing track / no vocals β
β β’ Bonus: official / audio / vevo signals β
β β’ Duration gate: Β±35β45 s window β
β β’ Download the highest-scoring candidate directly β
β β
β Phase 2 β Plain-query fallback (if Phase 1 fails) β
β β’ Simple ytsearch1 on "Artist Title" β
β β’ Wider duration window (Β±45β60 s) β
β β
β Always: bestaudio[acodec!=none] format filter β
β prevents silent alternate audio streams β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Lyrics Cascade
ββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. LRCLib - synced .lrc (best quality) β
β 2. lyrics.ovh - plain text, no key needed β
β 3. Genius - scraping, Hindi/non-Englishβ
β 4. syncedlyrics - Musixmatch, Apple, NetEaseβ
β β None found - funny animated placeholder β
ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Genre Resolution
ββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. Spotify artist.genres β
β 2. Last.fm artist.getTopTags (fallback) β
ββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
mutagen ID3v2.3 embed (lyrics always go into USLT tag)
β
βΌ
ZIP βββΊ files.download() βββΊ Your PC
Cell 3 accepts either URL type β no configuration needed, SonicBoard detects automatically:
| Type | Example URL | ZIP name |
|---|---|---|
| Playlist | https://open.spotify.com/playlist/5f080ra... |
Playlist Name.zip |
| Track | https://open.spotify.com/track/4cOdK2wGLETKBW... |
Song Title - Artist.zip |
Any other URL format (album, artist, podcast) will raise a clear error in Cell 3 before any API call is made.
The previous approach searched YouTube and blindly downloaded the first result that passed a duration check. This caused two recurring problems: karaoke/instrumental versions being downloaded instead of originals, and YouTube's multi-track audio system sometimes exposing a background-music-only stream as the "best" audio format.
SonicBoard now uses a two-phase matching system:
Phase 1 β Scored candidate selection
Instead of downloading the first result, the script fetches up to 8 candidates using extract_flat (fast - no audio downloaded yet) and scores every result before committing to any download.
| Signal | Score |
|---|---|
| Title words matching the track | +3 per word |
| Artist name words matching | +2 per word |
| "official", "audio", "vevo", "music video" in title | +2 each |
karaoke, instrumental, cover, tribute, backing track, no vocals, minus one, remake, rendition |
β100 (hard penalty) |
Only the highest-scoring candidate that also passes the duration gate (Β±35 s) gets downloaded - directly by URL, not via another search. If the winning score is deeply negative (all candidates are non-originals), the phase fails cleanly rather than downloading something wrong.
Phase 2 β Plain fallback
If Phase 1 finds nothing, a simple Artist Title query is used as a last resort with a slightly wider duration window.
Audio track guard
The format selector was changed from bestaudio/best to bestaudio[acodec!=none]/bestaudio/best. This prevents yt-dlp from picking a YouTube multi-track alternate stream (often a background-music-only dub track) that can surface as the top-ranked audio format.
SonicBoard tries four sources in order, stopping at the first hit:
| Priority | Source | Type | Notes |
|---|---|---|---|
| 1 | LRCLib | Synced + Plain | /api/get by duration; /api/search fallback with best-match picking |
| 2 | lyrics.ovh | Plain | Free REST API, no key, no scraping |
| 3 | Genius | Plain | Scraping fallback; strong Hindi/non-English coverage |
| 4 | syncedlyrics | Synced + Plain | Tries Musixmatch, Apple Music, NetEase; bypasses anti-scrape |
When no source finds lyrics, a fun animated placeholder .lrc is written instead of leaving the file empty. The placeholder is also embedded directly into the MP3's USLT tag - so it shows up correctly in your music player regardless of whether the .lrc file is in the same folder as the MP3 or not.
Spotify has largely stopped returning genre data for artists since early 2025. SonicBoard handles this with a two-layer approach:
- Fetch genres from
sp.artist()- used if Spotify returns any - Fall back to Last.fm
artist.getTopTags- filters out noise tags (e.g.seen live,favorites,beautiful) and only keeps tags with a community count β₯ 10
Results are cached per artist to avoid redundant API calls across the same playlist.
| Library | Role |
|---|---|
spotipy |
Spotify Web API client - playlist & track data |
yt-dlp |
YouTube audio extraction & candidate scoring |
ffmpeg |
Audio transcoding to MP3 320k |
mutagen |
ID3v2.3 metadata embedding |
requests |
HTTP - cover art, lyrics APIs |
beautifulsoup4 |
HTML parsing for Genius lyrics scraping |
syncedlyrics |
Lyrics package - Musixmatch, Apple Music, NetEase |
| LRCLib | Primary lyrics source (synced + plain) |
| lyrics.ovh | Secondary lyrics source (plain, no key) |
| Last.fm API | Genre fallback (artist.getTopTags) |
This notebook is updated for the February 2026 Spotify Web API changes:
| Change | Old | Fixed |
|---|---|---|
| Playlist items endpoint | sp.playlist_tracks() β /tracks |
sp.playlist_items() β /items β
|
| Track key in response | item['track'] only |
item.get('track') or item.get('item') β
|
| Single track support | Playlists only | sp.track() added for track/ URLs β
|
| Dev Mode requirement | Any account | Premium required on app-owner account |
| Artist genre data | Usually populated | Mostly empty - Last.fm fallback added β |
Q: Is this legal? SonicBoard downloads audio from YouTube, not from Spotify directly. Usage is subject to YouTube's Terms of Service. This tool is intended for personal, offline use of content you have rights to access.
Q: Will it work on free Spotify accounts? The Spotify API credentials (Client ID/Secret) need to belong to a Premium account owner as of February 2026. The playlist itself can be public.
Q: What if a track fails to download?
The script will skip it, log it as failed, and continue. A summary is printed at the end. You can re-run the notebook - already downloaded files are skipped automatically.
Q: Can I paste a single song link instead of a playlist?
Yes. Paste any open.spotify.com/track/... URL into Cell 3 and SonicBoard will download just that one song with full metadata, lyrics, and album art β same quality and output structure as a playlist, just one item.
Q: Can I use this for albums? Not yet. Album URL support is a planned enhancement β PRs welcome.
Q: Why did I get an instrumental or karaoke version before? The old script downloaded the first YouTube result that matched the duration, with no check of what the video actually was. The new smart matching system scores up to 8 candidates and applies a hard penalty to any result whose title contains karaoke, instrumental, cover, tribute, or similar keywords. See the Smart Audio Matching section for full details.
Q: What if lyrics aren't found?
All four sources are tried in order (LRCLib β lyrics.ovh β Genius β syncedlyrics). If all fail, a fun animated placeholder .lrc is created and embedded into the MP3 so it always displays correctly in your music player.
Q: Why do lyrics work from any folder?
Lyrics are embedded directly inside the MP3 file via the USLT ID3 tag. Your player reads them from the file itself - it doesn't need to find a matching .lrc file in the same folder. The separate .lrc file in the LRC/ folder is a bonus for players that support external synced lyrics.
Q: Do I need a Last.fm API key? It's optional but recommended. Without it, genre tags will only appear when Spotify returns data directly, which has been rare since early 2025.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with π§ and too much chai
If SonicBoard saved you time, consider starring the repo β