Skip to content

Commit de7f804

Browse files
committed
Merge branch 'kingwrcy-master'
2 parents c1c7cf0 + 286c77a commit de7f804

File tree

4 files changed

+94
-39
lines changed

4 files changed

+94
-39
lines changed

cmd/got/main.go

+19-12
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package main
33
import (
44
"flag"
55
"fmt"
6-
"log"
7-
86
"github.com/dustin/go-humanize"
97
"github.com/melbahja/got"
8+
"log"
9+
"time"
1010
)
1111

1212
var (
@@ -36,6 +36,7 @@ func main() {
3636
if url = flag.Arg(0); url == "" {
3737
log.Fatal("Empty download url.")
3838
}
39+
3940
if !(url[:7] == "http://" || url[:8] == "https://") {
4041
url = "https://" + url
4142
}
@@ -47,15 +48,6 @@ func main() {
4748
d := got.Download{
4849
URL: url,
4950
Dest: *dest,
50-
ProgressFunc: func(i int64, t int64, d *got.Download) {
51-
fmt.Printf(
52-
"\r\r\b Total Size: %s | Chunk Size: %s | Concurrency: %d | Progress: %s ",
53-
humanize.Bytes(uint64(t)),
54-
humanize.Bytes(uint64(d.ChunkSize)),
55-
d.Concurrency,
56-
humanize.Bytes(uint64(i)),
57-
)
58-
},
5951
ChunkSize: int64(*chunkSize),
6052
Interval: 100,
6153
Concurrency: *concurrency,
@@ -65,9 +57,24 @@ func main() {
6557
log.Fatal(err)
6658
}
6759

60+
// Set progress func to update cli output.
61+
d.Progress.ProgressFunc = func(p *got.Progress, d *got.Download) {
62+
63+
fmt.Printf(
64+
"\r\r\bTotal: %s | Chunk: %s | Concurrency: %d | Received: %s | Time: %s | Avg: %s/s | Speed: %s/s",
65+
humanize.Bytes(uint64(p.TotalSize)),
66+
humanize.Bytes(uint64(d.ChunkSize)),
67+
d.Concurrency,
68+
humanize.Bytes(uint64(p.Size)),
69+
p.TotalCost().Round(time.Second),
70+
humanize.Bytes(p.AvgSpeed()),
71+
humanize.Bytes(p.Speed()),
72+
)
73+
}
74+
6875
if err := d.Start(); err != nil {
6976
log.Fatal(err)
7077
}
7178

72-
fmt.Println("| Done!")
79+
fmt.Println(" | Done!")
7380
}

got.go

+33-17
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,12 @@ type (
3434
// Download file info.
3535
*Info
3636

37-
// Progress func.
38-
ProgressFunc
39-
4037
// URL to download.
4138
URL string
4239

4340
// File destination.
4441
Dest string
4542

46-
// Progress interval in ms.
47-
Interval int
48-
4943
// Split file into chunks by ChunkSize in bytes.
5044
ChunkSize int64
5145

@@ -58,6 +52,12 @@ type (
5852
// Max chunks to download at same time.
5953
Concurrency int
6054

55+
// Progress...
56+
Progress *Progress
57+
58+
// Progress interval in ms.
59+
Interval int
60+
6161
// Download file chunks.
6262
chunks []*Chunk
6363

@@ -66,9 +66,6 @@ type (
6666

6767
// Is the URL redirected to a different location.
6868
redirected bool
69-
70-
// Progress...
71-
progress *Progress
7269
}
7370
)
7471

@@ -95,9 +92,6 @@ func (d *Download) Init() error {
9592
},
9693
}
9794

98-
// Init progress.
99-
d.progress = &Progress{}
100-
10195
// Set default interval.
10296
if d.Interval == 0 {
10397
d.Interval = 20
@@ -108,6 +102,16 @@ func (d *Download) Init() error {
108102
return err
109103
}
110104

105+
// Set default progress.
106+
if d.Progress == nil {
107+
108+
d.Progress = &Progress{
109+
startedAt: time.Now(),
110+
Interval: d.Interval,
111+
TotalSize: d.Info.Length,
112+
}
113+
}
114+
111115
// Partial content not supported 😢!
112116
if d.Info.Rangeable == false || d.Info.Length == 0 {
113117
return nil
@@ -177,7 +181,7 @@ func (d *Download) Init() error {
177181
d.chunks = append(d.chunks, &Chunk{
178182
Start: startRange,
179183
End: endRange,
180-
Progress: d.progress,
184+
Progress: d.Progress,
181185
Done: make(chan struct{}),
182186
})
183187
}
@@ -187,6 +191,7 @@ func (d *Download) Init() error {
187191

188192
// Start downloading.
189193
func (d *Download) Start() (err error) {
194+
190195
// Create a new temp dir for this download.
191196
temp, err := ioutil.TempDir("", "GotChunks")
192197
if err != nil {
@@ -196,8 +201,9 @@ func (d *Download) Start() (err error) {
196201

197202
ctx, cancel := context.WithCancel(context.TODO())
198203
defer cancel()
204+
199205
// Run progress func.
200-
go d.progress.Run(ctx, d)
206+
go d.Progress.Run(ctx, d)
201207

202208
// Partial content not supported,
203209
// just download the file in one chunk.
@@ -212,7 +218,7 @@ func (d *Download) Start() (err error) {
212218
defer file.Close()
213219

214220
chunk := &Chunk{
215-
Progress: d.progress,
221+
Progress: d.Progress,
216222
}
217223

218224
return chunk.Download(d.URL, d.client, file)
@@ -235,9 +241,11 @@ func (d *Download) Start() (err error) {
235241
return err
236242
}
237243

238-
if d.ProgressFunc != nil {
239-
d.ProgressFunc(d.progress.Size, d.Info.Length, d)
244+
// Update progress output after chunks finished.
245+
if d.Progress.ProgressFunc != nil {
246+
d.Progress.ProgressFunc(d.Progress, d)
240247
}
248+
241249
return nil
242250
}
243251

@@ -283,6 +291,7 @@ func (d *Download) merge(ctx context.Context) error {
283291
defer file.Close()
284292

285293
for i := range d.chunks {
294+
286295
select {
287296
case <-d.chunks[i].Done:
288297
case <-ctx.Done():
@@ -309,17 +318,24 @@ func (d *Download) merge(ctx context.Context) error {
309318

310319
// Download chunks
311320
func (d *Download) dl(ctx context.Context, temp string) error {
321+
312322
eg, ctx := errgroup.WithContext(ctx)
323+
313324
// Concurrency limit.
314325
max := make(chan int, d.Concurrency)
315326

316327
for i := 0; i < len(d.chunks); i++ {
328+
317329
max <- 1
318330
i := i
331+
319332
eg.Go(func() error {
333+
320334
defer func() {
321335
<-max
322336
}()
337+
338+
// Create chunk in temp dir.
323339
chunk, err := os.Create(filepath.Join(temp, fmt.Sprintf("chunk-%d", i)))
324340

325341
if err != nil {

got_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ func downloadTest(t *testing.T, url string, size int64) {
199199
defer clean(tmpFile)
200200

201201
d := &got.Download{
202-
URL: url,
203-
Dest: tmpFile,
204-
Concurrency: 2,
202+
URL: url,
203+
Dest: tmpFile,
204+
Concurrency: 2,
205205
}
206206

207207
if err := d.Init(); err != nil {

progress.go

+39-7
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,62 @@ type (
1010

1111
// Progress can be used to show download progress to the user.
1212
Progress struct {
13-
Size int64
13+
ProgressFunc
14+
15+
Size, TotalSize int64
16+
Interval int
17+
18+
lastSize int64
19+
startedAt time.Time
1420
}
1521

1622
// ProgressFunc to show progress state, called based on Download interval.
17-
ProgressFunc func(size int64, total int64, d *Download)
23+
ProgressFunc func(p *Progress, d *Download)
1824
)
1925

2026
// Run runs ProgressFunc based on interval if ProgressFunc set.
2127
func (p *Progress) Run(ctx context.Context, d *Download) {
22-
if d.ProgressFunc != nil {
28+
29+
if p.ProgressFunc != nil {
30+
2331
for {
24-
select {
25-
case <-ctx.Done():
32+
// Context cancelled
33+
if ctx.Err() != nil {
2634
return
27-
default:
2835
}
2936

30-
d.ProgressFunc(atomic.LoadInt64(&p.Size), d.Info.Length, d)
37+
// Run progress func.
38+
p.ProgressFunc(p, d)
39+
40+
// Update last size
41+
atomic.StoreInt64(&p.lastSize, atomic.LoadInt64(&p.Size))
3142

3243
time.Sleep(time.Duration(d.Interval) * time.Millisecond)
3344
}
3445
}
3546
}
3647

48+
// Speed returns download speed.
49+
func (p *Progress) Speed() uint64 {
50+
return uint64((atomic.LoadInt64(&p.Size) - atomic.LoadInt64(&p.lastSize)) / int64(p.Interval) * 1000)
51+
}
52+
53+
// AvgSpeed returns average download speed.
54+
func (p *Progress) AvgSpeed() uint64 {
55+
56+
if totalMills := p.TotalCost().Milliseconds(); totalMills > 0 {
57+
return uint64(atomic.LoadInt64(&p.Size) / totalMills * 1000)
58+
}
59+
60+
return 0
61+
}
62+
63+
// TotalCost returns download duration.
64+
func (p *Progress) TotalCost() time.Duration {
65+
return time.Now().Sub(p.startedAt)
66+
}
67+
68+
// Write updates progress size.
3769
func (p *Progress) Write(b []byte) (int, error) {
3870
n := len(b)
3971
atomic.AddInt64(&p.Size, int64(n))

0 commit comments

Comments
 (0)