Passed
Branch master (f6e4d0)
by Abouzar
02:50
created
1
package main
2
3
import (
4
	"bufio"
5
	"flag"
6
	"io"
7
	"os"
8
	"os/signal"
9
	"path/filepath"
10
	"runtime"
11
	"syscall"
12
13
	"github.com/imkira/go-task"
14
)
15
16
var displayProgress = true
17
18
func main() {
19
	var err error
20
	var proxy, filepath, bwLimit string
21
22
	conn := flag.Int("n", runtime.NumCPU(), "connection")
23
	skiptls := flag.Bool("skip-tls", true, "skip verify certificate for https")
24
	flag.StringVar(&proxy, "proxy", "", "proxy for downloading, ex \n\t-proxy '127.0.0.1:12345' for socks5 proxy\n\t-proxy 'http://proxy.com:8080' for http proxy")
25
	flag.StringVar(&filepath, "file", "", "filepath that contains links in each line")
26
	flag.StringVar(&bwLimit, "rate", "", "bandwidth limit to use while downloading, ex\n\t -rate 10kB\n\t-rate 10MiB")
27
28
	flag.Parse()
29
	args := flag.Args()
30
	if len(args) < 1 {
31
		if len(filepath) > 1 {
32
			// Creating a SerialGroup.
33
			g1 := task.NewSerialGroup()
34
			file, err := os.Open(filepath)
35
			if err != nil {
36
				FatalCheck(err)
37
			}
38
39
			defer file.Close()
40
41
			reader := bufio.NewReader(file)
42
43
			for {
44
				line, _, err := reader.ReadLine()
45
46
				if err == io.EOF {
47
					break
48
				}
49
50
				g1.AddChild(downloadTask(string(line), nil, *conn, *skiptls, proxy, bwLimit))
51
			}
52
			g1.Run(nil)
53
			return
54
		} else {
0 ignored issues
show
if block ends with a return statement, so drop this else and outdent its block
Loading history...
55
			Errorln("url is required")
56
			usage()
57
			os.Exit(1)
58
		}
59
	}
60
61
	command := args[0]
62
	if command == "tasks" {
63
		if err = TaskPrint(); err != nil {
64
			Errorf("%v\n", err)
65
		}
66
		return
67
	} else if command == "resume" {
68
		if len(args) < 2 {
69
			Errorln("downloading task name is required")
70
			usage()
71
			os.Exit(1)
72
		}
73
74
		var task string
75
		if IsUrl(args[1]) {
76
			task = TaskFromUrl(args[1])
77
		} else {
78
			task = args[1]
79
		}
80
81
		state, err := Resume(task)
82
		FatalCheck(err)
83
		Execute(state.URL, state, *conn, *skiptls, proxy, bwLimit)
84
		return
85
	} else {
86
		if ExistDir(FolderOf(command)) {
87
			Warnf("Downloading task already exist, remove first \n")
88
			err := os.RemoveAll(FolderOf(command))
89
			FatalCheck(err)
90
		}
91
		Execute(command, nil, *conn, *skiptls, proxy, bwLimit)
92
	}
93
}
94
95
func downloadTask(url string, state *State, conn int, skiptls bool, proxy string, bwLimit string) task.Task {
96
	run := func(t task.Task, ctx task.Context) {
97
		Execute(url, state, conn, skiptls, proxy, bwLimit)
98
	}
99
	return task.NewTaskWithFunc(run)
100
}
101
102
func Execute(url string, state *State, conn int, skiptls bool, proxy string, bwLimit string) {
0 ignored issues
show
exported function Execute should have comment or be unexported
Loading history...
103
	//otherwise is hget <URL> command
104
105
	signal_chan := make(chan os.Signal, 1)
0 ignored issues
show
don't use underscores in Go names; var signal_chan should be signalChan
Loading history...
106
	signal.Notify(signal_chan,
107
		syscall.SIGHUP,
108
		syscall.SIGINT,
109
		syscall.SIGTERM,
110
		syscall.SIGQUIT)
111
112
	//set up parallel
113
114
	var files = make([]string, 0)
115
	var parts = make([]Part, 0)
116
	var isInterrupted = false
117
118
	doneChan := make(chan bool, conn)
119
	fileChan := make(chan string, conn)
120
	errorChan := make(chan error, 1)
121
	stateChan := make(chan Part, 1)
122
	interruptChan := make(chan bool, conn)
123
124
	var downloader *HTTPDownloader
125
	if state == nil {
126
		downloader = NewHTTPDownloader(url, conn, skiptls, proxy, bwLimit)
127
	} else {
128
		downloader = &HTTPDownloader{url: state.URL, file: filepath.Base(state.URL), par: int64(len(state.Parts)), parts: state.Parts, resumable: true}
129
	}
130
	go downloader.Do(doneChan, fileChan, errorChan, interruptChan, stateChan)
131
132
	for {
133
		select {
134
		case <-signal_chan:
135
			//send par number of interrupt for each routine
136
			isInterrupted = true
137
			for i := 0; i < conn; i++ {
138
				interruptChan <- true
139
			}
140
		case file := <-fileChan:
141
			files = append(files, file)
142
		case err := <-errorChan:
143
			Errorf("%v", err)
144
			panic(err) //maybe need better style
145
		case part := <-stateChan:
146
			parts = append(parts, part)
147
		case <-doneChan:
148
			if isInterrupted {
149
				if downloader.resumable {
150
					Printf("Interrupted, saving state ... \n")
151
					s := &State{URL: url, Parts: parts}
152
					if err := s.Save(); err != nil {
153
						Errorf("%v\n", err)
154
					}
155
				} else {
156
					Warnf("Interrupted, but downloading url is not resumable, silently die")
157
				}
158
			} else {
159
				err := JoinFile(files, filepath.Base(url))
160
				FatalCheck(err)
161
				err = os.RemoveAll(FolderOf(url))
162
				FatalCheck(err)
163
			}
164
			return
165
		}
166
	}
167
}
168
169
func usage() {
170
	Printf(`Usage:
171
hget [-n connection] [-skip-tls true] [-proxy proxy_address] [-file filename] URL
172
hget tasks
173
hget resume [TaskName]
174
`)
175
}
176