mirror of
https://github.com/navidrome/navidrome.git
synced 2025-07-14 07:31:28 +03:00
* fix(subsonic): Sort songs by presence of lyrics for `getLyrics` The current implementation of `getLyrics` fetches any songs matching the artist and title. However, this misses a case where there may be multiple matches for the same artist/song, and one has lyrics while the other doesn't. Resolve this by adding a custom SQL dynamic column that checks for the presence of lyrics. * add options to selectMediaFile, update test * more robust testing of GetAllByLyrics * fix(subsonic): refactor GetAllByLyrics to GetAll with lyrics sorting Signed-off-by: Deluan <deluan@navidrome.org> * use has_lyrics, and properly support multiple sort parts * better handle complicated internal sorts * just use a simpler filter * add note to setSortMappings * remove custom sort mapping, improve test with different updatedat * refactor tests and mock Signed-off-by: Deluan <deluan@navidrome.org> * default order when not specified is `asc` Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org> Co-authored-by: Deluan <deluan@navidrome.org>
159 lines
5.0 KiB
Go
159 lines
5.0 KiB
Go
package persistence
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/Masterminds/squirrel"
|
|
"github.com/navidrome/navidrome/log"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/model/id"
|
|
"github.com/navidrome/navidrome/model/request"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("MediaRepository", func() {
|
|
var mr model.MediaFileRepository
|
|
|
|
BeforeEach(func() {
|
|
ctx := log.NewContext(context.TODO())
|
|
ctx = request.WithUser(ctx, model.User{ID: "userid"})
|
|
mr = NewMediaFileRepository(ctx, GetDBXBuilder())
|
|
})
|
|
|
|
It("gets mediafile from the DB", func() {
|
|
actual, err := mr.Get("1004")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
actual.CreatedAt = time.Time{}
|
|
Expect(actual).To(Equal(&songAntenna))
|
|
})
|
|
|
|
It("returns ErrNotFound", func() {
|
|
_, err := mr.Get("56")
|
|
Expect(err).To(MatchError(model.ErrNotFound))
|
|
})
|
|
|
|
It("counts the number of mediafiles in the DB", func() {
|
|
Expect(mr.CountAll()).To(Equal(int64(6)))
|
|
})
|
|
|
|
It("returns songs ordered by lyrics with a specific title/artist", func() {
|
|
// attempt to mimic filters.SongsByArtistTitleWithLyricsFirst, except we want all items
|
|
results, err := mr.GetAll(model.QueryOptions{
|
|
Sort: "lyrics, updated_at",
|
|
Order: "desc",
|
|
Filters: squirrel.And{
|
|
squirrel.Eq{"title": "Antenna"},
|
|
squirrel.Or{
|
|
Exists("json_tree(participants, '$.albumartist')", squirrel.Eq{"value": "Kraftwerk"}),
|
|
Exists("json_tree(participants, '$.artist')", squirrel.Eq{"value": "Kraftwerk"}),
|
|
},
|
|
},
|
|
})
|
|
|
|
Expect(err).To(BeNil())
|
|
Expect(results).To(HaveLen(3))
|
|
Expect(results[0].Lyrics).To(Equal(`[{"lang":"xxx","line":[{"value":"This is a set of lyrics"}],"synced":false}]`))
|
|
for _, item := range results[1:] {
|
|
Expect(item.Lyrics).To(Equal("[]"))
|
|
Expect(item.Title).To(Equal("Antenna"))
|
|
Expect(item.Participants[model.RoleArtist][0].Name).To(Equal("Kraftwerk"))
|
|
}
|
|
})
|
|
|
|
It("checks existence of mediafiles in the DB", func() {
|
|
Expect(mr.Exists(songAntenna.ID)).To(BeTrue())
|
|
Expect(mr.Exists("666")).To(BeFalse())
|
|
})
|
|
|
|
It("delete tracks by id", func() {
|
|
newID := id.NewRandom()
|
|
Expect(mr.Put(&model.MediaFile{LibraryID: 1, ID: newID})).To(Succeed())
|
|
|
|
Expect(mr.Delete(newID)).To(Succeed())
|
|
|
|
_, err := mr.Get(newID)
|
|
Expect(err).To(MatchError(model.ErrNotFound))
|
|
})
|
|
|
|
It("deletes all missing files", func() {
|
|
new1 := model.MediaFile{ID: id.NewRandom(), LibraryID: 1}
|
|
new2 := model.MediaFile{ID: id.NewRandom(), LibraryID: 1}
|
|
Expect(mr.Put(&new1)).To(Succeed())
|
|
Expect(mr.Put(&new2)).To(Succeed())
|
|
Expect(mr.MarkMissing(true, &new1, &new2)).To(Succeed())
|
|
|
|
adminCtx := request.WithUser(log.NewContext(context.TODO()), model.User{ID: "userid", IsAdmin: true})
|
|
adminRepo := NewMediaFileRepository(adminCtx, GetDBXBuilder())
|
|
|
|
// Ensure the files are marked as missing and we have 2 of them
|
|
count, err := adminRepo.CountAll(model.QueryOptions{Filters: squirrel.Eq{"missing": true}})
|
|
Expect(count).To(BeNumerically("==", 2))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
count, err = adminRepo.DeleteAllMissing()
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(count).To(BeNumerically("==", 2))
|
|
|
|
_, err = mr.Get(new1.ID)
|
|
Expect(err).To(MatchError(model.ErrNotFound))
|
|
_, err = mr.Get(new2.ID)
|
|
Expect(err).To(MatchError(model.ErrNotFound))
|
|
})
|
|
|
|
Context("Annotations", func() {
|
|
It("increments play count when the tracks does not have annotations", func() {
|
|
id := "incplay.firsttime"
|
|
Expect(mr.Put(&model.MediaFile{LibraryID: 1, ID: id})).To(BeNil())
|
|
playDate := time.Now()
|
|
Expect(mr.IncPlayCount(id, playDate)).To(BeNil())
|
|
|
|
mf, err := mr.Get(id)
|
|
Expect(err).To(BeNil())
|
|
|
|
Expect(mf.PlayDate.Unix()).To(Equal(playDate.Unix()))
|
|
Expect(mf.PlayCount).To(Equal(int64(1)))
|
|
})
|
|
|
|
It("preserves play date if and only if provided date is older", func() {
|
|
id := "incplay.playdate"
|
|
Expect(mr.Put(&model.MediaFile{LibraryID: 1, ID: id})).To(BeNil())
|
|
playDate := time.Now()
|
|
Expect(mr.IncPlayCount(id, playDate)).To(BeNil())
|
|
mf, err := mr.Get(id)
|
|
Expect(err).To(BeNil())
|
|
Expect(mf.PlayDate.Unix()).To(Equal(playDate.Unix()))
|
|
Expect(mf.PlayCount).To(Equal(int64(1)))
|
|
|
|
playDateLate := playDate.AddDate(0, 0, 1)
|
|
Expect(mr.IncPlayCount(id, playDateLate)).To(BeNil())
|
|
mf, err = mr.Get(id)
|
|
Expect(err).To(BeNil())
|
|
Expect(mf.PlayDate.Unix()).To(Equal(playDateLate.Unix()))
|
|
Expect(mf.PlayCount).To(Equal(int64(2)))
|
|
|
|
playDateEarly := playDate.AddDate(0, 0, -1)
|
|
Expect(mr.IncPlayCount(id, playDateEarly)).To(BeNil())
|
|
mf, err = mr.Get(id)
|
|
Expect(err).To(BeNil())
|
|
Expect(mf.PlayDate.Unix()).To(Equal(playDateLate.Unix()))
|
|
Expect(mf.PlayCount).To(Equal(int64(3)))
|
|
})
|
|
|
|
It("increments play count on newly starred items", func() {
|
|
id := "star.incplay"
|
|
Expect(mr.Put(&model.MediaFile{LibraryID: 1, ID: id})).To(BeNil())
|
|
Expect(mr.SetStar(true, id)).To(BeNil())
|
|
playDate := time.Now()
|
|
Expect(mr.IncPlayCount(id, playDate)).To(BeNil())
|
|
|
|
mf, err := mr.Get(id)
|
|
Expect(err).To(BeNil())
|
|
|
|
Expect(mf.PlayDate.Unix()).To(Equal(playDate.Unix()))
|
|
Expect(mf.PlayCount).To(Equal(int64(1)))
|
|
})
|
|
})
|
|
})
|