mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 01:20:27 +00:00
refactor: package
This commit is contained in:
1 parent
359f28bb1f
commit
ede6a9834d
55 files changed
+279
-237
No files matched your search
@@ -1,20 +1,35 @@
|
||||
## gopeed
|
||||
支持多协议(HTTP、BitTorrent)下载的客户端,提供命令行、RESTful API、WebSocket、Go类库方式来使用。
|
||||
|
||||
支持多协议(HTTP、BitTorrent)下载的客户端,提供命令行、RESTful API、WebSocket、Go 类库方式来使用。
|
||||
|
||||
## 安装
|
||||
|
||||
```sh
|
||||
go install github.com/monkeyWie/gopeed-core
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
```go
|
||||
|
||||
```
|
||||
|
||||
## TODO
|
||||
- [x] HTTP下载实现
|
||||
- [ ] BitTorrent下载实现
|
||||
- [x] .torrent文件解析
|
||||
- [x] tracker协议实现
|
||||
- [x] peer wire protocol协议实现
|
||||
- [ ] DHT协议实现
|
||||
- [ ] 磁力链接支持
|
||||
- [ ] uTP协议实现
|
||||
- [ ] 下载接口抽象(不关心具体协议)
|
||||
|
||||
- [x] HTTP 下载实现
|
||||
- [ ] BitTorrent 下载实现
|
||||
- [x] .torrent 文件解析
|
||||
- [x] tracker 协议实现
|
||||
- [x] peer wire protocol 协议实现
|
||||
- [ ] DHT 协议实现
|
||||
- [ ] 磁力链接支持
|
||||
- [ ] uTP 协议实现
|
||||
- [x] 下载接口抽象(不关心具体协议)
|
||||
- [ ] 支持自定义配置
|
||||
- [ ] 限速功能实现
|
||||
- [ ] 命令行工具提供
|
||||
- [ ] RESTful服务提供
|
||||
- [x] 命令行工具提供
|
||||
- [ ] RESTful 服务提供
|
||||
|
||||
## 参与
|
||||
由于项目目前还未定型,代码可能随时有大的调整,所以暂不接受PR,当然如果有什么好的想法可以在issue区提出来。
|
||||
|
||||
由于项目目前还未定型,代码可能随时有大的调整,所以暂不接受 PR,当然如果有什么好的想法可以在 issue 区提出来。
|
||||
+7
-7
@@ -2,10 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/monkeyWie/gopeed-core/download"
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
"github.com/monkeyWie/gopeed-core/download/http"
|
||||
"github.com/monkeyWie/gopeed-core/util"
|
||||
"github.com/monkeyWie/gopeed-core/internal/protocol/http"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/download"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/util"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
@@ -33,11 +33,11 @@ func main() {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
downloader.Listener(func(taskInfo *download.TaskInfo, eventKey base.EventKey) {
|
||||
if eventKey == base.EventKeyProgress {
|
||||
downloader.Listener(func(taskInfo *download.TaskInfo, eventKey download.EventKey) {
|
||||
if eventKey == download.EventKeyProgress {
|
||||
printProgress(taskInfo, "downloading...")
|
||||
}
|
||||
if eventKey == base.EventKeyDone {
|
||||
if eventKey == download.EventKeyDone {
|
||||
printProgress(taskInfo, "complete")
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package base
|
||||
|
||||
type Event struct {
|
||||
Status
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package base
|
||||
|
||||
// 对应协议的下载支持
|
||||
type Fetcher interface {
|
||||
Setup(ctl Controller)
|
||||
// 解析请求
|
||||
Resolve(req *Request) (res *Resource, err error)
|
||||
// 创建任务
|
||||
Create(res *Resource, opts *Options) (err error)
|
||||
Start() (err error)
|
||||
Pause() (err error)
|
||||
Continue() (err error)
|
||||
|
||||
// 获取任务下载进度
|
||||
Progress() Progress
|
||||
// 该方法会一直阻塞,直到任务下载结束
|
||||
Wait() (err error)
|
||||
}
|
||||
|
||||
type DefaultFetcher struct {
|
||||
Ctl Controller
|
||||
DoneCh chan error
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Setup(ctl Controller) {
|
||||
f.Ctl = ctl
|
||||
f.DoneCh = make(chan error, 1)
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Wait() (err error) {
|
||||
return <-f.DoneCh
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package model
|
||||
|
||||
type Extra struct {
|
||||
Method string
|
||||
Header map[string]string
|
||||
Body string
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package base
|
||||
package controller
|
||||
|
||||
import (
|
||||
"golang.org/x/net/proxy"
|
||||
@@ -15,40 +15,6 @@ type Controller interface {
|
||||
ContextDialer() (proxy.Dialer, error)
|
||||
}
|
||||
|
||||
// 下载请求
|
||||
type Request struct {
|
||||
// 下载链接
|
||||
URL string
|
||||
// 附加信息
|
||||
Extra interface{}
|
||||
}
|
||||
|
||||
// 资源信息
|
||||
type Resource struct {
|
||||
Req *Request
|
||||
// 资源总大小
|
||||
TotalSize int64
|
||||
// 是否支持断点下载
|
||||
Range bool
|
||||
// 资源所包含的文件列表
|
||||
Files []*FileInfo
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
// 保存文件名
|
||||
Name string
|
||||
// 保存目录
|
||||
Path string
|
||||
// 并发连接数
|
||||
Connections int
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
Size int64
|
||||
}
|
||||
|
||||
type DefaultController struct {
|
||||
Files map[string]*os.File
|
||||
}
|
||||
@@ -142,15 +108,3 @@ 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
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package fetcher
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/internal/controller"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
)
|
||||
|
||||
// 对应协议的下载支持
|
||||
type Fetcher interface {
|
||||
Setup(ctl controller.Controller)
|
||||
// 解析请求
|
||||
Resolve(req *base.Request) (res *base.Resource, err error)
|
||||
// 创建任务
|
||||
Create(res *base.Resource, opts *base.Options) (err error)
|
||||
Start() (err error)
|
||||
Pause() (err error)
|
||||
Continue() (err error)
|
||||
|
||||
// 获取任务各个文件下载进度
|
||||
Progress() Progress
|
||||
// 该方法会一直阻塞,直到任务下载结束
|
||||
Wait() (err error)
|
||||
}
|
||||
|
||||
type DefaultFetcher struct {
|
||||
Ctl controller.Controller
|
||||
DoneCh chan error
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Setup(ctl controller.Controller) {
|
||||
f.Ctl = ctl
|
||||
f.DoneCh = make(chan error, 1)
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Wait() (err error) {
|
||||
return <-f.DoneCh
|
||||
}
|
||||
|
||||
// 获取任务中各个文件的已下载字节数
|
||||
type Progress []int64
|
||||
|
||||
// TotalDownloaded 获取任务总下载字节数
|
||||
func (p Progress) TotalDownloaded() int64 {
|
||||
total := int64(0)
|
||||
for _, d := range p {
|
||||
total += d
|
||||
}
|
||||
return total
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
"github.com/monkeyWie/gopeed-core/download/http/model"
|
||||
"github.com/monkeyWie/gopeed-core/internal/fetcher"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -34,13 +34,13 @@ func (re *RequestError) Error() string {
|
||||
}
|
||||
|
||||
type Fetcher struct {
|
||||
*base.DefaultFetcher
|
||||
*fetcher.DefaultFetcher
|
||||
|
||||
res *base.Resource
|
||||
opts *base.Options
|
||||
status base.Status
|
||||
clients []*http.Response
|
||||
chunks []*model.Chunk
|
||||
chunks []*Chunk
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -49,15 +49,15 @@ type Fetcher struct {
|
||||
|
||||
func NewFetcher() *Fetcher {
|
||||
return &Fetcher{
|
||||
DefaultFetcher: new(base.DefaultFetcher),
|
||||
DefaultFetcher: new(fetcher.DefaultFetcher),
|
||||
pauseCh: make(chan interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
var protocols = []string{"HTTP", "HTTPS"}
|
||||
|
||||
func FetcherBuilder() ([]string, func() base.Fetcher) {
|
||||
return protocols, func() base.Fetcher {
|
||||
func FetcherBuilder() ([]string, func() fetcher.Fetcher) {
|
||||
return protocols, func() fetcher.Fetcher {
|
||||
return NewFetcher()
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func (f *Fetcher) Start() (err error) {
|
||||
if f.res.Range {
|
||||
// 每个连接平均需要下载的分块大小
|
||||
chunkSize := f.res.TotalSize / int64(f.opts.Connections)
|
||||
f.chunks = make([]*model.Chunk, f.opts.Connections)
|
||||
f.chunks = make([]*Chunk, f.opts.Connections)
|
||||
f.clients = make([]*http.Response, f.opts.Connections)
|
||||
for i := 0; i < f.opts.Connections; i++ {
|
||||
var (
|
||||
@@ -163,14 +163,14 @@ func (f *Fetcher) Start() (err error) {
|
||||
} else {
|
||||
end = begin + chunkSize - 1
|
||||
}
|
||||
chunk := model.NewChunk(begin, end)
|
||||
chunk := NewChunk(begin, end)
|
||||
f.chunks[i] = chunk
|
||||
}
|
||||
} else {
|
||||
// 只支持单连接下载
|
||||
f.chunks = make([]*model.Chunk, 1)
|
||||
f.chunks = make([]*Chunk, 1)
|
||||
f.clients = make([]*http.Response, 1)
|
||||
f.chunks[0] = model.NewChunk(0, 0)
|
||||
f.chunks[0] = NewChunk(0, 0)
|
||||
}
|
||||
f.fetch()
|
||||
return
|
||||
@@ -200,8 +200,8 @@ func (f *Fetcher) Continue() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) Progress() base.Progress {
|
||||
p := make(base.Progress, 0)
|
||||
func (f *Fetcher) Progress() fetcher.Progress {
|
||||
p := make(fetcher.Progress, 0)
|
||||
if len(f.chunks) > 0 {
|
||||
total := int64(0)
|
||||
for _, chunk := range f.chunks {
|
||||
@@ -361,7 +361,7 @@ func buildRequest(ctx context.Context, req *base.Request) (httpReq *http.Request
|
||||
if req.Extra == nil {
|
||||
method = http.MethodGet
|
||||
} else {
|
||||
extra := req.Extra.(model.Extra)
|
||||
extra := req.Extra.(Extra)
|
||||
if extra.Method != "" {
|
||||
method = extra.Method
|
||||
} else {
|
||||
@@ -1,8 +1,10 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
"github.com/monkeyWie/gopeed-core/download/dltest"
|
||||
"github.com/monkeyWie/gopeed-core/internal/controller"
|
||||
"github.com/monkeyWie/gopeed-core/internal/fetcher"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/test"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -10,22 +12,22 @@ import (
|
||||
)
|
||||
|
||||
func TestFetcher_Resolve(t *testing.T) {
|
||||
testResolve(dltest.StartTestFileServer, &base.Resource{
|
||||
TotalSize: dltest.BuildSize,
|
||||
testResolve(test.StartTestFileServer, &base.Resource{
|
||||
TotalSize: test.BuildSize,
|
||||
Range: true,
|
||||
Files: []*base.FileInfo{
|
||||
{
|
||||
Name: dltest.BuildName,
|
||||
Size: dltest.BuildSize,
|
||||
Name: test.BuildName,
|
||||
Size: test.BuildSize,
|
||||
},
|
||||
},
|
||||
}, t)
|
||||
testResolve(dltest.StartTestChunkedServer, &base.Resource{
|
||||
testResolve(test.StartTestChunkedServer, &base.Resource{
|
||||
TotalSize: 0,
|
||||
Range: false,
|
||||
Files: []*base.FileInfo{
|
||||
{
|
||||
Name: dltest.BuildName,
|
||||
Name: test.BuildName,
|
||||
Size: 0,
|
||||
},
|
||||
},
|
||||
@@ -37,7 +39,7 @@ func testResolve(startTestServer func() net.Listener, want *base.Resource, t *te
|
||||
defer listener.Close()
|
||||
fetcher := NewFetcher()
|
||||
res, err := fetcher.Resolve(&base.Request{
|
||||
URL: "http://" + listener.Addr().String() + "/" + dltest.BuildName,
|
||||
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -49,7 +51,7 @@ func testResolve(startTestServer func() net.Listener, want *base.Resource, t *te
|
||||
}
|
||||
|
||||
func TestFetcher_DownloadNormal(t *testing.T) {
|
||||
listener := dltest.StartTestFileServer()
|
||||
listener := test.StartTestFileServer()
|
||||
defer listener.Close()
|
||||
// 正常下载
|
||||
downloadNormal(listener, 1, t)
|
||||
@@ -59,7 +61,7 @@ func TestFetcher_DownloadNormal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFetcher_DownloadContinue(t *testing.T) {
|
||||
listener := dltest.StartTestFileServer()
|
||||
listener := test.StartTestFileServer()
|
||||
defer listener.Close()
|
||||
// 暂停继续
|
||||
downloadContinue(listener, 1, t)
|
||||
@@ -69,7 +71,7 @@ func TestFetcher_DownloadContinue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFetcher_DownloadChunked(t *testing.T) {
|
||||
listener := dltest.StartTestChunkedServer()
|
||||
listener := test.StartTestChunkedServer()
|
||||
defer listener.Close()
|
||||
// chunked编码下载
|
||||
downloadNormal(listener, 1, t)
|
||||
@@ -77,31 +79,31 @@ func TestFetcher_DownloadChunked(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFetcher_DownloadRetry(t *testing.T) {
|
||||
listener := dltest.StartTestRetryServer()
|
||||
listener := test.StartTestRetryServer()
|
||||
defer listener.Close()
|
||||
// chunked编码下载
|
||||
downloadNormal(listener, 1, t)
|
||||
}
|
||||
|
||||
func TestFetcher_DownloadError(t *testing.T) {
|
||||
listener := dltest.StartTestErrorServer()
|
||||
listener := test.StartTestErrorServer()
|
||||
defer listener.Close()
|
||||
// chunked编码下载
|
||||
downloadError(listener, 1, t)
|
||||
}
|
||||
|
||||
func downloadReady(listener net.Listener, connections int, t *testing.T) base.Fetcher {
|
||||
func downloadReady(listener net.Listener, connections int, t *testing.T) fetcher.Fetcher {
|
||||
fetcher := NewFetcher()
|
||||
fetcher.Setup(base.NewController())
|
||||
fetcher.Setup(controller.NewController())
|
||||
res, err := fetcher.Resolve(&base.Request{
|
||||
URL: "http://" + listener.Addr().String() + "/" + dltest.BuildName,
|
||||
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = fetcher.Create(res, &base.Options{
|
||||
Name: dltest.DownloadName,
|
||||
Path: dltest.TestDir,
|
||||
Name: test.DownloadName,
|
||||
Path: test.Dir,
|
||||
Connections: connections,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -121,8 +123,8 @@ func downloadNormal(listener net.Listener, connections int, t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := dltest.FileMd5(dltest.BuildFile)
|
||||
got := dltest.FileMd5(dltest.DownloadFile)
|
||||
want := test.FileMd5(test.BuildFile)
|
||||
got := test.FileMd5(test.DownloadFile)
|
||||
if want != got {
|
||||
t.Errorf("Download error = %v, want %v", got, want)
|
||||
}
|
||||
@@ -146,8 +148,8 @@ func downloadContinue(listener net.Listener, connections int, t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := dltest.FileMd5(dltest.BuildFile)
|
||||
got := dltest.FileMd5(dltest.DownloadFile)
|
||||
want := test.FileMd5(test.BuildFile)
|
||||
got := test.FileMd5(test.DownloadFile)
|
||||
if want != got {
|
||||
t.Errorf("Download error = %v, want %v", got, want)
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package model
|
||||
package http
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
)
|
||||
import "github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
|
||||
type Chunk struct {
|
||||
Status base.Status
|
||||
@@ -18,3 +16,9 @@ func NewChunk(begin int64, end int64) *Chunk {
|
||||
End: end,
|
||||
}
|
||||
}
|
||||
|
||||
type Extra struct {
|
||||
Method string
|
||||
Header map[string]string
|
||||
Body string
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/http"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -12,17 +12,6 @@ const (
|
||||
DownloadStatusDone
|
||||
)
|
||||
|
||||
type EventKey string
|
||||
|
||||
const (
|
||||
EventKeyStart = "start"
|
||||
EventKeyPause = "pause"
|
||||
EventKeyContinue = "continue"
|
||||
EventKeyProgress = "progress"
|
||||
EventKeyError = "error"
|
||||
EventKeyDone = "done"
|
||||
)
|
||||
|
||||
const (
|
||||
HttpCodeOK = 200
|
||||
HttpCodePartialContent = 206
|
||||
@@ -0,0 +1,36 @@
|
||||
package base
|
||||
|
||||
// 下载请求
|
||||
type Request struct {
|
||||
// 下载链接
|
||||
URL string
|
||||
// 附加信息
|
||||
Extra interface{}
|
||||
}
|
||||
|
||||
// 资源信息
|
||||
type Resource struct {
|
||||
Req *Request
|
||||
// 资源总大小
|
||||
TotalSize int64
|
||||
// 是否支持断点下载
|
||||
Range bool
|
||||
// 资源所包含的文件列表
|
||||
Files []*FileInfo
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
Size int64
|
||||
}
|
||||
|
||||
// 下载选项
|
||||
type Options struct {
|
||||
// 保存文件名
|
||||
Name string
|
||||
// 保存目录
|
||||
Path string
|
||||
// 并发连接数
|
||||
Connections int
|
||||
}
|
||||
@@ -3,13 +3,19 @@ package download
|
||||
import (
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
"github.com/monkeyWie/gopeed-core/internal/controller"
|
||||
"github.com/monkeyWie/gopeed-core/internal/fetcher"
|
||||
"github.com/monkeyWie/gopeed-core/internal/protocol/http"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/util"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FetcherBuilder func() (protocols []string, builder func() fetcher.Fetcher)
|
||||
|
||||
type TaskInfo struct {
|
||||
ID string
|
||||
Res *base.Resource
|
||||
@@ -18,23 +24,32 @@ type TaskInfo struct {
|
||||
Files map[string]*base.FileInfo
|
||||
Progress *Progress
|
||||
|
||||
fetcher base.Fetcher
|
||||
timer *base.Timer
|
||||
fetcher fetcher.Fetcher
|
||||
timer *util.Timer
|
||||
locker *sync.Mutex
|
||||
}
|
||||
|
||||
type FetcherBuilder func() (protocols []string, builder func() base.Fetcher)
|
||||
|
||||
type Downloader struct {
|
||||
*base.DefaultController
|
||||
fetchBuilders map[string]func() base.Fetcher
|
||||
tasks map[string]*TaskInfo
|
||||
listener func(taskInfo *TaskInfo, eventKey base.EventKey)
|
||||
type Progress struct {
|
||||
// 下载耗时(纳秒)
|
||||
Used int64
|
||||
// 每秒下载字节数
|
||||
Speed int64
|
||||
// 已下载的字节数
|
||||
Downloaded int64
|
||||
}
|
||||
|
||||
type Downloader struct {
|
||||
*controller.DefaultController
|
||||
fetchBuilders map[string]func() fetcher.Fetcher
|
||||
tasks map[string]*TaskInfo
|
||||
listener func(taskInfo *TaskInfo, eventKey EventKey)
|
||||
}
|
||||
|
||||
var DefaultDownloader = NewDownloader(http.FetcherBuilder)
|
||||
|
||||
func NewDownloader(fbs ...FetcherBuilder) *Downloader {
|
||||
d := &Downloader{DefaultController: base.NewController()}
|
||||
d.fetchBuilders = make(map[string]func() base.Fetcher)
|
||||
d := &Downloader{DefaultController: controller.NewController()}
|
||||
d.fetchBuilders = make(map[string]func() fetcher.Fetcher)
|
||||
for _, f := range fbs {
|
||||
protocols, builder := f()
|
||||
for _, p := range protocols {
|
||||
@@ -57,7 +72,7 @@ func NewDownloader(fbs ...FetcherBuilder) *Downloader {
|
||||
task.Progress.Used = task.timer.Used()
|
||||
task.Progress.Speed = current - task.Progress.Downloaded
|
||||
task.Progress.Downloaded = current
|
||||
d.emit(task, base.EventKeyProgress)
|
||||
d.emit(task, EventKeyProgress)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
@@ -66,7 +81,7 @@ func NewDownloader(fbs ...FetcherBuilder) *Downloader {
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Downloader) buildFetcher(URL string) (base.Fetcher, error) {
|
||||
func (d *Downloader) buildFetcher(URL string) (fetcher.Fetcher, error) {
|
||||
url, err := url.Parse(URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -104,21 +119,21 @@ func (d *Downloader) Create(res *base.Resource, opts *base.Options) (err error)
|
||||
Status: base.DownloadStatusReady,
|
||||
Progress: &Progress{},
|
||||
fetcher: fetcher,
|
||||
timer: &base.Timer{},
|
||||
timer: &util.Timer{},
|
||||
locker: new(sync.Mutex),
|
||||
}
|
||||
d.tasks[id] = task
|
||||
task.timer.Start()
|
||||
d.emit(task, base.EventKeyStart)
|
||||
d.emit(task, EventKeyStart)
|
||||
err = fetcher.Start()
|
||||
if err != nil {
|
||||
d.emit(task, base.EventKeyError)
|
||||
d.emit(task, EventKeyError)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
err = fetcher.Wait()
|
||||
if err != nil {
|
||||
d.emit(task, base.EventKeyError)
|
||||
d.emit(task, EventKeyError)
|
||||
} else {
|
||||
task.Progress.Used = task.timer.Used()
|
||||
if task.Res.TotalSize == 0 {
|
||||
@@ -130,7 +145,7 @@ func (d *Downloader) Create(res *base.Resource, opts *base.Options) (err error)
|
||||
}
|
||||
task.Progress.Speed = task.Res.TotalSize / used
|
||||
task.Progress.Downloaded = task.Res.TotalSize
|
||||
d.emit(d.tasks[id], base.EventKeyDone)
|
||||
d.emit(d.tasks[id], EventKeyDone)
|
||||
}
|
||||
}()
|
||||
return
|
||||
@@ -142,7 +157,7 @@ func (d *Downloader) Pause(id string) {
|
||||
defer task.locker.Unlock()
|
||||
task.timer.Pause()
|
||||
task.fetcher.Pause()
|
||||
d.emit(task, base.EventKeyPause)
|
||||
d.emit(task, EventKeyPause)
|
||||
}
|
||||
|
||||
func (d *Downloader) Continue(id string) {
|
||||
@@ -151,24 +166,29 @@ func (d *Downloader) Continue(id string) {
|
||||
defer task.locker.Unlock()
|
||||
task.timer.Continue()
|
||||
task.fetcher.Continue()
|
||||
d.emit(task, base.EventKeyContinue)
|
||||
d.emit(task, EventKeyContinue)
|
||||
}
|
||||
|
||||
func (d *Downloader) Listener(fn func(taskInfo *TaskInfo, eventKey base.EventKey)) {
|
||||
func (d *Downloader) Listener(fn func(taskInfo *TaskInfo, eventKey EventKey)) {
|
||||
d.listener = fn
|
||||
}
|
||||
|
||||
func (d *Downloader) emit(taskInfo *TaskInfo, eventKey base.EventKey) {
|
||||
func (d *Downloader) emit(taskInfo *TaskInfo, eventKey EventKey) {
|
||||
if d.listener != nil {
|
||||
d.listener(taskInfo, eventKey)
|
||||
}
|
||||
}
|
||||
|
||||
type Progress struct {
|
||||
// 下载耗时(纳秒)
|
||||
Used int64
|
||||
// 每秒下载字节数
|
||||
Speed int64
|
||||
// 已下载的字节数
|
||||
Downloaded int64
|
||||
// Resolve 解析资源
|
||||
func Resolve(url string, extra ...interface{}) (*base.Resource, error) {
|
||||
var e interface{}
|
||||
if len(extra) == 0 {
|
||||
e = nil
|
||||
} else {
|
||||
e = extra[0]
|
||||
}
|
||||
return DefaultDownloader.Resolve(&base.Request{
|
||||
URL: url,
|
||||
Extra: e,
|
||||
})
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/download/base"
|
||||
"github.com/monkeyWie/gopeed-core/download/dltest"
|
||||
"github.com/monkeyWie/gopeed-core/download/http"
|
||||
"github.com/monkeyWie/gopeed-core/internal/protocol/http"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/base"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/test"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDownloader_Resolve(t *testing.T) {
|
||||
listener := dltest.StartTestFileServer()
|
||||
listener := test.StartTestFileServer()
|
||||
defer listener.Close()
|
||||
|
||||
downloader := NewDownloader(http.FetcherBuilder)
|
||||
req := &base.Request{
|
||||
URL: "http://" + listener.Addr().String() + "/" + dltest.BuildName,
|
||||
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
|
||||
}
|
||||
res, err := downloader.Resolve(req)
|
||||
if err != nil {
|
||||
@@ -23,28 +23,28 @@ func TestDownloader_Resolve(t *testing.T) {
|
||||
}
|
||||
want := &base.Resource{
|
||||
Req: req,
|
||||
TotalSize: dltest.BuildSize,
|
||||
TotalSize: test.BuildSize,
|
||||
Range: true,
|
||||
Files: []*base.FileInfo{
|
||||
{
|
||||
Name: dltest.BuildName,
|
||||
Name: test.BuildName,
|
||||
Path: "",
|
||||
Size: dltest.BuildSize,
|
||||
Size: test.BuildSize,
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(res, want) {
|
||||
t.Errorf("Resolve error = %s, want %s", dltest.ToJson(res), dltest.ToJson(want))
|
||||
t.Errorf("Resolve error = %s, want %s", test.ToJson(res), test.ToJson(want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloader_Create(t *testing.T) {
|
||||
listener := dltest.StartTestFileServer()
|
||||
listener := test.StartTestFileServer()
|
||||
defer listener.Close()
|
||||
|
||||
downloader := NewDownloader(http.FetcherBuilder)
|
||||
req := &base.Request{
|
||||
URL: "http://" + listener.Addr().String() + "/" + dltest.BuildName,
|
||||
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
|
||||
}
|
||||
res, err := downloader.Resolve(req)
|
||||
if err != nil {
|
||||
@@ -53,22 +53,22 @@ func TestDownloader_Create(t *testing.T) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
downloader.Listener(func(taskInfo *TaskInfo, eventKey base.EventKey) {
|
||||
if eventKey == base.EventKeyDone {
|
||||
downloader.Listener(func(taskInfo *TaskInfo, eventKey EventKey) {
|
||||
if eventKey == EventKeyDone {
|
||||
wg.Done()
|
||||
}
|
||||
})
|
||||
err = downloader.Create(res, &base.Options{
|
||||
Path: dltest.TestDir,
|
||||
Name: dltest.DownloadName,
|
||||
Path: test.Dir,
|
||||
Name: test.DownloadName,
|
||||
Connections: 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wg.Wait()
|
||||
want := dltest.FileMd5(dltest.BuildFile)
|
||||
got := dltest.FileMd5(dltest.DownloadFile)
|
||||
want := test.FileMd5(test.BuildFile)
|
||||
got := test.FileMd5(test.DownloadFile)
|
||||
if want != got {
|
||||
t.Errorf("Download error = %v, want %v", got, want)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package download
|
||||
|
||||
type EventKey string
|
||||
|
||||
const (
|
||||
EventKeyStart = "start"
|
||||
EventKeyPause = "pause"
|
||||
EventKeyContinue = "continue"
|
||||
EventKeyProgress = "progress"
|
||||
EventKeyError = "error"
|
||||
EventKeyDone = "done"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
EventKey
|
||||
TaskInfo
|
||||
}
|
||||
@@ -2,8 +2,8 @@ package bt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/torrent"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/torrent"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/torrent"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/torrent"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/mse"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer/message"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer/message"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package torrent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/tracker"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/tracker"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/tracker"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/tracker"
|
||||
)
|
||||
|
||||
type peerPool struct {
|
||||
@@ -1,7 +1,7 @@
|
||||
package torrent
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package torrent
|
||||
|
||||
import (
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"testing"
|
||||
)
|
||||
File renamed without changes.
@@ -15,8 +15,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/peer"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/peer"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -2,7 +2,7 @@ package tracker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/monkeyWie/gopeed-core/protocol/bt/metainfo"
|
||||
"github.com/monkeyWie/gopeed-core/pkg/protocol/bt/metainfo"
|
||||
"testing"
|
||||
)
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
package dltest
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,16 +12,16 @@ import (
|
||||
const (
|
||||
BuildName = "build.data"
|
||||
BuildSize = 200 * 1024 * 1024
|
||||
TestDir = "./"
|
||||
BuildFile = TestDir + BuildName
|
||||
Dir = "./"
|
||||
BuildFile = Dir + BuildName
|
||||
|
||||
DownloadName = "download.data"
|
||||
DownloadFile = TestDir + DownloadName
|
||||
DownloadFile = Dir + DownloadName
|
||||
)
|
||||
|
||||
func StartTestFileServer() net.Listener {
|
||||
return startTestServer(func() http.Handler {
|
||||
return http.FileServer(http.Dir(TestDir))
|
||||
return http.FileServer(http.Dir(Dir))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dltest
|
||||
package test
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
package base
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
Reference in new issue
Block a user