diff --git a/drivers/pikpak/driver.go b/drivers/pikpak/driver.go index f0fc57f7d29f..9180dc1b305e 100644 --- a/drivers/pikpak/driver.go +++ b/drivers/pikpak/driver.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "github.com/alist-org/alist/v3/internal/op" + "golang.org/x/oauth2" + "io" "net/http" "strconv" "strings" @@ -28,6 +30,7 @@ type PikPak struct { *Common RefreshToken string AccessToken string + oauth2Token oauth2.TokenSource } func (d *PikPak) Config() driver.Config { @@ -62,18 +65,44 @@ func (d *PikPak) Init(ctx context.Context) (err error) { d.SetCaptchaToken(d.Addition.CaptchaToken) } - // 如果已经有RefreshToken,直接刷新AccessToken + if d.Addition.DeviceID != "" { + d.SetDeviceID(d.Addition.DeviceID) + } else { + d.Addition.DeviceID = d.Common.DeviceID + op.MustSaveDriverStorage(d) + } + // 初始化 oauth2Config + oauth2Config := &oauth2.Config{ + ClientID: d.ClientID, + ClientSecret: d.ClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: "https://user.mypikpak.com/v1/auth/signin", + TokenURL: "https://user.mypikpak.com/v1/auth/token", + AuthStyle: oauth2.AuthStyleInParams, + }, + } + + // 如果已经有RefreshToken,直接获取AccessToken if d.Addition.RefreshToken != "" { - d.RefreshToken = d.Addition.RefreshToken - if err := d.refreshToken(); err != nil { + // 使用 oauth2 刷新令牌 + // 初始化 oauth2Token + d.oauth2Token = oauth2.ReuseTokenSource(nil, utils.TokenSource(func() (*oauth2.Token, error) { + return oauth2Config.TokenSource(ctx, &oauth2.Token{ + RefreshToken: d.Addition.RefreshToken, + }).Token() + })) + token, err := d.oauth2Token.Token() + if err != nil { return err } + d.RefreshToken = token.RefreshToken + d.AccessToken = token.AccessToken } else { + // 如果没有填写RefreshToken,尝试登录 获取 refreshToken if err := d.login(); err != nil { return err } } - // 获取CaptchaToken err = d.RefreshCaptchaTokenAtLogin(GetAction(http.MethodGet, "https://api-drive.mypikpak.com/drive/v1/files"), d.Common.UserID) if err != nil { @@ -81,6 +110,15 @@ func (d *PikPak) Init(ctx context.Context) (err error) { } // 更新UserAgent d.Common.UserAgent = BuildCustomUserAgent(d.Common.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, d.Common.UserID) + + // 保存 有效的 RefreshToken + d.Addition.RefreshToken = d.RefreshToken + d.oauth2Token = oauth2.ReuseTokenSource(nil, utils.TokenSource(func() (*oauth2.Token, error) { + return oauth2Config.TokenSource(ctx, &oauth2.Token{ + RefreshToken: d.Addition.RefreshToken, + }).Token() + })) + op.MustSaveDriverStorage(d) return nil } @@ -100,8 +138,18 @@ func (d *PikPak) List(ctx context.Context, dir model.Obj, args model.ListArgs) ( func (d *PikPak) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { var resp File - _, err := d.request(fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s?_magic=2021&thumbnail_size=SIZE_LARGE", file.GetID()), - http.MethodGet, nil, &resp) + queryParams := map[string]string{ + "_magic": "2021", + "usage": "FETCH", + "thumbnail_size": "SIZE_LARGE", + } + if !d.DisableMediaLink { + queryParams["usage"] = "CACHE" + } + _, err := d.request(fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s", file.GetID()), + http.MethodGet, func(req *resty.Request) { + req.SetQueryParams(queryParams) + }, &resp) if err != nil { return nil, err } @@ -224,7 +272,7 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr input := &s3manager.UploadInput{ Bucket: ¶ms.Bucket, Key: ¶ms.Key, - Body: stream, + Body: io.TeeReader(stream, driver.NewProgress(stream.GetSize(), up)), } _, err = uploader.UploadWithContext(ctx, input) return err diff --git a/drivers/pikpak/meta.go b/drivers/pikpak/meta.go index 51ba5c46937a..845820935504 100644 --- a/drivers/pikpak/meta.go +++ b/drivers/pikpak/meta.go @@ -13,7 +13,8 @@ type Addition struct { ClientSecret string `json:"client_secret" required:"true" default:"dbw2OtmVEeuUvIptb1Coyg"` RefreshToken string `json:"refresh_token" required:"true" default:""` CaptchaToken string `json:"captcha_token" default:""` - DisableMediaLink bool `json:"disable_media_link"` + DeviceID string `json:"device_id" required:"false" default:""` + DisableMediaLink bool `json:"disable_media_link" default:"true"` } var config = driver.Config{ diff --git a/drivers/pikpak/util.go b/drivers/pikpak/util.go index 7eb2b803162f..b957967dc87f 100644 --- a/drivers/pikpak/util.go +++ b/drivers/pikpak/util.go @@ -48,11 +48,15 @@ const ( func (d *PikPak) login() error { url := "https://user.mypikpak.com/v1/auth/signin" - if err := d.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), d.Username); err != nil { - return err + // 使用 用户填写的 CaptchaToken —————— (验证后的captcha_token) + if d.GetCaptchaToken() == "" { + if err := d.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), d.Username); err != nil { + return err + } } + var e ErrResp - res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{ + res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e).SetBody(base.Json{ "captcha_token": d.GetCaptchaToken(), "client_id": ClientID, "client_secret": ClientSecret, @@ -69,53 +73,60 @@ func (d *PikPak) login() error { d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() d.AccessToken = jsoniter.Get(data, "access_token").ToString() d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) - d.Addition.RefreshToken = d.RefreshToken - op.MustSaveDriverStorage(d) return nil } -func (d *PikPak) refreshToken() error { - url := "https://user.mypikpak.com/v1/auth/token" - var e ErrResp - res, err := base.RestyClient.R().SetError(&e). - SetHeader("user-agent", "").SetBody(base.Json{ - "client_id": ClientID, - "client_secret": ClientSecret, - "grant_type": "refresh_token", - "refresh_token": d.RefreshToken, - }).SetQueryParam("client_id", ClientID).Post(url) - if err != nil { - d.Status = err.Error() - op.MustSaveDriverStorage(d) - return err - } - if e.ErrorCode != 0 { - if e.ErrorCode == 4126 { - // refresh_token invalid, re-login - return d.login() - } - d.Status = e.Error() - op.MustSaveDriverStorage(d) - return errors.New(e.Error()) - } - data := res.Body() - d.Status = "work" - d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() - d.AccessToken = jsoniter.Get(data, "access_token").ToString() - d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) - d.Addition.RefreshToken = d.RefreshToken - op.MustSaveDriverStorage(d) - return nil -} +//func (d *PikPak) refreshToken() error { +// url := "https://user.mypikpak.com/v1/auth/token" +// var e ErrResp +// res, err := base.RestyClient.SetRetryCount(1).R().SetError(&e). +// SetHeader("user-agent", "").SetBody(base.Json{ +// "client_id": ClientID, +// "client_secret": ClientSecret, +// "grant_type": "refresh_token", +// "refresh_token": d.RefreshToken, +// }).SetQueryParam("client_id", ClientID).Post(url) +// if err != nil { +// d.Status = err.Error() +// op.MustSaveDriverStorage(d) +// return err +// } +// if e.ErrorCode != 0 { +// if e.ErrorCode == 4126 { +// // refresh_token invalid, re-login +// return d.login() +// } +// d.Status = e.Error() +// op.MustSaveDriverStorage(d) +// return errors.New(e.Error()) +// } +// data := res.Body() +// d.Status = "work" +// d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString() +// d.AccessToken = jsoniter.Get(data, "access_token").ToString() +// d.Common.SetUserID(jsoniter.Get(data, "sub").ToString()) +// d.Addition.RefreshToken = d.RefreshToken +// op.MustSaveDriverStorage(d) +// return nil +//} func (d *PikPak) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := base.RestyClient.R() req.SetHeaders(map[string]string{ - "Authorization": "Bearer " + d.AccessToken, + //"Authorization": "Bearer " + d.AccessToken, "User-Agent": d.GetUserAgent(), "X-Device-ID": d.GetDeviceID(), "X-Captcha-Token": d.GetCaptchaToken(), }) + if d.oauth2Token != nil { + // 使用oauth2 获取 access_token + token, err := d.oauth2Token.Token() + if err != nil { + return nil, err + } + req.SetAuthScheme(token.TokenType).SetAuthToken(token.AccessToken) + } + if callback != nil { callback(req) } @@ -132,18 +143,31 @@ func (d *PikPak) request(url string, method string, callback base.ReqCallback, r switch e.ErrorCode { case 0: return res.Body(), nil - case 4122, 4121, 10, 16: - if err1 := d.refreshToken(); err1 != nil { - return nil, err1 + case 4122, 4121, 16: + // access_token 过期 + + //if err1 := d.refreshToken(); err1 != nil { + // return nil, err1 + //} + t, err := d.oauth2Token.Token() + if err != nil { + return nil, err } + d.AccessToken = t.AccessToken + d.RefreshToken = t.RefreshToken + d.Addition.RefreshToken = t.RefreshToken + op.MustSaveDriverStorage(d) + return d.request(url, method, callback, resp) case 9: // 验证码token过期 if err = d.RefreshCaptchaTokenAtLogin(GetAction(method, url), d.Common.UserID); err != nil { return nil, err } return d.request(url, method, callback, resp) + case 10: // 操作频繁 + return nil, errors.New(e.ErrorDescription) default: - return nil, err + return nil, errors.New(e.Error()) } } @@ -311,9 +335,9 @@ func (c *Common) GetCaptchaSign() (timestamp, sign string) { func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) error { param := CaptchaTokenRequest{ Action: action, - CaptchaToken: d.Common.CaptchaToken, + CaptchaToken: d.GetCaptchaToken(), ClientID: ClientID, - DeviceID: d.Common.DeviceID, + DeviceID: d.GetDeviceID(), Meta: metas, RedirectUri: "xlaccsdk01://xbase.cloud/callback?state=harbor", } @@ -328,15 +352,7 @@ func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) err } if e.IsError() { - return &e - } - - if resp.CaptchaToken == "" { - return fmt.Errorf("empty captchaToken") - } else { - // 对 被风控的情况 进行处理 - d.Addition.CaptchaToken = resp.CaptchaToken - op.MustSaveDriverStorage(d) + return errors.New(e.Error()) } if resp.Url != "" {