mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
resizedItem is the only implementation of artworkReader, which is declared in image_cache.go, and it is used by the worker's precache as well as the serving path -- so worker.go was reaching into serving.go for a cache type. representationTag stays in serving.go, where the HTTP validator belongs. Pure move.
73 lines
1.9 KiB
Go
73 lines
1.9 KiB
Go
package artwork
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
"github.com/navidrome/navidrome/consts"
|
|
"github.com/navidrome/navidrome/core/ffmpeg"
|
|
"github.com/navidrome/navidrome/utils/cache"
|
|
"github.com/navidrome/navidrome/utils/singleton"
|
|
)
|
|
|
|
// artworkReader is the cache.Item the image cache loader dispatches on: Reader
|
|
// produces the (possibly resized) bytes to store under Key.
|
|
type artworkReader interface {
|
|
cache.Item
|
|
Reader(ctx context.Context) (io.ReadCloser, error)
|
|
}
|
|
|
|
type imageCache struct {
|
|
cache.FileCache
|
|
}
|
|
|
|
func GetImageCache() cache.FileCache {
|
|
return singleton.GetInstance(func() *imageCache {
|
|
return &imageCache{
|
|
FileCache: cache.NewFileCache("Image", conf.Server.ImageCacheSize, consts.ImageCacheDir, consts.DefaultImageCacheMaxItems,
|
|
func(ctx context.Context, arg cache.Item) (io.Reader, error) {
|
|
return arg.(artworkReader).Reader(ctx)
|
|
}),
|
|
}
|
|
})
|
|
}
|
|
|
|
// resizedItem is an artworkReader that resizes bytes opened by open() and caches the
|
|
// result under a hash-derived key.
|
|
type resizedItem struct {
|
|
hash string
|
|
size int
|
|
square bool
|
|
ffmpeg ffmpeg.FFmpeg
|
|
open func() (io.ReadCloser, error)
|
|
}
|
|
|
|
// Key is the ETag namespaced for the cache, so the validator a client holds and the entry it
|
|
// validates can never drift apart.
|
|
func (r *resizedItem) Key() string {
|
|
return "h-" + representationTag(r.hash, r.size, r.square)
|
|
}
|
|
|
|
func (r *resizedItem) Reader(ctx context.Context) (io.ReadCloser, error) {
|
|
orig, err := r.open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer orig.Close()
|
|
data, err := readCapped(orig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resized, _, err := resizeImageData(ctx, r.ffmpeg, data, r.size, r.square)
|
|
if err != nil || resized == nil {
|
|
// Resize failed or image already within bounds: serve the original bytes.
|
|
return io.NopCloser(bytes.NewReader(data)), nil
|
|
}
|
|
if rc, ok := resized.(io.ReadCloser); ok {
|
|
return rc, nil
|
|
}
|
|
return io.NopCloser(resized), nil
|
|
}
|