Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/aliyundrive_open/driver.go
1986 views
1
package aliyundrive_open
2
3
import (
4
"context"
5
"errors"
6
"fmt"
7
"net/http"
8
"path/filepath"
9
"time"
10
11
"github.com/Xhofe/rateg"
12
"github.com/alist-org/alist/v3/drivers/base"
13
"github.com/alist-org/alist/v3/internal/driver"
14
"github.com/alist-org/alist/v3/internal/errs"
15
"github.com/alist-org/alist/v3/internal/model"
16
"github.com/alist-org/alist/v3/pkg/utils"
17
"github.com/go-resty/resty/v2"
18
log "github.com/sirupsen/logrus"
19
)
20
21
type AliyundriveOpen struct {
22
model.Storage
23
Addition
24
25
DriveId string
26
27
limitList func(ctx context.Context, data base.Json) (*Files, error)
28
limitLink func(ctx context.Context, file model.Obj) (*model.Link, error)
29
ref *AliyundriveOpen
30
}
31
32
func (d *AliyundriveOpen) Config() driver.Config {
33
return config
34
}
35
36
func (d *AliyundriveOpen) GetAddition() driver.Additional {
37
return &d.Addition
38
}
39
40
func (d *AliyundriveOpen) Init(ctx context.Context) error {
41
if d.LIVPDownloadFormat == "" {
42
d.LIVPDownloadFormat = "jpeg"
43
}
44
if d.DriveType == "" {
45
d.DriveType = "default"
46
}
47
res, err := d.request("/adrive/v1.0/user/getDriveInfo", http.MethodPost, nil)
48
if err != nil {
49
return err
50
}
51
d.DriveId = utils.Json.Get(res, d.DriveType+"_drive_id").ToString()
52
d.limitList = rateg.LimitFnCtx(d.list, rateg.LimitFnOption{
53
Limit: 4,
54
Bucket: 1,
55
})
56
d.limitLink = rateg.LimitFnCtx(d.link, rateg.LimitFnOption{
57
Limit: 1,
58
Bucket: 1,
59
})
60
return nil
61
}
62
63
func (d *AliyundriveOpen) InitReference(storage driver.Driver) error {
64
refStorage, ok := storage.(*AliyundriveOpen)
65
if ok {
66
d.ref = refStorage
67
return nil
68
}
69
return errs.NotSupport
70
}
71
72
func (d *AliyundriveOpen) Drop(ctx context.Context) error {
73
d.ref = nil
74
return nil
75
}
76
77
// GetRoot implements the driver.GetRooter interface to properly set up the root object
78
func (d *AliyundriveOpen) GetRoot(ctx context.Context) (model.Obj, error) {
79
return &model.Object{
80
ID: d.RootFolderID,
81
Path: "/",
82
Name: "root",
83
Size: 0,
84
Modified: d.Modified,
85
IsFolder: true,
86
}, nil
87
}
88
89
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
90
if d.limitList == nil {
91
return nil, fmt.Errorf("driver not init")
92
}
93
files, err := d.getFiles(ctx, dir.GetID())
94
if err != nil {
95
return nil, err
96
}
97
98
objs, err := utils.SliceConvert(files, func(src File) (model.Obj, error) {
99
obj := fileToObj(src)
100
// Set the correct path for the object
101
if dir.GetPath() != "" {
102
obj.Path = filepath.Join(dir.GetPath(), obj.GetName())
103
}
104
return obj, nil
105
})
106
107
return objs, err
108
}
109
110
func (d *AliyundriveOpen) link(ctx context.Context, file model.Obj) (*model.Link, error) {
111
res, err := d.request("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) {
112
req.SetBody(base.Json{
113
"drive_id": d.DriveId,
114
"file_id": file.GetID(),
115
"expire_sec": 14400,
116
})
117
})
118
if err != nil {
119
return nil, err
120
}
121
url := utils.Json.Get(res, "url").ToString()
122
if url == "" {
123
if utils.Ext(file.GetName()) != "livp" {
124
return nil, errors.New("get download url failed: " + string(res))
125
}
126
url = utils.Json.Get(res, "streamsUrl", d.LIVPDownloadFormat).ToString()
127
}
128
exp := time.Minute
129
return &model.Link{
130
URL: url,
131
Expiration: &exp,
132
}, nil
133
}
134
135
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
136
if d.limitLink == nil {
137
return nil, fmt.Errorf("driver not init")
138
}
139
return d.limitLink(ctx, file)
140
}
141
142
func (d *AliyundriveOpen) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
143
nowTime, _ := getNowTime()
144
newDir := File{CreatedAt: nowTime, UpdatedAt: nowTime}
145
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
146
req.SetBody(base.Json{
147
"drive_id": d.DriveId,
148
"parent_file_id": parentDir.GetID(),
149
"name": dirName,
150
"type": "folder",
151
"check_name_mode": "refuse",
152
}).SetResult(&newDir)
153
})
154
if err != nil {
155
return nil, err
156
}
157
obj := fileToObj(newDir)
158
159
// Set the correct Path for the returned directory object
160
if parentDir.GetPath() != "" {
161
obj.Path = filepath.Join(parentDir.GetPath(), dirName)
162
} else {
163
obj.Path = "/" + dirName
164
}
165
166
return obj, nil
167
}
168
169
func (d *AliyundriveOpen) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
170
var resp MoveOrCopyResp
171
_, err := d.request("/adrive/v1.0/openFile/move", http.MethodPost, func(req *resty.Request) {
172
req.SetBody(base.Json{
173
"drive_id": d.DriveId,
174
"file_id": srcObj.GetID(),
175
"to_parent_file_id": dstDir.GetID(),
176
"check_name_mode": "ignore", // optional:ignore,auto_rename,refuse
177
//"new_name": "newName", // The new name to use when a file of the same name exists
178
}).SetResult(&resp)
179
})
180
if err != nil {
181
return nil, err
182
}
183
184
if srcObj, ok := srcObj.(*model.ObjThumb); ok {
185
srcObj.ID = resp.FileID
186
srcObj.Modified = time.Now()
187
srcObj.Path = filepath.Join(dstDir.GetPath(), srcObj.GetName())
188
189
// Check for duplicate files in the destination directory
190
if err := d.removeDuplicateFiles(ctx, dstDir.GetPath(), srcObj.GetName(), srcObj.GetID()); err != nil {
191
// Only log a warning instead of returning an error since the move operation has already completed successfully
192
log.Warnf("Failed to remove duplicate files after move: %v", err)
193
}
194
return srcObj, nil
195
}
196
return nil, nil
197
}
198
199
func (d *AliyundriveOpen) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
200
var newFile File
201
_, err := d.request("/adrive/v1.0/openFile/update", http.MethodPost, func(req *resty.Request) {
202
req.SetBody(base.Json{
203
"drive_id": d.DriveId,
204
"file_id": srcObj.GetID(),
205
"name": newName,
206
}).SetResult(&newFile)
207
})
208
if err != nil {
209
return nil, err
210
}
211
212
// Check for duplicate files in the parent directory
213
parentPath := filepath.Dir(srcObj.GetPath())
214
if err := d.removeDuplicateFiles(ctx, parentPath, newName, newFile.FileId); err != nil {
215
// Only log a warning instead of returning an error since the rename operation has already completed successfully
216
log.Warnf("Failed to remove duplicate files after rename: %v", err)
217
}
218
219
obj := fileToObj(newFile)
220
221
// Set the correct Path for the renamed object
222
if parentPath != "" && parentPath != "." {
223
obj.Path = filepath.Join(parentPath, newName)
224
} else {
225
obj.Path = "/" + newName
226
}
227
228
return obj, nil
229
}
230
231
func (d *AliyundriveOpen) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
232
var resp MoveOrCopyResp
233
_, err := d.request("/adrive/v1.0/openFile/copy", http.MethodPost, func(req *resty.Request) {
234
req.SetBody(base.Json{
235
"drive_id": d.DriveId,
236
"file_id": srcObj.GetID(),
237
"to_parent_file_id": dstDir.GetID(),
238
"auto_rename": false,
239
}).SetResult(&resp)
240
})
241
if err != nil {
242
return err
243
}
244
245
// Check for duplicate files in the destination directory
246
if err := d.removeDuplicateFiles(ctx, dstDir.GetPath(), srcObj.GetName(), resp.FileID); err != nil {
247
// Only log a warning instead of returning an error since the copy operation has already completed successfully
248
log.Warnf("Failed to remove duplicate files after copy: %v", err)
249
}
250
251
return nil
252
}
253
254
func (d *AliyundriveOpen) Remove(ctx context.Context, obj model.Obj) error {
255
uri := "/adrive/v1.0/openFile/recyclebin/trash"
256
if d.RemoveWay == "delete" {
257
uri = "/adrive/v1.0/openFile/delete"
258
}
259
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
260
req.SetBody(base.Json{
261
"drive_id": d.DriveId,
262
"file_id": obj.GetID(),
263
})
264
})
265
return err
266
}
267
268
func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
269
obj, err := d.upload(ctx, dstDir, stream, up)
270
271
// Set the correct Path for the returned file object
272
if obj != nil && obj.GetPath() == "" {
273
if dstDir.GetPath() != "" {
274
if objWithPath, ok := obj.(model.SetPath); ok {
275
objWithPath.SetPath(filepath.Join(dstDir.GetPath(), obj.GetName()))
276
}
277
}
278
}
279
280
return obj, err
281
}
282
283
func (d *AliyundriveOpen) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
284
var resp base.Json
285
var uri string
286
data := base.Json{
287
"drive_id": d.DriveId,
288
"file_id": args.Obj.GetID(),
289
}
290
switch args.Method {
291
case "video_preview":
292
uri = "/adrive/v1.0/openFile/getVideoPreviewPlayInfo"
293
data["category"] = "live_transcoding"
294
data["url_expire_sec"] = 14400
295
default:
296
return nil, errs.NotSupport
297
}
298
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
299
req.SetBody(data).SetResult(&resp)
300
})
301
if err != nil {
302
return nil, err
303
}
304
return resp, nil
305
}
306
307
var _ driver.Driver = (*AliyundriveOpen)(nil)
308
var _ driver.MkdirResult = (*AliyundriveOpen)(nil)
309
var _ driver.MoveResult = (*AliyundriveOpen)(nil)
310
var _ driver.RenameResult = (*AliyundriveOpen)(nil)
311
var _ driver.PutResult = (*AliyundriveOpen)(nil)
312
var _ driver.GetRooter = (*AliyundriveOpen)(nil)
313
314