diff --git a/backend/albumiterator.go b/backend/albumiterator.go index 697c449..b2ef1a1 100644 --- a/backend/albumiterator.go +++ b/backend/albumiterator.go @@ -142,12 +142,9 @@ func (r *baseIter) Next() *subsonic.AlbumID3 { } type searchIter struct { - query string - artistOffset int - albumOffset int - songOffset int + searchIterBase + l *LibraryManager - s *subsonic.Client filter func(*subsonic.AlbumID3) bool prefetched []*subsonic.AlbumID3 prefetchedPos int @@ -157,9 +154,11 @@ type searchIter struct { func (l *LibraryManager) newSearchIter(query string, filter func(*subsonic.AlbumID3) bool) *searchIter { return &searchIter{ - query: query, + searchIterBase: searchIterBase{ + query: query, + s: l.s.Server, + }, l: l, - s: l.s.Server, filter: filter, albumIDset: make(map[string]bool), } @@ -172,17 +171,8 @@ func (s *searchIter) Next() *subsonic.AlbumID3 { // prefetch more search results from server if s.prefetched == nil { - searchOpts := map[string]string{ - "artistOffset": strconv.Itoa(s.artistOffset), - "albumOffset": strconv.Itoa(s.albumOffset), - "songOffset": strconv.Itoa(s.songOffset), - } - results, err := s.s.Search3(s.query, searchOpts) - if err != nil { - log.Println(err) - results = nil - } - if results == nil || len(results.Album)+len(results.Artist)+len(results.Song) == 0 { + results := s.searchIterBase.fetchResults() + if results == nil { s.done = true s.albumIDset = nil return nil diff --git a/backend/searchiterbase.go b/backend/searchiterbase.go new file mode 100644 index 0000000..4fa880f --- /dev/null +++ b/backend/searchiterbase.go @@ -0,0 +1,33 @@ +package backend + +import ( + "log" + "strconv" + + "github.com/dweymouth/go-subsonic/subsonic" +) + +type searchIterBase struct { + query string + artistOffset int + albumOffset int + songOffset int + s *subsonic.Client +} + +func (s *searchIterBase) fetchResults() *subsonic.SearchResult3 { + searchOpts := map[string]string{ + "artistOffset": strconv.Itoa(s.artistOffset), + "albumOffset": strconv.Itoa(s.albumOffset), + "songOffset": strconv.Itoa(s.songOffset), + } + results, err := s.s.Search3(s.query, searchOpts) + if err != nil { + log.Println(err) + results = nil + } + if results == nil || len(results.Album)+len(results.Artist)+len(results.Song) == 0 { + return nil + } + return results +} diff --git a/backend/trackiterator.go b/backend/trackiterator.go index f11113f..60e9937 100644 --- a/backend/trackiterator.go +++ b/backend/trackiterator.go @@ -6,6 +6,20 @@ import ( "github.com/dweymouth/go-subsonic/subsonic" ) +func (l *LibraryManager) AllTracksIterator() TrackIterator { + return &allTracksIterator{ + l: l, + albumIter: l.AlbumsIter(AlbumSortArtistAZ), + } +} + +func (l *LibraryManager) SearchTracksIterator(query string) TrackIterator { + return &searchTracksIterator{searchIterBase: searchIterBase{ + s: l.s.Server, + query: query, + }} +} + type allTracksIterator struct { l *LibraryManager albumIter AlbumIterator @@ -14,13 +28,6 @@ type allTracksIterator struct { done bool } -func (l *LibraryManager) AllTracksIterator() TrackIterator { - return &allTracksIterator{ - l: l, - albumIter: l.AlbumsIter(AlbumSortArtistAZ), - } -} - func (a *allTracksIterator) Next() *subsonic.Child { if a.done { return nil @@ -50,3 +57,81 @@ func (a *allTracksIterator) Next() *subsonic.Child { a.curTrackIdx += 1 return tr } + +type searchTracksIterator struct { + searchIterBase + + prefetched []*subsonic.Child + prefetchedPos int + trackIDset map[string]bool + done bool +} + +func (s *searchTracksIterator) Next() *subsonic.Child { + if s.done { + return nil + } + + // prefetch more search results from server + if s.prefetched == nil { + results := s.searchIterBase.fetchResults() + if results == nil || len(results.Album)+len(results.Artist)+len(results.Song) == 0 { + s.done = true + s.trackIDset = nil + return nil + } + + // add results from songs search + s.addNewTracks(results.Song) + s.songOffset += len(results.Song) + + // add results from artists search + for _, artist := range results.Artist { + artist, err := s.s.GetArtist(artist.ID) + if err != nil { + log.Printf("error fetching artist: %s", err.Error()) + } else { + s.addNewTracksFromAlbums(artist.Album) + } + } + s.artistOffset += len(results.Artist) + + // add results from albums search + s.addNewTracksFromAlbums(results.Album) + s.albumOffset += len(results.Album) + } + + // return from prefetched results + if len(s.prefetched) > 0 { + tr := s.prefetched[s.prefetchedPos] + s.prefetchedPos++ + if s.prefetchedPos == len(s.prefetched) { + s.prefetched = nil + s.prefetchedPos = 0 + } + + return tr + } + + return nil +} + +func (s *searchTracksIterator) addNewTracks(tracks []*subsonic.Child) { + for _, tr := range tracks { + if _, have := s.trackIDset[tr.ID]; have { + continue + } + s.prefetched = append(s.prefetched, tr) + s.trackIDset[tr.ID] = true + } +} + +func (s *searchTracksIterator) addNewTracksFromAlbums(albums []*subsonic.AlbumID3) { + for _, al := range albums { + if album, err := s.s.GetAlbum(al.ID); err != nil { + log.Printf("error fetching album: %s", err.Error()) + } else { + s.addNewTracks(album.Song) + } + } +}