Files
satbd-explorer/album_cover_cache.go

122 lines
2.7 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"sync"
"time"
"github.com/peterbourgon/diskv"
)
// An AlbumCoverCache is used to fetch and cache Album Cover image from www.bedetheque.com
type AlbumCoverCache struct {
dv *diskv.Diskv
getter HTTPGetter
URLs map[AlbumID]string
lock sync.RWMutex
// time to live of the cache, data which is older than this TTL will be automatically removed
TTL time.Duration
}
type ErrorNotRegistered AlbumID
func (e ErrorNotRegistered) Error() string {
return fmt.Sprintf("Cover URL for Album %d is not registered", AlbumID(e))
}
// NewAlbumCoverCache is creating a new cache at specified location on the fs
func NewAlbumCoverCache(basepath string, maxRequest uint, window time.Duration) *AlbumCoverCache {
res := &AlbumCoverCache{
dv: diskv.New(diskv.Options{
BasePath: basepath,
CacheSizeMax: 100 * 1024 * 1024, // 100 Mb
}),
URLs: make(map[AlbumID]string),
getter: NewRateLimitedGetter(maxRequest, window),
TTL: 3 * 31 * 24 * time.Hour, // 3 Months
}
return res
}
func (c *AlbumCoverCache) fetch(ID AlbumID) (io.ReadCloser, string, error) {
URL, ok := c.URLs[ID]
if ok == false {
return nil, "", ErrorNotRegistered(ID)
}
ext := AlbumCoverExt(URL)
resp, err := c.getter.Get(URL)
if err != nil {
return nil, ext, err
}
defer closeOrPanic(resp.Body, "GET "+URL)
err = c.dv.WriteStream(c.dataKey(ID), resp.Body, false)
if err != nil {
return nil, ext, err
}
md := coverMetadata{
Age: time.Now(),
}
mdbyte, err := json.Marshal(&md)
if err == nil {
err = c.dv.Write(c.metadataKey(ID), mdbyte)
if err != nil {
return nil, ext, err
}
}
rc, err := c.dv.ReadStream(c.dataKey(ID), false)
return rc, ext, err
}
type coverMetadata struct {
Age time.Time
}
func (c *AlbumCoverCache) metadataKey(ID AlbumID) string {
return AlbumIDString(ID) + ".metadata"
}
func (c *AlbumCoverCache) dataKey(ID AlbumID) string {
return AlbumIDString(ID) + ".data"
}
func (c *AlbumCoverCache) RegisterCover(ID AlbumID, URL string) {
c.lock.Lock()
defer c.lock.Unlock()
c.URLs[ID] = URL
}
// GetCover retrieves from the cache or either from www.bedetheque.com the Cover of an album
func (c *AlbumCoverCache) GetCover(ID AlbumID) (io.ReadCloser, string, error) {
c.lock.RLock()
defer c.lock.RUnlock()
if c.dv.Has(c.metadataKey(ID)) == false {
return c.fetch(ID)
}
mdbytes, err := c.dv.Read(c.metadataKey(ID))
if err != nil {
return c.fetch(ID)
}
var md coverMetadata
err = json.Unmarshal(mdbytes, &md)
if err != nil {
return c.fetch(ID)
}
// check TTL
if time.Now().Add(-c.TTL).After(md.Age) == true {
return c.fetch(ID)
}
URL := c.URLs[ID]
rc, err := c.dv.ReadStream(c.dataKey(ID), false)
return rc, AlbumCoverExt(URL), err
}