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:
@@ -231,6 +231,32 @@ WHERE NOT EXISTS (
|
||||
ORDER BY p.cnt DESC, t.id
|
||||
LIMIT $2;
|
||||
|
||||
-- name: ListMostPlayedTracksForArtist :many
|
||||
-- Top tracks for one artist by this user's completed-play count (skips
|
||||
-- excluded, quarantine filtered). Same projection as
|
||||
-- ListMostPlayedTracksForUser plus an artist_id filter, so the handler
|
||||
-- reuses trackRefFrom(row.Track, row.AlbumTitle, row.ArtistName).
|
||||
WITH plays AS (
|
||||
SELECT track_id, count(*) AS cnt
|
||||
FROM play_events
|
||||
WHERE user_id = sqlc.arg(user_id) AND was_skipped = false
|
||||
GROUP BY track_id
|
||||
)
|
||||
SELECT sqlc.embed(t),
|
||||
albums.title AS album_title,
|
||||
artists.name AS artist_name
|
||||
FROM plays p
|
||||
JOIN tracks t ON t.id = p.track_id
|
||||
JOIN albums ON albums.id = t.album_id
|
||||
JOIN artists ON artists.id = t.artist_id
|
||||
WHERE t.artist_id = sqlc.arg(artist_id)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM lidarr_quarantine q
|
||||
WHERE q.user_id = sqlc.arg(user_id) AND q.track_id = t.id
|
||||
)
|
||||
ORDER BY p.cnt DESC, t.id
|
||||
LIMIT sqlc.arg(result_limit);
|
||||
|
||||
-- name: ListLastPlayedArtistsForUser :many
|
||||
-- M6a: artists ranked by max(play_events.started_at) for the user, with
|
||||
-- a derived cover_album_id via a representative-album lateral join (most
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user