package plugins
import (
"context"
"time"
"github.com/jellydator/ttlcache/v3"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/plugins/host"
)
const (
defaultCacheTTL = 24 * time.Hour
)
type cacheServiceImpl struct {
pluginName string
cache *ttlcache.Cache[string, any]
defaultTTL time.Duration
}
func newCacheService(pluginName string) *cacheServiceImpl {
cache := ttlcache.New[string, any](
ttlcache.WithTTL[string, any](defaultCacheTTL),
)
go cache.Start()
return &cacheServiceImpl{
pluginName: pluginName,
cache: cache,
defaultTTL: defaultCacheTTL,
}
}
func (s *cacheServiceImpl) getTTL(seconds int64) time.Duration {
if seconds <= 0 {
return s.defaultTTL
}
return time.Duration(seconds) * time.Second
}
func (s *cacheServiceImpl) SetString(ctx context.Context, key string, value string, ttlSeconds int64) error {
s.cache.Set(key, value, s.getTTL(ttlSeconds))
return nil
}
func (s *cacheServiceImpl) GetString(ctx context.Context, key string) (string, bool, error) {
item := s.cache.Get(key)
if item == nil {
return "", false, nil
}
value, ok := item.Value().(string)
if !ok {
log.Debug(ctx, "Cache type mismatch", "plugin", s.pluginName, "key", key, "expected", "string")
return "", false, nil
}
return value, true, nil
}
func (s *cacheServiceImpl) SetInt(ctx context.Context, key string, value int64, ttlSeconds int64) error {
s.cache.Set(key, value, s.getTTL(ttlSeconds))
return nil
}
func (s *cacheServiceImpl) GetInt(ctx context.Context, key string) (int64, bool, error) {
item := s.cache.Get(key)
if item == nil {
return 0, false, nil
}
value, ok := item.Value().(int64)
if !ok {
log.Debug(ctx, "Cache type mismatch", "plugin", s.pluginName, "key", key, "expected", "int64")
return 0, false, nil
}
return value, true, nil
}
func (s *cacheServiceImpl) SetFloat(ctx context.Context, key string, value float64, ttlSeconds int64) error {
s.cache.Set(key, value, s.getTTL(ttlSeconds))
return nil
}
func (s *cacheServiceImpl) GetFloat(ctx context.Context, key string) (float64, bool, error) {
item := s.cache.Get(key)
if item == nil {
return 0, false, nil
}
value, ok := item.Value().(float64)
if !ok {
log.Debug(ctx, "Cache type mismatch", "plugin", s.pluginName, "key", key, "expected", "float64")
return 0, false, nil
}
return value, true, nil
}
func (s *cacheServiceImpl) SetBytes(ctx context.Context, key string, value []byte, ttlSeconds int64) error {
s.cache.Set(key, value, s.getTTL(ttlSeconds))
return nil
}
func (s *cacheServiceImpl) GetBytes(ctx context.Context, key string) ([]byte, bool, error) {
item := s.cache.Get(key)
if item == nil {
return nil, false, nil
}
value, ok := item.Value().([]byte)
if !ok {
log.Debug(ctx, "Cache type mismatch", "plugin", s.pluginName, "key", key, "expected", "[]byte")
return nil, false, nil
}
return value, true, nil
}
func (s *cacheServiceImpl) Has(ctx context.Context, key string) (bool, error) {
item := s.cache.Get(key)
return item != nil, nil
}
func (s *cacheServiceImpl) Remove(ctx context.Context, key string) error {
s.cache.Delete(key)
return nil
}
func (s *cacheServiceImpl) Close() error {
s.cache.Stop()
s.cache.DeleteAll()
log.Debug("Closed plugin cache", "plugin", s.pluginName)
return nil
}
var _ host.CacheService = (*cacheServiceImpl)(nil)