construct subsonic models elsewhere
This commit is contained in:
@@ -13,8 +13,8 @@ import "time"
|
|||||||
type Album struct {
|
type Album struct {
|
||||||
IDBase
|
IDBase
|
||||||
CrudBase
|
CrudBase
|
||||||
AlbumArtist AlbumArtist
|
Artist Artist
|
||||||
AlbumArtistID int `gorm:"index" sql:"default: null; type:int REFERENCES album_artists(id) ON DELETE CASCADE"`
|
ArtistID int `gorm:"index" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
|
||||||
Title string `gorm:"not null; index"`
|
Title string `gorm:"not null; index"`
|
||||||
// an Album having a `Path` is a little weird when browsing by tags
|
// an Album having a `Path` is a little weird when browsing by tags
|
||||||
// (for the most part - the library's folder structure is treated as
|
// (for the most part - the library's folder structure is treated as
|
||||||
@@ -28,8 +28,8 @@ type Album struct {
|
|||||||
IsNew bool `gorm:"-"`
|
IsNew bool `gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlbumArtist represents the AlbumArtists table
|
// Artist represents the Artists table
|
||||||
type AlbumArtist struct {
|
type Artist struct {
|
||||||
IDBase
|
IDBase
|
||||||
CrudBase
|
CrudBase
|
||||||
Name string `gorm:"not null; unique_index"`
|
Name string `gorm:"not null; unique_index"`
|
||||||
@@ -42,9 +42,9 @@ type Track struct {
|
|||||||
CrudBase
|
CrudBase
|
||||||
Album Album
|
Album Album
|
||||||
AlbumID int `gorm:"index" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
|
AlbumID int `gorm:"index" sql:"default: null; type:int REFERENCES albums(id) ON DELETE CASCADE"`
|
||||||
AlbumArtist AlbumArtist
|
Artist Artist
|
||||||
AlbumArtistID int `gorm:"index" sql:"default: null; type:int REFERENCES album_artists(id) ON DELETE CASCADE"`
|
ArtistID int `gorm:"index" sql:"default: null; type:int REFERENCES artists(id) ON DELETE CASCADE"`
|
||||||
Artist string
|
TrackArtist string
|
||||||
Bitrate int
|
Bitrate int
|
||||||
Codec string
|
Codec string
|
||||||
DiscNumber int
|
DiscNumber int
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type Scanner struct {
|
|||||||
curTracks []model.Track
|
curTracks []model.Track
|
||||||
curCover model.Cover
|
curCover model.Cover
|
||||||
curAlbum model.Album
|
curAlbum model.Album
|
||||||
curAArtist model.AlbumArtist
|
curAArtist model.Artist
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(db *gorm.DB, musicPath string) *Scanner {
|
func New(db *gorm.DB, musicPath string) *Scanner {
|
||||||
@@ -43,7 +43,7 @@ func New(db *gorm.DB, musicPath string) *Scanner {
|
|||||||
curTracks: make([]model.Track, 0),
|
curTracks: make([]model.Track, 0),
|
||||||
curCover: model.Cover{},
|
curCover: model.Cover{},
|
||||||
curAlbum: model.Album{},
|
curAlbum: model.Album{},
|
||||||
curAArtist: model.AlbumArtist{},
|
curAArtist: model.Artist{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ func (s *Scanner) MigrateDB() error {
|
|||||||
defer s.tx.Commit()
|
defer s.tx.Commit()
|
||||||
s.tx.AutoMigrate(
|
s.tx.AutoMigrate(
|
||||||
model.Album{},
|
model.Album{},
|
||||||
model.AlbumArtist{},
|
model.Artist{},
|
||||||
model.Track{},
|
model.Track{},
|
||||||
model.Cover{},
|
model.Cover{},
|
||||||
model.User{},
|
model.User{},
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (s *Scanner) callbackPost(path string, info *godirwalk.Dirent) error {
|
|||||||
s.curTracks = make([]model.Track, 0)
|
s.curTracks = make([]model.Track, 0)
|
||||||
s.curCover = model.Cover{}
|
s.curCover = model.Cover{}
|
||||||
s.curAlbum = model.Album{}
|
s.curAlbum = model.Album{}
|
||||||
s.curAArtist = model.AlbumArtist{}
|
s.curAArtist = model.Artist{}
|
||||||
//
|
//
|
||||||
log.Printf("processed folder `%s`\n", path)
|
log.Printf("processed folder `%s`\n", path)
|
||||||
return nil
|
return nil
|
||||||
@@ -156,7 +156,7 @@ func (s *Scanner) handleTrack(it *item) error {
|
|||||||
track.ContentType = it.track.mime
|
track.ContentType = it.track.mime
|
||||||
track.Size = int(it.stat.Size())
|
track.Size = int(it.stat.Size())
|
||||||
track.Title = tags.Title()
|
track.Title = tags.Title()
|
||||||
track.Artist = tags.Artist()
|
track.TrackArtist = tags.Artist()
|
||||||
track.Year = tags.Year()
|
track.Year = tags.Year()
|
||||||
track.FolderID = s.curFolders.PeekID()
|
track.FolderID = s.curFolders.PeekID()
|
||||||
//
|
//
|
||||||
@@ -168,7 +168,7 @@ func (s *Scanner) handleTrack(it *item) error {
|
|||||||
s.curAArtist.Name = tags.AlbumArtist()
|
s.curAArtist.Name = tags.AlbumArtist()
|
||||||
s.tx.Save(&s.curAArtist)
|
s.tx.Save(&s.curAArtist)
|
||||||
}
|
}
|
||||||
track.AlbumArtistID = s.curAArtist.ID
|
track.ArtistID = s.curAArtist.ID
|
||||||
//
|
//
|
||||||
// set album if this is the first track in the folder
|
// set album if this is the first track in the folder
|
||||||
if len(s.curTracks) > 0 {
|
if len(s.curTracks) > 0 {
|
||||||
@@ -189,7 +189,7 @@ func (s *Scanner) handleTrack(it *item) error {
|
|||||||
s.curAlbum.Path = directory
|
s.curAlbum.Path = directory
|
||||||
s.curAlbum.Title = tags.Album()
|
s.curAlbum.Title = tags.Album()
|
||||||
s.curAlbum.Year = tags.Year()
|
s.curAlbum.Year = tags.Year()
|
||||||
s.curAlbum.AlbumArtistID = s.curAArtist.ID
|
s.curAlbum.ArtistID = s.curAArtist.ID
|
||||||
s.curAlbum.IsNew = true
|
s.curAlbum.IsNew = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
54
server/handler/construct_sub_by_folder.go
Normal file
54
server/handler/construct_sub_by_folder.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sentriz/gonic/model"
|
||||||
|
"github.com/sentriz/gonic/server/subsonic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeChildFromFolder(f *model.Folder, parent *model.Folder) *subsonic.Child {
|
||||||
|
return &subsonic.Child{
|
||||||
|
ID: f.ID,
|
||||||
|
Title: f.Name,
|
||||||
|
CoverID: f.CoverID,
|
||||||
|
ParentID: parent.ID,
|
||||||
|
IsDir: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeChildFromTrack(t *model.Track, parent *model.Folder) *subsonic.Child {
|
||||||
|
return &subsonic.Child{
|
||||||
|
ID: t.ID,
|
||||||
|
Album: t.Album.Title,
|
||||||
|
Artist: t.TrackArtist,
|
||||||
|
ContentType: t.ContentType,
|
||||||
|
Path: t.Path,
|
||||||
|
Size: t.Size,
|
||||||
|
Suffix: t.Suffix,
|
||||||
|
Title: t.Title,
|
||||||
|
Track: t.TrackNumber,
|
||||||
|
ParentID: parent.ID,
|
||||||
|
CoverID: parent.CoverID,
|
||||||
|
Duration: 0,
|
||||||
|
IsDir: false,
|
||||||
|
Type: "music",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAlbumFromFolder(f *model.Folder) *subsonic.Album {
|
||||||
|
return &subsonic.Album{
|
||||||
|
ID: f.ID,
|
||||||
|
Title: f.Name,
|
||||||
|
Album: f.Name,
|
||||||
|
CoverID: f.CoverID,
|
||||||
|
ParentID: f.ParentID,
|
||||||
|
Artist: f.Parent.Name,
|
||||||
|
IsDir: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArtistFromFolder(f *model.Folder) *subsonic.Artist {
|
||||||
|
return &subsonic.Artist{
|
||||||
|
ID: f.ID,
|
||||||
|
Name: f.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
43
server/handler/construct_sub_by_tags.go
Normal file
43
server/handler/construct_sub_by_tags.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sentriz/gonic/model"
|
||||||
|
"github.com/sentriz/gonic/server/subsonic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeAlbumFromAlbum(a *model.Album, artist *model.Artist) *subsonic.Album {
|
||||||
|
return &subsonic.Album{
|
||||||
|
ID: a.ID,
|
||||||
|
Name: a.Title,
|
||||||
|
Created: a.CreatedAt,
|
||||||
|
CoverID: a.CoverID,
|
||||||
|
Artist: artist.Name,
|
||||||
|
ArtistID: artist.ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTrackFromTrack(t *model.Track, album *model.Album) *subsonic.Track {
|
||||||
|
return &subsonic.Track{
|
||||||
|
ID: t.ID,
|
||||||
|
Title: t.Title,
|
||||||
|
Artist: t.TrackArtist,
|
||||||
|
TrackNumber: t.TrackNumber,
|
||||||
|
ContentType: t.ContentType,
|
||||||
|
Path: t.Path,
|
||||||
|
Suffix: t.Suffix,
|
||||||
|
CreatedAt: t.CreatedAt,
|
||||||
|
Size: t.Size,
|
||||||
|
Album: album.Title,
|
||||||
|
AlbumID: album.ID,
|
||||||
|
ArtistID: album.Artist.ID,
|
||||||
|
CoverID: album.CoverID,
|
||||||
|
Type: "music",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArtistFromArtist(a *model.Artist) *subsonic.Artist {
|
||||||
|
return &subsonic.Artist{
|
||||||
|
ID: a.ID,
|
||||||
|
Name: a.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
|
||||||
var data templateData
|
var data templateData
|
||||||
c.DB.Table("album_artists").Count(&data.ArtistCount)
|
c.DB.Table("artists").Count(&data.ArtistCount)
|
||||||
c.DB.Table("albums").Count(&data.AlbumCount)
|
c.DB.Table("albums").Count(&data.AlbumCount)
|
||||||
c.DB.Table("tracks").Count(&data.TrackCount)
|
c.DB.Table("tracks").Count(&data.TrackCount)
|
||||||
c.DB.Find(&data.AllUsers)
|
c.DB.Find(&data.AllUsers)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
func (c *Controller) GetIndexes(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) GetIndexes(w http.ResponseWriter, r *http.Request) {
|
||||||
// we are browsing by folder, but the subsonic docs show sub <artist> elements
|
// we are browsing by folder, but the subsonic docs show sub <artist> elements
|
||||||
// for this, so we're going to return root directories as "artists"
|
// for this, so we're going to return root directories as "artists"
|
||||||
var folders []*model.Folder
|
var folders []model.Folder
|
||||||
c.DB.Where("parent_id = ?", 1).Find(&folders)
|
c.DB.Where("parent_id = ?", 1).Find(&folders)
|
||||||
var indexMap = make(map[rune]*subsonic.Index)
|
var indexMap = make(map[rune]*subsonic.Index)
|
||||||
var indexes []*subsonic.Index
|
var indexes []*subsonic.Index
|
||||||
@@ -28,10 +28,8 @@ func (c *Controller) GetIndexes(w http.ResponseWriter, r *http.Request) {
|
|||||||
indexMap[i] = index
|
indexMap[i] = index
|
||||||
indexes = append(indexes, index)
|
indexes = append(indexes, index)
|
||||||
}
|
}
|
||||||
index.Artists = append(index.Artists, &subsonic.Artist{
|
index.Artists = append(index.Artists,
|
||||||
ID: folder.ID,
|
makeArtistFromFolder(&folder))
|
||||||
Name: folder.Name,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Indexes = &subsonic.Indexes{
|
sub.Indexes = &subsonic.Indexes{
|
||||||
@@ -48,61 +46,42 @@ func (c *Controller) GetMusicDirectory(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
childrenObj := []*subsonic.Child{}
|
childrenObj := []*subsonic.Child{}
|
||||||
var cFolder model.Folder
|
var folder model.Folder
|
||||||
c.DB.First(&cFolder, id)
|
c.DB.First(&folder, id)
|
||||||
//
|
//
|
||||||
// start looking for child folders in the current dir
|
// start looking for child childFolders in the current dir
|
||||||
var folders []*model.Folder
|
var childFolders []model.Folder
|
||||||
c.DB.
|
c.DB.
|
||||||
Where("parent_id = ?", id).
|
Where("parent_id = ?", id).
|
||||||
Find(&folders)
|
Find(&childFolders)
|
||||||
for _, folder := range folders {
|
for _, c := range childFolders {
|
||||||
childrenObj = append(childrenObj, &subsonic.Child{
|
childrenObj = append(childrenObj,
|
||||||
Parent: cFolder.ID,
|
makeChildFromFolder(&c, &folder))
|
||||||
ID: folder.ID,
|
|
||||||
Title: folder.Name,
|
|
||||||
IsDir: true,
|
|
||||||
CoverID: folder.CoverID,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// start looking for child tracks in the current dir
|
// start looking for child childTracks in the current dir
|
||||||
var tracks []*model.Track
|
var childTracks []model.Track
|
||||||
c.DB.
|
c.DB.
|
||||||
Where("folder_id = ?", id).
|
Where("folder_id = ?", id).
|
||||||
Preload("Album").
|
Preload("Album").
|
||||||
Order("title").
|
Order("title").
|
||||||
Find(&tracks)
|
Find(&childTracks)
|
||||||
for _, track := range tracks {
|
for _, c := range childTracks {
|
||||||
if getStrParam(r, "c") == "Jamstash" {
|
if getStrParam(r, "c") == "Jamstash" {
|
||||||
// jamstash thinks it can't play flacs
|
// jamstash thinks it can't play flacs
|
||||||
track.ContentType = "audio/mpeg"
|
c.ContentType = "audio/mpeg"
|
||||||
track.Suffix = "mp3"
|
c.Suffix = "mp3"
|
||||||
}
|
}
|
||||||
childrenObj = append(childrenObj, &subsonic.Child{
|
childrenObj = append(childrenObj,
|
||||||
ID: track.ID,
|
makeChildFromTrack(&c, &folder))
|
||||||
Album: track.Album.Title,
|
|
||||||
Artist: track.Artist,
|
|
||||||
ContentType: track.ContentType,
|
|
||||||
CoverID: cFolder.CoverID,
|
|
||||||
Duration: 0,
|
|
||||||
IsDir: false,
|
|
||||||
Parent: cFolder.ID,
|
|
||||||
Path: track.Path,
|
|
||||||
Size: track.Size,
|
|
||||||
Suffix: track.Suffix,
|
|
||||||
Title: track.Title,
|
|
||||||
Track: track.TrackNumber,
|
|
||||||
Type: "music",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// respond section
|
// respond section
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Directory = &subsonic.Directory{
|
sub.Directory = &subsonic.Directory{
|
||||||
ID: cFolder.ID,
|
ID: folder.ID,
|
||||||
Parent: cFolder.ParentID,
|
Parent: folder.ParentID,
|
||||||
Name: cFolder.Name,
|
Name: folder.Name,
|
||||||
Children: childrenObj,
|
Children: childrenObj,
|
||||||
}
|
}
|
||||||
respond(w, r, sub)
|
respond(w, r, sub)
|
||||||
@@ -152,28 +131,18 @@ func (c *Controller) GetAlbumList(w http.ResponseWriter, r *http.Request) {
|
|||||||
))
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var folders []*model.Folder
|
var folders []model.Folder
|
||||||
q.
|
q.
|
||||||
Where("folders.has_tracks = 1").
|
Where("folders.has_tracks = 1").
|
||||||
Offset(getIntParamOr(r, "offset", 0)).
|
Offset(getIntParamOr(r, "offset", 0)).
|
||||||
Limit(getIntParamOr(r, "size", 10)).
|
Limit(getIntParamOr(r, "size", 10)).
|
||||||
Preload("Parent").
|
Preload("Parent").
|
||||||
Find(&folders)
|
Find(&folders)
|
||||||
listObj := []*subsonic.Album{}
|
|
||||||
for _, folder := range folders {
|
|
||||||
listObj = append(listObj, &subsonic.Album{
|
|
||||||
ID: folder.ID,
|
|
||||||
Title: folder.Name,
|
|
||||||
Album: folder.Name,
|
|
||||||
CoverID: folder.CoverID,
|
|
||||||
ParentID: folder.ParentID,
|
|
||||||
IsDir: true,
|
|
||||||
Artist: folder.Parent.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Albums = &subsonic.Albums{
|
sub.Albums = &subsonic.Albums{}
|
||||||
List: listObj,
|
for _, folder := range folders {
|
||||||
|
sub.Albums.List = append(sub.Albums.List,
|
||||||
|
makeAlbumFromFolder(&folder))
|
||||||
}
|
}
|
||||||
respond(w, r, sub)
|
respond(w, r, sub)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Controller) GetArtists(w http.ResponseWriter, r *http.Request) {
|
func (c *Controller) GetArtists(w http.ResponseWriter, r *http.Request) {
|
||||||
var artists []*model.AlbumArtist
|
var artists []model.Artist
|
||||||
c.DB.Find(&artists)
|
c.DB.Find(&artists)
|
||||||
var indexMap = make(map[rune]*subsonic.Index)
|
var indexMap = make(map[rune]*subsonic.Index)
|
||||||
var indexes subsonic.Artists
|
var indexes subsonic.Artists
|
||||||
@@ -26,10 +26,8 @@ func (c *Controller) GetArtists(w http.ResponseWriter, r *http.Request) {
|
|||||||
indexMap[i] = index
|
indexMap[i] = index
|
||||||
indexes.List = append(indexes.List, index)
|
indexes.List = append(indexes.List, index)
|
||||||
}
|
}
|
||||||
index.Artists = append(index.Artists, &subsonic.Artist{
|
index.Artists = append(index.Artists,
|
||||||
ID: artist.ID,
|
makeArtistFromArtist(&artist))
|
||||||
Name: artist.Name,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Artists = &indexes
|
sub.Artists = &indexes
|
||||||
@@ -42,26 +40,15 @@ func (c *Controller) GetArtist(w http.ResponseWriter, r *http.Request) {
|
|||||||
respondError(w, r, 10, "please provide an `id` parameter")
|
respondError(w, r, 10, "please provide an `id` parameter")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var artist model.AlbumArtist
|
var artist model.Artist
|
||||||
c.DB.
|
c.DB.
|
||||||
Preload("Albums").
|
Preload("Albums").
|
||||||
First(&artist, id)
|
First(&artist, id)
|
||||||
albumsObj := []*subsonic.Album{}
|
|
||||||
for _, album := range artist.Albums {
|
|
||||||
albumsObj = append(albumsObj, &subsonic.Album{
|
|
||||||
ID: album.ID,
|
|
||||||
Name: album.Title,
|
|
||||||
Created: album.CreatedAt,
|
|
||||||
Artist: artist.Name,
|
|
||||||
ArtistID: artist.ID,
|
|
||||||
CoverID: album.CoverID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Artist = &subsonic.Artist{
|
sub.Artist = makeArtistFromArtist(&artist)
|
||||||
ID: artist.ID,
|
for _, album := range artist.Albums {
|
||||||
Name: artist.Name,
|
sub.Artist.Albums = append(sub.Artist.Albums,
|
||||||
Albums: albumsObj,
|
makeAlbumFromAlbum(&album, &artist))
|
||||||
}
|
}
|
||||||
respond(w, r, sub)
|
respond(w, r, sub)
|
||||||
}
|
}
|
||||||
@@ -73,39 +60,22 @@ func (c *Controller) GetAlbum(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var album model.Album
|
var album model.Album
|
||||||
c.DB.
|
err = c.DB.
|
||||||
Preload("AlbumArtist").
|
Preload("Artist").
|
||||||
Preload("Tracks", func(db *gorm.DB) *gorm.DB {
|
Preload("Tracks", func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Order("tracks.track_number")
|
return db.Order("tracks.track_number")
|
||||||
}).
|
}).
|
||||||
First(&album, id)
|
First(&album, id).
|
||||||
tracksObj := []*subsonic.Track{}
|
Error
|
||||||
for _, track := range album.Tracks {
|
if gorm.IsRecordNotFoundError(err) {
|
||||||
tracksObj = append(tracksObj, &subsonic.Track{
|
respondError(w, r, 10, "couldn't find an album with that id")
|
||||||
ID: track.ID,
|
return
|
||||||
Title: track.Title,
|
|
||||||
Artist: track.Artist, // track artist
|
|
||||||
TrackNo: track.TrackNumber,
|
|
||||||
ContentType: track.ContentType,
|
|
||||||
Path: track.Path,
|
|
||||||
Suffix: track.Suffix,
|
|
||||||
Created: track.CreatedAt,
|
|
||||||
Size: track.Size,
|
|
||||||
Album: album.Title,
|
|
||||||
AlbumID: album.ID,
|
|
||||||
ArtistID: album.AlbumArtist.ID, // album artist
|
|
||||||
CoverID: album.CoverID,
|
|
||||||
Type: "music",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.Album = &subsonic.Album{
|
sub.Album = makeAlbumFromAlbum(&album, &album.Artist)
|
||||||
ID: album.ID,
|
for _, track := range album.Tracks {
|
||||||
Name: album.Title,
|
sub.Album.Tracks = append(sub.Album.Tracks,
|
||||||
CoverID: album.CoverID,
|
makeTrackFromTrack(&track, &album))
|
||||||
Created: album.CreatedAt,
|
|
||||||
Artist: album.AlbumArtist.Name,
|
|
||||||
Tracks: tracksObj,
|
|
||||||
}
|
}
|
||||||
respond(w, r, sub)
|
respond(w, r, sub)
|
||||||
}
|
}
|
||||||
@@ -122,9 +92,9 @@ func (c *Controller) GetAlbumListTwo(w http.ResponseWriter, r *http.Request) {
|
|||||||
switch listType {
|
switch listType {
|
||||||
case "alphabeticalByArtist":
|
case "alphabeticalByArtist":
|
||||||
q = q.Joins(`
|
q = q.Joins(`
|
||||||
JOIN album_artists
|
JOIN artists
|
||||||
ON albums.album_artist_id = album_artists.id`)
|
ON albums.artist_id = artists.id`)
|
||||||
q = q.Order("album_artists.name")
|
q = q.Order("artists.name")
|
||||||
case "alphabeticalByName":
|
case "alphabeticalByName":
|
||||||
q = q.Order("title")
|
q = q.Order("title")
|
||||||
case "byYear":
|
case "byYear":
|
||||||
@@ -157,26 +127,17 @@ func (c *Controller) GetAlbumListTwo(w http.ResponseWriter, r *http.Request) {
|
|||||||
))
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var albums []*model.Album
|
var albums []model.Album
|
||||||
q.
|
q.
|
||||||
Offset(getIntParamOr(r, "offset", 0)).
|
Offset(getIntParamOr(r, "offset", 0)).
|
||||||
Limit(getIntParamOr(r, "size", 10)).
|
Limit(getIntParamOr(r, "size", 10)).
|
||||||
Preload("AlbumArtist").
|
Preload("Artist").
|
||||||
Find(&albums)
|
Find(&albums)
|
||||||
listObj := []*subsonic.Album{}
|
|
||||||
for _, album := range albums {
|
|
||||||
listObj = append(listObj, &subsonic.Album{
|
|
||||||
ID: album.ID,
|
|
||||||
Name: album.Title,
|
|
||||||
Created: album.CreatedAt,
|
|
||||||
CoverID: album.CoverID,
|
|
||||||
Artist: album.AlbumArtist.Name,
|
|
||||||
ArtistID: album.AlbumArtist.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sub := subsonic.NewResponse()
|
sub := subsonic.NewResponse()
|
||||||
sub.AlbumsTwo = &subsonic.Albums{
|
sub.AlbumsTwo = &subsonic.Albums{}
|
||||||
List: listObj,
|
for _, album := range albums {
|
||||||
|
sub.AlbumsTwo.List = append(sub.AlbumsTwo.List,
|
||||||
|
makeAlbumFromAlbum(&album, &album.Artist))
|
||||||
}
|
}
|
||||||
respond(w, r, sub)
|
respond(w, r, sub)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,14 +96,14 @@ func (c *Controller) Scrobble(w http.ResponseWriter, r *http.Request) {
|
|||||||
// fetch user to get lastfm session
|
// fetch user to get lastfm session
|
||||||
user := r.Context().Value(contextUserKey).(*model.User)
|
user := r.Context().Value(contextUserKey).(*model.User)
|
||||||
if user.LastFMSession == "" {
|
if user.LastFMSession == "" {
|
||||||
respondError(w, r, 0, fmt.Sprintf("no last.fm session for this user: %v", err))
|
respondError(w, r, 0, "you don't have a last.fm session")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// fetch track for getting info to send to last.fm function
|
// fetch track for getting info to send to last.fm function
|
||||||
var track model.Track
|
var track model.Track
|
||||||
c.DB.
|
c.DB.
|
||||||
Preload("Album").
|
Preload("Album").
|
||||||
Preload("AlbumArtist").
|
Preload("Artist").
|
||||||
First(&track, id)
|
First(&track, id)
|
||||||
// scrobble with above info
|
// scrobble with above info
|
||||||
err = lastfm.Scrobble(
|
err = lastfm.Scrobble(
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ func checkCredentialsToken(password, token, salt string) bool {
|
|||||||
return token == expToken
|
return token == expToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCredentialsBasic(password, givenPassword string) bool {
|
func checkCredentialsBasic(password, given string) bool {
|
||||||
if givenPassword[:4] == "enc:" {
|
if given[:4] == "enc:" {
|
||||||
bytes, _ := hex.DecodeString(givenPassword[4:])
|
bytes, _ := hex.DecodeString(given[4:])
|
||||||
givenPassword = string(bytes)
|
given = string(bytes)
|
||||||
}
|
}
|
||||||
return password == givenPassword
|
return password == given
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) WithValidSubsonicArgs(next http.HandlerFunc) http.HandlerFunc {
|
func (c *Controller) WithValidSubsonicArgs(next http.HandlerFunc) http.HandlerFunc {
|
||||||
@@ -62,7 +62,6 @@ func (c *Controller) WithValidSubsonicArgs(next http.HandlerFunc) http.HandlerFu
|
|||||||
}
|
}
|
||||||
user := c.GetUserFromName(username)
|
user := c.GetUserFromName(username)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
// the user does not exist
|
|
||||||
respondError(w, r, 40, "invalid username")
|
respondError(w, r, 40, "invalid username")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ func Scrobble(apiKey, secret, session string, track *model.Track,
|
|||||||
}
|
}
|
||||||
params.Add("api_key", apiKey)
|
params.Add("api_key", apiKey)
|
||||||
params.Add("sk", session)
|
params.Add("sk", session)
|
||||||
params.Add("artist", track.Artist)
|
params.Add("artist", track.TrackArtist)
|
||||||
params.Add("track", track.Title)
|
params.Add("track", track.Title)
|
||||||
params.Add("album", track.Album.Title)
|
params.Add("album", track.Album.Title)
|
||||||
params.Add("albumArtist", track.AlbumArtist.Name)
|
params.Add("albumArtist", track.Artist.Name)
|
||||||
params.Add("trackNumber", strconv.Itoa(track.TrackNumber))
|
params.Add("trackNumber", strconv.Itoa(track.TrackNumber))
|
||||||
params.Add("api_sig", getParamSignature(params, secret))
|
params.Add("api_sig", getParamSignature(params, secret))
|
||||||
_, err := makeRequest("POST", params)
|
_, err := makeRequest("POST", params)
|
||||||
|
|||||||
23
server/lastfm/lastfm_test.go
Normal file
23
server/lastfm/lastfm_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package lastfm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetParamSignature(t *testing.T) {
|
||||||
|
params := url.Values{}
|
||||||
|
params.Add("ccc", "CCC")
|
||||||
|
params.Add("bbb", "BBB")
|
||||||
|
params.Add("aaa", "AAA")
|
||||||
|
params.Add("ddd", "DDD")
|
||||||
|
actual := getParamSignature(params, "secret")
|
||||||
|
expected := fmt.Sprintf("%x", md5.Sum([]byte(
|
||||||
|
"aaaAAAbbbBBBcccCCCdddDDDsecret",
|
||||||
|
)))
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected %x, got %s", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package subsonic
|
package subsonic
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Albums struct {
|
type Albums struct {
|
||||||
List []*Album `xml:"album" json:"album,omitempty"`
|
List []*Album `xml:"album" json:"album,omitempty"`
|
||||||
@@ -37,7 +39,7 @@ type Track struct {
|
|||||||
Artist string `xml:"artist,attr,omitempty" json:"artist"`
|
Artist string `xml:"artist,attr,omitempty" json:"artist"`
|
||||||
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir"`
|
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir"`
|
||||||
CoverID int `xml:"coverArt,attr,omitempty" json:"coverArt"`
|
CoverID int `xml:"coverArt,attr,omitempty" json:"coverArt"`
|
||||||
Created time.Time `xml:"created,attr,omitempty" json:"created"`
|
CreatedAt time.Time `xml:"created,attr,omitempty" json:"created"`
|
||||||
Duration int `xml:"duration,attr,omitempty" json:"duration"`
|
Duration int `xml:"duration,attr,omitempty" json:"duration"`
|
||||||
Genre string `xml:"genre,attr,omitempty" json:"genre"`
|
Genre string `xml:"genre,attr,omitempty" json:"genre"`
|
||||||
Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate"`
|
Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate"`
|
||||||
@@ -48,7 +50,7 @@ type Track struct {
|
|||||||
Path string `xml:"path,attr,omitempty" json:"path"`
|
Path string `xml:"path,attr,omitempty" json:"path"`
|
||||||
AlbumID int `xml:"albumId,attr,omitempty" json:"albumId"`
|
AlbumID int `xml:"albumId,attr,omitempty" json:"albumId"`
|
||||||
ArtistID int `xml:"artistId,attr,omitempty" json:"artistId"`
|
ArtistID int `xml:"artistId,attr,omitempty" json:"artistId"`
|
||||||
TrackNo int `xml:"track,attr,omitempty" json:"track"`
|
TrackNumber int `xml:"track,attr,omitempty" json:"track"`
|
||||||
Type string `xml:"type,attr,omitempty" json:"type"`
|
Type string `xml:"type,attr,omitempty" json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ type Directory struct {
|
|||||||
|
|
||||||
type Child struct {
|
type Child struct {
|
||||||
ID int `xml:"id,attr,omitempty" json:"id,omitempty"`
|
ID int `xml:"id,attr,omitempty" json:"id,omitempty"`
|
||||||
Parent int `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
ParentID int `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
||||||
Title string `xml:"title,attr,omitempty" json:"title,omitempty"`
|
Title string `xml:"title,attr,omitempty" json:"title,omitempty"`
|
||||||
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir,omitempty"`
|
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir,omitempty"`
|
||||||
Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
|
Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user