implement library browsing for Subsonic
This commit is contained in:
@@ -59,6 +59,14 @@ func (j *jellyfinMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID stri
|
||||
j.prefetchCoverCB = cb
|
||||
}
|
||||
|
||||
func (j *jellyfinMediaProvider) GetLibraries() ([]mediaprovider.Library, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (j *jellyfinMediaProvider) SetLibrary(string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *jellyfinMediaProvider) CreatePlaylist(name string, trackIDs []string) error {
|
||||
return j.client.CreatePlaylist(name, trackIDs)
|
||||
}
|
||||
|
||||
@@ -197,6 +197,15 @@ type Server interface {
|
||||
type MediaProvider interface {
|
||||
SetPrefetchCoverCallback(cb func(coverArtID string))
|
||||
|
||||
// GetLibraries gets the list of top-level music libraries
|
||||
// (musicFolders in Subsonic)
|
||||
GetLibraries() ([]Library, error)
|
||||
|
||||
// SetLibrary sets the current library that all other
|
||||
// MediaProvider API calls will filter to. Use empty string
|
||||
// to reset to all libraries.
|
||||
SetLibrary(id string) error
|
||||
|
||||
GetTrack(trackID string) (*Track, error)
|
||||
|
||||
GetAlbum(albumID string) (*AlbumWithTracks, error)
|
||||
|
||||
@@ -32,6 +32,11 @@ const (
|
||||
ReleaseTypeSpokenWord ReleaseType = 0x8000
|
||||
)
|
||||
|
||||
type Library struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
|
||||
type ItemDate struct {
|
||||
Year *int
|
||||
Month *int
|
||||
|
||||
@@ -62,8 +62,11 @@ func (s *subsonicMediaProvider) IterateAlbums(sortOrder string, filter mediaprov
|
||||
modifiedOptions.Genres = nil
|
||||
modifiedFilter.SetOptions(modifiedOptions)
|
||||
fetchFn := func(offset, limit int) ([]*subsonic.AlbumID3, error) {
|
||||
return s.client.GetAlbumList2("byGenre",
|
||||
map[string]string{"genre": genre, "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)})
|
||||
params := map[string]string{"genre": genre, "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)}
|
||||
if s.currentLibraryID != "" {
|
||||
params["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
return s.client.GetAlbumList2("byGenre", params)
|
||||
}
|
||||
return helpers.NewAlbumIterator(makeFetchFn(fetchFn), modifiedFilter, s.prefetchCoverCB)
|
||||
}
|
||||
@@ -92,14 +95,20 @@ func (s *subsonicMediaProvider) IterateAlbums(sortOrder string, filter mediaprov
|
||||
return s.baseIterFromSimpleSortOrder("alphabeticalByArtist", filter)
|
||||
case mediaprovider.AlbumSortYearAscending:
|
||||
fetchFn := func(offset, limit int) ([]*subsonic.AlbumID3, error) {
|
||||
return s.client.GetAlbumList2("byYear",
|
||||
map[string]string{"fromYear": "0", "toYear": "3000", "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)})
|
||||
params := map[string]string{"fromYear": "0", "toYear": "3000", "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)}
|
||||
if s.currentLibraryID != "" {
|
||||
params["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
return s.client.GetAlbumList2("byYear", params)
|
||||
}
|
||||
return helpers.NewAlbumIterator(makeFetchFn(fetchFn), filter, s.prefetchCoverCB)
|
||||
case mediaprovider.AlbumSortYearDescending:
|
||||
fetchFn := func(offset, limit int) ([]*subsonic.AlbumID3, error) {
|
||||
return s.client.GetAlbumList2("byYear",
|
||||
map[string]string{"fromYear": "3000", "toYear": "0", "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)})
|
||||
params := map[string]string{"fromYear": "3000", "toYear": "0", "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit)}
|
||||
if s.currentLibraryID != "" {
|
||||
params["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
return s.client.GetAlbumList2("byYear", params)
|
||||
}
|
||||
return helpers.NewAlbumIterator(makeFetchFn(fetchFn), filter, s.prefetchCoverCB)
|
||||
default:
|
||||
@@ -128,6 +137,7 @@ func (s *subsonicMediaProvider) newSearchAlbumIter(query string, filter mediapro
|
||||
searchIterBase: searchIterBase{
|
||||
query: query,
|
||||
s: s.client,
|
||||
musicFolderId: s.currentLibraryID,
|
||||
},
|
||||
prefetchCB: cb,
|
||||
filter: filter,
|
||||
@@ -218,6 +228,9 @@ func (s *subsonicMediaProvider) newRandomIter(filter mediaprovider.AlbumFilter,
|
||||
"size": strconv.Itoa(limit),
|
||||
"offset": strconv.Itoa(offset),
|
||||
}
|
||||
if s.currentLibraryID != "" {
|
||||
args["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
return s.client.GetAlbumList2("random", args)
|
||||
}),
|
||||
filter, s.prefetchCoverCB)
|
||||
@@ -229,7 +242,11 @@ func (s *subsonicMediaProvider) baseIterFromSimpleSortOrder(sort string, filter
|
||||
|
||||
func (s *subsonicMediaProvider) fetchFnFromStandardSort(sort string) helpers.AlbumFetchFn {
|
||||
return makeFetchFn(func(offset, limit int) ([]*subsonic.AlbumID3, error) {
|
||||
return s.client.GetAlbumList2(sort, map[string]string{"size": strconv.Itoa(limit), "offset": strconv.Itoa(offset)})
|
||||
params := map[string]string{"size": strconv.Itoa(limit), "offset": strconv.Itoa(offset)}
|
||||
if s.currentLibraryID != "" {
|
||||
params["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
return s.client.GetAlbumList2(sort, params)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ func (s *subsonicMediaProvider) newSearchArtistIter(query string, filter mediapr
|
||||
searchIterBase: searchIterBase{
|
||||
query: query,
|
||||
s: s.client,
|
||||
musicFolderId: s.currentLibraryID,
|
||||
},
|
||||
prefetchCB: cb,
|
||||
filter: filter,
|
||||
@@ -165,7 +166,11 @@ func (s *subsonicMediaProvider) artistFetchFnFromStandardSort(sortFn func([]*sub
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
idxs, err := s.client.GetArtists(map[string]string{})
|
||||
var params map[string]string
|
||||
if s.currentLibraryID != "" {
|
||||
params = map[string]string{"musicFolderId": s.currentLibraryID}
|
||||
}
|
||||
idxs, err := s.client.GetArtists(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -23,11 +23,15 @@ func (s *subsonicMediaProvider) SearchAll(searchQuery string, maxResults int) ([
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
count := strconv.Itoa(maxResults / 3)
|
||||
res, e := s.client.Search3(searchQuery, map[string]string{
|
||||
params := map[string]string{
|
||||
"artistCount": count,
|
||||
"albumCount": count,
|
||||
"songCount": count,
|
||||
})
|
||||
}
|
||||
if s.currentLibraryID != "" {
|
||||
params["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
res, e := s.client.Search3(searchQuery, params)
|
||||
if e != nil {
|
||||
err = e
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type searchIterBase struct {
|
||||
musicFolderId string
|
||||
query string
|
||||
artistOffset int
|
||||
albumOffset int
|
||||
@@ -21,6 +22,9 @@ func (s *searchIterBase) fetchResults() *subsonic.SearchResult3 {
|
||||
"albumOffset": strconv.Itoa(s.albumOffset),
|
||||
"songOffset": strconv.Itoa(s.songOffset),
|
||||
}
|
||||
if s.musicFolderId != "" {
|
||||
searchOpts["musicFolderId"] = s.musicFolderId
|
||||
}
|
||||
results, err := s.s.Search3(s.query, searchOpts)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
@@ -24,6 +24,8 @@ const (
|
||||
)
|
||||
|
||||
type subsonicMediaProvider struct {
|
||||
currentLibraryID string
|
||||
|
||||
client *subsonic.Client
|
||||
prefetchCoverCB func(coverArtID string)
|
||||
|
||||
@@ -45,6 +47,21 @@ func (s *subsonicMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID stri
|
||||
s.prefetchCoverCB = cb
|
||||
}
|
||||
|
||||
func (s *subsonicMediaProvider) GetLibraries() ([]mediaprovider.Library, error) {
|
||||
folders, err := s.client.GetMusicFolders()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sharedutil.MapSlice(folders, func(f *subsonic.MusicFolder) mediaprovider.Library {
|
||||
return mediaprovider.Library{ID: f.ID, Name: f.Name}
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *subsonicMediaProvider) SetLibrary(id string) error {
|
||||
s.currentLibraryID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *subsonicMediaProvider) CreatePlaylist(name string, trackIDs []string) error {
|
||||
s.playlistsCached = nil
|
||||
return s.client.CreatePlaylistWithTracks(trackIDs, map[string]string{"name": name})
|
||||
@@ -157,7 +174,11 @@ func (s *subsonicMediaProvider) GetCoverArt(id string, size int) (image.Image, e
|
||||
}
|
||||
|
||||
func (s *subsonicMediaProvider) GetFavorites() (mediaprovider.Favorites, error) {
|
||||
fav, err := s.client.GetStarred2(map[string]string{})
|
||||
var params map[string]string
|
||||
if s.currentLibraryID != "" {
|
||||
params = map[string]string{"musicFolderId": s.currentLibraryID}
|
||||
}
|
||||
fav, err := s.client.GetStarred2(params)
|
||||
if err != nil {
|
||||
return mediaprovider.Favorites{}, err
|
||||
}
|
||||
@@ -219,6 +240,9 @@ func (s *subsonicMediaProvider) GetRandomTracks(genreName string, count int) ([]
|
||||
if genreName != "" {
|
||||
opts["genre"] = genreName
|
||||
}
|
||||
if s.currentLibraryID != "" {
|
||||
opts["musicFolderId"] = s.currentLibraryID
|
||||
}
|
||||
tr, err := s.client.GetRandomSongs(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -21,6 +21,7 @@ func (s *subsonicMediaProvider) IterateTracks(searchQuery string) mediaprovider.
|
||||
searchIterBase: searchIterBase{
|
||||
s: s.client,
|
||||
query: searchQuery,
|
||||
musicFolderId: s.currentLibraryID,
|
||||
},
|
||||
trackIDset: make(map[string]bool),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user