parent
3185aa4bef
commit
4705133ce7
|
|
@ -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 一样
|
||||
## 二次开发操作手册:
|
||||
|
|
|
|||
73
download.go
73
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) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
BuildCliBinary() // 编译二进制
|
||||
//BuildCliBinary() // 编译二进制
|
||||
CreateLibForQtUi() // 创建Qt需要使用的.a库文件
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
7
merge.go
7
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue