fix(baidu_share): large file download (#3887 close #3876)

* fix(baidushare): large file download

* refactor: optimize client
pull/3927/head
Brian 2023-03-20 17:46:15 +08:00 committed by GitHub
parent aeb48b2ecc
commit c65d868e09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 179 additions and 96 deletions

View File

@ -2,25 +2,30 @@ package baidu_share
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv"
"time" "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/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/go-resty/resty/v2"
) )
type BaiduShare struct { type BaiduShare struct {
model.Storage model.Storage
Addition Addition
config map[string]string client *resty.Client
dlinkCache cache.ICache[string] info struct {
Root string
Seckey string
Shareid string
Uk string
}
} }
func (d *BaiduShare) Config() driver.Config { func (d *BaiduShare) Config() driver.Config {
@ -34,115 +39,208 @@ func (d *BaiduShare) GetAddition() driver.Additional {
func (d *BaiduShare) Init(ctx context.Context) error { func (d *BaiduShare) Init(ctx context.Context) error {
// TODO login / refresh token // TODO login / refresh token
//op.MustSaveDriverStorage(d) //op.MustSaveDriverStorage(d)
d.config = map[string]string{} d.client = resty.New().
d.dlinkCache = cache.NewMemCache(cache.WithClearInterval[string](time.Duration(d.CacheExpiration) * time.Minute)) SetBaseURL("https://pan.baidu.com").
return nil 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 { func (d *BaiduShare) Drop(ctx context.Context) error {
d.dlinkCache.Clear()
return nil return nil
} }
func (d *BaiduShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { func (d *BaiduShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
// TODO return the files list // TODO return the files list, required
body := url.Values{ reqDir := dir.GetPath()
"shorturl": {d.Surl}, isRoot := "0"
"dir": {dir.GetPath()}, if reqDir == d.RootFolderPath {
"root": {"0"}, reqDir = path.Join(d.info.Root, reqDir)
"pwd": {d.Pwd},
"num": {"1000"},
"order": {"time"},
} }
if body.Get("dir") == "" || body.Get("dir") == d.config["root"] { if reqDir == d.info.Root {
body.Set("root", "1") isRoot = "1"
} }
res := []model.Obj{} objs := []model.Obj{}
var err error var err error
var page int64 = 1 var page uint64 = 1
more := true more := true
for more { for more && err == nil {
body.Set("page", strconv.FormatInt(page, 10)) respJson := struct {
req := base.RestyClient.R(). Errno int64 `json:"errno"`
SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}}). Data struct {
SetBody(body.Encode()) More bool `json:"has_more"`
resp, e := req.Post("https://pan.baidu.com/share/wxlist?channel=weixin&version=2.2.2&clienttype=25&web=1") 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 err = e
jsonresp := jsonResp{}
if err == nil { if err == nil {
err = base.RestyClient.JSONUnmarshal(resp.Body(), &jsonresp) if resp.IsSuccess() && respJson.Errno == 0 {
} page++
if err == nil && jsonresp.Errno == 0 { more = respJson.Data.More
more = jsonresp.Data.More for _, v := range respJson.Data.List {
page += 1 size, _ := v.Size.Int64()
for _, v := range jsonresp.Data.List { mtime, _ := v.Mtime.Int64()
size, _ := v.Size.Int64() objs = append(objs, &model.Object{
mtime, _ := v.Time.Int64() ID: v.Fsid.String(),
res = append(res, &model.Object{ Path: v.Path,
ID: v.ID.String(), Name: v.Name,
Path: v.Path, Size: size,
Name: v.Name, Modified: time.Unix(mtime, 0),
Size: size, IsFolder: v.Isdir.String() == "1",
Modified: time.Unix(mtime, 0), })
IsFolder: v.Dir.String() == "1", }
}) } else {
d.dlinkCache.Set(v.Path, v.Dlink, cache.WithEx[string](time.Duration(d.CacheExpiration/2)*time.Minute)) 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) { func (d *BaiduShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
// TODO return link of file // TODO return link of file, required
var err error link := model.Link{Header: d.client.Header}
req := base.RestyClient.R().SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}}) sign := ""
url, found := d.dlinkCache.Get(file.GetPath()) stamp := ""
if !found { signJson := struct {
_, err = d.List(ctx, &model.Object{Path: path.Dir(file.GetPath())}, model.ListArgs{}) Errno int64 `json:"errno"`
url, found = d.dlinkCache.Get(file.GetPath()) Data struct {
if err == nil && !found { Stamp json.Number `json:"timestamp"`
err = errs.NotSupport 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 { func (d *BaiduShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
// TODO create folder // TODO create folder, optional
return errs.NotSupport return errs.NotSupport
} }
func (d *BaiduShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error { func (d *BaiduShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO move obj // TODO move obj, optional
return errs.NotSupport return errs.NotSupport
} }
func (d *BaiduShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error { func (d *BaiduShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
// TODO rename obj // TODO rename obj, optional
return errs.NotSupport return errs.NotSupport
} }
func (d *BaiduShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { func (d *BaiduShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO copy obj // TODO copy obj, optional
return errs.NotSupport return errs.NotSupport
} }
func (d *BaiduShare) Remove(ctx context.Context, obj model.Obj) error { func (d *BaiduShare) Remove(ctx context.Context, obj model.Obj) error {
// TODO remove obj // TODO remove obj, optional
return errs.NotSupport return errs.NotSupport
} }
func (d *BaiduShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { 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 return errs.NotSupport
} }

View File

@ -17,14 +17,17 @@ type Addition struct {
} }
var config = driver.Config{ var config = driver.Config{
Name: "BaiduShare", Name: "BaiduShare",
LocalSort: true, LocalSort: true,
OnlyLocal: false, OnlyLocal: false,
OnlyProxy: false, OnlyProxy: false,
NoCache: false, NoCache: false,
NoUpload: true, NoUpload: true,
NeedMs: false, NeedMs: false,
DefaultRoot: "", DefaultRoot: "/",
CheckStatus: false,
Alert: "",
NoOverwriteUpload: false,
} }
func init() { func init() {

View File

@ -1,19 +1 @@
package baidu_share 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"`
}