package gallery import ( "bytes" "context" "fmt" "hash/fnv" "log/slog" "os" "path" "strings" "time" "github.com/valkey-io/valkey-go" ) type Image struct { UID string X int Y int Width int Height int } const ( imagesKey = "gallery:images:list" hashKey = "gallery:images:hash" ) func (s *Service) autoRefresh() { t := time.NewTicker(time.Minute) for { ctx, cancle := context.WithTimeout(context.Background(), time.Minute) if err := s.assembleImageList(ctx); err != nil { slog.Error("error while refreshing gallery", "err", err) } cancle() <-t.C } } func (s *Service) assembleImageList(ctx context.Context) error { files, err := s.fs.ReadDir(s.cfg.Path) if err != nil { return fmt.Errorf("could not read contents of %s: %w", s.cfg.Path, err) } savedHash, _ := s.cache.Do(ctx, s.cache.B().Get().Key(hashKey).Build()).AsBytes() newHash := hashImageList(files) if bytes.Equal(savedHash, newHash) { slog.Debug("images up to date no refreshing of gallery", "savedHash", savedHash, "newHash", newHash) return nil } slog.Info("starting gallery refresh") defer slog.Info("gallery refresh completed") imgs := make([]Image, 0, len(files)) for _, file := range files { if strings.HasSuffix(file.Name(), ".md") { continue } imagePath := path.Join(s.cfg.Path, file.Name()) img, err := s.img.GetImage(imagePath) if err != nil || img.Image == nil { continue } uid, err := img.UID() if err != nil { continue } imgs = append(imgs, Image{ UID: uid, Width: img.Bounds().Dx(), Height: img.Bounds().Dy(), }) } if err = s.cache.Do(ctx, s.cache.B().Set().Key(hashKey).Value(valkey.BinaryString(newHash)).Build()).Error(); err != nil { return err } if err = s.cache.Do(ctx, s.cache.B().Set().Key(imagesKey).Value(valkey.JSON(imgs)).Build()).Error(); err != nil { return err } return nil } func hashImageList(files []os.FileInfo) []byte { h := fnv.New128() for _, file := range files { fmt.Fprint(h, file.ModTime()) fmt.Fprint(h, file.Size()) fmt.Fprint(h, file.Name()) } return h.Sum([]byte{}) } func (s *Service) GetImages(ctx context.Context) (images []Image, err error) { err = s.cache.Do(ctx, s.cache.B().Get().Key(imagesKey).Build()).DecodeJSON(&images) return }