1
0
Fork 0
porg/main.go

159 lines
2.8 KiB
Go

package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/rwcarlsen/goexif/exif"
)
type processFn func(path, format string) error
var counters = make(map[string]int)
func main() {
srcDir := flag.String("in", ".", "Source directory")
format := flag.String("out", "dist/%Y/%m/%d-%H%M%S",
"Output format (use strftime symbols %Y, %y, %m, %d, %H, %M, %S)",
)
dryRun := flag.Bool("dryrun", false, "Dry run")
flag.Parse()
formatNorm := toTimeFormat(*format)
var fn processFn
n := -1
if *dryRun {
fmt.Println("Dry run. Examples of moved files:")
fn = previewFile
n = 10
} else {
fn = processFile
}
if err := processDir(*srcDir, formatNorm, n, fn); err != nil {
log.Println(err)
}
}
func processDir(dir, format string, max int, fn processFn) error {
var i int
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if i < 0 || i > max {
return filepath.SkipDir
}
if !info.IsDir() {
i++
return fn(path, format)
}
return nil
})
}
func processFile(path, format string) error {
if ext := strings.ToLower(filepath.Ext(path)); !isSupportedExt(ext) {
return nil
}
src, err := os.Open(path)
if err != nil {
return err
}
defer src.Close()
ts, err := getDate(src)
if err != nil {
return err
}
newPath := ts.Format(format) + "-" + strconv.Itoa(nextIndex(ts)) + ".jpg"
dir := filepath.Dir(newPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
dest, err := os.Create(newPath)
if err != nil {
return err
}
defer dest.Close()
if _, err := io.Copy(dest, src); err != nil {
return err
}
return nil
}
func previewFile(path, format string) error {
if ext := strings.ToLower(filepath.Ext(path)); !isSupportedExt(ext) {
return nil
}
src, err := os.Open(path)
if err != nil {
return err
}
defer src.Close()
ts, err := getDate(src)
if err != nil {
return err
}
newPath := ts.Format(format) + "-" + strconv.Itoa(nextIndex(ts)) + ".jpg"
fmt.Printf("%s -> %s\n", path, newPath)
return nil
}
func isSupportedExt(ext string) bool {
switch ext {
case ".jpg":
return true
default:
return false
}
}
func getDate(fd io.ReadSeeker) (time.Time, error) {
ef, err := exif.Decode(fd)
if err != nil {
return time.Time{}, err
}
_, err = fd.Seek(0, 0)
if err != nil {
return time.Time{}, err
}
return ef.DateTime()
}
func nextIndex(ts time.Time) int {
key := ts.Format("20060102150405")
counters[key]++
return counters[key]
}
func toTimeFormat(f string) string {
f = strings.Replace(f, "%y", "2006", -1)
f = strings.Replace(f, "%Y", "06", -1)
f = strings.Replace(f, "%m", "01", -1)
f = strings.Replace(f, "%d", "02", -1)
f = strings.Replace(f, "%H", "15", -1)
f = strings.Replace(f, "%M", "04", -1)
f = strings.Replace(f, "%S", "05", -1)
return f
}