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}