Skip to content

Commit

Permalink
Merge pull request #5 from waynezhang/feat/concurrency
Browse files Browse the repository at this point in the history
Process image simultaneously
  • Loading branch information
waynezhang authored Jan 4, 2024
2 parents 44a3d49 + 2c478de commit 15c6616
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 34 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
github.com/tdewolff/parse/v2 v2.7.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
Expand Down
41 changes: 30 additions & 11 deletions internal/export/default_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io/fs"
"os"
"path/filepath"
"sync"

cp "github.com/otiai10/copy"
"github.com/waynezhang/foto/internal/cache"
Expand All @@ -27,30 +28,48 @@ func (ctx defaultExportContext) buildIndex(cfg config.Config) ([]indexer.Section
return indexer.Build(cfg.GetSectionMetadata(), cfg.GetExtractOption())
}

func (ctx defaultExportContext) exportPhotos(sections []indexer.Section, outputPath string, cache cache.Cache, progressFn progressFunc) {
func (ctx defaultExportContext) exportPhotos(
sections []indexer.Section,
outputPath string,
cache cache.Cache,
postProgressFn progressFunc,
) {
if err := files.EnsureDirectory(outputPath); err != nil {
utils.CheckFatalError(err, "Failed to prepare output directory")
return
}

wg := &sync.WaitGroup{}

for _, s := range sections {
for _, set := range s.ImageSets {
srcPath := filepath.Join(s.Folder, set.FileName)

log.Debug("Processing image %s", srcPath)
if progressFn != nil {
progressFn(srcPath)
}
wg.Add(1)

slug := s.Slug
thumbnailWidth := set.ThumbnailSize.Width
originalWidth := set.OriginalSize.Width
go func() {
defer wg.Done()

thumbnailPath := files.OutputPhotoThumbnailFilePath(outputPath, s.Slug, srcPath)
err := resizeImageAndCache(srcPath, thumbnailPath, set.ThumbnailSize.Width, cache)
utils.CheckFatalError(err, "Failed to generate thumbnail image")
thumbnailPath := files.OutputPhotoThumbnailFilePath(outputPath, slug, srcPath)
err := resizeImageAndCache(srcPath, thumbnailPath, thumbnailWidth, cache)
utils.CheckFatalError(err, "Failed to generate thumbnail image")

originalPath := files.OutputPhotoOriginalFilePath(outputPath, s.Slug, srcPath)
err = resizeImageAndCache(srcPath, originalPath, set.OriginalSize.Width, cache)
utils.CheckFatalError(err, "Failed to generate original image")
originalPath := files.OutputPhotoOriginalFilePath(outputPath, slug, srcPath)
err = resizeImageAndCache(srcPath, originalPath, originalWidth, cache)
utils.CheckFatalError(err, "Failed to generate original image")

log.Debug("Processing image %s", srcPath)
if postProgressFn != nil {
postProgressFn(srcPath)
}
}()
}
}

wg.Wait()
}

func (ctx defaultExportContext) generateIndexHtml(cfg config.Config, templatePath string, sections []indexer.Section, path string, minimizer mm.Minimizer) {
Expand Down
55 changes: 35 additions & 20 deletions internal/indexer/indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"regexp"
"sort"
"sync"

"github.com/waynezhang/foto/internal/config"
"github.com/waynezhang/foto/internal/images"
Expand Down Expand Up @@ -61,36 +62,50 @@ func Build(metadata []config.SectionMetadata, option config.ExtractOption) ([]Se
}

func buildImageSets(folder string, ascending bool, option config.ExtractOption) []ImageSet {
imageSet := []ImageSet{}
err := filepath.WalkDir(folder, func(path string, info os.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() || !images.IsPhotoSupported(path) {
return nil
}
files, err := os.ReadDir(folder)
if err != nil {
log.Fatal("Failed to get photos from %s (%v)", folder, err)
return []ImageSet{}
}

imgSet, err := buildImageSet(path, option)
if err != nil {
return err
ptrs := make([]*ImageSet, len(files))
wg := &sync.WaitGroup{}
for i, f := range files {
path := filepath.Join(folder, f.Name())
if f.IsDir() || !images.IsPhotoSupported(path) {
continue
}
imageSet = append(imageSet, *imgSet)

return nil
})
if err != nil {
log.Fatal("Failed to get photos from %s (%v)", folder, err)
wg.Add(1)

go func(idx int) {
defer wg.Done()

s, err := buildImageSet(path, option)
if err != nil {
log.Fatal("Failed to extract info from %s (%v)", path, err)
}
ptrs[idx] = s
}(i)
}
wg.Wait()

sets := []ImageSet{}
for _, s := range ptrs {
if s != nil {
sets = append(sets, *s)
}
}

sort.SliceStable(imageSet, func(i, j int) bool {
sort.SliceStable(sets, func(i, j int) bool {
if ascending {
return imageSet[i].FileName < imageSet[j].FileName
return sets[i].FileName < sets[j].FileName
} else {
return imageSet[i].FileName > imageSet[j].FileName
return sets[i].FileName > sets[j].FileName
}
})

return imageSet
return sets
}

func buildImageSet(path string, option config.ExtractOption) (*ImageSet, error) {
Expand Down

0 comments on commit 15c6616

Please sign in to comment.