move the id type into spec
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
|
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
|
||||||
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
|
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
|
||||||
|
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||||
"go.senan.xyz/gonic/server/db"
|
"go.senan.xyz/gonic/server/db"
|
||||||
"go.senan.xyz/gonic/server/lastfm"
|
"go.senan.xyz/gonic/server/lastfm"
|
||||||
)
|
)
|
||||||
@@ -263,9 +264,11 @@ func (c *Controller) ServeGetArtistInfoTwo(r *http.Request) *spec.Response {
|
|||||||
if gorm.IsRecordNotFoundError(err) && !inclNotPresent {
|
if gorm.IsRecordNotFoundError(err) && !inclNotPresent {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
similar := &spec.SimilarArtist{ID: -1}
|
similar := &spec.SimilarArtist{
|
||||||
|
ID: specid.ID{Type: specid.Artist, Value: -1},
|
||||||
|
}
|
||||||
if artist.ID != 0 {
|
if artist.ID != 0 {
|
||||||
similar.ID = artist.ID
|
similar.ID = artist.SID()
|
||||||
}
|
}
|
||||||
similar.Name = similarInfo.Name
|
similar.Name = similarInfo.Name
|
||||||
similar.AlbumCount = artist.AlbumCount
|
similar.AlbumCount = artist.AlbumCount
|
||||||
|
|||||||
@@ -30,15 +30,15 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"go.senan.xyz/gonic/server/ids"
|
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// some thin wrappers
|
// some thin wrappers
|
||||||
// may be needed when cleaning up parse() below
|
// may be needed when cleaning up parse() below
|
||||||
func parseStr(in string) (string, error) { return in, nil }
|
func parseStr(in string) (string, error) { return in, nil }
|
||||||
func parseInt(in string) (int, error) { return strconv.Atoi(in) }
|
func parseInt(in string) (int, error) { return strconv.Atoi(in) }
|
||||||
func parseID(in string) (ids.IDV, error) { return ids.Parse(in) }
|
func parseID(in string) (specid.ID, error) { return specid.New(in) }
|
||||||
func parseBool(in string) (bool, error) { return strconv.ParseBool(in) }
|
func parseBool(in string) (bool, error) { return strconv.ParseBool(in) }
|
||||||
|
|
||||||
func parse(values []string, i interface{}) error {
|
func parse(values []string, i interface{}) error {
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
@@ -50,7 +50,7 @@ func parse(values []string, i interface{}) error {
|
|||||||
*v, err = parseStr(values[0])
|
*v, err = parseStr(values[0])
|
||||||
case *int:
|
case *int:
|
||||||
*v, err = parseInt(values[0])
|
*v, err = parseInt(values[0])
|
||||||
case *ids.IDV:
|
case *specid.ID:
|
||||||
*v, err = parseID(values[0])
|
*v, err = parseID(values[0])
|
||||||
case *bool:
|
case *bool:
|
||||||
*v, err = parseBool(values[0])
|
*v, err = parseBool(values[0])
|
||||||
@@ -70,7 +70,7 @@ func parse(values []string, i interface{}) error {
|
|||||||
}
|
}
|
||||||
*v = append(*v, parsed)
|
*v = append(*v, parsed)
|
||||||
}
|
}
|
||||||
case *[]ids.IDV:
|
case *[]specid.ID:
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
parsed, err := parseID(value)
|
parsed, err := parseID(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -229,56 +229,56 @@ func (p Params) GetFirstOrIntList(or []int, keys ...string) []int {
|
|||||||
return or
|
return or
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** begin ids.IDV {get, get first, get or, get first or}
|
// ** begin specid.ID {get, get first, get or, get first or}
|
||||||
|
|
||||||
func (p Params) GetID(key string) (ids.IDV, error) {
|
func (p Params) GetID(key string) (specid.ID, error) {
|
||||||
var ret ids.IDV
|
var ret specid.ID
|
||||||
return ret, parse(p.get(key), &ret)
|
return ret, parse(p.get(key), &ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetFirstID(keys ...string) (ids.IDV, error) {
|
func (p Params) GetFirstID(keys ...string) (specid.ID, error) {
|
||||||
var ret ids.IDV
|
var ret specid.ID
|
||||||
return ret, parse(p.getFirst(keys), &ret)
|
return ret, parse(p.getFirst(keys), &ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetOrID(key string, or ids.IDV) ids.IDV {
|
func (p Params) GetOrID(key string, or specid.ID) specid.ID {
|
||||||
var ret ids.IDV
|
var ret specid.ID
|
||||||
if err := parse(p.get(key), &ret); err == nil {
|
if err := parse(p.get(key), &ret); err == nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
return or
|
return or
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetFirstOrID(or ids.IDV, keys ...string) ids.IDV {
|
func (p Params) GetFirstOrID(or specid.ID, keys ...string) specid.ID {
|
||||||
var ret ids.IDV
|
var ret specid.ID
|
||||||
if err := parse(p.getFirst(keys), &ret); err == nil {
|
if err := parse(p.getFirst(keys), &ret); err == nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
return or
|
return or
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** begin []ids.IDV {get, get first, get or, get first or}
|
// ** begin []specid.ID {get, get first, get or, get first or}
|
||||||
|
|
||||||
func (p Params) GetIDList(key string) ([]ids.IDV, error) {
|
func (p Params) GetIDList(key string) ([]specid.ID, error) {
|
||||||
var ret []ids.IDV
|
var ret []specid.ID
|
||||||
return ret, parse(p.get(key), &ret)
|
return ret, parse(p.get(key), &ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetFirstIDList(keys ...string) ([]ids.IDV, error) {
|
func (p Params) GetFirstIDList(keys ...string) ([]specid.ID, error) {
|
||||||
var ret []ids.IDV
|
var ret []specid.ID
|
||||||
return ret, parse(p.getFirst(keys), &ret)
|
return ret, parse(p.getFirst(keys), &ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetOrIDList(key string, or []ids.IDV) []ids.IDV {
|
func (p Params) GetOrIDList(key string, or []specid.ID) []specid.ID {
|
||||||
var ret []ids.IDV
|
var ret []specid.ID
|
||||||
if err := parse(p.get(key), &ret); err == nil {
|
if err := parse(p.get(key), &ret); err == nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
return or
|
return or
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Params) GetFirstOrIDList(or []ids.IDV, keys ...string) []ids.IDV {
|
func (p Params) GetFirstOrIDList(or []specid.ID, keys ...string) []specid.ID {
|
||||||
var ret []ids.IDV
|
var ret []specid.ID
|
||||||
if err := parse(p.getFirst(keys), &ret); err == nil {
|
if err := parse(p.getFirst(keys), &ret); err == nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,42 +3,41 @@ package spec
|
|||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"go.senan.xyz/gonic/server/ctrlsubsonic/params"
|
|
||||||
"go.senan.xyz/gonic/server/db"
|
"go.senan.xyz/gonic/server/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAlbumByFolder(f *db.Album) *Album {
|
func NewAlbumByFolder(f *db.Album) *Album {
|
||||||
a := &Album{
|
a := &Album{
|
||||||
Artist: f.Parent.RightPath,
|
Artist: f.Parent.RightPath,
|
||||||
ID: params.IDAlbum(f.ID),
|
ID: f.SID(),
|
||||||
IsDir: true,
|
IsDir: true,
|
||||||
ParentID: params.IDAlbum(f.ParentID),
|
ParentID: f.ParentSID(),
|
||||||
Title: f.RightPath,
|
Title: f.RightPath,
|
||||||
TrackCount: f.ChildCount,
|
TrackCount: f.ChildCount,
|
||||||
}
|
}
|
||||||
if f.Cover != "" {
|
if f.Cover != "" {
|
||||||
a.CoverID = f.ID
|
a.CoverID = f.SID()
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCAlbumByFolder(f *db.Album) *TrackChild {
|
func NewTCAlbumByFolder(f *db.Album) *TrackChild {
|
||||||
trCh := &TrackChild{
|
trCh := &TrackChild{
|
||||||
ID: params.IDAlbum(f.ID),
|
ID: f.SID(),
|
||||||
IsDir: true,
|
IsDir: true,
|
||||||
Title: f.RightPath,
|
Title: f.RightPath,
|
||||||
ParentID: params.IDAlbum(f.ParentID),
|
ParentID: f.ParentSID(),
|
||||||
CreatedAt: f.UpdatedAt,
|
CreatedAt: f.UpdatedAt,
|
||||||
}
|
}
|
||||||
if f.Cover != "" {
|
if f.Cover != "" {
|
||||||
trCh.CoverID = f.ID
|
trCh.CoverID = f.SID()
|
||||||
}
|
}
|
||||||
return trCh
|
return trCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCTrackByFolder(t *db.Track, parent *db.Album) *TrackChild {
|
func NewTCTrackByFolder(t *db.Track, parent *db.Album) *TrackChild {
|
||||||
trCh := &TrackChild{
|
trCh := &TrackChild{
|
||||||
ID: params.IDTrack(t.ID),
|
ID: t.SID(),
|
||||||
ContentType: t.MIME(),
|
ContentType: t.MIME(),
|
||||||
Suffix: t.Ext(),
|
Suffix: t.Ext(),
|
||||||
Size: t.Size,
|
Size: t.Size,
|
||||||
@@ -51,7 +50,7 @@ func NewTCTrackByFolder(t *db.Track, parent *db.Album) *TrackChild {
|
|||||||
parent.RightPath,
|
parent.RightPath,
|
||||||
t.Filename,
|
t.Filename,
|
||||||
),
|
),
|
||||||
ParentID: params.IDAlbum(parent.ID),
|
ParentID: parent.SID(),
|
||||||
Duration: t.Length,
|
Duration: t.Length,
|
||||||
Bitrate: t.Bitrate,
|
Bitrate: t.Bitrate,
|
||||||
IsDir: false,
|
IsDir: false,
|
||||||
@@ -59,7 +58,7 @@ func NewTCTrackByFolder(t *db.Track, parent *db.Album) *TrackChild {
|
|||||||
CreatedAt: t.CreatedAt,
|
CreatedAt: t.CreatedAt,
|
||||||
}
|
}
|
||||||
if parent.Cover != "" {
|
if parent.Cover != "" {
|
||||||
trCh.CoverID = parent.ID
|
trCh.CoverID = parent.SID()
|
||||||
}
|
}
|
||||||
if t.Album != nil {
|
if t.Album != nil {
|
||||||
trCh.Album = t.Album.RightPath
|
trCh.Album = t.Album.RightPath
|
||||||
@@ -73,7 +72,7 @@ func NewArtistByFolder(f *db.Album) *Artist {
|
|||||||
// from an "album" where
|
// from an "album" where
|
||||||
// maybe TODO: rename the Album model to Folder
|
// maybe TODO: rename the Album model to Folder
|
||||||
return &Artist{
|
return &Artist{
|
||||||
ID: params.IDAlbum(f.ID),
|
ID: f.SID(),
|
||||||
Name: f.RightPath,
|
Name: f.RightPath,
|
||||||
AlbumCount: f.ChildCount,
|
AlbumCount: f.ChildCount,
|
||||||
}
|
}
|
||||||
@@ -81,13 +80,13 @@ func NewArtistByFolder(f *db.Album) *Artist {
|
|||||||
|
|
||||||
func NewDirectoryByFolder(f *db.Album, children []*TrackChild) *Directory {
|
func NewDirectoryByFolder(f *db.Album, children []*TrackChild) *Directory {
|
||||||
dir := &Directory{
|
dir := &Directory{
|
||||||
ID: f.ID,
|
ID: f.SID(),
|
||||||
Name: f.RightPath,
|
Name: f.RightPath,
|
||||||
Children: children,
|
Children: children,
|
||||||
}
|
}
|
||||||
// don't show the root dir as a parent
|
// don't show the root dir as a parent
|
||||||
if f.ParentID != 1 {
|
if f.ParentID != 1 {
|
||||||
dir.ParentID = f.ParentID
|
dir.ParentID = f.ParentSID()
|
||||||
}
|
}
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
func NewAlbumByTags(a *db.Album, artist *db.Artist) *Album {
|
func NewAlbumByTags(a *db.Album, artist *db.Artist) *Album {
|
||||||
ret := &Album{
|
ret := &Album{
|
||||||
Created: a.ModifiedAt,
|
Created: a.ModifiedAt,
|
||||||
ID: a.ID,
|
ID: a.SID(),
|
||||||
Name: a.TagTitle,
|
Name: a.TagTitle,
|
||||||
Year: a.TagYear,
|
Year: a.TagYear,
|
||||||
TrackCount: a.ChildCount,
|
TrackCount: a.ChildCount,
|
||||||
@@ -18,21 +18,21 @@ func NewAlbumByTags(a *db.Album, artist *db.Artist) *Album {
|
|||||||
ret.Genre = a.TagGenre.Name
|
ret.Genre = a.TagGenre.Name
|
||||||
}
|
}
|
||||||
if a.Cover != "" {
|
if a.Cover != "" {
|
||||||
ret.CoverID = a.ID
|
ret.CoverID = a.SID()
|
||||||
}
|
}
|
||||||
if artist != nil {
|
if artist != nil {
|
||||||
ret.Artist = artist.Name
|
ret.Artist = artist.Name
|
||||||
ret.ArtistID = artist.ID
|
ret.ArtistID = artist.SID()
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTrackByTags(t *db.Track, album *db.Album) *TrackChild {
|
func NewTrackByTags(t *db.Track, album *db.Album) *TrackChild {
|
||||||
ret := &TrackChild{
|
ret := &TrackChild{
|
||||||
ID: t.ID,
|
ID: t.SID(),
|
||||||
ContentType: t.MIME(),
|
ContentType: t.MIME(),
|
||||||
Suffix: t.Ext(),
|
Suffix: t.Ext(),
|
||||||
ParentID: t.AlbumID,
|
ParentID: t.AlbumSID(),
|
||||||
CreatedAt: t.CreatedAt,
|
CreatedAt: t.CreatedAt,
|
||||||
Size: t.Size,
|
Size: t.Size,
|
||||||
Title: t.TagTitle,
|
Title: t.TagTitle,
|
||||||
@@ -45,16 +45,16 @@ func NewTrackByTags(t *db.Track, album *db.Album) *TrackChild {
|
|||||||
t.Filename,
|
t.Filename,
|
||||||
),
|
),
|
||||||
Album: album.TagTitle,
|
Album: album.TagTitle,
|
||||||
AlbumID: album.ID,
|
AlbumID: album.SID(),
|
||||||
Duration: t.Length,
|
Duration: t.Length,
|
||||||
Bitrate: t.Bitrate,
|
Bitrate: t.Bitrate,
|
||||||
Type: "music",
|
Type: "music",
|
||||||
}
|
}
|
||||||
if album.Cover != "" {
|
if album.Cover != "" {
|
||||||
ret.CoverID = album.ID
|
ret.CoverID = album.SID()
|
||||||
}
|
}
|
||||||
if album.TagArtist != nil {
|
if album.TagArtist != nil {
|
||||||
ret.ArtistID = album.TagArtist.ID
|
ret.ArtistID = album.TagArtist.SID()
|
||||||
}
|
}
|
||||||
// replace tags that we're present
|
// replace tags that we're present
|
||||||
if ret.Title == "" {
|
if ret.Title == "" {
|
||||||
@@ -71,7 +71,7 @@ func NewTrackByTags(t *db.Track, album *db.Album) *TrackChild {
|
|||||||
|
|
||||||
func NewArtistByTags(a *db.Artist) *Artist {
|
func NewArtistByTags(a *db.Artist) *Artist {
|
||||||
return &Artist{
|
return &Artist{
|
||||||
ID: a.ID,
|
ID: a.SID(),
|
||||||
Name: a.Name,
|
Name: a.Name,
|
||||||
AlbumCount: a.AlbumCount,
|
AlbumCount: a.AlbumCount,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||||
"go.senan.xyz/gonic/version"
|
"go.senan.xyz/gonic/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,15 +91,15 @@ type Albums struct {
|
|||||||
|
|
||||||
type Album struct {
|
type Album struct {
|
||||||
// common
|
// common
|
||||||
ID string `xml:"id,attr,omitempty" json:"id"`
|
ID specid.ID `xml:"id,attr,omitempty" json:"id"`
|
||||||
CoverID int `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty,string"`
|
CoverID specid.ID `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
||||||
ArtistID string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
ArtistID specid.ID `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
||||||
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
||||||
// browsing by folder (eg. getAlbumList)
|
// browsing by folder (eg. getAlbumList)
|
||||||
Title string `xml:"title,attr" json:"title"`
|
Title string `xml:"title,attr" json:"title"`
|
||||||
Album string `xml:"album,attr" json:"album"`
|
Album string `xml:"album,attr" json:"album"`
|
||||||
ParentID string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
ParentID specid.ID `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
||||||
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir,omitempty"`
|
IsDir bool `xml:"isDir,attr,omitempty" json:"isDir,omitempty"`
|
||||||
// browsing by tags (eg. getAlbumList2)
|
// browsing by tags (eg. getAlbumList2)
|
||||||
Name string `xml:"name,attr" json:"name"`
|
Name string `xml:"name,attr" json:"name"`
|
||||||
TrackCount int `xml:"songCount,attr" json:"songCount"`
|
TrackCount int `xml:"songCount,attr" json:"songCount"`
|
||||||
@@ -119,19 +120,19 @@ type TracksByGenre struct {
|
|||||||
|
|
||||||
type TrackChild struct {
|
type TrackChild struct {
|
||||||
Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
|
Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
|
||||||
AlbumID string `xml:"albumId,attr,omitempty" json:"albumId,omitempty"`
|
AlbumID specid.ID `xml:"albumId,attr,omitempty" json:"albumId,omitempty"`
|
||||||
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
||||||
ArtistID string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
ArtistID specid.ID `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
||||||
Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate,omitempty"`
|
Bitrate int `xml:"bitRate,attr,omitempty" json:"bitRate,omitempty"`
|
||||||
ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"`
|
ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"`
|
||||||
CoverID int `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty,string"`
|
CoverID specid.ID `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
||||||
CreatedAt time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
|
CreatedAt time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
|
||||||
Duration int `xml:"duration,attr,omitempty" json:"duration,omitempty"`
|
Duration int `xml:"duration,attr,omitempty" json:"duration,omitempty"`
|
||||||
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`
|
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`
|
||||||
ID string `xml:"id,attr,omitempty" json:"id,omitempty"`
|
ID specid.ID `xml:"id,attr,omitempty" json:"id,omitempty"`
|
||||||
IsDir bool `xml:"isDir,attr" json:"isDir"`
|
IsDir bool `xml:"isDir,attr" json:"isDir"`
|
||||||
IsVideo bool `xml:"isVideo,attr" json:"isVideo"`
|
IsVideo bool `xml:"isVideo,attr" json:"isVideo"`
|
||||||
ParentID string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
ParentID specid.ID `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
||||||
Path string `xml:"path,attr,omitempty" json:"path,omitempty"`
|
Path string `xml:"path,attr,omitempty" json:"path,omitempty"`
|
||||||
Size int `xml:"size,attr,omitempty" json:"size,omitempty"`
|
Size int `xml:"size,attr,omitempty" json:"size,omitempty"`
|
||||||
Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"`
|
Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"`
|
||||||
@@ -147,11 +148,11 @@ type Artists struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Artist struct {
|
type Artist struct {
|
||||||
ID string `xml:"id,attr,omitempty" json:"id"`
|
ID specid.ID `xml:"id,attr,omitempty" json:"id"`
|
||||||
Name string `xml:"name,attr" json:"name"`
|
Name string `xml:"name,attr" json:"name"`
|
||||||
CoverID int `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty,string"`
|
CoverID specid.ID `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
||||||
AlbumCount int `xml:"albumCount,attr" json:"albumCount"`
|
AlbumCount int `xml:"albumCount,attr" json:"albumCount"`
|
||||||
Albums []*Album `xml:"album,omitempty" json:"album,omitempty"`
|
Albums []*Album `xml:"album,omitempty" json:"album,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Indexes struct {
|
type Indexes struct {
|
||||||
@@ -166,8 +167,8 @@ type Index struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Directory struct {
|
type Directory struct {
|
||||||
ID string `xml:"id,attr,omitempty" json:"id"`
|
ID specid.ID `xml:"id,attr,omitempty" json:"id"`
|
||||||
ParentID string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
ParentID specid.ID `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
||||||
Name string `xml:"name,attr,omitempty" json:"name"`
|
Name string `xml:"name,attr,omitempty" json:"name"`
|
||||||
Starred string `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
Starred string `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
||||||
Children []*TrackChild `xml:"child,omitempty" json:"child,omitempty"`
|
Children []*TrackChild `xml:"child,omitempty" json:"child,omitempty"`
|
||||||
@@ -178,7 +179,7 @@ type MusicFolders struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MusicFolder struct {
|
type MusicFolder struct {
|
||||||
ID string `xml:"id,attr,omitempty" json:"id,omitempty"`
|
ID int `xml:"id,attr,omitempty" json:"id,omitempty"`
|
||||||
Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
|
Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,9 +239,9 @@ type Playlist struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SimilarArtist struct {
|
type SimilarArtist struct {
|
||||||
ID string `xml:"id,attr" json:"id"`
|
ID specid.ID `xml:"id,attr" json:"id"`
|
||||||
Name string `xml:"name,attr" json:"name"`
|
Name string `xml:"name,attr" json:"name"`
|
||||||
AlbumCount int `xml:"albumCount,attr,omitempty" json:"albumCount,omitempty"`
|
AlbumCount int `xml:"albumCount,attr,omitempty" json:"albumCount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArtistInfo struct {
|
type ArtistInfo struct {
|
||||||
|
|||||||
59
server/ctrlsubsonic/specid/ids.go
Normal file
59
server/ctrlsubsonic/specid/ids.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package specid
|
||||||
|
|
||||||
|
// this package is at such a high level in the hierarchy because
|
||||||
|
// it's used by both `server/db` (for now) and `server/ctrlsubsonic`
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrBadSeparator = errors.New("bad separator")
|
||||||
|
ErrNotAnInt = errors.New("not an int")
|
||||||
|
ErrBadPrefix = errors.New("bad prefix")
|
||||||
|
)
|
||||||
|
|
||||||
|
type IDT string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Artist IDT = "ar"
|
||||||
|
Album IDT = "al"
|
||||||
|
Track IDT = "tr"
|
||||||
|
separator = "-"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ID struct {
|
||||||
|
Type IDT
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(in string) (ID, error) {
|
||||||
|
parts := strings.Split(in, separator)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return ID{}, ErrBadSeparator
|
||||||
|
}
|
||||||
|
partType := parts[0]
|
||||||
|
partValue := parts[1]
|
||||||
|
val, err := strconv.Atoi(partValue)
|
||||||
|
if err != nil {
|
||||||
|
return ID{}, fmt.Errorf("%q: %w", partValue, ErrNotAnInt)
|
||||||
|
}
|
||||||
|
for _, acc := range []IDT{Artist, Album, Track} {
|
||||||
|
if partType == string(acc) {
|
||||||
|
return ID{Type: acc, Value: val}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ID{}, fmt.Errorf("%q: %w", partType, ErrBadPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ID) String() string {
|
||||||
|
return fmt.Sprintf("%s%s%d", i.Type, separator, i.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ID) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.String())
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
package ids
|
package specid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParseID(t *testing.T) {
|
||||||
tcases := []struct {
|
tcases := []struct {
|
||||||
param string
|
param string
|
||||||
expType ID
|
expType IDT
|
||||||
expValue int
|
expValue int
|
||||||
expErr error
|
expErr error
|
||||||
}{
|
}{
|
||||||
@@ -19,8 +19,9 @@ func TestParse(t *testing.T) {
|
|||||||
{param: "al-howdy", expErr: ErrNotAnInt},
|
{param: "al-howdy", expErr: ErrNotAnInt},
|
||||||
}
|
}
|
||||||
for _, tcase := range tcases {
|
for _, tcase := range tcases {
|
||||||
|
tcase := tcase // pin
|
||||||
t.Run(tcase.param, func(t *testing.T) {
|
t.Run(tcase.param, func(t *testing.T) {
|
||||||
act, err := Parse(tcase.param)
|
act, err := New(tcase.param)
|
||||||
if !errors.Is(err, tcase.expErr) {
|
if !errors.Is(err, tcase.expErr) {
|
||||||
t.Fatalf("expected err %q, got %q", tcase.expErr, err)
|
t.Fatalf("expected err %q, got %q", tcase.expErr, err)
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
// TODO: remove this dep
|
||||||
|
|
||||||
|
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||||
"go.senan.xyz/gonic/server/mime"
|
"go.senan.xyz/gonic/server/mime"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,6 +49,10 @@ type Artist struct {
|
|||||||
AlbumCount int `sql:"-"`
|
AlbumCount int `sql:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Artist) SID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Artist, Value: a.ID}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Artist) IndexName() string {
|
func (a *Artist) IndexName() string {
|
||||||
if len(a.NameUDec) > 0 {
|
if len(a.NameUDec) > 0 {
|
||||||
return a.NameUDec
|
return a.NameUDec
|
||||||
@@ -85,6 +92,18 @@ type Track struct {
|
|||||||
TagBrainzID string `sql:"default: null"`
|
TagBrainzID string `sql:"default: null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Track) SID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Track, Value: t.ID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Track) AlbumSID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Album, Value: t.AlbumID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Track) ArtistSID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Artist, Value: t.ArtistID}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Track) Ext() string {
|
func (t *Track) Ext() string {
|
||||||
longExt := path.Ext(t.Filename)
|
longExt := path.Ext(t.Filename)
|
||||||
if len(longExt) < 1 {
|
if len(longExt) < 1 {
|
||||||
@@ -157,6 +176,14 @@ type Album struct {
|
|||||||
ReceivedTags bool `gorm:"-"`
|
ReceivedTags bool `gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Album) SID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Album, Value: a.ID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Album) ParentSID() specid.ID {
|
||||||
|
return specid.ID{Type: specid.Album, Value: a.ParentID}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Album) IndexRightPath() string {
|
func (a *Album) IndexRightPath() string {
|
||||||
if len(a.RightPathUDec) > 0 {
|
if len(a.RightPathUDec) > 0 {
|
||||||
return a.RightPathUDec
|
return a.RightPathUDec
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
package ids
|
|
||||||
|
|
||||||
// this package is at such a high level in the hierarchy because
|
|
||||||
// it's used by both `server/db` (for now) and `server/ctrlsubsonic`
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrBadSeparator = errors.New("bad separator")
|
|
||||||
ErrNotAnInt = errors.New("not an int")
|
|
||||||
ErrBadPrefix = errors.New("bad prefix")
|
|
||||||
)
|
|
||||||
|
|
||||||
type ID string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// type values copied from subsonic
|
|
||||||
Artist ID = "ar"
|
|
||||||
Album ID = "al"
|
|
||||||
Track ID = "tr"
|
|
||||||
)
|
|
||||||
|
|
||||||
var accepted = []ID{Artist,
|
|
||||||
Album,
|
|
||||||
Track,
|
|
||||||
}
|
|
||||||
|
|
||||||
type IDV struct {
|
|
||||||
Type ID
|
|
||||||
Value int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i IDV) String() string {
|
|
||||||
return fmt.Sprintf("%s-%d", i.Type, i.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(in string) (IDV, error) {
|
|
||||||
parts := strings.Split(in, "-")
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return IDV{}, ErrBadSeparator
|
|
||||||
}
|
|
||||||
partType := parts[0]
|
|
||||||
partValue := parts[1]
|
|
||||||
val, err := strconv.Atoi(partValue)
|
|
||||||
if err != nil {
|
|
||||||
return IDV{}, fmt.Errorf("%q: %w", partValue, ErrNotAnInt)
|
|
||||||
}
|
|
||||||
for _, acc := range accepted {
|
|
||||||
if partType == string(acc) {
|
|
||||||
return IDV{Type: acc, Value: val}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return IDV{}, fmt.Errorf("%q: %w", partType, ErrBadPrefix)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user