feat(subsonic): add getStarred and getStarred2 handlers
Both return user's starred artists/albums/songs sorted liked_at DESC. Cap at 500 entries per category for v1 (Subsonic spec doesn't define pagination on these endpoints; M3+ can revisit if needed).
This commit is contained in:
@@ -126,3 +126,108 @@ func resolveStarID(ctx context.Context, q *dbq.Queries, raw string, kind entityK
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (m *mediaHandlers) handleGetStarred(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := UserFromContext(r.Context())
|
||||
if !ok {
|
||||
WriteFail(w, r, ErrGeneric, "Unauthenticated")
|
||||
return
|
||||
}
|
||||
q := dbq.New(m.pool)
|
||||
songs, albums, artists, err := loadStarred(r.Context(), q, user.ID)
|
||||
if err != nil {
|
||||
WriteFail(w, r, ErrGeneric, "Could not load starred")
|
||||
return
|
||||
}
|
||||
Write(w, r, GetStarredResponse{
|
||||
Envelope: NewEnvelope("ok"),
|
||||
Starred: GetStarredContainer{
|
||||
Artists: artists,
|
||||
Albums: albums,
|
||||
Songs: songs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (m *mediaHandlers) handleGetStarred2(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := UserFromContext(r.Context())
|
||||
if !ok {
|
||||
WriteFail(w, r, ErrGeneric, "Unauthenticated")
|
||||
return
|
||||
}
|
||||
q := dbq.New(m.pool)
|
||||
songs, albums, artists, err := loadStarred(r.Context(), q, user.ID)
|
||||
if err != nil {
|
||||
WriteFail(w, r, ErrGeneric, "Could not load starred")
|
||||
return
|
||||
}
|
||||
Write(w, r, GetStarred2Response{
|
||||
Envelope: NewEnvelope("ok"),
|
||||
Starred2: StarredContainer{
|
||||
Artists: artists,
|
||||
Albums: albums,
|
||||
Songs: songs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// loadStarred fetches the user's full starred set in liked_at DESC order,
|
||||
// projects to Subsonic refs (Song / Album / Artist).
|
||||
func loadStarred(ctx context.Context, q *dbq.Queries, userID pgtype.UUID) ([]SongRef, []AlbumRef, []ArtistRef, error) {
|
||||
const cap = 500 // bounded for safety; M2 doesn't paginate getStarred
|
||||
songs := []SongRef{}
|
||||
albums := []AlbumRef{}
|
||||
artists := []ArtistRef{}
|
||||
|
||||
trackRows, err := q.ListLikedTrackRows(ctx, dbq.ListLikedTrackRowsParams{
|
||||
UserID: userID, Limit: cap, Offset: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for _, t := range trackRows {
|
||||
album, err := q.GetAlbumByID(ctx, t.AlbumID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
artist, err := q.GetArtistByID(ctx, t.ArtistID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
songs = append(songs, songRef(t, album.Title, artist.Name))
|
||||
}
|
||||
|
||||
albumRows, err := q.ListLikedAlbumRows(ctx, dbq.ListLikedAlbumRowsParams{
|
||||
UserID: userID, Limit: cap, Offset: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for _, a := range albumRows {
|
||||
artist, err := q.GetArtistByID(ctx, a.ArtistID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
count, err := q.CountTracksByAlbum(ctx, a.ID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
albums = append(albums, albumRef(a, artist.Name, int(count), 0))
|
||||
}
|
||||
|
||||
artistRows, err := q.ListLikedArtistRows(ctx, dbq.ListLikedArtistRowsParams{
|
||||
UserID: userID, Limit: cap, Offset: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for _, a := range artistRows {
|
||||
albums, err := q.ListAlbumsByArtist(ctx, a.ID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
artists = append(artists, artistRef(a, len(albums)))
|
||||
}
|
||||
|
||||
return songs, albums, artists, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user