mirror of https://github.com/Xhofe/alist
				
				
				
			fix(xunlei):download link speed limit
							parent
							
								
									75c98429bf
								
							
						
					
					
						commit
						11830bb51c
					
				| 
						 | 
				
			
			@ -7,6 +7,7 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/Xhofe/alist/conf"
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +16,7 @@ import (
 | 
			
		|||
	"github.com/Xhofe/alist/utils"
 | 
			
		||||
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
 | 
			
		||||
	"github.com/go-resty/resty/v2"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type XunLeiCloud struct{}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,11 +48,65 @@ func (driver XunLeiCloud) Items() []base.Item {
 | 
			
		|||
			Required:    true,
 | 
			
		||||
			Description: "account password",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "captcha_token",
 | 
			
		||||
			Label: "verified captcha token",
 | 
			
		||||
			Type:  base.TypeString,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "root_folder",
 | 
			
		||||
			Label: "root folder file_id",
 | 
			
		||||
			Type:  base.TypeString,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "client_version",
 | 
			
		||||
			Label:    "client version",
 | 
			
		||||
			Default:  "7.43.0.7998",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "client_id",
 | 
			
		||||
			Label:    "client id",
 | 
			
		||||
			Default:  "Xp6vsxz_7IYVw2BB",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "client_secret",
 | 
			
		||||
			Label:    "client secret",
 | 
			
		||||
			Default:  "Xp6vsy4tN9toTVdMSpomVdXpRmES",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "algorithms",
 | 
			
		||||
			Label:    "algorithms",
 | 
			
		||||
			Default:  "hrVPGbeqYPs+CIscj05VpAtjalzY5yjpvlMS8bEo,DrI0uTP,HHK0VXyMgY0xk2K0o,BBaXsExvL3GadmIacjWv7ISUJp3ifAwqbJumu,5toJ7ejB+bh1,5LsZTFAFjgvFvIl1URBgOAJ,QcJ5Ry+,hYgZVz8r7REROaCYfd9,zw6gXgkk/8TtGrmx6EGfekPESLnbZfDFwqR,gtSwLnMBa8h12nF3DU6+LwEQPHxd,fMG8TvtAYbCkxuEbIm0Xi/Lb7Z",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "package_name",
 | 
			
		||||
			Label:    "package name",
 | 
			
		||||
			Default:  "com.xunlei.downloadprovider",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "user_agent",
 | 
			
		||||
			Label:    "user agent",
 | 
			
		||||
			Default:  "ANDROID-com.xunlei.downloadprovider/7.43.0.7998 netWorkType/WIFI appid/40 deviceName/Samsung_Sm-g9810 deviceModel/SM-G9810 OSVersion/7.1.2 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_0_9+) (JAVA 0)",
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:     "device_id",
 | 
			
		||||
			Label:    "device id",
 | 
			
		||||
			Default:  utils.GetMD5Encode(uuid.NewString()),
 | 
			
		||||
			Type:     base.TypeString,
 | 
			
		||||
			Required: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,10 +114,18 @@ func (driver XunLeiCloud) Save(account *model.Account, old *model.Account) error
 | 
			
		|||
	if account == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := GetClient(account)
 | 
			
		||||
	// 指定验证通过的captchaToken
 | 
			
		||||
	if client.captchaToken != "" {
 | 
			
		||||
		client.captchaToken = account.CaptchaToken
 | 
			
		||||
		account.CaptchaToken = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if client.token == "" {
 | 
			
		||||
		return client.Login(account)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	account.Status = "work"
 | 
			
		||||
	model.SaveAccount(account)
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +169,7 @@ func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.Fi
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time.Sleep(time.Millisecond * 400)
 | 
			
		||||
	files := make([]model.File, 0)
 | 
			
		||||
	for {
 | 
			
		||||
		var fileList FileList
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +226,9 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li
 | 
			
		|||
		return nil, base.ErrNotFile
 | 
			
		||||
	}
 | 
			
		||||
	var lFile Files
 | 
			
		||||
	_, err = GetClient(account).Request("GET", FILE_API_URL+"/"+file.Id, func(r *resty.Request) {
 | 
			
		||||
	clinet := GetClient(account)
 | 
			
		||||
	_, err = clinet.Request("GET", FILE_API_URL+"/{fileID}", func(r *resty.Request) {
 | 
			
		||||
		r.SetPathParam("fileID", file.Id)
 | 
			
		||||
		r.SetQueryParam("with_audit", "true")
 | 
			
		||||
		r.SetResult(&lFile)
 | 
			
		||||
	}, account)
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +237,7 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li
 | 
			
		|||
	}
 | 
			
		||||
	return &base.Link{
 | 
			
		||||
		Headers: []base.Header{
 | 
			
		||||
			{Name: "User-Agent", Value: base.UserAgent},
 | 
			
		||||
			{Name: "User-Agent", Value: clinet.userAgent},
 | 
			
		||||
		},
 | 
			
		||||
		Url: lFile.WebContentLink,
 | 
			
		||||
	}, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -201,7 +268,8 @@ func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account)
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = GetClient(account).Request("PATCH", FILE_API_URL+"/"+srcFile.Id, func(r *resty.Request) {
 | 
			
		||||
	_, err = GetClient(account).Request("PATCH", FILE_API_URL+"/{fileID}", func(r *resty.Request) {
 | 
			
		||||
		r.SetPathParam("fileID", srcFile.Id)
 | 
			
		||||
		r.SetBody(&base.Json{"name": filepath.Base(dst)})
 | 
			
		||||
	}, account)
 | 
			
		||||
	return err
 | 
			
		||||
| 
						 | 
				
			
			@ -270,7 +338,8 @@ func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = GetClient(account).Request("PATCH", FILE_API_URL+"/"+srcFile.Id+"/trash", func(r *resty.Request) {
 | 
			
		||||
	_, err = GetClient(account).Request("PATCH", FILE_API_URL+"/{fileID}/trash", func(r *resty.Request) {
 | 
			
		||||
		r.SetPathParam("fileID", srcFile.Id)
 | 
			
		||||
		r.SetBody(&base.Json{})
 | 
			
		||||
	}, account)
 | 
			
		||||
	return err
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +387,7 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
 | 
			
		|||
 | 
			
		||||
	param := resp.Resumable.Params
 | 
			
		||||
	if resp.UploadType == UPLOAD_TYPE_RESUMABLE {
 | 
			
		||||
		param.Endpoint = strings.TrimLeft(param.Endpoint, param.Bucket+".")
 | 
			
		||||
		client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
| 
						 | 
				
			
			@ -331,7 +401,6 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
 | 
			
		|||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	time.Sleep(time.Millisecond * 200)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,10 @@ type Erron struct {
 | 
			
		|||
	//	ErrorDetails   interface{} `json:"error_details"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Erron) HasError() bool {
 | 
			
		||||
	return e.ErrorCode != 0 || e.ErrorMsg != "" || e.ErrorDescription != ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *Erron) Error() string {
 | 
			
		||||
	return fmt.Sprintf("ErrorCode: %d ,Error: %s ,ErrorDescription: %s ", e.ErrorCode, e.ErrorMsg, e.ErrorDescription)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +29,7 @@ type CaptchaTokenRequest struct {
 | 
			
		|||
	ClientID     string            `json:"client_id"`
 | 
			
		||||
	DeviceID     string            `json:"device_id"`
 | 
			
		||||
	Meta         map[string]string `json:"meta"`
 | 
			
		||||
	//RedirectUri  string            `json:"redirect_uri"`
 | 
			
		||||
	RedirectUri  string            `json:"redirect_uri"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CaptchaTokenResponse struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -173,9 +177,3 @@ type UploadTaskResponse struct {
 | 
			
		|||
 | 
			
		||||
	File Files `json:"file"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Tasks struct {
 | 
			
		||||
	Tasks         []interface{}
 | 
			
		||||
	NextPageToken string `json:"next_page_token"`
 | 
			
		||||
	//ExpiresIn     int64  `json:"expires_in"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,41 +3,10 @@ package xunlei
 | 
			
		|||
import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"github.com/Xhofe/alist/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// 小米浏览器
 | 
			
		||||
	CLIENT_ID      = "X7MtiU0Gb5YqWv-6"
 | 
			
		||||
	CLIENT_SECRET  = "84MYEih3Eeu2HF4RrGce3Q"
 | 
			
		||||
	CLIENT_VERSION = "5.1.0.51045"
 | 
			
		||||
 | 
			
		||||
	ALG_VERSION  = "1"
 | 
			
		||||
	PACKAGE_NAME = "com.xunlei.xcloud.lib"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Algorithms = []string{
 | 
			
		||||
	"",
 | 
			
		||||
	"BXza40wm+P4zw8rEFpHA",
 | 
			
		||||
	"UfZLfKfYRmKTA0",
 | 
			
		||||
	"OMBGVt/9Wcaln1XaBz",
 | 
			
		||||
	"Jn217F4rk5FPPWyhoeV",
 | 
			
		||||
	"w5OwkGo0pGpb0Xe/XZ5T3",
 | 
			
		||||
	"5guM3DNiY4F78x49zQ97q75",
 | 
			
		||||
	"QXwn4D2j884wJgrYXjGClM/IVrJX",
 | 
			
		||||
	"NXBRosYvbHIm6w8vEB",
 | 
			
		||||
	"2kZ8Ie1yW2ib4O2iAkNpJobP",
 | 
			
		||||
	"11CoVJJQEc",
 | 
			
		||||
	"xf3QWysVwnVsNv5DCxU+cgNT1rK",
 | 
			
		||||
	"9eEfKkrqkfw",
 | 
			
		||||
	"T78dnANexYRbiZy",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	API_URL        = "https://api-pan.xunlei.com/drive/v1"
 | 
			
		||||
	FILE_API_URL   = API_URL + "/files"
 | 
			
		||||
| 
						 | 
				
			
			@ -45,9 +14,8 @@ const (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FOLDER = "drive#folder"
 | 
			
		||||
	FILE   = "drive#file"
 | 
			
		||||
 | 
			
		||||
	FOLDER    = "drive#folder"
 | 
			
		||||
	FILE      = "drive#file"
 | 
			
		||||
	RESUMABLE = "drive#resumable"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,18 +26,9 @@ const (
 | 
			
		|||
	UPLOAD_TYPE_URL       = "UPLOAD_TYPE_URL"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 验证码签名
 | 
			
		||||
func captchaSign(driverID string, time int64) string {
 | 
			
		||||
	str := fmt.Sprint(CLIENT_ID, CLIENT_VERSION, PACKAGE_NAME, driverID, time)
 | 
			
		||||
	for _, algorithm := range Algorithms {
 | 
			
		||||
		str = utils.GetMD5Encode(str + algorithm)
 | 
			
		||||
	}
 | 
			
		||||
	return ALG_VERSION + "." + str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAction(method string, u string) string {
 | 
			
		||||
	c, _ := url.Parse(u)
 | 
			
		||||
	return fmt.Sprint(method, ":", c.Path)
 | 
			
		||||
	return method + ":" + c.Path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 计算文件Gcid
 | 
			
		||||
| 
						 | 
				
			
			@ -102,13 +61,3 @@ func getGcid(r io.Reader, size int64) (string, error) {
 | 
			
		|||
	}
 | 
			
		||||
	return hex.EncodeToString(hash1.Sum(nil)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取driverID
 | 
			
		||||
func getDriverID(username string) string {
 | 
			
		||||
	interfaces, _ := net.Interfaces()
 | 
			
		||||
	str := username
 | 
			
		||||
	for _, inter := range interfaces {
 | 
			
		||||
		str += inter.HardwareAddr.String()
 | 
			
		||||
	}
 | 
			
		||||
	return utils.GetMD5Encode(str)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package xunlei
 | 
			
		|||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,12 +14,7 @@ import (
 | 
			
		|||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var xunleiClient = resty.New().
 | 
			
		||||
	SetHeaders(map[string]string{
 | 
			
		||||
		"Accept": "application/json;charset=UTF-8",
 | 
			
		||||
	}).
 | 
			
		||||
	SetTimeout(base.DefaultTimeout)
 | 
			
		||||
 | 
			
		||||
// 缓存登录状态
 | 
			
		||||
var userClients sync.Map
 | 
			
		||||
 | 
			
		||||
func GetClient(account *model.Account) *Client {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +23,15 @@ func GetClient(account *model.Account) *Client {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	client := &Client{
 | 
			
		||||
		Client:   xunleiClient,
 | 
			
		||||
		driverID: getDriverID(account.Username),
 | 
			
		||||
		Client: base.RestyClient,
 | 
			
		||||
 | 
			
		||||
		clientID:      account.ClientId,
 | 
			
		||||
		clientSecret:  account.ClientSecret,
 | 
			
		||||
		clientVersion: account.ClientVersion,
 | 
			
		||||
		packageName:   account.PackageName,
 | 
			
		||||
		algorithms:    strings.Split(account.Algorithms, ","),
 | 
			
		||||
		userAgent:     account.UserAgent,
 | 
			
		||||
		deviceID:      account.DeviceId,
 | 
			
		||||
	}
 | 
			
		||||
	userClients.Store(account.Username, client)
 | 
			
		||||
	return client
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +41,14 @@ type Client struct {
 | 
			
		|||
	*resty.Client
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
 | 
			
		||||
	driverID     string
 | 
			
		||||
	clientID      string
 | 
			
		||||
	clientSecret  string
 | 
			
		||||
	clientVersion string
 | 
			
		||||
	packageName   string
 | 
			
		||||
	algorithms    []string
 | 
			
		||||
	userAgent     string
 | 
			
		||||
	deviceID      string
 | 
			
		||||
 | 
			
		||||
	captchaToken string
 | 
			
		||||
 | 
			
		||||
	token        string
 | 
			
		||||
| 
						 | 
				
			
			@ -48,25 +58,26 @@ type Client struct {
 | 
			
		|||
 | 
			
		||||
// 请求验证码token
 | 
			
		||||
func (c *Client) requestCaptchaToken(action string, meta map[string]string) error {
 | 
			
		||||
	req := CaptchaTokenRequest{
 | 
			
		||||
	param := CaptchaTokenRequest{
 | 
			
		||||
		Action:       action,
 | 
			
		||||
		CaptchaToken: c.captchaToken,
 | 
			
		||||
		ClientID:     CLIENT_ID,
 | 
			
		||||
		DeviceID:     c.driverID,
 | 
			
		||||
		ClientID:     c.clientID,
 | 
			
		||||
		DeviceID:     c.deviceID,
 | 
			
		||||
		Meta:         meta,
 | 
			
		||||
		RedirectUri:  "xlaccsdk01://xunlei.com/callback?state=harbor",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var e Erron
 | 
			
		||||
	var resp CaptchaTokenResponse
 | 
			
		||||
	_, err := xunleiClient.R().
 | 
			
		||||
		SetBody(&req).
 | 
			
		||||
	_, err := c.Client.R().
 | 
			
		||||
		SetBody(¶m).
 | 
			
		||||
		SetError(&e).
 | 
			
		||||
		SetResult(&resp).
 | 
			
		||||
		Post(XLUSER_API_URL + "/shield/captcha/init")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if e.ErrorCode != 0 || e.ErrorMsg != "" {
 | 
			
		||||
	if e.HasError() {
 | 
			
		||||
		return &e
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +92,15 @@ func (c *Client) requestCaptchaToken(action string, meta map[string]string) erro
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 验证码签名
 | 
			
		||||
func (c *Client) captchaSign(time string) string {
 | 
			
		||||
	str := fmt.Sprint(c.clientID, c.clientVersion, c.packageName, c.deviceID, time)
 | 
			
		||||
	for _, algorithm := range c.algorithms {
 | 
			
		||||
		str = utils.GetMD5Encode(str + algorithm)
 | 
			
		||||
	}
 | 
			
		||||
	return "1." + str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 登录
 | 
			
		||||
func (c *Client) Login(account *model.Account) (err error) {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
| 
						 | 
				
			
			@ -103,13 +123,13 @@ func (c *Client) Login(account *model.Account) (err error) {
 | 
			
		|||
 | 
			
		||||
	var e Erron
 | 
			
		||||
	var resp TokenResponse
 | 
			
		||||
	_, err = xunleiClient.R().
 | 
			
		||||
	_, err = c.Client.R().
 | 
			
		||||
		SetResult(&resp).
 | 
			
		||||
		SetError(&e).
 | 
			
		||||
		SetBody(&SignInRequest{
 | 
			
		||||
			CaptchaToken: c.captchaToken,
 | 
			
		||||
			ClientID:     CLIENT_ID,
 | 
			
		||||
			ClientSecret: CLIENT_SECRET,
 | 
			
		||||
			ClientID:     c.clientID,
 | 
			
		||||
			ClientSecret: c.clientSecret,
 | 
			
		||||
			Username:     account.Username,
 | 
			
		||||
			Password:     account.Password,
 | 
			
		||||
		}).
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +138,7 @@ func (c *Client) Login(account *model.Account) (err error) {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if e.ErrorCode != 0 || e.ErrorMsg != "" {
 | 
			
		||||
	if e.HasError() {
 | 
			
		||||
		return &e
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,14 +157,15 @@ func (c *Client) RefreshCaptchaToken(action string) error {
 | 
			
		|||
	c.Lock()
 | 
			
		||||
	defer c.Unlock()
 | 
			
		||||
 | 
			
		||||
	ctime := time.Now().UnixMilli()
 | 
			
		||||
	return c.requestCaptchaToken(action, map[string]string{
 | 
			
		||||
		"captcha_sign":   captchaSign(c.driverID, ctime),
 | 
			
		||||
		"client_version": CLIENT_VERSION,
 | 
			
		||||
		"package_name":   PACKAGE_NAME,
 | 
			
		||||
		"timestamp":      fmt.Sprint(ctime),
 | 
			
		||||
	timestamp := fmt.Sprint(time.Now().UnixMilli())
 | 
			
		||||
	param := map[string]string{
 | 
			
		||||
		"client_version": c.clientVersion,
 | 
			
		||||
		"package_name":   c.packageName,
 | 
			
		||||
		"user_id":        c.userID,
 | 
			
		||||
	})
 | 
			
		||||
		"captcha_sign":   c.captchaSign(timestamp),
 | 
			
		||||
		"timestamp":      timestamp,
 | 
			
		||||
	}
 | 
			
		||||
	return c.requestCaptchaToken(action, param)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 刷新token
 | 
			
		||||
| 
						 | 
				
			
			@ -154,22 +175,27 @@ func (c *Client) RefreshToken() error {
 | 
			
		|||
 | 
			
		||||
	var e Erron
 | 
			
		||||
	var resp TokenResponse
 | 
			
		||||
	_, err := xunleiClient.R().
 | 
			
		||||
	_, err := c.Client.R().
 | 
			
		||||
		SetError(&e).
 | 
			
		||||
		SetResult(&resp).
 | 
			
		||||
		SetBody(&base.Json{
 | 
			
		||||
			"grant_type":    "refresh_token",
 | 
			
		||||
			"refresh_token": c.refreshToken,
 | 
			
		||||
			"client_id":     CLIENT_ID,
 | 
			
		||||
			"client_secret": CLIENT_SECRET,
 | 
			
		||||
			"client_id":     c.clientID,
 | 
			
		||||
			"client_secret": c.clientSecret,
 | 
			
		||||
		}).
 | 
			
		||||
		Post(XLUSER_API_URL + "/auth/token")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if e.ErrorCode != 0 || e.ErrorMsg != "" {
 | 
			
		||||
	if e.HasError() {
 | 
			
		||||
		return &e
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if resp.RefreshToken == "" {
 | 
			
		||||
		return base.ErrEmptyToken
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.token = resp.TokenType + " " + resp.AccessToken
 | 
			
		||||
	c.refreshToken = resp.RefreshToken
 | 
			
		||||
	c.userID = resp.UserID
 | 
			
		||||
| 
						 | 
				
			
			@ -178,13 +204,14 @@ func (c *Client) RefreshToken() error {
 | 
			
		|||
 | 
			
		||||
func (c *Client) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
 | 
			
		||||
	c.Lock()
 | 
			
		||||
	req := xunleiClient.R().
 | 
			
		||||
	req := c.Client.R().
 | 
			
		||||
		SetHeaders(map[string]string{
 | 
			
		||||
			"X-Device-Id":     c.driverID,
 | 
			
		||||
			"X-Device-Id":     c.deviceID,
 | 
			
		||||
			"Authorization":   c.token,
 | 
			
		||||
			"X-Captcha-Token": c.captchaToken,
 | 
			
		||||
		}).
 | 
			
		||||
		SetQueryParam("client_id", CLIENT_ID)
 | 
			
		||||
			"User-Agent":      c.userAgent,
 | 
			
		||||
			"client_id":       c.clientID,
 | 
			
		||||
		})
 | 
			
		||||
	if callback != nil {
 | 
			
		||||
		callback(req)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +232,7 @@ func (c *Client) Request(method string, url string, callback func(*resty.Request
 | 
			
		|||
	switch e.ErrorCode {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return res, nil
 | 
			
		||||
	case 4122, 4121: // token过期
 | 
			
		||||
	case 4122, 4121, 10: // token过期
 | 
			
		||||
		if err = c.RefreshToken(); err == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,13 @@ type Account struct {
 | 
			
		|||
	CustomHost    string `json:"custom_host"`
 | 
			
		||||
	ExtractFolder string `json:"extract_folder"`
 | 
			
		||||
	Bool1         bool   `json:"bool_1"`
 | 
			
		||||
	// for xunlei
 | 
			
		||||
	Algorithms    string `json:"algorithms"`
 | 
			
		||||
	ClientVersion string `json:"client_version"`
 | 
			
		||||
	PackageName   string `json:"package_name"`
 | 
			
		||||
	UserAgent    string `json:"user_agent"`
 | 
			
		||||
	CaptchaToken string `json:"captcha_token"`
 | 
			
		||||
	DeviceId     string `json:"device_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var accountsMap = make(map[string]Account)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue