diff --git a/README.md b/README.md index 0c82f04..4b0b1af 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,6 @@ ## 实现说明 * download.go 大部分抄自 https://github.com/llychao/m3u8-downloader -* 将ffmpeg直接编译进windows/linux-arm/linux-386/darwin的二进制了. - 转换mp4时,会自动将二进制里的ffmpeg解压到系统临时目录,然后使用. - 因此linux/mac版本的可执行文件达到了32MB, 带Qt ui的可执行文件达到了惊人的 50MB * 使用 https://github.com/yapingcat/gomedia 代替ffmpeg进行格式转换 * 支持跳过前面几个ts文件(一般是广告, 嘿嘿) * 程序会在下载保存目录创建: @@ -24,7 +21,7 @@ * [x] 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载 * [x] windows、linux、mac都支持ffmpeg合并ts列表为mp4 * [x] 充分测试后,使用 https://github.com/yapingcat/gomedia 代替ffmpeg进行格式转换 - * [ ] 支持嵌套m3u8的url + * [x] 支持嵌套m3u8的url * [ ] 支持设置代理 * [ ] 支持从curl命令解析出需要的header、auth-basic、cookie等信息,正如 https://github.com/cxjava/m3u8-downloader 一样 ## 二次开发操作手册: diff --git a/download.go b/download.go index 25d4135..0ca496d 100644 --- a/download.go +++ b/download.go @@ -110,7 +110,8 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp "Referer": []string{host}, } SetProgressBarTitle("嗅探m3u8") - req.M3u8Url, err = this.sniffM3u8(req.M3u8Url) + var m3u8Body []byte + req.M3u8Url, m3u8Body, err = this.sniffM3u8(req.M3u8Url) if err != nil { resp.ErrMsg = "sniffM3u8: " + err.Error() resp.IsCancel = this.GetIsCancel() @@ -154,12 +155,6 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp return resp } // 获取m3u8地址的内容体 - m3u8Body, err := this.doGetRequest(req.M3u8Url) - if err != nil { - resp.ErrMsg = "getM3u8Body: " + err.Error() - resp.IsCancel = this.GetIsCancel() - return resp - } ts_key, err := this.getM3u8Key(m3u8Host, string(m3u8Body)) if err != nil { resp.ErrMsg = "getM3u8Key: " + err.Error() @@ -194,6 +189,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{ TsFileList: tsFileList, OutputMp4: tmpOutputName, + ctx: this.ctx, }) if err != nil { resp.ErrMsg = "合并错误: " + err.Error() @@ -241,6 +237,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp resp.ErrMsg = "删除下载目录失败: " + err.Error() return resp } + _ = os.Remove(filepath.Join(req.SaveDir, "downloading")) SetProgressBarTitle("下载进度") return resp } @@ -267,7 +264,10 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) { } gOldEnv = env gOldEnvLocker.Unlock() - return env.RunDownload(req) + resp = env.RunDownload(req) + SetProgressBarTitle("下载进度") + DrawProgressBar(1, 0) + return resp } func CloseOldEnv() { @@ -522,19 +522,52 @@ func getFileSha256(targetFile string) (v string) { return hex.EncodeToString(tmp[:]) } -func (this *downloadEnv) sniffM3u8(urlS string) (afterUrl string, err error) { - if strings.HasSuffix(strings.ToLower(urlS), ".m3u8") { - return urlS, nil +func (this *downloadEnv) sniffM3u8(urlS string) (afterUrl string, content []byte, err error) { + for idx := 0; idx < 5; idx++ { + content, err = this.doGetRequest(urlS) + if err != nil { + return "", nil, err + } + if strings.HasSuffix(strings.ToLower(urlS), ".m3u8") { + // 看这个是不是嵌套的m3u8 + var m3u8Url string + containsTs := false + for _, line := range strings.Split(string(content), "\n") { + lineOrigin := strings.TrimSpace(line) + line = strings.ToLower(lineOrigin) + if strings.HasSuffix(line, ".m3u8") { + m3u8Url = lineOrigin + break + } + if strings.HasSuffix(line, ".ts") { + containsTs = true + break + } + } + if containsTs { + return urlS, content, err + } + if m3u8Url == "" { + return "", nil, errors.New("未发现m3u8资源_1") + } + urlObj, err := url.Parse(urlS) + if err != nil { + return "", nil, err + } + lineObj, err := url.Parse(m3u8Url) + if err != nil { + return "", nil, err + } + urlS = urlObj.ResolveReference(lineObj).String() + continue + } + groups := regexp.MustCompile(`http[s]://[a-zA-Z0-9/\\.%_-]+.m3u8`).FindSubmatch(content) + if len(groups) == 0 { + return "", nil, errors.New("未发现m3u8资源_2") + } + urlS = string(groups[0]) } - content, err := this.doGetRequest(urlS) - if err != nil { - return "", err - } - groups := regexp.MustCompile(`http[s]://[a-zA-Z0-9/\\.%_-]+.m3u8`).FindSubmatch(content) - if len(groups) == 0 { - return "", errors.New("未发现m3u8资源") - } - return string(groups[0]), nil + return "", nil, errors.New("未发现m3u8资源_3") } func (this *downloadEnv) doGetRequest(urlS string) (data []byte, err error) { diff --git a/export/main.go b/export/main.go index 0186ddd..28c7852 100644 --- a/export/main.go +++ b/export/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - BuildCliBinary() // 编译二进制 + //BuildCliBinary() // 编译二进制 CreateLibForQtUi() // 创建Qt需要使用的.a库文件 } diff --git a/m3u8d-qt/mainwindow.cpp b/m3u8d-qt/mainwindow.cpp index f04f7b3..ed8f2b5 100644 --- a/m3u8d-qt/mainwindow.cpp +++ b/m3u8d-qt/mainwindow.cpp @@ -35,7 +35,6 @@ void MainWindow::on_pushButton_RunDownload_clicked() ui->pushButton_RunDownload->setEnabled(false); ui->checkBox_Insecure->setEnabled(false); ui->progressBar->setValue(0); -// ui->pushButton_RunDownload->setText("正在下载..."); ui->pushButton_StopDownload->setEnabled(true); m_syncUi.AddRunFnOn_OtherThread([this](){ @@ -45,9 +44,9 @@ void MainWindow::on_pushButton_RunDownload_clicked() while(isFinished->load() == false) { QThread::msleep(50); - GetProgress_Resp resp = GetProgress(); // 可能以下闭包在运行前, other thread已经退出了, 所以isFinished需要使用shared_ptr - m_syncUi.AddRunFnOn_UiThread([resp, this, isFinished](){ + m_syncUi.AddRunFnOn_UiThread([this, isFinished](){ + GetProgress_Resp resp = GetProgress(); ui->progressBar->setValue(resp.Percent); ui->label_progressBar->setText(QString::fromStdString(resp.Title)); if (ui->pushButton_RunDownload->isEnabled()) { diff --git a/m3u8d-qt/mainwindow.ui b/m3u8d-qt/mainwindow.ui index 3db0f42..6da1585 100644 --- a/m3u8d-qt/mainwindow.ui +++ b/m3u8d-qt/mainwindow.ui @@ -63,7 +63,11 @@ - + + + + + diff --git a/merge.go b/merge.go index be11b91..b3909ce 100644 --- a/merge.go +++ b/merge.go @@ -2,6 +2,7 @@ package m3u8d import ( "bytes" + "context" "errors" "github.com/yapingcat/gomedia/mp4" "github.com/yapingcat/gomedia/mpeg2" @@ -13,6 +14,7 @@ import ( type MergeTsFileListToSingleMp4_Req struct { TsFileList []string OutputMp4 string + ctx context.Context } func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error) { @@ -42,6 +44,11 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error) } for idx, tsFile := range req.TsFileList { + select { + case <-req.ctx.Done(): + return req.ctx.Err() + default: + } DrawProgressBar(len(req.TsFileList), idx) var buf []byte buf, err = ioutil.ReadFile(tsFile)