diff --git a/core/playlists.go b/core/playlists.go index ed90cc23..1f98bf50 100644 --- a/core/playlists.go +++ b/core/playlists.go @@ -168,6 +168,11 @@ func (s *playlists) parseNSP(_ context.Context, pls *model.Playlist, reader io.R if nsp.Comment != "" { pls.Comment = nsp.Comment } + if nsp.Public != nil { + pls.Public = *nsp.Public + } else { + pls.Public = conf.Server.DefaultPlaylistPublicVisibility + } return nil } @@ -409,7 +414,10 @@ func (s *playlists) updatePlaylist(ctx context.Context, newPls *model.Playlist) } else { log.Info(ctx, "Adding synced playlist", "playlist", newPls.Name, "path", newPls.Path, "owner", owner.UserName) newPls.OwnerID = owner.ID - newPls.Public = conf.Server.DefaultPlaylistPublicVisibility + // For NSP files, Public may already be set from the file; for M3U, use server default + if !newPls.IsSmartPlaylist() { + newPls.Public = conf.Server.DefaultPlaylistPublicVisibility + } } return s.ds.Playlist(ctx).Put(newPls) } @@ -473,6 +481,7 @@ type nspFile struct { criteria.Criteria Name string `json:"name"` Comment string `json:"comment"` + Public *bool `json:"public"` } func (i *nspFile) UnmarshalJSON(data []byte) error { @@ -483,5 +492,8 @@ func (i *nspFile) UnmarshalJSON(data []byte) error { } i.Name, _ = m["name"].(string) i.Comment, _ = m["comment"].(string) + if public, ok := m["public"].(bool); ok { + i.Public = &public + } return json.Unmarshal(data, &i.Criteria) } diff --git a/core/playlists_test.go b/core/playlists_test.go index 6aa8aac9..f3347ae7 100644 --- a/core/playlists_test.go +++ b/core/playlists_test.go @@ -112,6 +112,27 @@ var _ = Describe("Playlists", func() { _, err := ps.ImportFile(ctx, folder, "invalid_json.nsp") Expect(err.Error()).To(ContainSubstring("line 19, column 1: invalid character '\\n'")) }) + It("parses NSP with public: true and creates public playlist", func() { + pls, err := ps.ImportFile(ctx, folder, "public_playlist.nsp") + Expect(err).ToNot(HaveOccurred()) + Expect(pls.Name).To(Equal("Public Playlist")) + Expect(pls.Public).To(BeTrue()) + }) + It("parses NSP with public: false and creates private playlist", func() { + pls, err := ps.ImportFile(ctx, folder, "private_playlist.nsp") + Expect(err).ToNot(HaveOccurred()) + Expect(pls.Name).To(Equal("Private Playlist")) + Expect(pls.Public).To(BeFalse()) + }) + It("uses server default when public field is absent", func() { + DeferCleanup(configtest.SetupConfig()) + conf.Server.DefaultPlaylistPublicVisibility = true + + pls, err := ps.ImportFile(ctx, folder, "recently_played.nsp") + Expect(err).ToNot(HaveOccurred()) + Expect(pls.Name).To(Equal("Recently Played")) + Expect(pls.Public).To(BeTrue()) // Should be true since server default is true + }) }) Describe("Cross-library relative paths", func() { diff --git a/tests/fixtures/playlists/private_playlist.nsp b/tests/fixtures/playlists/private_playlist.nsp new file mode 100644 index 00000000..de73b369 --- /dev/null +++ b/tests/fixtures/playlists/private_playlist.nsp @@ -0,0 +1,11 @@ +{ + "name": "Private Playlist", + "comment": "A smart playlist that is explicitly private", + "public": false, + "all": [ + {"is": {"loved": true}} + ], + "sort": "title", + "order": "asc", + "limit": 100 +} diff --git a/tests/fixtures/playlists/public_playlist.nsp b/tests/fixtures/playlists/public_playlist.nsp new file mode 100644 index 00000000..e303169e --- /dev/null +++ b/tests/fixtures/playlists/public_playlist.nsp @@ -0,0 +1,11 @@ +{ + "name": "Public Playlist", + "comment": "A smart playlist that is public", + "public": true, + "all": [ + {"inTheLast": {"lastPlayed": 30}} + ], + "sort": "lastPlayed", + "order": "desc", + "limit": 50 +}