feat(subsonic): getAlbumList2 recent and frequent read the caller's play history (#6293)
release / govulncheck (push) Successful in 20s
release / web (push) Successful in 1m37s
release / go (push) Successful in 1m45s
release / integration (push) Failing after 5m27s
release / android (push) Successful in 5m40s
release / Build signed APK (releases and dev) (push) Successful in 5m55s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped

type=recent orders the caller's albums by their latest play (skips
included); type=frequent orders them by completed plays (skips
excluded). Plays of tracks whose file is gone do not count, and both
page by size/offset like the other types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 13:58:09 -04:00
co-authored by Claude Opus 5.5
parent 09e78962b2
commit f3b124f9b2
4 changed files with 375 additions and 4 deletions
+17 -4
View File
@@ -236,8 +236,9 @@ func (b *browseHandlers) getSong(w http.ResponseWriter, r *http.Request) {
}
// getAlbumList2 returns a paged album list filtered by type. Supported types
// match the Subsonic spec § getAlbumList2. "recent" and "frequent" return an
// empty list rather than an error, so clients that poll them don't break.
// match the Subsonic spec § getAlbumList2. "recent" and "frequent" read the
// caller's own play history: most recently played first, and most played
// first.
func (b *browseHandlers) getAlbumList2(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
listType := params.Get("type")
@@ -291,8 +292,20 @@ func (b *browseHandlers) getAlbumList2(w http.ResponseWriter, r *http.Request) {
Genre: genre, Lim: int32(size), Off: int32(offset),
})
case "recent", "frequent":
// Not served from play history: empty keeps clients working.
albums = nil
user, ok := UserFromContext(r.Context())
if !ok {
WriteFail(w, r, ErrNotAuthorized, "Not authenticated")
return
}
if listType == "recent" {
albums, err = q.ListAlbumsRecentlyPlayedByUser(r.Context(), dbq.ListAlbumsRecentlyPlayedByUserParams{
UserID: user.ID, Lim: int32(size), Off: int32(offset),
})
} else {
albums, err = q.ListAlbumsFrequentlyPlayedByUser(r.Context(), dbq.ListAlbumsFrequentlyPlayedByUserParams{
UserID: user.ID, Lim: int32(size), Off: int32(offset),
})
}
default:
WriteFail(w, r, ErrGeneric, "Invalid list type: "+listType)
return