feat(server): similar-artists + per-user artist top-tracks endpoints

GET /api/artists/{id}/similar — in-library artists ranked by similarity
score (deduped across sources), ArtistRef list with cover + album count.
GET /api/artists/{id}/top-tracks — current user's most-played tracks for
the artist (skips excluded, quarantine filtered).
This commit is contained in:
2026-06-06 22:30:41 -04:00
parent a766b7193f
commit 63b25e65ad
6 changed files with 281 additions and 0 deletions
+26
View File
@@ -60,3 +60,29 @@ ON CONFLICT (seed_artist_id, candidate_mbid, source) DO UPDATE SET
candidate_name = EXCLUDED.candidate_name,
score = EXCLUDED.score,
fetched_at = now();
-- name: ListSimilarArtistsForArtist :many
-- In-library artists similar to a seed artist, ranked by best similarity
-- score across sources (deduped per candidate). cover_album_id + album_count
-- mirror ListArtistsAlphaWithCovers so the strip renders identical cards.
SELECT sqlc.embed(artists),
cov.id AS cover_album_id,
cnt.album_count::bigint AS album_count
FROM (
SELECT artist_b_id, max(score) AS sim_score
FROM artist_similarity
WHERE artist_a_id = sqlc.arg(seed_artist_id)
GROUP BY artist_b_id
) s
JOIN artists ON artists.id = s.artist_b_id
LEFT JOIN LATERAL (
SELECT id FROM albums
WHERE artist_id = artists.id AND cover_art_path IS NOT NULL
ORDER BY created_at DESC LIMIT 1
) cov ON true
LEFT JOIN LATERAL (
SELECT count(*) AS album_count
FROM albums WHERE artist_id = artists.id
) cnt ON true
ORDER BY s.sim_score DESC, artists.sort_name
LIMIT sqlc.arg(result_limit);