parent
3185aa4bef
commit
4705133ce7
|
|
@ -7,9 +7,6 @@
|
||||||
|
|
||||||
## 实现说明
|
## 实现说明
|
||||||
* download.go 大部分抄自 https://github.com/llychao/m3u8-downloader
|
* 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进行格式转换
|
* 使用 https://github.com/yapingcat/gomedia 代替ffmpeg进行格式转换
|
||||||
* 支持跳过前面几个ts文件(一般是广告, 嘿嘿)
|
* 支持跳过前面几个ts文件(一般是广告, 嘿嘿)
|
||||||
* 程序会在下载保存目录创建:
|
* 程序会在下载保存目录创建:
|
||||||
|
|
@ -24,7 +21,7 @@
|
||||||
* [x] 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载
|
* [x] 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载
|
||||||
* [x] windows、linux、mac都支持ffmpeg合并ts列表为mp4
|
* [x] windows、linux、mac都支持ffmpeg合并ts列表为mp4
|
||||||
* [x] 充分测试后,使用 https://github.com/yapingcat/gomedia 代替ffmpeg进行格式转换
|
* [x] 充分测试后,使用 https://github.com/yapingcat/gomedia 代替ffmpeg进行格式转换
|
||||||
* [ ] 支持嵌套m3u8的url
|
* [x] 支持嵌套m3u8的url
|
||||||
* [ ] 支持设置代理
|
* [ ] 支持设置代理
|
||||||
* [ ] 支持从curl命令解析出需要的header、auth-basic、cookie等信息,正如 https://github.com/cxjava/m3u8-downloader 一样
|
* [ ] 支持从curl命令解析出需要的header、auth-basic、cookie等信息,正如 https://github.com/cxjava/m3u8-downloader 一样
|
||||||
## 二次开发操作手册:
|
## 二次开发操作手册:
|
||||||
|
|
|
||||||
65
download.go
65
download.go
|
|
@ -110,7 +110,8 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
"Referer": []string{host},
|
"Referer": []string{host},
|
||||||
}
|
}
|
||||||
SetProgressBarTitle("嗅探m3u8")
|
SetProgressBarTitle("嗅探m3u8")
|
||||||
req.M3u8Url, err = this.sniffM3u8(req.M3u8Url)
|
var m3u8Body []byte
|
||||||
|
req.M3u8Url, m3u8Body, err = this.sniffM3u8(req.M3u8Url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "sniffM3u8: " + err.Error()
|
resp.ErrMsg = "sniffM3u8: " + err.Error()
|
||||||
resp.IsCancel = this.GetIsCancel()
|
resp.IsCancel = this.GetIsCancel()
|
||||||
|
|
@ -154,12 +155,6 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
// 获取m3u8地址的内容体
|
// 获取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))
|
ts_key, err := this.getM3u8Key(m3u8Host, string(m3u8Body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "getM3u8Key: " + err.Error()
|
resp.ErrMsg = "getM3u8Key: " + err.Error()
|
||||||
|
|
@ -194,6 +189,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{
|
err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{
|
||||||
TsFileList: tsFileList,
|
TsFileList: tsFileList,
|
||||||
OutputMp4: tmpOutputName,
|
OutputMp4: tmpOutputName,
|
||||||
|
ctx: this.ctx,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "合并错误: " + err.Error()
|
resp.ErrMsg = "合并错误: " + err.Error()
|
||||||
|
|
@ -241,6 +237,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
resp.ErrMsg = "删除下载目录失败: " + err.Error()
|
resp.ErrMsg = "删除下载目录失败: " + err.Error()
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
_ = os.Remove(filepath.Join(req.SaveDir, "downloading"))
|
||||||
SetProgressBarTitle("下载进度")
|
SetProgressBarTitle("下载进度")
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +264,10 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) {
|
||||||
}
|
}
|
||||||
gOldEnv = env
|
gOldEnv = env
|
||||||
gOldEnvLocker.Unlock()
|
gOldEnvLocker.Unlock()
|
||||||
return env.RunDownload(req)
|
resp = env.RunDownload(req)
|
||||||
|
SetProgressBarTitle("下载进度")
|
||||||
|
DrawProgressBar(1, 0)
|
||||||
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func CloseOldEnv() {
|
func CloseOldEnv() {
|
||||||
|
|
@ -522,19 +522,52 @@ func getFileSha256(targetFile string) (v string) {
|
||||||
return hex.EncodeToString(tmp[:])
|
return hex.EncodeToString(tmp[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) sniffM3u8(urlS string) (afterUrl string, err error) {
|
func (this *downloadEnv) sniffM3u8(urlS string) (afterUrl string, content []byte, err error) {
|
||||||
if strings.HasSuffix(strings.ToLower(urlS), ".m3u8") {
|
for idx := 0; idx < 5; idx++ {
|
||||||
return urlS, nil
|
content, err = this.doGetRequest(urlS)
|
||||||
}
|
|
||||||
content, err := this.doGetRequest(urlS)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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)
|
groups := regexp.MustCompile(`http[s]://[a-zA-Z0-9/\\.%_-]+.m3u8`).FindSubmatch(content)
|
||||||
if len(groups) == 0 {
|
if len(groups) == 0 {
|
||||||
return "", errors.New("未发现m3u8资源")
|
return "", nil, errors.New("未发现m3u8资源_2")
|
||||||
}
|
}
|
||||||
return string(groups[0]), nil
|
urlS = string(groups[0])
|
||||||
|
}
|
||||||
|
return "", nil, errors.New("未发现m3u8资源_3")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) doGetRequest(urlS string) (data []byte, err error) {
|
func (this *downloadEnv) doGetRequest(urlS string) (data []byte, err error) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
BuildCliBinary() // 编译二进制
|
//BuildCliBinary() // 编译二进制
|
||||||
CreateLibForQtUi() // 创建Qt需要使用的.a库文件
|
CreateLibForQtUi() // 创建Qt需要使用的.a库文件
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ void MainWindow::on_pushButton_RunDownload_clicked()
|
||||||
ui->pushButton_RunDownload->setEnabled(false);
|
ui->pushButton_RunDownload->setEnabled(false);
|
||||||
ui->checkBox_Insecure->setEnabled(false);
|
ui->checkBox_Insecure->setEnabled(false);
|
||||||
ui->progressBar->setValue(0);
|
ui->progressBar->setValue(0);
|
||||||
// ui->pushButton_RunDownload->setText("正在下载...");
|
|
||||||
ui->pushButton_StopDownload->setEnabled(true);
|
ui->pushButton_StopDownload->setEnabled(true);
|
||||||
|
|
||||||
m_syncUi.AddRunFnOn_OtherThread([this](){
|
m_syncUi.AddRunFnOn_OtherThread([this](){
|
||||||
|
|
@ -45,9 +44,9 @@ void MainWindow::on_pushButton_RunDownload_clicked()
|
||||||
while(isFinished->load() == false)
|
while(isFinished->load() == false)
|
||||||
{
|
{
|
||||||
QThread::msleep(50);
|
QThread::msleep(50);
|
||||||
GetProgress_Resp resp = GetProgress();
|
|
||||||
// 可能以下闭包在运行前, other thread已经退出了, 所以isFinished需要使用shared_ptr
|
// 可能以下闭包在运行前, 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->progressBar->setValue(resp.Percent);
|
||||||
ui->label_progressBar->setText(QString::fromStdString(resp.Title));
|
ui->label_progressBar->setText(QString::fromStdString(resp.Title));
|
||||||
if (ui->pushButton_RunDownload->isEnabled()) {
|
if (ui->pushButton_RunDownload->isEnabled()) {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,11 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<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>
|
||||||
<item row="1" column="2">
|
<item row="1" column="2">
|
||||||
<widget class="QLineEdit" name="lineEdit_SaveDir"/>
|
<widget class="QLineEdit" name="lineEdit_SaveDir"/>
|
||||||
|
|
|
||||||
7
merge.go
7
merge.go
|
|
@ -2,6 +2,7 @@ package m3u8d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/yapingcat/gomedia/mp4"
|
"github.com/yapingcat/gomedia/mp4"
|
||||||
"github.com/yapingcat/gomedia/mpeg2"
|
"github.com/yapingcat/gomedia/mpeg2"
|
||||||
|
|
@ -13,6 +14,7 @@ import (
|
||||||
type MergeTsFileListToSingleMp4_Req struct {
|
type MergeTsFileListToSingleMp4_Req struct {
|
||||||
TsFileList []string
|
TsFileList []string
|
||||||
OutputMp4 string
|
OutputMp4 string
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error) {
|
func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error) {
|
||||||
|
|
@ -42,6 +44,11 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, tsFile := range req.TsFileList {
|
for idx, tsFile := range req.TsFileList {
|
||||||
|
select {
|
||||||
|
case <-req.ctx.Done():
|
||||||
|
return req.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
DrawProgressBar(len(req.TsFileList), idx)
|
DrawProgressBar(len(req.TsFileList), idx)
|
||||||
var buf []byte
|
var buf []byte
|
||||||
buf, err = ioutil.ReadFile(tsFile)
|
buf, err = ioutil.ReadFile(tsFile)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue