mirror of
https://github.com/axllent/goptimize.git
synced 2025-11-02 19:07:17 -05:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9bf16d6c4 | ||
|
|
6c1aabaa70 | ||
|
|
8c3e478384 | ||
|
|
7ffb944b0f | ||
|
|
cb4bde8c4e | ||
|
|
49d7b9b12f | ||
|
|
fcdbf3e90b | ||
|
|
5096e6fbf0 | ||
|
|
d7d827fed7 | ||
|
|
19cf54b633 | ||
|
|
57b1d33129 | ||
|
|
c239a54ead | ||
|
|
e6296f4719 | ||
|
|
ee6aa69087 | ||
|
|
8362617920 | ||
|
|
b157fcd72b | ||
|
|
0c000fe262 | ||
|
|
97fa6c94c5 | ||
|
|
36e9b4c8fa | ||
|
|
5df486c53e | ||
|
|
120e5fefde | ||
|
|
d2a591a6d9 | ||
|
|
8e5e7b6098 | ||
|
|
47024e030b | ||
|
|
45915d4b42 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,2 @@
|
|||||||
/dist/
|
/dist/
|
||||||
goptimize
|
goptimize*
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [dev]
|
||||||
|
|
||||||
|
- Switch to go mods - go (>= 1.11 required)
|
||||||
|
- Switch to axllent/semver for app updating
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.3]
|
||||||
|
|
||||||
|
- Detect & skip animated GIFs
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.2]
|
||||||
|
|
||||||
|
- Switch to [pflag](https://github.com/spf13/pflag) for better flag management
|
||||||
|
|
||||||
|
|
||||||
## [0.0.1]
|
## [0.0.1]
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|||||||
3
Makefile
3
Makefile
@@ -5,8 +5,7 @@ LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}"
|
|||||||
build = echo "\n\nBuilding $(1)-$(2)" && GOOS=$(1) GOARCH=$(2) go build ${LDFLAGS} -o dist/goptimize_${VERSION}_$(1)_$(2) \
|
build = echo "\n\nBuilding $(1)-$(2)" && GOOS=$(1) GOARCH=$(2) go build ${LDFLAGS} -o dist/goptimize_${VERSION}_$(1)_$(2) \
|
||||||
&& bzip2 dist/goptimize_${VERSION}_$(1)_$(2)
|
&& bzip2 dist/goptimize_${VERSION}_$(1)_$(2)
|
||||||
|
|
||||||
goptimize: *.go
|
goptimize: *.go go.*
|
||||||
go get github.com/disintegration/imaging
|
|
||||||
go build ${LDFLAGS} -o goptimize
|
go build ${LDFLAGS} -o goptimize
|
||||||
rm -rf /tmp/go-*
|
rm -rf /tmp/go-*
|
||||||
|
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -1,11 +1,12 @@
|
|||||||
# Goptimizer - downscales and optimizes images
|
# Goptimizer - downscales and optimizes images
|
||||||
|
|
||||||
Goptimizer is a commandline utility written in Golang. It downscales and optimize existing images JPEG, PNG and Gif files.
|
[](https://goreportcard.com/report/github.com/axllent/goptimize)
|
||||||
|
|
||||||
Image downscaling is done within Goptimize (`-m <width>x<height>`, see [Usage](#usage-options)), however optimization is done using the following additional tools (if they are installed):
|
Goptimizer is a commandline utility written in Golang. It downscales and optimizes JPEG, PNG, GIF, TIFF and BMP files.
|
||||||
|
|
||||||
- jpegoptim
|
Image downscaling/rotation is done within goptimize (`-m <width>x<height>`, see [Usage](#usage-options)), however optimization is done using the following additional tools (if they are installed):
|
||||||
- jpegtran (`libjpeg-turbo-progs`)
|
|
||||||
|
- jpegtran (`libjpeg-turbo-progs`) or jpegoptim
|
||||||
- optipng
|
- optipng
|
||||||
- pngquant
|
- pngquant
|
||||||
- gifsicle
|
- gifsicle
|
||||||
@@ -13,14 +14,16 @@ Image downscaling is done within Goptimize (`-m <width>x<height>`, see [Usage](#
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
Both `jpegoptim` & `jpegtran` have almost identical optimization, so if both are installed then just `jpegtran` is used for JPG optimization. PNG optimization however will run through both `optipng` & `pngquant` (if installed) as this has definite advantages.
|
Both `jpegoptim` & `jpegtran` have almost identical optimization, so if both are installed then just `jpegtran` is used for JPG optimization. PNG optimization however will run through both `optipng` & `pngquant` (if installed) as this can result in better optimization.
|
||||||
|
|
||||||
It is highly recommended to install the necessary optimization tools, however they are not required to run goptimize.
|
It is highly recommended to install the necessary optimization tools, however they are not required to run goptimize.
|
||||||
|
|
||||||
Goptimize will remove all exif data from JPEG files, auto-rotating those that relied on it.
|
Goptimize will remove all exif data from JPEG files, auto-rotating those that depend on it for orientation.
|
||||||
|
|
||||||
It will also preserve (by default) the file's original modification times (`-p=false` to disable).
|
It will also preserve (by default) the file's original modification times (`-p=false` to disable).
|
||||||
|
|
||||||
|
Animated GIF files are not supported and automatically get skipped.
|
||||||
|
|
||||||
|
|
||||||
## Usage options
|
## Usage options
|
||||||
|
|
||||||
@@ -28,23 +31,18 @@ It will also preserve (by default) the file's original modification times (`-p=f
|
|||||||
Usage: ./goptimize [options] <images>
|
Usage: ./goptimize [options] <images>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-gifsicle string
|
-q, --quality int quality, JPEG only (default 75)
|
||||||
gifsicle binary (default "gifsicle")
|
-m, --max string downscale to a maximum width & height in pixels (<width>x<height>)
|
||||||
-jpegoptim string
|
-o, --out string output directory (default overwrites original)
|
||||||
jpegoptim binary (default "jpegoptim")
|
-p, --preserve preserve file modification times (default true)
|
||||||
-jpegtran string
|
-u, --update update to latest release
|
||||||
jpegtran binary (default "jpegtran")
|
-v, --version show version number
|
||||||
-m string
|
-h, --help show help
|
||||||
downscale to a maximum width & height in pixels (<width>x<height>)
|
--jpegtran string jpegtran binary (default "jpegtran")
|
||||||
-o string
|
--jpegoptim string jpegoptim binary (default "jpegoptim")
|
||||||
output directory (default overwrites original)
|
--gifsicle string gifsicle binary (default "gifsicle")
|
||||||
-optipng string
|
--pngquant string pngquant binary (default "pngquant")
|
||||||
optipng binary (default "optipng")
|
--optipng string optipng binary (default "optipng")
|
||||||
-p preserve file modification times (default true)
|
|
||||||
-pngquant string
|
|
||||||
pngquant binary (default "pngquant")
|
|
||||||
-q int
|
|
||||||
quality - JPEG only (default 75)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -54,3 +52,24 @@ Options:
|
|||||||
- `./goptimize -m 800x800 *` - optimize and downscale all image files to a maximum size of 800x800px
|
- `./goptimize -m 800x800 *` - optimize and downscale all image files to a maximum size of 800x800px
|
||||||
- `./goptimize -m 1200x0 image.jpg` - optimize and downscale a JPG file to a maximum size of width of 1200px
|
- `./goptimize -m 1200x0 image.jpg` - optimize and downscale a JPG file to a maximum size of width of 1200px
|
||||||
- `./goptimize -o out/ image.jpg` - optimize a JPG file and save it to `out/`
|
- `./goptimize -o out/ image.jpg` - optimize a JPG file and save it to `out/`
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Download the appropriate binary from the [releases](https://github.com/axllent/goptimize/releases/latest), or if you have golang installed
|
||||||
|
|
||||||
|
|
||||||
|
### Build requirements
|
||||||
|
|
||||||
|
Go >= 1.11 required.
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/axllent/goptimize
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
Some ideas for the future:
|
||||||
|
|
||||||
|
- Dry run
|
||||||
|
- Option to copy exif data (how?)
|
||||||
|
|||||||
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module github.com/axllent/goptimize
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/axllent/ghru v1.1.3
|
||||||
|
github.com/disintegration/imaging v1.6.1
|
||||||
|
github.com/spf13/pflag v1.0.5
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
|
||||||
|
)
|
||||||
12
go.sum
Normal file
12
go.sum
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
github.com/axllent/ghru v1.1.3 h1:n0jYsuqCYaHHAR6DraXZl8hpBY4j0XV47y5Lyym/jGo=
|
||||||
|
github.com/axllent/ghru v1.1.3/go.mod h1:rFvMhcO1UAv2Cv6bXscS8EOc7qqNpfe8ZLp23utzs88=
|
||||||
|
github.com/axllent/semver v0.0.0-20191103011746-394cefa91ee9 h1:LHNcCfePzgC/agAJs5a/5K3hFo8uW04bEdDKDkoX4do=
|
||||||
|
github.com/axllent/semver v0.0.0-20191103011746-394cefa91ee9/go.mod h1:2xSPzvG8n9mRfdtxSvWvfTfQGWfHsMsHO1iZnKATMSc=
|
||||||
|
github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE=
|
||||||
|
github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
76
goptimize.go
76
goptimize.go
@@ -22,31 +22,38 @@ func Goptimize(file string) {
|
|||||||
info, err := os.Stat(file)
|
info, err := os.Stat(file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%s doesn't exist\n", file)
|
fmt.Printf("Error: %s doesn't exist\n", file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !info.Mode().IsRegular() {
|
if !info.Mode().IsRegular() {
|
||||||
// not a file
|
fmt.Printf("Error: %s is not a file\n", file)
|
||||||
fmt.Printf("%s is not a file\n", file)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// open original, rotate if neccesary
|
// open original, rotate if necessary
|
||||||
src, err := imaging.Open(file, imaging.AutoOrientation(true))
|
src, err := imaging.Open(file, imaging.AutoOrientation(true))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%v (%s)\n", err, file)
|
fmt.Printf("Error: %v (%s)\n", err, file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := imaging.FormatFromFilename(file)
|
format, err := imaging.FormatFromFilename(file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Cannot detect format: %v\n", err)
|
fmt.Printf("Error: cannot detect format: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if format.String() == "GIF" {
|
||||||
|
// return if GIF is animated - unsupported
|
||||||
|
if err := IsGIFAnimated(file); err != nil {
|
||||||
|
fmt.Printf("Error: animated GIF not supported (%v)\n", file)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
outFilename := filepath.Base(file)
|
outFilename := filepath.Base(file)
|
||||||
outDir := filepath.Dir(file)
|
outDir := filepath.Dir(file)
|
||||||
dstFile := filepath.Join(outDir, outFilename)
|
dstFile := filepath.Join(outDir, outFilename)
|
||||||
@@ -60,7 +67,7 @@ func Goptimize(file string) {
|
|||||||
srcW := srcBounds.Dx()
|
srcW := srcBounds.Dx()
|
||||||
srcH := srcBounds.Dy()
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
// Ensure scaling does not upscale image
|
// do not upscale image
|
||||||
imgMaxW := maxWidth
|
imgMaxW := maxWidth
|
||||||
if imgMaxW == 0 || imgMaxW > srcW {
|
if imgMaxW == 0 || imgMaxW > srcW {
|
||||||
imgMaxW = srcW
|
imgMaxW = srcW
|
||||||
@@ -79,24 +86,25 @@ func Goptimize(file string) {
|
|||||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "Goptimized-")
|
tmpFile, err := ioutil.TempFile(os.TempDir(), "Goptimized-")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Cannot create temporary file: %v\n", err)
|
fmt.Printf("Error: cannot create temporary file: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer os.Remove(tmpFile.Name())
|
defer os.Remove(tmpFile.Name())
|
||||||
|
|
||||||
if format.String() == "JPEG" {
|
switch imgType := format.String(); imgType {
|
||||||
|
case "JPEG":
|
||||||
err = jpeg.Encode(tmpFile, resized, &jpeg.Options{Quality: quality})
|
err = jpeg.Encode(tmpFile, resized, &jpeg.Options{Quality: quality})
|
||||||
} else if format.String() == "PNG" {
|
case "PNG":
|
||||||
err = png.Encode(tmpFile, resized)
|
err = png.Encode(tmpFile, resized)
|
||||||
} else if format.String() == "GIF" {
|
case "GIF":
|
||||||
err = gif.Encode(tmpFile, resized, nil)
|
err = gif.Encode(tmpFile, resized, nil)
|
||||||
} else if format.String() == "TIFF" {
|
case "TIFF":
|
||||||
err = tiff.Encode(tmpFile, resized, nil)
|
err = tiff.Encode(tmpFile, resized, nil)
|
||||||
} else if format.String() == "BMP" {
|
case "BMP":
|
||||||
err = bmp.Encode(tmpFile, resized)
|
err = bmp.Encode(tmpFile, resized)
|
||||||
} else {
|
default:
|
||||||
fmt.Printf("Unsupported file type %s\n", file)
|
fmt.Printf("Error: unsupported file type (%s)\n", file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,23 +120,23 @@ func Goptimize(file string) {
|
|||||||
// so we can modify it with system processes
|
// so we can modify it with system processes
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
|
|
||||||
// Run through optimizers
|
// run through optimizers
|
||||||
if format.String() == "JPEG" {
|
if format.String() == "JPEG" {
|
||||||
// run one or the other, running both has no advantage
|
// run one or the other, running both has no advantage
|
||||||
if jpegtran != "" {
|
if jpegtran != "" {
|
||||||
RunOptimiser(tmpFilename, true, jpegtran, "-optimize", "-outfile")
|
RunOptimizer(tmpFilename, true, jpegtran, "-optimize", "-outfile")
|
||||||
} else if jpegoptim != "" {
|
} else if jpegoptim != "" {
|
||||||
RunOptimiser(tmpFilename, false, jpegoptim, "-f", "-s", "-o")
|
RunOptimizer(tmpFilename, false, jpegoptim, "-f", "-s", "-o")
|
||||||
}
|
}
|
||||||
} else if format.String() == "PNG" {
|
} else if format.String() == "PNG" {
|
||||||
if pngquant != "" {
|
if pngquant != "" {
|
||||||
RunOptimiser(tmpFilename, true, pngquant, "-f", "--output")
|
RunOptimizer(tmpFilename, true, pngquant, "-f", "--output")
|
||||||
}
|
}
|
||||||
if optipng != "" {
|
if optipng != "" {
|
||||||
RunOptimiser(tmpFilename, true, optipng, "-out")
|
RunOptimizer(tmpFilename, true, optipng, "-out")
|
||||||
}
|
}
|
||||||
} else if format.String() == "GIF" && gifsicle != "" {
|
} else if format.String() == "GIF" && gifsicle != "" {
|
||||||
RunOptimiser(tmpFilename, true, gifsicle, "-o")
|
RunOptimizer(tmpFilename, true, gifsicle, "-o")
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-open modified temporary file
|
// re-open modified temporary file
|
||||||
@@ -166,7 +174,7 @@ func Goptimize(file string) {
|
|||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(out, tmpFile); err != nil {
|
if _, err := io.Copy(out, tmpFile); err != nil {
|
||||||
fmt.Printf("Error ovewriting original file: %v\n", err)
|
fmt.Printf("Error overwriting original file: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +187,7 @@ func Goptimize(file string) {
|
|||||||
|
|
||||||
fmt.Printf("Goptimized %s (%dx%d %s > %s %v%%)\n", dstFile, resultW, resultH, ByteCountSI(srcSize), ByteCountSI(dstSize), savedPercent)
|
fmt.Printf("Goptimized %s (%dx%d %s > %s %v%%)\n", dstFile, resultW, resultH, ByteCountSI(srcSize), ByteCountSI(dstSize), savedPercent)
|
||||||
} else {
|
} else {
|
||||||
// If the output directory is not the same,
|
// if the output directory is not the same,
|
||||||
// then write a copy of the original file
|
// then write a copy of the original file
|
||||||
if outputDir != "" {
|
if outputDir != "" {
|
||||||
out, err := os.Create(dstFile)
|
out, err := os.Create(dstFile)
|
||||||
@@ -215,9 +223,9 @@ func Goptimize(file string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunOptimiser will run the specified command on a copy of the temporary file,
|
// RunOptimizer will run the specified command on a copy of the temporary file,
|
||||||
// and overwrite it if the output is smaller than the original
|
// and overwrite it if the output is smaller than the original
|
||||||
func RunOptimiser(src string, outFileArg bool, args ...string) {
|
func RunOptimizer(src string, outFileArg bool, args ...string) {
|
||||||
// create a new temp file
|
// create a new temp file
|
||||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "Goptimized-")
|
tmpFile, err := ioutil.TempFile(os.TempDir(), "Goptimized-")
|
||||||
|
|
||||||
@@ -289,3 +297,21 @@ func ByteCountSI(b int64) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp])
|
return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsGIFAnimated will return an error if the GIF file has more than 1 frame
|
||||||
|
func IsGIFAnimated(gifFile string) error {
|
||||||
|
file, _ := os.Open(gifFile)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
g, err := gif.DecodeAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single frame = OK
|
||||||
|
if len(g.Image) == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Animated gif")
|
||||||
|
}
|
||||||
|
|||||||
48
main.go
48
main.go
@@ -1,14 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/axllent/gitrel"
|
"github.com/axllent/ghru"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -26,11 +26,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// set up new flag instance
|
||||||
|
flag := pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
|
||||||
|
|
||||||
// set the default help
|
// set the default help
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Println("Goptimize - downscales and optimizes images")
|
fmt.Println("Goptimize - downscales and optimizes images")
|
||||||
fmt.Printf("\nUsage: %s [options] <images>\n", os.Args[0])
|
fmt.Printf("\nUsage: %s [options] <images>\n", os.Args[0])
|
||||||
fmt.Println("\nOptions:")
|
fmt.Println("\nOptions:")
|
||||||
|
flag.SortFlags = false
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
fmt.Println("\nExamples:")
|
fmt.Println("\nExamples:")
|
||||||
fmt.Printf(" %s image.png\n", os.Args[0])
|
fmt.Printf(" %s image.png\n", os.Args[0])
|
||||||
@@ -47,24 +51,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var maxSizes string
|
var maxSizes string
|
||||||
var update, showversion bool
|
var update, showversion, showhelp bool
|
||||||
|
|
||||||
flag.IntVar(&quality, "q", 75, "quality - JPEG only")
|
flag.IntVarP(&quality, "quality", "q", 75, "quality, JPEG only")
|
||||||
flag.StringVar(&outputDir, "o", "", "output directory (default overwrites original)")
|
flag.StringVarP(&maxSizes, "max", "m", "", "downscale to a maximum width & height in pixels (<width>x<height>)")
|
||||||
flag.BoolVar(&preserveModTimes, "p", true, "preserve file modification times")
|
flag.StringVarP(&outputDir, "out", "o", "", "output directory (default overwrites original)")
|
||||||
flag.StringVar(&maxSizes, "m", "", "downscale to a maximum width & height in pixels (<width>x<height>)")
|
flag.BoolVarP(&preserveModTimes, "preserve", "p", true, "preserve file modification times")
|
||||||
flag.BoolVar(&update, "u", false, "update to latest release")
|
flag.BoolVarP(&update, "update", "u", false, "update to latest release")
|
||||||
flag.BoolVar(&showversion, "v", false, "show version number")
|
flag.BoolVarP(&showversion, "version", "v", false, "show version number")
|
||||||
|
flag.BoolVarP(&showhelp, "help", "h", false, "show help")
|
||||||
|
|
||||||
// third-party optimizers
|
// third-party optimizers
|
||||||
flag.StringVar(&gifsicle, "gifsicle", "gifsicle", "gifsicle binary")
|
|
||||||
flag.StringVar(&jpegoptim, "jpegoptim", "jpegoptim", "jpegoptim binary")
|
|
||||||
flag.StringVar(&jpegtran, "jpegtran", "jpegtran", "jpegtran binary")
|
flag.StringVar(&jpegtran, "jpegtran", "jpegtran", "jpegtran binary")
|
||||||
flag.StringVar(&optipng, "optipng", "optipng", "optipng binary")
|
flag.StringVar(&jpegoptim, "jpegoptim", "jpegoptim", "jpegoptim binary")
|
||||||
|
flag.StringVar(&gifsicle, "gifsicle", "gifsicle", "gifsicle binary")
|
||||||
flag.StringVar(&pngquant, "pngquant", "pngquant", "pngquant binary")
|
flag.StringVar(&pngquant, "pngquant", "pngquant", "pngquant binary")
|
||||||
|
flag.StringVar(&optipng, "optipng", "optipng", "optipng binary")
|
||||||
|
|
||||||
// parse flags
|
flag.SortFlags = false
|
||||||
flag.Parse()
|
|
||||||
|
// parse args excluding os.Args[0]
|
||||||
|
flag.Parse(os.Args[1:])
|
||||||
|
|
||||||
// detect optimizer paths
|
// detect optimizer paths
|
||||||
gifsicle, _ = exec.LookPath(gifsicle)
|
gifsicle, _ = exec.LookPath(gifsicle)
|
||||||
@@ -73,22 +80,27 @@ func main() {
|
|||||||
optipng, _ = exec.LookPath(optipng)
|
optipng, _ = exec.LookPath(optipng)
|
||||||
pngquant, _ = exec.LookPath(pngquant)
|
pngquant, _ = exec.LookPath(pngquant)
|
||||||
|
|
||||||
|
if showhelp {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
if showversion {
|
if showversion {
|
||||||
fmt.Println(fmt.Sprintf("Version: %s", version))
|
fmt.Println(fmt.Sprintf("Version: %s", version))
|
||||||
latest, _, _, err := gitrel.Latest("axllent/goptimize", "goptimize")
|
latest, _, _, err := ghru.Latest("axllent/goptimize", "goptimize")
|
||||||
if err == nil && latest != version {
|
if err == nil && ghru.GreaterThan(latest, version) {
|
||||||
fmt.Printf("Update available: %s\nRun `%s -u` to update.\n", latest, os.Args[0])
|
fmt.Printf("Update available: %s\nRun `%s -u` to update.\n", latest, os.Args[0])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if update {
|
if update {
|
||||||
rel, err := gitrel.Update("axllent/goptimize", "goptimize", version)
|
rel, err := ghru.Update("axllent/goptimize", "goptimize", version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Printf("Updated %s to version %s", os.Args[0], rel)
|
fmt.Printf("Updated %s to version %s\n", os.Args[0], rel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user