A Go program to generate a Markdown file with a gallery of images.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: initial version

Marcos Gabarda a257e086

+230
+32
.gitignore
··· 1 + # Created by https://gitignore.org 2 + # Go.gitignore 3 + 4 + # If you prefer the allow list template instead of the deny list, see community template: 5 + # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 6 + # 7 + # Binaries for programs and plugins 8 + wallpaper-gallery 9 + *.exe 10 + *.exe~ 11 + *.dll 12 + *.so 13 + *.dylib 14 + 15 + # Test binary, built with `go test -c` 16 + *.test 17 + 18 + # Code coverage profiles and other test artifacts 19 + *.out 20 + coverage.* 21 + *.coverprofile 22 + profile.cov 23 + 24 + # Dependency directories (remove the comment below to include it) 25 + # vendor/ 26 + 27 + # Go workspace file 28 + go.work 29 + go.work.sum 30 + 31 + # env file 32 + .env
+13
.pre-commit-config.yaml
··· 1 + repos: 2 + - repo: https://github.com/pre-commit/pre-commit-hooks 3 + rev: v5.0.0 4 + hooks: 5 + - id: end-of-file-fixer 6 + - id: trailing-whitespace 7 + - repo: local 8 + hooks: 9 + - id: gofmt 10 + name: gofmt 11 + entry: gofmt -w . 12 + language: system 13 + pass_filenames: false
+3
README.md
··· 1 + # wallpaper-gallery 2 + 3 + A small program in Go to create a Markdown library of images.
+5
go.mod
··· 1 + module wallpaper-gallery 2 + 3 + go 1.26.1 4 + 5 + require golang.org/x/image v0.37.0 // indirect
+2
go.sum
··· 1 + golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA= 2 + golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
+175
main.go
··· 1 + package main 2 + 3 + import ( 4 + "flag" 5 + "fmt" 6 + "image" 7 + "image/jpeg" 8 + "image/png" 9 + "log" 10 + "os" 11 + "path/filepath" 12 + "text/template" 13 + "time" 14 + 15 + "golang.org/x/image/draw" 16 + ) 17 + 18 + // ImageElement represents an image element in the gallery. 19 + type ImageElement struct { 20 + Path string 21 + FileName string 22 + RelativePath string 23 + } 24 + 25 + func main() { 26 + start := time.Now() 27 + 28 + location := flag.String("f", "", "path to the folder with the images") 29 + maxSize := flag.Int("s", 600, "size of the thumbnail") 30 + 31 + flag.Parse() 32 + 33 + entries, err := os.ReadDir(*location) 34 + if err != nil { 35 + log.Fatal(err) 36 + } 37 + 38 + numImages := 0 39 + thumbDirName := ".thumbnails" 40 + thumbDir := filepath.Join(*location, thumbDirName) 41 + os.Mkdir(thumbDir, 0755) 42 + 43 + ch := make(chan string) 44 + 45 + for _, e := range entries { 46 + if !e.IsDir() { 47 + srcPath := filepath.Join(*location, e.Name()) 48 + thumbPath := filepath.Join(thumbDir, e.Name()) 49 + ext := filepath.Ext(srcPath) 50 + if ext == ".jpeg" || ext == ".jpg" || ext == ".png" { 51 + numImages++ 52 + go createThumbnail(srcPath, thumbPath, *maxSize, ch) 53 + } 54 + } 55 + } 56 + 57 + createdThumbnails := make([]ImageElement, numImages) 58 + for i := 0; i < numImages; i++ { 59 + path := <-ch 60 + fileName := filepath.Base(path) 61 + createdThumbnails[i] = ImageElement{ 62 + Path: path, 63 + FileName: fileName, 64 + RelativePath: filepath.Join(thumbDirName, fileName), 65 + } 66 + } 67 + 68 + createMarkdownFile(*location, &createdThumbnails) 69 + 70 + fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds()) 71 + } 72 + 73 + // Thumbnail creation 74 + // ------------------------------------------------------------------------------------ 75 + 76 + // createThumbnail take the src path and create the thumbnail in the thumbPath 77 + func createThumbnail(srcPath, thumbPath string, maxSize int, ch chan<- string) { 78 + srcImg, err := load(srcPath) 79 + if err != nil { 80 + log.Fatal("Error loading the image.") 81 + } 82 + dstImg := scale(srcImg, maxSize) 83 + save(thumbPath, dstImg) 84 + ch <- thumbPath 85 + } 86 + 87 + // scale the image to the max size 88 + func scale(src image.Image, maxSize int) image.Image { 89 + bounds := src.Bounds() 90 + 91 + width, height := bounds.Dx(), bounds.Dy() 92 + 93 + var newWidth, newHeight int 94 + if width >= height { 95 + newWidth = maxSize 96 + newHeight = int(float64(height) * float64(maxSize) / float64(width)) 97 + } else { 98 + newHeight = maxSize 99 + newWidth = int(float64(width) * float64(maxSize) / float64(height)) 100 + } 101 + dst := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) 102 + draw.CatmullRom.Scale(dst, dst.Bounds(), src, bounds, draw.Over, nil) 103 + return dst 104 + } 105 + 106 + // load the image, decodes it, and creates a NRGBA image. 107 + func load(filePath string) (image.Image, error) { 108 + 109 + imgFile, err := os.Open(filePath) 110 + if err != nil { 111 + log.Fatal("Cannot read file:", err) 112 + } 113 + 114 + defer imgFile.Close() 115 + 116 + img, _, err := image.Decode(imgFile) 117 + if err != nil { 118 + log.Fatal("Cannot decode file:", err) 119 + } 120 + 121 + return img, nil 122 + } 123 + 124 + // save the image in the given path 125 + func save(filePath string, img image.Image) { 126 + imgFile, err := os.Create(filePath) 127 + if err != nil { 128 + log.Fatal("Cannot create file:", err) 129 + } 130 + defer imgFile.Close() 131 + extension := filepath.Ext(filePath) 132 + switch extension { 133 + case ".jpeg", ".jpg": 134 + jpeg.Encode(imgFile, img, &jpeg.Options{Quality: 85}) 135 + case ".png": 136 + png.Encode(imgFile, img) 137 + } 138 + } 139 + 140 + // Template 141 + // ------------------------------------------------------------------------------------- 142 + 143 + const mdTemplate = `# wallpapers gallery 144 + 145 + Preview of all the available wallpapers. 146 + 147 + --- 148 + {{ range $index, $element := .Images }} 149 + [![wallparer-{{ $index }}]({{ $element.RelativePath }})]({{ $element.FileName }}) 150 + {{ end }} 151 + --- 152 + ` 153 + 154 + // GalleryData is the context to the template. 155 + type GalleryData struct { 156 + Images []ImageElement 157 + } 158 + 159 + // createMarkdownFile cretes the Markdown file using the template. 160 + func createMarkdownFile(location string, images *[]ImageElement) error { 161 + tmpl, err := template.New("gallery").Parse(mdTemplate) 162 + if err != nil { 163 + log.Fatal("Error parsing template", err) 164 + } 165 + 166 + galleryFileName := filepath.Join(location, "README.md") 167 + galleryFile, err := os.OpenFile(galleryFileName, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) 168 + if err != nil { 169 + log.Fatal("Error creating file", err) 170 + } 171 + defer galleryFile.Close() 172 + 173 + data := GalleryData{Images: *images} 174 + return tmpl.Execute(galleryFile, data) 175 + }