| name | deezer-playlist-creator |
| description | >- |
Deezer Playlist Creator
End-to-end Deezer playlist creation. Three data sources, one pipeline: discover tracks โ build tracklist โ push to Deezer.
Data Pipeline Architecture
Every playlist request flows through a three-tier data sourcing strategy:
Tier 1: Deezer Public REST API (no auth)
Primary source for structured music discovery. Fast, rate-limited to ~50 req/5s, no credentials needed.
Load the deezer skill before using these endpoints โ it has the full reference.
| Query type | API call | Returns |
|---|
| "Artists similar to X" | GET /artist/{id}/related | Up to 100 related artists with IDs |
| "Top tracks by X" | GET /artist/{id}/top?limit=N | Ranked top tracks (max 50) |
| "Discography of X" | GET /artist/{id}/albums | All albums by artist |
| "What's trending" | GET /chart/0/tracks | Worldwide chart |
| "New releases" | GET /editorial/0/releases | This week's new albums |
| "Genre exploration" | GET /genre/{id}/artists | Artists in a genre |
| "Search anything" | GET /search?q=QUERY&order=RANKING | Tracks, albums, artists |
| "Album tracklist" | GET /album/{id}/tracks | All tracks in an album |
Full workflow example: "playlist of artists similar to Nirvana"
1. Public API: GET /search/artist?q=Nirvana โ artist ID: 543
2. Public API: GET /artist/543/related โ [Pearl Jam, Soundgarden, Alice in Chains, ...]
3. Public API: GET /artist/{id}/top?limit=3 โ top 3 tracks per related artist
4. Build JSON tracklist from all those tracks
5. Pipe to deezer_create_playlist.py โ playlist URL
Every step uses the public REST API for discovery. The GQL API only enters at the final step to create the playlist.
Tier 1.5: Last.fm API (API key)
Supplementary source for artist similarity and scrobble-ranked tracks. Last.fm's data is crowd-sourced from real listening behavior across millions of users โ often better quality than algorithmic recommendations.
Load the new-music-digest skill for the full Last.fm endpoint reference and API key.
| Query type | API call | Returns | Why use over Deezer |
|---|
| "Artists similar to X" | artist.getsimilar&artist=NAME&limit=10 | Scrobble-based similar artists | Real fan overlap, not algorithmic |
| "Top tracks by X (by plays)" | artist.gettoptracks&artist=NAME&limit=10 | Tracks ranked by scrobble count | Fan play counts, not stream counts |
| "My most-played artists" | user.gettopartists&user=USERNAME&period=3month | Personal top artists by scrobble | Personalizes playlists from your history |
When to use Last.fm:
- Established artists (pre-2020) โ Last.fm has deeper listening history
- Niche/subgenre artists โ fan communities create better similarity signals
- "Make me a playlist from my library" โ
user.gettopartists seeds from your actual listening
- Cross-referencing Deezer's
related results โ pick artists that appear in both
When to skip Last.fm:
- Brand new artists (minimal scrobble history)
- The Deezer public API returns good results quickly โ try it first, supplement with Last.fm if the results seem off
Integration pattern:
1. Deezer: GET /search/artist?q=X โ Deezer artist ID
2. Last.fm: GET artist.getsimilar&artist=X โ similar artists
3. For each similar artist: Deezer GET /search/artist?q=Y โ Deezer artist ID โ GET /artist/{id}/top
4. Build JSON and pipe
Tier 2: Deezer GQL Pipe API (ARL auth)
Adds algorithmic personalization. Use these when the public API can't answer the question.
| Method | What it gives | Use when | Response shape |
|---|
get_artist_mix(artist_id) | Algorithmic mix based on artist | "Deep cuts similar to X" | tracks.edges[].node |
get_track_mix(track_ids, limit, start_with_input_track) | Mix from seed track(s) | "Songs like this one" | tracks[].track โ ๏ธ flat list, not edges |
get_similar_tracks(track_id) | Similar to one track | "More tracks in this vein" | tracks.edges[].node |
get_recommendations() | Personalized "you might like" | "Surprise me" | varies |
get_flow() | Deezer's infinite Flow radio | "Put on something I'll like" | tracks.edges[].node |
get_charts() | Current chart data | "What's hot right now" | varies |
search(query) | Authenticated search | Better results than public API | results.tracks.edges[].node |
Common genre IDs for public API: 0=All, 132=Pop, 116=Rap/Hip Hop, 152=Rock, 113=Dance, 129=Jazz, 98=Classical, 85=Alternative, 106=Electro
Tier 3: Web search (last resort)
For subjective or human-curated questions APIs can't answer:
- "Best deep cuts by The Cure"
- "Underrated 90s shoegaze albums"
- "Songs that feel like autumn in New England"
- Festival lineups, music blog picks, Reddit threads
Always prefer Tier 1 or Tier 2 first โ web search is for when the API endpoints don't exist for the question being asked.
Script: deezer_create_playlist.py
Located at scripts/deezer_create_playlist.py in this skill directory.
The final step in every pipeline. Takes a JSON tracklist, searches Deezer for each track, creates a playlist, and populates it.
Usage
echo '{"title":"Name","description":"","tracks":[...]}' | \
infisical run --projectId af3b8a09-35ab-4acc-b0ea-c4ef2201eb29 --env dev -- \
python3.14 scripts/deezer_create_playlist.py
python3.14 scripts/deezer_create_playlist.py --input /tmp/playlist.json
Input format
{
"title": "Playlist name",
"description": "Optional description",
"tracks": [
{"title": "Track Title", "artist": "Artist Name"},
...
]
}
Output
{
"playlist_url": "https://www.deezer.com/playlist/15515468061",
"playlist_id": "15515468061",
"matched": 15,
"missed": 2,
"missed_tracks": [{"title": "...", "artist": "..."}],
"tracks": [{"title": "...", "artist": "...", "link": "..."}]
}
How it works
- Search โ Each track searched via GQL
search(), returns up to 20 candidates
- Score โ Python
SequenceMatcher (0.7 title + 0.3 artist weight), score โฅ 0.5 = match
- Create โ
create_playlist() โ public, non-collaborative
- Populate โ
add_tracks_to_playlist() in batches of 50
Match scoring details
- Extracts artist via
contributors.edges[].node.name (not a flat artists array)
- Sorts all candidates by score, takes the highest
- Score < 0.5 โ track is skipped and reported in
missed_tracks
Full Workflow Examples
"Make me a playlist of artists similar to Nirvana"
1. Load deezer skill
2. GET /search/artist?q=Nirvana โ artist ID 543
3. GET /artist/543/related โ [Pearl Jam, Soundgarden, Alice in Chains, Stone Temple Pilots, ...]
4. For each related artist: GET /artist/{id}/top?limit=3
5. Build JSON: {title: "Artists Like Nirvana", tracks: [...]}
6. Pipe to deezer_create_playlist.py
"Make me a 90s trip-hop playlist"
1. GET /search?q=trip-hop 90s&order=RANKING&limit=50 (public API)
2. Filter results for quality/relevance
3. For top artists found: GET /artist/{id}/top?limit=3
4. Build JSON and pipe to script
"What's trending in electronic music right now?"
1. GET /genre/106/artists (electronic genre)
2. For each artist: GET /artist/{id}/top?limit=2
3. OR: GET /chart/0/tracks (worldwide chart, filter by genre)
4. Build JSON and pipe
"Surprise me with something I'll like"
1. Use GQL get_recommendations() or get_flow()
2. Extract tracks
3. Build JSON and pipe
"Make me a playlist from my Last.fm library"
1. Load new-music-digest skill (for Last.fm key and endpoint reference)
2. GET Last.fm: user.gettopartists&user=USERNAME&period=3month&limit=20
3. For each artist: Deezer GET /search/artist?q=NAME โ artist ID โ GET /artist/{id}/top?limit=3
4. (Optional) GET Last.fm: artist.gettoptracks for scrobble-ranked picks instead
5. Build JSON and pipe
Decision Tree
When the user asks for a playlist, follow this order:
- Is this a personal "from my library" request? โ Use Last.fm
user.gettopartists to seed, then Deezer to resolve tracks
- Can the public REST API answer this directly? (similar artists, top tracks, genre, charts, search) โ Use Tier 1
- Would scrobble data improve similarity? (established artists, niche genres, pre-2020 acts) โ Supplement Tier 1 with Last.fm
artist.getsimilar
- Does it need algorithmic smarts? (track-based mixes, personal recommendations, flow) โ Use Tier 2
- Is it subjective or requires human cultural knowledge? (vibes, deep cuts, "best of" lists, festival lineups) โ Use Tier 3 (web search)
Never use web search when the public API has a direct endpoint for the query. Never use the GQL API for simple searches the public API handles faster.
Pitfalls
- ARL expires ~3 months. If auth fails, re-extract from browser and update Infisical
- Null nodes in search edges โ Some GQL track search results contain
edge.node = None. The script skips these silently, but if all edges for a query are null the track is reported as missed. This is a Deezer API quirk, not a script bug.
- 0.5 scoring threshold is generous โ It catches nearly everything (103/103 in testing) but produces false matches on ambiguous track names. Example failures: Amira Elfeky's "Paradise" โ I Prevail (0.79), Novelists' "WHERE TO FIND ME" โ Bilmuri (0.77). These all scored well above 0.5 but got the wrong artist. Raise the threshold near line 117 in
deezer_create_playlist.py if you prefer fewer false matches at the cost of more misses.
get_track_mix has a different response shape than search โ Returns mix.tracks[i].track (flat list of {track: ...} objects), not mix.tracks.edges[i].node. Iterate with for item in mix.tracks: node = item.track. Also takes 3 required args: track_ids (list), limit (int), and start_with_input_track (bool).
search returns results.tracks.edges[].node โ Not tracks.edges directly. Access via results.results.tracks.edges.
- Search errors in stderr โ The GQL API logs non-critical errors (album not found, playlist owner access denied) but search still returns results. Ignore the noise; check for actual data, not error messages.
contributors not artists โ Track nodes use contributors.edges[].node.name, not a flat artists array
create_playlist returns {playlist: {id}} โ Need result.playlist.id, not result.id
- Requires python3.14 โ The script uses asyncio features from Python 3.14
- Rate limiting โ 0.2s delay between searches. Large playlists (50+ tracks) take a few minutes.
- Public API limit is 50 req/5s โ Add delays in bulk artist/track fetching loops
- Some tracks won't match โ ~5-10% miss rate is normal. Less common tracks, regional releases, or tracks with special characters in the title are the usual culprits. The script reports misses so the user can manually add them.
- Genre IDs are Deezer-specific โ Use the list above; don't guess
/radio/top is unreliable โ Use /radio/genres + /radio/lists instead
Troubleshooting
| Issue | Fix |
|---|
DEEZER_ARL not found | Run with infisical run -- or export the var |
| All tracks return "No match found" | ARL may be expired; re-extract from browser |
Object of type ... is not JSON serializable | Check .playlist.id extraction and track ID types |
| GQL search returns 0 results | Fall back to public REST API search (deezer skill) |
| Public API returns 0 for related artists | Some artists have empty related lists; try web search |
| Playlist created but empty | add_tracks_to_playlist may have failed silently; check stderr |
| Tracks matching wrong artists (high recall, low precision) | Raise the score threshold near line 117 in the script; 0.5 is tuned for recall, 0.8+ for precision |