commit 1275619fa069db7d4e46505875e101eefbe9c516 Author: Jay Date: Wed May 15 18:09:36 2019 +0800 first version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d3ed4c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.yml diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b90dfda --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +.PHONY: clean build + +build: + GOOS=linux go build -o convert-webp -ldflags "-s -w" . +clean: + rm -rf convert-webp && go clean diff --git a/config.default.yml b/config.default.yml new file mode 100644 index 0000000..a732e39 --- /dev/null +++ b/config.default.yml @@ -0,0 +1,2 @@ +src: +dist: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2180889 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module git.trj.tw/golang/go-watch-webp + +go 1.12 + +require ( + git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 + golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect + golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec + golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect + golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f // indirect + golang.org/x/text v0.3.2 // indirect + golang.org/x/tools v0.0.0-20190515035509-2196cb7019cc // indirect + gopkg.in/fsnotify.v1 v1.4.7 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0c0595b --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 h1:V6ifeiJ3ExnjaUylTOz37n6z5uLwm6fjKjnztbTCaQI= +git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec h1:arXJwtMuk5vqI1NHX0UTnNw977rYk5Sl4jQqHj+hun4= +golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f h1:Xab8gg26GrI/x3RNdVhVkHHM1XLyGeRBEvz4Q5x4YW8= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190515035509-2196cb7019cc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..fe09903 --- /dev/null +++ b/main.go @@ -0,0 +1,159 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "log" + "os" + "os/signal" + "path/filepath" + "time" + + "git.trj.tw/golang/go-watch-webp/module/config" + "git.trj.tw/golang/go-watch-webp/module/image" + "git.trj.tw/golang/go-watch-webp/module/option" + "git.trj.tw/golang/utils" + "gopkg.in/fsnotify.v1" +) + +func init() { + option.RegOptions() +} + +func main() { + fmt.Println("watch dir webp image") + var err error + lock := make(chan os.Signal, 1) + signal.Notify(lock, os.Interrupt) + + opts := option.GetOptions() + + if opts.Help { + flag.Usage() + return + } + + err = config.LoadConfig(opts.Config) + if err != nil { + log.Fatal(err) + } + + conf := config.GetConfig() + + if exists := checkDirectory(conf.Src); !exists { + log.Fatal(errors.New("source folder not exists")) + } + if exists := checkDirectory(conf.Dist); !exists { + log.Fatal(errors.New("target folder not exists")) + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + + // watch fs notify + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Println("fs event :: ", event) + if event.Op&fsnotify.Create == fsnotify.Create { + log.Println("create file :: ", event.Name) + go procFile(event.Name) + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error: ", err) + } + } + }() + + err = watcher.Add(conf.Src) + if err != nil { + log.Fatal(err) + } + + <-lock +} + +func procFile(loc string) { + // only proc webp file + if ext := filepath.Ext(loc); ext != ".webp" { + return + } + go func(loc string) { + fin := waitFile(loc) + if fin { + log.Println("file write ok") + file, err := os.Open(loc) + if err != nil { + log.Println("err :: ", err) + } + defer file.Close() + err = image.ConvertToPng(file) + if err != nil { + log.Println("err :: ", err) + } + } + }(loc) +} + +func waitFile(loc string) bool { + if len(loc) == 0 { + return false + } + + for { + // check file modify time + t1, err := getFileModTime(loc) + if err != nil { + return false + } + time.Sleep(100 * time.Millisecond) + t2, err := getFileModTime(loc) + if err != nil { + return false + } + + if t1 == t2 { + break + } + } + return true +} + +func getFileModTime(loc string) (int64, error) { + file, err := os.Open(loc) + if err != nil { + return -1, err + } + defer file.Close() + info, err := file.Stat() + if err != nil { + return -1, err + } + + return info.ModTime().UnixNano(), nil +} + +func checkDirectory(dir string) bool { + if len(dir) == 0 { + return false + } + + dir = utils.ParsePath(dir) + if exists := utils.CheckExists(dir, true); !exists { + return false + } + if isDir := utils.IsDir(dir); !isDir { + return false + } + return true +} diff --git a/module/config/config.go b/module/config/config.go new file mode 100644 index 0000000..f61d56f --- /dev/null +++ b/module/config/config.go @@ -0,0 +1,56 @@ +package config + +import ( + "errors" + "io/ioutil" + "os" + "path" + + "git.trj.tw/golang/utils" + "gopkg.in/yaml.v2" +) + +// Config - +type Config struct { + Src string `yaml:"src"` + Dist string `yaml:"dist"` +} + +var conf *Config + +// LoadConfig - +func LoadConfig(p ...string) error { + var fp string + + if len(p) > 0 && len(p[0]) > 0 { + fp = p[0] + } else { + wd, err := os.Getwd() + if err != nil { + return err + } + fp = path.Join(wd, "config.yml") + } + + fp = utils.ParsePath(fp) + if exists := utils.CheckExists(fp, false); !exists { + return errors.New("config not found") + } + + dataByte, err := ioutil.ReadFile(fp) + if err != nil { + return err + } + + conf = &Config{} + + err = yaml.Unmarshal(dataByte, conf) + if err != nil { + return err + } + + return nil +} + +// GetConfig - +func GetConfig() *Config { return conf } diff --git a/module/image/image.go b/module/image/image.go new file mode 100644 index 0000000..4a880a4 --- /dev/null +++ b/module/image/image.go @@ -0,0 +1,40 @@ +package image + +import ( + "fmt" + "io" + "log" + "os" + "path" + "time" + + "image/png" + + "git.trj.tw/golang/go-watch-webp/module/config" + "golang.org/x/image/webp" +) + +// ConvertToPng - +func ConvertToPng(file io.Reader) error { + log.Println("convert func ") + conf := config.GetConfig() + + webpImg, err := webp.Decode(file) + if err != nil { + return err + } + + filename := fmt.Sprintf("%d.png", time.Now().UnixNano()) + fp := path.Join(conf.Dist, filename) + + target, err := os.Create(fp) + if err != nil { + return err + } + + if err := png.Encode(target, webpImg); err != nil { + return err + } + + return nil +} diff --git a/module/option/option.go b/module/option/option.go new file mode 100644 index 0000000..022246c --- /dev/null +++ b/module/option/option.go @@ -0,0 +1,23 @@ +package option + +import "flag" + +// Options - +type Options struct { + Help bool + Config string +} + +var opts *Options + +// RegOptions - +func RegOptions() { + opts = &Options{} + flag.StringVar(&opts.Config, "config", "", "config file path - default: `pwd/config.yml`") + flag.StringVar(&opts.Config, "f", "", "config file path - default: `pwd/config.yml`") + flag.BoolVar(&opts.Help, "help", false, "show help") + flag.Parse() +} + +// GetOptions - +func GetOptions() *Options { return opts } diff --git a/module/pool/pool.go b/module/pool/pool.go new file mode 100644 index 0000000..d906bc8 --- /dev/null +++ b/module/pool/pool.go @@ -0,0 +1,40 @@ +package pool + +import "sync" + +// Pool - +type Pool struct { + q chan bool + wg *sync.WaitGroup +} + +// NewPool - +func NewPool(size int) *Pool { + if size < 1 { + size = 1 + } + + p := &Pool{ + q: make(chan bool, size), + wg: &sync.WaitGroup{}, + } + + return p +} + +// Add new queue work +func (p *Pool) Add() { + p.q <- true + p.wg.Add(1) +} + +// Done finish work +func (p *Pool) Done() { + <-p.q + p.wg.Done() +} + +// Wait - +func (p *Pool) Wait() { + p.wg.Wait() +}