diff --git a/core/playlists.go b/core/playlists.go index 2551ba0a..3551f454 100644 --- a/core/playlists.go +++ b/core/playlists.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/RaveNoX/go-jsoncommentstrip" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model/criteria" @@ -112,7 +113,8 @@ func (s *playlists) newSyncedPlaylist(baseDir string, playlistFile string) (*mod func (s *playlists) parseNSP(ctx context.Context, pls *model.Playlist, file io.Reader) (*model.Playlist, error) { nsp := &nspFile{} - dec := json.NewDecoder(file) + reader := jsoncommentstrip.NewReader(file) + dec := json.NewDecoder(reader) err := dec.Decode(nsp) if err != nil { log.Error(ctx, "Error parsing SmartPlaylist", "playlist", pls.Name, err) diff --git a/core/playlists_test.go b/core/playlists_test.go index c06b2ca2..2d17a48f 100644 --- a/core/playlists_test.go +++ b/core/playlists_test.go @@ -5,6 +5,7 @@ import ( "os" "time" + "github.com/navidrome/navidrome/model/criteria" "github.com/navidrome/navidrome/model/request" "github.com/navidrome/navidrome/model" @@ -33,29 +34,45 @@ var _ = Describe("Playlists", func() { ps = NewPlaylists(ds) }) - It("parses well-formed playlists", func() { - pls, err := ps.ImportFile(ctx, "tests/fixtures", "playlists/pls1.m3u") - Expect(err).To(BeNil()) - Expect(pls.OwnerID).To(Equal("123")) - Expect(pls.Tracks).To(HaveLen(3)) - Expect(pls.Tracks[0].Path).To(Equal("tests/fixtures/test.mp3")) - Expect(pls.Tracks[1].Path).To(Equal("tests/fixtures/test.ogg")) - Expect(pls.Tracks[2].Path).To(Equal("/tests/fixtures/01 Invisible (RED) Edit Version.mp3")) - Expect(mp.last).To(Equal(pls)) + Describe("M3U", func() { + It("parses well-formed playlists", func() { + pls, err := ps.ImportFile(ctx, "tests/fixtures", "playlists/pls1.m3u") + Expect(err).To(BeNil()) + Expect(pls.OwnerID).To(Equal("123")) + Expect(pls.Tracks).To(HaveLen(3)) + Expect(pls.Tracks[0].Path).To(Equal("tests/fixtures/test.mp3")) + Expect(pls.Tracks[1].Path).To(Equal("tests/fixtures/test.ogg")) + Expect(pls.Tracks[2].Path).To(Equal("/tests/fixtures/01 Invisible (RED) Edit Version.mp3")) + Expect(mp.last).To(Equal(pls)) + }) + + It("parses playlists using LF ending", func() { + pls, err := ps.ImportFile(ctx, "tests/fixtures/playlists", "lf-ended.m3u") + Expect(err).To(BeNil()) + Expect(pls.Tracks).To(HaveLen(2)) + }) + + It("parses playlists using CR ending (old Mac format)", func() { + pls, err := ps.ImportFile(ctx, "tests/fixtures/playlists", "cr-ended.m3u") + Expect(err).To(BeNil()) + Expect(pls.Tracks).To(HaveLen(2)) + }) }) - It("parses playlists using LF ending", func() { - pls, err := ps.ImportFile(ctx, "tests/fixtures/playlists", "lf-ended.m3u") - Expect(err).To(BeNil()) - Expect(pls.Tracks).To(HaveLen(2)) + Describe("NSP", func() { + It("parses well-formed playlists", func() { + pls, err := ps.ImportFile(ctx, "tests/fixtures", "playlists/recently_played.nsp") + Expect(err).To(BeNil()) + Expect(mp.last).To(Equal(pls)) + Expect(pls.OwnerID).To(Equal("123")) + Expect(pls.Name).To(Equal("Recently Played")) + Expect(pls.Comment).To(Equal("Recently played tracks")) + Expect(pls.Rules.Sort).To(Equal("lastPlayed")) + Expect(pls.Rules.Order).To(Equal("desc")) + Expect(pls.Rules.Limit).To(Equal(100)) + Expect(pls.Rules.Expression).To(BeAssignableToTypeOf(criteria.All{})) + }) }) - - It("parses playlists using CR ending (old Mac format)", func() { - pls, err := ps.ImportFile(ctx, "tests/fixtures/playlists", "cr-ended.m3u") - Expect(err).To(BeNil()) - Expect(pls.Tracks).To(HaveLen(2)) - }) - }) Describe("ImportM3U", func() { diff --git a/go.mod b/go.mod index dcae6189..42afbf6c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/Masterminds/squirrel v1.5.4 + github.com/RaveNoX/go-jsoncommentstrip v1.0.0 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 github.com/deluan/rest v0.0.0-20211102003136-6260bc399cbf github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 diff --git a/go.sum b/go.sum index f0831d6e..bb2ec287 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0 h1:t527LHHE3HmiHrq74QMpNPZpGCIJzTx+apLkMKt4HC0= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk= diff --git a/scanner/playlist_importer_test.go b/scanner/playlist_importer_test.go index 410bb1ee..3e4836f5 100644 --- a/scanner/playlist_importer_test.go +++ b/scanner/playlist_importer_test.go @@ -59,7 +59,7 @@ var _ = Describe("playlistImporter", func() { conf.Server.PlaylistsPath = "." ps = newPlaylistImporter(ds, pls, cw, "tests/fixtures/playlists") - Expect(ps.processPlaylists(ctx, "tests/fixtures/playlists")).To(Equal(int64(5))) + Expect(ps.processPlaylists(ctx, "tests/fixtures/playlists")).To(Equal(int64(6))) Expect(ps.processPlaylists(ctx, "tests/fixtures/playlists/subfolder1")).To(Equal(int64(0))) }) diff --git a/tests/fixtures/playlists/recently_played.nsp b/tests/fixtures/playlists/recently_played.nsp new file mode 100644 index 00000000..bafc0dbb --- /dev/null +++ b/tests/fixtures/playlists/recently_played.nsp @@ -0,0 +1,14 @@ +/* + Top Level Comment +*/ +{ + "name": "Recently Played", + "comment": "Recently played tracks", + "all": [ + // This is a inline comment + {"inTheLast": {"lastPlayed": 30}} + ], + "sort": "lastPlayed", + "order": "desc", + "limit": 100 +} \ No newline at end of file