From 00b8fbd7894a0124973fc77404845a2d0f703e1e Mon Sep 17 00:00:00 2001 From: Deluan Date: Wed, 18 Mar 2026 07:59:10 -0400 Subject: [PATCH] feat(artwork): add UIThumbnailSize constant and update cache warmer to pre-cache thumbnails Signed-off-by: Deluan --- consts/consts.go | 1 + core/artwork/cache_warmer.go | 17 +++++++++-------- core/artwork/cache_warmer_test.go | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/consts/consts.go b/consts/consts.go index 9f4387ae6..38b277eab 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -71,6 +71,7 @@ const ( PlaceholderAlbumArt = "album-placeholder.webp" PlaceholderAvatar = "logo-192x192.png" UICoverArtSize = 300 + UIThumbnailSize = 80 DefaultUIVolume = 100 DefaultUISearchDebounceMs = 200 diff --git a/core/artwork/cache_warmer.go b/core/artwork/cache_warmer.go index f13820d00..83c98c806 100644 --- a/core/artwork/cache_warmer.go +++ b/core/artwork/cache_warmer.go @@ -142,14 +142,15 @@ func (a *cacheWarmer) doCacheImage(ctx context.Context, id model.ArtworkID) erro ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - r, _, err := a.artwork.Get(ctx, id, consts.UICoverArtSize, true) - if err != nil { - return fmt.Errorf("caching id='%s': %w", id, err) - } - defer r.Close() - _, err = io.Copy(io.Discard, r) - if err != nil { - return err + for _, size := range []int{consts.UICoverArtSize, consts.UIThumbnailSize} { + r, _, err := a.artwork.Get(ctx, id, size, true) + if err != nil { + return fmt.Errorf("caching id='%s', size=%d: %w", id, size, err) + } + defer r.Close() + if _, err = io.Copy(io.Discard, r); err != nil { + return err + } } return nil } diff --git a/core/artwork/cache_warmer_test.go b/core/artwork/cache_warmer_test.go index abf4f259a..47e187f41 100644 --- a/core/artwork/cache_warmer_test.go +++ b/core/artwork/cache_warmer_test.go @@ -6,11 +6,13 @@ import ( "fmt" "io" "strings" + "sync" "sync/atomic" "time" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/conf/configtest" + "github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/utils/cache" . "github.com/onsi/ginkgo/v2" @@ -173,20 +175,47 @@ var _ = Describe("CacheWarmer", func() { return len(cw.buffer) }).Should(Equal(0)) }) + + It("pre-caches both UICoverArtSize and UIThumbnailSize", func() { + cw := NewCacheWarmer(aw, fc).(*cacheWarmer) + cw.PreCache(model.MustParseArtworkID("al-1")) + + Eventually(func() int { + cw.mutex.Lock() + defer cw.mutex.Unlock() + return len(cw.buffer) + }).Should(Equal(0)) + + sizes := aw.getCachedSizes() + Expect(sizes).To(ContainElements(consts.UICoverArtSize, consts.UIThumbnailSize)) + }) }) }) type mockArtwork struct { - err error + err error + mu sync.Mutex + cachedSizes []int } func (m *mockArtwork) Get(ctx context.Context, artID model.ArtworkID, size int, square bool) (io.ReadCloser, time.Time, error) { if m.err != nil { return nil, time.Time{}, m.err } + m.mu.Lock() + m.cachedSizes = append(m.cachedSizes, size) + m.mu.Unlock() return io.NopCloser(strings.NewReader("test")), time.Now(), nil } +func (m *mockArtwork) getCachedSizes() []int { + m.mu.Lock() + defer m.mu.Unlock() + result := make([]int, len(m.cachedSizes)) + copy(result, m.cachedSizes) + return result +} + func (m *mockArtwork) GetOrPlaceholder(ctx context.Context, id string, size int, square bool) (io.ReadCloser, time.Time, error) { return m.Get(ctx, model.ArtworkID{}, size, square) }