package subsonic import ( "log" "github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/supersonic-app/go-subsonic/subsonic" ) func (s *subsonicMediaProvider) IterateTracks(searchQuery string) mediaprovider.TrackIterator { if searchQuery == "" { return &allTracksIterator{ s: s, albumIter: s.IterateAlbums( mediaprovider.AlbumSortRecentlyAdded, mediaprovider.NewAlbumFilter(mediaprovider.AlbumFilterOptions{}), ), } } return &searchTracksIterator{ searchIterBase: searchIterBase{ s: s.client, query: searchQuery, }, trackIDset: make(map[string]bool), } } type allTracksIterator struct { s *subsonicMediaProvider albumIter mediaprovider.AlbumIterator curAlbum *mediaprovider.AlbumWithTracks curTrackIdx int done bool } func (a *allTracksIterator) Next() *mediaprovider.Track { if a.done { return nil } // fetch next album if a.curAlbum == nil || a.curTrackIdx >= len(a.curAlbum.Tracks) { al := a.albumIter.Next() if al == nil { a.done = true return nil } alWithTracks, err := a.s.GetAlbum(al.ID) if err != nil { log.Printf("error fetching album: %s", err.Error()) } if len(alWithTracks.Tracks) == 0 { // in the unlikely case of an album with zero tracks, // just call recursively to move to next album return a.Next() } a.curAlbum = alWithTracks a.curTrackIdx = 0 } tr := a.curAlbum.Tracks[a.curTrackIdx] a.curTrackIdx += 1 return tr } type searchTracksIterator struct { searchIterBase prefetched []*subsonic.Child prefetchedPos int trackIDset map[string]bool done bool } func (s *searchTracksIterator) Next() *mediaprovider.Track { if s.done { return nil } // prefetch more search results from server if len(s.prefetched) == 0 { results := s.searchIterBase.fetchResults() if results != 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 = s.prefetched[:0] s.prefetchedPos = 0 } return toTrack(tr) } // no more results s.done = true s.prefetched = nil s.trackIDset = nil 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) } } }