170 lines
4.3 KiB
Go
170 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"itrans/codec"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
var codecs = map[string]codec.Coder{
|
|
".jpg": codec.JPGCoder{},
|
|
".jpeg": codec.JPGCoder{},
|
|
".png": codec.PNGCoder{},
|
|
".webp": codec.WebpCoder{},
|
|
}
|
|
|
|
var Error *log.Logger
|
|
var Warning *log.Logger
|
|
|
|
func init() {
|
|
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
|
|
logger := io.MultiWriter(os.Stdout)
|
|
log.SetOutput(logger)
|
|
|
|
Error = log.New(io.MultiWriter(os.Stderr, os.Stdout),
|
|
fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"),
|
|
log.Lmicroseconds|log.Lshortfile)
|
|
Warning = log.New(io.MultiWriter(os.Stdout),
|
|
fmt.Sprintf("%sWarning:%s ", "\033[0;93m", "\033[0m"),
|
|
log.Lmicroseconds|log.Lshortfile)
|
|
}
|
|
|
|
func main() {
|
|
quality := flag.Int("quality", 100, "Quality of the image")
|
|
to := flag.String("to", ".jpg", "Extension to transcode to")
|
|
nosafe := flag.Bool("nosafe", false, "Prevents backup of the original before encoding")
|
|
rm := flag.Bool("rm", false, "Removes the original after transcoding")
|
|
w := flag.Int("w", 8, "Workers")
|
|
flag.Parse()
|
|
|
|
workers := make(chan struct{}, *w)
|
|
|
|
if _, ok := codecs[*to]; !ok {
|
|
Error.Println("Invalid extension")
|
|
return
|
|
}
|
|
|
|
log.Printf("Using quality: %d", *quality)
|
|
log.Printf("Transcoding to: %s", *to)
|
|
log.Printf("Nosafe mode: %v", *nosafe)
|
|
log.Printf("Remove mode: %v", *rm)
|
|
|
|
var wg sync.WaitGroup
|
|
for _, file := range flag.Args() {
|
|
wg.Add(1)
|
|
workers <- struct{}{}
|
|
go func(file string) {
|
|
defer func() { <-workers }()
|
|
defer wg.Done()
|
|
file = filepath.ToSlash(file)
|
|
file = filepath.Clean(file)
|
|
ext := filepath.Ext(file)
|
|
coder, ok := codecs[ext]
|
|
if !ok {
|
|
Error.Printf("No encoder found for file: %s", file)
|
|
return
|
|
}
|
|
|
|
filedir := filepath.Dir(file)
|
|
|
|
filename := filepath.Base(file)
|
|
if !*nosafe {
|
|
bckp := filename + ".bak"
|
|
bckp = filepath.Join(filedir, bckp)
|
|
err := os.Link(file, bckp)
|
|
if err != nil {
|
|
Error.Printf("Failed to backup file: %v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
filehandle, err := os.Open(file)
|
|
if err != nil {
|
|
Error.Printf("Failed to open file: %v", err)
|
|
return
|
|
}
|
|
stat, err := filehandle.Stat()
|
|
if err != nil {
|
|
Error.Printf("Failed to get file stats: %v", err)
|
|
return
|
|
}
|
|
originalSize := stat.Size()
|
|
|
|
log.Printf("Decoding %s", file)
|
|
image, err := coder.Decode(filehandle)
|
|
if err != nil {
|
|
Error.Printf("Failed to decode file: %v", err)
|
|
return
|
|
}
|
|
filehandle.Close()
|
|
|
|
newfile := strings.Replace(filename, ext, *to, 1)
|
|
newfile = filepath.Join(filedir, newfile)
|
|
log.Printf("Encoding %s to %s", file, newfile)
|
|
|
|
buf, err := coder.Encode(image, *quality)
|
|
if err != nil {
|
|
Error.Printf("Failed to encode image: %v", err)
|
|
return
|
|
}
|
|
|
|
filehandle, err = os.Create(newfile)
|
|
if err != nil {
|
|
Error.Printf("Failed to create file %s: %v", newfile, err)
|
|
return
|
|
}
|
|
|
|
n, err := filehandle.Write(buf.Bytes())
|
|
if n != len(buf.Bytes()) || err != nil {
|
|
Error.Printf("Failed to write file %s: %v", newfile, err)
|
|
return
|
|
}
|
|
log.Printf("Wrote %dKB; Old size: %dKB; Compression: %.2f", n/1024, originalSize/1024, float32(n)/float32(originalSize))
|
|
|
|
if *rm {
|
|
if file == newfile {
|
|
log.Printf("Won't remove newfile %s", file)
|
|
return
|
|
}
|
|
log.Printf("Removing %s", file)
|
|
err = os.Remove(file)
|
|
if err != nil {
|
|
Error.Printf("Failed to remove file %v: %v", file, err)
|
|
return
|
|
}
|
|
}
|
|
}(file)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func instrument() {
|
|
numGoroutines := runtime.NumGoroutine()
|
|
|
|
var m runtime.MemStats
|
|
runtime.ReadMemStats(&m)
|
|
// log.Printf("%+v", m)
|
|
|
|
sys := float64(m.Sys)
|
|
ramUsedMB := sys / 1024 / 1024
|
|
kbPerGoroutine := sys / 1024 / float64(numGoroutines)
|
|
|
|
var numGoroutinesPretty string
|
|
switch {
|
|
case numGoroutines >= 1_000_000:
|
|
numGoroutinesPretty = fmt.Sprintf("%.2fM", float64(numGoroutines)/1_000_000)
|
|
case numGoroutines >= 1_000:
|
|
numGoroutinesPretty = fmt.Sprintf("%.2fk", float64(numGoroutines)/1_000)
|
|
default:
|
|
numGoroutinesPretty = fmt.Sprintf("%d", numGoroutines)
|
|
}
|
|
|
|
fmt.Printf("\rNumber of active goroutines: %d (%s); RAM used: %.2f MB; KB per goroutine: %.2f", numGoroutines, numGoroutinesPretty, ramUsedMB, kbPerGoroutine)
|
|
} |