mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 20:47:35 +03:00
Load SmartPlaylists rules from DB
This commit is contained in:
parent
7221b49b98
commit
815623715e
4 changed files with 84 additions and 38 deletions
|
@ -20,8 +20,8 @@ type Playlist struct {
|
|||
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
|
||||
|
||||
// SmartPlaylist attributes
|
||||
//Rules *SmartPlaylist `structs:"rules" json:"rules"`
|
||||
//EvaluatedAt time.Time `structs:"evaluated_at" json:"evaluatedAt"`
|
||||
Rules *SmartPlaylist `structs:"-" json:"rules"`
|
||||
EvaluatedAt time.Time `structs:"evaluated_at" json:"evaluatedAt"`
|
||||
}
|
||||
|
||||
type Playlists []Playlist
|
||||
|
@ -33,6 +33,7 @@ type PlaylistRepository interface {
|
|||
Get(id string) (*Playlist, error)
|
||||
GetAll(options ...QueryOptions) (Playlists, error)
|
||||
FindByPath(path string) (*Playlist, error)
|
||||
FindByID(id string) (*Playlist, error)
|
||||
Delete(id string) error
|
||||
Tracks(playlistId string) PlaylistTrackRepository
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package persistence
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/Masterminds/squirrel"
|
||||
|
@ -16,6 +18,11 @@ type playlistRepository struct {
|
|||
sqlRestful
|
||||
}
|
||||
|
||||
type dbPlaylist struct {
|
||||
model.Playlist `structs:",flatten"`
|
||||
RawRules string `structs:"rules"`
|
||||
}
|
||||
|
||||
func NewPlaylistRepository(ctx context.Context, o orm.Ormer) model.PlaylistRepository {
|
||||
r := &playlistRepository{}
|
||||
r.ctx = ctx
|
||||
|
@ -59,10 +66,18 @@ func (r *playlistRepository) Delete(id string) error {
|
|||
}
|
||||
|
||||
func (r *playlistRepository) Put(p *model.Playlist) error {
|
||||
if p.ID == "" {
|
||||
p.CreatedAt = time.Now()
|
||||
pls := dbPlaylist{Playlist: *p}
|
||||
if p.Rules != nil {
|
||||
j, err := json.Marshal(p.Rules)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pls.RawRules = string(j)
|
||||
}
|
||||
if pls.ID == "" {
|
||||
pls.CreatedAt = time.Now()
|
||||
} else {
|
||||
ok, err := r.Exists(p.ID)
|
||||
ok, err := r.Exists(pls.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -70,54 +85,85 @@ func (r *playlistRepository) Put(p *model.Playlist) error {
|
|||
return model.ErrNotAuthorized
|
||||
}
|
||||
}
|
||||
p.UpdatedAt = time.Now()
|
||||
pls.UpdatedAt = time.Now()
|
||||
|
||||
// Save tracks for later and set it to nil, to avoid trying to save it to the DB
|
||||
tracks := p.Tracks
|
||||
p.Tracks = nil
|
||||
tracks := pls.Tracks
|
||||
pls.Tracks = nil
|
||||
|
||||
id, err := r.put(p.ID, p)
|
||||
id, err := r.put(pls.ID, pls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.ID = id
|
||||
|
||||
// Only update tracks if they are specified
|
||||
if tracks != nil {
|
||||
err = r.updateTracks(id, tracks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tracks == nil {
|
||||
return nil
|
||||
}
|
||||
return r.loadTracks(p)
|
||||
return r.updateTracks(id, tracks)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) Get(id string) (*model.Playlist, error) {
|
||||
sel := r.newSelect().Columns("*").Where(And{Eq{"id": id}, r.userFilter()})
|
||||
var pls model.Playlist
|
||||
err := r.queryOne(sel, &pls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.loadTracks(&pls)
|
||||
return &pls, err
|
||||
return r.findBy(And{Eq{"id": id}, r.userFilter()}, true)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) FindByPath(path string) (*model.Playlist, error) {
|
||||
sel := r.newSelect().Columns("*").Where(Eq{"path": path})
|
||||
var pls model.Playlist
|
||||
err := r.queryOne(sel, &pls)
|
||||
return r.findBy(Eq{"path": path}, false)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) FindByID(id string) (*model.Playlist, error) {
|
||||
return r.findBy(And{Eq{"id": id}, r.userFilter()}, false)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) findBy(sql Sqlizer, includeTracks bool) (*model.Playlist, error) {
|
||||
sel := r.newSelect().Columns("*").Where(sql)
|
||||
var pls []dbPlaylist
|
||||
err := r.queryAll(sel, &pls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pls, err
|
||||
if len(pls) == 0 {
|
||||
return nil, model.ErrNotFound
|
||||
}
|
||||
|
||||
return r.toModel(pls[0], includeTracks)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) toModel(pls dbPlaylist, includeTracks bool) (*model.Playlist, error) {
|
||||
var err error
|
||||
if strings.TrimSpace(pls.RawRules) != "" {
|
||||
r := model.SmartPlaylist{}
|
||||
err = json.Unmarshal([]byte(pls.RawRules), &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pls.Playlist.Rules = &r
|
||||
} else {
|
||||
pls.Playlist.Rules = nil
|
||||
}
|
||||
if includeTracks {
|
||||
err = r.loadTracks(&pls)
|
||||
}
|
||||
return &pls.Playlist, err
|
||||
}
|
||||
|
||||
func (r *playlistRepository) GetAll(options ...model.QueryOptions) (model.Playlists, error) {
|
||||
sel := r.newSelect(options...).Columns("*").Where(r.userFilter())
|
||||
res := model.Playlists{}
|
||||
var res []dbPlaylist
|
||||
err := r.queryAll(sel, &res)
|
||||
return res, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
playlists := make(model.Playlists, len(res))
|
||||
for i, p := range res {
|
||||
pls, err := r.toModel(p, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
playlists[i] = *pls
|
||||
}
|
||||
return playlists, err
|
||||
}
|
||||
|
||||
func (r *playlistRepository) updateTracks(id string, tracks model.MediaFiles) error {
|
||||
|
@ -128,7 +174,7 @@ func (r *playlistRepository) updateTracks(id string, tracks model.MediaFiles) er
|
|||
return r.Tracks(id).Update(ids)
|
||||
}
|
||||
|
||||
func (r *playlistRepository) loadTracks(pls *model.Playlist) error {
|
||||
func (r *playlistRepository) loadTracks(pls *dbPlaylist) error {
|
||||
tracksQuery := Select().From("playlist_tracks").
|
||||
LeftJoin("annotation on ("+
|
||||
"annotation.item_id = media_file_id"+
|
||||
|
|
|
@ -231,7 +231,7 @@ func (r *playlistTrackRepository) isWritable() bool {
|
|||
if usr.IsAdmin {
|
||||
return true
|
||||
}
|
||||
pls, err := r.playlistRepo.Get(r.playlistId)
|
||||
pls, err := r.playlistRepo.FindByID(r.playlistId)
|
||||
return err == nil && pls.Owner == usr.UserName
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,12 @@ import (
|
|||
)
|
||||
|
||||
//{
|
||||
// "combinator": "and",
|
||||
// "rules": [
|
||||
// {"field": "loved", "operator": "is true"},
|
||||
// {"field": "lastPlayed", "operator": "in the last", "value": "90"}
|
||||
// ],
|
||||
// "order": "artist asc",
|
||||
// "limit": 100
|
||||
//"combinator": "and",
|
||||
//"rules": [
|
||||
// {"field": "lastPlayed", "operator": "in the last", "value": "30"}
|
||||
//],
|
||||
//"order": "lastPlayed desc",
|
||||
//"limit": 10
|
||||
//}
|
||||
type SmartPlaylist model.SmartPlaylist
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue