github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/internal/pcsupdate/pcsupdate.go (about) 1 // Package pcsupdate 更新包 2 package pcsupdate 3 4 import ( 5 "archive/zip" 6 "bytes" 7 "fmt" 8 "github.com/qjfoidnh/BaiduPCS-Go/internal/pcsconfig" 9 "github.com/qjfoidnh/BaiduPCS-Go/pcsliner" 10 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil" 11 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/cachepool" 12 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/checkaccess" 13 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" 14 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/jsonhelper" 15 "github.com/qjfoidnh/BaiduPCS-Go/requester/downloader" 16 "github.com/qjfoidnh/BaiduPCS-Go/requester/rio" 17 "github.com/qjfoidnh/BaiduPCS-Go/requester/transfer" 18 "net/http" 19 "path/filepath" 20 "regexp" 21 "runtime" 22 "strconv" 23 "strings" 24 ) 25 26 const ( 27 // ReleaseName 分享根目录名称 28 ReleaseName = "BaiduPCS-Go-releases" 29 ) 30 31 type info struct { 32 filename string 33 size int64 34 downloadURL string 35 } 36 37 // CheckUpdate 检测更新 38 func CheckUpdate(version string, yes bool) { 39 if !checkaccess.AccessRDWR(pcsutil.ExecutablePath()) { 40 fmt.Printf("程序目录不可写, 无法更新.\n") 41 return 42 } 43 fmt.Println("检测更新中, 稍候...") 44 c := pcsconfig.Config.HTTPClient() 45 resp, err := c.Req(http.MethodGet, "https://api.github.com/repos/qjfoidnh/BaiduPCS-Go/releases/latest", nil, nil) 46 if resp != nil { 47 defer resp.Body.Close() 48 } 49 if err != nil { 50 fmt.Printf("获取数据错误: %s\n", err) 51 return 52 } 53 54 releaseInfo := ReleaseInfo{} 55 err = jsonhelper.UnmarshalData(resp.Body, &releaseInfo) 56 if err != nil { 57 fmt.Printf("json数据解析失败: %s\n", err) 58 return 59 } 60 61 // 没有更新, 或忽略 Beta 版本, 和版本前缀不符的 62 if strings.Contains(releaseInfo.TagName, "Beta") || !strings.HasPrefix(releaseInfo.TagName, "v") || version >= releaseInfo.TagName { 63 fmt.Printf("未检测到更新!\n") 64 return 65 } 66 67 fmt.Printf("检测到新版本: %s\n", releaseInfo.TagName) 68 69 line := pcsliner.NewLiner() 70 defer line.Close() 71 72 if !yes { 73 y, err := line.State.Prompt("是否进行更新 (y/n): ") 74 if err != nil { 75 fmt.Printf("输入错误: %s\n", err) 76 return 77 } 78 79 if y != "y" && y != "Y" { 80 fmt.Printf("更新取消.\n") 81 return 82 } 83 } 84 85 builder := &strings.Builder{} 86 builder.WriteString("BaiduPCS-Go-" + releaseInfo.TagName + "-" + runtime.GOOS + "-.*?") 87 if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { 88 builder.WriteString("arm") 89 } else { 90 switch runtime.GOARCH { 91 case "amd64": 92 builder.WriteString("(amd64|x86_64|x64)") 93 case "386": 94 builder.WriteString("(386|x86)") 95 case "arm": 96 builder.WriteString("(armv5|armv7|arm)") 97 case "arm64": 98 builder.WriteString("arm64") 99 case "mips": 100 builder.WriteString("mips") 101 case "mips64": 102 builder.WriteString("mips64") 103 case "mipsle": 104 builder.WriteString("(mipsle|mipsel)") 105 case "mips64le": 106 builder.WriteString("(mips64le|mips64el)") 107 default: 108 builder.WriteString(runtime.GOARCH) 109 } 110 } 111 builder.WriteString("\\.zip") 112 113 exp := regexp.MustCompile(builder.String()) 114 115 var targetList []*info 116 for _, asset := range releaseInfo.Assets { 117 if asset == nil || asset.State != "uploaded" { 118 continue 119 } 120 121 if exp.MatchString(asset.Name) { 122 targetList = append(targetList, &info{ 123 filename: asset.Name, 124 size: asset.Size, 125 downloadURL: asset.BrowserDownloadURL, 126 }) 127 } 128 } 129 130 var target info 131 switch len(targetList) { 132 case 0: 133 fmt.Printf("未匹配到当前系统的程序更新文件, GOOS: %s, GOARCH: %s\n", runtime.GOOS, runtime.GOARCH) 134 return 135 case 1: 136 target = *targetList[0] 137 default: 138 fmt.Println() 139 for k := range targetList { 140 fmt.Printf("%d: %s\n", k, targetList[k].filename) 141 } 142 143 fmt.Println() 144 t, err := line.State.Prompt("输入序号以下载更新: ") 145 if err != nil { 146 fmt.Printf("%s\n", err) 147 return 148 } 149 150 i, err := strconv.Atoi(t) 151 if err != nil { 152 fmt.Printf("输入错误: %s\n", err) 153 return 154 } 155 156 if i < 0 || i >= len(targetList) { 157 fmt.Printf("输入错误: 序号不在范围内\n") 158 return 159 } 160 161 target = *targetList[i] 162 } 163 164 if target.size > 0x7fffffff { 165 fmt.Printf("file size too large: %d\n", target.size) 166 return 167 } 168 169 fmt.Printf("准备下载更新: %s\n", target.filename) 170 171 // 开始下载 172 buf := rio.NewBuffer(cachepool.RawMallocByteSlice(int(target.size))) 173 der := downloader.NewDownloader(target.downloadURL, buf, &downloader.Config{ 174 MaxParallel: 20, 175 CacheSize: 10000, 176 }) 177 der.SetClient(c) 178 179 der.OnDownloadStatusEvent(func(status transfer.DownloadStatuser, workersCallback func(downloader.RangeWorkerFunc)) { 180 var leftStr string 181 left := status.TimeLeft() 182 if left < 0 { 183 leftStr = "-" 184 } else { 185 leftStr = left.String() 186 } 187 188 fmt.Printf("\r ↓ %s/%s %s/s in %s, left %s ............", 189 converter.ConvertFileSize(status.Downloaded(), 2), 190 converter.ConvertFileSize(status.TotalSize(), 2), 191 converter.ConvertFileSize(status.SpeedsPerSecond(), 2), 192 status.TimeElapsed()/1e7*1e7, leftStr, 193 ) 194 }) 195 der.OnFinish(func() { 196 fmt.Println() 197 }) 198 der.OnSuccess(func() { 199 fmt.Printf("下载完毕\n") 200 }) 201 202 err = der.Execute() 203 if err != nil { 204 fmt.Printf("下载发生错误: %s\n", err) 205 return 206 } 207 208 // 读取文件 209 reader, err := zip.NewReader(bytes.NewReader(buf.Bytes()), target.size) 210 if err != nil { 211 fmt.Printf("读取更新文件发生错误: %s\n", err) 212 return 213 } 214 215 execPath := pcsutil.ExecutablePath() 216 217 var fileNum, errTimes int 218 for _, zipFile := range reader.File { 219 if zipFile == nil { 220 continue 221 } 222 223 info := zipFile.FileInfo() 224 225 if info.IsDir() { 226 continue 227 } 228 229 rc, err := zipFile.Open() 230 if err != nil { 231 fmt.Printf("解析 zip 文件错误: %s\n", err) 232 continue 233 } 234 235 fileNum++ 236 237 name := zipFile.Name[strings.Index(zipFile.Name, "/")+1:] 238 if name == "BaiduPCS-Go" { 239 err = update(pcsutil.Executable(), rc) 240 } else { 241 err = update(filepath.Join(execPath, name), rc) 242 } 243 244 if err != nil { 245 errTimes++ 246 fmt.Printf("发生错误, zip 路径: %s, 错误: %s\n", zipFile.Name, err) 247 continue 248 } 249 } 250 251 if errTimes == fileNum { 252 fmt.Printf("更新失败\n") 253 return 254 } 255 256 fmt.Printf("更新完毕, 请重启程序\n") 257 }