diff --git a/build-release.sh b/build-release.sh old mode 100644 new mode 100755 diff --git a/m3u8-downloader.go b/m3u8-downloader.go index 34dbeaa..de3c75b 100644 --- a/m3u8-downloader.go +++ b/m3u8-downloader.go @@ -15,7 +15,7 @@ import ( "net/url" "os" "os/exec" - "path" + "path/filepath" "runtime" "strconv" "strings" @@ -38,7 +38,7 @@ var ( // 命令行参数 urlFlag = flag.String("u", "", "m3u8下载地址(http(s)://url/xx/xx/index.m3u8)") nFlag = flag.Int("n", 16, "下载线程数(max goroutines num)") - htFlag = flag.String("ht", "apiv1", "设置getHost的方式(apiv1: `http(s):// + url.Host + path.Dir(url.Path)`; apiv2: `http(s)://+ u.Host`") + htFlag = flag.String("ht", "apiv1", "设置getHost的方式(apiv1: `http(s):// + url.Host + filepath.Dir(url.Path)`; apiv2: `http(s)://+ u.Host`") oFlag = flag.String("o", "movie", "自定义文件名(默认为movie)") cFlag = flag.String("c", "", "自定义请求 cookie") sFlag = flag.Int("s", 0, "是否允许不安全的请求(默认为0)") @@ -105,12 +105,23 @@ func Run() { pwd = savePath } //pwd = "/Users/chao/Desktop" //自定义地址 - download_dir = path.Join(pwd, movieDir) + download_dir = filepath.Join(pwd, movieDir) if isExist, _ := pathExists(download_dir); !isExist { os.MkdirAll(download_dir, os.ModePerm) } m3u8Host := getHost(m3u8Url, hostType) m3u8Body := getM3u8Body(m3u8Url) + + // 判断该文件是二级目录还是ts列表 + if file, path := parseM3U8File(m3u8Body); file == TypeMenu { + // 请求这个地址,并将其作为原代码逻辑中的顶级路径,即 u 指定的参数 + parse, err := url.Parse(m3u8Url) + if err != nil { + panic(err) + } + m3u8Body = getM3u8Body(parse.Scheme + "://" + parse.Host + path) // 注意:这里的path直接假设为相同域名下了 + } + //m3u8Body := getFromFile() ts_key := getM3u8Key(m3u8Host, m3u8Body) if ts_key != "" { @@ -130,7 +141,7 @@ func Run() { default: unix_merge_file(download_dir) } - os.Rename(download_dir+"/merge.mp4", download_dir+".mp4") + os.Rename(filepath.Join(download_dir, "merge.mp4"), download_dir+".mp4") os.RemoveAll(download_dir) DrawProgressBar("Merging", float32(1), PROGRESS_WIDTH, "merge.ts") fmt.Printf("\n[Success] 下载保存路径:%s | 共耗时: %6.2fs\n", download_dir+".mp4", time.Now().Sub(now).Seconds()) @@ -142,7 +153,7 @@ func getHost(Url, ht string) (host string) { checkErr(err) switch ht { case "apiv1": - host = u.Scheme + "://" + u.Host + path.Dir(u.EscapedPath()) + host = u.Scheme + "://" + u.Host + filepath.Dir(u.EscapedPath()) case "apiv2": host = u.Scheme + "://" + u.Host } @@ -204,6 +215,28 @@ func getTsList(host, body string) (tsList []TsInfo) { return } +// 文件内容的类别 +const TypeMenu = 0 // 二级目录 +const TypePlayList = 1 // 播放列表,即 ts 列表 + +// 解析 M3U8 文件 +func parseM3U8File(content string) (int, string) { + lines := strings.Split(content, "\n") + for i, l := 0, len(lines); i < l; i++ { + // 二级 M3U8。多码率适配流。 + if strings.HasPrefix(lines[i], "#EXT-X-STREAM-INF") { + // 那么下一行应该是一个播放地址 + // 对于我的需求,我只要第一个地址的文件就行,因此我这里直接返回了 + if i+1 < l { + return TypeMenu, lines[i+1] + } + } + // 我只考虑这一种形式,因此其他类别不再继续写了 + // key、ts的获取都可以在这里完成 + } + return TypePlayList, "" // 默认代码原行为 +} + func getFromFile() string { data, _ := ioutil.ReadFile("./ts.txt") return string(data) @@ -229,7 +262,7 @@ func downloadTsFile(ts TsInfo, download_dir, key string, retries int) { downloadTsFile(ts, download_dir, key, retries-1) return } else { - //logger.Printf("[warn] File :%s, Retry %d \n", ts.Url, retries-1) + //logger.Printf("[warn] File :%s", ts.Url) return } } @@ -294,7 +327,7 @@ func downloader(tsList []TsInfo, maxGoroutines int, downloadDir string, key stri } func checkTsDownDir(dir string) bool { - if isExist, _ := pathExists(path.Join(dir, fmt.Sprintf(TS_NAME_TEMPLATE, 0))); !isExist { + if isExist, _ := pathExists(filepath.Join(dir, fmt.Sprintf(TS_NAME_TEMPLATE, 0))); !isExist { return true } return false