diff --git a/drivers/thunderx/driver.go b/drivers/thunderx/driver.go index 7b5daf414ac..b9ee668c2f9 100644 --- a/drivers/thunderx/driver.go +++ b/drivers/thunderx/driver.go @@ -3,10 +3,6 @@ package thunderx import ( "context" "fmt" - "github.com/go-resty/resty/v2" - "net/http" - "strings" - "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/errs" @@ -18,6 +14,9 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/go-resty/resty/v2" + "net/http" + "strings" ) type ThunderX struct { @@ -41,26 +40,15 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { if x.XunLeiXCommon == nil { x.XunLeiXCommon = &XunLeiXCommon{ Common: &Common{ - client: base.NewRestyClient(), - Algorithms: []string{ - "lHwINjLeqssT28Ym99p5MvR", - "xvFcxvtqPKCa9Ajf", - "2ywOP8spKHzfuhZMUYZ9IpsViq0t8vT0", - "FTBrJism20SHKQ2m2", - "BHrWJsPwjnr5VeLtOUr2191X9uXhWmt", - "yu0QgHEjNmDoPNwXN17so2hQlDT83T", - "OcaMfLMCGZ7oYlvZGIbTqb4U7cCY", - "jBGGu0GzXOjtCXYwkOBb+c6TZ/Nymv", - "YLWRjVor2rOuYEL", - "94wjoPazejyNC+gRpOj+JOm1XXvxa", - }, + client: base.NewRestyClient(), + Algorithms: Algorithms, DeviceID: utils.GetMD5EncodeStr(x.Username + x.Password), - ClientID: "ZQL_zwA4qhHcoe_2", - ClientSecret: "Og9Vr1L8Ee6bh0olFxFDRg", - ClientVersion: "1.05.0.2115", - PackageName: "com.thunder.downloader", - UserAgent: "ANDROID-com.thunder.downloader/1.05.0.2115 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)", - DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)", + ClientID: ClientID, + ClientSecret: ClientSecret, + ClientVersion: ClientVersion, + PackageName: PackageName, + UserAgent: BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, ""), + DownloadUserAgent: DownloadUserAgent, UseVideoUrl: x.UseVideoUrl, refreshCTokenCk: func(token string) { @@ -76,6 +64,10 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { token, err = x.Login(x.Username, x.Password) if err != nil { x.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error())) + if token.UserID != "" { + x.SetUserID(token.UserID) + x.UserAgent = BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, token.UserID) + } op.MustSaveDriverStorage(x) } } @@ -86,10 +78,14 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { } // 自定义验证码token - ctoekn := strings.TrimSpace(x.CaptchaToken) - if ctoekn != "" { - x.SetCaptchaToken(ctoekn) + ctoken := strings.TrimSpace(x.CaptchaToken) + if ctoken != "" { + x.SetCaptchaToken(ctoken) + } + if x.DeviceID == "" { + x.SetDeviceID(utils.GetMD5EncodeStr(x.Username + x.Password)) } + x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.Addition.RootFolderID = x.RootFolderID // 防止重复登录 @@ -102,6 +98,10 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { return err } x.SetTokenResp(token) + if token.UserID != "" { + x.SetUserID(token.UserID) + x.UserAgent = BuildCustomUserAgent(x.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, token.UserID) + } } return nil } @@ -137,18 +137,33 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { DeviceID: func() string { if len(x.DeviceID) != 32 { - return utils.GetMD5EncodeStr(x.DeviceID) + if x.LoginType == "user" { + return utils.GetMD5EncodeStr(x.Username + x.Password) + } + return utils.GetMD5EncodeStr(x.ExpertAddition.RefreshToken) } return x.DeviceID }(), - ClientID: x.ClientID, - ClientSecret: x.ClientSecret, - ClientVersion: x.ClientVersion, - PackageName: x.PackageName, - UserAgent: x.UserAgent, - DownloadUserAgent: x.DownloadUserAgent, - UseVideoUrl: x.UseVideoUrl, - + ClientID: x.ClientID, + ClientSecret: x.ClientSecret, + ClientVersion: x.ClientVersion, + PackageName: x.PackageName, + UserAgent: func() string { + if x.ExpertAddition.UserAgent != "" { + return x.ExpertAddition.UserAgent + } + if x.LoginType == "user" { + return BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, "") + } + return BuildCustomUserAgent(utils.GetMD5EncodeStr(x.ExpertAddition.RefreshToken), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, "") + }(), + DownloadUserAgent: func() string { + if x.ExpertAddition.DownloadUserAgent != "" { + return x.ExpertAddition.DownloadUserAgent + } + return DownloadUserAgent + }(), + UseVideoUrl: x.UseVideoUrl, refreshCTokenCk: func(token string) { x.CaptchaToken = token op.MustSaveDriverStorage(x) @@ -156,8 +171,17 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { }, } - if x.CaptchaToken != "" { - x.SetCaptchaToken(x.CaptchaToken) + if x.ExpertAddition.CaptchaToken != "" { + x.SetCaptchaToken(x.ExpertAddition.CaptchaToken) + op.MustSaveDriverStorage(x) + } + if x.Common.DeviceID != "" { + x.ExpertAddition.DeviceID = x.Common.DeviceID + op.MustSaveDriverStorage(x) + } + if x.Common.DownloadUserAgent != "" { + x.ExpertAddition.DownloadUserAgent = x.Common.DownloadUserAgent + op.MustSaveDriverStorage(x) } x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.ExpertAddition.RootFolderID = x.RootFolderID @@ -177,7 +201,6 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { return err } x.SetTokenResp(token) - // 刷新token方法 x.SetRefreshTokenFunc(func() error { token, err := x.XunLeiXCommon.RefreshToken(x.TokenResp.RefreshToken) @@ -208,13 +231,19 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { return err }) } + // 更新 UserAgent + if x.TokenResp.UserID != "" { + x.ExpertAddition.UserAgent = BuildCustomUserAgent(x.ExpertAddition.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, x.TokenResp.UserID) + x.SetUserAgent(x.ExpertAddition.UserAgent) + op.MustSaveDriverStorage(x) + } } else { // 仅修改验证码token if x.CaptchaToken != "" { x.SetCaptchaToken(x.CaptchaToken) } - x.XunLeiXCommon.UserAgent = x.UserAgent - x.XunLeiXCommon.DownloadUserAgent = x.DownloadUserAgent + x.XunLeiXCommon.UserAgent = x.ExpertAddition.UserAgent + x.XunLeiXCommon.DownloadUserAgent = x.ExpertAddition.UserAgent x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.ExpertAddition.RootFolderID = x.RootFolderID } @@ -426,17 +455,17 @@ func (xc *XunLeiXCommon) getFiles(ctx context.Context, folderId string) ([]model return files, nil } -// 设置刷新Token的方法 +// SetRefreshTokenFunc 设置刷新Token的方法 func (xc *XunLeiXCommon) SetRefreshTokenFunc(fn func() error) { xc.refreshTokenFunc = fn } -// 设置Token +// SetTokenResp 设置Token func (xc *XunLeiXCommon) SetTokenResp(tr *TokenResp) { xc.TokenResp = tr } -// 携带Authorization和CaptchaToken的请求 +// Request 携带Authorization和CaptchaToken的请求 func (xc *XunLeiXCommon) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { data, err := xc.Common.Request(url, method, func(req *resty.Request) { req.SetHeaders(map[string]string{ @@ -473,7 +502,7 @@ func (xc *XunLeiXCommon) Request(url string, method string, callback base.ReqCal return xc.Request(url, method, callback, resp) } -// 刷新Token +// RefreshToken 刷新Token func (xc *XunLeiXCommon) RefreshToken(refreshToken string) (*TokenResp, error) { var resp TokenResp _, err := xc.Common.Request(XLUSER_API_URL+"/auth/token", http.MethodPost, func(req *resty.Request) { @@ -491,10 +520,11 @@ func (xc *XunLeiXCommon) RefreshToken(refreshToken string) (*TokenResp, error) { if resp.RefreshToken == "" { return nil, errs.EmptyToken } + resp.UserID = resp.Sub return &resp, nil } -// 登录 +// Login 登录 func (xc *XunLeiXCommon) Login(username, password string) (*TokenResp, error) { url := XLUSER_API_URL + "/auth/signin" err := xc.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), username) diff --git a/drivers/thunderx/meta.go b/drivers/thunderx/meta.go index 2c114c0f284..fa60ebbdb0a 100644 --- a/drivers/thunderx/meta.go +++ b/drivers/thunderx/meta.go @@ -23,7 +23,7 @@ type ExpertAddition struct { RefreshToken string `json:"refresh_token" required:"true" help:"login type is refresh_token,this is required"` // 签名方法1 - Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"lHwINjLeqssT28Ym99p5MvR,xvFcxvtqPKCa9Ajf,2ywOP8spKHzfuhZMUYZ9IpsViq0t8vT0,FTBrJism20SHKQ2m2,BHrWJsPwjnr5VeLtOUr2191X9uXhWmt,yu0QgHEjNmDoPNwXN17so2hQlDT83T,OcaMfLMCGZ7oYlvZGIbTqb4U7cCY,jBGGu0GzXOjtCXYwkOBb+c6TZ/Nymv,YLWRjVor2rOuYEL,94wjoPazejyNC+gRpOj+JOm1XXvxa"` + Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"kVy0WbPhiE4v6oxXZ88DvoA3Q,lON/AUoZKj8/nBtcE85mVbkOaVdVa,rLGffQrfBKH0BgwQ33yZofvO3Or,FO6HWqw,GbgvyA2,L1NU9QvIQIH7DTRt,y7llk4Y8WfYflt6,iuDp1WPbV3HRZudZtoXChxH4HNVBX5ZALe,8C28RTXmVcco0,X5Xh,7xe25YUgfGgD0xW3ezFS,,CKCR,8EmDjBo6h3eLaK7U6vU2Qys0NsMx,t2TeZBXKqbdP09Arh9C3"` // 签名方法2 CaptchaSign string `json:"captcha_sign" required:"true" help:"sign type is captcha_sign,this is required"` Timestamp string `json:"timestamp" required:"true" help:"sign type is captcha_sign,this is required"` @@ -32,15 +32,15 @@ type ExpertAddition struct { CaptchaToken string `json:"captcha_token"` // 必要且影响登录,由签名决定 - DeviceID string `json:"device_id" required:"true" default:"9aa5c268e7bcfc197a9ad88e2fb330e5"` + DeviceID string `json:"device_id" required:"false" default:""` ClientID string `json:"client_id" required:"true" default:"ZQL_zwA4qhHcoe_2"` ClientSecret string `json:"client_secret" required:"true" default:"Og9Vr1L8Ee6bh0olFxFDRg"` - ClientVersion string `json:"client_version" required:"true" default:"1.05.0.2115"` + ClientVersion string `json:"client_version" required:"true" default:"1.06.0.2132"` PackageName string `json:"package_name" required:"true" default:"com.thunder.downloader"` - //不影响登录,影响下载速度 - UserAgent string `json:"user_agent" required:"true" default:"ANDROID-com.thunder.downloader/1.05.0.2115 netWorkType/4G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gdcf98eab238b) (JAVA 0)"` - DownloadUserAgent string `json:"download_user_agent" required:"true" default:"Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)"` + ////不影响登录,影响下载速度 + UserAgent string `json:"user_agent" required:"false" default:""` + DownloadUserAgent string `json:"download_user_agent" required:"false" default:""` //优先使用视频链接代替下载链接 UseVideoUrl bool `json:"use_video_url"` @@ -85,7 +85,7 @@ func (i *Addition) GetIdentity() string { var config = driver.Config{ Name: "ThunderX", LocalSort: true, - OnlyProxy: true, + OnlyProxy: false, } var configExpert = driver.Config{ diff --git a/drivers/thunderx/util.go b/drivers/thunderx/util.go index 6fa323ebc28..661da87e0b0 100644 --- a/drivers/thunderx/util.go +++ b/drivers/thunderx/util.go @@ -1,12 +1,14 @@ package thunderx import ( + "crypto/md5" "crypto/sha1" "encoding/hex" "fmt" "io" "net/http" "regexp" + "strings" "time" "github.com/alist-org/alist/v3/drivers/base" @@ -20,6 +22,33 @@ const ( XLUSER_API_URL = "https://xluser-ssl.xunleix.com/v1" ) +var Algorithms = []string{ + "kVy0WbPhiE4v6oxXZ88DvoA3Q", + "lON/AUoZKj8/nBtcE85mVbkOaVdVa", + "rLGffQrfBKH0BgwQ33yZofvO3Or", + "FO6HWqw", + "GbgvyA2", + "L1NU9QvIQIH7DTRt", + "y7llk4Y8WfYflt6", + "iuDp1WPbV3HRZudZtoXChxH4HNVBX5ZALe", + "8C28RTXmVcco0", + "X5Xh", + "7xe25YUgfGgD0xW3ezFS", + "", + "CKCR", + "8EmDjBo6h3eLaK7U6vU2Qys0NsMx", + "t2TeZBXKqbdP09Arh9C3", +} + +const ( + ClientID = "ZQL_zwA4qhHcoe_2" + ClientSecret = "Og9Vr1L8Ee6bh0olFxFDRg" + ClientVersion = "1.06.0.2132" + PackageName = "com.thunder.downloader" + DownloadUserAgent = "Dalvik/2.1.0 (Linux; U; Android 13; M2004J7AC Build/SP1A.210812.016)" + SdkVersion = "2.0.3.203100 " +) + const ( FOLDER = "drive#folder" FILE = "drive#file" @@ -42,7 +71,7 @@ type Common struct { client *resty.Client captchaToken string - + userID string // 签名相关,二选一 Algorithms []string Timestamp, CaptchaSign string @@ -61,6 +90,18 @@ type Common struct { refreshCTokenCk func(token string) } +func (c *Common) SetDeviceID(deviceID string) { + c.DeviceID = deviceID +} + +func (c *Common) SetUserID(userID string) { + c.userID = userID +} + +func (c *Common) SetUserAgent(userAgent string) { + c.UserAgent = userAgent +} + func (c *Common) SetCaptchaToken(captchaToken string) { c.captchaToken = captchaToken } @@ -145,7 +186,7 @@ func (c *Common) refreshCaptchaToken(action string, metas map[string]string) err return nil } -// 只有基础信息的请求 +// Request 只有基础信息的请求 func (c *Common) Request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := c.client.R().SetHeaders(map[string]string{ "user-agent": c.UserAgent, @@ -200,3 +241,57 @@ func getGcid(r io.Reader, size int64) (string, error) { } return hex.EncodeToString(hash1.Sum(nil)), nil } + +func generateDeviceSign(deviceID, packageName string) string { + + signatureBase := fmt.Sprintf("%s%s%s%s", deviceID, packageName, "1", "appkey") + + sha1Hash := sha1.New() + sha1Hash.Write([]byte(signatureBase)) + sha1Result := sha1Hash.Sum(nil) + + sha1String := hex.EncodeToString(sha1Result) + + md5Hash := md5.New() + md5Hash.Write([]byte(sha1String)) + md5Result := md5Hash.Sum(nil) + + md5String := hex.EncodeToString(md5Result) + + deviceSign := fmt.Sprintf("div101.%s%s", deviceID, md5String) + + return deviceSign +} + +func BuildCustomUserAgent(deviceID, clientID, appName, sdkVersion, clientVersion, packageName, userID string) string { + deviceSign := generateDeviceSign(deviceID, packageName) + var sb strings.Builder + + sb.WriteString(fmt.Sprintf("ANDROID-%s/%s ", appName, clientVersion)) + sb.WriteString("protocolVersion/200 ") + sb.WriteString("accesstype/ ") + sb.WriteString(fmt.Sprintf("clientid/%s ", clientID)) + sb.WriteString(fmt.Sprintf("clientversion/%s ", clientVersion)) + sb.WriteString("action_type/ ") + sb.WriteString("networktype/WIFI ") + sb.WriteString("sessionid/ ") + sb.WriteString(fmt.Sprintf("deviceid/%s ", deviceID)) + sb.WriteString("providername/NONE ") + sb.WriteString(fmt.Sprintf("devicesign/%s ", deviceSign)) + sb.WriteString("refresh_token/ ") + sb.WriteString(fmt.Sprintf("sdkversion/%s ", sdkVersion)) + sb.WriteString(fmt.Sprintf("datetime/%d ", time.Now().UnixMilli())) + sb.WriteString(fmt.Sprintf("usrno/%s ", userID)) + sb.WriteString(fmt.Sprintf("appname/%s ", appName)) + sb.WriteString(fmt.Sprintf("session_origin/ ")) + sb.WriteString(fmt.Sprintf("grant_type/ ")) + sb.WriteString(fmt.Sprintf("appid/ ")) + sb.WriteString(fmt.Sprintf("clientip/ ")) + sb.WriteString(fmt.Sprintf("devicename/Xiaomi_M2004j7ac ")) + sb.WriteString(fmt.Sprintf("osversion/13 ")) + sb.WriteString(fmt.Sprintf("platformversion/10 ")) + sb.WriteString(fmt.Sprintf("accessmode/ ")) + sb.WriteString(fmt.Sprintf("devicemodel/M2004J7AC ")) + + return sb.String() +}