130 lines
2.5 KiB
Go
130 lines
2.5 KiB
Go
package backend
|
|
|
|
import (
|
|
"log"
|
|
"strconv"
|
|
|
|
"github.com/bluele/gcache"
|
|
subsonic "github.com/dweymouth/go-subsonic"
|
|
)
|
|
|
|
type AlbumIterator interface {
|
|
Next() *subsonic.AlbumID3
|
|
NextN(int, func(*subsonic.AlbumID3))
|
|
}
|
|
|
|
type LibraryManager struct {
|
|
s *subsonic.Client
|
|
albumCache gcache.Cache
|
|
}
|
|
|
|
func NewLibraryManager(s *subsonic.Client) *LibraryManager {
|
|
cache := gcache.New(250).LRU().Build()
|
|
return &LibraryManager{
|
|
s: s,
|
|
albumCache: cache,
|
|
}
|
|
}
|
|
|
|
func (l *LibraryManager) RecentlyAddedIter() AlbumIterator {
|
|
return l.newBaseIter("newest")
|
|
}
|
|
|
|
func (l *LibraryManager) RecentlyPlayedIter() AlbumIterator {
|
|
return l.newBaseIter("recent")
|
|
}
|
|
|
|
func (l *LibraryManager) StarredIter() AlbumIterator {
|
|
return l.newBaseIter("starred")
|
|
}
|
|
|
|
func (l *LibraryManager) FrequentlyPlayedIter() AlbumIterator {
|
|
return l.newBaseIter("frequent")
|
|
}
|
|
|
|
func (l *LibraryManager) CacheAlbum(a *subsonic.AlbumID3) {
|
|
l.albumCache.Set(a.ID, a)
|
|
}
|
|
|
|
func (l *LibraryManager) GetAlbum(id string) (*subsonic.AlbumID3, error) {
|
|
if l.albumCache.Has(id) {
|
|
if a, err := l.albumCache.Get(id); err == nil {
|
|
return a.(*subsonic.AlbumID3), nil
|
|
}
|
|
}
|
|
a, err := l.s.GetAlbum(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
l.albumCache.Set(a.ID, a)
|
|
return a, nil
|
|
}
|
|
|
|
type baseIter struct {
|
|
listType string
|
|
pos int
|
|
l *LibraryManager
|
|
s *subsonic.Client
|
|
prefetched []*subsonic.AlbumID3
|
|
prefetchedPos int
|
|
done bool
|
|
}
|
|
|
|
func (l *LibraryManager) newBaseIter(listType string) *baseIter {
|
|
return &baseIter{
|
|
listType: listType,
|
|
l: l,
|
|
s: l.s,
|
|
}
|
|
}
|
|
|
|
// TODO: figure out why the iterator sometimes returns an album twice
|
|
|
|
func (r *baseIter) Next() *subsonic.AlbumID3 {
|
|
if r.done {
|
|
return nil
|
|
}
|
|
if r.prefetched != nil {
|
|
a := r.prefetched[r.prefetchedPos]
|
|
r.prefetchedPos++
|
|
if r.prefetchedPos == len(r.prefetched) {
|
|
r.prefetched = nil
|
|
r.prefetchedPos = 0
|
|
}
|
|
r.pos++
|
|
|
|
r.l.CacheAlbum(a)
|
|
return a
|
|
}
|
|
albums, err := r.s.GetAlbumList2(r.listType, map[string]string{"size": "20", "offset": strconv.Itoa(r.pos)})
|
|
if err != nil {
|
|
log.Println(err)
|
|
albums = nil
|
|
}
|
|
if len(albums) == 0 {
|
|
r.done = true
|
|
return nil
|
|
} else if len(albums) == 1 {
|
|
r.l.CacheAlbum(albums[0])
|
|
r.done = true
|
|
return albums[0]
|
|
}
|
|
r.prefetched = albums
|
|
r.prefetchedPos = 1
|
|
|
|
r.l.CacheAlbum(r.prefetched[0])
|
|
return r.prefetched[0]
|
|
}
|
|
|
|
func (r *baseIter) NextN(n int, cb func(*subsonic.AlbumID3)) {
|
|
go func() {
|
|
for i := 0; i < n; i++ {
|
|
a := r.Next()
|
|
cb(a)
|
|
if a == nil {
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
}
|