Ajoute un cache pour les couvertures

This commit is contained in:
2025-09-09 09:07:48 +02:00
parent 78c3dfa686
commit cd01b5b2ca
17 changed files with 1018 additions and 34 deletions

129
pmocover/cache.go Normal file
View File

@@ -0,0 +1,129 @@
package pmocover
import (
"bytes"
"crypto/sha1"
"encoding/hex"
"errors"
"image"
"io"
"net/http"
"os"
"path/filepath"
"sync"
)
// CacheEntry représente une image stockée (original + dérivés)
type CacheEntry struct {
PK string `json:"pk"`
SourceURL string `json:"source_url"`
Hits int `json:"hits"`
LastUsed string `json:"last_used"` // ISO8601
}
// Cache persistant avec SQLite
type Cache struct {
dir string
limit int
db *DB
mu sync.Mutex
}
// NewCache ouvre ou crée un cache persistant avec SQLite
func NewCache(dir string, limit int) (*Cache, error) {
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
db, err := InitDB(dir)
if err != nil {
return nil, err
}
return &Cache{
dir: dir,
limit: limit,
db: db,
}, nil
}
// AddFromURL télécharge une image et la met en cache
func (c *Cache) AddFromURL(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", errors.New("bad status")
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return c.Add(url, data)
}
// Add ajoute une image déjà téléchargée
func (c *Cache) Add(url string, data []byte) (string, error) {
pk := pkFromURL(url)
origPath := filepath.Join(c.dir, pk+".orig.webp")
c.mu.Lock()
defer c.mu.Unlock()
if _, err := os.Stat(origPath); errors.Is(err, os.ErrNotExist) {
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return "", err
}
buf, err := encodeWebP(img)
if err != nil {
return "", err
}
if err := os.WriteFile(origPath, buf, 0o644); err != nil {
return "", err
}
}
_ = c.db.Add(pk, url) // ajoute ou met à jour la base
return pk, nil
}
// Get renvoie le chemin du fichier original et met à jour hits/last_used
func (c *Cache) Get(pk string) (string, error) {
c.mu.Lock()
defer c.mu.Unlock()
_, err := c.db.Get(pk)
if err != nil {
return "", err
}
_ = c.db.UpdateHit(pk)
origPath := filepath.Join(c.dir, pk+".orig.webp")
if _, err := os.Stat(origPath); err != nil {
return "", err
}
return origPath, nil
}
// Purge vide complètement le cache
func (c *Cache) Purge() error {
c.mu.Lock()
defer c.mu.Unlock()
files, _ := filepath.Glob(filepath.Join(c.dir, "*"))
for _, f := range files {
os.Remove(f)
}
return c.db.Purge()
}
// pkFromURL → hash stable basé sur lURL
func pkFromURL(url string) string {
h := sha1.Sum([]byte(url))
return hex.EncodeToString(h[:8])
}