diff --git a/drivers/thunder_browser/driver.go b/drivers/thunder_browser/driver.go index f3a08f93..a389f610 100644 --- a/drivers/thunder_browser/driver.go +++ b/drivers/thunder_browser/driver.go @@ -2,10 +2,10 @@ package thunder_browser import ( "context" + "errors" "fmt" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" - "github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/op" "github.com/alist-org/alist/v3/pkg/utils" @@ -53,33 +53,17 @@ func (x *ThunderBrowser) Init(ctx context.Context) (err error) { if x.XunLeiBrowserCommon == nil { x.XunLeiBrowserCommon = &XunLeiBrowserCommon{ Common: &Common{ - client: base.NewRestyClient(), - Algorithms: []string{ - "x+I5XiTByg", - "6QU1x5DqGAV3JKg6h", - "VI1vL1WXr7st0es", - "n+/3yhlrnKs4ewhLgZhZ5ITpt554", - "UOip2PE7BLIEov/ZX6VOnsz", - "Q70h9lpViNCOC8sGVkar9o22LhBTjfP", - "IVHFuB1JcMlaZHnW", - "bKE", - "HZRbwxOiQx+diNopi6Nu", - "fwyasXgYL3rP314331b", - "LWxXAiSW4", - "UlWIjv1HGrC6Ngmt4Nohx", - "FOa+Lc0bxTDpTwIh2", - "0+RY", - "xmRVMqokHHpvsiH0", - }, + client: base.NewRestyClient(), + Algorithms: Algorithms, DeviceID: utils.GetMD5EncodeStr(x.Username + x.Password), - ClientID: "ZUBzD9J_XPXfn7f7", - ClientSecret: "yESVmHecEe6F0aou69vl-g", - ClientVersion: "1.0.7.1938", - PackageName: "com.xunlei.browser", - UserAgent: "ANDROID-com.xunlei.browser/1.0.7.1938 netWorkType/5G appid/22062 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/233100 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)", - DownloadUserAgent: "AndroidDownloadManager/12 (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), PackageName, SdkVersion, ClientVersion, PackageName), + DownloadUserAgent: DownloadUserAgent, UseVideoUrl: x.UseVideoUrl, - + RemoveWay: x.Addition.RemoveWay, refreshCTokenCk: func(token string) { x.CaptchaToken = token op.MustSaveDriverStorage(x) @@ -107,6 +91,9 @@ func (x *ThunderBrowser) Init(ctx context.Context) (err error) { if ctoekn != "" { x.SetCaptchaToken(ctoekn) } + if x.DeviceID == "" { + x.SetDeviceID(utils.GetMD5EncodeStr(x.Username + x.Password)) + } x.XunLeiBrowserCommon.UseVideoUrl = x.UseVideoUrl x.Addition.RootFolderID = x.RootFolderID // 防止重复登录 @@ -170,21 +157,36 @@ func (x *ThunderBrowserExpert) Init(ctx context.Context) (err error) { x.XunLeiBrowserCommon = &XunLeiBrowserCommon{ Common: &Common{ client: base.NewRestyClient(), - 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), x.PackageName, SdkVersion, x.ClientVersion, x.PackageName) + } + return BuildCustomUserAgent(utils.GetMD5EncodeStr(x.ExpertAddition.RefreshToken), x.PackageName, SdkVersion, x.ClientVersion, x.PackageName) + }(), + DownloadUserAgent: func() string { + if x.ExpertAddition.DownloadUserAgent != "" { + return x.ExpertAddition.DownloadUserAgent + } + return DownloadUserAgent + }(), + UseVideoUrl: x.UseVideoUrl, + RemoveWay: x.ExpertAddition.RemoveWay, refreshCTokenCk: func(token string) { x.CaptchaToken = token op.MustSaveDriverStorage(x) @@ -192,8 +194,21 @@ func (x *ThunderBrowserExpert) 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.UserAgent != "" { + x.ExpertAddition.UserAgent = x.Common.UserAgent + op.MustSaveDriverStorage(x) + } + if x.Common.DownloadUserAgent != "" { + x.ExpertAddition.DownloadUserAgent = x.Common.DownloadUserAgent + op.MustSaveDriverStorage(x) } x.XunLeiBrowserCommon.UseVideoUrl = x.UseVideoUrl x.ExpertAddition.RootFolderID = x.RootFolderID @@ -488,7 +503,8 @@ func (xc *XunLeiBrowserCommon) Remove(ctx context.Context, obj model.Obj) error } } - if xc.RemoveWay == "delete" && obj.GetPath() == ThunderDriveFileID { + // 先判断是否是特殊情况 + if obj.GetPath() == ThunderDriveFileID { _, err := xc.Request(FILE_API_URL+"/{fileID}/trash", http.MethodPatch, func(r *resty.Request) { r.SetContext(ctx) r.SetPathParam("fileID", obj.GetID()) @@ -503,12 +519,20 @@ func (xc *XunLeiBrowserCommon) Remove(ctx context.Context, obj model.Obj) error return err } - _, err := xc.Request(FILE_API_URL+":batchTrash", http.MethodPost, func(r *resty.Request) { - r.SetContext(ctx) - r.SetBody(&js) - }, nil) - return err - + // 根据用户选择的删除方式进行删除 + if xc.RemoveWay == "delete" { + _, err := xc.Request(FILE_API_URL+":batchDelete", http.MethodPost, func(r *resty.Request) { + r.SetContext(ctx) + r.SetBody(&js) + }, nil) + return err + } else { + _, err := xc.Request(FILE_API_URL+":batchTrash", http.MethodPost, func(r *resty.Request) { + r.SetContext(ctx) + r.SetBody(&js) + }, nil) + return err + } } func (xc *XunLeiBrowserCommon) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { @@ -756,7 +780,7 @@ func (xc *XunLeiBrowserCommon) RefreshToken(refreshToken string) (*TokenResp, er } if resp.RefreshToken == "" { - return nil, errs.EmptyToken + return nil, errors.New("refresh token is empty") } return &resp, nil } @@ -775,7 +799,7 @@ func (xc *XunLeiBrowserCommon) GetSafeAccessToken(safePassword string) (string, } if resp.Token == "" { - return "", errs.EmptyToken + return "", errors.New("SafePassword is incorrect ") } return resp.Token, nil } diff --git a/drivers/thunder_browser/meta.go b/drivers/thunder_browser/meta.go index 9d16cd78..247353b7 100644 --- a/drivers/thunder_browser/meta.go +++ b/drivers/thunder_browser/meta.go @@ -17,14 +17,15 @@ type ExpertAddition struct { SignType string `json:"sign_type" type:"select" options:"algorithms,captcha_sign" default:"algorithms"` // 登录方式1 - Username string `json:"username" required:"true" help:"login type is user,this is required"` - Password string `json:"password" required:"true" help:"login type is user,this is required"` - SafePassword string `json:"safe_password" required:"false" help:"login type is user,this is required"` // 超级保险箱密码 + Username string `json:"username" required:"true" help:"login type is user,this is required"` + Password string `json:"password" required:"true" help:"login type is user,this is required"` // 登录方式2 RefreshToken string `json:"refresh_token" required:"true" help:"login type is refresh_token,this is required"` + SafePassword string `json:"safe_password" required:"true" help:"super safe password"` // 超级保险箱密码 + // 签名方法1 - Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"x+I5XiTByg,6QU1x5DqGAV3JKg6h,VI1vL1WXr7st0es,n+/3yhlrnKs4ewhLgZhZ5ITpt554,UOip2PE7BLIEov/ZX6VOnsz,Q70h9lpViNCOC8sGVkar9o22LhBTjfP,IVHFuB1JcMlaZHnW,bKE,HZRbwxOiQx+diNopi6Nu,fwyasXgYL3rP314331b,LWxXAiSW4,UlWIjv1HGrC6Ngmt4Nohx,FOa+Lc0bxTDpTwIh2,0+RY,xmRVMqokHHpvsiH0"` + Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"p+ExqPV,LwdwKlprzv7cQBQmxN5,vc08P1NwUBnbGsl58LzTW,VVNeXaXmZ8HH1SJEnp6YpVFSFU,pNAOJ,CNChvyDehAmUR1TDodfOusBAx,MS98NnX4Np8nxvEh6Ulv+SMMKMzKvD34C7lGWbb,9MpFF21GnVOYku0NM9Y/hzsK471UCUZ2o+,EY1QfeA06fXlw9wZNoZaXEED5zZPvNWI,,sciE,FIPqgQDUUW1e0GkiBFd5w7mCQ,zW,75XFdEO0Gi"` // 签名方法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"` @@ -33,15 +34,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:"ZUBzD9J_XPXfn7f7"` ClientSecret string `json:"client_secret" required:"true" default:"yESVmHecEe6F0aou69vl-g"` - ClientVersion string `json:"client_version" required:"true" default:"1.0.7.1938"` + ClientVersion string `json:"client_version" required:"true" default:"1.0.8.2215"` PackageName string `json:"package_name" required:"true" default:"com.xunlei.browser"` // 不影响登录,影响下载速度 - UserAgent string `json:"user_agent" required:"true" default:"ANDROID-com.xunlei.browser/1.0.7.1938 netWorkType/5G appid/22062 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/233100 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)"` - DownloadUserAgent string `json:"download_user_agent" required:"true" default:"AndroidDownloadManager/12 (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"` @@ -76,7 +77,7 @@ type Addition struct { driver.RootID Username string `json:"username" required:"true"` Password string `json:"password" required:"true"` - SafePassword string `json:"safe_password" required:"false"` // 超级保险箱密码 + SafePassword string `json:"safe_password" required:"true"` // 超级保险箱密码 CaptchaToken string `json:"captcha_token"` UseVideoUrl bool `json:"use_video_url" default:"false"` RemoveWay string `json:"remove_way" required:"true" type:"select" options:"trash,delete"` @@ -90,7 +91,6 @@ func (i *Addition) GetIdentity() string { var config = driver.Config{ Name: "ThunderBrowser", LocalSort: true, - OnlyProxy: true, } var configExpert = driver.Config{ diff --git a/drivers/thunder_browser/util.go b/drivers/thunder_browser/util.go index fd8a4047..a5f6f663 100644 --- a/drivers/thunder_browser/util.go +++ b/drivers/thunder_browser/util.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "regexp" + "strings" "time" "github.com/alist-org/alist/v3/drivers/base" @@ -21,6 +22,32 @@ const ( XLUSER_API_URL = "https://xluser-ssl.xunlei.com/v1" ) +var Algorithms = []string{ + "p+ExqPV", + "LwdwKlprzv7cQBQmxN5", + "vc08P1NwUBnbGsl58LzTW", + "VVNeXaXmZ8HH1SJEnp6YpVFSFU", + "pNAOJ", + "CNChvyDehAmUR1TDodfOusBAx", + "MS98NnX4Np8nxvEh6Ulv+SMMKMzKvD34C7lGWbb", + "9MpFF21GnVOYku0NM9Y/hzsK471UCUZ2o+", + "EY1QfeA06fXlw9wZNoZaXEED5zZPvNWI", + "", + "sciE", + "FIPqgQDUUW1e0GkiBFd5w7mCQ", + "zW", + "75XFdEO0Gi", +} + +const ( + ClientID = "ZUBzD9J_XPXfn7f7" + ClientSecret = "yESVmHecEe6F0aou69vl-g" + ClientVersion = "1.0.8.2215" + PackageName = "com.xunlei.browser" + DownloadUserAgent = "AndroidDownloadManager/13 (Linux; U; Android 13; M2004J7AC Build/SP1A.210812.016)" + SdkVersion = "2.0.3.262" +) + const ( FOLDER = "drive#folder" FILE = "drive#file" @@ -74,6 +101,10 @@ type Common struct { refreshCTokenCk func(token string) } +func (c *Common) SetDeviceID(deviceID string) { + c.DeviceID = deviceID +} + func (c *Common) SetCaptchaToken(captchaToken string) { c.captchaToken = captchaToken } @@ -247,3 +278,41 @@ func EncryptPassword(password string) string { // 将哈希值转换为十六进制字符串 return hex.EncodeToString(hash[:]) } + +func generateDeviceSign(deviceID, packageName string) string { + + signatureBase := fmt.Sprintf("%s%s%s%s", deviceID, packageName, "22062", "a5d7416858147a4ab99573872ffccef8") + + 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, appName, sdkVersion, clientVersion, packageName string) string { + //deviceSign := generateDeviceSign(deviceID, packageName) + var sb strings.Builder + + sb.WriteString(fmt.Sprintf("ANDROID-%s/%s ", appName, clientVersion)) + sb.WriteString("networkType/WIFI ") + sb.WriteString(fmt.Sprintf("appid/%s ", "22062")) + sb.WriteString(fmt.Sprintf("deviceName/Xiaomi_M2004j7ac ")) + sb.WriteString(fmt.Sprintf("deviceModel/M2004J7AC ")) + sb.WriteString(fmt.Sprintf("OSVersion/13 ")) + sb.WriteString(fmt.Sprintf("protocolVersion/301 ")) + sb.WriteString(fmt.Sprintf("platformversion/10 ")) + sb.WriteString(fmt.Sprintf("sdkVersion/%s ", sdkVersion)) + sb.WriteString(fmt.Sprintf("Oauth2Client/0.9 (Linux 4_9_337-perf-sn-uotan-gd9d488809c3d) (JAVA 0) ")) + return sb.String() +}