Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/terabox/driver.go
1520 views
1
package terabox
2
3
import (
4
"bytes"
5
"context"
6
"crypto/md5"
7
"encoding/hex"
8
"fmt"
9
"io"
10
"math"
11
stdpath "path"
12
"strconv"
13
14
"github.com/alist-org/alist/v3/drivers/base"
15
"github.com/alist-org/alist/v3/pkg/utils"
16
log "github.com/sirupsen/logrus"
17
18
"github.com/alist-org/alist/v3/internal/driver"
19
"github.com/alist-org/alist/v3/internal/model"
20
)
21
22
type Terabox struct {
23
model.Storage
24
Addition
25
JsToken string
26
url_domain_prefix string
27
base_url string
28
}
29
30
func (d *Terabox) Config() driver.Config {
31
return config
32
}
33
34
func (d *Terabox) GetAddition() driver.Additional {
35
return &d.Addition
36
}
37
38
func (d *Terabox) Init(ctx context.Context) error {
39
var resp CheckLoginResp
40
d.base_url = "https://www.terabox.com"
41
d.url_domain_prefix = "jp"
42
_, err := d.get("/api/check/login", nil, &resp)
43
if err != nil {
44
return err
45
}
46
if resp.Errno != 0 {
47
if resp.Errno == 9000 {
48
return fmt.Errorf("terabox is not yet available in this area")
49
}
50
return fmt.Errorf("failed to check login status according to cookie")
51
}
52
return err
53
}
54
55
func (d *Terabox) Drop(ctx context.Context) error {
56
return nil
57
}
58
59
func (d *Terabox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
60
files, err := d.getFiles(dir.GetPath())
61
if err != nil {
62
return nil, err
63
}
64
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
65
return fileToObj(src), nil
66
})
67
}
68
69
func (d *Terabox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
70
if d.DownloadAPI == "crack" {
71
return d.linkCrack(file, args)
72
}
73
return d.linkOfficial(file, args)
74
}
75
76
func (d *Terabox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
77
params := map[string]string{
78
"a": "commit",
79
}
80
data := map[string]string{
81
"path": stdpath.Join(parentDir.GetPath(), dirName),
82
"isdir": "1",
83
"block_list": "[]",
84
}
85
res, err := d.post_form("/api/create", params, data, nil)
86
log.Debugln(string(res))
87
return err
88
}
89
90
func (d *Terabox) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
91
data := []base.Json{
92
{
93
"path": srcObj.GetPath(),
94
"dest": dstDir.GetPath(),
95
"newname": srcObj.GetName(),
96
},
97
}
98
_, err := d.manage("move", data)
99
return err
100
}
101
102
func (d *Terabox) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
103
data := []base.Json{
104
{
105
"path": srcObj.GetPath(),
106
"newname": newName,
107
},
108
}
109
_, err := d.manage("rename", data)
110
return err
111
}
112
113
func (d *Terabox) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
114
data := []base.Json{
115
{
116
"path": srcObj.GetPath(),
117
"dest": dstDir.GetPath(),
118
"newname": srcObj.GetName(),
119
},
120
}
121
_, err := d.manage("copy", data)
122
return err
123
}
124
125
func (d *Terabox) Remove(ctx context.Context, obj model.Obj) error {
126
data := []string{obj.GetPath()}
127
_, err := d.manage("delete", data)
128
return err
129
}
130
131
func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
132
resp, err := base.RestyClient.R().
133
SetContext(ctx).
134
Get("https://" + d.url_domain_prefix + "-data.terabox.com/rest/2.0/pcs/file?method=locateupload")
135
if err != nil {
136
return err
137
}
138
var locateupload_resp LocateUploadResp
139
err = utils.Json.Unmarshal(resp.Body(), &locateupload_resp)
140
if err != nil {
141
log.Debugln(resp)
142
return err
143
}
144
log.Debugln(locateupload_resp)
145
146
// precreate file
147
rawPath := stdpath.Join(dstDir.GetPath(), stream.GetName())
148
path := encodeURIComponent(rawPath)
149
150
var precreateBlockListStr string
151
if stream.GetSize() > initialChunkSize {
152
precreateBlockListStr = `["5910a591dd8fc18c32a8f3df4fdc1761","a5fc157d78e6ad1c7e114b056c92821e"]`
153
} else {
154
precreateBlockListStr = `["5910a591dd8fc18c32a8f3df4fdc1761"]`
155
}
156
157
data := map[string]string{
158
"path": rawPath,
159
"autoinit": "1",
160
"target_path": dstDir.GetPath(),
161
"block_list": precreateBlockListStr,
162
"local_mtime": strconv.FormatInt(stream.ModTime().Unix(), 10),
163
"file_limit_switch_v34": "true",
164
}
165
var precreateResp PrecreateResp
166
log.Debugln(data)
167
res, err := d.post_form("/api/precreate", nil, data, &precreateResp)
168
if err != nil {
169
return err
170
}
171
log.Debugf("%+v", precreateResp)
172
if precreateResp.Errno != 0 {
173
log.Debugln(string(res))
174
return fmt.Errorf("[terabox] failed to precreate file, errno: %d", precreateResp.Errno)
175
}
176
if precreateResp.ReturnType == 2 {
177
return nil
178
}
179
180
// upload chunks
181
tempFile, err := stream.CacheFullInTempFile()
182
if err != nil {
183
return err
184
}
185
186
params := map[string]string{
187
"method": "upload",
188
"path": path,
189
"uploadid": precreateResp.Uploadid,
190
"app_id": "250528",
191
"web": "1",
192
"channel": "dubox",
193
"clienttype": "0",
194
}
195
196
streamSize := stream.GetSize()
197
chunkSize := calculateChunkSize(streamSize)
198
chunkByteData := make([]byte, chunkSize)
199
count := int(math.Ceil(float64(streamSize) / float64(chunkSize)))
200
left := streamSize
201
uploadBlockList := make([]string, 0, count)
202
h := md5.New()
203
for partseq := 0; partseq < count; partseq++ {
204
if utils.IsCanceled(ctx) {
205
return ctx.Err()
206
}
207
byteSize := chunkSize
208
var byteData []byte
209
if left >= chunkSize {
210
byteData = chunkByteData
211
} else {
212
byteSize = left
213
byteData = make([]byte, byteSize)
214
}
215
left -= byteSize
216
_, err = io.ReadFull(tempFile, byteData)
217
if err != nil {
218
return err
219
}
220
221
// calculate md5
222
h.Write(byteData)
223
uploadBlockList = append(uploadBlockList, hex.EncodeToString(h.Sum(nil)))
224
h.Reset()
225
226
u := "https://" + locateupload_resp.Host + "/rest/2.0/pcs/superfile2"
227
params["partseq"] = strconv.Itoa(partseq)
228
res, err := base.RestyClient.R().
229
SetContext(ctx).
230
SetQueryParams(params).
231
SetFileReader("file", stream.GetName(), driver.NewLimitedUploadStream(ctx, bytes.NewReader(byteData))).
232
SetHeader("Cookie", d.Cookie).
233
Post(u)
234
if err != nil {
235
return err
236
}
237
log.Debugln(res.String())
238
if count > 0 {
239
up(float64(partseq) * 100 / float64(count))
240
}
241
}
242
243
// create file
244
params = map[string]string{
245
"isdir": "0",
246
"rtype": "1",
247
}
248
249
uploadBlockListStr, err := utils.Json.MarshalToString(uploadBlockList)
250
if err != nil {
251
return err
252
}
253
data = map[string]string{
254
"path": rawPath,
255
"size": strconv.FormatInt(stream.GetSize(), 10),
256
"uploadid": precreateResp.Uploadid,
257
"target_path": dstDir.GetPath(),
258
"block_list": uploadBlockListStr,
259
"local_mtime": strconv.FormatInt(stream.ModTime().Unix(), 10),
260
}
261
var createResp CreateResp
262
res, err = d.post_form("/api/create", params, data, &createResp)
263
log.Debugln(string(res))
264
if err != nil {
265
return err
266
}
267
if createResp.Errno != 0 {
268
return fmt.Errorf("[terabox] failed to create file, errno: %d", createResp.Errno)
269
}
270
return nil
271
}
272
273
var _ driver.Driver = (*Terabox)(nil)
274
275