From c65d868e09dd020d41f8a758b3d59ea69a5b3e43 Mon Sep 17 00:00:00 2001 From: Brian <126593039+Rektyfikowany@users.noreply.github.com> Date: Mon, 20 Mar 2023 17:46:15 +0800 Subject: [PATCH] fix(baidu_share): large file download (#3887 close #3876) * fix(baidushare): large file download * refactor: optimize client --- drivers/baidu_share/driver.go | 238 ++++++++++++++++++++++++---------- drivers/baidu_share/meta.go | 19 +-- drivers/baidu_share/types.go | 18 --- 3 files changed, 179 insertions(+), 96 deletions(-) diff --git a/drivers/baidu_share/driver.go b/drivers/baidu_share/driver.go index 5d75f680..a77303aa 100644 --- a/drivers/baidu_share/driver.go +++ b/drivers/baidu_share/driver.go @@ -2,25 +2,30 @@ package baidu_share import ( "context" + "encoding/json" "fmt" + "io" "net/http" "net/url" "path" - "strconv" "time" - "github.com/Xhofe/go-cache" - "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/model" + "github.com/go-resty/resty/v2" ) type BaiduShare struct { model.Storage Addition - config map[string]string - dlinkCache cache.ICache[string] + client *resty.Client + info struct { + Root string + Seckey string + Shareid string + Uk string + } } func (d *BaiduShare) Config() driver.Config { @@ -34,115 +39,208 @@ func (d *BaiduShare) GetAddition() driver.Additional { func (d *BaiduShare) Init(ctx context.Context) error { // TODO login / refresh token //op.MustSaveDriverStorage(d) - d.config = map[string]string{} - d.dlinkCache = cache.NewMemCache(cache.WithClearInterval[string](time.Duration(d.CacheExpiration) * time.Minute)) - return nil + d.client = resty.New(). + SetBaseURL("https://pan.baidu.com"). + SetHeader("User-Agent", "netdisk"). + SetCookie(&http.Cookie{Name: "BDUSS", Value: d.BDUSS}). + SetCookie(&http.Cookie{Name: "ndut_fmt"}) + respJson := struct { + Errno int64 `json:"errno"` + Data struct { + List [1]struct { + Path string `json:"path"` + } `json:"list"` + Uk json.Number `json:"uk"` + Shareid json.Number `json:"shareid"` + Seckey string `json:"seckey"` + } `json:"data"` + }{} + resp, err := d.client.R(). + SetBody(url.Values{ + "pwd": {d.Pwd}, + "root": {"1"}, + "shorturl": {d.Surl}, + }.Encode()). + SetResult(&respJson). + Post("share/wxlist?channel=weixin&version=2.2.2&clienttype=25&web=1") + if err == nil { + if resp.IsSuccess() && respJson.Errno == 0 { + d.info.Root = path.Dir(respJson.Data.List[0].Path) + d.info.Seckey = respJson.Data.Seckey + d.info.Shareid = respJson.Data.Shareid.String() + d.info.Uk = respJson.Data.Uk.String() + } else { + err = fmt.Errorf(" %s; %s; ", resp.Status(), resp.Body()) + } + } + return err } func (d *BaiduShare) Drop(ctx context.Context) error { - d.dlinkCache.Clear() return nil } func (d *BaiduShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { - // TODO return the files list - body := url.Values{ - "shorturl": {d.Surl}, - "dir": {dir.GetPath()}, - "root": {"0"}, - "pwd": {d.Pwd}, - "num": {"1000"}, - "order": {"time"}, + // TODO return the files list, required + reqDir := dir.GetPath() + isRoot := "0" + if reqDir == d.RootFolderPath { + reqDir = path.Join(d.info.Root, reqDir) } - if body.Get("dir") == "" || body.Get("dir") == d.config["root"] { - body.Set("root", "1") + if reqDir == d.info.Root { + isRoot = "1" } - res := []model.Obj{} + objs := []model.Obj{} var err error - var page int64 = 1 + var page uint64 = 1 more := true - for more { - body.Set("page", strconv.FormatInt(page, 10)) - req := base.RestyClient.R(). - SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}}). - SetBody(body.Encode()) - resp, e := req.Post("https://pan.baidu.com/share/wxlist?channel=weixin&version=2.2.2&clienttype=25&web=1") + for more && err == nil { + respJson := struct { + Errno int64 `json:"errno"` + Data struct { + More bool `json:"has_more"` + List []struct { + Fsid json.Number `json:"fs_id"` + Isdir json.Number `json:"isdir"` + Path string `json:"path"` + Name string `json:"server_filename"` + Mtime json.Number `json:"server_mtime"` + Size json.Number `json:"size"` + } `json:"list"` + } `json:"data"` + }{} + resp, e := d.client.R(). + SetBody(url.Values{ + "dir": {reqDir}, + "num": {"1000"}, + "order": {"time"}, + "page": {fmt.Sprint(page)}, + "pwd": {d.Pwd}, + "root": {isRoot}, + "shorturl": {d.Surl}, + }.Encode()). + SetResult(&respJson). + Post("share/wxlist?channel=weixin&version=2.2.2&clienttype=25&web=1") err = e - jsonresp := jsonResp{} if err == nil { - err = base.RestyClient.JSONUnmarshal(resp.Body(), &jsonresp) - } - if err == nil && jsonresp.Errno == 0 { - more = jsonresp.Data.More - page += 1 - for _, v := range jsonresp.Data.List { - size, _ := v.Size.Int64() - mtime, _ := v.Time.Int64() - res = append(res, &model.Object{ - ID: v.ID.String(), - Path: v.Path, - Name: v.Name, - Size: size, - Modified: time.Unix(mtime, 0), - IsFolder: v.Dir.String() == "1", - }) - d.dlinkCache.Set(v.Path, v.Dlink, cache.WithEx[string](time.Duration(d.CacheExpiration/2)*time.Minute)) + if resp.IsSuccess() && respJson.Errno == 0 { + page++ + more = respJson.Data.More + for _, v := range respJson.Data.List { + size, _ := v.Size.Int64() + mtime, _ := v.Mtime.Int64() + objs = append(objs, &model.Object{ + ID: v.Fsid.String(), + Path: v.Path, + Name: v.Name, + Size: size, + Modified: time.Unix(mtime, 0), + IsFolder: v.Isdir.String() == "1", + }) + } + } else { + err = fmt.Errorf(" %s; %s; ", resp.Status(), resp.Body()) } - if len(res) > 0 && body.Get("root") == "1" { - d.config["root"] = path.Dir(res[0].GetPath()) - } - } else { - if err == nil { - err = fmt.Errorf("errno:%d", jsonresp.Errno) - } - break } } - return res, err + return objs, err } func (d *BaiduShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - // TODO return link of file - var err error - req := base.RestyClient.R().SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}}) - url, found := d.dlinkCache.Get(file.GetPath()) - if !found { - _, err = d.List(ctx, &model.Object{Path: path.Dir(file.GetPath())}, model.ListArgs{}) - url, found = d.dlinkCache.Get(file.GetPath()) - if err == nil && !found { - err = errs.NotSupport + // TODO return link of file, required + link := model.Link{Header: d.client.Header} + sign := "" + stamp := "" + signJson := struct { + Errno int64 `json:"errno"` + Data struct { + Stamp json.Number `json:"timestamp"` + Sign string `json:"sign"` + } `json:"data"` + }{} + resp, err := d.client.R(). + SetQueryParam("surl", d.Surl). + SetResult(&signJson). + Get("share/tplconfig?fields=sign,timestamp&channel=chunlei&web=1&app_id=250528&clienttype=0") + if err == nil { + if resp.IsSuccess() && signJson.Errno == 0 { + stamp = signJson.Data.Stamp.String() + sign = signJson.Data.Sign + } else { + err = fmt.Errorf(" %s; %s; ", resp.Status(), resp.Body()) } } - return &model.Link{URL: url, Header: req.Header}, err + if err == nil { + respJson := struct { + Errno int64 `json:"errno"` + List [1]struct { + Dlink string `json:"dlink"` + } `json:"list"` + }{} + resp, err = d.client.R(). + SetQueryParam("sign", sign). + SetQueryParam("timestamp", stamp). + SetBody(url.Values{ + "encrypt": {"0"}, + "extra": {fmt.Sprintf(`{"sekey":"%s"}`, d.info.Seckey)}, + "fid_list": {fmt.Sprintf("[%s]", file.GetID())}, + "primaryid": {d.info.Shareid}, + "product": {"share"}, + "type": {"nolimit"}, + "uk": {d.info.Uk}, + }.Encode()). + SetResult(&respJson). + Post("api/sharedownload?app_id=250528&channel=chunlei&clienttype=12&web=1") + if err == nil { + if resp.IsSuccess() && respJson.Errno == 0 && respJson.List[0].Dlink != "" { + link.URL = respJson.List[0].Dlink + } else { + err = fmt.Errorf(" %s; %s; ", resp.Status(), resp.Body()) + } + } + if err == nil { + resp, err = d.client.R(). + SetDoNotParseResponse(true). + Get(link.URL) + if err == nil { + defer resp.RawBody().Close() + if resp.IsError() { + byt, _ := io.ReadAll(resp.RawBody()) + err = fmt.Errorf(" %s; %s; ", resp.Status(), byt) + } + } + } + } + return &link, err } func (d *BaiduShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { - // TODO create folder + // TODO create folder, optional return errs.NotSupport } func (d *BaiduShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error { - // TODO move obj + // TODO move obj, optional return errs.NotSupport } func (d *BaiduShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error { - // TODO rename obj + // TODO rename obj, optional return errs.NotSupport } func (d *BaiduShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - // TODO copy obj + // TODO copy obj, optional return errs.NotSupport } func (d *BaiduShare) Remove(ctx context.Context, obj model.Obj) error { - // TODO remove obj + // TODO remove obj, optional return errs.NotSupport } func (d *BaiduShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { - // TODO upload file + // TODO upload file, optional return errs.NotSupport } diff --git a/drivers/baidu_share/meta.go b/drivers/baidu_share/meta.go index ca6cb30a..271a692d 100644 --- a/drivers/baidu_share/meta.go +++ b/drivers/baidu_share/meta.go @@ -17,14 +17,17 @@ type Addition struct { } var config = driver.Config{ - Name: "BaiduShare", - LocalSort: true, - OnlyLocal: false, - OnlyProxy: false, - NoCache: false, - NoUpload: true, - NeedMs: false, - DefaultRoot: "", + Name: "BaiduShare", + LocalSort: true, + OnlyLocal: false, + OnlyProxy: false, + NoCache: false, + NoUpload: true, + NeedMs: false, + DefaultRoot: "/", + CheckStatus: false, + Alert: "", + NoOverwriteUpload: false, } func init() { diff --git a/drivers/baidu_share/types.go b/drivers/baidu_share/types.go index 2f479b9d..1f65cb60 100644 --- a/drivers/baidu_share/types.go +++ b/drivers/baidu_share/types.go @@ -1,19 +1 @@ package baidu_share - -import "encoding/json" - -type jsonResp struct { - Errno int64 `json:"errno"` - Data struct { - More bool `json:"has_more"` - List []struct { - ID json.Number `json:"fs_id"` - Dir json.Number `json:"isdir"` - Path string `json:"path"` - Name string `json:"server_filename"` - Time json.Number `json:"server_mtime"` - Size json.Number `json:"size"` - Dlink string `json:"dlink"` - } `json:"list"` - } `json:"data"` -}