alist/drivers/xunlei/xunlei.go

226 lines
4.5 KiB
Go

package xunlei
import (
"fmt"
"net/http"
"sync"
"time"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
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 {
if v, ok := userClients.Load(account.Username); ok {
return v.(*Client)
}
client := &Client{
Client: xunleiClient,
driverID: getDriverID(account.Username),
}
userClients.Store(account.Username, client)
return client
}
type Client struct {
*resty.Client
sync.Mutex
driverID string
captchaToken string
token string
refreshToken string
userID string
}
// 请求验证码token
func (c *Client) requestCaptchaToken(action string, meta map[string]string) error {
req := CaptchaTokenRequest{
Action: action,
CaptchaToken: c.captchaToken,
ClientID: CLIENT_ID,
DeviceID: c.driverID,
Meta: meta,
}
var e Erron
var resp CaptchaTokenResponse
_, err := xunleiClient.R().
SetBody(&req).
SetError(&e).
SetResult(&resp).
Post(XLUSER_API_URL + "/shield/captcha/init")
if err != nil {
return err
}
if e.ErrorCode != 0 || e.ErrorMsg != "" {
return &e
}
if resp.Url != "" {
return fmt.Errorf("need verify:%s", resp.Url)
}
if resp.CaptchaToken == "" {
return fmt.Errorf("empty captchaToken")
}
c.captchaToken = resp.CaptchaToken
return nil
}
// 登录
func (c *Client) Login(account *model.Account) (err error) {
c.Lock()
defer c.Unlock()
defer func() {
if err != nil {
account.Status = err.Error()
} else {
account.Status = "work"
}
model.SaveAccount(account)
}()
url := XLUSER_API_URL + "/auth/signin"
err = c.requestCaptchaToken(getAction(http.MethodPost, url), map[string]string{"username": account.Username})
if err != nil {
return err
}
var e Erron
var resp TokenResponse
_, err = xunleiClient.R().
SetResult(&resp).
SetError(&e).
SetBody(&SignInRequest{
CaptchaToken: c.captchaToken,
ClientID: CLIENT_ID,
ClientSecret: CLIENT_SECRET,
Username: account.Username,
Password: account.Password,
}).
Post(url)
if err != nil {
return err
}
if e.ErrorCode != 0 || e.ErrorMsg != "" {
return &e
}
if resp.RefreshToken == "" {
return base.ErrEmptyToken
}
c.token = resp.Token()
c.refreshToken = resp.RefreshToken
c.userID = resp.UserID
return nil
}
// 刷新验证码token
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),
"user_id": c.userID,
})
}
// 刷新token
func (c *Client) RefreshToken() error {
c.Lock()
defer c.Unlock()
var e Erron
var resp TokenResponse
_, err := xunleiClient.R().
SetError(&e).
SetResult(&resp).
SetBody(&base.Json{
"grant_type": "refresh_token",
"refresh_token": c.refreshToken,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
}).
Post(XLUSER_API_URL + "/auth/token")
if err != nil {
return err
}
if e.ErrorCode != 0 || e.ErrorMsg != "" {
return &e
}
c.token = resp.TokenType + " " + resp.AccessToken
c.refreshToken = resp.RefreshToken
c.userID = resp.UserID
return nil
}
func (c *Client) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
c.Lock()
req := xunleiClient.R().
SetHeaders(map[string]string{
"X-Device-Id": c.driverID,
"Authorization": c.token,
"X-Captcha-Token": c.captchaToken,
}).
SetQueryParam("client_id", CLIENT_ID)
if callback != nil {
callback(req)
}
c.Unlock()
res, err := req.Execute(method, url)
if err != nil {
return nil, err
}
log.Debug(res.String())
var e Erron
if err = utils.Json.Unmarshal(res.Body(), &e); err != nil {
return nil, err
}
// 处理错误
switch e.ErrorCode {
case 0:
return res, nil
case 4122, 4121: // token过期
if err = c.RefreshToken(); err == nil {
break
}
fallthrough
case 16: // 登录失效
if err = c.Login(account); err != nil {
return nil, err
}
case 9: // 验证码token过期
if err = c.RefreshCaptchaToken(getAction(method, url)); err != nil {
return nil, err
}
default:
return nil, &e
}
return c.Request(method, url, callback, account)
}