indexer.go 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package albums
  2. import (
  3. "archive/zip"
  4. "errors"
  5. "fmt"
  6. "image"
  7. _ "image/jpeg"
  8. _ "image/png"
  9. "path/filepath"
  10. "sort"
  11. "strings"
  12. "time"
  13. _ "golang.org/x/image/webp"
  14. "viewer/internal/models"
  15. )
  16. var ErrNoValidImages = errors.New("no valid images in zip")
  17. var allowedExtensions = map[string]struct{}{
  18. ".jpg": {},
  19. ".jpeg": {},
  20. ".png": {},
  21. ".webp": {},
  22. }
  23. type Indexer struct{}
  24. func NewIndexer() *Indexer {
  25. return &Indexer{}
  26. }
  27. func (i *Indexer) BuildFromZip(zipPath string, albumID string, originalFilename string) (*models.AlbumIndex, error) {
  28. r, err := zip.OpenReader(zipPath)
  29. if err != nil {
  30. return nil, fmt.Errorf("open zip: %w", err)
  31. }
  32. defer r.Close()
  33. entries := make([]*zip.File, 0, len(r.File))
  34. for _, f := range r.File {
  35. if f.FileInfo().IsDir() {
  36. continue
  37. }
  38. ext := strings.ToLower(filepath.Ext(f.Name))
  39. if _, ok := allowedExtensions[ext]; !ok {
  40. continue
  41. }
  42. entries = append(entries, f)
  43. }
  44. sort.Slice(entries, func(a, b int) bool {
  45. return strings.ToLower(entries[a].Name) < strings.ToLower(entries[b].Name)
  46. })
  47. photos := make([]models.PhotoMeta, 0, len(entries))
  48. for _, f := range entries {
  49. rc, err := f.Open()
  50. if err != nil {
  51. continue
  52. }
  53. cfg, _, err := image.DecodeConfig(rc)
  54. rc.Close()
  55. if err != nil || cfg.Width <= 0 || cfg.Height <= 0 {
  56. continue
  57. }
  58. idx := len(photos)
  59. photos = append(photos, models.PhotoMeta{
  60. I: idx,
  61. Name: f.Name,
  62. W: cfg.Width,
  63. H: cfg.Height,
  64. Ratio: float64(cfg.Width) / float64(cfg.Height),
  65. })
  66. }
  67. if len(photos) == 0 {
  68. return nil, ErrNoValidImages
  69. }
  70. return &models.AlbumIndex{
  71. AlbumID: albumID,
  72. OriginalFilename: originalFilename,
  73. CreatedAt: time.Now().UTC().Format(time.RFC3339),
  74. PhotoCount: len(photos),
  75. Photos: photos,
  76. }, nil
  77. }