From ede6a9834d1172e584e14f0015dcf2166fa8f759 Mon Sep 17 00:00:00 2001 From: liwei Date: Thu, 25 Mar 2021 19:38:23 +0800 Subject: [PATCH] refactor: package --- README.md | 41 ++++++--- cmd/cli/gopeed.go | 14 +-- download/base/event.go | 5 -- download/base/fetcher.go | 32 ------- download/http/model/extra.go | 7 -- .../controller}/controller.go | 48 +---------- internal/fetcher/fetcher.go | 49 +++++++++++ .../protocol}/http/fetcher.go | 28 +++--- .../protocol}/http/fetcher_test.go | 48 ++++++----- .../protocol/http/model.go | 12 ++- main.go | 2 +- {download => pkg}/base/constants.go | 11 --- pkg/base/model.go | 36 ++++++++ {download => pkg/download}/downloader.go | 80 +++++++++++------- {download => pkg/download}/downloader_test.go | 34 ++++---- pkg/download/event.go | 17 ++++ {protocol => pkg/protocol}/bt/client.go | 4 +- {protocol => pkg/protocol}/bt/client_test.go | 0 {protocol => pkg/protocol}/bt/dht/ping.go | 0 .../protocol}/bt/dht/ping_test.go | 0 .../protocol}/bt/metainfo/metainfo.go | 0 .../protocol}/bt/metainfo/metainfo_test.go | 0 .../protocol}/bt/peer/handshake.go | 0 .../protocol}/bt/peer/message/message.go | 0 .../protocol}/bt/peer/message/msg_bitfield.go | 0 .../bt/peer/message/msg_bitfield_test.go | 0 .../protocol}/bt/peer/message/msg_choke.go | 0 .../bt/peer/message/msg_interested.go | 0 .../bt/peer/message/msg_keepalive.go | 0 .../bt/peer/message/msg_not_interested.go | 0 .../protocol}/bt/peer/message/msg_piece.go | 0 .../protocol}/bt/peer/message/msg_request.go | 0 .../protocol}/bt/peer/message/msg_unchoke.go | 0 {protocol => pkg/protocol}/bt/peer/peer.go | 0 ...rones.S08E05.720p.WEB.H264-MEMENTO.torrent | Bin .../protocol}/bt/testdata/office.torrent | Bin .../protocol}/bt/torrent/main/main.go | 6 +- .../protocol}/bt/torrent/peer_conn.go | 6 +- .../protocol}/bt/torrent/peer_conn_test.go | 4 +- .../protocol}/bt/torrent/peer_pool.go | 4 +- .../protocol}/bt/torrent/piece_state.go | 2 +- .../protocol}/bt/torrent/torrent.go | 2 +- .../protocol}/bt/torrent/torrent_test.go | 4 +- .../protocol}/bt/tracker/errors.go | 0 .../protocol}/bt/tracker/tracker.go | 4 +- .../protocol}/bt/tracker/tracker_test.go | 2 +- {protocol => pkg/protocol}/http/client.go | 0 .../protocol}/http/client_test.go | 0 {protocol => pkg/protocol}/http/request.go | 0 {protocol => pkg/protocol}/http/response.go | 0 {download/dltest => pkg/test}/httptest.go | 10 +-- {download/dltest => pkg/test}/util.go | 2 +- {util => pkg/util}/bytefmt.go | 0 {util => pkg/util}/bytefmt_test.go | 0 {download/base => pkg/util}/timer.go | 2 +- 55 files changed, 279 insertions(+), 237 deletions(-) delete mode 100644 download/base/event.go delete mode 100644 download/base/fetcher.go delete mode 100644 download/http/model/extra.go rename {download/base => internal/controller}/controller.go (75%) create mode 100644 internal/fetcher/fetcher.go rename {download => internal/protocol}/http/fetcher.go (93%) rename {download => internal/protocol}/http/fetcher_test.go (73%) rename download/http/model/chunk.go => internal/protocol/http/model.go (62%) rename {download => pkg}/base/constants.go (71%) create mode 100644 pkg/base/model.go rename {download => pkg/download}/downloader.go (69%) rename {download => pkg/download}/downloader_test.go (52%) create mode 100644 pkg/download/event.go rename {protocol => pkg/protocol}/bt/client.go (89%) rename {protocol => pkg/protocol}/bt/client_test.go (100%) rename {protocol => pkg/protocol}/bt/dht/ping.go (100%) rename {protocol => pkg/protocol}/bt/dht/ping_test.go (100%) rename {protocol => pkg/protocol}/bt/metainfo/metainfo.go (100%) rename {protocol => pkg/protocol}/bt/metainfo/metainfo_test.go (100%) rename {protocol => pkg/protocol}/bt/peer/handshake.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/message.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_bitfield.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_bitfield_test.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_choke.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_interested.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_keepalive.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_not_interested.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_piece.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_request.go (100%) rename {protocol => pkg/protocol}/bt/peer/message/msg_unchoke.go (100%) rename {protocol => pkg/protocol}/bt/peer/peer.go (100%) rename {protocol => pkg/protocol}/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent (100%) rename {protocol => pkg/protocol}/bt/testdata/office.torrent (100%) rename {protocol => pkg/protocol}/bt/torrent/main/main.go (93%) rename {protocol => pkg/protocol}/bt/torrent/peer_conn.go (98%) rename {protocol => pkg/protocol}/bt/torrent/peer_conn_test.go (94%) rename {protocol => pkg/protocol}/bt/torrent/peer_pool.go (93%) rename {protocol => pkg/protocol}/bt/torrent/piece_state.go (97%) rename {protocol => pkg/protocol}/bt/torrent/torrent.go (96%) rename {protocol => pkg/protocol}/bt/torrent/torrent_test.go (95%) rename {protocol => pkg/protocol}/bt/tracker/errors.go (100%) rename {protocol => pkg/protocol}/bt/tracker/tracker.go (98%) rename {protocol => pkg/protocol}/bt/tracker/tracker_test.go (88%) rename {protocol => pkg/protocol}/http/client.go (100%) rename {protocol => pkg/protocol}/http/client_test.go (100%) rename {protocol => pkg/protocol}/http/request.go (100%) rename {protocol => pkg/protocol}/http/response.go (100%) rename {download/dltest => pkg/test}/httptest.go (94%) rename {download/dltest => pkg/test}/util.go (97%) rename {util => pkg/util}/bytefmt.go (100%) rename {util => pkg/util}/bytefmt_test.go (100%) rename {download/base => pkg/util}/timer.go (96%) diff --git a/README.md b/README.md index 342ee267..2bf72494 100644 --- a/README.md +++ b/README.md @@ -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区提出来。 \ No newline at end of file + +由于项目目前还未定型,代码可能随时有大的调整,所以暂不接受 PR,当然如果有什么好的想法可以在 issue 区提出来。 diff --git a/cmd/cli/gopeed.go b/cmd/cli/gopeed.go index 04f3d464..08abcdba 100644 --- a/cmd/cli/gopeed.go +++ b/cmd/cli/gopeed.go @@ -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() } diff --git a/download/base/event.go b/download/base/event.go deleted file mode 100644 index 02c555dd..00000000 --- a/download/base/event.go +++ /dev/null @@ -1,5 +0,0 @@ -package base - -type Event struct { - Status -} diff --git a/download/base/fetcher.go b/download/base/fetcher.go deleted file mode 100644 index 2dd879a8..00000000 --- a/download/base/fetcher.go +++ /dev/null @@ -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 -} diff --git a/download/http/model/extra.go b/download/http/model/extra.go deleted file mode 100644 index 48b5c4f1..00000000 --- a/download/http/model/extra.go +++ /dev/null @@ -1,7 +0,0 @@ -package model - -type Extra struct { - Method string - Header map[string]string - Body string -} diff --git a/download/base/controller.go b/internal/controller/controller.go similarity index 75% rename from download/base/controller.go rename to internal/controller/controller.go index 33a24f7e..cae683ca 100644 --- a/download/base/controller.go +++ b/internal/controller/controller.go @@ -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 -} diff --git a/internal/fetcher/fetcher.go b/internal/fetcher/fetcher.go new file mode 100644 index 00000000..eafd68e2 --- /dev/null +++ b/internal/fetcher/fetcher.go @@ -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 +} diff --git a/download/http/fetcher.go b/internal/protocol/http/fetcher.go similarity index 93% rename from download/http/fetcher.go rename to internal/protocol/http/fetcher.go index 4d4835f1..7f1a1fbf 100644 --- a/download/http/fetcher.go +++ b/internal/protocol/http/fetcher.go @@ -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 { diff --git a/download/http/fetcher_test.go b/internal/protocol/http/fetcher_test.go similarity index 73% rename from download/http/fetcher_test.go rename to internal/protocol/http/fetcher_test.go index 71ae6e64..eb807fd0 100644 --- a/download/http/fetcher_test.go +++ b/internal/protocol/http/fetcher_test.go @@ -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) } diff --git a/download/http/model/chunk.go b/internal/protocol/http/model.go similarity index 62% rename from download/http/model/chunk.go rename to internal/protocol/http/model.go index bd7de418..1219791d 100644 --- a/download/http/model/chunk.go +++ b/internal/protocol/http/model.go @@ -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 +} diff --git a/main.go b/main.go index e4e48729..77b32144 100644 --- a/main.go +++ b/main.go @@ -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() { diff --git a/download/base/constants.go b/pkg/base/constants.go similarity index 71% rename from download/base/constants.go rename to pkg/base/constants.go index 4de61146..6059a3e7 100644 --- a/download/base/constants.go +++ b/pkg/base/constants.go @@ -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 diff --git a/pkg/base/model.go b/pkg/base/model.go new file mode 100644 index 00000000..93bc9130 --- /dev/null +++ b/pkg/base/model.go @@ -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 +} diff --git a/download/downloader.go b/pkg/download/downloader.go similarity index 69% rename from download/downloader.go rename to pkg/download/downloader.go index ae864556..665aa2b4 100644 --- a/download/downloader.go +++ b/pkg/download/downloader.go @@ -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, + }) } diff --git a/download/downloader_test.go b/pkg/download/downloader_test.go similarity index 52% rename from download/downloader_test.go rename to pkg/download/downloader_test.go index 57ac7071..a9526a15 100644 --- a/download/downloader_test.go +++ b/pkg/download/downloader_test.go @@ -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) } diff --git a/pkg/download/event.go b/pkg/download/event.go new file mode 100644 index 00000000..19d65923 --- /dev/null +++ b/pkg/download/event.go @@ -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 +} diff --git a/protocol/bt/client.go b/pkg/protocol/bt/client.go similarity index 89% rename from protocol/bt/client.go rename to pkg/protocol/bt/client.go index 22b15304..17c907ee 100644 --- a/protocol/bt/client.go +++ b/pkg/protocol/bt/client.go @@ -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" ) diff --git a/protocol/bt/client_test.go b/pkg/protocol/bt/client_test.go similarity index 100% rename from protocol/bt/client_test.go rename to pkg/protocol/bt/client_test.go diff --git a/protocol/bt/dht/ping.go b/pkg/protocol/bt/dht/ping.go similarity index 100% rename from protocol/bt/dht/ping.go rename to pkg/protocol/bt/dht/ping.go diff --git a/protocol/bt/dht/ping_test.go b/pkg/protocol/bt/dht/ping_test.go similarity index 100% rename from protocol/bt/dht/ping_test.go rename to pkg/protocol/bt/dht/ping_test.go diff --git a/protocol/bt/metainfo/metainfo.go b/pkg/protocol/bt/metainfo/metainfo.go similarity index 100% rename from protocol/bt/metainfo/metainfo.go rename to pkg/protocol/bt/metainfo/metainfo.go diff --git a/protocol/bt/metainfo/metainfo_test.go b/pkg/protocol/bt/metainfo/metainfo_test.go similarity index 100% rename from protocol/bt/metainfo/metainfo_test.go rename to pkg/protocol/bt/metainfo/metainfo_test.go diff --git a/protocol/bt/peer/handshake.go b/pkg/protocol/bt/peer/handshake.go similarity index 100% rename from protocol/bt/peer/handshake.go rename to pkg/protocol/bt/peer/handshake.go diff --git a/protocol/bt/peer/message/message.go b/pkg/protocol/bt/peer/message/message.go similarity index 100% rename from protocol/bt/peer/message/message.go rename to pkg/protocol/bt/peer/message/message.go diff --git a/protocol/bt/peer/message/msg_bitfield.go b/pkg/protocol/bt/peer/message/msg_bitfield.go similarity index 100% rename from protocol/bt/peer/message/msg_bitfield.go rename to pkg/protocol/bt/peer/message/msg_bitfield.go diff --git a/protocol/bt/peer/message/msg_bitfield_test.go b/pkg/protocol/bt/peer/message/msg_bitfield_test.go similarity index 100% rename from protocol/bt/peer/message/msg_bitfield_test.go rename to pkg/protocol/bt/peer/message/msg_bitfield_test.go diff --git a/protocol/bt/peer/message/msg_choke.go b/pkg/protocol/bt/peer/message/msg_choke.go similarity index 100% rename from protocol/bt/peer/message/msg_choke.go rename to pkg/protocol/bt/peer/message/msg_choke.go diff --git a/protocol/bt/peer/message/msg_interested.go b/pkg/protocol/bt/peer/message/msg_interested.go similarity index 100% rename from protocol/bt/peer/message/msg_interested.go rename to pkg/protocol/bt/peer/message/msg_interested.go diff --git a/protocol/bt/peer/message/msg_keepalive.go b/pkg/protocol/bt/peer/message/msg_keepalive.go similarity index 100% rename from protocol/bt/peer/message/msg_keepalive.go rename to pkg/protocol/bt/peer/message/msg_keepalive.go diff --git a/protocol/bt/peer/message/msg_not_interested.go b/pkg/protocol/bt/peer/message/msg_not_interested.go similarity index 100% rename from protocol/bt/peer/message/msg_not_interested.go rename to pkg/protocol/bt/peer/message/msg_not_interested.go diff --git a/protocol/bt/peer/message/msg_piece.go b/pkg/protocol/bt/peer/message/msg_piece.go similarity index 100% rename from protocol/bt/peer/message/msg_piece.go rename to pkg/protocol/bt/peer/message/msg_piece.go diff --git a/protocol/bt/peer/message/msg_request.go b/pkg/protocol/bt/peer/message/msg_request.go similarity index 100% rename from protocol/bt/peer/message/msg_request.go rename to pkg/protocol/bt/peer/message/msg_request.go diff --git a/protocol/bt/peer/message/msg_unchoke.go b/pkg/protocol/bt/peer/message/msg_unchoke.go similarity index 100% rename from protocol/bt/peer/message/msg_unchoke.go rename to pkg/protocol/bt/peer/message/msg_unchoke.go diff --git a/protocol/bt/peer/peer.go b/pkg/protocol/bt/peer/peer.go similarity index 100% rename from protocol/bt/peer/peer.go rename to pkg/protocol/bt/peer/peer.go diff --git a/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent b/pkg/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent similarity index 100% rename from protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent rename to pkg/protocol/bt/testdata/Game.of.Thrones.S08E05.720p.WEB.H264-MEMENTO.torrent diff --git a/protocol/bt/testdata/office.torrent b/pkg/protocol/bt/testdata/office.torrent similarity index 100% rename from protocol/bt/testdata/office.torrent rename to pkg/protocol/bt/testdata/office.torrent diff --git a/protocol/bt/torrent/main/main.go b/pkg/protocol/bt/torrent/main/main.go similarity index 93% rename from protocol/bt/torrent/main/main.go rename to pkg/protocol/bt/torrent/main/main.go index be974b91..7bb0e09b 100644 --- a/protocol/bt/torrent/main/main.go +++ b/pkg/protocol/bt/torrent/main/main.go @@ -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" ) diff --git a/protocol/bt/torrent/peer_conn.go b/pkg/protocol/bt/torrent/peer_conn.go similarity index 98% rename from protocol/bt/torrent/peer_conn.go rename to pkg/protocol/bt/torrent/peer_conn.go index 457aa267..e90a574a 100644 --- a/protocol/bt/torrent/peer_conn.go +++ b/pkg/protocol/bt/torrent/peer_conn.go @@ -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" ) diff --git a/protocol/bt/torrent/peer_conn_test.go b/pkg/protocol/bt/torrent/peer_conn_test.go similarity index 94% rename from protocol/bt/torrent/peer_conn_test.go rename to pkg/protocol/bt/torrent/peer_conn_test.go index f5f76cbb..07e38bd2 100644 --- a/protocol/bt/torrent/peer_conn_test.go +++ b/pkg/protocol/bt/torrent/peer_conn_test.go @@ -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" diff --git a/protocol/bt/torrent/peer_pool.go b/pkg/protocol/bt/torrent/peer_pool.go similarity index 93% rename from protocol/bt/torrent/peer_pool.go rename to pkg/protocol/bt/torrent/peer_pool.go index bcbffd57..c126d8d4 100644 --- a/protocol/bt/torrent/peer_pool.go +++ b/pkg/protocol/bt/torrent/peer_pool.go @@ -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 { diff --git a/protocol/bt/torrent/piece_state.go b/pkg/protocol/bt/torrent/piece_state.go similarity index 97% rename from protocol/bt/torrent/piece_state.go rename to pkg/protocol/bt/torrent/piece_state.go index a6614cd7..82133735 100644 --- a/protocol/bt/torrent/piece_state.go +++ b/pkg/protocol/bt/torrent/piece_state.go @@ -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" diff --git a/protocol/bt/torrent/torrent.go b/pkg/protocol/bt/torrent/torrent.go similarity index 96% rename from protocol/bt/torrent/torrent.go rename to pkg/protocol/bt/torrent/torrent.go index 39c9c3df..f11c3a14 100644 --- a/protocol/bt/torrent/torrent.go +++ b/pkg/protocol/bt/torrent/torrent.go @@ -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" ) diff --git a/protocol/bt/torrent/torrent_test.go b/pkg/protocol/bt/torrent/torrent_test.go similarity index 95% rename from protocol/bt/torrent/torrent_test.go rename to pkg/protocol/bt/torrent/torrent_test.go index 3c796f3e..2690ce33 100644 --- a/protocol/bt/torrent/torrent_test.go +++ b/pkg/protocol/bt/torrent/torrent_test.go @@ -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" ) diff --git a/protocol/bt/tracker/errors.go b/pkg/protocol/bt/tracker/errors.go similarity index 100% rename from protocol/bt/tracker/errors.go rename to pkg/protocol/bt/tracker/errors.go diff --git a/protocol/bt/tracker/tracker.go b/pkg/protocol/bt/tracker/tracker.go similarity index 98% rename from protocol/bt/tracker/tracker.go rename to pkg/protocol/bt/tracker/tracker.go index fc740a46..048f832a 100644 --- a/protocol/bt/tracker/tracker.go +++ b/pkg/protocol/bt/tracker/tracker.go @@ -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 ( diff --git a/protocol/bt/tracker/tracker_test.go b/pkg/protocol/bt/tracker/tracker_test.go similarity index 88% rename from protocol/bt/tracker/tracker_test.go rename to pkg/protocol/bt/tracker/tracker_test.go index 75fe9dae..56e30c98 100644 --- a/protocol/bt/tracker/tracker_test.go +++ b/pkg/protocol/bt/tracker/tracker_test.go @@ -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" ) diff --git a/protocol/http/client.go b/pkg/protocol/http/client.go similarity index 100% rename from protocol/http/client.go rename to pkg/protocol/http/client.go diff --git a/protocol/http/client_test.go b/pkg/protocol/http/client_test.go similarity index 100% rename from protocol/http/client_test.go rename to pkg/protocol/http/client_test.go diff --git a/protocol/http/request.go b/pkg/protocol/http/request.go similarity index 100% rename from protocol/http/request.go rename to pkg/protocol/http/request.go diff --git a/protocol/http/response.go b/pkg/protocol/http/response.go similarity index 100% rename from protocol/http/response.go rename to pkg/protocol/http/response.go diff --git a/download/dltest/httptest.go b/pkg/test/httptest.go similarity index 94% rename from download/dltest/httptest.go rename to pkg/test/httptest.go index 5f069a02..1f64f1b6 100644 --- a/download/dltest/httptest.go +++ b/pkg/test/httptest.go @@ -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)) }) } diff --git a/download/dltest/util.go b/pkg/test/util.go similarity index 97% rename from download/dltest/util.go rename to pkg/test/util.go index 26ec6148..588b9b2b 100644 --- a/download/dltest/util.go +++ b/pkg/test/util.go @@ -1,4 +1,4 @@ -package dltest +package test import ( "crypto/md5" diff --git a/util/bytefmt.go b/pkg/util/bytefmt.go similarity index 100% rename from util/bytefmt.go rename to pkg/util/bytefmt.go diff --git a/util/bytefmt_test.go b/pkg/util/bytefmt_test.go similarity index 100% rename from util/bytefmt_test.go rename to pkg/util/bytefmt_test.go diff --git a/download/base/timer.go b/pkg/util/timer.go similarity index 96% rename from download/base/timer.go rename to pkg/util/timer.go index 2f3bf617..017029d5 100644 --- a/download/base/timer.go +++ b/pkg/util/timer.go @@ -1,4 +1,4 @@ -package base +package util import "time"