Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/mediaprovider/jellyfin/artistiterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (j *jellyfinMediaProvider) IterateArtists(sortOrder string, filter mediapro
return j.client.GetAlbumArtists(jellyfin.QueryOpts{
Sort: jfSort,
Paging: paging,
Filter: jellyfin.Filter{ParentID: j.currentLibraryID},
})
},
sortFn,
Expand Down
16 changes: 14 additions & 2 deletions backend/mediaprovider/jellyfin/iterators.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ func (j *jellyfinMediaProvider) IterateAlbums(sortOrder string, filter mediaprov
jfSort.Mode = jellyfin.SortDesc
}
jfFilt, modifiedFilter := jfFilterFromFilter(filter)
if j.currentLibraryID != "" {
jfFilt.ParentID = j.currentLibraryID
}

fetcher := func(offs, limit int) ([]*mediaprovider.Album, error) {
al, err := j.client.GetAlbums(jellyfin.QueryOpts{
Expand Down Expand Up @@ -74,7 +77,10 @@ func (j *jellyfinMediaProvider) IterateAlbums(sortOrder string, filter mediaprov

func (j *jellyfinMediaProvider) SearchAlbums(searchQuery string, filter mediaprovider.AlbumFilter) mediaprovider.AlbumIterator {
fetcher := func(offs, limit int) ([]*mediaprovider.Album, error) {
sr, err := j.client.Search(searchQuery, jellyfin.TypeAlbum, jellyfin.Paging{StartIndex: offs, Limit: limit})
var opts jellyfin.QueryOpts
opts.Paging = jellyfin.Paging{StartIndex: offs, Limit: limit}
opts.Filter.ParentID = j.currentLibraryID
sr, err := j.client.Search(searchQuery, jellyfin.TypeAlbum, opts)
if err != nil {
return nil, err
}
Expand All @@ -89,6 +95,9 @@ func (j *jellyfinMediaProvider) IterateTracks(searchQuery string) mediaprovider.
fetcher = func(offs, limit int) ([]*mediaprovider.Track, error) {
var opts jellyfin.QueryOpts
opts.Paging = jellyfin.Paging{StartIndex: offs, Limit: limit}
if j.currentLibraryID != "" {
opts.Filter.ParentID = j.currentLibraryID
}
s, err := j.client.GetSongs(opts)
if err != nil {
return nil, err
Expand All @@ -97,7 +106,10 @@ func (j *jellyfinMediaProvider) IterateTracks(searchQuery string) mediaprovider.
}
} else {
fetcher = func(offs, limit int) ([]*mediaprovider.Track, error) {
sr, err := j.client.Search(searchQuery, jellyfin.TypeSong, jellyfin.Paging{StartIndex: offs, Limit: limit})
var opts jellyfin.QueryOpts
opts.Paging = jellyfin.Paging{StartIndex: offs, Limit: limit}
opts.Filter.ParentID = j.currentLibraryID
sr, err := j.client.Search(searchQuery, jellyfin.TypeSong, opts)
if err != nil {
return nil, err
}
Expand Down
43 changes: 32 additions & 11 deletions backend/mediaprovider/jellyfin/jellyfinmediaprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type jellyfinMediaProvider struct {
client *jellyfin.Client
prefetchCoverCB func(coverArtID string)

currentLibraryID string

genresCached []*mediaprovider.Genre
genresCachedAt int64 // unix
}
Expand All @@ -59,6 +61,22 @@ func (j *jellyfinMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID stri
j.prefetchCoverCB = cb
}

func (j *jellyfinMediaProvider) GetLibraries() ([]mediaprovider.Library, error) {
v, err := j.client.GetUserViews()
if err != nil {
return nil, err
}
return sharedutil.FilterMapSlice(v, func(v *jellyfin.BaseItem) (mediaprovider.Library, bool) {
return mediaprovider.Library{Name: v.Name, ID: v.ID}, v.CollectionType == string(jellyfin.CollectionTypeMusic)
}), nil
}

func (j *jellyfinMediaProvider) SetLibrary(id string) error {
j.currentLibraryID = id
j.genresCached = nil
return nil
}

func (j *jellyfinMediaProvider) CreatePlaylist(name string, trackIDs []string) error {
return j.client.CreatePlaylist(name, trackIDs)
}
Expand Down Expand Up @@ -178,6 +196,9 @@ func (j *jellyfinMediaProvider) GetTopTracks(artist mediaprovider.Artist, limit
opts.Filter.ArtistID = artist.ID
opts.Sort.Field = jellyfin.SortByCommunityRating
opts.Sort.Mode = jellyfin.SortDesc
if j.currentLibraryID != "" {
opts.Filter.ParentID = j.currentLibraryID
}
tr, err := j.client.GetSongs(opts)
if err != nil {
return nil, err
Expand All @@ -193,6 +214,9 @@ func (j *jellyfinMediaProvider) GetRandomTracks(genreName string, limit int) ([]
opts.Paging.Limit = limit
opts.Filter.Genres = []string{genreName}
opts.Sort.Field = jellyfin.SortByRandom
if j.currentLibraryID != "" {
opts.Filter.ParentID = j.currentLibraryID
}
tr, err := j.client.GetSongs(opts)
if err != nil {
return nil, err
Expand All @@ -212,15 +236,16 @@ func (j *jellyfinMediaProvider) GetCoverArt(id string, size int) (image.Image, e
return j.client.GetItemImage(id, "Primary", size, 92)
}

func (s *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error) {
func (j *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error) {
var wg sync.WaitGroup
var favorites mediaprovider.Favorites

var opts jellyfin.QueryOpts
opts.Filter.Favorite = true
opts.Filter.ParentID = j.currentLibraryID
wg.Add(1)
go func() {
var opts jellyfin.QueryOpts
opts.Filter.Favorite = true
al, err := s.client.GetAlbums(opts)
al, err := j.client.GetAlbums(opts)
if err == nil && len(al) > 0 {
favorites.Albums = sharedutil.MapSlice(al, toAlbum)
}
Expand All @@ -229,9 +254,7 @@ func (s *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error)

wg.Add(1)
go func() {
var opts jellyfin.QueryOpts
opts.Filter.Favorite = true
ar, err := s.client.GetAlbumArtists(opts)
ar, err := j.client.GetAlbumArtists(opts)
if err == nil && len(ar) > 0 {
favorites.Artists = sharedutil.MapSlice(ar, toArtist)
}
Expand All @@ -240,9 +263,7 @@ func (s *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error)

wg.Add(1)
go func() {
var opts jellyfin.QueryOpts
opts.Filter.Favorite = true
tr, err := s.client.GetSongs(opts)
tr, err := j.client.GetSongs(opts)
if err == nil && len(tr) > 0 {
favorites.Tracks = sharedutil.MapSlice(tr, toTrack)
}
Expand All @@ -258,7 +279,7 @@ func (j *jellyfinMediaProvider) GetGenres() ([]*mediaprovider.Genre, error) {
return j.genresCached, nil
}

g, err := j.client.GetGenres(jellyfin.Paging{})
g, err := j.client.GetGenres(jellyfin.Paging{}, j.currentLibraryID)
if err != nil {
return nil, err
}
Expand Down
11 changes: 7 additions & 4 deletions backend/mediaprovider/jellyfin/searchall.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,24 @@ func (j *jellyfinMediaProvider) SearchAll(searchQuery string, maxResults int) ([
var genres []jellyfin.NameID
var playlists []*jellyfin.Playlist

var opts jellyfin.QueryOpts
opts.Paging.Limit = limit
opts.Filter.ParentID = j.currentLibraryID
wg.Add(1)
go func() {
albumResult, _ := j.client.Search(searchQuery, jellyfin.TypeAlbum, jellyfin.Paging{Limit: limit})
albumResult, _ := j.client.Search(searchQuery, jellyfin.TypeAlbum, opts)
albums = albumResult.Albums
wg.Done()
}()
wg.Add(1)
go func() {
artistResult, _ := j.client.Search(searchQuery, jellyfin.TypeArtist, jellyfin.Paging{Limit: limit})
artistResult, _ := j.client.Search(searchQuery, jellyfin.TypeArtist, opts)
artists = artistResult.Artists
wg.Done()
}()
wg.Add(1)
go func() {
songResult, _ := j.client.Search(searchQuery, jellyfin.TypeSong, jellyfin.Paging{Limit: limit})
songResult, _ := j.client.Search(searchQuery, jellyfin.TypeSong, opts)
songs = songResult.Songs
wg.Done()
}()
Expand All @@ -55,7 +58,7 @@ func (j *jellyfinMediaProvider) SearchAll(searchQuery string, maxResults int) ([

wg.Add(1)
go func() {
g, e := j.client.GetGenres(jellyfin.Paging{})
g, e := j.client.GetGenres(jellyfin.Paging{}, "")
if e == nil {
genres = sharedutil.FilterSlice(g, func(g jellyfin.NameID) bool {
return helpers.AllTermsMatch(strings.ToLower(sanitize.Accents(g.Name)), queryLowerWords)
Expand Down
9 changes: 9 additions & 0 deletions backend/mediaprovider/mediaprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions backend/mediaprovider/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ const (
ReleaseTypeSpokenWord ReleaseType = 0x8000
)

type Library struct {
ID string
Name string
}

type ItemDate struct {
Year *int
Month *int
Expand Down
35 changes: 26 additions & 9 deletions backend/mediaprovider/subsonic/albumiterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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:
Expand All @@ -126,8 +135,9 @@ type searchAlbumIter struct {
func (s *subsonicMediaProvider) newSearchAlbumIter(query string, filter mediaprovider.AlbumFilter, cb func(string)) *searchAlbumIter {
return &searchAlbumIter{
searchIterBase: searchIterBase{
query: query,
s: s.client,
query: query,
s: s.client,
musicFolderId: s.currentLibraryID,
},
prefetchCB: cb,
filter: filter,
Expand Down Expand Up @@ -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)
Expand All @@ -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)
})
}

Expand Down
11 changes: 8 additions & 3 deletions backend/mediaprovider/subsonic/artistiterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ type searchArtistIter struct {
func (s *subsonicMediaProvider) newSearchArtistIter(query string, filter mediaprovider.ArtistFilter, cb func(string)) *searchArtistIter {
return &searchArtistIter{
searchIterBase: searchIterBase{
query: query,
s: s.client,
query: query,
s: s.client,
musicFolderId: s.currentLibraryID,
},
prefetchCB: cb,
filter: filter,
Expand Down Expand Up @@ -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
}
Expand Down
8 changes: 6 additions & 2 deletions backend/mediaprovider/subsonic/searchall.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 9 additions & 5 deletions backend/mediaprovider/subsonic/searchiterbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import (
)

type searchIterBase struct {
query string
artistOffset int
albumOffset int
songOffset int
s *subsonic.Client
musicFolderId string
query string
artistOffset int
albumOffset int
songOffset int
s *subsonic.Client
}

func (s *searchIterBase) fetchResults() *subsonic.SearchResult3 {
Expand All @@ -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)
Expand Down
Loading