package main import ( "fmt" "io" "net/http" "os" "strings" "sync" "time" "github.com/schollz/progressbar/v3" ) type M3u8Res struct { IsMaster bool NextUrl string TsFile []string } // 创建一个连接池 var httpClient = &http.Client{ Transport: &http.Transport{ MaxIdleConnsPerHost: 5, }, } // 获取输入的m3u8链接 func getM3u8Path() (path string) { for { fmt.Println("请输入m3u8链接地址:") _, err := fmt.Scanln(&path) if err != nil { if err.Error() == "unexpected newline" { fmt.Println("你啥也没输入") continue } fmt.Println(err) continue } return } } // Get链接 func fetchM3u8Content(path string) string { res, err := httpClient.Get(path) if err != nil { fmt.Println(err) return "" } defer res.Body.Close() if res.StatusCode != 200 { fmt.Println("出了点问题", res.StatusCode) return "" } body, err := io.ReadAll(res.Body) if err != nil { fmt.Println(err) return "" } return string(body) } func parseM3u8(content string, baseUrl string) M3u8Res { lines := strings.Split(content, "\n") // 优先级最高:检查是否包含 Master 标签 for i, line := range lines { if strings.Contains(line, "#EXT-X-STREAM-INF") { return parseMainContent(lines, i, baseUrl) } } return M3u8Res{ IsMaster: false, TsFile: parseSubContent(lines), } } func parseMainContent(lines []string, index int, baseUrl string) M3u8Res { // 取匹配标签的下一行作为 URL if index+1 < len(lines) { nextUrl := strings.TrimSpace(lines[index+1]) return M3u8Res{ IsMaster: true, NextUrl: baseUrl + nextUrl, } } return M3u8Res{IsMaster: false} } func parseSubContent(lines []string) []string { var tsFiles []string for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "#") || line == "" { continue } if strings.HasSuffix(line, ".jpeg") || strings.HasSuffix(line, ".ts") { tsFiles = append(tsFiles, line) } } return tsFiles } // 提取基础路径 func getBaseUrl(path string) string { lastSlash := strings.LastIndex(path, "/") if lastSlash == -1 { return path + "/" } return path[:lastSlash+1] } //func getNewBaseUrl(path string) string { // lastSlash := strings.LastIndex(path, "/") // if lastSlash == -1 { // return path + "/" // } // subPath := path[:lastSlash] // secondLastSlash := strings.LastIndex(subPath, "/") // return path[:secondLastSlash+1] //} // 拼接下载地址 func buildUrl(baseUrl string, m3u8TsFile []string) []string { var downLoadUrl []string for _, tsUrl := range m3u8TsFile { fullUrl := baseUrl + tsUrl downLoadUrl = append(downLoadUrl, fullUrl) } return downLoadUrl } // 协程用 index int 是用来标识每个文件的序号,在并发下载中特别重要 func downLoadOne(Url string, index int) error { maxRetry := 3 // 定义最大重试次数 baseDelay := time.Second for retry := 0; retry < maxRetry; retry++ { if retry > 0 { waitTime := baseDelay * time.Duration(1<