From 0cb5a719089c3318d749c87bf5999eb83e4ce1cf Mon Sep 17 00:00:00 2001 From: "U-LIMIT\\15051" Date: Fri, 6 Mar 2026 08:52:55 +0800 Subject: [PATCH] ALL --- .idea/.gitignore | 10 ++ .idea/dictionaries/project.xml | 7 + .idea/go.imports.xml | 11 ++ .idea/m3u8.iml | 9 ++ .idea/modules.xml | 8 + go.mod | 11 ++ go.sum | 10 ++ main.go | 263 +++++++++++++++++++++++++++++++++ 8 files changed, 329 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/dictionaries/project.xml create mode 100644 .idea/go.imports.xml create mode 100644 .idea/m3u8.iml create mode 100644 .idea/modules.xml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..4647a46 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 已忽略包含查询文件的默认文件夹 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..e485277 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + outputpath + + + \ No newline at end of file diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..b893f17 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/m3u8.iml b/.idea/m3u8.iml new file mode 100644 index 0000000..338a266 --- /dev/null +++ b/.idea/m3u8.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2e4c3d6 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a00d452 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module m3u8-downloader + +go 1.25.0 + +require ( + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/schollz/progressbar/v3 v3.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3aad9e3 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc= +github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6818ed3 --- /dev/null +++ b/main.go @@ -0,0 +1,263 @@ +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<