Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/offline_download/tool/download.go
1562 views
1
package tool
2
3
import (
4
"fmt"
5
"time"
6
7
"github.com/alist-org/alist/v3/internal/conf"
8
"github.com/alist-org/alist/v3/internal/errs"
9
"github.com/alist-org/alist/v3/internal/setting"
10
"github.com/alist-org/alist/v3/internal/task"
11
"github.com/pkg/errors"
12
log "github.com/sirupsen/logrus"
13
"github.com/xhofe/tache"
14
)
15
16
type DownloadTask struct {
17
task.TaskExtension
18
Url string `json:"url"`
19
DstDirPath string `json:"dst_dir_path"`
20
TempDir string `json:"temp_dir"`
21
DeletePolicy DeletePolicy `json:"delete_policy"`
22
Toolname string `json:"toolname"`
23
Status string `json:"-"`
24
Signal chan int `json:"-"`
25
GID string `json:"-"`
26
tool Tool
27
callStatusRetried int
28
}
29
30
func (t *DownloadTask) Run() error {
31
t.ReinitCtx()
32
t.ClearEndTime()
33
t.SetStartTime(time.Now())
34
defer func() { t.SetEndTime(time.Now()) }()
35
if t.tool == nil {
36
tool, err := Tools.Get(t.Toolname)
37
if err != nil {
38
return errors.WithMessage(err, "failed get tool")
39
}
40
t.tool = tool
41
}
42
if err := t.tool.Run(t); !errs.IsNotSupportError(err) {
43
if err == nil {
44
return t.Transfer()
45
}
46
return err
47
}
48
t.Signal = make(chan int)
49
defer func() {
50
t.Signal = nil
51
}()
52
gid, err := t.tool.AddURL(&AddUrlArgs{
53
Url: t.Url,
54
UID: t.ID,
55
TempDir: t.TempDir,
56
Signal: t.Signal,
57
})
58
if err != nil {
59
return err
60
}
61
t.GID = gid
62
var ok bool
63
outer:
64
for {
65
select {
66
case <-t.CtxDone():
67
err := t.tool.Remove(t)
68
return err
69
case <-t.Signal:
70
ok, err = t.Update()
71
if ok {
72
break outer
73
}
74
case <-time.After(time.Second * 3):
75
ok, err = t.Update()
76
if ok {
77
break outer
78
}
79
}
80
}
81
if err != nil {
82
return err
83
}
84
if t.tool.Name() == "Pikpak" {
85
return nil
86
}
87
if t.tool.Name() == "Thunder" {
88
return nil
89
}
90
if t.tool.Name() == "115 Cloud" {
91
// hack for 115
92
<-time.After(time.Second * 1)
93
err := t.tool.Remove(t)
94
if err != nil {
95
log.Errorln(err.Error())
96
}
97
return nil
98
}
99
t.Status = "offline download completed, maybe transferring"
100
// hack for qBittorrent
101
if t.tool.Name() == "qBittorrent" {
102
seedTime := setting.GetInt(conf.QbittorrentSeedtime, 0)
103
if seedTime >= 0 {
104
t.Status = "offline download completed, waiting for seeding"
105
<-time.After(time.Minute * time.Duration(seedTime))
106
err := t.tool.Remove(t)
107
if err != nil {
108
log.Errorln(err.Error())
109
}
110
}
111
}
112
113
if t.tool.Name() == "Transmission" {
114
// hack for transmission
115
seedTime := setting.GetInt(conf.TransmissionSeedtime, 0)
116
if seedTime >= 0 {
117
t.Status = "offline download completed, waiting for seeding"
118
<-time.After(time.Minute * time.Duration(seedTime))
119
err := t.tool.Remove(t)
120
if err != nil {
121
log.Errorln(err.Error())
122
}
123
}
124
}
125
return nil
126
}
127
128
// Update download status, return true if download completed
129
func (t *DownloadTask) Update() (bool, error) {
130
info, err := t.tool.Status(t)
131
if err != nil {
132
t.callStatusRetried++
133
log.Errorf("failed to get status of %s, retried %d times", t.ID, t.callStatusRetried)
134
return false, nil
135
}
136
if t.callStatusRetried > 5 {
137
return true, errors.Errorf("failed to get status of %s, retried %d times", t.ID, t.callStatusRetried)
138
}
139
t.callStatusRetried = 0
140
t.SetProgress(info.Progress)
141
t.SetTotalBytes(info.TotalBytes)
142
t.Status = fmt.Sprintf("[%s]: %s", t.tool.Name(), info.Status)
143
if info.NewGID != "" {
144
log.Debugf("followen by: %+v", info.NewGID)
145
t.GID = info.NewGID
146
return false, nil
147
}
148
// if download completed
149
if info.Completed {
150
err := t.Transfer()
151
return true, errors.WithMessage(err, "failed to transfer file")
152
}
153
// if download failed
154
if info.Err != nil {
155
return true, errors.Errorf("failed to download %s, error: %s", t.ID, info.Err.Error())
156
}
157
return false, nil
158
}
159
160
func (t *DownloadTask) Transfer() error {
161
toolName := t.tool.Name()
162
if toolName == "115 Cloud" || toolName == "PikPak" || toolName == "Thunder" {
163
// 如果不是直接下载到目标路径,则进行转存
164
if t.TempDir != t.DstDirPath {
165
return transferObj(t.Ctx(), t.TempDir, t.DstDirPath, t.DeletePolicy)
166
}
167
return nil
168
}
169
return transferStd(t.Ctx(), t.TempDir, t.DstDirPath, t.DeletePolicy)
170
}
171
172
func (t *DownloadTask) GetName() string {
173
return fmt.Sprintf("download %s to (%s)", t.Url, t.DstDirPath)
174
}
175
176
func (t *DownloadTask) GetStatus() string {
177
return t.Status
178
}
179
180
var DownloadTaskManager *tache.Manager[*DownloadTask]
181
182