Add methods to Playlist model

Also, don't load genres for Playlists tracks (not necessary for now)
This commit is contained in:
Deluan 2021-10-16 22:44:44 -04:00 committed by Deluan Quintão
parent d200933b68
commit c72add516a
7 changed files with 102 additions and 53 deletions

View File

@ -59,7 +59,7 @@ func (a *archiver) ZipPlaylist(ctx context.Context, id string, out io.Writer) er
log.Error(ctx, "Error loading mediafiles from playlist", "id", id, err)
return err
}
return a.zipTracks(ctx, id, out, pls.Tracks, a.createPlaylistHeader)
return a.zipTracks(ctx, id, out, pls.MediaFiles(), a.createPlaylistHeader)
}
func (a *archiver) zipTracks(ctx context.Context, id string, out io.Writer, mfs model.MediaFiles, ch createHeader) error {

View File

@ -1,32 +1,87 @@
package model
import (
"strconv"
"time"
"github.com/navidrome/navidrome/utils"
)
type Playlist struct {
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
Comment string `structs:"comment" json:"comment"`
Duration float32 `structs:"duration" json:"duration"`
Size int64 `structs:"size" json:"size"`
SongCount int `structs:"song_count" json:"songCount"`
Owner string `structs:"owner" json:"owner"`
Public bool `structs:"public" json:"public"`
Tracks MediaFiles `structs:"-" json:"tracks,omitempty"`
Path string `structs:"path" json:"path"`
Sync bool `structs:"sync" json:"sync"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
Comment string `structs:"comment" json:"comment"`
Duration float32 `structs:"duration" json:"duration"`
Size int64 `structs:"size" json:"size"`
SongCount int `structs:"song_count" json:"songCount"`
Owner string `structs:"owner" json:"owner"`
Public bool `structs:"public" json:"public"`
Tracks PlaylistTracks `structs:"-" json:"tracks,omitempty"`
Path string `structs:"path" json:"path"`
Sync bool `structs:"sync" json:"sync"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
// SmartPlaylist attributes
Rules *SmartPlaylist `structs:"-" json:"rules"`
EvaluatedAt time.Time `structs:"evaluated_at" json:"evaluatedAt"`
}
func (pls Playlist) IsSmartPlaylist() bool {
return pls.Rules != nil && pls.Rules.Combinator != ""
}
func (pls Playlist) MediaFiles() MediaFiles {
mfs := make(MediaFiles, len(pls.Tracks))
for i, t := range pls.Tracks {
mfs[i] = t.MediaFile
}
return mfs
}
func (pls *Playlist) RemoveTracks(idxToRemove []int) {
var newTracks PlaylistTracks
for i, t := range pls.Tracks {
if utils.IntInSlice(i, idxToRemove) {
continue
}
newTracks = append(newTracks, t)
}
pls.Tracks = newTracks
}
func (pls *Playlist) AddTracks(mediaFileIds []string) {
pos := len(pls.Tracks)
for _, mfId := range mediaFileIds {
pos++
t := PlaylistTrack{
ID: strconv.Itoa(pos),
MediaFileID: mfId,
MediaFile: MediaFile{ID: mfId},
PlaylistID: pls.ID,
}
pls.Tracks = append(pls.Tracks, t)
}
}
func (pls *Playlist) AddMediaFiles(mfs MediaFiles) {
pos := len(pls.Tracks)
for _, mf := range mfs {
pos++
t := PlaylistTrack{
ID: strconv.Itoa(pos),
MediaFileID: mf.ID,
MediaFile: mf,
PlaylistID: pls.ID,
}
pls.Tracks = append(pls.Tracks, t)
}
}
type Playlists []Playlist
type PlaylistRepository interface {
ResourceRepository
CountAll(options ...QueryOptions) (int64, error)
Exists(id string) (bool, error)
Put(pls *Playlist) error

View File

@ -70,16 +70,9 @@ var (
)
var (
plsBest = model.Playlist{
Name: "Best",
Comment: "No Comments",
Owner: "userid",
Public: true,
SongCount: 2,
Tracks: model.MediaFiles{{ID: "1001"}, {ID: "1003"}},
}
plsCool = model.Playlist{Name: "Cool", Owner: "userid", Tracks: model.MediaFiles{{ID: "1004"}}}
testPlaylists = []*model.Playlist{&plsBest, &plsCool}
plsBest model.Playlist
plsCool model.Playlist
testPlaylists []*model.Playlist
)
func P(path string) string {
@ -130,6 +123,18 @@ var _ = Describe("Initialize test DB", func() {
}
}
plsBest = model.Playlist{
Name: "Best",
Comment: "No Comments",
Owner: "userid",
Public: true,
SongCount: 2,
}
plsBest.AddTracks([]string{"1001", "1003"})
plsCool = model.Playlist{Name: "Cool", Owner: "userid"}
plsCool.AddTracks([]string{"1004"})
testPlaylists = []*model.Playlist{&plsBest, &plsCool}
pr := NewPlaylistRepository(ctx, o)
for i := range testPlaylists {
err := pr.Put(testPlaylists[i])
@ -162,6 +167,5 @@ var _ = Describe("Initialize test DB", func() {
songComeTogether.Starred = true
songComeTogether.StarredAt = mf.StarredAt
testSongs[1] = songComeTogether
})
})

View File

@ -101,7 +101,7 @@ func (r *playlistRepository) Put(p *model.Playlist) error {
if tracks == nil {
return nil
}
return r.updateTracks(id, tracks)
return r.updateTracks(id, p.MediaFiles())
}
func (r *playlistRepository) Get(id string) (*model.Playlist, error) {
@ -185,9 +185,8 @@ func (r *playlistRepository) loadTracks(pls *dbPlaylist) error {
Where(Eq{"playlist_id": pls.ID}).OrderBy("playlist_tracks.id")
err := r.queryAll(tracksQuery, &pls.Tracks)
if err != nil {
log.Error("Error loading playlist tracks", "playlist", pls.Name, "id", pls.ID)
log.Error(r.ctx, "Error loading playlist tracks", "playlist", pls.Name, "id", pls.ID, err)
}
err = r.loadMediaFileGenres(&pls.Tracks)
return err
}

View File

@ -58,23 +58,23 @@ var _ = Describe("PlaylistRepository", func() {
pls, err := repo.GetWithTracks(plsBest.ID)
Expect(err).To(BeNil())
Expect(pls.Name).To(Equal(plsBest.Name))
Expect(pls.Tracks).To(Equal(model.MediaFiles{
songDayInALife,
songRadioactivity,
}))
mfs := pls.MediaFiles()
Expect(mfs).To(HaveLen(2))
Expect(mfs[0].ID).To(Equal(songDayInALife.ID))
Expect(mfs[1].ID).To(Equal(songRadioactivity.ID))
})
})
It("Put/Exists/Delete", func() {
By("saves the playlist to the DB")
newPls := model.Playlist{Name: "Great!", Owner: "userid",
Tracks: model.MediaFiles{{ID: "1004"}, {ID: "1003"}}}
newPls := model.Playlist{Name: "Great!", Owner: "userid"}
newPls.AddTracks([]string{"1004", "1003"})
By("saves the playlist to the DB")
Expect(repo.Put(&newPls)).To(BeNil())
By("adds repeated songs to a playlist and keeps the order")
newPls.Tracks = append(newPls.Tracks, model.MediaFile{ID: "1004"})
newPls.AddTracks([]string{"1004"})
Expect(repo.Put(&newPls)).To(BeNil())
saved, _ := repo.GetWithTracks(newPls.ID)
Expect(saved.Tracks).To(HaveLen(3))

View File

@ -83,6 +83,7 @@ func (s *playlistSync) parsePlaylist(ctx context.Context, playlistFile string, b
mediaFileRepository := s.ds.MediaFile(ctx)
scanner := bufio.NewScanner(file)
scanner.Split(scanLines)
var mfs model.MediaFiles
for scanner.Scan() {
path := scanner.Text()
// Skip extended info
@ -101,8 +102,10 @@ func (s *playlistSync) parsePlaylist(ctx context.Context, playlistFile string, b
log.Warn(ctx, "Path in playlist not found", "playlist", playlistFile, "path", path, err)
continue
}
pls.Tracks = append(pls.Tracks, *mf)
mfs = append(mfs, *mf)
}
pls.Tracks = nil
pls.AddMediaFiles(mfs)
return pls, scanner.Err()
}

View File

@ -76,16 +76,14 @@ func (c *PlaylistsController) create(ctx context.Context, playlistId, name strin
if owner != pls.Owner {
return model.ErrNotAuthorized
}
pls.Tracks = nil
} else {
pls = &model.Playlist{
Name: name,
Owner: owner,
}
}
for _, id := range ids {
pls.Tracks = append(pls.Tracks, model.MediaFile{ID: id})
}
pls.Tracks = nil
pls.AddTracks(ids)
err = tx.Playlist(ctx).Put(pls)
playlistId = pls.ID
@ -143,18 +141,8 @@ func (c *PlaylistsController) update(ctx context.Context, playlistId string, nam
pls.Public = *public
}
newTracks := model.MediaFiles{}
for i, t := range pls.Tracks {
if utils.IntInSlice(i, idxToRemove) {
continue
}
newTracks = append(newTracks, t)
}
for _, id := range idsToAdd {
newTracks = append(newTracks, model.MediaFile{ID: id})
}
pls.Tracks = newTracks
pls.RemoveTracks(idxToRemove)
pls.AddTracks(idsToAdd)
return tx.Playlist(ctx).Put(pls)
})
@ -203,7 +191,7 @@ func (c *PlaylistsController) buildPlaylistWithSongs(ctx context.Context, p *mod
pls := &responses.PlaylistWithSongs{
Playlist: *c.buildPlaylist(*p),
}
pls.Entry = childrenFromMediaFiles(ctx, p.Tracks)
pls.Entry = childrenFromMediaFiles(ctx, p.MediaFiles())
return pls
}