1. 支持嵌套m3u8

2. 支持在合并mp4过程中快速取消退出
main
orestonce 2022-06-20 21:34:52 +08:00
parent 3185aa4bef
commit 4705133ce7
6 changed files with 69 additions and 29 deletions

View File

@ -7,9 +7,6 @@
## 实现说明
* download.go 大部分抄自 https://github.com/llychao/m3u8-downloader
* <del>将ffmpeg直接编译进windows/linux-arm/linux-386/darwin的二进制了.
转换mp4时,会自动将二进制里的ffmpeg解压到系统临时目录,然后使用.
因此linux/mac版本的可执行文件达到了32MB, 带Qt ui的可执行文件达到了惊人的 50MB</del>
* 使用 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 一样
## 二次开发操作手册:

View File

@ -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) {

View File

@ -10,7 +10,7 @@ import (
)
func main() {
BuildCliBinary() // 编译二进制
//BuildCliBinary() // 编译二进制
CreateLibForQtUi() // 创建Qt需要使用的.a库文件
}

View File

@ -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()) {

View File

@ -63,7 +63,11 @@
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="lineEdit_M3u8Url"/>
<widget class="QLineEdit" name="lineEdit_M3u8Url">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="lineEdit_SaveDir"/>

View File

@ -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)