f102036dc6
Signed-off-by: Deluan <deluan@navidrome.org>
102 lines
3.2 KiB
Go
102 lines
3.2 KiB
Go
package playlists
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/deluan/rest"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/model/request"
|
|
)
|
|
|
|
// --- REST adapter (follows Share/Library pattern) ---
|
|
|
|
func (s *playlists) NewRepository(ctx context.Context) rest.Repository {
|
|
return &playlistRepositoryWrapper{
|
|
ctx: ctx,
|
|
PlaylistRepository: s.ds.Playlist(ctx),
|
|
service: s,
|
|
}
|
|
}
|
|
|
|
// playlistRepositoryWrapper wraps the playlist repository as a thin REST-to-service adapter.
|
|
// It satisfies rest.Repository through the embedded PlaylistRepository (via ResourceRepository),
|
|
// and rest.Persistable by delegating to service methods for all mutations.
|
|
type playlistRepositoryWrapper struct {
|
|
model.PlaylistRepository
|
|
ctx context.Context
|
|
service *playlists
|
|
}
|
|
|
|
func (r *playlistRepositoryWrapper) Save(entity any) (string, error) {
|
|
return r.service.savePlaylist(r.ctx, entity.(*model.Playlist))
|
|
}
|
|
|
|
func (r *playlistRepositoryWrapper) Update(id string, entity any, cols ...string) error {
|
|
return r.service.updatePlaylistEntity(r.ctx, id, entity.(*model.Playlist), cols...)
|
|
}
|
|
|
|
func (r *playlistRepositoryWrapper) Delete(id string) error {
|
|
err := r.service.Delete(r.ctx, id)
|
|
switch {
|
|
case errors.Is(err, model.ErrNotFound):
|
|
return rest.ErrNotFound
|
|
case errors.Is(err, model.ErrNotAuthorized):
|
|
return rest.ErrPermissionDenied
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
|
|
func (s *playlists) TracksRepository(ctx context.Context, playlistId string, refreshSmartPlaylist bool) rest.Repository {
|
|
repo := s.ds.Playlist(ctx)
|
|
tracks := repo.Tracks(playlistId, refreshSmartPlaylist)
|
|
if tracks == nil {
|
|
return nil
|
|
}
|
|
return tracks.(rest.Repository)
|
|
}
|
|
|
|
// savePlaylist creates a new playlist, assigning the owner from context.
|
|
// Only Name, Comment, Public, and Rules are user-settable via the REST API.
|
|
func (s *playlists) savePlaylist(ctx context.Context, pls *model.Playlist) (string, error) {
|
|
usr, _ := request.UserFrom(ctx)
|
|
pls.OwnerID = usr.ID
|
|
pls.ID = "" // Force new creation
|
|
pls.Path = "" // Server-managed (M3U file path)
|
|
pls.Sync = false // Server-managed (M3U sync flag)
|
|
pls.UploadedImage = "" // Managed by image upload endpoint
|
|
pls.ExternalImageURL = "" // Managed by M3U import / plugins only
|
|
pls.EvaluatedAt = nil // Server-managed
|
|
err := s.ds.Playlist(ctx).Put(pls)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return pls.ID, nil
|
|
}
|
|
|
|
// updatePlaylistEntity updates playlist metadata with permission checks.
|
|
// Used by the REST API wrapper.
|
|
func (s *playlists) updatePlaylistEntity(ctx context.Context, id string, entity *model.Playlist, cols ...string) error {
|
|
current, err := s.checkWritable(ctx, id)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, model.ErrNotFound):
|
|
return rest.ErrNotFound
|
|
case errors.Is(err, model.ErrNotAuthorized):
|
|
return rest.ErrPermissionDenied
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
usr, _ := request.UserFrom(ctx)
|
|
if !usr.IsAdmin && entity.OwnerID != "" && entity.OwnerID != current.OwnerID {
|
|
return rest.ErrPermissionDenied
|
|
}
|
|
// Apply ownership change (admin only)
|
|
if entity.OwnerID != "" {
|
|
current.OwnerID = entity.OwnerID
|
|
}
|
|
return s.updateMetadata(ctx, s.ds, current, &entity.Name, &entity.Comment, &entity.Public)
|
|
}
|