feat(artwork): add UIThumbnailSize constant and update cache warmer to pre-cache thumbnails

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2026-03-18 07:59:10 -04:00
parent 31d94acfe7
commit 00b8fbd789
3 changed files with 40 additions and 9 deletions
+1
View File
@@ -71,6 +71,7 @@ const (
PlaceholderAlbumArt = "album-placeholder.webp" PlaceholderAlbumArt = "album-placeholder.webp"
PlaceholderAvatar = "logo-192x192.png" PlaceholderAvatar = "logo-192x192.png"
UICoverArtSize = 300 UICoverArtSize = 300
UIThumbnailSize = 80
DefaultUIVolume = 100 DefaultUIVolume = 100
DefaultUISearchDebounceMs = 200 DefaultUISearchDebounceMs = 200
+9 -8
View File
@@ -142,14 +142,15 @@ func (a *cacheWarmer) doCacheImage(ctx context.Context, id model.ArtworkID) erro
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel() defer cancel()
r, _, err := a.artwork.Get(ctx, id, consts.UICoverArtSize, true) for _, size := range []int{consts.UICoverArtSize, consts.UIThumbnailSize} {
if err != nil { r, _, err := a.artwork.Get(ctx, id, size, true)
return fmt.Errorf("caching id='%s': %w", id, err) if err != nil {
} return fmt.Errorf("caching id='%s', size=%d: %w", id, size, err)
defer r.Close() }
_, err = io.Copy(io.Discard, r) defer r.Close()
if err != nil { if _, err = io.Copy(io.Discard, r); err != nil {
return err return err
}
} }
return nil return nil
} }
+30 -1
View File
@@ -6,11 +6,13 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest" "github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils/cache" "github.com/navidrome/navidrome/utils/cache"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
@@ -173,20 +175,47 @@ var _ = Describe("CacheWarmer", func() {
return len(cw.buffer) return len(cw.buffer)
}).Should(Equal(0)) }).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 { 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) { func (m *mockArtwork) Get(ctx context.Context, artID model.ArtworkID, size int, square bool) (io.ReadCloser, time.Time, error) {
if m.err != nil { if m.err != nil {
return nil, time.Time{}, m.err 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 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) { 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) return m.Get(ctx, model.ArtworkID{}, size, square)
} }