feat(thunder): add offline download tool (#7673)
* feat(thunder): add offline download tool * fix(thunder): improve error handling and parse file size in status response --------- Co-authored-by: Andy Hsu <i@nn.ci>
This commit is contained in:
@ -6,5 +6,6 @@ import (
|
||||
_ "github.com/alist-org/alist/v3/internal/offline_download/http"
|
||||
_ "github.com/alist-org/alist/v3/internal/offline_download/pikpak"
|
||||
_ "github.com/alist-org/alist/v3/internal/offline_download/qbit"
|
||||
_ "github.com/alist-org/alist/v3/internal/offline_download/thunder"
|
||||
_ "github.com/alist-org/alist/v3/internal/offline_download/transmission"
|
||||
)
|
||||
|
126
internal/offline_download/thunder/thunder.go
Normal file
126
internal/offline_download/thunder/thunder.go
Normal file
@ -0,0 +1,126 @@
|
||||
package thunder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/alist-org/alist/v3/drivers/thunder"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/offline_download/tool"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
)
|
||||
|
||||
type Thunder struct {
|
||||
refreshTaskCache bool
|
||||
}
|
||||
|
||||
func (t *Thunder) Name() string {
|
||||
return "thunder"
|
||||
}
|
||||
|
||||
func (t *Thunder) Items() []model.SettingItem {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thunder) Run(task *tool.DownloadTask) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (t *Thunder) Init() (string, error) {
|
||||
t.refreshTaskCache = false
|
||||
return "ok", nil
|
||||
}
|
||||
|
||||
func (t *Thunder) IsReady() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Thunder) AddURL(args *tool.AddUrlArgs) (string, error) {
|
||||
// 添加新任务刷新缓存
|
||||
t.refreshTaskCache = true
|
||||
// args.TempDir 已经被修改为了 DstDirPath
|
||||
storage, actualPath, err := op.GetStorageAndActualPath(args.TempDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
thunderDriver, ok := storage.(*thunder.Thunder)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unsupported storage driver for offline download, only Thunder is supported")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
parentDir, err := op.GetUnwrap(ctx, storage, actualPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
task, err := thunderDriver.OfflineDownload(ctx, args.Url, parentDir, "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to add offline download task: %w", err)
|
||||
}
|
||||
|
||||
return task.ID, nil
|
||||
}
|
||||
|
||||
func (t *Thunder) Remove(task *tool.DownloadTask) error {
|
||||
storage, _, err := op.GetStorageAndActualPath(task.DstDirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
thunderDriver, ok := storage.(*thunder.Thunder)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported storage driver for offline download, only Thunder is supported")
|
||||
}
|
||||
ctx := context.Background()
|
||||
err = thunderDriver.DeleteOfflineTasks(ctx, []string{task.GID}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thunder) Status(task *tool.DownloadTask) (*tool.Status, error) {
|
||||
storage, _, err := op.GetStorageAndActualPath(task.DstDirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
thunderDriver, ok := storage.(*thunder.Thunder)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported storage driver for offline download, only Thunder is supported")
|
||||
}
|
||||
tasks, err := t.GetTasks(thunderDriver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &tool.Status{
|
||||
Progress: 0,
|
||||
NewGID: "",
|
||||
Completed: false,
|
||||
Status: "the task has been deleted",
|
||||
Err: nil,
|
||||
}
|
||||
for _, t := range tasks {
|
||||
if t.ID == task.GID {
|
||||
s.Progress = float64(t.Progress)
|
||||
s.Status = t.Message
|
||||
s.Completed = (t.Phase == "PHASE_TYPE_COMPLETE")
|
||||
s.TotalBytes, err = strconv.ParseInt(t.FileSize, 10, 64)
|
||||
if err != nil {
|
||||
s.TotalBytes = 0
|
||||
}
|
||||
if t.Phase == "PHASE_TYPE_ERROR" {
|
||||
s.Err = errors.New(t.Message)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
s.Err = fmt.Errorf("the task has been deleted")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
tool.Tools.Add(&Thunder{})
|
||||
}
|
42
internal/offline_download/thunder/util.go
Normal file
42
internal/offline_download/thunder/util.go
Normal file
@ -0,0 +1,42 @@
|
||||
package thunder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/Xhofe/go-cache"
|
||||
"github.com/alist-org/alist/v3/drivers/thunder"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/pkg/singleflight"
|
||||
)
|
||||
|
||||
var taskCache = cache.NewMemCache(cache.WithShards[[]thunder.OfflineTask](16))
|
||||
var taskG singleflight.Group[[]thunder.OfflineTask]
|
||||
|
||||
func (t *Thunder) GetTasks(thunderDriver *thunder.Thunder) ([]thunder.OfflineTask, error) {
|
||||
key := op.Key(thunderDriver, "/drive/v1/task")
|
||||
if !t.refreshTaskCache {
|
||||
if tasks, ok := taskCache.Get(key); ok {
|
||||
return tasks, nil
|
||||
}
|
||||
}
|
||||
t.refreshTaskCache = false
|
||||
tasks, err, _ := taskG.Do(key, func() ([]thunder.OfflineTask, error) {
|
||||
ctx := context.Background()
|
||||
tasks, err := thunderDriver.OfflineList(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 添加缓存 10s
|
||||
if len(tasks) > 0 {
|
||||
taskCache.Set(key, tasks, cache.WithEx[[]thunder.OfflineTask](time.Second*10))
|
||||
} else {
|
||||
taskCache.Del(key)
|
||||
}
|
||||
return tasks, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
@ -77,6 +77,10 @@ func AddURL(ctx context.Context, args *AddURLArgs) (task.TaskExtensionInfo, erro
|
||||
tempDir = args.DstDirPath
|
||||
// 防止将下载好的文件删除
|
||||
deletePolicy = DeleteNever
|
||||
case "thunder":
|
||||
tempDir = args.DstDirPath
|
||||
// 防止将下载好的文件删除
|
||||
deletePolicy = DeleteNever
|
||||
}
|
||||
|
||||
taskCreator, _ := ctx.Value("user").(*model.User) // taskCreator is nil when convert failed
|
||||
|
@ -83,6 +83,9 @@ outer:
|
||||
if t.tool.Name() == "pikpak" {
|
||||
return nil
|
||||
}
|
||||
if t.tool.Name() == "thunder" {
|
||||
return nil
|
||||
}
|
||||
if t.tool.Name() == "115 Cloud" {
|
||||
// hack for 115
|
||||
<-time.After(time.Second * 1)
|
||||
@ -161,6 +164,9 @@ func (t *DownloadTask) Complete() error {
|
||||
if t.tool.Name() == "pikpak" {
|
||||
return nil
|
||||
}
|
||||
if t.tool.Name() == "thunder" {
|
||||
return nil
|
||||
}
|
||||
if t.tool.Name() == "115 Cloud" {
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user