From 584a160809395c5fac209fa606ba47fdacf07d42 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 7 Jan 2023 16:24:18 -0800 Subject: [PATCH] add cache warming for album covers to album iterators --- backend/albumiterator.go | 16 ++++++++++++++++ backend/app.go | 3 +++ backend/imagemanager.go | 7 +++++++ ui/browsing/albumspage.go | 4 ++-- ui/browsing/artistpage.go | 2 +- ui/browsing/genrepage.go | 4 ++-- ui/widgets/albumgrid.go | 40 +++++++++++++++++++++++---------------- 7 files changed, 55 insertions(+), 21 deletions(-) diff --git a/backend/albumiterator.go b/backend/albumiterator.go index cb91ac4..c065434 100644 --- a/backend/albumiterator.go +++ b/backend/albumiterator.go @@ -14,6 +14,8 @@ type AlbumIterator interface { } type LibraryManager struct { + PreCacheCoverFn func(string) + s *ServerManager albumDetailCache gcache.Cache } @@ -153,6 +155,11 @@ func (r *baseIter) Next() *subsonic.AlbumID3 { } r.prefetched = albums r.prefetchedPos = 1 + if r.l.PreCacheCoverFn != nil { + for _, album := range albums { + go r.l.PreCacheCoverFn(album.ID) + } + } return r.prefetched[0] } @@ -277,6 +284,9 @@ func (s *searchIter) addNewAlbums(al []*subsonic.AlbumID3) { continue } s.prefetched = append(s.prefetched, album) + if s.l.PreCacheCoverFn != nil { + go s.l.PreCacheCoverFn(album.ID) + } s.albumIDset[album.ID] = true } } @@ -328,6 +338,9 @@ func (r *randomIter) Next() *subsonic.AlbumID3 { for _, album := range albums { if _, ok := r.albumIDSet[album.ID]; !ok { r.prefetched = append(r.prefetched, album) + if r.l.PreCacheCoverFn != nil { + go r.l.PreCacheCoverFn(album.ID) + } r.albumIDSet[album.ID] = true } } @@ -346,6 +359,9 @@ func (r *randomIter) Next() *subsonic.AlbumID3 { if _, ok := r.albumIDSet[album.ID]; !ok { hitCount++ r.prefetched = append(r.prefetched, album) + if r.l.PreCacheCoverFn != nil { + go r.l.PreCacheCoverFn(album.ID) + } r.albumIDSet[album.ID] = true } } diff --git a/backend/app.go b/backend/app.go index d4f2b0b..4d3713e 100644 --- a/backend/app.go +++ b/backend/app.go @@ -51,6 +51,9 @@ func StartupApp() (*App, error) { a.PlaybackManager = NewPlaybackManager(a.bgrndCtx, a.ServerManager, a.Player) a.LibraryManager = NewLibraryManager(a.ServerManager) a.ImageManager = NewImageManager(a.ServerManager, configdir.LocalCache(AppName)) + a.LibraryManager.PreCacheCoverFn = func(albumID string) { + _, _ = a.ImageManager.GetAlbumThumbnail(albumID) + } return a, nil } diff --git a/backend/imagemanager.go b/backend/imagemanager.go index 44e0724..cc88c8a 100644 --- a/backend/imagemanager.go +++ b/backend/imagemanager.go @@ -32,6 +32,13 @@ func NewImageManager(s *ServerManager, baseCacheDir string) *ImageManager { } } +func (i *ImageManager) GetAlbumThumbnailFromCache(albumID string) (image.Image, bool) { + if img, err := i.thumbnailCache.Get(albumID); err == nil && img != nil { + return img.(image.Image), true + } + return nil, false +} + func (i *ImageManager) GetAlbumThumbnail(albumID string) (image.Image, error) { // in-memory cache if i.thumbnailCache.Has(albumID) { diff --git a/ui/browsing/albumspage.go b/ui/browsing/albumspage.go index 0fab219..22d2dd0 100644 --- a/ui/browsing/albumspage.go +++ b/ui/browsing/albumspage.go @@ -67,7 +67,7 @@ func NewAlbumsPage(title string, sortOrder string, lm *backend.LibraryManager, i a.sortOrder.OnChanged = a.onSortOrderChanged sortVbox := container.NewVBox(layout.NewSpacer(), a.sortOrder, layout.NewSpacer()) iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected)) - a.grid = widgets.NewAlbumGrid(iter, im.GetAlbumThumbnail, false /*showYear*/) + a.grid = widgets.NewAlbumGrid(iter, im, false /*showYear*/) a.grid.OnPlayAlbum = a.onPlayAlbum a.grid.OnShowArtistPage = a.onShowArtistPage a.grid.OnShowAlbumPage = a.onShowAlbumPage @@ -116,7 +116,7 @@ func (a *AlbumsPage) Reload() { func (a *AlbumsPage) doSearch(query string) { if a.searchGrid == nil { - a.searchGrid = widgets.NewAlbumGrid(a.lm.SearchIter(query), a.im.GetAlbumThumbnail, false /*showYear*/) + a.searchGrid = widgets.NewAlbumGrid(a.lm.SearchIter(query), a.im, false /*showYear*/) a.searchGrid.OnPlayAlbum = a.onPlayAlbum a.searchGrid.OnShowAlbumPage = a.onShowAlbumPage a.searchGrid.OnShowArtistPage = a.onShowArtistPage diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index d89b4e0..16aa4e1 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -76,7 +76,7 @@ func (a *ArtistPage) loadAsync() { } a.titleDisp.Segments[0].(*widget.TextSegment).Text = artist.Name a.titleDisp.Refresh() - ag := widgets.NewFixedAlbumGrid(artist.Album, a.im.GetAlbumThumbnail, true /*showYear*/) + ag := widgets.NewFixedAlbumGrid(artist.Album, a.im, true /*showYear*/) ag.OnPlayAlbum = a.onPlayAlbum ag.OnShowAlbumPage = a.onShowAlbumPage a.container.Objects[0] = ag diff --git a/ui/browsing/genrepage.go b/ui/browsing/genrepage.go index 370a1c4..6843ab3 100644 --- a/ui/browsing/genrepage.go +++ b/ui/browsing/genrepage.go @@ -45,7 +45,7 @@ func NewGenrePage(genre string, lm *backend.LibraryManager, im *backend.ImageMan SizeName: theme.SizeNameHeadingText, } iter := g.lm.GenreIter(g.genre) - g.grid = widgets.NewAlbumGrid(iter, g.im.GetAlbumThumbnail, false) + g.grid = widgets.NewAlbumGrid(iter, g.im, false) g.grid.OnPlayAlbum = g.onPlayAlbum g.grid.OnShowArtistPage = g.onShowArtistPage g.grid.OnShowAlbumPage = g.onShowAlbumPage @@ -115,7 +115,7 @@ func (g *GenrePage) doSearch(query string) { return al.Genre == g.genre }) if g.searchGrid == nil { - g.searchGrid = widgets.NewAlbumGrid(iter, g.im.GetAlbumThumbnail, false /*showYear*/) + g.searchGrid = widgets.NewAlbumGrid(iter, g.im, false /*showYear*/) g.searchGrid.OnPlayAlbum = g.onPlayAlbum g.searchGrid.OnShowAlbumPage = g.onShowAlbumPage g.searchGrid.OnShowArtistPage = g.onShowArtistPage diff --git a/ui/widgets/albumgrid.go b/ui/widgets/albumgrid.go index 0d418ab..89d2456 100644 --- a/ui/widgets/albumgrid.go +++ b/ui/widgets/albumgrid.go @@ -10,7 +10,10 @@ import ( "github.com/dweymouth/go-subsonic" ) -type ImageFetcher func(string) (image.Image, error) +type ImageFetcher interface { + GetAlbumThumbnailFromCache(string) (image.Image, bool) + GetAlbumThumbnail(string) (image.Image, error) +} type AlbumIterator interface { NextN(int, func(*subsonic.AlbumID3)) @@ -121,22 +124,27 @@ func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *AlbumCard) { ac.ImgLoadCancel() ac.ImgLoadCancel = nil } - ctx, cancel := context.WithCancel(context.Background()) - go func(ctx context.Context) { - i, err := ag.imageFetcher(album.ID) - select { - case <-ctx.Done(): - return - default: - if err == nil { - ac.Cover.SetImage(i) - ac.Cover.Refresh() - } else { - log.Printf("error fetching image: %s", err.Error()) + if img, ok := ag.imageFetcher.GetAlbumThumbnailFromCache(album.ID); ok { + ac.Cover.SetImage(img) + ac.Cover.Refresh() + } else { + ctx, cancel := context.WithCancel(context.Background()) + go func(ctx context.Context) { + i, err := ag.imageFetcher.GetAlbumThumbnail(album.ID) + select { + case <-ctx.Done(): + return + default: + if err == nil { + ac.Cover.SetImage(i) + ac.Cover.Refresh() + } else { + log.Printf("error fetching image: %s", err.Error()) + } } - } - }(ctx) - ac.ImgLoadCancel = cancel + }(ctx) + ac.ImgLoadCancel = cancel + } // TODO: remove magic number 10 if !ag.done && !ag.fetching && albumIdx > len(ag.albums)-10 {