mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-05 13:37:38 +03:00
Create and configure image cache
This commit is contained in:
parent
d308e7ca46
commit
1bc68c20fc
7 changed files with 54 additions and 11 deletions
4
Makefile
4
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -18,5 +18,6 @@ var Set = wire.NewSet(
|
||||||
NewMediaStreamer,
|
NewMediaStreamer,
|
||||||
transcoder.New,
|
transcoder.New,
|
||||||
NewTranscodingCache,
|
NewTranscodingCache,
|
||||||
|
NewImageCache,
|
||||||
NewPlayers,
|
NewPlayers,
|
||||||
)
|
)
|
||||||
|
|
10
wire_gen.go
10
wire_gen.go
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue