Initial wiring for getPlaylists endpoint
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/deluan/gosonic/api/responses"
|
||||||
|
"github.com/deluan/gosonic/engine"
|
||||||
|
"github.com/deluan/gosonic/utils"
|
||||||
|
"github.com/karlkfi/inject"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlaylistsController struct {
|
||||||
|
BaseAPIController
|
||||||
|
pls engine.Playlists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PlaylistsController) Prepare() {
|
||||||
|
inject.ExtractAssignable(utils.Graph, &c.pls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PlaylistsController) GetAll() {
|
||||||
|
allPls, err := c.pls.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
beego.Error(err)
|
||||||
|
c.SendError(responses.ERROR_GENERIC, "Internal error")
|
||||||
|
}
|
||||||
|
playlists := make([]responses.Playlist, len(*allPls))
|
||||||
|
for i, f := range *allPls {
|
||||||
|
playlists[i].Id = f.Id
|
||||||
|
playlists[i].Name = f.Name
|
||||||
|
}
|
||||||
|
response := c.NewEmpty()
|
||||||
|
response.Playlists = &responses.Playlists{Playlist: playlists}
|
||||||
|
c.SendResponse(response)
|
||||||
|
}
|
||||||
@@ -14,8 +14,9 @@ type Subsonic struct {
|
|||||||
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`
|
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`
|
||||||
Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"`
|
Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"`
|
||||||
Directory *Directory `xml:"directory,omitempty" json:"directory,omitempty"`
|
Directory *Directory `xml:"directory,omitempty" json:"directory,omitempty"`
|
||||||
User *User `xml:"user,omitempty" json:"user,omitempty"`
|
User *User `xml:"user,omitempty" json:"user,omitempty"`
|
||||||
AlbumList *AlbumList `xml:"albumList,omitempty" json:"albumList,omitempty"`
|
AlbumList *AlbumList `xml:"albumList,omitempty" json:"albumList,omitempty"`
|
||||||
|
Playlists *Playlists `xml:"playlists,omitempty" json:"playlists,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JsonWrapper struct {
|
type JsonWrapper struct {
|
||||||
@@ -87,6 +88,31 @@ type AlbumList struct {
|
|||||||
Album []Child `xml:"album" json:"album,omitempty"`
|
Album []Child `xml:"album" json:"album,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Playlist struct {
|
||||||
|
Id string `xml:"id,attr" json:"id"`
|
||||||
|
Name string `xml:"name,attr" json:"name"`
|
||||||
|
/*
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="allowedUser" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <!--Added in 1.8.0-->
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="id" type="xs:string" use="required"/>
|
||||||
|
<xs:attribute name="name" type="xs:string" use="required"/>
|
||||||
|
<xs:attribute name="comment" type="xs:string" use="optional"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="owner" type="xs:string" use="optional"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="public" type="xs:boolean" use="optional"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="songCount" type="xs:int" use="required"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="duration" type="xs:int" use="required"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="created" type="xs:dateTime" use="required"/> <!--Added in 1.8.0-->
|
||||||
|
<xs:attribute name="changed" type="xs:dateTime" use="required"/> <!--Added in 1.13.0-->
|
||||||
|
<xs:attribute name="coverArt" type="xs:string" use="optional"/> <!--Added in 1.11.0-->
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
type Playlists struct {
|
||||||
|
Playlist []Playlist `xml:"playlist" json:"playlist,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string `xml:"username,attr" json:"username"`
|
Username string `xml:"username,attr" json:"username"`
|
||||||
Email string `xml:"email,attr,omitempty" json:"email,omitempty"`
|
Email string `xml:"email,attr,omitempty" json:"email,omitempty"`
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package responses_test
|
package responses_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/deluan/gosonic/api/responses"
|
. "github.com/deluan/gosonic/api/responses"
|
||||||
. "github.com/deluan/gosonic/tests"
|
. "github.com/deluan/gosonic/tests"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSubsonicResponses(t *testing.T) {
|
func TestSubsonicResponses(t *testing.T) {
|
||||||
@@ -172,6 +173,31 @@ func TestSubsonicResponses(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Convey("Playlists", func() {
|
||||||
|
response.Playlists = &Playlists{}
|
||||||
|
|
||||||
|
Convey("Without data", func() {
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><playlists></playlists></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"playlists":{},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Convey("With data", func() {
|
||||||
|
pls := make([]Playlist, 2)
|
||||||
|
pls[0] = Playlist{Id: "111", Name: "aaa"}
|
||||||
|
pls[1] = Playlist{Id: "222", Name: "bbb"}
|
||||||
|
response.Playlists.Playlist = pls
|
||||||
|
|
||||||
|
Convey("XML", func() {
|
||||||
|
So(response, ShouldMatchXML, `<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.0.0"><playlists><playlist id="111" name="aaa"></playlist><playlist id="222" name="bbb"></playlist></playlists></subsonic-response>`)
|
||||||
|
})
|
||||||
|
Convey("JSON", func() {
|
||||||
|
So(response, ShouldMatchJSON, `{"playlists":{"playlist":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]},"status":"ok","version":"1.0.0"}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Reset(func() {
|
Reset(func() {
|
||||||
response = &Subsonic{Status: "ok", Version: "1.0.0"}
|
response = &Subsonic{Status: "ok", Version: "1.0.0"}
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ func init() {
|
|||||||
utils.DefineSingleton(new(domain.ArtistRepository), persistence.NewArtistRepository)
|
utils.DefineSingleton(new(domain.ArtistRepository), persistence.NewArtistRepository)
|
||||||
utils.DefineSingleton(new(domain.AlbumRepository), persistence.NewAlbumRepository)
|
utils.DefineSingleton(new(domain.AlbumRepository), persistence.NewAlbumRepository)
|
||||||
utils.DefineSingleton(new(domain.MediaFileRepository), persistence.NewMediaFileRepository)
|
utils.DefineSingleton(new(domain.MediaFileRepository), persistence.NewMediaFileRepository)
|
||||||
|
utils.DefineSingleton(new(domain.PlaylistRepository), persistence.NewPlaylistRepository)
|
||||||
|
|
||||||
// Engine (Use cases)
|
// Engine (Use cases)
|
||||||
utils.DefineSingleton(new(engine.PropertyRepository), persistence.NewPropertyRepository)
|
utils.DefineSingleton(new(engine.PropertyRepository), persistence.NewPropertyRepository)
|
||||||
utils.DefineSingleton(new(engine.Browser), engine.NewBrowser)
|
utils.DefineSingleton(new(engine.Browser), engine.NewBrowser)
|
||||||
utils.DefineSingleton(new(engine.ListGenerator), engine.NewListGenerator)
|
utils.DefineSingleton(new(engine.ListGenerator), engine.NewListGenerator)
|
||||||
utils.DefineSingleton(new(engine.Cover), engine.NewCover)
|
utils.DefineSingleton(new(engine.Cover), engine.NewCover)
|
||||||
|
utils.DefineSingleton(new(engine.Playlists), engine.NewPlaylists)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func mapEndpoints() {
|
|||||||
beego.NSRouter("/download.view", &api.StreamController{}, "*:Download"),
|
beego.NSRouter("/download.view", &api.StreamController{}, "*:Download"),
|
||||||
beego.NSRouter("/getUser.view", &api.UsersController{}, "*:GetUser"),
|
beego.NSRouter("/getUser.view", &api.UsersController{}, "*:GetUser"),
|
||||||
beego.NSRouter("/getAlbumList.view", &api.GetAlbumListController{}, "*:Get"),
|
beego.NSRouter("/getAlbumList.view", &api.GetAlbumListController{}, "*:Get"),
|
||||||
|
beego.NSRouter("/getPlaylists.view", &api.PlaylistsController{}, "*:GetAll"),
|
||||||
)
|
)
|
||||||
beego.AddNamespace(ns)
|
beego.AddNamespace(ns)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
type Playlist struct {
|
||||||
|
Id string
|
||||||
|
Name string
|
||||||
|
Tracks []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlaylistRepository interface {
|
||||||
|
BaseRepository
|
||||||
|
Put(m *Playlist) error
|
||||||
|
Get(id string) (*Playlist, error)
|
||||||
|
GetAll(options QueryOptions) (*Playlists, error)
|
||||||
|
PurgeInactive(active *Playlists) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Playlists []Playlist
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/deluan/gosonic/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Playlists interface {
|
||||||
|
GetAll() (*domain.Playlists, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type playlists struct {
|
||||||
|
plsRepo domain.PlaylistRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlaylists(pr domain.PlaylistRepository) Playlists {
|
||||||
|
return playlists{pr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p playlists) GetAll() (*domain.Playlists, error) {
|
||||||
|
return p.plsRepo.GetAll(domain.QueryOptions{})
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/deluan/gosonic/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type playlistRepository struct {
|
||||||
|
ledisRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlaylistRepository() domain.PlaylistRepository {
|
||||||
|
r := &playlistRepository{}
|
||||||
|
r.init("playlist", &domain.Playlist{})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *playlistRepository) Put(m *domain.Playlist) error {
|
||||||
|
if m.Id == "" {
|
||||||
|
return errors.New("Playlist Id is not set")
|
||||||
|
}
|
||||||
|
return r.saveOrUpdate(m.Id, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *playlistRepository) Get(id string) (*domain.Playlist, error) {
|
||||||
|
var rec interface{}
|
||||||
|
rec, err := r.readEntity(id)
|
||||||
|
return rec.(*domain.Playlist), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *playlistRepository) GetAll(options domain.QueryOptions) (*domain.Playlists, error) {
|
||||||
|
var as = make(domain.Playlists, 0)
|
||||||
|
err := r.loadAll(&as, options)
|
||||||
|
return &as, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *playlistRepository) PurgeInactive(active *domain.Playlists) error {
|
||||||
|
currentIds, err := r.getAllIds()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, a := range *active {
|
||||||
|
currentIds[a.Id] = false
|
||||||
|
}
|
||||||
|
inactiveIds := make(map[string]bool)
|
||||||
|
for id, inactive := range currentIds {
|
||||||
|
if inactive {
|
||||||
|
inactiveIds[id] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.DeleteAll(inactiveIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ domain.PlaylistRepository = (*playlistRepository)(nil)
|
||||||
Reference in New Issue
Block a user