mirror of https://github.com/Xhofe/alist
				
				
				
			fix(pikpak): refresh_token cannot be obtained (#7017)
							parent
							
								
									6bff5b6107
								
							
						
					
					
						commit
						8e6c1aa78d
					
				| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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: <a target="_blank" href="%s">Click Here</a>`, 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: <a target="_blank" href="%s">Click Here</a>`, resp.Url)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.Common.RefreshCTokenCk != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue