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  }