From 94d2696c8434171f1b67f4645be0c6914fe34c19 Mon Sep 17 00:00:00 2001 From: Deluan Date: Tue, 29 Jul 2025 17:59:58 -0400 Subject: [PATCH] feat(subsonic): populate Folder field with user's accessible library IDs Added functionality to populate the Folder field in GetUser and GetUsers API responses with the library IDs that the user has access to. This allows Subsonic API clients to understand which music folders (libraries) a user can access for proper content filtering and UI presentation. Signed-off-by: Deluan --- server/subsonic/users.go | 3 ++- server/subsonic/users_test.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/server/subsonic/users.go b/server/subsonic/users.go index 39214eee2..733f3fddb 100644 --- a/server/subsonic/users.go +++ b/server/subsonic/users.go @@ -7,6 +7,7 @@ import ( "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/request" "github.com/navidrome/navidrome/server/subsonic/responses" + "github.com/navidrome/navidrome/utils/slice" ) // buildUserResponse creates a User response object from a User model @@ -19,6 +20,7 @@ func buildUserResponse(user model.User) responses.User { ScrobblingEnabled: true, DownloadRole: conf.Server.EnableDownloads, ShareRole: conf.Server.EnableSharing, + Folder: slice.Map(user.Libraries, func(lib model.Library) int32 { return int32(lib.ID) }), } if conf.Server.Jukebox.Enabled { @@ -28,7 +30,6 @@ func buildUserResponse(user model.User) responses.User { return userResponse } -// TODO This is a placeholder. The real one has to read this info from a config file or the database func (api *Router) GetUser(r *http.Request) (*responses.Subsonic, error) { loggedUser, ok := request.UserFrom(r.Context()) if !ok { diff --git a/server/subsonic/users_test.go b/server/subsonic/users_test.go index d08462290..e41c1af63 100644 --- a/server/subsonic/users_test.go +++ b/server/subsonic/users_test.go @@ -36,6 +36,12 @@ var _ = Describe("Users", func() { conf.Server.EnableSharing = true conf.Server.Jukebox.Enabled = false + // Set up user with libraries + testUser.Libraries = model.Libraries{ + {ID: 10, Name: "Music"}, + {ID: 20, Name: "Podcasts"}, + } + // Create request with user in context req := httptest.NewRequest("GET", "/rest/getUser", nil) ctx := request.WithUser(context.Background(), testUser) @@ -57,6 +63,7 @@ var _ = Describe("Users", func() { Expect(userResponse.User.ScrobblingEnabled).To(BeTrue()) Expect(userResponse.User.DownloadRole).To(BeTrue()) Expect(userResponse.User.ShareRole).To(BeTrue()) + Expect(userResponse.User.Folder).To(ContainElements(int32(10), int32(20))) // Verify GetUsers response structure Expect(usersResponse.Status).To(Equal(responses.StatusOK)) @@ -75,6 +82,7 @@ var _ = Describe("Users", func() { Expect(singleUser.DownloadRole).To(Equal(userFromList.DownloadRole)) Expect(singleUser.ShareRole).To(Equal(userFromList.ShareRole)) Expect(singleUser.JukeboxRole).To(Equal(userFromList.JukeboxRole)) + Expect(singleUser.Folder).To(Equal(userFromList.Folder)) }) }) @@ -93,4 +101,19 @@ var _ = Describe("Users", func() { Entry("jukebox enabled, admin-only, regular user", true, true, false, false), Entry("jukebox enabled, admin-only, admin user", true, true, true, true), ) + + Describe("Folder list population", func() { + It("should populate Folder field with user's accessible library IDs", func() { + testUser.Libraries = model.Libraries{ + {ID: 1, Name: "Music"}, + {ID: 2, Name: "Podcasts"}, + {ID: 5, Name: "Audiobooks"}, + } + + response := buildUserResponse(testUser) + + Expect(response.Folder).To(HaveLen(3)) + Expect(response.Folder).To(ContainElements(int32(1), int32(2), int32(5))) + }) + }) })