diff --git a/core/artwork/cache_warmer.go b/core/artwork/cache_warmer.go index a3ef8d8a..0ad33747 100644 --- a/core/artwork/cache_warmer.go +++ b/core/artwork/cache_warmer.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "io" + "sync" + "time" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" + "github.com/navidrome/navidrome/model/request" "github.com/navidrome/navidrome/utils/pl" ) @@ -22,24 +25,63 @@ func NewCacheWarmer(artwork Artwork) CacheWarmer { } a := &cacheWarmer{ - artwork: artwork, - input: make(chan string), + artwork: artwork, + wakeSignal: make(chan struct{}, 1), } - go a.run(context.TODO()) + + // Create a context with a fake admin user, to be able to pre-cache Playlist CoverArts + ctx := request.WithUser(context.TODO(), model.User{IsAdmin: true}) + go a.run(ctx) return a } type cacheWarmer struct { - artwork Artwork - input chan string + artwork Artwork + buffer []string + mutex sync.Mutex + wakeSignal chan struct{} } func (a *cacheWarmer) PreCache(artID model.ArtworkID) { - a.input <- artID.String() + a.mutex.Lock() + defer a.mutex.Unlock() + a.buffer = append(a.buffer, artID.String()) + a.sendWakeSignal() +} + +func (a *cacheWarmer) sendWakeSignal() { + // Don't block if the previous signal was not read yet + select { + case a.wakeSignal <- struct{}{}: + default: + } } func (a *cacheWarmer) run(ctx context.Context) { - errs := pl.Sink(ctx, 2, a.input, a.doCacheImage) + for { + time.AfterFunc(5*time.Second, func() { + a.sendWakeSignal() + }) + <-a.wakeSignal + + a.mutex.Lock() + var batch []string + if len(a.buffer) > 0 { + batch = a.buffer + a.buffer = nil + } + a.mutex.Unlock() + + if len(batch) > 0 { + a.processBatch(ctx, batch) + } + } +} + +func (a *cacheWarmer) processBatch(ctx context.Context, batch []string) { + log.Trace(ctx, "PreCaching a new batch of artwork", "batchSize", len(batch)) + input := pl.FromSlice(ctx, batch) + errs := pl.Sink(ctx, 2, input, a.doCacheImage) for err := range errs { log.Warn(ctx, "Error warming cache", err) }