Create and configure image cache

This commit is contained in:
Deluan 2020-04-04 22:23:20 -04:00 committed by Deluan Quintão
parent d308e7ca46
commit 1bc68c20fc
7 changed files with 54 additions and 11 deletions

View file

@ -16,6 +16,10 @@ server: check_go_env
@reflex -d none -c reflex.conf @reflex -d none -c reflex.conf
.PHONY: server .PHONY: server
wire: check_go_env
wire ./...
.PHONY: wire
watch: check_go_env watch: check_go_env
ginkgo watch -notify ./... ginkgo watch -notify ./...
.PHONY: watch .PHONY: watch

View file

@ -26,6 +26,7 @@ type nd struct {
IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"` IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"`
TranscodingCacheSize string `default:"100MB"` // in MB TranscodingCacheSize string `default:"100MB"` // in MB
ImageCacheSize string `default:"100MB"` // in MB
ProbeCommand string `default:"ffmpeg -i %s -f ffmetadata"` ProbeCommand string `default:"ffmpeg -i %s -f ffmetadata"`
// DevFlags. These are used to enable/disable debugging and incomplete features // DevFlags. These are used to enable/disable debugging and incomplete features

View file

@ -20,11 +20,16 @@ const (
UIAssetsLocalPath = "ui/build" UIAssetsLocalPath = "ui/build"
CacheDir = "cache" TranscodingCacheDir = "cache/transcoding"
DefaultTranscodingCacheSize = 100 * 1024 * 1024 // 100MB DefaultTranscodingCacheSize = 100 * 1024 * 1024 // 100MB
DefaultTranscodingCacheMaxItems = 0 // Unlimited DefaultTranscodingCacheMaxItems = 0 // Unlimited
DefaultTranscodingCachePurgeInterval = 10 * time.Minute DefaultTranscodingCachePurgeInterval = 10 * time.Minute
ImageCacheDir = "cache/images"
DefaultImageCacheSize = 100 * 1024 * 1024 // 100MB
DefaultImageCacheMaxItems = 0 // Unlimited
DefaultImageCachePurgeInterval = 10 * time.Minute
DevInitialUserName = "admin" DevInitialUserName = "admin"
DevInitialName = "Dev Admin" DevInitialName = "Dev Admin"

View file

@ -11,24 +11,33 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/static" "github.com/deluan/navidrome/static"
"github.com/dhowden/tag" "github.com/dhowden/tag"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/djherbis/fscache"
"github.com/dustin/go-humanize"
) )
type Cover interface { type Cover interface {
Get(ctx context.Context, id string, size int, out io.Writer) error Get(ctx context.Context, id string, size int, out io.Writer) error
} }
type cover struct { type ImageCache fscache.Cache
ds model.DataStore
func NewCover(ds model.DataStore, cache ImageCache) Cover {
return &cover{ds: ds, cache: cache}
} }
func NewCover(ds model.DataStore) Cover { type cover struct {
return &cover{ds} ds model.DataStore
cache fscache.Cache
} }
func (c *cover) getCoverPath(ctx context.Context, id string) (string, error) { func (c *cover) getCoverPath(ctx context.Context, id string) (string, error) {
@ -116,3 +125,20 @@ func readFromTag(path string) (io.Reader, error) {
} }
return bytes.NewReader(picture.Data), nil return bytes.NewReader(picture.Data), nil
} }
func NewImageCache() (ImageCache, error) {
cacheSize, err := humanize.ParseBytes(conf.Server.ImageCacheSize)
if err != nil {
cacheSize = consts.DefaultImageCacheSize
}
lru := fscache.NewLRUHaunter(consts.DefaultImageCacheMaxItems, int64(cacheSize), consts.DefaultImageCachePurgeInterval)
h := fscache.NewLRUHaunterStrategy(lru)
cacheFolder := filepath.Join(conf.Server.DataFolder, consts.ImageCacheDir)
log.Info("Creating image cache", "path", cacheFolder, "maxSize", humanize.Bytes(cacheSize),
"cleanUpInterval", consts.DefaultImageCachePurgeInterval)
fs, err := fscache.NewFs(cacheFolder, 0755)
if err != nil {
return nil, err
}
return fscache.NewCacheWithHaunter(fs, h)
}

View file

@ -22,7 +22,9 @@ type MediaStreamer interface {
NewStream(ctx context.Context, id string, reqFormat string, reqBitRate int) (*Stream, error) NewStream(ctx context.Context, id string, reqFormat string, reqBitRate int) (*Stream, error)
} }
func NewMediaStreamer(ds model.DataStore, ffm transcoder.Transcoder, cache fscache.Cache) MediaStreamer { type TranscodingCache fscache.Cache
func NewMediaStreamer(ds model.DataStore, ffm transcoder.Transcoder, cache TranscodingCache) MediaStreamer {
return &mediaStreamer{ds: ds, ffm: ffm, cache: cache} return &mediaStreamer{ds: ds, ffm: ffm, cache: cache}
} }
@ -205,14 +207,14 @@ func getFinalCachedSize(r fscache.ReadAtCloser) int64 {
return -1 return -1
} }
func NewTranscodingCache() (fscache.Cache, error) { func NewTranscodingCache() (TranscodingCache, error) {
cacheSize, err := humanize.ParseBytes(conf.Server.TranscodingCacheSize) cacheSize, err := humanize.ParseBytes(conf.Server.TranscodingCacheSize)
if err != nil { if err != nil {
cacheSize = consts.DefaultTranscodingCacheSize cacheSize = consts.DefaultTranscodingCacheSize
} }
lru := fscache.NewLRUHaunter(consts.DefaultTranscodingCacheMaxItems, int64(cacheSize), consts.DefaultTranscodingCachePurgeInterval) lru := fscache.NewLRUHaunter(consts.DefaultTranscodingCacheMaxItems, int64(cacheSize), consts.DefaultTranscodingCachePurgeInterval)
h := fscache.NewLRUHaunterStrategy(lru) h := fscache.NewLRUHaunterStrategy(lru)
cacheFolder := filepath.Join(conf.Server.DataFolder, consts.CacheDir) cacheFolder := filepath.Join(conf.Server.DataFolder, consts.TranscodingCacheDir)
log.Info("Creating transcoding cache", "path", cacheFolder, "maxSize", humanize.Bytes(cacheSize), log.Info("Creating transcoding cache", "path", cacheFolder, "maxSize", humanize.Bytes(cacheSize),
"cleanUpInterval", consts.DefaultTranscodingCachePurgeInterval) "cleanUpInterval", consts.DefaultTranscodingCachePurgeInterval)
fs, err := fscache.NewFs(cacheFolder, 0755) fs, err := fscache.NewFs(cacheFolder, 0755)

View file

@ -18,5 +18,6 @@ var Set = wire.NewSet(
NewMediaStreamer, NewMediaStreamer,
transcoder.New, transcoder.New,
NewTranscodingCache, NewTranscodingCache,
NewImageCache,
NewPlayers, NewPlayers,
) )

View file

@ -34,7 +34,11 @@ func CreateAppRouter() *app.Router {
func CreateSubsonicAPIRouter() (*subsonic.Router, error) { func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
dataStore := persistence.New() dataStore := persistence.New()
browser := engine.NewBrowser(dataStore) browser := engine.NewBrowser(dataStore)
cover := engine.NewCover(dataStore) imageCache, err := engine.NewImageCache()
if err != nil {
return nil, err
}
cover := engine.NewCover(dataStore, imageCache)
nowPlayingRepository := engine.NewNowPlayingRepository() nowPlayingRepository := engine.NewNowPlayingRepository()
listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository) listGenerator := engine.NewListGenerator(dataStore, nowPlayingRepository)
users := engine.NewUsers(dataStore) users := engine.NewUsers(dataStore)
@ -43,11 +47,11 @@ func CreateSubsonicAPIRouter() (*subsonic.Router, error) {
scrobbler := engine.NewScrobbler(dataStore, nowPlayingRepository) scrobbler := engine.NewScrobbler(dataStore, nowPlayingRepository)
search := engine.NewSearch(dataStore) search := engine.NewSearch(dataStore)
transcoderTranscoder := transcoder.New() transcoderTranscoder := transcoder.New()
cache, err := engine.NewTranscodingCache() transcodingCache, err := engine.NewTranscodingCache()
if err != nil { if err != nil {
return nil, err return nil, err
} }
mediaStreamer := engine.NewMediaStreamer(dataStore, transcoderTranscoder, cache) mediaStreamer := engine.NewMediaStreamer(dataStore, transcoderTranscoder, transcodingCache)
players := engine.NewPlayers(dataStore) players := engine.NewPlayers(dataStore)
router := subsonic.New(browser, cover, listGenerator, users, playlists, ratings, scrobbler, search, mediaStreamer, players) router := subsonic.New(browser, cover, listGenerator, users, playlists, ratings, scrobbler, search, mediaStreamer, players)
return router, nil return router, nil