purge history since May 2016
This commit is contained in:
commit
a5bf332911
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
finstr
|
||||
.gitdist
|
||||
|
18
License
Normal file
18
License
Normal file
|
@ -0,0 +1,18 @@
|
|||
finstr: A bullshit-free image gallery generator.
|
||||
Copyright (C) 2016 Alexander Weinhold
|
||||
|
||||
Unless explicitly stated otherwise, the following applies:
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
29
Readme.md
Normal file
29
Readme.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
finstr
|
||||
======
|
||||
|
||||
finstr is a bullshit-free image gallery generator. It produces pages according to a template and groups images by tag or month. Releases can be found at [releases.gutmet.org](https://releases.gutmet.org).
|
||||
|
||||
build
|
||||
=======
|
||||
|
||||
If you want to build the executable yourself, you need go1.11+ with module support. Checkout the repository and run 'go build cmd/finstr.go'.
|
||||
|
||||
quick start
|
||||
=====
|
||||
|
||||
Put the executable somewhere in your path, create a folder for your gallery, then
|
||||
|
||||
```
|
||||
> cd YOUR_FOLDER
|
||||
> finstr init
|
||||
```
|
||||
|
||||
Put some .jpgs inside the img folder. Tag them by creating files with the same name but .tags suffix. Each line is a tag, then
|
||||
|
||||
```
|
||||
> finstr gen
|
||||
> cd pages
|
||||
> firefox index.html
|
||||
```
|
||||
|
||||
finstr has also been integrated into the static site generator [wombat](/wombat/about).
|
21
cmd/finstr.go
Normal file
21
cmd/finstr.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.gutmet.org/finstr.git"
|
||||
"git.gutmet.org/finstr.git/initer"
|
||||
"git.gutmet.org/goutil.git"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
commands := []goutil.Command{
|
||||
goutil.NewCommandWithFlags("init", initer.Command, "initialize finstr directory"),
|
||||
goutil.NewCommandWithFlags("gen", finstr.Command, "generate gallery"),
|
||||
}
|
||||
err := goutil.Execute(commands)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
40
crop/crop.go
Normal file
40
crop/crop.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package crop
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.gutmet.org/goutil.git"
|
||||
"image"
|
||||
)
|
||||
|
||||
func boundInt(i, min, max int) int {
|
||||
return goutil.IntMin(goutil.IntMax(i, min), max)
|
||||
}
|
||||
|
||||
func bound(p image.Point, bounds image.Rectangle) image.Point {
|
||||
min := bounds.Min
|
||||
max := bounds.Max
|
||||
return image.Point{X: boundInt(p.X, min.X, max.X), Y: boundInt(p.Y, min.Y, max.Y)}
|
||||
}
|
||||
|
||||
type subImageSupport interface {
|
||||
SubImage(rec image.Rectangle) image.Image
|
||||
}
|
||||
|
||||
func Crop(img image.Image, topleft image.Point, w int, h int) (image.Image, error) {
|
||||
bounds := img.Bounds()
|
||||
topleft = bound(topleft, bounds)
|
||||
bottomright := bound(image.Point{X: topleft.X + w, Y: topleft.Y + h}, bounds)
|
||||
croparea := image.Rect(topleft.X, topleft.Y, bottomright.X, bottomright.Y)
|
||||
if timg, ok := img.(subImageSupport); ok {
|
||||
return timg.SubImage(croparea), nil
|
||||
} else {
|
||||
return nil, errors.New("Crop: image format does not support 'SubImage' method (cropping not supported)")
|
||||
}
|
||||
}
|
||||
|
||||
func CropCenter(img image.Image, w int, h int) (image.Image, error) {
|
||||
bounds := img.Bounds()
|
||||
center := image.Point{X: bounds.Min.X + bounds.Dx()/2, Y: bounds.Min.Y + bounds.Dy()/2}
|
||||
topleft := image.Point{X: center.X - w/2, Y: center.Y - h/2}
|
||||
return Crop(img, topleft, w, h)
|
||||
}
|
426
finstr.go
Normal file
426
finstr.go
Normal file
|
@ -0,0 +1,426 @@
|
|||
package finstr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.gutmet.org/finstr.git/crop"
|
||||
"git.gutmet.org/finstr.git/initer"
|
||||
"git.gutmet.org/go-resize.git"
|
||||
"git.gutmet.org/goutil.git"
|
||||
goimg "image"
|
||||
"image/jpeg"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dir string
|
||||
var outdir string
|
||||
var t *template.Template
|
||||
var suffix string
|
||||
var artsy bool
|
||||
|
||||
type link struct {
|
||||
Path string
|
||||
Name string
|
||||
Space bool
|
||||
}
|
||||
|
||||
type image struct {
|
||||
FilenameMedium string
|
||||
FilenameSmall string
|
||||
}
|
||||
|
||||
type page struct {
|
||||
Title string
|
||||
Links []link
|
||||
Images []image
|
||||
Back link
|
||||
Artsy bool
|
||||
}
|
||||
|
||||
func replaceFirst(s string, search string, replace string) string {
|
||||
return strings.Replace(s, search, replace, 1)
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
type calendarMonth struct {
|
||||
year int
|
||||
month int
|
||||
}
|
||||
|
||||
func (m calendarMonth) String() string {
|
||||
return fmt.Sprintf("%04d %v", m.year, time.Month(m.month).String())
|
||||
}
|
||||
|
||||
func (m calendarMonth) laterThan(m2 calendarMonth) bool {
|
||||
if m.year == m2.year {
|
||||
return m.month > m2.month
|
||||
} else {
|
||||
return m.year > m2.year
|
||||
}
|
||||
}
|
||||
|
||||
func byMonthDesc(m []calendarMonth) func(int, int) bool {
|
||||
return func(i, j int) bool { return m[i].laterThan(m[j]) }
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
type photo struct {
|
||||
filename string
|
||||
modified calendarMonth
|
||||
tags []string
|
||||
}
|
||||
|
||||
const dirImg = "img"
|
||||
|
||||
func dirOrig() string {
|
||||
return filepath.Join(dir, dirImg)
|
||||
}
|
||||
|
||||
func dirMedium() string {
|
||||
return filepath.Join(outdir, "img_m")
|
||||
}
|
||||
|
||||
func dirSmall() string {
|
||||
return filepath.Join(outdir, "img_k")
|
||||
}
|
||||
|
||||
func dirPages() string {
|
||||
return filepath.Join(outdir, "pages")
|
||||
}
|
||||
|
||||
func (p *photo) srcLocation(folder string) string {
|
||||
return replaceFirst(p.filename, dirOrig(), filepath.Base(folder))
|
||||
}
|
||||
|
||||
func (p *photo) filenameMedium() string {
|
||||
return p.srcLocation(dirMedium())
|
||||
}
|
||||
|
||||
func (p *photo) filenameSmall() string {
|
||||
return p.srcLocation(dirSmall())
|
||||
}
|
||||
|
||||
func (p *photo) img() image {
|
||||
img := image{}
|
||||
img.FilenameMedium = p.filenameMedium()
|
||||
img.FilenameSmall = p.filenameSmall()
|
||||
return img
|
||||
}
|
||||
|
||||
func byModified(p []photo) func(int, int) bool {
|
||||
return func(i, j int) bool {
|
||||
if p[i].modified == p[j].modified {
|
||||
return p[i].filename > p[j].filename
|
||||
} else {
|
||||
return p[i].modified.laterThan(p[j].modified)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
func filename(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
|
||||
func filenameDate(date calendarMonth) string {
|
||||
return fmt.Sprintf("%04d%02d", date.year, date.month)
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
func (p *page) String() string {
|
||||
buf := new(bytes.Buffer)
|
||||
p.Artsy = artsy
|
||||
err := t.Execute(buf, p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (p *page) write(name string) {
|
||||
goutil.OptPanic(goutil.WriteFile(filepath.Join(dirPages(), name+suffix), p.String()))
|
||||
}
|
||||
|
||||
func singlePage(arg *pageArg, photos []photo) {
|
||||
sort.Slice(photos, byModified(photos))
|
||||
img := make([]image, 0)
|
||||
for _, p := range photos {
|
||||
img = append(img, p.img())
|
||||
}
|
||||
p := &page{Images: img, Title: arg.title(), Back: arg.back()}
|
||||
p.write(arg.filename())
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
type pageArg struct {
|
||||
tag string
|
||||
date calendarMonth
|
||||
}
|
||||
|
||||
func (arg *pageArg) filename() string {
|
||||
if arg.tag != "" {
|
||||
return filename(arg.tag)
|
||||
} else {
|
||||
return filenameDate(arg.date)
|
||||
}
|
||||
}
|
||||
|
||||
func (arg *pageArg) title() string {
|
||||
if arg.tag != "" {
|
||||
return arg.tag
|
||||
} else {
|
||||
return arg.date.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (arg *pageArg) back() link {
|
||||
if arg.tag != "" {
|
||||
return link{Path: "ByTag.html", Name: "Back"}
|
||||
} else {
|
||||
return link{Path: "ByDate.html", Name: "Back"}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------*/
|
||||
|
||||
type generator struct {
|
||||
photos []photo
|
||||
}
|
||||
|
||||
func (g *generator) byTag() {
|
||||
l := make([]link, 0)
|
||||
photosWithTag := make(map[string][]photo)
|
||||
for _, photo := range g.photos {
|
||||
for _, tag := range photo.tags {
|
||||
photosWithTag[tag] = append(photosWithTag[tag], photo)
|
||||
}
|
||||
}
|
||||
tags := make([]string, 0)
|
||||
for tag := range photosWithTag {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
sort.Sort(sort.StringSlice(tags))
|
||||
for _, tag := range tags {
|
||||
photos := photosWithTag[tag]
|
||||
l = append(l, link{Path: filename(tag) + ".html", Name: tag})
|
||||
args := &pageArg{tag: tag}
|
||||
singlePage(args, photos)
|
||||
}
|
||||
back := link{Path: "index.html", Name: "Back"}
|
||||
p := &page{Links: l, Title: "photos by tag", Back: back}
|
||||
p.write("ByTag")
|
||||
}
|
||||
|
||||
func keys(m map[calendarMonth][]photo) []calendarMonth {
|
||||
keys := make([]calendarMonth, 0)
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (g *generator) byDate() {
|
||||
l := make([]link, 0)
|
||||
photosByMonth := make(map[calendarMonth][]photo)
|
||||
for _, photo := range g.photos {
|
||||
month := photo.modified
|
||||
photosByMonth[month] = append(photosByMonth[month], photo)
|
||||
}
|
||||
months := keys(photosByMonth)
|
||||
sort.Slice(months, byMonthDesc(months))
|
||||
for _, month := range months {
|
||||
photos := photosByMonth[month]
|
||||
l = append(l, link{Path: filenameDate(month) + ".html", Name: month.String(), Space: true})
|
||||
arg := &pageArg{date: month}
|
||||
singlePage(arg, photos)
|
||||
}
|
||||
back := link{Path: "index.html", Name: "Back"}
|
||||
p := page{Links: l, Title: "photos by date", Back: back}
|
||||
p.write("ByDate")
|
||||
}
|
||||
|
||||
func (g *generator) index() {
|
||||
byDate := link{Path: "ByDate.html", Name: "By Date", Space: true}
|
||||
byTag := link{Path: "ByTag.html", Name: "By Tag"}
|
||||
l := []link{byDate, byTag}
|
||||
back := link{Path: "/", Name: "Home"}
|
||||
p := &page{Links: l, Title: "photos", Back: back}
|
||||
p.write("index")
|
||||
}
|
||||
|
||||
func (g *generator) generate() {
|
||||
photoFiles := goutil.ListFilesExt(dirOrig(), ".jpg")
|
||||
tagFiles := goutil.ListFilesExt(dirOrig(), ".tags")
|
||||
photos := make(map[string]*photo)
|
||||
|
||||
for _, photoFile := range photoFiles {
|
||||
name := goutil.TrimExt(path.Base(photoFile))
|
||||
stat, err := os.Stat(photoFile)
|
||||
goutil.OptPanic(err)
|
||||
modified := stat.ModTime()
|
||||
month := calendarMonth{year: modified.Year(), month: int(modified.Month())}
|
||||
photos[name] = &photo{filename: photoFile, modified: month}
|
||||
}
|
||||
for _, tagFile := range tagFiles {
|
||||
name := goutil.TrimExt(path.Base(tagFile))
|
||||
tagString, err := goutil.ReadFile(tagFile)
|
||||
goutil.OptPanic(err)
|
||||
tags := strings.Split(tagString, "\n")
|
||||
for _, tag := range tags {
|
||||
if _, ok := photos[name]; !ok {
|
||||
panic("No matching photo file for " + tagFile)
|
||||
}
|
||||
if tag = strings.TrimSpace(tag); tag != "" {
|
||||
photos[name].tags = append(photos[name].tags, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range photos {
|
||||
g.photos = append(g.photos, *v)
|
||||
}
|
||||
g.byDate()
|
||||
g.byTag()
|
||||
g.index()
|
||||
}
|
||||
|
||||
func resizeAll(src string, dest string, w uint, h uint) {
|
||||
thumbnail := (w == h)
|
||||
rsz := func(w uint, h uint, img goimg.Image) goimg.Image { return resize.Resize(w, h, img, resize.Lanczos3) }
|
||||
files := goutil.ListFilesExt(src, ".jpg")
|
||||
for _, f := range files {
|
||||
outfile := filepath.Join(dest, strings.TrimPrefix(f, src))
|
||||
if goutil.PathExists(outfile) {
|
||||
fmt.Println(f + " already converted")
|
||||
continue
|
||||
}
|
||||
fmt.Println("Converting " + f)
|
||||
file, err := os.Open(f)
|
||||
goutil.OptPanic(err)
|
||||
img, err := jpeg.Decode(file)
|
||||
goutil.OptPanic(err)
|
||||
file.Close()
|
||||
var m goimg.Image
|
||||
if thumbnail {
|
||||
bounds := img.Bounds()
|
||||
if bounds.Dx() < bounds.Dy() {
|
||||
m = rsz(w, 0, img)
|
||||
} else {
|
||||
m = rsz(0, h, img)
|
||||
}
|
||||
if !artsy {
|
||||
m, err = crop.CropCenter(m, int(w), int(h))
|
||||
goutil.OptPanic(err)
|
||||
}
|
||||
} else {
|
||||
m = rsz(w, h, img)
|
||||
}
|
||||
out, err := os.Create(outfile)
|
||||
defer out.Close()
|
||||
goutil.OptPanic(err)
|
||||
goutil.OptPanic(jpeg.Encode(out, m, &jpeg.Options{Quality: 95}))
|
||||
}
|
||||
}
|
||||
|
||||
func boundSize() {
|
||||
fmt.Println("medium images")
|
||||
fmt.Println("-------------")
|
||||
resizeAll(dirOrig(), dirMedium(), 0, 1080)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func makeThumbnails() {
|
||||
fmt.Println("thumbnails")
|
||||
fmt.Println("----------")
|
||||
var size uint
|
||||
if artsy {
|
||||
size = 300
|
||||
} else {
|
||||
size = 200
|
||||
}
|
||||
resizeAll(dirMedium(), dirSmall(), size, size)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
type Flags struct {
|
||||
InDir string
|
||||
OutDir string
|
||||
Suffix string
|
||||
Clean bool
|
||||
}
|
||||
|
||||
func ensureFolders(clean bool) {
|
||||
outfolders := []string{dirMedium(), dirSmall(), dirPages()}
|
||||
for _, d := range outfolders {
|
||||
if clean {
|
||||
goutil.OptPanic(os.RemoveAll(d))
|
||||
}
|
||||
goutil.OptPanic(os.MkdirAll(d, 0755))
|
||||
}
|
||||
}
|
||||
|
||||
func readStyle() {
|
||||
style, err := goutil.ReadFile(initer.InitFlag(dir))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if style == "artsy" {
|
||||
artsy = true
|
||||
} else {
|
||||
artsy = false
|
||||
}
|
||||
}
|
||||
|
||||
func Generate(fl Flags) (e error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
e = errors.New(fmt.Sprint("finstr: ", r))
|
||||
}
|
||||
}()
|
||||
dir = fl.InDir
|
||||
if fl.InDir != "" && fl.OutDir == "" {
|
||||
outdir = fl.InDir
|
||||
} else {
|
||||
outdir = fl.OutDir
|
||||
}
|
||||
suffix = fl.Suffix
|
||||
initer.InitializedOrDie(dir)
|
||||
ensureFolders(fl.Clean)
|
||||
readStyle()
|
||||
boundSize()
|
||||
makeThumbnails()
|
||||
pageTemplate, err := goutil.ReadFile(initer.TemplateFile(dir))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t = template.Must(template.New("site").Parse(pageTemplate))
|
||||
gen := &generator{}
|
||||
gen.generate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func Command() (goutil.CommandFlagsInit, goutil.CommandFunc) {
|
||||
f := Flags{}
|
||||
flagsInit := func(s *flag.FlagSet) {
|
||||
s.StringVar(&f.InDir, "in", "", "use 'in' directory as input instead of current working directory")
|
||||
s.StringVar(&f.OutDir, "out", "", "use 'out' directory as output instead of 'in' directory")
|
||||
s.StringVar(&f.Suffix, "suffix", ".html", "file extension of generated pages")
|
||||
s.BoolVar(&f.Clean, "clean", false, "remove old files in output directory before generating (use with caution)")
|
||||
}
|
||||
return flagsInit, func([]string) error {
|
||||
return Generate(f)
|
||||
}
|
||||
}
|
6
go.mod
Normal file
6
go.mod
Normal file
|
@ -0,0 +1,6 @@
|
|||
module git.gutmet.org/finstr.git
|
||||
|
||||
require (
|
||||
git.gutmet.org/go-resize.git v0.0.0-20180221191011-83c6a9932646
|
||||
git.gutmet.org/goutil.git v0.0.0-20181222191234-18f9120c8c1e
|
||||
)
|
131
initer/initer.go
Normal file
131
initer/initer.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package initer
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"git.gutmet.org/goutil.git"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const htmlTemplate = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: Open Sans,Clear Sans,Verdana,Helvetica,Arial;
|
||||
font-size : 12pt;
|
||||
max-width: 850px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-color : black;
|
||||
color: #E8E8E8;
|
||||
text-align:justify;
|
||||
}
|
||||
|
||||
a {
|
||||
color : #0D0;
|
||||
}
|
||||
a:hover {
|
||||
color : red;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
div.gallery {
|
||||
text-align: left;
|
||||
}
|
||||
h1 {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Title}}</h1>
|
||||
<div class="gallery">
|
||||
|
||||
{{if .Links}}{{range $index, $link := .Links}}<a href="{{$link.Path}}">{{$link.Name}}</a><br>{{if $link.Space}}<br>{{end}}{{end}}{{end}}
|
||||
|
||||
{{if .Images}}{{range $index, $img := .Images}}<a href="../{{$img.FilenameMedium}}"><img src="../{{$img.FilenameSmall}}" /></a>{{if $.Artsy}}<br><br>{{end}}{{end}}{{end}}
|
||||
|
||||
</div>
|
||||
<br><br>
|
||||
<a href="{{.Back.Path}}">{{.Back.Name}}</a>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const mdTemplate = `---
|
||||
title: {{.Title}}
|
||||
---
|
||||
{{if .Links}}{{range $index, $link := .Links}}<a href="{{$link.Path}}">{{$link.Name}}</a><br>{{if $link.Space}}<br>{{end}}{{end}}{{end}}
|
||||
|
||||
{{if .Images}}{{range $index, $img := .Images}}<a href="../{{$img.FilenameMedium}}"><img src="../{{$img.FilenameSmall}}" /></a>{{if $.Artsy}}<br><br>{{end}}{{end}}{{end}}
|
||||
<br><br>
|
||||
<a href="{{.Back.Path}}">{{.Back.Name}}</a>
|
||||
`
|
||||
|
||||
type IniterFlags struct {
|
||||
Dir string
|
||||
Markdown bool
|
||||
Artsy bool
|
||||
}
|
||||
|
||||
func InitializedOrDie(dir string) {
|
||||
paths := []string{imgFolder(dir), InitFlag(dir), TemplateFile(dir)}
|
||||
for _, p := range paths {
|
||||
if !goutil.PathExists(p) {
|
||||
panic("Not a finstr folder? Did not find " + p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func imgFolder(dir string) string {
|
||||
return filepath.Join(dir, "img")
|
||||
}
|
||||
|
||||
func InitFlag(dir string) string {
|
||||
return filepath.Join(dir, ".finstr")
|
||||
}
|
||||
|
||||
func readme(dir string) string {
|
||||
return filepath.Join(imgFolder(dir), "Readme")
|
||||
}
|
||||
|
||||
func TemplateFile(dir string) string {
|
||||
return filepath.Join(dir, "template")
|
||||
}
|
||||
|
||||
func Init(fl IniterFlags) error {
|
||||
dir := fl.Dir
|
||||
err := os.MkdirAll(imgFolder(dir), 0755)
|
||||
var style string
|
||||
if fl.Artsy {
|
||||
style = "artsy"
|
||||
} else {
|
||||
style = "square"
|
||||
}
|
||||
var template string
|
||||
if fl.Markdown {
|
||||
template = mdTemplate
|
||||
} else {
|
||||
template = htmlTemplate
|
||||
}
|
||||
err = goutil.OptDo(err, func() error { return goutil.WriteFile(InitFlag(dir), style) })
|
||||
err = goutil.OptDo(err, func() error { return goutil.WriteFile(readme(dir), "Put your .jpgs and .tags here") })
|
||||
err = goutil.OptDo(err, func() error { return goutil.WriteFile(TemplateFile(dir), template) })
|
||||
return err
|
||||
}
|
||||
|
||||
func Command() (goutil.CommandFlagsInit, goutil.CommandFunc) {
|
||||
f := IniterFlags{}
|
||||
flagsInit := func(s *flag.FlagSet) {
|
||||
s.BoolVar(&f.Markdown, "markdown", false, "init with Markdown template instead of HTML")
|
||||
s.StringVar(&f.Dir, "dir", "", "initialize 'dir' instead of current working directory")
|
||||
s.BoolVar(&f.Artsy, "artsy", false, "mark gallery to generate thumbnails with original ratio instead of square")
|
||||
}
|
||||
return flagsInit, func([]string) error {
|
||||
return Init(f)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user