fix(subsonic): strip OpenSubsonic extensions from playlists for legacy clients
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-1 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-2 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-3 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-4 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-5 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-6 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-7 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-8 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-9 (push) Has been cancelled
Pipeline: Test, Lint, Build / Build-10 (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled

buildOSPlaylist was the only OpenSubsonic builder function missing the
LegacyClients guard, causing attributes like `validUntil` and `readonly`
to appear in playlist XML responses for legacy clients like DSub2000.
This caused a crash when DSub2000 tried to parse evaluated smart
playlists containing the `validUntil` attribute.
This commit is contained in:
Deluan 2026-04-02 16:37:52 -04:00
parent c60637de24
commit 23f3556371
2 changed files with 38 additions and 0 deletions

View file

@ -159,6 +159,10 @@ func (api *Router) buildPlaylist(ctx context.Context, p model.Playlist) response
}
func buildOSPlaylist(ctx context.Context, p model.Playlist) *responses.OpenSubsonicPlaylist {
player, ok := request.PlayerFrom(ctx)
if ok && isClientInList(conf.Server.Subsonic.LegacyClients, player.Client) {
return nil
}
pls := responses.OpenSubsonicPlaylist{}
if p.IsSmartPlaylist() {

View file

@ -128,6 +128,23 @@ var _ = Describe("buildPlaylist", func() {
})
})
Context("with legacy client", func() {
BeforeEach(func() {
conf.Server.Subsonic.LegacyClients = "legacy-client"
player := model.Player{Client: "legacy-client"}
ctx = request.WithPlayer(ctx, player)
})
It("returns all standard fields but no OpenSubsonic extensions", func() {
result := router.buildPlaylist(ctx, playlist)
Expect(result.Comment).To(Equal("Test comment"))
Expect(result.Owner).To(Equal("admin"))
Expect(result.Public).To(BeTrue())
Expect(result.OpenSubsonicPlaylist).To(BeNil())
})
})
Context("when no player in context", func() {
It("returns all fields", func() {
result := router.buildPlaylist(ctx, playlist)
@ -213,6 +230,23 @@ var _ = Describe("buildPlaylist", func() {
Expect(result.ValidUntil).To(Equal(&validUntil))
})
})
Context("with legacy client", func() {
BeforeEach(func() {
conf.Server.Subsonic.LegacyClients = "legacy-client"
player := model.Player{Client: "legacy-client"}
ctx = request.WithPlayer(ctx, player)
})
It("returns standard fields but no OpenSubsonic extensions", func() {
result := router.buildPlaylist(ctx, playlist)
Expect(result.Comment).To(Equal("Test comment"))
Expect(result.Owner).To(Equal("admin"))
Expect(result.Public).To(BeTrue())
Expect(result.OpenSubsonicPlaylist).To(BeNil())
})
})
})
})