internal/library/metacache.go

72 lines · 1646 bytes

 1package library
 2
 3import (
 4	"encoding/json"
 5	"errors"
 6	"io/fs"
 7	"log"
 8	"os"
 9	"path/filepath"
10
11	"gitbay.org/cmc/gallery/internal/format"
12)
13
14type metaEntry struct {
15	ModTime int64       `json:"mtime"`
16	Size    int64       `json:"size"`
17	Meta    format.Meta `json:"meta"`
18}
19
20// MetaCache persists extracted metadata keyed by album-relative path.
21type MetaCache struct {
22	path    string
23	entries map[string]metaEntry
24}
25
26func OpenMetaCache(path string) (*MetaCache, error) {
27	c := &MetaCache{path: path, entries: map[string]metaEntry{}}
28	data, err := os.ReadFile(path)
29	if errors.Is(err, fs.ErrNotExist) {
30		return c, nil
31	}
32	if err != nil {
33		return nil, err
34	}
35	if err := json.Unmarshal(data, &c.entries); err != nil {
36		log.Printf("meta cache %s unreadable, starting empty: %v", path, err)
37		c.entries = map[string]metaEntry{}
38	}
39	return c, nil
40}
41
42func (c *MetaCache) get(rel string, fi fs.FileInfo) (format.Meta, bool) {
43	e, ok := c.entries[rel]
44	if !ok || e.ModTime != fi.ModTime().UnixNano() || e.Size != fi.Size() {
45		return format.Meta{}, false
46	}
47	return e.Meta, true
48}
49
50func (c *MetaCache) put(rel string, fi fs.FileInfo, m format.Meta) {
51	c.entries[rel] = metaEntry{ModTime: fi.ModTime().UnixNano(), Size: fi.Size(), Meta: m}
52}
53
54func (c *MetaCache) save(keep map[string]bool) error {
55	for k := range c.entries {
56		if !keep[k] {
57			delete(c.entries, k)
58		}
59	}
60	data, err := json.Marshal(c.entries)
61	if err != nil {
62		return err
63	}
64	if err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil {
65		return err
66	}
67	tmp := c.path + ".tmp"
68	if err := os.WriteFile(tmp, data, 0o644); err != nil {
69		return err
70	}
71	return os.Rename(tmp, c.path)
72}