mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-14 04:13:43 +00:00
feat: 命令行工具
This commit is contained in:
1 parent
f150ec23c4
commit
046729bfaa
10 files changed
+195
-31
No files matched your search
@@ -12,6 +12,16 @@ const (
|
||||
DownloadStatusDone
|
||||
)
|
||||
|
||||
type EventKey string
|
||||
|
||||
const (
|
||||
EventKeyStart = "start"
|
||||
EventKeyPause = "pause"
|
||||
EventKeyProgress = "progress"
|
||||
EventKeyError = "error"
|
||||
EventKeyDone = "done"
|
||||
)
|
||||
|
||||
const (
|
||||
HttpCodeOK = 200
|
||||
HttpCodePartialContent = 206
|
||||
|
||||
@@ -142,3 +142,15 @@ func (d *DialerWarp) Dial(network, addr string) (c net.Conn, err error) {
|
||||
}
|
||||
return &ConnWarp{conn: conn}, nil
|
||||
}
|
||||
|
||||
// 获取任务中各个文件的已下载字节数
|
||||
type Progress []int64
|
||||
|
||||
// TotalDownloaded 获取任务总下载字节数
|
||||
func (p Progress) TotalDownloaded() int64 {
|
||||
total := int64(0)
|
||||
for _, d := range p {
|
||||
total += d
|
||||
}
|
||||
return total
|
||||
}
|
||||
@@ -11,6 +11,8 @@ type Fetcher interface {
|
||||
Pause() (err error)
|
||||
Continue() (err error)
|
||||
|
||||
// 获取任务下载进度
|
||||
Progress() Progress
|
||||
// 该方法会一直阻塞,直到任务下载结束
|
||||
Wait() (err error)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package base
|
||||
|
||||
import "time"
|
||||
|
||||
// 计时器
|
||||
type Timer struct {
|
||||
t int64
|
||||
used int64
|
||||
}
|
||||
|
||||
func (t *Timer) Start() {
|
||||
t.t = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (t *Timer) Pause() {
|
||||
t.used += time.Now().UnixNano() - t.t
|
||||
}
|
||||
|
||||
func (t *Timer) Continue() {
|
||||
t.t = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (t *Timer) Used() int64 {
|
||||
return (time.Now().UnixNano() - t.t) + t.used
|
||||
}
|
||||
+70
-28
@@ -7,16 +7,19 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskInfo struct {
|
||||
ID string
|
||||
Res *base.Resource
|
||||
Opts *base.Options
|
||||
Status base.Status
|
||||
Files map[string]*base.FileInfo
|
||||
ID string
|
||||
Res *base.Resource
|
||||
Opts *base.Options
|
||||
Status base.Status
|
||||
Files map[string]*base.FileInfo
|
||||
Progress *Progress
|
||||
|
||||
fetcher base.Fetcher
|
||||
timer *base.Timer
|
||||
locker *sync.Mutex
|
||||
}
|
||||
|
||||
@@ -26,7 +29,7 @@ type Downloader struct {
|
||||
*base.DefaultController
|
||||
fetchBuilders map[string]func() base.Fetcher
|
||||
tasks map[string]*TaskInfo
|
||||
listener func(taskInfo *TaskInfo, status base.Status)
|
||||
listener func(taskInfo *TaskInfo, eventKey base.EventKey)
|
||||
}
|
||||
|
||||
func NewDownloader(fbs ...FetcherBuilder) *Downloader {
|
||||
@@ -39,6 +42,27 @@ func NewDownloader(fbs ...FetcherBuilder) *Downloader {
|
||||
}
|
||||
}
|
||||
d.tasks = make(map[string]*TaskInfo)
|
||||
|
||||
// 每秒统计一次下载速度
|
||||
go func() {
|
||||
for {
|
||||
if len(d.tasks) > 0 {
|
||||
for _, task := range d.tasks {
|
||||
if task.Status == base.DownloadStatusDone ||
|
||||
task.Status == base.DownloadStatusError ||
|
||||
task.Status == base.DownloadStatusPause {
|
||||
continue
|
||||
}
|
||||
current := task.fetcher.Progress().TotalDownloaded()
|
||||
task.Progress.Used = task.timer.Used()
|
||||
task.Progress.Speed = current - task.Progress.Downloaded
|
||||
task.Progress.Downloaded = current
|
||||
d.emit(task, base.EventKeyProgress)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}()
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -73,26 +97,33 @@ func (d *Downloader) Create(res *base.Resource, opts *base.Options) (err error)
|
||||
return
|
||||
}
|
||||
id := uuid.New().String()
|
||||
d.tasks[id] = &TaskInfo{
|
||||
ID: id,
|
||||
Res: res,
|
||||
Opts: opts,
|
||||
Status: base.DownloadStatusReady,
|
||||
fetcher: fetcher,
|
||||
locker: new(sync.Mutex),
|
||||
task := &TaskInfo{
|
||||
ID: id,
|
||||
Res: res,
|
||||
Opts: opts,
|
||||
Status: base.DownloadStatusReady,
|
||||
Progress: &Progress{},
|
||||
fetcher: fetcher,
|
||||
timer: &base.Timer{},
|
||||
locker: new(sync.Mutex),
|
||||
}
|
||||
d.tasks[id] = task
|
||||
task.timer.Start()
|
||||
d.emit(task, base.EventKeyStart)
|
||||
err = fetcher.Start()
|
||||
if err != nil {
|
||||
d.emit(task, base.EventKeyError)
|
||||
return
|
||||
}
|
||||
d.emit(d.tasks[id], base.DownloadStatusStart)
|
||||
go func() {
|
||||
err = fetcher.Start()
|
||||
if err != nil {
|
||||
d.emit(d.tasks[id], base.DownloadStatusError)
|
||||
return
|
||||
}
|
||||
err = fetcher.Wait()
|
||||
if err != nil {
|
||||
d.emit(d.tasks[id], base.DownloadStatusError)
|
||||
d.emit(task, base.EventKeyError)
|
||||
} else {
|
||||
d.emit(d.tasks[id], base.DownloadStatusDone)
|
||||
task.Progress.Used = task.timer.Used()
|
||||
task.Progress.Speed = task.Res.TotalSize / (task.Progress.Used / int64(time.Second))
|
||||
task.Progress.Downloaded = task.Res.TotalSize
|
||||
d.emit(d.tasks[id], base.EventKeyDone)
|
||||
}
|
||||
}()
|
||||
return
|
||||
@@ -102,24 +133,35 @@ func (d *Downloader) Pause(id string) {
|
||||
task := d.tasks[id]
|
||||
task.locker.Lock()
|
||||
defer task.locker.Unlock()
|
||||
d.tasks[id].fetcher.Pause()
|
||||
d.emit(d.tasks[id], base.DownloadStatusPause)
|
||||
task.timer.Pause()
|
||||
task.fetcher.Pause()
|
||||
d.emit(task, base.EventKeyPause)
|
||||
}
|
||||
|
||||
func (d *Downloader) Continue(id string) {
|
||||
task := d.tasks[id]
|
||||
task.locker.Lock()
|
||||
defer task.locker.Unlock()
|
||||
d.tasks[id].fetcher.Continue()
|
||||
d.emit(d.tasks[id], base.DownloadStatusStart)
|
||||
task.timer.Continue()
|
||||
task.fetcher.Continue()
|
||||
d.emit(task, base.EventKeyStart)
|
||||
}
|
||||
|
||||
func (d *Downloader) Listener(fn func(taskInfo *TaskInfo, status base.Status)) {
|
||||
func (d *Downloader) Listener(fn func(taskInfo *TaskInfo, eventKey base.EventKey)) {
|
||||
d.listener = fn
|
||||
}
|
||||
|
||||
func (d *Downloader) emit(taskInfo *TaskInfo, status base.Status) {
|
||||
func (d *Downloader) emit(taskInfo *TaskInfo, eventKey base.EventKey) {
|
||||
if d.listener != nil {
|
||||
d.listener(taskInfo, status)
|
||||
d.listener(taskInfo, eventKey)
|
||||
}
|
||||
}
|
||||
|
||||
type Progress struct {
|
||||
// 下载耗时(纳秒)
|
||||
Used int64
|
||||
// 每秒下载字节数
|
||||
Speed int64
|
||||
// 已下载的字节数
|
||||
Downloaded int64
|
||||
}
|
||||
@@ -53,8 +53,8 @@ func TestDownloader_Create(t *testing.T) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
downloader.Listener(func(taskInfo *TaskInfo, status base.Status) {
|
||||
if status == base.DownloadStatusDone {
|
||||
downloader.Listener(func(taskInfo *TaskInfo, eventKey base.EventKey) {
|
||||
if eventKey == base.EventKeyDone {
|
||||
wg.Done()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -199,6 +199,18 @@ func (f *Fetcher) Continue() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) Progress() base.Progress {
|
||||
p := make(base.Progress, 0)
|
||||
if len(f.chunks) > 0 {
|
||||
total := int64(0)
|
||||
for _, chunk := range f.chunks {
|
||||
total += chunk.Downloaded
|
||||
}
|
||||
p = append(p, total)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (f *Fetcher) filename() string {
|
||||
// 创建文件
|
||||
var filename = f.opts.Name
|
||||
|
||||
Reference in new issue
Block a user