Skip to content

Commit

Permalink
feat: reuse fetcher (GopeedLab#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
monkeyWie authored Feb 6, 2023
1 parent 17cb59c commit 1a3c8c0
Show file tree
Hide file tree
Showing 36 changed files with 474 additions and 296 deletions.
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Gopeed contributors guide

Firstly, thank you for your interest in contributing to Gopeed. This guide will help you better participate in the development of Gopeed.

## Branch description

This project only has one main branch, namely the `main` branch. If you want to participate in the development of Gopeed, please fork this project first, and then develop in your fork project. After development is completed, submit a PR to this project and merge it into the `main` branch.

## Local development

It is recommended to develop and debug through the web. First, start the backend service, the code is located in `cmd/api/main.go`, the default port of the service is `9999`, and then start the front-end project in `debug` mode to run.

## Translation

The internationalization files of Gopeed are located in the `ui/flutter/lib/i18n/langs` directory. You only need to add the corresponding language file in this directory.

> Tip: Please refer to `en_us.dart` for translation.
17 changes: 17 additions & 0 deletions CONTRIBUTING_zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Gopeed 贡献指南

首先感谢您对贡献代码感兴趣,这份指南将帮助您更好的参与到 Gopeed 的开发中来。

## 分支说明

本项目只有一个主分支,即 `main` 分支,如果您想要参与到 Gopeed 的开发中来,请先 fork 本项目,然后在您的 fork 项目中进行开发,开发完成后再向本项目提交 PR,合并到 `main` 分支。

## 本地开发

建议通过 web 端进行开发调试,首先启动后端服务,代码位于 `cmd/api/main.go` ,服务启动默认端口为 `9999`,然后以 `debug` 模式启动前端项目即可运行。

## 翻译

Gopeed 的国际化文件位于 `ui/flutter/lib/i18n/langs` 目录下,只需要在该目录下添加对应的语言文件即可。

> 请注意以 `en_us.dart` 为参照进行翻译。
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ This project is divided into two parts, the front end uses `flutter`, the back e
git clone git@github.com:monkeyWie/gopeed.git
```

### Contributing

Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md)

### Build

#### Desktop
Expand Down Expand Up @@ -144,8 +148,6 @@ flutter build apk

#### Web

Web platform communicates directly with the backend http server, no additional environment is required.

command:

```bash
Expand Down
8 changes: 5 additions & 3 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ docker-compose up -d
git clone git@github.com:monkeyWie/gopeed.git
```

### 贡献代码

请参考[贡献指南](CONTRIBUTING_zh-CN.md)

### 编译

#### 桌面端
Expand Down Expand Up @@ -142,9 +146,7 @@ cd ui/flutter
flutter build apk
```

#### Web 端(推荐本地调试使用)

Web 端直接与后端 http 服务通讯,不需要额外准备环境。
#### Web 端

构建命令:

Expand Down
4 changes: 2 additions & 2 deletions cmd/gopeed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ var (
)

func printProgress(task *download.Task, title string) {
rate := float64(task.Progress.Downloaded) / float64(task.Res.Size)
rate := float64(task.Progress.Downloaded) / float64(task.Meta.Res.Size)
completeWidth := int(progressWidth * rate)
speed := util.ByteFmt(task.Progress.Speed)
totalSize := util.ByteFmt(task.Res.Size)
totalSize := util.ByteFmt(task.Meta.Res.Size)
sb.WriteString(fmt.Sprintf("\r%s [", title))
for i := 0; i < progressWidth; i++ {
if i < completeWidth {
Expand Down
27 changes: 19 additions & 8 deletions internal/fetcher/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,34 @@ import (
)

// Fetcher defines the interface for a download protocol.
// One fetcher for each download task
type Fetcher interface {
// Name return the name of the protocol.
Name() string

Setup(ctl *controller.Controller) error
// Resolve resource info from request
Resolve(req *base.Request) (res *base.Resource, err error)
Resolve(req *base.Request) error
// Create ready to download, but not started
Create(res *base.Resource, opts *base.Options) (err error)
Start() (err error)
Pause() (err error)
Continue() (err error)
Close() (err error)
Create(opts *base.Options) error
Start() error
Pause() error
Continue() error
Close() error

// Meta returns the meta information of the download.
Meta() *FetcherMeta
// Progress returns the progress of the download.
Progress() Progress
// Wait for the download to complete, this method will block until the download is done.
Wait() (err error)
Wait() error
}

// FetcherMeta defines the meta information of a fetcher.
type FetcherMeta struct {
Req *base.Request `json:"req"`
Res *base.Resource `json:"res"`
Opts *base.Options `json:"opts"`
}

// FetcherBuilder defines the interface for a fetcher builder.
Expand All @@ -36,11 +46,12 @@ type FetcherBuilder interface {
// Store fetcher
Store(fetcher Fetcher) (any, error)
// Restore fetcher
Restore() (v any, f func(res *base.Resource, opts *base.Options, v any) Fetcher)
Restore() (v any, f func(meta *FetcherMeta, v any) Fetcher)
}

type DefaultFetcher struct {
Ctl *controller.Controller
Meta *FetcherMeta
DoneCh chan error
}

Expand Down
131 changes: 68 additions & 63 deletions internal/protocol/bt/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import (
var client *torrent.Client

type Fetcher struct {
Ctl *controller.Controller
ctl *controller.Controller

torrent *torrent.Torrent
res *base.Resource
opts *base.Options
meta *fetcher.FetcherMeta

ready atomic.Bool
progress fetcher.Progress
Expand All @@ -34,7 +33,10 @@ func (f *Fetcher) Name() string {
}

func (f *Fetcher) Setup(ctl *controller.Controller) (err error) {
f.Ctl = ctl
f.ctl = ctl
if f.meta == nil {
f.meta = &fetcher.FetcherMeta{}
}
var once sync.Once
once.Do(func() {
cfg := torrent.NewDefaultClientConfig()
Expand Down Expand Up @@ -66,46 +68,12 @@ func (f *Fetcher) Setup(ctl *controller.Controller) (err error) {
return
}

func (f *Fetcher) Wait() (err error) {
for {
if f.ready.Load() {
done := true
for _, selectIndex := range f.opts.SelectFiles {
file := f.torrent.Files()[selectIndex]
if file.BytesCompleted() < file.Length() {
done = false
break
}
}
if done {
// remove unselected files
for i, file := range f.torrent.Files() {
selected := false
for _, selectIndex := range f.opts.SelectFiles {
if i == selectIndex {
selected = true
break
}
}
if !selected {
util.SafeRemove(filepath.Join(f.opts.Path, file.Path()))
}
}
break
}
}
time.Sleep(time.Millisecond * 500)
}
return nil
}

func (f *Fetcher) Resolve(req *base.Request) (res *base.Resource, err error) {
if err = f.addTorrent(req.URL, true); err != nil {
return
func (f *Fetcher) Resolve(req *base.Request) error {
if err := f.addTorrent(req.URL); err != nil {
return err
}
defer f.torrent.Drop()
res = &base.Resource{
Req: req,
res := &base.Resource{
Name: f.torrent.Name(),
Range: true,
Files: make([]*base.FileInfo, len(f.torrent.Files())),
Expand All @@ -119,36 +87,39 @@ func (f *Fetcher) Resolve(req *base.Request) (res *base.Resource, err error) {
}
res.Size += file.Length()
}
return
f.meta.Req = req
f.meta.Res = res
return nil
}

func (f *Fetcher) Create(res *base.Resource, opts *base.Options) (err error) {
f.res = res
f.opts = opts
if len(f.opts.SelectFiles) == 0 {
f.opts.SelectFiles = make([]int, 0)
func (f *Fetcher) Create(opts *base.Options) (err error) {
f.meta.Opts = opts
if len(opts.SelectFiles) == 0 {
opts.SelectFiles = make([]int, 0)
for i := range f.torrent.Files() {
f.opts.SelectFiles = append(f.opts.SelectFiles, i)
opts.SelectFiles = append(opts.SelectFiles, i)
}
}
if f.opts.Path != "" {
f.torrentPaths[res.Hash] = f.opts.Path
if opts.Path != "" {
f.torrentPaths[f.meta.Res.Hash] = f.meta.Opts.Path
}

f.progress = make(fetcher.Progress, len(f.opts.SelectFiles))
f.progress = make(fetcher.Progress, len(f.meta.Opts.SelectFiles))
f.ready.Store(false)
return nil
}

func (f *Fetcher) Start() (err error) {
if err = f.addTorrent(f.res.Req.URL, false); err != nil {
return
if f.torrent == nil {
if err = f.addTorrent(f.meta.Req.URL); err != nil {
return
}
}
files := f.torrent.Files()
if len(f.opts.SelectFiles) == len(files) {
if len(f.meta.Opts.SelectFiles) == len(files) {
f.torrent.DownloadAll()
} else {
for _, selectIndex := range f.opts.SelectFiles {
for _, selectIndex := range f.meta.Opts.SelectFiles {
file := files[selectIndex]
file.Download()
}
Expand All @@ -171,19 +142,56 @@ func (f *Fetcher) Close() (err error) {
return nil
}

func (f *Fetcher) Wait() (err error) {
for {
if f.ready.Load() {
done := true
for _, selectIndex := range f.meta.Opts.SelectFiles {
file := f.torrent.Files()[selectIndex]
if file.BytesCompleted() < file.Length() {
done = false
break
}
}
if done {
// remove unselected files
for i, file := range f.torrent.Files() {
selected := false
for _, selectIndex := range f.meta.Opts.SelectFiles {
if i == selectIndex {
selected = true
break
}
}
if !selected {
util.SafeRemove(filepath.Join(f.meta.Opts.Path, file.Path()))
}
}
break
}
}
time.Sleep(time.Millisecond * 500)
}
return nil
}

func (f *Fetcher) Meta() *fetcher.FetcherMeta {
return f.meta
}

func (f *Fetcher) Progress() fetcher.Progress {
if !f.ready.Load() {
return f.progress
}
for i := range f.progress {
selectIndex := f.opts.SelectFiles[i]
selectIndex := f.meta.Opts.SelectFiles[i]
file := f.torrent.Files()[selectIndex]
f.progress[i] = file.BytesCompleted()
}
return f.progress
}

func (f *Fetcher) addTorrent(url string, resolve bool) (err error) {
func (f *Fetcher) addTorrent(url string) (err error) {
schema := util.ParseSchema(url)
if schema == "MAGNET" {
f.torrent, err = client.AddMagnet(url)
Expand All @@ -194,7 +202,7 @@ func (f *Fetcher) addTorrent(url string, resolve bool) (err error) {
return
}
var cfg config
exist, err := f.Ctl.GetConfig(&cfg)
exist, err := f.ctl.GetConfig(&cfg)
if err != nil {
return
}
Expand All @@ -206,9 +214,6 @@ func (f *Fetcher) addTorrent(url string, resolve bool) (err error) {
f.torrent.AddTrackers(announceList)
}
<-f.torrent.GotInfo()
if resolve {
return
}
f.ready.Store(true)
return
}
Expand All @@ -230,6 +235,6 @@ func (fb *FetcherBuilder) Store(f fetcher.Fetcher) (data any, err error) {
return nil, nil
}

func (fb *FetcherBuilder) Restore() (v any, f func(res *base.Resource, opts *base.Options, v any) fetcher.Fetcher) {
func (fb *FetcherBuilder) Restore() (v any, f func(meta *fetcher.FetcherMeta, v any) fetcher.Fetcher) {
return nil, nil
}
7 changes: 3 additions & 4 deletions internal/protocol/bt/fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ func TestFetcher_Config(t *testing.T) {
}

func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
res, err := fetcher.Resolve(&base.Request{
err := fetcher.Resolve(&base.Request{
URL: "./testdata/ubuntu-22.04-live-server-amd64.iso.torrent",
})
if err != nil {
panic(err)
}

want := &base.Resource{
Req: res.Req,
Name: "ubuntu-22.04-live-server-amd64.iso",
Size: 1466714112,
Range: true,
Expand All @@ -39,8 +38,8 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
},
Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed",
}
if !reflect.DeepEqual(want, res) {
t.Errorf("Resolve() got = %v, want %v", res, want)
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want)
}
}

Expand Down
Loading

0 comments on commit 1a3c8c0

Please sign in to comment.