1package persistence
2
3import (
4	"context"
5	"time"
6
7	"github.com/astaxie/beego/orm"
8	"github.com/google/uuid"
9	"github.com/navidrome/navidrome/log"
10	"github.com/navidrome/navidrome/model"
11	"github.com/navidrome/navidrome/model/request"
12	. "github.com/onsi/ginkgo"
13	. "github.com/onsi/gomega"
14)
15
16var _ = Describe("MediaRepository", func() {
17	var mr model.MediaFileRepository
18
19	BeforeEach(func() {
20		ctx := log.NewContext(context.TODO())
21		ctx = request.WithUser(ctx, model.User{ID: "userid"})
22		mr = NewMediaFileRepository(ctx, orm.NewOrm())
23	})
24
25	It("gets mediafile from the DB", func() {
26		Expect(mr.Get("1004")).To(Equal(&songAntenna))
27	})
28
29	It("returns ErrNotFound", func() {
30		_, err := mr.Get("56")
31		Expect(err).To(MatchError(model.ErrNotFound))
32	})
33
34	It("counts the number of mediafiles in the DB", func() {
35		Expect(mr.CountAll()).To(Equal(int64(4)))
36	})
37
38	It("checks existence of mediafiles in the DB", func() {
39		Expect(mr.Exists(songAntenna.ID)).To(BeTrue())
40		Expect(mr.Exists("666")).To(BeFalse())
41	})
42
43	It("find mediafiles by album", func() {
44		Expect(mr.FindByAlbum("103")).To(Equal(model.MediaFiles{
45			songAntenna,
46			songRadioactivity,
47		}))
48	})
49
50	It("returns empty array when no tracks are found", func() {
51		Expect(mr.FindByAlbum("67")).To(Equal(model.MediaFiles{}))
52	})
53
54	It("finds tracks by path when using wildcards chars", func() {
55		Expect(mr.Put(&model.MediaFile{ID: "7001", Path: P("/Find:By'Path/_/123.mp3")})).To(BeNil())
56		Expect(mr.Put(&model.MediaFile{ID: "7002", Path: P("/Find:By'Path/1/123.mp3")})).To(BeNil())
57
58		found, err := mr.FindAllByPath(P("/Find:By'Path/_/"))
59		Expect(err).To(BeNil())
60		Expect(found).To(HaveLen(1))
61		Expect(found[0].ID).To(Equal("7001"))
62	})
63
64	It("finds tracks by path when using UTF8 chars", func() {
65		Expect(mr.Put(&model.MediaFile{ID: "7010", Path: P("/Пётр Ильич Чайковский/123.mp3")})).To(BeNil())
66		Expect(mr.Put(&model.MediaFile{ID: "7011", Path: P("/Пётр Ильич Чайковский/222.mp3")})).To(BeNil())
67
68		found, err := mr.FindAllByPath(P("/Пётр Ильич Чайковский/"))
69		Expect(err).To(BeNil())
70		Expect(found).To(HaveLen(2))
71	})
72
73	It("finds tracks by path case sensitively", func() {
74		Expect(mr.Put(&model.MediaFile{ID: "7003", Path: P("/Casesensitive/file1.mp3")})).To(BeNil())
75		Expect(mr.Put(&model.MediaFile{ID: "7004", Path: P("/casesensitive/file2.mp3")})).To(BeNil())
76
77		found, err := mr.FindAllByPath(P("/Casesensitive"))
78		Expect(err).To(BeNil())
79		Expect(found).To(HaveLen(1))
80		Expect(found[0].ID).To(Equal("7003"))
81
82		found, err = mr.FindAllByPath(P("/casesensitive/"))
83		Expect(err).To(BeNil())
84		Expect(found).To(HaveLen(1))
85		Expect(found[0].ID).To(Equal("7004"))
86	})
87
88	It("returns starred tracks", func() {
89		Expect(mr.GetStarred()).To(Equal(model.MediaFiles{
90			songComeTogether,
91		}))
92	})
93
94	It("delete tracks by id", func() {
95		id := uuid.NewString()
96		Expect(mr.Put(&model.MediaFile{ID: id})).To(BeNil())
97
98		Expect(mr.Delete(id)).To(BeNil())
99
100		_, err := mr.Get(id)
101		Expect(err).To(MatchError(model.ErrNotFound))
102	})
103
104	It("delete tracks by path", func() {
105		id1 := "6001"
106		Expect(mr.Put(&model.MediaFile{ID: id1, Path: P("/abc/123/" + id1 + ".mp3")})).To(BeNil())
107		id2 := "6002"
108		Expect(mr.Put(&model.MediaFile{ID: id2, Path: P("/abc/123/" + id2 + ".mp3")})).To(BeNil())
109		id3 := "6003"
110		Expect(mr.Put(&model.MediaFile{ID: id3, Path: P("/ab_/" + id3 + ".mp3")})).To(BeNil())
111		id4 := "6004"
112		Expect(mr.Put(&model.MediaFile{ID: id4, Path: P("/abc/" + id4 + ".mp3")})).To(BeNil())
113		id5 := "6005"
114		Expect(mr.Put(&model.MediaFile{ID: id5, Path: P("/Ab_/" + id5 + ".mp3")})).To(BeNil())
115
116		Expect(mr.DeleteByPath(P("/ab_"))).To(Equal(int64(1)))
117
118		Expect(mr.Get(id1)).ToNot(BeNil())
119		Expect(mr.Get(id2)).ToNot(BeNil())
120		Expect(mr.Get(id4)).ToNot(BeNil())
121		Expect(mr.Get(id5)).ToNot(BeNil())
122		_, err := mr.Get(id3)
123		Expect(err).To(MatchError(model.ErrNotFound))
124	})
125
126	It("delete tracks by path containing UTF8 chars", func() {
127		id1 := "6011"
128		Expect(mr.Put(&model.MediaFile{ID: id1, Path: P("/Legião Urbana/" + id1 + ".mp3")})).To(BeNil())
129		id2 := "6012"
130		Expect(mr.Put(&model.MediaFile{ID: id2, Path: P("/Legião Urbana/" + id2 + ".mp3")})).To(BeNil())
131		id3 := "6003"
132		Expect(mr.Put(&model.MediaFile{ID: id3, Path: P("/Legião Urbana/" + id3 + ".mp3")})).To(BeNil())
133
134		Expect(mr.FindAllByPath(P("/Legião Urbana"))).To(HaveLen(3))
135		Expect(mr.DeleteByPath(P("/Legião Urbana"))).To(Equal(int64(3)))
136		Expect(mr.FindAllByPath(P("/Legião Urbana"))).To(HaveLen(0))
137	})
138
139	It("only deletes tracks that match exact path", func() {
140		id1 := "6021"
141		Expect(mr.Put(&model.MediaFile{ID: id1, Path: P("/music/overlap/Ella Fitzgerald/" + id1 + ".mp3")})).To(BeNil())
142		id2 := "6022"
143		Expect(mr.Put(&model.MediaFile{ID: id2, Path: P("/music/overlap/Ella Fitzgerald/" + id2 + ".mp3")})).To(BeNil())
144		id3 := "6023"
145		Expect(mr.Put(&model.MediaFile{ID: id3, Path: P("/music/overlap/Ella Fitzgerald & Louis Armstrong - They Can't Take That Away From Me.mp3")})).To(BeNil())
146
147		Expect(mr.FindAllByPath(P("/music/overlap/Ella Fitzgerald"))).To(HaveLen(2))
148		Expect(mr.DeleteByPath(P("/music/overlap/Ella Fitzgerald"))).To(Equal(int64(2)))
149		Expect(mr.FindAllByPath(P("/music/overlap"))).To(HaveLen(1))
150	})
151
152	Context("Annotations", func() {
153		It("increments play count when the tracks does not have annotations", func() {
154			id := "incplay.firsttime"
155			Expect(mr.Put(&model.MediaFile{ID: id})).To(BeNil())
156			playDate := time.Now()
157			Expect(mr.IncPlayCount(id, playDate)).To(BeNil())
158
159			mf, err := mr.Get(id)
160			Expect(err).To(BeNil())
161
162			Expect(mf.PlayDate.Unix()).To(Equal(playDate.Unix()))
163			Expect(mf.PlayCount).To(Equal(int64(1)))
164		})
165
166		It("increments play count on newly starred items", func() {
167			id := "star.incplay"
168			Expect(mr.Put(&model.MediaFile{ID: id})).To(BeNil())
169			Expect(mr.SetStar(true, id)).To(BeNil())
170			playDate := time.Now()
171			Expect(mr.IncPlayCount(id, playDate)).To(BeNil())
172
173			mf, err := mr.Get(id)
174			Expect(err).To(BeNil())
175
176			Expect(mf.PlayDate.Unix()).To(Equal(playDate.Unix()))
177			Expect(mf.PlayCount).To(Equal(int64(1)))
178		})
179	})
180})
181