From d04a586cd6769c0aaa2317d6583d8d6f03ec40fc Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Mon, 26 Dec 2022 15:28:07 -0800 Subject: [PATCH] add searchiterator --- backend/albumiterator.go | 112 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/backend/albumiterator.go b/backend/albumiterator.go index fb1798c..122fb52 100644 --- a/backend/albumiterator.go +++ b/backend/albumiterator.go @@ -42,6 +42,10 @@ func (l *LibraryManager) FrequentlyPlayedIter() AlbumIterator { return l.newBaseIter("frequent") } +func (l *LibraryManager) SearchIter(query string) AlbumIterator { + return l.newSearchIter(query) +} + func (l *LibraryManager) CacheAlbum(a *subsonic.AlbumID3) { l.albumCache.Set(a.ID, a) } @@ -78,8 +82,6 @@ func (l *LibraryManager) newBaseIter(listType string) *baseIter { } } -// TODO: figure out why the iterator sometimes returns an album twice - func (r *baseIter) Next() *subsonic.AlbumID3 { if r.done { return nil @@ -128,3 +130,109 @@ func (r *baseIter) NextN(n int, cb func(*subsonic.AlbumID3)) { } }() } + +type searchIter struct { + query string + artistOffset int + albumOffset int + songOffset int + l *LibraryManager + s *subsonic.Client + prefetched []*subsonic.AlbumID3 + prefetchedPos int + albumIDset map[string]bool + done bool +} + +func (l *LibraryManager) newSearchIter(query string) *searchIter { + return &searchIter{ + query: query, + l: l, + s: l.s.Server, + } +} + +func (s *searchIter) Next() *subsonic.AlbumID3 { + if s.done { + return nil + } + + // 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 { + s.done = true + return nil + } + + // add results from albums search + s.addNewAlbums(results.Album) + s.albumOffset += len(results.Album) + + // 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()) + } + s.addNewAlbums(artist.Album) + } + s.artistOffset += len(results.Artist) + + // add results from songs search + for _, song := range results.Song { + album, err := s.s.GetAlbum(song.Parent) + if err != nil { + log.Printf("error fetching album: %s", err.Error()) + } + s.addNewAlbums([]*subsonic.AlbumID3{album}) + } + s.songOffset += len(results.Song) + } + + // return from prefetched results + if len(s.prefetched) > 0 { + a := s.prefetched[s.prefetchedPos] + s.prefetchedPos++ + if s.prefetchedPos == len(s.prefetched) { + s.prefetched = nil + s.prefetchedPos = 0 + } + + s.l.CacheAlbum(a) + return a + } + + return nil +} + +func (s *searchIter) NextN(n int, cb func(*subsonic.AlbumID3)) { + go func() { + for i := 0; i < n; i++ { + a := s.Next() + cb(a) + if a == nil { + break + } + } + }() +} + +func (s *searchIter) addNewAlbums(al []*subsonic.AlbumID3) { + for _, album := range al { + if _, have := s.albumIDset[album.ID]; have { + continue + } + s.prefetched = append(s.prefetched, album) + s.albumIDset[album.ID] = true + } +}