From 8c035ae784f6fea01859bdd40af747b2eb8b1354 Mon Sep 17 00:00:00 2001 From: qjfoidnh Date: Wed, 6 Sep 2023 21:42:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E7=A7=92=E4=BC=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 测试版本谨慎更新, 由使用新接口带来的一切风险及后果自担 --- README.md | 4 + baidupcs/baidupcs.go | 26 +++--- baidupcs/pcserror/panerrorinfo.go | 6 +- baidupcs/prepare.go | 18 ++-- baidupcs/upload.go | 12 +++ internal/pcscommand/transfer.go | 27 +++--- internal/pcscommand/upload.go | 4 +- internal/pcsconfig/baidu.go | 3 + main.go | 135 +++++++++++++++++++----------- 9 files changed, 148 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index bf7b351..a5fe258 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,10 @@ iikira/BaiduPCS-Go was largely inspired by [GangZhuo/BaiduPCS](https://github.co [离线下载](#离线下载), 支持http/https/ftp/电驴/磁力链协议. # 版本更新 +**2023.09.06** v3.9.5-beta +- 恢复了秒传转存(支持长短链), 感谢油猴脚本开发者tousakarin的贡献 +- 新秒传接口需要开发者授权, 稳定性未知, 该测试版本仅供有秒传强需求的用户试用, 请谨慎更新 + **2023.09.05** v3.9.4 - fix #244, 修复断点上传时偶发崩溃 - 优化本地上传秒传失败时的处理逻辑 diff --git a/baidupcs/baidupcs.go b/baidupcs/baidupcs.go index b5c5a46..a4fef48 100644 --- a/baidupcs/baidupcs.go +++ b/baidupcs/baidupcs.go @@ -135,16 +135,17 @@ var ( type ( // BaiduPCS 百度 PCS API 详情 BaiduPCS struct { - appID int // app_id - isHTTPS bool // 是否启用https - uid uint64 // 百度uid - client *requester.HTTPClient // http 客户端 - pcsUA string - pcsAddr string - panUA string - isSetPanUA bool - ph *panhome.PanHome - cacheOpMap cachemap.CacheOpMap + appID int // app_id + isHTTPS bool // 是否启用https + uid uint64 // 百度uid + client *requester.HTTPClient // http 客户端 + accessToken string // accessToken + pcsUA string + pcsAddr string + panUA string + isSetPanUA bool + ph *panhome.PanHome + cacheOpMap cachemap.CacheOpMap } userInfoJSON struct { @@ -310,6 +311,11 @@ func (pcs *BaiduPCS) SetUID(uid uint64) { pcs.uid = uid } +// SetaccessToken 设置秒传转存用的accesstoken +func (pcs *BaiduPCS) SetaccessToken(accessToken string) { + pcs.accessToken = accessToken +} + // SetStoken 设置stoken func (pcs *BaiduPCS) SetStoken(stoken string) { pcs.lazyInit() diff --git a/baidupcs/pcserror/panerrorinfo.go b/baidupcs/pcserror/panerrorinfo.go index b1d4a68..580e3b0 100644 --- a/baidupcs/pcserror/panerrorinfo.go +++ b/baidupcs/pcserror/panerrorinfo.go @@ -117,7 +117,7 @@ func FindPanErr(errno int) (errmsg string) { case -7: return "该分享已删除或已取消" case -8: - return "该分享已经过期" + return "已存在同名文件" case -9: return "文件不存在" case -10: @@ -149,7 +149,7 @@ func FindPanErr(errno int) (errmsg string) { case -70: return "你分享的文件中包含病毒或疑似病毒,为了你和他人的数据安全,换个文件分享吧" case 2: - return "参数错误" + return "请稍后再试, 或更换保存路径" case 3: return "未登录或帐号无效" case 4: @@ -170,6 +170,8 @@ func FindPanErr(errno int) (errmsg string) { return "该文件禁止分享" case 132: return "您的帐号可能存在安全风险,为了确保为您本人操作,请先进行安全验证。" + case 9019: + return "accesstoken未设置或过期, 请使用setastoken命令设置`" default: return "未知错误" } diff --git a/baidupcs/prepare.go b/baidupcs/prepare.go index f5d3ef1..acb3ea1 100644 --- a/baidupcs/prepare.go +++ b/baidupcs/prepare.go @@ -2,6 +2,7 @@ package baidupcs import ( "bytes" + "fmt" "io" "net/http" "net/url" @@ -302,15 +303,16 @@ func (pcs *BaiduPCS) PrepareMove(cpmvJSON ...*CpMvJSON) (dataReadCloser io.ReadC // prepareRapidUpload 秒传文件, 不进行文件夹检查 func (pcs *BaiduPCS) prepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) { - pcsURL := pcs.generatePanURL("rapidupload", nil) - baiduPCSVerbose.Infof("%s URL: %s\n", OperationRapidUpload, pcsURL) - post := map[string]string{ - "rtype": "0", - "path": targetPath, - "content-md5": contentMD5, - "slice-md5": sliceMD5, - "content-length": strconv.FormatInt(length, 10), + bdstoken, pcsError := pcs.BDSToken() + if pcsError != nil { + return } + pcsURL := pcs.generatePCSURL2("xpan/file", "create", map[string]string{ + "access_token": pcs.accessToken, + "bdstoken": bdstoken, + }) + baiduPCSVerbose.Infof("%s URL: %s\n", OperationRapidUpload, pcsURL) + post := fmt.Sprintf("&block_list=[\"%s\"]&path=%s&size=%d&isdir=0&rtype=0", contentMD5, targetPath, length) baiduPCSVerbose.Infof("%s URL: %s, Post: %v\n", OperationRapidUpload, pcsURL, post) dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePan, OperationRapidUpload, http.MethodPost, pcsURL.String(), post, nil) diff --git a/baidupcs/upload.go b/baidupcs/upload.go index 9c36bc6..dfaa569 100644 --- a/baidupcs/upload.go +++ b/baidupcs/upload.go @@ -120,6 +120,18 @@ func (pcs *BaiduPCS) RapidUpload(targetPath, contentMD5, sliceMD5, dataContent, return } +// APIRapidUpload openapi秒传文件 +func (pcs *BaiduPCS) APIRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) { + defer func() { + if pcsError == nil { + // 更新缓存 + pcs.deleteCache([]string{path.Dir(targetPath)}) + } + }() + pcsError = pcs.rapidUpload(targetPath, strings.ToLower(contentMD5), strings.ToLower(sliceMD5), "", length) + return +} + func (pcs *BaiduPCS) rapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) { dataReadCloser, pcsError := pcs.PrepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length) if pcsError != nil { diff --git a/internal/pcscommand/transfer.go b/internal/pcscommand/transfer.go index 2ccd8d9..9df9439 100644 --- a/internal/pcscommand/transfer.go +++ b/internal/pcscommand/transfer.go @@ -3,7 +3,6 @@ package pcscommand import ( "encoding/base64" "fmt" - "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" "path" "regexp" "strconv" @@ -19,8 +18,8 @@ func RunShareTransfer(params []string, opt *baidupcs.TransferOption) { if len(params) == 1 { link = params[0] if strings.Contains(link, "bdlink=") || !strings.Contains(link, "pan.baidu.com/") { - //RunRapidTransfer(link) - fmt.Printf("%s失败: %s\n", baidupcs.OperationShareFileSavetoLocal, "秒传已不再被支持") + RunRapidTransfer(link) + //fmt.Printf("%s失败: %s\n", baidupcs.OperationShareFileSavetoLocal, "秒传已不再被支持") return } extracode = "none" @@ -127,19 +126,17 @@ func RunRapidTransfer(link string) { link = string(decodeBytes) } link = strings.TrimSpace(link) - substrs := strings.SplitN(link, "#", 7) - if len(substrs) == 7 { + substrs := strings.SplitN(link, "#", 4) + if len(substrs) == 4 { md5, slicemd5 := substrs[0], substrs[1] - dataContent := substrs[3] - dataOffset, err := strconv.ParseInt(substrs[2], 10, 64) - totalSize, err := strconv.ParseInt(substrs[4], 10, 64) - dataTime, err := strconv.ParseInt(substrs[5], 10, 64) - if err != nil { - fmt.Printf("%s失败: %s\n", baidupcs.OperationRapidLinkSavetoLocal, "秒传链接格式错误") - return - } - filename := path.Join(GetActiveUser().Workdir, substrs[6]) - RunRapidUpload(filename, md5, slicemd5, dataContent, "", dataOffset, totalSize, 4 * converter.KB, dataTime) + size, _ := strconv.ParseInt(substrs[2], 10, 64) + filename := path.Join(GetActiveUser().Workdir, substrs[3]) + RunRapidUpload(filename, md5, slicemd5, size) + } else if len(substrs) == 3 { + md5 := substrs[0] + size, _ := strconv.ParseInt(substrs[1], 10, 64) + filename := path.Join(GetActiveUser().Workdir, substrs[2]) + RunRapidUpload(filename, md5, "", size) } else { fmt.Printf("%s失败: %s\n", baidupcs.OperationRapidLinkSavetoLocal, "秒传链接格式错误") } diff --git a/internal/pcscommand/upload.go b/internal/pcscommand/upload.go index a22979d..fb11e28 100644 --- a/internal/pcscommand/upload.go +++ b/internal/pcscommand/upload.go @@ -42,13 +42,13 @@ func uploadPrintFormat(load int) string { } // RunRapidUpload 执行秒传文件, 前提是知道文件的大小, md5, 前256KB切片的 md5, crc32 -func RunRapidUpload(targetPath, contentMD5, sliceMD5, dataContent, crc32 string, offset, length, totalSize, dataTime int64) { +func RunRapidUpload(targetPath, contentMD5, sliceMD5 string, length int64) { dirname := path.Dir(targetPath) err := matchPathByShellPatternOnce(&dirname) if err != nil { fmt.Printf("警告: %s, 获取网盘路径 %s 错误, %s\n", baidupcs.OperationRapidUpload, dirname, err) } - err = GetBaiduPCS().RapidUpload(targetPath, contentMD5, sliceMD5, dataContent, "", offset, length, totalSize, dataTime) + err = GetBaiduPCS().APIRapidUpload(targetPath, contentMD5, sliceMD5, "", length) if err != nil { fmt.Printf("%s失败, 消息: %s\n", baidupcs.OperationRapidUpload, err) return diff --git a/internal/pcsconfig/baidu.go b/internal/pcsconfig/baidu.go index fcb3624..ff0437b 100644 --- a/internal/pcsconfig/baidu.go +++ b/internal/pcsconfig/baidu.go @@ -40,6 +40,8 @@ type Baidu struct { SBOXTKN string `json:"sboxtkn"` COOKIES string `json:"cookies"` + AccessToken string `json:"accesstoken"` + Workdir string `json:"workdir"` // 工作目录 } @@ -58,6 +60,7 @@ func (baidu *Baidu) BaiduPCS() *baidupcs.BaiduPCS { pcs.SetPCSUserAgent(Config.PCSUA) pcs.SetPanUserAgent(Config.PanUA) pcs.SetUID(baidu.UID) + pcs.SetaccessToken(baidu.AccessToken) return pcs } diff --git a/main.go b/main.go index ddf22b1..93cf3ff 100644 --- a/main.go +++ b/main.go @@ -55,7 +55,8 @@ const ( var ( // Version 版本号 - Version = "v3.9.4-devel" + //Version = "v3.9.4-devel" + Version = "v3.9.5-beta" historyFilePath = filepath.Join(pcsconfig.GetConfigDir(), "pcs_command_history.txt") reloadFn = func(c *cli.Context) error { @@ -154,7 +155,7 @@ func main() { lineArgs = args.Parse(line) numArgs = len(lineArgs) acceptCompleteFileCommands = []string{ - "cd", "cp", "download", "export", "fixmd5", "locate", "ls", "meta", "mkdir", "mv", "rm", "share", "transfer", "tree", "upload", + "cd", "cp", "download", "export", "fixmd5", "locate", "ls", "meta", "mkdir", "mv", "rapidupload", "rm", "setastoken", "share", "transfer", "tree", "upload", } closed = strings.LastIndex(line, " ") == len(line)-1 ) @@ -602,6 +603,41 @@ func main() { return nil }, }, + { + Name: "setastoken", + Usage: "设定当前账号的accessToken", + Description: ` + 设定当前登录帐号的accessToken: + 若不使用秒传链接转存, 可不设定; accessToken获取网址如下: + https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=L6g70tBRRIXLsY0Z3HwKqlRE&redirect_uri=oob&scope=netdisk + 请在浏览器登录当前网盘账户后打开上述链接, 如果出现授权确认界面请确认授权, 然后复制跳转到的页面url, 找到access_token=xxxxxxx&这段, xxxxxxx即为accessToken + 注意accessToken的有效期为一个月, 过期后请重复上述步骤更新token + + 示例: + BaiduPCS-Go accessToken 156.182v9052tgf1006c89891bsfb2401974.YmKOAwBD9yGaG2s4p5NNkX4CXeIbJxx4hAxotfS.PyuHEs +`, + Category: "百度帐号", + Before: reloadFn, + After: saveFunc, + Action: func(c *cli.Context) error { + activeUser := pcsconfig.Config.ActiveUser() + if activeUser.UID == 0 { + fmt.Println("请先登录") + return nil + } + if c.NArg() >= 2 { + cli.ShowCommandHelp(c, c.Command.Name) + return nil + } else if c.NArg() == 0 { + cli.ShowCommandHelp(c, c.Command.Name) + return nil + } + activeUser.AccessToken = c.Args().Get(0) + pcsconfig.Config.ActiveUserBaiduPCS().SetaccessToken(c.Args().Get(0)) + fmt.Printf("当前用户名: %s 成功设置accessToken: %s\n", activeUser.Name, activeUser.AccessToken) + return nil + }, + }, { Name: "who", Usage: "获取当前帐号", @@ -1248,53 +1284,52 @@ func main() { }, }, }, -// { -// Name: "rapidupload", -// Aliases: []string{"ru"}, -// Usage: "手动秒传文件", -// UsageText: app.Name + " rapidupload -length=<文件的大小> -md5=<文件的md5值> -slicemd5=<文件前256KB切片的md5值(可选)> -crc32=<文件的crc32值(可选)> <保存的网盘路径, 需包含文件名>", -// Description: ` -// 使用此功能秒传文件, 前提是知道文件的大小, md5, 前256KB切片的 md5 (可选), crc32 (可选), 且百度网盘中存在一模一样的文件. -// 上传的文件将会保存到网盘的目标目录. -// 遇到同名文件将会自动覆盖! -// -// 可能无法秒传 20GB 以上的文件!! -// -// 示例: -// -// 1. 如果秒传成功, 则保存到网盘路径 /test -// BaiduPCS-Go rapidupload -length=56276137 -md5=fbe082d80e90f90f0fb1f94adbbcfa7f -slicemd5=38c6a75b0ec4499271d4ea38a667ab61 -crc32=314332359 /test -//`, -// Category: "百度网盘", -// Before: reloadFn, -// Action: func(c *cli.Context) error { -// if c.NArg() <= 0 || !c.IsSet("md5") || !c.IsSet("length") || !c.IsSet("slicemd5") { -// cli.ShowCommandHelp(c, c.Command.Name) -// return nil -// } -// -// pcscommand.RunRapidUpload(c.Args().Get(0), c.String("md5"), c.String("slicemd5"), c.String("datacontent"), "", c.Int64("offset"), c.Int64("length")) -// return nil -// }, -// Flags: []cli.Flag{ -// cli.StringFlag{ -// Name: "md5", -// Usage: "文件的 md5 值", -// }, -// cli.StringFlag{ -// Name: "slicemd5", -// Usage: "文件前 256KB 切片的 md5 值", -// }, -// cli.StringFlag{ -// Name: "crc32", -// Usage: "文件的 crc32 值 (可选)", -// }, -// cli.Int64Flag{ -// Name: "length", -// Usage: "文件的大小", -// }, -// }, -// }, + { + Name: "rapidupload", + Aliases: []string{"ru"}, + Usage: "手动秒传文件", + UsageText: app.Name + " rapidupload -length=<文件的大小> -md5=<文件的md5值> -slicemd5=<文件前256KB切片的md5值(可选)> -crc32=<文件的crc32值(可选)> <保存的网盘路径, 需包含文件名>", + Description: ` + 使用此功能秒传文件, 前提是知道文件的大小, md5, 前256KB切片的 md5 (可选), crc32 (可选), 且百度网盘中存在一模一样的文件. + 上传的文件将会保存到网盘的目标目录. + 遇到同名文件将会自动覆盖! + + 可能无法秒传 20GB 以上的文件!! + + 示例: + + 1. 如果秒传成功, 则保存到网盘路径 /test + BaiduPCS-Go rapidupload -length=56276137 -md5=fbe082d80e90f90f0fb1f94adbbcfa7f -slicemd5=38c6a75b0ec4499271d4ea38a667ab61 -crc32=314332359 /test +`, + Category: "百度网盘", + Before: reloadFn, + Action: func(c *cli.Context) error { + if c.NArg() <= 0 || !c.IsSet("md5") || !c.IsSet("length") || !c.IsSet("slicemd5") { + cli.ShowCommandHelp(c, c.Command.Name) + return nil + } + pcscommand.RunRapidUpload(c.Args().Get(0), c.String("md5"), c.String("slicemd5"), c.Int64("length")) + return nil + }, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "md5", + Usage: "文件的 md5 值", + }, + cli.StringFlag{ + Name: "slicemd5", + Usage: "文件前 256KB 切片的 md5 值 (可选)", + }, + cli.StringFlag{ + Name: "crc32", + Usage: "文件的 crc32 值 (可选)", + }, + cli.Int64Flag{ + Name: "length", + Usage: "文件的大小", + }, + }, + }, { Name: "createsuperfile", Aliases: []string{"csf"}, @@ -1426,7 +1461,7 @@ func main() { Description: ` 转存文件/目录 如果没有提取码或为整合式链接,则第二个位置留空;只能转存到当前网盘目录下, - 分享链接支持只常规百度云链接, 不再支持秒传 + 分享链接支持常规百度云链接, 支持长短秒传链接 实例: BaiduPCS-Go transfer pan.baidu.com/s/1VYzSl7465sdrQXe8GT5RdQ 704e