github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/internal/pcscommand/download.go (about) 1 package pcscommand 2 3 import ( 4 "fmt" 5 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs" 6 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror" 7 "github.com/qjfoidnh/BaiduPCS-Go/internal/pcsconfig" 8 "github.com/qjfoidnh/BaiduPCS-Go/internal/pcsfunctions/pcsdownload" 9 "github.com/qjfoidnh/BaiduPCS-Go/pcstable" 10 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" 11 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/taskframework" 12 "github.com/qjfoidnh/BaiduPCS-Go/requester/downloader" 13 "github.com/qjfoidnh/BaiduPCS-Go/requester/transfer" 14 "os" 15 "path/filepath" 16 "runtime" 17 "sort" 18 ) 19 20 type ( 21 //DownloadOptions 下载可选参数 22 DownloadOptions struct { 23 IsTest bool 24 IsPrintStatus bool 25 IsExecutedPermission bool 26 IsOverwrite bool 27 DownloadMode pcsdownload.DownloadMode 28 SaveTo string 29 Parallel int 30 Load int 31 MaxRetry int 32 NoCheck bool 33 ModifyMTime bool 34 FullPath bool 35 LinkPrefer int 36 } 37 38 // LocateDownloadOption 获取下载链接可选参数 39 LocateDownloadOption struct { 40 FromPan bool 41 } 42 ) 43 44 func downloadPrintFormat(load int) string { 45 if load <= 1 { 46 return pcsdownload.DefaultPrintFormat 47 } 48 return "[%s] ↓ %s/%s %s/s in %s, left %s ...\n" 49 } 50 51 // RunDownload 执行下载网盘内文件 52 func RunDownload(paths []string, options *DownloadOptions) { 53 if options == nil { 54 options = &DownloadOptions{} 55 } 56 57 if options.Load <= 0 { 58 options.Load = pcsconfig.Config.MaxDownloadLoad 59 } 60 61 if options.MaxRetry < 0 { 62 options.MaxRetry = pcsdownload.DefaultDownloadMaxRetry 63 } 64 65 if !options.NoCheck { 66 options.NoCheck = pcsconfig.Config.NoCheck 67 } 68 69 if runtime.GOOS == "windows" { 70 // windows下不加执行权限 71 options.IsExecutedPermission = false 72 } 73 74 // 设置下载配置 75 cfg := &downloader.Config{ 76 Mode: transfer.RangeGenMode_BlockSize, 77 CacheSize: pcsconfig.Config.CacheSize, 78 BlockSize: baidupcs.InitRangeSize, 79 MaxRate: pcsconfig.Config.MaxDownloadRate, 80 InstanceStateStorageFormat: downloader.InstanceStateStorageFormatProto3, 81 IsTest: options.IsTest, 82 TryHTTP: !pcsconfig.Config.EnableHTTPS, 83 } 84 85 // 设置下载最大并发量 86 if options.Parallel < 1 { 87 options.Parallel = pcsconfig.Config.MaxParallel 88 } 89 90 paths, err := matchPathByShellPattern(paths...) 91 if err != nil { 92 fmt.Println(err) 93 return 94 } 95 96 fmt.Print("\n") 97 fmt.Printf("[0] 提示: 当前下载最大并发量为: %d, 下载缓存为: %d\n", options.Parallel, cfg.CacheSize) 98 99 var ( 100 pcs = GetBaiduPCS() 101 loadCount = 0 102 ) 103 104 // 预测要下载的文件数量 105 file_dir_list := make([]*baidupcs.FileDirectory,0,10) 106 for k := range paths { 107 pcs.FilesDirectoriesRecurseList(paths[k], baidupcs.DefaultOrderOptions, func(depth int, _ string, fd *baidupcs.FileDirectory, pcsError pcserror.Error) bool { 108 if pcsError != nil { 109 pcsCommandVerbose.Warnf("%s\n", pcsError) 110 return true 111 } 112 file_dir_list = append(file_dir_list, fd) 113 // 忽略统计文件夹数量 114 if !fd.Isdir { 115 loadCount++ 116 if loadCount >= options.Load { 117 loadCount = options.Load 118 } 119 } 120 return true 121 }) 122 } 123 // 修改Load, 设置MaxParallel 124 if loadCount > 0 { 125 options.Load = loadCount 126 // 取平均值 127 cfg.MaxParallel = pcsconfig.AverageParallel(options.Parallel, loadCount) 128 } else { 129 cfg.MaxParallel = options.Parallel 130 } 131 132 var ( 133 executor = taskframework.TaskExecutor{ 134 IsFailedDeque: true, // 统计失败的列表 135 } 136 statistic = &pcsdownload.DownloadStatistic{} 137 ) 138 139 // 处理队列, 小文件优先下载 140 sort.Slice(file_dir_list, func(i, j int) bool { 141 return file_dir_list[i].Size < file_dir_list[j].Size 142 }) 143 for _,v := range file_dir_list { 144 newCfg := *cfg 145 unit := pcsdownload.DownloadTaskUnit{ 146 Cfg: &newCfg, // 复制一份新的cfg 147 PCS: pcs, 148 VerbosePrinter: pcsCommandVerbose, 149 PrintFormat: downloadPrintFormat(options.Load), 150 ParentTaskExecutor: &executor, 151 DownloadStatistic: statistic, 152 IsPrintStatus: options.IsPrintStatus, 153 IsExecutedPermission: options.IsExecutedPermission, 154 IsOverwrite: options.IsOverwrite, 155 NoCheck: options.NoCheck, 156 DlinkPrefer: options.LinkPrefer, 157 DownloadMode: options.DownloadMode, 158 ModifyMTime: options.ModifyMTime, 159 PcsPath: v.Path, 160 FileInfo: v, 161 } 162 // 设置下载并发数 163 executor.SetParallel(loadCount) 164 // 设置储存的路径 165 vPath := v.Path 166 if !options.FullPath { 167 vPath = filepath.Join(v.PreBase, filepath.Base(v.Path)) 168 } 169 if options.SaveTo != "" { 170 unit.SavePath = filepath.Join(options.SaveTo, vPath) 171 } else { 172 // 使用默认的保存路径 173 unit.SavePath = GetActiveUser().GetSavePath(vPath) 174 } 175 info := executor.Append(&unit, options.MaxRetry) 176 fmt.Printf("[%s] 加入下载队列: %s\n", info.Id(), v.Path) 177 } 178 179 // 开始计时 180 statistic.StartTimer() 181 182 // 开始执行 183 executor.Execute() 184 185 fmt.Printf("\n下载结束, 时间: %s, 数据总量: %s\n", statistic.Elapsed()/1e6*1e6, converter.ConvertFileSize(statistic.TotalSize())) 186 187 // 输出失败的文件列表 188 failedList := executor.FailedDeque() 189 if failedList.Size() != 0 { 190 fmt.Printf("以下文件下载失败: \n") 191 tb := pcstable.NewTable(os.Stdout) 192 for e := failedList.Shift(); e != nil; e = failedList.Shift() { 193 item := e.(*taskframework.TaskInfoItem) 194 tb.Append([]string{item.Info.Id(), item.Unit.(*pcsdownload.DownloadTaskUnit).PcsPath}) 195 } 196 tb.Render() 197 } 198 }