diff options
Diffstat (limited to 'internal/thumbnail/cache.go')
| -rw-r--r-- | internal/thumbnail/cache.go | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/internal/thumbnail/cache.go b/internal/thumbnail/cache.go new file mode 100644 index 0000000..2a1af40 --- /dev/null +++ b/internal/thumbnail/cache.go @@ -0,0 +1,121 @@ +package thumbnail + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "sync" + "time" +) + +type entry struct { + VideoPath string `json:"video_path"` + Thumbnail string `json:"thumbnail"` + ModTime time.Time `json:"mod_time"` + Timestamp time.Time `json:"timestamp"` +} + +type Cache struct { + entries map[string]entry + mu sync.RWMutex + path string +} + +func NewCache(root string) *Cache { + return newCache(root) +} + +func newCache(root string) *Cache { + path := filepath.Join(root, cacheFilename) + return &Cache{ + entries: make(map[string]entry), + path: path, + } +} + +func (c *Cache) Load() error { + c.mu.Lock() + defer c.mu.Unlock() + + data, err := os.ReadFile(c.path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return fmt.Errorf("read cache file: %w", err) + } + + var loaded []entry + if err := json.Unmarshal(data, &loaded); err != nil { + return fmt.Errorf("unmarshal cache: %w", err) + } + + c.entries = make(map[string]entry, len(loaded)) + for _, e := range loaded { + c.entries[e.VideoPath] = e + } + + return nil +} + +func (c *Cache) Lookup(videoPath string, modTime time.Time) (string, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + + e, ok := c.entries[videoPath] + if !ok { + return "", false + } + + if !e.ModTime.Equal(modTime) { + return "", false + } + + if _, err := os.Stat(e.Thumbnail); err != nil { + return "", false + } + + return e.Thumbnail, true +} + +func (c *Cache) Store(videoPath string, modTime time.Time, thumbnailPath string) error { + c.mu.Lock() + defer c.mu.Unlock() + + c.entries[videoPath] = entry{ + VideoPath: videoPath, + Thumbnail: thumbnailPath, + ModTime: modTime, + Timestamp: time.Now(), + } + + return c.flush() +} + +func (c *Cache) Remove(videoPath string) error { + c.mu.Lock() + defer c.mu.Unlock() + + delete(c.entries, videoPath) + return c.flush() +} + +func (c *Cache) flush() error { + loaded := make([]entry, 0, len(c.entries)) + for _, e := range c.entries { + loaded = append(loaded, e) + } + + data, err := json.MarshalIndent(loaded, "", " ") + if err != nil { + return fmt.Errorf("marshal cache: %w", err) + } + + if err := os.WriteFile(c.path, data, 0o644); err != nil { + return fmt.Errorf("write cache file: %w", err) + } + + return nil +} |
