github.com/fzfile/BaiduPCS-Go@v0.0.0-20200606205115-4408961cf336/main.go (about) 1 package main 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "github.com/fzfile/BaiduPCS-Go/baidupcs" 7 "github.com/fzfile/BaiduPCS-Go/internal/pcscommand" 8 "github.com/fzfile/BaiduPCS-Go/internal/pcsconfig" 9 "github.com/fzfile/BaiduPCS-Go/internal/pcsfunctions/pcsdownload" 10 _ "github.com/fzfile/BaiduPCS-Go/internal/pcsinit" 11 "github.com/fzfile/BaiduPCS-Go/internal/pcsupdate" 12 "github.com/fzfile/BaiduPCS-Go/pcsliner" 13 "github.com/fzfile/BaiduPCS-Go/pcsliner/args" 14 "github.com/fzfile/BaiduPCS-Go/pcstable" 15 "github.com/fzfile/BaiduPCS-Go/pcsutil" 16 "github.com/fzfile/BaiduPCS-Go/pcsutil/checksum" 17 "github.com/fzfile/BaiduPCS-Go/pcsutil/converter" 18 "github.com/fzfile/BaiduPCS-Go/pcsutil/escaper" 19 "github.com/fzfile/BaiduPCS-Go/pcsutil/getip" 20 "github.com/fzfile/BaiduPCS-Go/pcsutil/pcstime" 21 "github.com/fzfile/BaiduPCS-Go/pcsverbose" 22 "github.com/olekukonko/tablewriter" 23 "github.com/peterh/liner" 24 "github.com/urfave/cli" 25 "os" 26 "os/exec" 27 "path" 28 "path/filepath" 29 "runtime" 30 "sort" 31 "strconv" 32 "strings" 33 "unicode" 34 ) 35 36 const ( 37 // NameShortDisplayNum 文件名缩略显示长度 38 NameShortDisplayNum = 16 39 40 cryptoDescription = ` 41 可用的方法 <method>: 42 aes-128-ctr, aes-192-ctr, aes-256-ctr, 43 aes-128-cfb, aes-192-cfb, aes-256-cfb, 44 aes-128-ofb, aes-192-ofb, aes-256-ofb. 45 46 密钥 <key>: 47 aes-128 对应key长度为16, aes-192 对应key长度为24, aes-256 对应key长度为32, 48 如果key长度不符合, 则自动修剪key, 舍弃超出长度的部分, 长度不足的部分用'\0'填充. 49 50 GZIP <disable-gzip>: 51 在文件加密之前, 启用GZIP压缩文件; 文件解密之后启用GZIP解压缩文件, 默认启用, 52 如果不启用, 则无法检测文件是否解密成功, 解密文件时会保留源文件, 避免解密失败造成文件数据丢失.` 53 ) 54 55 var ( 56 // Version 版本号 57 Version = "v3.6.2-devel" 58 59 historyFilePath = filepath.Join(pcsconfig.GetConfigDir(), "pcs_command_history.txt") 60 reloadFn = func(c *cli.Context) error { 61 err := pcsconfig.Config.Reload() 62 if err != nil { 63 fmt.Printf("重载配置错误: %s\n", err) 64 } 65 return nil 66 } 67 saveFunc = func(c *cli.Context) error { 68 err := pcsconfig.Config.Save() 69 if err != nil { 70 fmt.Printf("保存配置错误: %s\n", err) 71 } 72 return nil 73 } 74 75 isCli bool 76 ) 77 78 func init() { 79 pcsutil.ChWorkDir() 80 81 err := pcsconfig.Config.Init() 82 switch err { 83 case nil: 84 case pcsconfig.ErrConfigFileNoPermission, pcsconfig.ErrConfigContentsParseError: 85 fmt.Fprintf(os.Stderr, "FATAL ERROR: config file error: %s\n", err) 86 os.Exit(1) 87 default: 88 fmt.Printf("WARNING: config init error: %s\n", err) 89 } 90 } 91 92 func main() { 93 defer pcsconfig.Config.Close() 94 95 app := cli.NewApp() 96 app.Name = "BaiduPCS-Go" 97 app.Version = Version 98 app.Author = "fzfile/BaiduPCS-Go: https://github.com/fzfile/BaiduPCS-Go" 99 app.Copyright = "(c) 2016-2020 fzfile." 100 app.Usage = "百度网盘客户端 for " + runtime.GOOS + "/" + runtime.GOARCH 101 app.Description = `BaiduPCS-Go 使用Go语言编写的百度网盘命令行客户端, 为操作百度网盘, 提供实用功能. 102 具体功能, 参见 COMMANDS 列表 103 104 特色: 105 网盘内列出文件和目录, 支持通配符匹配路径; 106 下载网盘内文件, 支持网盘内目录 (文件夹) 下载, 支持多个文件或目录下载, 支持断点续传和高并发高速下载. 107 108 --------------------------------------------------- 109 前往 https://github.com/fzfile/BaiduPCS-Go 以获取更多帮助信息! 110 前往 https://github.com/fzfile/BaiduPCS-Go/releases 以获取程序更新信息! 111 --------------------------------------------------- 112 113 交流反馈: 114 提交Issue: https://github.com/fzfile/BaiduPCS-Go/issues 115 邮箱: i@mail.fzfile.com` 116 117 app.Flags = []cli.Flag{ 118 cli.BoolFlag{ 119 Name: "verbose", 120 Usage: "启用调试", 121 EnvVar: pcsverbose.EnvVerbose, 122 Destination: &pcsverbose.IsVerbose, 123 }, 124 } 125 app.Action = func(c *cli.Context) { 126 if c.NArg() != 0 { 127 fmt.Printf("未找到命令: %s\n运行命令 %s help 获取帮助\n", c.Args().Get(0), app.Name) 128 return 129 } 130 131 isCli = true 132 pcsverbose.Verbosef("VERBOSE: 这是一条调试信息\n\n") 133 134 var ( 135 line = pcsliner.NewLiner() 136 err error 137 ) 138 139 line.History, err = pcsliner.NewLineHistory(historyFilePath) 140 if err != nil { 141 fmt.Printf("警告: 读取历史命令文件错误, %s\n", err) 142 } 143 144 line.ReadHistory() 145 defer func() { 146 line.DoWriteHistory() 147 line.Close() 148 }() 149 150 // tab 自动补全命令 151 line.State.SetCompleter(func(line string) (s []string) { 152 var ( 153 lineArgs = args.Parse(line) 154 numArgs = len(lineArgs) 155 acceptCompleteFileCommands = []string{ 156 "cd", "cp", "download", "export", "fixmd5", "locate", "ls", "meta", "mkdir", "mv", "rapidupload", "rm", "share", "tree", "upload", 157 } 158 closed = strings.LastIndex(line, " ") == len(line)-1 159 ) 160 161 for _, cmd := range app.Commands { 162 for _, name := range cmd.Names() { 163 if !strings.HasPrefix(name, line) { 164 continue 165 } 166 167 s = append(s, name+" ") 168 } 169 } 170 171 switch numArgs { 172 case 0: 173 return 174 case 1: 175 if !closed { 176 return 177 } 178 } 179 180 thisCmd := app.Command(lineArgs[0]) 181 if thisCmd == nil { 182 return 183 } 184 185 if !pcsutil.ContainsString(acceptCompleteFileCommands, thisCmd.FullName()) { 186 return 187 } 188 189 var ( 190 activeUser = pcsconfig.Config.ActiveUser() 191 pcs = pcsconfig.Config.ActiveUserBaiduPCS() 192 runeFunc = unicode.IsSpace 193 pcsRuneFunc = func(r rune) bool { 194 switch r { 195 case '\'', '"': 196 return true 197 } 198 return unicode.IsSpace(r) 199 } 200 targetPath string 201 ) 202 203 if !closed { 204 targetPath = lineArgs[numArgs-1] 205 escaper.EscapeStringsByRuneFunc(lineArgs[:numArgs-1], runeFunc) // 转义 206 } else { 207 escaper.EscapeStringsByRuneFunc(lineArgs, runeFunc) 208 } 209 210 switch { 211 case targetPath == "." || strings.HasSuffix(targetPath, "/."): 212 s = append(s, line+"/") 213 return 214 case targetPath == ".." || strings.HasSuffix(targetPath, "/.."): 215 s = append(s, line+"/") 216 return 217 } 218 219 var ( 220 targetDir string 221 isAbs = path.IsAbs(targetPath) 222 isDir = strings.LastIndex(targetPath, "/") == len(targetPath)-1 223 ) 224 225 if isAbs { 226 targetDir = path.Dir(targetPath) 227 } else { 228 targetDir = path.Join(activeUser.Workdir, targetPath) 229 if !isDir { 230 targetDir = path.Dir(targetDir) 231 } 232 } 233 files, err := pcs.CacheFilesDirectoriesList(targetDir, baidupcs.DefaultOrderOptions) 234 if err != nil { 235 return 236 } 237 238 // fmt.Println("-", targetDir, targetPath, "-") 239 240 for _, file := range files { 241 if file == nil { 242 continue 243 } 244 245 var ( 246 appendLine string 247 ) 248 249 // 已经有的情况 250 if !closed { 251 if !strings.HasPrefix(file.Path, path.Clean(path.Join(targetDir, path.Base(targetPath)))) { 252 if path.Base(targetDir) == path.Base(targetPath) { 253 appendLine = strings.Join(append(lineArgs[:numArgs-1], escaper.EscapeByRuneFunc(path.Join(targetPath, file.Filename), pcsRuneFunc)), " ") 254 goto handle 255 } 256 // fmt.Println(file.Path, targetDir, targetPath) 257 continue 258 } 259 // fmt.Println(path.Clean(path.Join(path.Dir(targetPath), file.Filename)), targetPath, file.Filename) 260 appendLine = strings.Join(append(lineArgs[:numArgs-1], escaper.EscapeByRuneFunc(path.Clean(path.Join(path.Dir(targetPath), file.Filename)), pcsRuneFunc)), " ") 261 goto handle 262 } 263 // 没有的情况 264 appendLine = strings.Join(append(lineArgs, escaper.EscapeByRuneFunc(file.Filename, pcsRuneFunc)), " ") 265 goto handle 266 267 handle: 268 if file.Isdir { 269 s = append(s, appendLine+"/") 270 continue 271 } 272 s = append(s, appendLine+" ") 273 continue 274 } 275 276 return 277 }) 278 279 fmt.Printf("提示: 方向键上下可切换历史命令.\n") 280 fmt.Printf("提示: Ctrl + A / E 跳转命令 首 / 尾.\n") 281 fmt.Printf("提示: 输入 help 获取帮助.\n") 282 283 for { 284 var ( 285 prompt string 286 activeUser = pcsconfig.Config.ActiveUser() 287 ) 288 289 if activeUser.Name != "" { 290 // 格式: BaiduPCS-Go:<工作目录> <百度ID>$ 291 // 工作目录太长时, 会自动缩略 292 prompt = app.Name + ":" + converter.ShortDisplay(path.Base(activeUser.Workdir), NameShortDisplayNum) + " " + activeUser.Name + "$ " 293 } else { 294 // BaiduPCS-Go > 295 prompt = app.Name + " > " 296 } 297 298 commandLine, err := line.State.Prompt(prompt) 299 switch err { 300 case liner.ErrPromptAborted: 301 return 302 case nil: 303 // continue 304 default: 305 fmt.Println(err) 306 return 307 } 308 309 line.State.AppendHistory(commandLine) 310 311 cmdArgs := args.Parse(commandLine) 312 if len(cmdArgs) == 0 { 313 continue 314 } 315 316 s := []string{os.Args[0]} 317 s = append(s, cmdArgs...) 318 319 // 恢复原始终端状态 320 // 防止运行命令时程序被结束, 终端出现异常 321 line.Pause() 322 c.App.Run(s) 323 line.Resume() 324 } 325 } 326 327 app.Commands = []cli.Command{ 328 { 329 Name: "run", 330 Usage: "执行系统命令", 331 Category: "其他", 332 Action: func(c *cli.Context) error { 333 if c.NArg() == 0 { 334 cli.ShowCommandHelp(c, c.Command.Name) 335 return nil 336 } 337 338 cmd := exec.Command(c.Args().First(), c.Args().Tail()...) 339 cmd.Stdout = os.Stdout 340 cmd.Stdin = os.Stdin 341 cmd.Stderr = os.Stderr 342 343 err := cmd.Run() 344 if err != nil { 345 fmt.Println(err) 346 } 347 348 return nil 349 }, 350 }, 351 { 352 Name: "env", 353 Usage: "显示程序环境变量", 354 Description: ` 355 BAIDUPCS_GO_CONFIG_DIR: 配置文件路径, 356 BAIDUPCS_GO_VERBOSE: 是否启用调试. 357 `, 358 Category: "其他", 359 Action: func(c *cli.Context) error { 360 envStr := "%s=\"%s\"\n" 361 envVar, ok := os.LookupEnv(pcsverbose.EnvVerbose) 362 if ok { 363 fmt.Printf(envStr, pcsverbose.EnvVerbose, envVar) 364 } else { 365 fmt.Printf(envStr, pcsverbose.EnvVerbose, "0") 366 } 367 368 envVar, ok = os.LookupEnv(pcsconfig.EnvConfigDir) 369 if ok { 370 fmt.Printf(envStr, pcsconfig.EnvConfigDir, envVar) 371 } else { 372 fmt.Printf(envStr, pcsconfig.EnvConfigDir, pcsconfig.GetConfigDir()) 373 } 374 375 return nil 376 }, 377 }, 378 { 379 Name: "update", 380 Usage: "检测程序更新", 381 Category: "其他", 382 Action: func(c *cli.Context) error { 383 if c.IsSet("y") { 384 if !c.Bool("y") { 385 return nil 386 } 387 } 388 pcsupdate.CheckUpdate(app.Version, c.Bool("y")) 389 return nil 390 }, 391 Flags: []cli.Flag{ 392 cli.BoolFlag{ 393 Name: "y", 394 Usage: "确认更新", 395 }, 396 }, 397 }, 398 { 399 Name: "login", 400 Usage: "登录百度账号", 401 Description: ` 402 示例: 403 BaiduPCS-Go login 404 BaiduPCS-Go login -username=liuhua 405 BaiduPCS-Go login -bduss=123456789 406 407 常规登录: 408 按提示一步一步来即可. 409 410 百度BDUSS获取方法: 411 参考这篇 Wiki: https://github.com/fzfile/BaiduPCS-Go/wiki/关于-获取百度-BDUSS 412 或者百度搜索: 获取百度BDUSS`, 413 Category: "百度帐号", 414 Before: reloadFn, 415 After: saveFunc, 416 Action: func(c *cli.Context) error { 417 var bduss, ptoken, stoken string 418 if c.IsSet("bduss") { 419 bduss = c.String("bduss") 420 ptoken = c.String("ptoken") 421 stoken = c.String("stoken") 422 } else if c.NArg() == 0 { 423 var err error 424 bduss, ptoken, stoken, err = pcscommand.RunLogin(c.String("username"), c.String("password")) 425 if err != nil { 426 fmt.Println(err) 427 return err 428 } 429 } else { 430 cli.ShowCommandHelp(c, c.Command.Name) 431 return nil 432 } 433 434 baidu, err := pcsconfig.Config.SetupUserByBDUSS(bduss, ptoken, stoken) 435 if err != nil { 436 fmt.Println(err) 437 return nil 438 } 439 440 fmt.Println("百度帐号登录成功:", baidu.Name) 441 return nil 442 }, 443 Flags: []cli.Flag{ 444 cli.StringFlag{ 445 Name: "username", 446 Usage: "登录百度帐号的用户名(手机号/邮箱/用户名)", 447 }, 448 cli.StringFlag{ 449 Name: "password", 450 Usage: "登录百度帐号的用户名的密码", 451 }, 452 cli.StringFlag{ 453 Name: "bduss", 454 Usage: "使用百度 BDUSS 来登录百度帐号", 455 }, 456 cli.StringFlag{ 457 Name: "ptoken", 458 Usage: "百度 PTOKEN, 配合 -bduss 参数使用 (可选)", 459 }, 460 cli.StringFlag{ 461 Name: "stoken", 462 Usage: "百度 STOKEN, 配合 -bduss 参数使用 (可选)", 463 }, 464 }, 465 }, 466 { 467 Name: "su", 468 Usage: "切换百度帐号", 469 Description: ` 470 切换已登录的百度帐号: 471 如果运行该条命令没有提供参数, 程序将会列出所有的百度帐号, 供选择切换. 472 473 示例: 474 BaiduPCS-Go su 475 BaiduPCS-Go su <uid or name> 476 `, 477 Category: "百度帐号", 478 Before: reloadFn, 479 After: saveFunc, 480 Action: func(c *cli.Context) error { 481 if c.NArg() >= 2 { 482 cli.ShowCommandHelp(c, c.Command.Name) 483 return nil 484 } 485 486 numLogins := pcsconfig.Config.NumLogins() 487 488 if numLogins == 0 { 489 fmt.Printf("未设置任何百度帐号, 不能切换\n") 490 return nil 491 } 492 493 var ( 494 inputData = c.Args().Get(0) 495 uid uint64 496 ) 497 498 if c.NArg() == 1 { 499 // 直接切换 500 uid, _ = strconv.ParseUint(inputData, 10, 64) 501 } else if c.NArg() == 0 { 502 // 输出所有帐号供选择切换 503 cli.HandleAction(app.Command("loglist").Action, c) 504 505 // 提示输入 index 506 var index string 507 fmt.Printf("输入要切换帐号的 # 值 > ") 508 _, err := fmt.Scanln(&index) 509 if err != nil { 510 return nil 511 } 512 513 if n, err := strconv.Atoi(index); err == nil && n >= 0 && n < numLogins { 514 uid = pcsconfig.Config.BaiduUserList[n].UID 515 } else { 516 fmt.Printf("切换用户失败, 请检查 # 值是否正确\n") 517 return nil 518 } 519 } else { 520 cli.ShowCommandHelp(c, c.Command.Name) 521 } 522 523 switchedUser, err := pcsconfig.Config.SwitchUser(&pcsconfig.BaiduBase{ 524 Name: inputData, 525 }) 526 if err != nil { 527 switchedUser, err = pcsconfig.Config.SwitchUser(&pcsconfig.BaiduBase{ 528 UID: uid, 529 }) 530 if err != nil { 531 fmt.Printf("切换用户失败, %s\n", err) 532 return nil 533 } 534 } 535 536 fmt.Printf("切换用户: %s\n", switchedUser.Name) 537 return nil 538 }, 539 }, 540 { 541 Name: "logout", 542 Usage: "退出百度帐号", 543 Description: "退出当前登录的百度帐号", 544 Category: "百度帐号", 545 Before: reloadFn, 546 After: saveFunc, 547 Action: func(c *cli.Context) error { 548 if pcsconfig.Config.NumLogins() == 0 { 549 fmt.Println("未设置任何百度帐号, 不能退出") 550 return nil 551 } 552 553 var ( 554 confirm string 555 activeUser = pcsconfig.Config.ActiveUser() 556 ) 557 558 if !c.Bool("y") { 559 fmt.Printf("确认退出百度帐号: %s ? (y/n) > ", activeUser.Name) 560 _, err := fmt.Scanln(&confirm) 561 if err != nil || (confirm != "y" && confirm != "Y") { 562 return err 563 } 564 } 565 566 deletedUser, err := pcsconfig.Config.DeleteUser(&pcsconfig.BaiduBase{ 567 UID: activeUser.UID, 568 }) 569 if err != nil { 570 fmt.Printf("退出用户 %s, 失败, 错误: %s\n", activeUser.Name, err) 571 } 572 573 fmt.Printf("退出用户成功, %s\n", deletedUser.Name) 574 return nil 575 }, 576 Flags: []cli.Flag{ 577 cli.BoolFlag{ 578 Name: "y", 579 Usage: "确认退出帐号", 580 }, 581 }, 582 }, 583 { 584 Name: "loglist", 585 Usage: "列出帐号列表", 586 Description: "列出所有已登录的百度帐号", 587 Category: "百度帐号", 588 Before: reloadFn, 589 Action: func(c *cli.Context) error { 590 fmt.Println(pcsconfig.Config.BaiduUserList.String()) 591 return nil 592 }, 593 }, 594 { 595 Name: "who", 596 Usage: "获取当前帐号", 597 Description: "获取当前帐号的信息", 598 Category: "百度帐号", 599 Before: reloadFn, 600 Action: func(c *cli.Context) error { 601 activeUser := pcsconfig.Config.ActiveUser() 602 fmt.Printf("当前帐号 uid: %d, 用户名: %s, 性别: %s, 年龄: %.1f\n", activeUser.UID, activeUser.Name, activeUser.Sex, activeUser.Age) 603 return nil 604 }, 605 }, 606 { 607 Name: "quota", 608 Usage: "获取网盘配额", 609 Description: "获取网盘的总储存空间, 和已使用的储存空间", 610 Category: "百度网盘", 611 Before: reloadFn, 612 Action: func(c *cli.Context) error { 613 pcscommand.RunGetQuota() 614 return nil 615 }, 616 }, 617 { 618 Name: "cd", 619 Category: "百度网盘", 620 Usage: "切换工作目录", 621 Description: ` 622 BaiduPCS-Go cd <目录, 绝对路径或相对路径> 623 624 示例: 625 626 切换 /我的资源 工作目录: 627 BaiduPCS-Go cd /我的资源 628 629 切换上级目录: 630 BaiduPCS-Go cd .. 631 632 切换根目录: 633 BaiduPCS-Go cd / 634 635 切换 /我的资源 工作目录, 并自动列出 /我的资源 下的文件和目录 636 BaiduPCS-Go cd -l 我的资源 637 638 使用通配符: 639 BaiduPCS-Go cd /我的* 640 `, 641 Before: reloadFn, 642 After: saveFunc, 643 Action: func(c *cli.Context) error { 644 if c.NArg() == 0 { 645 cli.ShowCommandHelp(c, c.Command.Name) 646 return nil 647 } 648 649 pcscommand.RunChangeDirectory(c.Args().Get(0), c.Bool("l")) 650 651 return nil 652 }, 653 Flags: []cli.Flag{ 654 cli.BoolFlag{ 655 Name: "l", 656 Usage: "切换工作目录后自动列出工作目录下的文件和目录", 657 }, 658 }, 659 }, 660 { 661 Name: "ls", 662 Aliases: []string{"l", "ll"}, 663 Usage: "列出目录", 664 UsageText: app.Name + " ls <目录>", 665 Description: ` 666 列出当前工作目录内的文件和目录, 或指定目录内的文件和目录 667 668 示例: 669 670 列出 我的资源 内的文件和目录 671 BaiduPCS-Go ls 我的资源 672 673 绝对路径 674 BaiduPCS-Go ls /我的资源 675 676 降序排序 677 BaiduPCS-Go ls -desc 我的资源 678 679 按文件大小降序排序 680 BaiduPCS-Go ls -size -desc 我的资源 681 682 使用通配符 683 BaiduPCS-Go ls /我的* 684 `, 685 Category: "百度网盘", 686 Before: reloadFn, 687 Action: func(c *cli.Context) error { 688 orderOptions := &baidupcs.OrderOptions{} 689 switch { 690 case c.IsSet("asc"): 691 orderOptions.Order = baidupcs.OrderAsc 692 case c.IsSet("desc"): 693 orderOptions.Order = baidupcs.OrderDesc 694 default: 695 orderOptions.Order = baidupcs.OrderAsc 696 } 697 698 switch { 699 case c.IsSet("time"): 700 orderOptions.By = baidupcs.OrderByTime 701 case c.IsSet("name"): 702 orderOptions.By = baidupcs.OrderByName 703 case c.IsSet("size"): 704 orderOptions.By = baidupcs.OrderBySize 705 default: 706 orderOptions.By = baidupcs.OrderByName 707 } 708 709 pcscommand.RunLs(c.Args().Get(0), &pcscommand.LsOptions{ 710 Total: c.Bool("l") || c.Parent().Args().Get(0) == "ll", 711 }, orderOptions) 712 713 return nil 714 }, 715 Flags: []cli.Flag{ 716 cli.BoolFlag{ 717 Name: "l", 718 Usage: "详细显示", 719 }, 720 cli.BoolFlag{ 721 Name: "asc", 722 Usage: "升序排序", 723 }, 724 cli.BoolFlag{ 725 Name: "desc", 726 Usage: "降序排序", 727 }, 728 cli.BoolFlag{ 729 Name: "time", 730 Usage: "根据时间排序", 731 }, 732 cli.BoolFlag{ 733 Name: "name", 734 Usage: "根据文件名排序", 735 }, 736 cli.BoolFlag{ 737 Name: "size", 738 Usage: "根据大小排序", 739 }, 740 }, 741 }, 742 { 743 Name: "search", 744 Aliases: []string{"s"}, 745 Usage: "搜索文件", 746 UsageText: app.Name + " search [-path=<需要检索的目录>] [-r] 关键字", 747 Description: ` 748 按文件名搜索文件(不支持查找目录)。 749 默认在当前工作目录搜索. 750 751 示例: 752 753 搜索根目录的文件 754 BaiduPCS-Go search -path=/ 关键字 755 756 搜索当前工作目录的文件 757 BaiduPCS-Go search 关键字 758 759 递归搜索当前工作目录的文件 760 BaiduPCS-Go search -r 关键字 761 `, 762 Category: "百度网盘", 763 Before: reloadFn, 764 Action: func(c *cli.Context) error { 765 if c.NArg() < 1 { 766 cli.ShowCommandHelp(c, c.Command.Name) 767 return nil 768 } 769 770 pcscommand.RunSearch(c.String("path"), c.Args().Get(0), &pcscommand.SearchOptions{ 771 Total: c.Bool("l"), 772 Recurse: c.Bool("r"), 773 }) 774 775 return nil 776 }, 777 Flags: []cli.Flag{ 778 cli.BoolFlag{ 779 Name: "l", 780 Usage: "详细显示", 781 }, 782 cli.BoolFlag{ 783 Name: "r", 784 Usage: "递归搜索", 785 }, 786 cli.StringFlag{ 787 Name: "path", 788 Usage: "需要检索的目录", 789 Value: ".", 790 }, 791 }, 792 }, 793 { 794 Name: "tree", 795 Aliases: []string{"t"}, 796 Usage: "列出目录的树形图", 797 UsageText: app.Name + " tree <目录>", 798 Category: "百度网盘", 799 Before: reloadFn, 800 Action: func(c *cli.Context) error { 801 pcscommand.RunTree(c.Args().Get(0)) 802 return nil 803 }, 804 }, 805 { 806 Name: "pwd", 807 Usage: "输出工作目录", 808 UsageText: app.Name + " pwd", 809 Category: "百度网盘", 810 Before: reloadFn, 811 Action: func(c *cli.Context) error { 812 fmt.Println(pcsconfig.Config.ActiveUser().Workdir) 813 return nil 814 }, 815 }, 816 { 817 Name: "meta", 818 Usage: "获取文件/目录的元信息", 819 UsageText: app.Name + " meta <文件/目录1> <文件/目录2> <文件/目录3> ...", 820 Description: "默认获取工作目录元信息", 821 Category: "百度网盘", 822 Before: reloadFn, 823 Action: func(c *cli.Context) error { 824 var ( 825 ca = c.Args() 826 as []string 827 ) 828 if len(ca) == 0 { 829 as = []string{""} 830 } else { 831 as = ca 832 } 833 834 pcscommand.RunGetMeta(as...) 835 return nil 836 }, 837 }, 838 { 839 Name: "rm", 840 Usage: "删除文件/目录", 841 UsageText: app.Name + " rm <文件/目录的路径1> <文件/目录2> <文件/目录3> ...", 842 Description: ` 843 注意: 删除多个文件和目录时, 请确保每一个文件和目录都存在, 否则删除操作会失败. 844 被删除的文件或目录可在网盘文件回收站找回. 845 846 示例: 847 848 删除 /我的资源/1.mp4 849 BaiduPCS-Go rm /我的资源/1.mp4 850 851 删除 /我的资源/1.mp4 和 /我的资源/2.mp4 852 BaiduPCS-Go rm /我的资源/1.mp4 /我的资源/2.mp4 853 854 删除 /我的资源 内的所有文件和目录, 但不删除该目录 855 BaiduPCS-Go rm /我的资源/* 856 857 删除 /我的资源 整个目录 !! 858 BaiduPCS-Go rm /我的资源 859 `, 860 Category: "百度网盘", 861 Before: reloadFn, 862 Action: func(c *cli.Context) error { 863 if c.NArg() == 0 { 864 cli.ShowCommandHelp(c, c.Command.Name) 865 return nil 866 } 867 868 pcscommand.RunRemove(c.Args()...) 869 return nil 870 }, 871 }, 872 { 873 Name: "mkdir", 874 Usage: "创建目录", 875 UsageText: app.Name + " mkdir <目录>", 876 Category: "百度网盘", 877 Before: reloadFn, 878 Action: func(c *cli.Context) error { 879 if c.NArg() == 0 { 880 cli.ShowCommandHelp(c, c.Command.Name) 881 return nil 882 } 883 884 pcscommand.RunMkdir(c.Args().Get(0)) 885 return nil 886 }, 887 }, 888 { 889 Name: "cp", 890 Usage: "拷贝文件/目录", 891 UsageText: `BaiduPCS-Go cp <文件/目录> <目标文件/目录> 892 BaiduPCS-Go cp <文件/目录1> <文件/目录2> <文件/目录3> ... <目标目录>`, 893 Description: ` 894 注意: 拷贝多个文件和目录时, 请确保每一个文件和目录都存在, 否则拷贝操作会失败. 895 896 示例: 897 898 将 /我的资源/1.mp4 复制到 根目录 / 899 BaiduPCS-Go cp /我的资源/1.mp4 / 900 901 将 /我的资源/1.mp4 和 /我的资源/2.mp4 复制到 根目录 / 902 BaiduPCS-Go cp /我的资源/1.mp4 /我的资源/2.mp4 / 903 `, 904 Category: "百度网盘", 905 Before: reloadFn, 906 Action: func(c *cli.Context) error { 907 if c.NArg() <= 1 { 908 cli.ShowCommandHelp(c, c.Command.Name) 909 return nil 910 } 911 912 pcscommand.RunCopy(c.Args()...) 913 return nil 914 }, 915 }, 916 { 917 Name: "mv", 918 Usage: "移动/重命名文件/目录", 919 UsageText: `移动: 920 BaiduPCS-Go mv <文件/目录1> <文件/目录2> <文件/目录3> ... <目标目录> 921 922 重命名: 923 BaiduPCS-Go mv <文件/目录> <重命名的文件/目录>`, 924 Description: ` 925 注意: 移动多个文件和目录时, 请确保每一个文件和目录都存在, 否则移动操作会失败. 926 927 示例: 928 929 将 /我的资源/1.mp4 移动到 根目录 / 930 BaiduPCS-Go mv /我的资源/1.mp4 / 931 932 将 /我的资源/1.mp4 重命名为 /我的资源/3.mp4 933 BaiduPCS-Go mv /我的资源/1.mp4 /我的资源/3.mp4 934 `, 935 Category: "百度网盘", 936 Before: reloadFn, 937 Action: func(c *cli.Context) error { 938 if c.NArg() <= 1 { 939 cli.ShowCommandHelp(c, c.Command.Name) 940 return nil 941 } 942 943 pcscommand.RunMove(c.Args()...) 944 return nil 945 }, 946 }, 947 { 948 Name: "download", 949 Aliases: []string{"d"}, 950 Usage: "下载文件/目录", 951 UsageText: app.Name + " download <文件/目录路径1> <文件/目录2> <文件/目录3> ...", 952 Description: ` 953 下载的文件默认保存到, 程序所在目录的 download/ 目录. 954 通过 BaiduPCS-Go config set -savedir <savedir>, 自定义保存的目录. 955 支持多个文件或目录下载. 956 支持下载完成后自动校验文件, 但并不是所有的文件都支持校验! 957 自动跳过下载重名的文件! 958 959 下载模式说明: 960 pcs: 通过百度网盘的 PCS API 下载 961 stream: 通过百度网盘的 PCS API, 以流式文件的方式下载, 效果同 pcs 962 locate: 默认的下载模式。从百度网盘 Android 客户端, 获取下载链接的方式来下载 963 964 示例: 965 966 设置保存目录, 保存到 D:\Downloads 967 注意区别反斜杠 "\" 和 斜杠 "/" !!! 968 BaiduPCS-Go config set -savedir D:\\Downloads 969 或者 970 BaiduPCS-Go config set -savedir D:/Downloads 971 972 下载 /我的资源/1.mp4 973 BaiduPCS-Go d /我的资源/1.mp4 974 975 下载 /我的资源 整个目录!! 976 BaiduPCS-Go d /我的资源 977 978 下载网盘内的全部文件!! 979 BaiduPCS-Go d / 980 BaiduPCS-Go d * 981 `, 982 Category: "百度网盘", 983 Before: reloadFn, 984 Action: func(c *cli.Context) error { 985 if c.NArg() == 0 { 986 cli.ShowCommandHelp(c, c.Command.Name) 987 return nil 988 } 989 990 // 处理saveTo 991 var ( 992 saveTo string 993 ) 994 if c.Bool("save") { 995 saveTo = "." 996 } else if c.String("saveto") != "" { 997 saveTo = filepath.Clean(c.String("saveto")) 998 } 999 1000 // 处理解析downloadMode 1001 var ( 1002 downloadMode pcsdownload.DownloadMode 1003 ) 1004 switch c.String("mode") { 1005 case "pcs": 1006 downloadMode = pcsdownload.DownloadModePCS 1007 case "stream": 1008 downloadMode = pcsdownload.DownloadModeStreaming 1009 case "locate": 1010 downloadMode = pcsdownload.DownloadModeLocate 1011 default: 1012 fmt.Println("下载方式解析失败") 1013 cli.ShowCommandHelp(c, c.Command.Name) 1014 return nil 1015 } 1016 1017 do := &pcscommand.DownloadOptions{ 1018 IsTest: c.Bool("test"), 1019 IsPrintStatus: c.Bool("status"), 1020 IsExecutedPermission: c.Bool("x"), 1021 IsOverwrite: c.Bool("ow"), 1022 DownloadMode: downloadMode, 1023 SaveTo: saveTo, 1024 Parallel: c.Int("p"), 1025 Load: c.Int("l"), 1026 MaxRetry: c.Int("retry"), 1027 NoCheck: c.Bool("nocheck"), 1028 } 1029 1030 pcscommand.RunDownload(c.Args(), do) 1031 1032 return nil 1033 }, 1034 Flags: []cli.Flag{ 1035 cli.BoolFlag{ 1036 Name: "test", 1037 Usage: "测试下载, 此操作不会保存文件到本地", 1038 }, 1039 cli.BoolFlag{ 1040 Name: "ow", 1041 Usage: "overwrite, 覆盖已存在的文件", 1042 }, 1043 cli.BoolFlag{ 1044 Name: "status", 1045 Usage: "输出所有线程的工作状态", 1046 }, 1047 cli.BoolFlag{ 1048 Name: "save", 1049 Usage: "将下载的文件直接保存到当前工作目录", 1050 }, 1051 cli.StringFlag{ 1052 Name: "saveto", 1053 Usage: "将下载的文件直接保存到指定的目录", 1054 }, 1055 cli.BoolFlag{ 1056 Name: "x", 1057 Usage: "为文件加上执行权限, (windows系统无效)", 1058 }, 1059 cli.StringFlag{ 1060 Name: "mode", 1061 Usage: "下载模式, 可选值: pcs, stream, locate, 默认为 locate, 相关说明见上面的帮助", 1062 Value: "locate", 1063 }, 1064 cli.IntFlag{ 1065 Name: "p", 1066 Usage: "指定下载线程数", 1067 }, 1068 cli.IntFlag{ 1069 Name: "l", 1070 Usage: "指定同时进行下载文件的数量", 1071 }, 1072 cli.IntFlag{ 1073 Name: "retry", 1074 Usage: "下载失败最大重试次数", 1075 Value: pcsdownload.DefaultDownloadMaxRetry, 1076 }, 1077 cli.BoolFlag{ 1078 Name: "nocheck", 1079 Usage: "下载文件完成后不校验文件", 1080 }, 1081 }, 1082 }, 1083 { 1084 Name: "upload", 1085 Aliases: []string{"u"}, 1086 Usage: "上传文件/目录", 1087 UsageText: app.Name + " upload <本地文件/目录的路径1> <文件/目录2> <文件/目录3> ... <目标目录>", 1088 Description: ` 1089 上传默认采用分片上传的方式, 上传的文件将会保存到, <目标目录>. 1090 遇到同名文件将会自动覆盖!! 1091 当上传的文件名和网盘的目录名称相同时, 不会覆盖目录, 防止丢失数据. 1092 1093 注意: 1094 1095 分片上传之后, 服务器可能会记录到错误的文件md5, 可使用 fixmd5 命令尝试修复文件的MD5值, 修复md5不一定能成功, 但文件的完整性是没问题的. 1096 fixmd5 命令使用方法: 1097 BaiduPCS-Go fixmd5 -h 1098 1099 禁用分片上传可以保证服务器记录到正确的md5. 1100 禁用分片上传时只能使用单线程上传, 指定的单个文件上传最大线程数将会无效. 1101 1102 示例: 1103 1104 1. 将本地的 C:\Users\Administrator\Desktop\1.mp4 上传到网盘 /视频 目录 1105 注意区别反斜杠 "\" 和 斜杠 "/" !!! 1106 BaiduPCS-Go upload C:/Users/Administrator/Desktop/1.mp4 /视频 1107 1108 2. 将本地的 C:\Users\Administrator\Desktop\1.mp4 和 C:\Users\Administrator\Desktop\2.mp4 上传到网盘 /视频 目录 1109 BaiduPCS-Go upload C:/Users/Administrator/Desktop/1.mp4 C:/Users/Administrator/Desktop/2.mp4 /视频 1110 1111 3. 将本地的 C:\Users\Administrator\Desktop 整个目录上传到网盘 /视频 目录 1112 BaiduPCS-Go upload C:/Users/Administrator/Desktop /视频 1113 1114 4. 使用相对路径 1115 BaiduPCS-Go upload 1.mp4 /视频 1116 `, 1117 Category: "百度网盘", 1118 Before: reloadFn, 1119 Action: func(c *cli.Context) error { 1120 if c.NArg() < 2 { 1121 cli.ShowCommandHelp(c, c.Command.Name) 1122 return nil 1123 } 1124 1125 subArgs := c.Args() 1126 pcscommand.RunUpload(subArgs[:c.NArg()-1], subArgs[c.NArg()-1], &pcscommand.UploadOptions{ 1127 Parallel: c.Int("p"), 1128 MaxRetry: c.Int("retry"), 1129 NoRapidUpload: c.Bool("norapid"), 1130 NoSplitFile: c.Bool("nosplit"), 1131 }) 1132 return nil 1133 }, 1134 Flags: []cli.Flag{ 1135 cli.IntFlag{ 1136 Name: "p", 1137 Usage: "指定单个文件上传的最大线程数", 1138 }, 1139 cli.IntFlag{ 1140 Name: "retry", 1141 Usage: "上传失败最大重试次数", 1142 Value: pcscommand.DefaultUploadMaxRetry, 1143 }, 1144 cli.BoolFlag{ 1145 Name: "norapid", 1146 Usage: "不检测秒传", 1147 }, 1148 cli.BoolFlag{ 1149 Name: "nosplit", 1150 Usage: "禁用分片上传", 1151 }, 1152 }, 1153 }, 1154 { 1155 Name: "locate", 1156 Aliases: []string{"lt"}, 1157 Usage: "获取下载直链", 1158 UsageText: app.Name + " locate <文件1> <文件2> ...", 1159 Description: fmt.Sprintf(` 1160 获取下载直链 1161 1162 若该功能无法正常使用, 提示"user is not authorized, hitcode:xxx", 尝试更换 User-Agent 为 %s: 1163 BaiduPCS-Go config set -user_agent "%s" 1164 `, baidupcs.NetdiskUA, baidupcs.NetdiskUA), 1165 Category: "百度网盘", 1166 Before: reloadFn, 1167 Action: func(c *cli.Context) error { 1168 if c.NArg() < 1 { 1169 cli.ShowCommandHelp(c, c.Command.Name) 1170 return nil 1171 } 1172 1173 opt := &pcscommand.LocateDownloadOption{ 1174 FromPan: c.Bool("pan"), 1175 } 1176 1177 pcscommand.RunLocateDownload(c.Args(), opt) 1178 return nil 1179 }, 1180 Flags: []cli.Flag{ 1181 cli.BoolFlag{ 1182 Name: "pan", 1183 Usage: "从百度网盘首页获取下载链接", 1184 }, 1185 }, 1186 }, 1187 { 1188 Name: "rapidupload", 1189 Aliases: []string{"ru"}, 1190 Usage: "手动秒传文件", 1191 UsageText: app.Name + " rapidupload -length=<文件的大小> -md5=<文件的md5值> -slicemd5=<文件前256KB切片的md5值(可选)> -crc32=<文件的crc32值(可选)> <保存的网盘路径, 需包含文件名>", 1192 Description: ` 1193 使用此功能秒传文件, 前提是知道文件的大小, md5, 前256KB切片的 md5 (可选), crc32 (可选), 且百度网盘中存在一模一样的文件. 1194 上传的文件将会保存到网盘的目标目录. 1195 遇到同名文件将会自动覆盖! 1196 1197 可能无法秒传 20GB 以上的文件!! 1198 1199 示例: 1200 1201 1. 如果秒传成功, 则保存到网盘路径 /test 1202 BaiduPCS-Go rapidupload -length=56276137 -md5=fbe082d80e90f90f0fb1f94adbbcfa7f -slicemd5=38c6a75b0ec4499271d4ea38a667ab61 -crc32=314332359 /test 1203 `, 1204 Category: "百度网盘", 1205 Before: reloadFn, 1206 Action: func(c *cli.Context) error { 1207 if c.NArg() <= 0 || !c.IsSet("md5") || !c.IsSet("length") || !c.IsSet("slicemd5") { 1208 cli.ShowCommandHelp(c, c.Command.Name) 1209 return nil 1210 } 1211 1212 pcscommand.RunRapidUpload(c.Args().Get(0), c.String("md5"), c.String("slicemd5"), c.String("crc32"), c.Int64("length")) 1213 return nil 1214 }, 1215 Flags: []cli.Flag{ 1216 cli.StringFlag{ 1217 Name: "md5", 1218 Usage: "文件的 md5 值", 1219 }, 1220 cli.StringFlag{ 1221 Name: "slicemd5", 1222 Usage: "文件前 256KB 切片的 md5 值", 1223 }, 1224 cli.StringFlag{ 1225 Name: "crc32", 1226 Usage: "文件的 crc32 值 (可选)", 1227 }, 1228 cli.Int64Flag{ 1229 Name: "length", 1230 Usage: "文件的大小", 1231 }, 1232 }, 1233 }, 1234 { 1235 Name: "createsuperfile", 1236 Aliases: []string{"csf"}, 1237 Usage: "手动分片上传—合并分片文件", 1238 UsageText: app.Name + " createsuperfile -path=<保存的网盘路径, 需包含文件名> block1 block2 ... ", 1239 Description: ` 1240 block1, block2 ... 为文件分片的md5值 1241 上传的文件将会保存到网盘的目标目录. 1242 遇到同名文件将会自动覆盖! 1243 1244 示例: 1245 1246 BaiduPCS-Go createsuperfile -path=1.mp4 ec87a838931d4d5d2e94a04644788a55 ec87a838931d4d5d2e94a04644788a55 1247 `, 1248 Category: "百度网盘", 1249 Before: reloadFn, 1250 Action: func(c *cli.Context) error { 1251 if c.NArg() < 1 { 1252 cli.ShowCommandHelp(c, c.Command.Name) 1253 return nil 1254 } 1255 1256 pcscommand.RunCreateSuperFile(c.String("path"), c.Args()...) 1257 return nil 1258 }, 1259 Flags: []cli.Flag{ 1260 cli.StringFlag{ 1261 Name: "path", 1262 Usage: "保存的网盘路径", 1263 Value: "superfile", 1264 }, 1265 }, 1266 }, 1267 { 1268 Name: "fixmd5", 1269 Usage: "修复文件MD5", 1270 UsageText: app.Name + " fixmd5 <文件1> <文件2> <文件3> ...", 1271 Description: ` 1272 尝试修复文件的MD5值, 以便于校验文件的完整性和导出文件. 1273 1274 使用分片上传文件, 当文件分片数大于1时, 百度网盘服务端最终计算所得的md5值和本地的不一致, 这可能是百度网盘的bug. 1275 不过把上传的文件下载到本地后,对比md5值是匹配的, 也就是文件在传输中没有发生损坏. 1276 1277 对于MD5值可能有误的文件, 程序会在获取文件的元信息时, 给出MD5值 "可能不正确" 的提示, 表示此文件可以尝试进行MD5值修复. 1278 修复文件MD5不一定能成功, 原因可能是服务器未刷新, 可过几天后再尝试. 1279 修复文件MD5的原理为秒传文件, 即修复文件MD5成功后, 文件的创建日期, 修改日期, fs_id, 版本历史等信息将会被覆盖, 修复的MD5值将覆盖原先的MD5值, 但不影响文件的完整性. 1280 1281 注意: 无法修复 20GB 以上文件的 md5!! 1282 1283 示例: 1284 1285 1. 修复 /我的资源/1.mp4 的 MD5 值 1286 BaiduPCS-Go fixmd5 /我的资源/1.mp4 1287 `, 1288 Category: "百度网盘", 1289 Before: reloadFn, 1290 Action: func(c *cli.Context) error { 1291 if c.NArg() <= 0 { 1292 cli.ShowCommandHelp(c, c.Command.Name) 1293 return nil 1294 } 1295 1296 pcscommand.RunFixMD5(c.Args()...) 1297 return nil 1298 }, 1299 }, 1300 { 1301 Name: "sumfile", 1302 Aliases: []string{"sf"}, 1303 Usage: "获取本地文件的秒传信息", 1304 UsageText: app.Name + " sumfile <本地文件的路径1> <本地文件的路径2> ...", 1305 Description: ` 1306 获取本地文件的大小, md5, 前256KB切片的md5, crc32, 可用于秒传文件. 1307 1308 示例: 1309 1310 获取 C:\Users\Administrator\Desktop\1.mp4 的秒传信息 1311 BaiduPCS-Go sumfile C:/Users/Administrator/Desktop/1.mp4 1312 `, 1313 Category: "其他", 1314 Before: reloadFn, 1315 Action: func(c *cli.Context) error { 1316 if c.NArg() <= 0 { 1317 cli.ShowCommandHelp(c, c.Command.Name) 1318 return nil 1319 } 1320 1321 for k, filePath := range c.Args() { 1322 lp, err := checksum.GetFileSum(filePath, checksum.CHECKSUM_MD5|checksum.CHECKSUM_SLICE_MD5|checksum.CHECKSUM_CRC32) 1323 if err != nil { 1324 fmt.Printf("[%d] %s\n", k+1, err) 1325 continue 1326 } 1327 1328 fmt.Printf("[%d] - [%s]:\n", k+1, filePath) 1329 1330 strLength, strMd5, strSliceMd5, strCrc32 := strconv.FormatInt(lp.Length, 10), hex.EncodeToString(lp.MD5), hex.EncodeToString(lp.SliceMD5), strconv.FormatUint(uint64(lp.CRC32), 10) 1331 fileName := filepath.Base(filePath) 1332 1333 tb := pcstable.NewTable(os.Stdout) 1334 tb.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}) 1335 tb.AppendBulk([][]string{ 1336 []string{"文件大小", strLength}, 1337 []string{"md5", strMd5}, 1338 []string{"前256KB切片的md5", strSliceMd5}, 1339 []string{"crc32", strCrc32}, 1340 []string{"秒传命令", app.Name + " rapidupload -length=" + strLength + " -md5=" + strMd5 + " -slicemd5=" + strSliceMd5 + " -crc32=" + strCrc32 + " " + fileName}, 1341 }) 1342 tb.Render() 1343 fmt.Printf("\n") 1344 } 1345 1346 return nil 1347 }, 1348 }, 1349 { 1350 Name: "share", 1351 Usage: "分享文件/目录", 1352 UsageText: app.Name + " share", 1353 Category: "百度网盘", 1354 Before: reloadFn, 1355 Action: func(c *cli.Context) error { 1356 cli.ShowCommandHelp(c, c.Command.Name) 1357 return nil 1358 }, 1359 Subcommands: []cli.Command{ 1360 { 1361 Name: "set", 1362 Aliases: []string{"s"}, 1363 Usage: "设置分享文件/目录", 1364 UsageText: app.Name + " share set <文件/目录1> <文件/目录2> ...", 1365 Description: `目前只支持创建私密链接.`, 1366 Action: func(c *cli.Context) error { 1367 if c.NArg() < 1 { 1368 cli.ShowCommandHelp(c, c.Command.Name) 1369 return nil 1370 } 1371 pcscommand.RunShareSet(c.Args(), nil) 1372 return nil 1373 }, 1374 }, 1375 { 1376 Name: "list", 1377 Aliases: []string{"l"}, 1378 Usage: "列出已分享文件/目录", 1379 UsageText: app.Name + " share list", 1380 Action: func(c *cli.Context) error { 1381 pcscommand.RunShareList(c.Int("page")) 1382 return nil 1383 }, 1384 Flags: []cli.Flag{ 1385 cli.IntFlag{ 1386 Name: "page", 1387 Usage: "分享列表的页数", 1388 Value: 1, 1389 }, 1390 }, 1391 }, 1392 { 1393 Name: "cancel", 1394 Aliases: []string{"c"}, 1395 Usage: "取消分享文件/目录", 1396 UsageText: app.Name + " share cancel <shareid_1> <shareid_2> ...", 1397 Description: `目前只支持通过分享id (shareid) 来取消分享.`, 1398 Action: func(c *cli.Context) error { 1399 if c.NArg() < 1 { 1400 cli.ShowCommandHelp(c, c.Command.Name) 1401 return nil 1402 } 1403 pcscommand.RunShareCancel(converter.SliceStringToInt64(c.Args())) 1404 return nil 1405 }, 1406 }, 1407 }, 1408 }, 1409 { 1410 Name: "export", 1411 Aliases: []string{"ep"}, 1412 Usage: "导出文件/目录", 1413 UsageText: app.Name + " export <文件/目录1> <文件/目录2> ...", 1414 Description: ` 1415 导出网盘内的文件或目录, 原理为秒传文件, 此操作会生成导出文件或目录的命令. 1416 1417 注意!!! : 1418 无法导出 20GB 以上的文件!! 1419 无法导出文件的版本历史等数据!! 1420 并不是所有的文件都能导出成功, 程序会列出无法导出的文件列表. 1421 1422 示例: 1423 1424 导出当前工作目录: 1425 BaiduPCS-Go export 1426 1427 导出所有文件和目录, 并设置新的根目录为 /root 1428 BaiduPCS-Go export -root=/root / 1429 1430 导出 /我的资源 1431 BaiduPCS-Go export /我的资源 1432 `, 1433 Category: "百度网盘", 1434 Before: reloadFn, 1435 Action: func(c *cli.Context) error { 1436 pcspaths := c.Args() 1437 if len(pcspaths) == 0 { 1438 pcspaths = []string{"."} 1439 } 1440 1441 pcscommand.RunExport(pcspaths, &pcscommand.ExportOptions{ 1442 RootPath: c.String("root"), 1443 SavePath: c.String("out"), 1444 MaxRetry: c.Int("retry"), 1445 Recursive: c.Bool("r"), 1446 }) 1447 return nil 1448 }, 1449 Flags: []cli.Flag{ 1450 cli.StringFlag{ 1451 Name: "root", 1452 Usage: "设置要导出文件或目录的根路径, 可以是相对路径", 1453 }, 1454 cli.StringFlag{ 1455 Name: "out", 1456 Usage: "导出文件信息的保存路径", 1457 }, 1458 cli.IntFlag{ 1459 Name: "retry", 1460 Usage: "导出失败的重试次数", 1461 Value: 3, 1462 }, 1463 cli.BoolFlag{ 1464 Name: "r", 1465 Usage: "递归导出", 1466 }, 1467 }, 1468 }, 1469 { 1470 Name: "offlinedl", 1471 Aliases: []string{"clouddl", "od"}, 1472 Usage: "离线下载", 1473 Description: `支持http/https/ftp/电驴/磁力链协议 1474 离线下载同时进行的任务数量有限, 超出限制的部分将无法添加. 1475 1476 示例: 1477 1478 1. 将百度和腾讯主页, 离线下载到根目录 / 1479 BaiduPCS-Go offlinedl add -path=/ http://baidu.com http://qq.com 1480 1481 2. 添加磁力链接任务 1482 BaiduPCS-Go offlinedl add magnet:?xt=urn:btih:xxx 1483 1484 3. 查询任务ID为 12345 的离线下载任务状态 1485 BaiduPCS-Go offlinedl query 12345 1486 1487 4. 取消任务ID为 12345 的离线下载任务 1488 BaiduPCS-Go offlinedl cancel 12345`, 1489 Category: "百度网盘", 1490 Before: reloadFn, 1491 Action: func(c *cli.Context) error { 1492 cli.ShowCommandHelp(c, c.Command.Name) 1493 return nil 1494 }, 1495 Subcommands: []cli.Command{ 1496 { 1497 Name: "add", 1498 Aliases: []string{"a"}, 1499 Usage: "添加离线下载任务", 1500 UsageText: app.Name + " offlinedl add -path=<离线下载文件保存的路径> 资源地址1 地址2 ...", 1501 Action: func(c *cli.Context) error { 1502 if c.NArg() < 1 { 1503 cli.ShowCommandHelp(c, c.Command.Name) 1504 return nil 1505 } 1506 1507 pcscommand.RunCloudDlAddTask(c.Args(), c.String("path")) 1508 return nil 1509 }, 1510 Flags: []cli.Flag{ 1511 cli.StringFlag{ 1512 Name: "path", 1513 Usage: "离线下载文件保存的路径, 默认为工作目录", 1514 }, 1515 }, 1516 }, 1517 { 1518 Name: "query", 1519 Aliases: []string{"q"}, 1520 Usage: "精确查询离线下载任务", 1521 UsageText: app.Name + " offlinedl query 任务ID1 任务ID2 ...", 1522 Action: func(c *cli.Context) error { 1523 if c.NArg() < 1 { 1524 cli.ShowCommandHelp(c, c.Command.Name) 1525 return nil 1526 } 1527 1528 taskIDs := converter.SliceStringToInt64(c.Args()) 1529 1530 if len(taskIDs) == 0 { 1531 fmt.Printf("未找到合法的任务ID, task_id\n") 1532 return nil 1533 } 1534 1535 pcscommand.RunCloudDlQueryTask(taskIDs) 1536 return nil 1537 }, 1538 }, 1539 { 1540 Name: "list", 1541 Aliases: []string{"ls", "l"}, 1542 Usage: "查询离线下载任务列表", 1543 UsageText: app.Name + " offlinedl list", 1544 Action: func(c *cli.Context) error { 1545 pcscommand.RunCloudDlListTask() 1546 return nil 1547 }, 1548 }, 1549 { 1550 Name: "cancel", 1551 Aliases: []string{"c"}, 1552 Usage: "取消离线下载任务", 1553 UsageText: app.Name + " offlinedl cancel 任务ID1 任务ID2 ...", 1554 Action: func(c *cli.Context) error { 1555 if c.NArg() < 1 { 1556 cli.ShowCommandHelp(c, c.Command.Name) 1557 return nil 1558 } 1559 1560 taskIDs := converter.SliceStringToInt64(c.Args()) 1561 1562 if len(taskIDs) == 0 { 1563 fmt.Printf("未找到合法的任务ID, task_id\n") 1564 return nil 1565 } 1566 1567 pcscommand.RunCloudDlCancelTask(taskIDs) 1568 return nil 1569 }, 1570 }, 1571 { 1572 Name: "delete", 1573 Aliases: []string{"del", "d"}, 1574 Usage: "删除离线下载任务", 1575 UsageText: app.Name + " offlinedl delete 任务ID1 任务ID2 ...", 1576 Action: func(c *cli.Context) error { 1577 isClear := c.Bool("all") 1578 if c.NArg() < 1 && !isClear { 1579 cli.ShowCommandHelp(c, c.Command.Name) 1580 return nil 1581 } 1582 1583 // 清空离线下载任务记录 1584 if isClear { 1585 pcscommand.RunCloudDlClearTask() 1586 return nil 1587 } 1588 1589 // 删除特定的离线下载任务记录 1590 taskIDs := converter.SliceStringToInt64(c.Args()) 1591 if len(taskIDs) == 0 { 1592 fmt.Printf("未找到合法的任务ID, task_id\n") 1593 return nil 1594 } 1595 1596 pcscommand.RunCloudDlDeleteTask(taskIDs) 1597 return nil 1598 }, 1599 Flags: []cli.Flag{ 1600 cli.BoolFlag{ 1601 Name: "all", 1602 Usage: "清空离线下载任务记录, 程序不会进行二次确认, 谨慎操作!!!", 1603 }, 1604 }, 1605 }, 1606 }, 1607 }, 1608 { 1609 Name: "recycle", 1610 Usage: "回收站", 1611 Description: ` 1612 回收站操作. 1613 1614 示例: 1615 1616 1. 从回收站还原两个文件, 其中的两个文件的 fs_id 分别为 1013792297798440 和 643596340463870 1617 BaiduPCS-Go recycle restore 1013792297798440 643596340463870 1618 1619 2. 从回收站删除两个文件, 其中的两个文件的 fs_id 分别为 1013792297798440 和 643596340463870 1620 BaiduPCS-Go recycle delete 1013792297798440 643596340463870 1621 1622 3. 清空回收站, 程序不会进行二次确认, 谨慎操作!!! 1623 BaiduPCS-Go recycle delete -all 1624 `, 1625 Category: "百度网盘", 1626 Before: reloadFn, 1627 Action: func(c *cli.Context) error { 1628 if c.NumFlags() <= 0 || c.NArg() <= 0 { 1629 cli.ShowCommandHelp(c, c.Command.Name) 1630 } 1631 return nil 1632 }, 1633 Subcommands: []cli.Command{ 1634 { 1635 Name: "list", 1636 Aliases: []string{"ls", "l"}, 1637 Usage: baidupcs.OperationRecycleList, 1638 UsageText: app.Name + " recycle list", 1639 Action: func(c *cli.Context) error { 1640 pcscommand.RunRecycleList(c.Int("page")) 1641 return nil 1642 }, 1643 Flags: []cli.Flag{ 1644 cli.IntFlag{ 1645 Name: "page", 1646 Usage: "回收站文件列表页数", 1647 Value: 1, 1648 }, 1649 }, 1650 }, 1651 { 1652 Name: "restore", 1653 Aliases: []string{"r"}, 1654 Usage: baidupcs.OperationRecycleRestore, 1655 UsageText: app.Name + " recycle restore <fs_id 1> <fs_id 2> <fs_id 3> ...", 1656 Description: `根据文件/目录的 fs_id, 还原回收站指定的文件或目录`, 1657 Action: func(c *cli.Context) error { 1658 if c.NArg() <= 0 { 1659 cli.ShowCommandHelp(c, c.Command.Name) 1660 return nil 1661 } 1662 pcscommand.RunRecycleRestore(c.Args()...) 1663 return nil 1664 }, 1665 }, 1666 { 1667 Name: "delete", 1668 Aliases: []string{"d"}, 1669 Usage: baidupcs.OperationRecycleDelete + "/" + baidupcs.OperationRecycleClear, 1670 UsageText: app.Name + " recycle delete [-all] <fs_id 1> <fs_id 2> <fs_id 3> ...", 1671 Description: `根据文件/目录的 fs_id 或 -all 参数, 删除回收站指定的文件或目录或清空回收站`, 1672 Action: func(c *cli.Context) error { 1673 if c.Bool("all") { 1674 // 清空回收站 1675 pcscommand.RunRecycleClear() 1676 return nil 1677 } 1678 1679 if c.NArg() <= 0 { 1680 cli.ShowCommandHelp(c, c.Command.Name) 1681 return nil 1682 } 1683 pcscommand.RunRecycleDelete(c.Args()...) 1684 return nil 1685 }, 1686 Flags: []cli.Flag{ 1687 cli.BoolFlag{ 1688 Name: "all", 1689 Usage: "清空回收站, 程序不会进行二次确认, 谨慎操作!!!", 1690 }, 1691 }, 1692 }, 1693 }, 1694 }, 1695 { 1696 Name: "config", 1697 Usage: "显示和修改程序配置项", 1698 Description: "显示和修改程序配置项", 1699 Category: "配置", 1700 Before: reloadFn, 1701 After: saveFunc, 1702 Action: func(c *cli.Context) error { 1703 fmt.Printf("----\n运行 %s config set 可进行设置配置\n\n当前配置:\n", app.Name) 1704 pcsconfig.Config.PrintTable() 1705 return nil 1706 }, 1707 Subcommands: []cli.Command{ 1708 { 1709 Name: "set", 1710 Usage: "修改程序配置项", 1711 UsageText: app.Name + " config set [arguments...]", 1712 Description: ` 1713 注意: 1714 可通过设置环境变量 BAIDUPCS_GO_CONFIG_DIR, 指定配置文件存放的目录. 1715 1716 谨慎修改 appid, user_agent, pcs_ua, pan_ua 的值, 否则访问网盘服务器时, 可能会出现错误 1717 cache_size 的值支持可选设置单位了, 单位不区分大小写, b 和 B 均表示字节的意思, 如 64KB, 1MB, 32kb, 65536b, 65536 1718 max_upload_parallel, max_download_load 的值支持可选设置单位了, 单位为每秒的传输速率, 后缀'/s' 可省略, 如 2MB/s, 2MB, 2m, 2mb 均为一个意思 1719 1720 例子: 1721 BaiduPCS-Go config set -appid=266719 1722 BaiduPCS-Go config set -enable_https=false 1723 BaiduPCS-Go config set -user_agent="netdisk;2.2.51.6;netdisk;10.0.63;PC;android-android" 1724 BaiduPCS-Go config set -cache_size 64KB 1725 BaiduPCS-Go config set -cache_size 16384 -max_parallel 200 -savedir D:/download`, 1726 Action: func(c *cli.Context) error { 1727 if c.NumFlags() <= 0 || c.NArg() > 0 { 1728 cli.ShowCommandHelp(c, c.Command.Name) 1729 return nil 1730 } 1731 1732 if c.IsSet("appid") { 1733 pcsconfig.Config.SetAppID(c.Int("appid")) 1734 } 1735 if c.IsSet("enable_https") { 1736 pcsconfig.Config.SetEnableHTTPS(c.Bool("enable_https")) 1737 } 1738 if c.IsSet("user_agent") { 1739 pcsconfig.Config.SetUserAgent(c.String("user_agent")) 1740 } 1741 if c.IsSet("pcs_ua") { 1742 pcsconfig.Config.SetPCSUA(c.String("pcs_ua")) 1743 } 1744 if c.IsSet("pan_ua") { 1745 pcsconfig.Config.SetPanUA(c.String("pan_ua")) 1746 } 1747 if c.IsSet("cache_size") { 1748 err := pcsconfig.Config.SetCacheSizeByStr(c.String("cache_size")) 1749 if err != nil { 1750 fmt.Printf("设置 cache_size 错误: %s\n", err) 1751 return nil 1752 } 1753 } 1754 if c.IsSet("max_parallel") { 1755 pcsconfig.Config.MaxParallel = c.Int("max_parallel") 1756 } 1757 if c.IsSet("max_upload_parallel") { 1758 pcsconfig.Config.MaxUploadParallel = c.Int("max_upload_parallel") 1759 } 1760 if c.IsSet("max_download_load") { 1761 pcsconfig.Config.MaxDownloadLoad = c.Int("max_download_load") 1762 } 1763 if c.IsSet("max_download_rate") { 1764 err := pcsconfig.Config.SetMaxDownloadRateByStr(c.String("max_download_rate")) 1765 if err != nil { 1766 fmt.Printf("设置 max_download_rate 错误: %s\n", err) 1767 return nil 1768 } 1769 } 1770 if c.IsSet("max_upload_rate") { 1771 err := pcsconfig.Config.SetMaxUploadRateByStr(c.String("max_upload_rate")) 1772 if err != nil { 1773 fmt.Printf("设置 max_upload_rate 错误: %s\n", err) 1774 return nil 1775 } 1776 } 1777 if c.IsSet("savedir") { 1778 pcsconfig.Config.SaveDir = c.String("savedir") 1779 } 1780 if c.IsSet("proxy") { 1781 pcsconfig.Config.SetProxy(c.String("proxy")) 1782 } 1783 if c.IsSet("local_addrs") { 1784 pcsconfig.Config.SetLocalAddrs(c.String("local_addrs")) 1785 } 1786 1787 err := pcsconfig.Config.Save() 1788 if err != nil { 1789 fmt.Println(err) 1790 return err 1791 } 1792 1793 pcsconfig.Config.PrintTable() 1794 fmt.Printf("\n保存配置成功!\n\n") 1795 1796 return nil 1797 }, 1798 Flags: []cli.Flag{ 1799 cli.IntFlag{ 1800 Name: "appid", 1801 Usage: "百度 PCS 应用ID", 1802 }, 1803 cli.StringFlag{ 1804 Name: "cache_size", 1805 Usage: "下载缓存", 1806 }, 1807 cli.IntFlag{ 1808 Name: "max_parallel", 1809 Usage: "下载网络连接的最大并发量", 1810 }, 1811 cli.IntFlag{ 1812 Name: "max_upload_parallel", 1813 Usage: "上传网络连接的最大并发量", 1814 }, 1815 cli.IntFlag{ 1816 Name: "max_download_load", 1817 Usage: "同时进行下载文件的最大数量", 1818 }, 1819 cli.StringFlag{ 1820 Name: "max_download_rate", 1821 Usage: "限制最大下载速度, 0代表不限制", 1822 }, 1823 cli.StringFlag{ 1824 Name: "max_upload_rate", 1825 Usage: "限制最大上传速度, 0代表不限制", 1826 }, 1827 cli.StringFlag{ 1828 Name: "savedir", 1829 Usage: "下载文件的储存目录", 1830 }, 1831 cli.BoolFlag{ 1832 Name: "enable_https", 1833 Usage: "启用 https", 1834 }, 1835 cli.StringFlag{ 1836 Name: "user_agent", 1837 Usage: "浏览器标识", 1838 }, 1839 cli.StringFlag{ 1840 Name: "pcs_ua", 1841 Usage: "PCS 浏览器标识", 1842 }, 1843 cli.StringFlag{ 1844 Name: "pan_ua", 1845 Usage: "Pan 浏览器标识", 1846 }, 1847 cli.StringFlag{ 1848 Name: "proxy", 1849 Usage: "设置代理, 支持 http/socks5 代理", 1850 }, 1851 cli.StringFlag{ 1852 Name: "local_addrs", 1853 Usage: "设置本地网卡地址, 多个地址用逗号隔开", 1854 }, 1855 }, 1856 }, 1857 }, 1858 }, 1859 { 1860 Name: "match", 1861 Usage: "测试通配符", 1862 UsageText: app.Name + " match <通配符表达式>", 1863 Description: ` 1864 测试通配符匹配路径, 操作成功则输出所有匹配到的路径. 1865 1866 示例: 1867 1868 1. 匹配 /我的资源 目录下所有mp4格式的文件 1869 BaiduPCS-Go match /我的资源/*.mp4 1870 `, 1871 Category: "百度网盘", 1872 Before: reloadFn, 1873 Action: func(c *cli.Context) error { 1874 if c.NArg() != 1 { 1875 cli.ShowCommandHelp(c, c.Command.Name) 1876 return nil 1877 } 1878 1879 pcscommand.RunTestShellPattern(c.Args()[0]) 1880 return nil 1881 }, 1882 }, 1883 { 1884 Name: "tool", 1885 Usage: "工具箱", 1886 Action: func(c *cli.Context) error { 1887 cli.ShowCommandHelp(c, c.Command.Name) 1888 return nil 1889 }, 1890 Subcommands: []cli.Command{ 1891 { 1892 Name: "showtime", 1893 Usage: "显示当前时间(北京时间)", 1894 Action: func(c *cli.Context) error { 1895 fmt.Printf(pcstime.BeijingTimeOption("printLog")) 1896 return nil 1897 }, 1898 }, 1899 { 1900 Name: "getip", 1901 Usage: "获取IP地址", 1902 Action: func(c *cli.Context) error { 1903 fmt.Printf("内网IP地址: \n") 1904 for _, address := range pcsutil.ListAddresses() { 1905 fmt.Printf("%s\n", address) 1906 } 1907 fmt.Printf("\n") 1908 1909 ipAddr, err := getip.IPInfoFromTechainBaiduByClient(pcsconfig.Config.HTTPClient()) 1910 if err != nil { 1911 fmt.Printf("获取公网IP错误: %s\n", err) 1912 return nil 1913 } 1914 1915 fmt.Printf("公网IP地址: %s\n", ipAddr) 1916 return nil 1917 }, 1918 }, 1919 { 1920 Name: "enc", 1921 Usage: "加密文件", 1922 UsageText: app.Name + " enc -method=<method> -key=<key> [files...]", 1923 Description: cryptoDescription, 1924 Action: func(c *cli.Context) error { 1925 if c.NArg() <= 0 { 1926 cli.ShowCommandHelp(c, c.Command.Name) 1927 return nil 1928 } 1929 1930 for _, filePath := range c.Args() { 1931 encryptedFilePath, err := pcsutil.EncryptFile(c.String("method"), []byte(c.String("key")), filePath, !c.Bool("disable-gzip")) 1932 if err != nil { 1933 fmt.Printf("%s\n", err) 1934 continue 1935 } 1936 1937 fmt.Printf("加密成功, %s -> %s\n", filePath, encryptedFilePath) 1938 } 1939 1940 return nil 1941 }, 1942 Flags: []cli.Flag{ 1943 cli.StringFlag{ 1944 Name: "method", 1945 Usage: "加密方法", 1946 Value: "aes-128-ctr", 1947 }, 1948 cli.StringFlag{ 1949 Name: "key", 1950 Usage: "加密密钥", 1951 Value: app.Name, 1952 }, 1953 cli.BoolFlag{ 1954 Name: "disable-gzip", 1955 Usage: "不启用GZIP", 1956 }, 1957 }, 1958 }, 1959 { 1960 Name: "dec", 1961 Usage: "解密文件", 1962 UsageText: app.Name + " dec -method=<method> -key=<key> [files...]", 1963 Description: cryptoDescription, 1964 Action: func(c *cli.Context) error { 1965 if c.NArg() <= 0 { 1966 cli.ShowCommandHelp(c, c.Command.Name) 1967 return nil 1968 } 1969 1970 for _, filePath := range c.Args() { 1971 decryptedFilePath, err := pcsutil.DecryptFile(c.String("method"), []byte(c.String("key")), filePath, !c.Bool("disable-gzip")) 1972 if err != nil { 1973 fmt.Printf("%s\n", err) 1974 continue 1975 } 1976 1977 fmt.Printf("解密成功, %s -> %s\n", filePath, decryptedFilePath) 1978 } 1979 1980 return nil 1981 }, 1982 Flags: []cli.Flag{ 1983 cli.StringFlag{ 1984 Name: "method", 1985 Usage: "加密方法", 1986 Value: "aes-128-ctr", 1987 }, 1988 cli.StringFlag{ 1989 Name: "key", 1990 Usage: "加密密钥", 1991 Value: app.Name, 1992 }, 1993 cli.BoolFlag{ 1994 Name: "disable-gzip", 1995 Usage: "不启用GZIP", 1996 }, 1997 }, 1998 }, 1999 }, 2000 }, 2001 { 2002 Name: "clear", 2003 Aliases: []string{"cls"}, 2004 Usage: "清空控制台", 2005 UsageText: app.Name + " clear", 2006 Description: "清空控制台屏幕", 2007 Category: "其他", 2008 Action: func(c *cli.Context) error { 2009 pcsliner.ClearScreen() 2010 return nil 2011 }, 2012 }, 2013 { 2014 Name: "quit", 2015 Aliases: []string{"exit"}, 2016 Usage: "退出程序", 2017 Action: func(c *cli.Context) error { 2018 return cli.NewExitError("", 0) 2019 }, 2020 Hidden: true, 2021 HideHelp: true, 2022 }, 2023 } 2024 2025 sort.Sort(cli.FlagsByName(app.Flags)) 2026 sort.Sort(cli.CommandsByName(app.Commands)) 2027 2028 app.Run(os.Args) 2029 }