package engine import ( "context" "time" "github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model/request" "github.com/deluan/navidrome/utils" ) type Playlists interface { GetAll(ctx context.Context) (model.Playlists, error) Get(ctx context.Context, id string) (*PlaylistInfo, error) Create(ctx context.Context, playlistId, name string, ids []string) error Delete(ctx context.Context, playlistId string) error Update(ctx context.Context, playlistId string, name *string, idsToAdd []string, idxToRemove []int) error } func NewPlaylists(ds model.DataStore) Playlists { return &playlists{ds} } type playlists struct { ds model.DataStore } func (p *playlists) Create(ctx context.Context, playlistId, name string, ids []string) error { return p.ds.WithTx(func(tx model.DataStore) error { owner := p.getUser(ctx) var pls *model.Playlist var err error // If playlistID is present, override tracks if playlistId != "" { pls, err = tx.Playlist(ctx).Get(playlistId) if err != nil { return err } 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}) } return tx.Playlist(ctx).Put(pls) }) } func (p *playlists) getUser(ctx context.Context) string { user, ok := request.UserFrom(ctx) if ok { return user.UserName } return "" } func (p *playlists) Delete(ctx context.Context, playlistId string) error { return p.ds.WithTx(func(tx model.DataStore) error { pls, err := tx.Playlist(ctx).Get(playlistId) if err != nil { return err } owner := p.getUser(ctx) if owner != pls.Owner { return model.ErrNotAuthorized } return tx.Playlist(ctx).Delete(playlistId) }) } func (p *playlists) Update(ctx context.Context, playlistId string, name *string, idsToAdd []string, idxToRemove []int) error { return p.ds.WithTx(func(tx model.DataStore) error { pls, err := tx.Playlist(ctx).Get(playlistId) if err != nil { return err } owner := p.getUser(ctx) if owner != pls.Owner { return model.ErrNotAuthorized } if name != nil { pls.Name = *name } 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 return tx.Playlist(ctx).Put(pls) }) } func (p *playlists) GetAll(ctx context.Context) (model.Playlists, error) { return p.ds.Playlist(ctx).GetAll() } type PlaylistInfo struct { Id string Name string Entries Entries SongCount int Duration int Public bool Owner string Comment string Created time.Time Changed time.Time } func (p *playlists) Get(ctx context.Context, id string) (*PlaylistInfo, error) { pl, err := p.ds.Playlist(ctx).Get(id) if err != nil { return nil, err } // TODO Use model.Playlist when got rid of Entries plsInfo := &PlaylistInfo{ Id: pl.ID, Name: pl.Name, SongCount: pl.SongCount, Duration: int(pl.Duration), Public: pl.Public, Owner: pl.Owner, Comment: pl.Comment, Changed: pl.UpdatedAt, Created: pl.CreatedAt, } plsInfo.Entries = FromMediaFiles(pl.Tracks) return plsInfo, nil }