add basic playlist
This commit is contained in:
2
db/db.go
2
db/db.go
@@ -44,6 +44,8 @@ func New(path string) (*DB, error) {
|
|||||||
model.Setting{},
|
model.Setting{},
|
||||||
model.Play{},
|
model.Play{},
|
||||||
model.Album{},
|
model.Album{},
|
||||||
|
model.Playlist{},
|
||||||
|
model.PlaylistItem{},
|
||||||
)
|
)
|
||||||
// TODO: don't log if user already exists
|
// TODO: don't log if user already exists
|
||||||
db.FirstOrCreate(&model.User{}, model.User{
|
db.FirstOrCreate(&model.User{}, model.User{
|
||||||
|
|||||||
@@ -108,3 +108,21 @@ func (a *Album) IndexRightPath() string {
|
|||||||
}
|
}
|
||||||
return a.RightPath
|
return a.RightPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Playlist struct {
|
||||||
|
ID int `gorm:"primary_key"`
|
||||||
|
UpdatedAt time.Time
|
||||||
|
ModifiedAt time.Time
|
||||||
|
User *User
|
||||||
|
UserID int `sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
|
||||||
|
Name string
|
||||||
|
Comment string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlaylistItem struct {
|
||||||
|
ID int `gorm:"primary_key"`
|
||||||
|
Playlist Playlist
|
||||||
|
PlaylistID int `sql:"default: null; type:int REFERENCES playlists(id) ON DELETE CASCADE"`
|
||||||
|
Track Track
|
||||||
|
TrackID int `sql:"default: null; type:int REFERENCES tracks(id) ON DELETE CASCADE"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package ctrlsubsonic
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
"senan.xyz/g/gonic/model"
|
"senan.xyz/g/gonic/model"
|
||||||
"senan.xyz/g/gonic/scanner"
|
"senan.xyz/g/gonic/scanner"
|
||||||
"senan.xyz/g/gonic/server/ctrlsubsonic/spec"
|
"senan.xyz/g/gonic/server/ctrlsubsonic/spec"
|
||||||
@@ -114,3 +117,94 @@ func (c *Controller) ServeGetUser(r *http.Request) *spec.Response {
|
|||||||
func (c *Controller) ServeNotFound(r *http.Request) *spec.Response {
|
func (c *Controller) ServeNotFound(r *http.Request) *spec.Response {
|
||||||
return spec.NewError(70, "view not found")
|
return spec.NewError(70, "view not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeGetPlaylists(r *http.Request) *spec.Response {
|
||||||
|
user := r.Context().Value(key.User).(*model.User)
|
||||||
|
var playlists []*model.Playlist
|
||||||
|
c.DB.
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
Find(&playlists)
|
||||||
|
sub := spec.NewResponse()
|
||||||
|
sub.Playlists = &spec.Playlists{
|
||||||
|
List: make([]*spec.Playlist, len(playlists)),
|
||||||
|
}
|
||||||
|
for i, playlist := range playlists {
|
||||||
|
sub.Playlists.List[i] = spec.NewPlaylist(playlist)
|
||||||
|
sub.Playlists.List[i].Owner = user.Name
|
||||||
|
}
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeGetPlaylist(r *http.Request) *spec.Response {
|
||||||
|
playlistID, err := parsing.GetIntParam(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
return spec.NewError(10, "please provide an `id` parameter")
|
||||||
|
}
|
||||||
|
playlist := model.Playlist{}
|
||||||
|
err = c.DB.
|
||||||
|
Where("id = ?", playlistID).
|
||||||
|
Find(&playlist).
|
||||||
|
Error
|
||||||
|
if gorm.IsRecordNotFoundError(err) {
|
||||||
|
return spec.NewError(70, "playlist with id `%d` not found", playlistID)
|
||||||
|
}
|
||||||
|
var tracks []*model.Track
|
||||||
|
c.DB.
|
||||||
|
Joins(`
|
||||||
|
JOIN playlist_items
|
||||||
|
ON playlist_items.track_id = tracks.id
|
||||||
|
`).
|
||||||
|
Where("playlist_items.playlist_id = ?", playlistID).
|
||||||
|
Group("tracks.id").
|
||||||
|
Preload("Album").
|
||||||
|
Find(&tracks)
|
||||||
|
user := r.Context().Value(key.User).(*model.User)
|
||||||
|
sub := spec.NewResponse()
|
||||||
|
sub.Playlist = spec.NewPlaylist(&playlist)
|
||||||
|
sub.Playlist.Owner = user.Name
|
||||||
|
sub.Playlist.List = make([]*spec.TrackChild, len(tracks))
|
||||||
|
for i, track := range tracks {
|
||||||
|
sub.Playlist.List[i] = spec.NewTCTrackByFolder(track, track.Album)
|
||||||
|
}
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeCreatePlaylist(r *http.Request) *spec.Response {
|
||||||
|
user := r.Context().Value(key.User).(*model.User)
|
||||||
|
playlist := &model.Playlist{}
|
||||||
|
c.DB.
|
||||||
|
Select("id").
|
||||||
|
Where("id = ?", parsing.GetIntParamOr(r, "id", 0)).
|
||||||
|
First(playlist)
|
||||||
|
playlist.UserID = user.ID
|
||||||
|
playlist.Name = parsing.GetStrParam(r, "name")
|
||||||
|
c.DB.Save(playlist)
|
||||||
|
sub := spec.NewResponse()
|
||||||
|
tracks, ok := r.URL.Query()["songId"]
|
||||||
|
if !ok {
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
for _, trackIDStr := range tracks {
|
||||||
|
trackID, err := strconv.Atoi(trackIDStr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.DB.Save(&model.PlaylistItem{
|
||||||
|
PlaylistID: playlist.ID,
|
||||||
|
TrackID: trackID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeUpdatePlaylist(r *http.Request) *spec.Response {
|
||||||
|
// user := r.Context().Value(key.User).(*model.User)
|
||||||
|
sub := spec.NewResponse()
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ServeDeletePlaylist(r *http.Request) *spec.Response {
|
||||||
|
// user := r.Context().Value(key.User).(*model.User)
|
||||||
|
sub := spec.NewResponse()
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|||||||
11
server/ctrlsubsonic/spec/construct.go
Normal file
11
server/ctrlsubsonic/spec/construct.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package spec
|
||||||
|
|
||||||
|
import "senan.xyz/g/gonic/model"
|
||||||
|
|
||||||
|
func NewPlaylist(p *model.Playlist) *Playlist {
|
||||||
|
return &Playlist{
|
||||||
|
ID: p.ID,
|
||||||
|
Name: p.Name,
|
||||||
|
Comment: p.Comment,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ type Response struct {
|
|||||||
SearchResultTwo *SearchResultTwo `xml:"searchResult2" json:"searchResult2,omitempty"`
|
SearchResultTwo *SearchResultTwo `xml:"searchResult2" json:"searchResult2,omitempty"`
|
||||||
SearchResultThree *SearchResultThree `xml:"searchResult3" json:"searchResult3,omitempty"`
|
SearchResultThree *SearchResultThree `xml:"searchResult3" json:"searchResult3,omitempty"`
|
||||||
User *User `xml:"user" json:"user,omitempty"`
|
User *User `xml:"user" json:"user,omitempty"`
|
||||||
|
Playlists *Playlists `xml:"playlists" json:"playlists,omitempty"`
|
||||||
|
Playlist *Playlist `xml:"playlist" json:"playlist,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResponse() *Response {
|
func NewResponse() *Response {
|
||||||
@@ -196,3 +198,17 @@ type User struct {
|
|||||||
VideoConversionRole bool `xml:"videoConversionRole,attr" json:"videoConversionRole"`
|
VideoConversionRole bool `xml:"videoConversionRole,attr" json:"videoConversionRole"`
|
||||||
Folder []int `xml:"folder,attr" json:"folder"`
|
Folder []int `xml:"folder,attr" json:"folder"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Playlists struct {
|
||||||
|
List []*Playlist `xml:"playlist" json:"playlist,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Playlist struct {
|
||||||
|
ID int `xml:"id,attr" json:"id,omitempty"`
|
||||||
|
Name string `xml:"name,attr" json:"name,omitempty"`
|
||||||
|
Comment string `xml:"comment,attr" json:"comment,omitempty"`
|
||||||
|
Owner string `xml:"owner,attr" json:"owner,omitempty"`
|
||||||
|
SongCount string `xml:"songCount,attr" json:"songCount,omitempty"`
|
||||||
|
Created string `xml:"created,attr" json:"created,omitempty"`
|
||||||
|
List []*TrackChild `xml:"entry" json:"entry,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -126,6 +126,11 @@ func (s *Server) SetupSubsonic() error {
|
|||||||
rout.Handle("/scrobble{_:(?:\\.view)?}", ctrl.H(ctrl.ServeScrobble))
|
rout.Handle("/scrobble{_:(?:\\.view)?}", ctrl.H(ctrl.ServeScrobble))
|
||||||
rout.Handle("/startScan{_:(?:\\.view)?}", ctrl.H(ctrl.ServeStartScan))
|
rout.Handle("/startScan{_:(?:\\.view)?}", ctrl.H(ctrl.ServeStartScan))
|
||||||
rout.Handle("/getUser{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetUser))
|
rout.Handle("/getUser{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetUser))
|
||||||
|
rout.Handle("/getPlaylists{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetPlaylists))
|
||||||
|
rout.Handle("/getPlaylist{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetPlaylist))
|
||||||
|
rout.Handle("/createPlaylist{_:(?:\\.view)?}", ctrl.H(ctrl.ServeCreatePlaylist))
|
||||||
|
rout.Handle("/updatePlaylist{_:(?:\\.view)?}", ctrl.H(ctrl.ServeUpdatePlaylist))
|
||||||
|
rout.Handle("/deletePlaylist{_:(?:\\.view)?}", ctrl.H(ctrl.ServeDeletePlaylist))
|
||||||
//
|
//
|
||||||
// begin raw
|
// begin raw
|
||||||
rout.Handle("/download{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeStream))
|
rout.Handle("/download{_:(?:\\.view)?}", ctrl.HR(ctrl.ServeStream))
|
||||||
|
|||||||
Reference in New Issue
Block a user