@@ -3,13 +3,11 @@ package main
33import (
44 "bytes"
55 "compressor/common"
6- "encoding/json"
76 "flag"
87 "fmt"
98 "image"
109 "image/jpeg"
1110 _ "image/png"
12- "io"
1311 "io/ioutil"
1412 "log"
1513 "os"
@@ -21,34 +19,26 @@ import (
2119 "time"
2220)
2321
24- type Config struct {
25- ThreadCount int `json:"thread_count"`
26- InputFormat []string `json:"input_format"`
27- InputPath string `json:"input_path"`
28- OutputPath string `json:"output_path"`
29- Quality int `json:"quality"`
30- }
31-
3222type node struct {
3323 Input string
3424 Output string
3525}
3626
37- const OutputFormat = ".jpg"
27+ var (
28+ id string // use unix timestamp as id
29+ logger * log.Logger // global logger
30+ config * Config // from json
31+ failList []node // gather all failed jobs for summary
32+ )
3833
34+ // runtime variable
3935var (
40- id string // use unix timestamp as id
41- logger * log.Logger
42- config Config // from json
43- total , count int32 // the number of images
44- acceptFormat map [string ]bool
45- jpegQuality * jpeg.Options
46- failList []node // gather all failed jobs for summary
47- nodeCh chan node
48- failCh chan node
49- wg = sync.WaitGroup {}
50- dirMutex = sync.Mutex {}
51- travelDone = false
36+ total , count int32 // the number of images
37+ wg * sync.WaitGroup // thread limit
38+ dirMutex * sync.Mutex // dir lock for creating file
39+ travelDone bool // if travel is finished
40+ nodeCh chan node // task channel
41+ failCh chan node // task channel
5242)
5343
5444func init () {
@@ -57,77 +47,18 @@ func init() {
5747 configPathPtr := flag .String ("c" , "config.json" , "Configuration Filepath" )
5848 flag .Parse ()
5949
60- // init process id
50+ // initialize process id
6151 id = strconv .FormatInt (time .Now ().Unix (), 10 )
6252
63- // create log file and init logger
64- logFile , err := os .OpenFile (id + ".log" , os .O_WRONLY | os .O_CREATE , 0644 )
65- if err != nil {
66- logger = log .New (os .Stdout , "" , log .LstdFlags )
67- logger .Println (common .Red ("Cannot Create Log File" ))
68- } else {
69- logger = log .New (io .MultiWriter (os .Stdout , logFile ), "" , log .LstdFlags )
70- }
53+ // initialize logger
54+ logger = common .GetLogger (id )
7155
72- // load json
73- configFile , err := ioutil .ReadFile (* configPathPtr )
74- if err != nil {
75- logger .Println (common .Red ("Config File Load Failed" ))
76- os .Exit (1 )
77- }
56+ // parse config file
57+ config = ParseConfig (configPathPtr )
7858
79- // parse json
80- if err := json .Unmarshal (configFile , & config ); err != nil {
81- logger .Println (common .Red ("Json Unmarshal Failed" ))
82- os .Exit (1 )
83- }
84-
85- // check quality
86- if config .Quality <= 0 || config .Quality > 100 {
87- logger .Println (common .Red ("Quality Value Should Between 1 and 100" ))
88- os .Exit (1 )
89- }
90- jpegQuality = & jpeg.Options {Quality : config .Quality }
91-
92- // check input path
93- if inputInfo , err := os .Stat (config .InputPath ); err != nil {
94- if os .IsNotExist (err ) {
95- logger .Println (common .Red ("Input Path Not Found" ))
96- os .Exit (1 )
97- }
98- if ! inputInfo .IsDir () {
99- logger .Println (common .Red ("Input Path Should Be a Directory" ))
100- os .Exit (1 )
101- }
102- }
103-
104- // check output path
105- if config .OutputPath != "" {
106- if config .InputPath == config .OutputPath {
107- logger .Println (common .Red ("Output Path Cannot Be Same As Input Path" ))
108- os .Exit (1 )
109- }
110- if _ , err := os .Stat (config .OutputPath ); err != nil {
111- if os .IsNotExist (err ) {
112- if e := os .MkdirAll (config .OutputPath , 0755 ); e != nil {
113- logger .Println (common .Red ("Create Output Path Failed" ))
114- os .Exit (1 )
115- }
116- }
117- }
118- } else {
119- config .OutputPath = config .InputPath + "_" + id
120- if e := os .Mkdir (config .OutputPath , 0755 ); e != nil {
121- logger .Println (common .Red ("Create Output Path Failed" ))
122- os .Exit (1 )
123- }
124- }
125-
126- // initialize accept input format
127- acceptFormat = make (map [string ]bool )
128- for _ , v := range config .InputFormat {
129- acceptFormat [fmt .Sprintf (".%s" , v )] = true
130- }
59+ travelDone = false
60+ dirMutex = & sync.Mutex {}
61+ wg = & sync.WaitGroup {}
13162
13263 // initialize channel
13364 nodeCh = make (chan node , 32768 )
@@ -146,8 +77,8 @@ func travel() {
14677 return e
14778 }
14879 if ! info .IsDir () {
149- if ext := strings .ToLower (filepath .Ext (info .Name ())); acceptFormat [ext ] {
150- newPath := filepath .Join (config .OutputPath , filepath . Base ( path ) )
80+ if ext := strings .ToLower (filepath .Ext (info .Name ())); config . acceptFormat [ext ] {
81+ newPath := filepath .Join (config .OutputPath , path [ len ( config . InputPath ):] )
15182 newPath = strings .TrimSuffix (newPath , filepath .Ext (newPath )) + OutputFormat
15283 if err := os .MkdirAll (filepath .Dir (newPath ), 0755 ); err != nil {
15384 logger .Println (common .Red ("Create New Path Failed" ))
@@ -166,28 +97,6 @@ func travel() {
16697 travelDone = true
16798}
16899
169- // touch file
170- func touch (filename * string ) bool {
171- dirMutex .Lock ()
172- defer dirMutex .Unlock ()
173- touched := false
174- for i := 0 ; i < 7 ; i ++ {
175- _ , err := os .Stat (* filename )
176- // if file exist
177- if err == nil {
178- * filename = fmt .Sprintf ("%s (%d)%s" , (* filename )[:len (* filename )- len (OutputFormat )], i , OutputFormat )
179- } else if os .IsNotExist (err ) {
180- // touch
181- _ , err := os .OpenFile (* filename , os .O_RDONLY | os .O_CREATE , 0644 )
182- if err == nil {
183- touched = true
184- }
185- break
186- }
187- }
188- return touched
189- }
190-
191100// compress job, multiple goroutine
192101func compress () {
193102 defer wg .Done ()
@@ -202,7 +111,7 @@ func compress() {
202111 continue
203112 }
204113
205- if ! touch (& n .Output ) {
114+ if ! common . Touch (& n .Output , dirMutex , MaxRenameRetry ) {
206115 failCh <- n
207116 continue
208117 }
@@ -214,7 +123,7 @@ func compress() {
214123 }
215124
216125 buf := new (bytes.Buffer )
217- err = jpeg .Encode (buf , img , jpegQuality )
126+ err = jpeg .Encode (buf , img , config . jpegQuality )
218127 if err != nil {
219128 failCh <- n
220129 continue
@@ -239,27 +148,27 @@ func compress() {
239148 } else {
240149 logger .Printf ("%s %s %s %s" ,
241150 common .Purple (fmt .Sprintf ("(%d/loading)" , v + 1 )),
242- n .Input , common .Green ("->" ), n .Output ) }
151+ n .Input , common .Green ("->" ), n .Output )
152+ }
243153 }
244154}
245155
246156func process () {
247157 defer close (failCh )
248158
249- logger .Println (common .Blue ("========= Pending =========" ))
250- // travel filepath
251- wg .Add (1 )
252- go travel ()
253-
254159 // transfer
255- // cuz failCh sender is about to close and buffer is limited
256160 // when process finished, failCh will be closed
257161 go func () {
258162 for n := range failCh {
259163 failList = append (failList , n )
260164 }
261165 }()
262166
167+ logger .Println (common .Blue ("========= Pending =========" ))
168+ // travel filepath
169+ wg .Add (1 )
170+ go travel ()
171+
263172 // multi-thread compress
264173 for i := 0 ; i < config .ThreadCount ; i ++ {
265174 wg .Add (1 )
@@ -289,7 +198,7 @@ func main() {
289198
290199func usage () {
291200 _ , _ = fmt .Fprintf (os .Stderr ,
292- `Version: 2.0
201+ `Version: 2.1
293202Usage: compressor [-h] [-c filename]
294203
295204Options:
0 commit comments