Skip to content

Commit 3499c4d

Browse files
authored
feat: 115 open driver (#8139)
* wip: 115 open * chore(go.mod): update 115-sdk-go dependency version * feat(115_open): implement directory management and file operations * chore(go.mod): update 115-sdk-go dependency to v0.1.1 and adjust callback handling in driver * chore: rename driver
1 parent d20f41d commit 3499c4d

File tree

7 files changed

+436
-23
lines changed

7 files changed

+436
-23
lines changed

drivers/115_open/driver.go

+308
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
package _115_open
2+
3+
import (
4+
"context"
5+
"encoding/base64"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"strconv"
10+
"strings"
11+
"time"
12+
13+
"github.com/alist-org/alist/v3/cmd/flags"
14+
"github.com/alist-org/alist/v3/drivers/base"
15+
"github.com/alist-org/alist/v3/internal/driver"
16+
"github.com/alist-org/alist/v3/internal/model"
17+
"github.com/alist-org/alist/v3/internal/op"
18+
"github.com/alist-org/alist/v3/pkg/utils"
19+
"github.com/aliyun/aliyun-oss-go-sdk/oss"
20+
sdk "github.com/xhofe/115-sdk-go"
21+
)
22+
23+
type Open115 struct {
24+
model.Storage
25+
Addition
26+
client *sdk.Client
27+
}
28+
29+
func (d *Open115) Config() driver.Config {
30+
return config
31+
}
32+
33+
func (d *Open115) GetAddition() driver.Additional {
34+
return &d.Addition
35+
}
36+
37+
func (d *Open115) Init(ctx context.Context) error {
38+
d.client = sdk.New(sdk.WithRefreshToken(d.Addition.RefreshToken),
39+
sdk.WithAccessToken(d.Addition.AccessToken),
40+
sdk.WithOnRefreshToken(func(s1, s2 string) {
41+
d.Addition.AccessToken = s1
42+
d.Addition.RefreshToken = s2
43+
op.MustSaveDriverStorage(d)
44+
}))
45+
if flags.Debug || flags.Dev {
46+
d.client.SetDebug(true)
47+
}
48+
_, err := d.client.UserInfo(ctx)
49+
if err != nil {
50+
return err
51+
}
52+
return nil
53+
}
54+
55+
func (d *Open115) Drop(ctx context.Context) error {
56+
return nil
57+
}
58+
59+
func (d *Open115) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
60+
var res []model.Obj
61+
pageSize := int64(200)
62+
offset := int64(0)
63+
for {
64+
resp, err := d.client.GetFiles(ctx, &sdk.GetFilesReq{
65+
CID: dir.GetID(),
66+
Limit: pageSize,
67+
Offset: offset,
68+
ASC: d.Addition.OrderDirection == "asc",
69+
O: d.Addition.OrderBy,
70+
// Cur: 1,
71+
ShowDir: true,
72+
})
73+
if err != nil {
74+
return nil, err
75+
}
76+
res = append(res, utils.MustSliceConvert(resp.Data, func(src sdk.GetFilesResp_File) model.Obj {
77+
obj := Obj(src)
78+
return &obj
79+
})...)
80+
if len(res) >= int(resp.Count) {
81+
break
82+
}
83+
offset += pageSize
84+
}
85+
return res, nil
86+
}
87+
88+
func (d *Open115) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
89+
var ua string
90+
if args.Header != nil {
91+
ua = args.Header.Get("User-Agent")
92+
}
93+
if ua == "" {
94+
ua = base.UserAgent
95+
}
96+
obj, ok := file.(*Obj)
97+
if !ok {
98+
return nil, fmt.Errorf("can't convert obj")
99+
}
100+
pc := obj.Pc
101+
resp, err := d.client.DownURL(ctx, pc, ua)
102+
if err != nil {
103+
return nil, err
104+
}
105+
u, ok := resp[obj.GetID()]
106+
if !ok {
107+
return nil, fmt.Errorf("can't get link")
108+
}
109+
return &model.Link{
110+
URL: u.URL.URL,
111+
Header: http.Header{
112+
"User-Agent": []string{ua},
113+
},
114+
}, nil
115+
}
116+
117+
func (d *Open115) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
118+
resp, err := d.client.Mkdir(ctx, parentDir.GetID(), dirName)
119+
if err != nil {
120+
return nil, err
121+
}
122+
return &Obj{
123+
Fid: resp.FileID,
124+
Pid: parentDir.GetID(),
125+
Fn: dirName,
126+
Fc: "0",
127+
Upt: time.Now().Unix(),
128+
Uet: time.Now().Unix(),
129+
UpPt: time.Now().Unix(),
130+
}, nil
131+
}
132+
133+
func (d *Open115) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
134+
_, err := d.client.Move(ctx, &sdk.MoveReq{
135+
FileIDs: srcObj.GetID(),
136+
ToCid: dstDir.GetID(),
137+
})
138+
if err != nil {
139+
return nil, err
140+
}
141+
return srcObj, nil
142+
}
143+
144+
func (d *Open115) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
145+
_, err := d.client.UpdateFile(ctx, &sdk.UpdateFileReq{
146+
FileID: srcObj.GetID(),
147+
FileNma: newName,
148+
})
149+
if err != nil {
150+
return nil, err
151+
}
152+
return srcObj, nil
153+
}
154+
155+
func (d *Open115) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
156+
_, err := d.client.Copy(ctx, &sdk.CopyReq{
157+
PID: dstDir.GetID(),
158+
FileID: srcObj.GetID(),
159+
NoDupli: "1",
160+
})
161+
if err != nil {
162+
return nil, err
163+
}
164+
return srcObj, nil
165+
}
166+
167+
func (d *Open115) Remove(ctx context.Context, obj model.Obj) error {
168+
_obj, ok := obj.(*Obj)
169+
if !ok {
170+
return fmt.Errorf("can't convert obj")
171+
}
172+
_, err := d.client.DelFile(ctx, &sdk.DelFileReq{
173+
FileIDs: _obj.GetID(),
174+
ParentID: _obj.Pid,
175+
})
176+
if err != nil {
177+
return err
178+
}
179+
return nil
180+
}
181+
182+
func (d *Open115) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
183+
tempF, err := file.CacheFullInTempFile()
184+
if err != nil {
185+
return err
186+
}
187+
// cal full sha1
188+
sha1, err := utils.HashReader(utils.SHA1, tempF)
189+
if err != nil {
190+
return err
191+
}
192+
_, err = tempF.Seek(0, io.SeekStart)
193+
if err != nil {
194+
return err
195+
}
196+
// pre 128k sha1
197+
sha1128k, err := utils.HashReader(utils.SHA1, io.LimitReader(tempF, 128*1024))
198+
if err != nil {
199+
return err
200+
}
201+
_, err = tempF.Seek(0, io.SeekStart)
202+
if err != nil {
203+
return err
204+
}
205+
// 1. Init
206+
resp, err := d.client.UploadInit(ctx, &sdk.UploadInitReq{
207+
FileName: file.GetName(),
208+
FileSize: file.GetSize(),
209+
Target: dstDir.GetID(),
210+
FileID: strings.ToUpper(sha1),
211+
PreID: strings.ToUpper(sha1128k),
212+
})
213+
if err != nil {
214+
return err
215+
}
216+
if resp.Status == 2 {
217+
return nil
218+
}
219+
// 2. two way verify
220+
if utils.SliceContains([]int{6, 7, 8}, resp.Status) {
221+
signCheck := strings.Split(resp.SignCheck, "-") //"sign_check": "2392148-2392298" 取2392148-2392298之间的内容(包含2392148、2392298)的sha1
222+
start, err := strconv.ParseInt(signCheck[0], 10, 64)
223+
if err != nil {
224+
return err
225+
}
226+
end, err := strconv.ParseInt(signCheck[1], 10, 64)
227+
if err != nil {
228+
return err
229+
}
230+
_, err = tempF.Seek(start, io.SeekStart)
231+
if err != nil {
232+
return err
233+
}
234+
signVal, err := utils.HashReader(utils.SHA1, io.LimitReader(tempF, end-start+1))
235+
if err != nil {
236+
return err
237+
}
238+
_, err = tempF.Seek(0, io.SeekStart)
239+
if err != nil {
240+
return err
241+
}
242+
resp, err = d.client.UploadInit(ctx, &sdk.UploadInitReq{
243+
FileName: file.GetName(),
244+
FileSize: file.GetSize(),
245+
Target: dstDir.GetID(),
246+
FileID: strings.ToUpper(sha1),
247+
PreID: strings.ToUpper(sha1128k),
248+
SignKey: resp.SignKey,
249+
SignVal: strings.ToUpper(signVal),
250+
})
251+
if err != nil {
252+
return err
253+
}
254+
if resp.Status == 2 {
255+
return nil
256+
}
257+
}
258+
// 3. get upload token
259+
tokenResp, err := d.client.UploadGetToken(ctx)
260+
if err != nil {
261+
return err
262+
}
263+
// 4. upload
264+
ossClient, err := oss.New(tokenResp.Endpoint, tokenResp.AccessKeyId, tokenResp.AccessKeySecret, oss.SecurityToken(tokenResp.SecurityToken))
265+
if err != nil {
266+
return err
267+
}
268+
bucket, err := ossClient.Bucket(resp.Bucket)
269+
if err != nil {
270+
return err
271+
}
272+
err = bucket.PutObject(resp.Object, tempF,
273+
oss.Callback(base64.StdEncoding.EncodeToString([]byte(resp.Callback.Value.Callback))),
274+
oss.CallbackVar(base64.StdEncoding.EncodeToString([]byte(resp.Callback.Value.CallbackVar))),
275+
)
276+
if err != nil {
277+
return err
278+
}
279+
return nil
280+
}
281+
282+
// func (d *Open115) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
283+
// // TODO get archive file meta-info, return errs.NotImplement to use an internal archive tool, optional
284+
// return nil, errs.NotImplement
285+
// }
286+
287+
// func (d *Open115) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
288+
// // TODO list args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
289+
// return nil, errs.NotImplement
290+
// }
291+
292+
// func (d *Open115) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
293+
// // TODO return link of file args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
294+
// return nil, errs.NotImplement
295+
// }
296+
297+
// func (d *Open115) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) ([]model.Obj, error) {
298+
// // TODO extract args.InnerPath path in the archive srcObj to the dstDir location, optional
299+
// // a folder with the same name as the archive file needs to be created to store the extracted results if args.PutIntoNewDir
300+
// // return errs.NotImplement to use an internal archive tool
301+
// return nil, errs.NotImplement
302+
// }
303+
304+
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
305+
// return nil, errs.NotSupport
306+
//}
307+
308+
var _ driver.Driver = (*Open115)(nil)

drivers/115_open/meta.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package _115_open
2+
3+
import (
4+
"github.com/alist-org/alist/v3/internal/driver"
5+
"github.com/alist-org/alist/v3/internal/op"
6+
)
7+
8+
type Addition struct {
9+
// Usually one of two
10+
driver.RootID
11+
// define other
12+
RefreshToken string `json:"refresh_token" required:"true"`
13+
OrderBy string `json:"order_by" type:"select" options:"file_name,file_size,user_utime,file_type"`
14+
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
15+
AccessToken string
16+
}
17+
18+
var config = driver.Config{
19+
Name: "115 Open",
20+
LocalSort: false,
21+
OnlyLocal: false,
22+
OnlyProxy: false,
23+
NoCache: false,
24+
NoUpload: false,
25+
NeedMs: false,
26+
DefaultRoot: "0",
27+
CheckStatus: false,
28+
Alert: "",
29+
NoOverwriteUpload: false,
30+
}
31+
32+
func init() {
33+
op.RegisterDriver(func() driver.Driver {
34+
return &Open115{}
35+
})
36+
}

0 commit comments

Comments
 (0)