diff --git a/drivers/pikpak/driver.go b/drivers/pikpak/driver.go index 1d086e0a..f0fc57f7 100644 --- a/drivers/pikpak/driver.go +++ b/drivers/pikpak/driver.go @@ -20,14 +20,14 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/go-resty/resty/v2" log "github.com/sirupsen/logrus" - "golang.org/x/oauth2" ) type PikPak struct { model.Storage Addition *Common - oauth2Token oauth2.TokenSource + RefreshToken string + AccessToken string } func (d *PikPak) Config() driver.Config { @@ -58,29 +58,27 @@ func (d *PikPak) Init(ctx context.Context) (err error) { } } - 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, - }, + if d.Addition.CaptchaToken != "" && d.Addition.RefreshToken == "" { + d.SetCaptchaToken(d.Addition.CaptchaToken) } - d.oauth2Token = oauth2.ReuseTokenSource(nil, utils.TokenSource(func() (*oauth2.Token, error) { - return oauth2Config.PasswordCredentialsToken( - context.WithValue(context.Background(), oauth2.HTTPClient, base.HttpClient), - d.Username, - d.Password, - ) - })) - - // 获取用户ID - _ = d.GetUserID() + // 如果已经有RefreshToken,直接刷新AccessToken + if d.Addition.RefreshToken != "" { + d.RefreshToken = d.Addition.RefreshToken + if err := d.refreshToken(); err != nil { + return err + } + } else { + if err := d.login(); err != nil { + return err + } + } // 获取CaptchaToken - _ = d.RefreshCaptchaTokenAtLogin(GetAction(http.MethodGet, "https://api-drive.mypikpak.com/drive/v1/files"), d.Common.UserID) + err = d.RefreshCaptchaTokenAtLogin(GetAction(http.MethodGet, "https://api-drive.mypikpak.com/drive/v1/files"), d.Common.UserID) + if err != nil { + return err + } // 更新UserAgent d.Common.UserAgent = BuildCustomUserAgent(d.Common.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, d.Common.UserID) return nil @@ -102,7 +100,7 @@ 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.requestWithCaptchaToken(fmt.Sprintf("https://api-drive.mypikpak.com/drive/v1/files/%s?_magic=2021&thumbnail_size=SIZE_LARGE", file.GetID()), + _, 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) if err != nil { return nil, err @@ -320,19 +318,4 @@ func (d *PikPak) DeleteOfflineTasks(ctx context.Context, taskIDs []string, delet return nil } -func (d *PikPak) GetUserID() error { - - token, err := d.oauth2Token.Token() - if err != nil { - return err - } - - userID := token.Extra("sub").(string) - - if userID != "" { - d.Common.SetUserID(userID) - } - return nil -} - var _ driver.Driver = (*PikPak)(nil) diff --git a/drivers/pikpak/meta.go b/drivers/pikpak/meta.go index c462ed13..51ba5c46 100644 --- a/drivers/pikpak/meta.go +++ b/drivers/pikpak/meta.go @@ -11,6 +11,8 @@ type Addition struct { Password string `json:"password" required:"true"` ClientID string `json:"client_id" required:"true" default:"YNxT9w7GMdWvEOKa"` 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"` } diff --git a/drivers/pikpak/util.go b/drivers/pikpak/util.go index d4cf64f0..7eb2b803 100644 --- a/drivers/pikpak/util.go +++ b/drivers/pikpak/util.go @@ -4,8 +4,11 @@ import ( "crypto/md5" "crypto/sha1" "encoding/hex" + "errors" "fmt" + "github.com/alist-org/alist/v3/internal/op" "github.com/alist-org/alist/v3/pkg/utils" + jsoniter "github.com/json-iterator/go" "net/http" "regexp" "strings" @@ -18,34 +21,101 @@ import ( // do others that not defined in Driver interface var Algorithms = []string{ - "PAe56I7WZ6FCSkFy77A96jHWcQA27ui80Qy4", - "SUbmk67TfdToBAEe2cZyP8vYVeN", - "1y3yFSZVWiGN95fw/2FQlRuH/Oy6WnO", - "8amLtHJpGzHPz4m9hGz7r+i+8dqQiAk", - "tmIEq5yl2g/XWwM3sKZkY4SbL8YUezrvxPksNabUJ", - "4QvudeJwgJuSf/qb9/wjC21L5aib", - "D1RJd+FZ+LBbt+dAmaIyYrT9gxJm0BB", - "1If", - "iGZr/SJPUFRkwvC174eelKy", + "Gez0T9ijiI9WCeTsKSg3SMlx", + "zQdbalsolyb1R/", + "ftOjr52zt51JD68C3s", + "yeOBMH0JkbQdEFNNwQ0RI9T3wU/v", + "BRJrQZiTQ65WtMvwO", + "je8fqxKPdQVJiy1DM6Bc9Nb1", + "niV", + "9hFCW2R1", + "sHKHpe2i96", + "p7c5E6AcXQ/IJUuAEC9W6", + "", + "aRv9hjc9P+Pbn+u3krN6", + "BzStcgE8qVdqjEH16l4", + "SqgeZvL5j9zoHP95xWHt", + "zVof5yaJkPe3VFpadPof", } const ( ClientID = "YNxT9w7GMdWvEOKa" ClientSecret = "dbw2OtmVEeuUvIptb1Coyg" - ClientVersion = "1.46.2" + ClientVersion = "1.47.1" PackageName = "com.pikcloud.pikpak" SdkVersion = "2.0.4.204000 " ) +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 + } + var e ErrResp + res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{ + "captcha_token": d.GetCaptchaToken(), + "client_id": ClientID, + "client_secret": ClientSecret, + "username": d.Username, + "password": d.Password, + }).SetQueryParam("client_id", ClientID).Post(url) + if err != nil { + return err + } + if e.ErrorCode != 0 { + return &e + } + data := res.Body() + 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) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := base.RestyClient.R() - - token, err := d.oauth2Token.Token() - if err != nil { - return nil, err - } - req.SetAuthScheme(token.TokenType).SetAuthToken(token.AccessToken) - + req.SetHeaders(map[string]string{ + "Authorization": "Bearer " + d.AccessToken, + "User-Agent": d.GetUserAgent(), + "X-Device-ID": d.GetDeviceID(), + "X-Captcha-Token": d.GetCaptchaToken(), + }) if callback != nil { callback(req) } @@ -59,48 +129,22 @@ func (d *PikPak) request(url string, method string, callback base.ReqCallback, r return nil, err } - if e.IsError() { - return nil, &e - } - return res.Body(), nil -} - -func (d *PikPak) requestWithCaptchaToken(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { - - data, err := d.request(url, method, func(req *resty.Request) { - req.SetHeaders(map[string]string{ - "User-Agent": d.GetUserAgent(), - "X-Device-ID": d.GetDeviceID(), - "X-Captcha-Token": d.GetCaptchaToken(), - }) - if callback != nil { - callback(req) - } - }, resp) - - errResp, ok := err.(*ErrResp) - if !ok { - return nil, err - } - - switch errResp.ErrorCode { + switch e.ErrorCode { case 0: - return data, nil - //case 4122, 4121, 10, 16: - // if d.refreshTokenFunc != nil { - // if err = xc.refreshTokenFunc(); err == nil { - // break - // } - // } - // return nil, err + return res.Body(), nil + case 4122, 4121, 10, 16: + if err1 := d.refreshToken(); err1 != nil { + return nil, err1 + } + 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) default: return nil, err } - return d.requestWithCaptchaToken(url, method, callback, resp) } func (d *PikPak) getFiles(id string) ([]File, error) { @@ -276,7 +320,7 @@ func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) err var e ErrResp var resp CaptchaTokenResponse _, err := d.request("https://user.mypikpak.com/v1/shield/captcha/init", http.MethodPost, func(req *resty.Request) { - req.SetError(&e).SetBody(param) + req.SetError(&e).SetBody(param).SetQueryParam("client_id", ClientID) }, &resp) if err != nil { @@ -287,12 +331,16 @@ func (d *PikPak) refreshCaptchaToken(action string, metas map[string]string) err return &e } - if resp.Url != "" { - return fmt.Errorf(`need verify: Click Here`, resp.Url) - } - if resp.CaptchaToken == "" { return fmt.Errorf("empty captchaToken") + } else { + // 对 被风控的情况 进行处理 + d.Addition.CaptchaToken = resp.CaptchaToken + op.MustSaveDriverStorage(d) + } + + if resp.Url != "" { + return fmt.Errorf(`need verify: Click Here`, resp.Url) } if d.Common.RefreshCTokenCk != nil {