Compare commits
37 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
4e02c29b59 | |
|
|
b82cc35649 | |
|
|
c49ab965e2 | |
|
|
a5352a067e | |
|
|
2e35b99cee | |
|
|
90c365dbdb | |
|
|
554eae5185 | |
|
|
b362a35ac1 | |
|
|
dfdf772e87 | |
|
|
1519511baf | |
|
|
7b1fa07cdf | |
|
|
3b3b18a4b8 | |
|
|
b866b25c2e | |
|
|
17a66b55f0 | |
|
|
75fc573345 | |
|
|
dc26c9b80f | |
|
|
8904ddbfd7 | |
|
|
9c4d189000 | |
|
|
d10b6c4fea | |
|
|
c5b39b9306 | |
|
|
b9f83a334f | |
|
|
92b1f422fb | |
|
|
011fc3e3e8 | |
|
|
29d6f00afe | |
|
|
0970e0e372 | |
|
|
0b9b67986f | |
|
|
6ff8da018e | |
|
|
eee8bd49cc | |
|
|
50a77efcb1 | |
|
|
f6ecf916b7 | |
|
|
3f97ecebda | |
|
|
c6b731d558 | |
|
|
71f55b91e9 | |
|
|
4812ef3523 | |
|
|
f4b668b7ac | |
|
|
0df35e75ef | |
|
|
912f62c5a4 |
|
|
@ -16,35 +16,18 @@ jobs:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Cache Primes
|
- name: install qt static
|
||||||
id: cache-primes
|
uses: orestonce/install-qt-static@v0.4.2
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: qt-static/
|
version: Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104
|
||||||
key: ${{ runner.os }}_qt-static
|
dir: qt_static_install
|
||||||
|
|
||||||
# Runs a set of commands using the runners shell
|
|
||||||
- name: Run a multi-line script
|
|
||||||
if: steps.cache-primes.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
mkdir download/
|
|
||||||
curl -L https://osdn.net/downloads/users/30/30723/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z -o download/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z
|
|
||||||
curl -L https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.4/threads-posix/dwarf/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z -o download/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z
|
|
||||||
7z x download/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z
|
|
||||||
7z x download/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z
|
|
||||||
mkdir qt-static
|
|
||||||
move Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104 qt-static\
|
|
||||||
move mingw32 qt-static\
|
|
||||||
rm -Force -r .\download\
|
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v3.2.1
|
uses: actions/setup-go@v3.2.1
|
||||||
with:
|
with:
|
||||||
# The Go version to download (if necessary) and use. Supports semver spec and ranges.
|
# The Go version to download (if necessary) and use. Supports semver spec and ranges.
|
||||||
go-version: 1.18 # optional
|
go-version: 1.18 # optional
|
||||||
- name: set env
|
- name: build
|
||||||
run: |
|
run: |
|
||||||
$env:Path="$PWD\qt-static\Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104\bin;$PWD\qt-static\mingw32\bin;$env:Path"
|
|
||||||
qt-static\Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104\QQtPatcher.exe
|
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go run export/main.go check-only
|
go run export/main.go check-only
|
||||||
cd m3u8d-qt && qmake && mingw32-make release && cd ..
|
cd m3u8d-qt && qmake && mingw32-make release && cd ..
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ jobs:
|
||||||
- name: set up qt-static env
|
- name: set up qt-static env
|
||||||
if: steps.cache-primes.outputs.cache-hit != 'true'
|
if: steps.cache-primes.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
# https://build-qt.fsu0413.me/5.15-series/5.15.4-for-macos/
|
# https://build-qt.fsu0413.me/5.15-series/5.15.6-for-macos/
|
||||||
mkdir -p env/download/ && cd env/download
|
mkdir -p env/download/ && cd env/download
|
||||||
curl -L https://osdn.net/downloads/users/38/38696/Qt5.15.4-macOS-x86_64-AppleClang12.0.5-20220513.tar.xz -o Qt5.15.4-macOS-x86_64-AppleClang12.0.5-20220513.tar.xz
|
curl -L https://github.com/orestonce/action/releases/download/qt-static5.15.6/Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework-20220914.tar.xz -o Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework-20220914.tar.xz
|
||||||
tar xf Qt5.15.4-macOS-x86_64-AppleClang12.0.5-20220513.tar.xz
|
tar xf Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework-20220914.tar.xz
|
||||||
pwd
|
pwd
|
||||||
ls -al Qt5.15.4-macOS-x86_64-AppleClang12.0.5
|
ls -al Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework
|
||||||
chmod +x Qt5.15.4-macOS-x86_64-AppleClang12.0.5/bin/*
|
chmod +x Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework/bin/*
|
||||||
mv Qt5.15.4-macOS-x86_64-AppleClang12.0.5 ../
|
mv Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework ../
|
||||||
cd ../../ && rm -rf env/download
|
cd ../../ && rm -rf env/download
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v3.2.1
|
uses: actions/setup-go@v3.2.1
|
||||||
|
|
@ -44,7 +44,7 @@ jobs:
|
||||||
go-version: 1.18 # optional
|
go-version: 1.18 # optional
|
||||||
- name: build
|
- name: build
|
||||||
run: |
|
run: |
|
||||||
export PATH=$(pwd)"/env/Qt5.15.4-macOS-x86_64-AppleClang12.0.5/bin:"${PATH}
|
export PATH=$(pwd)"/env/Qt5.15.6-macOS-x86_64-AppleClang12.0.5-noFramework/bin:"${PATH}
|
||||||
echo $PATH
|
echo $PATH
|
||||||
go mod tidy && go run export/main.go "check-only"
|
go mod tidy && go run export/main.go "check-only"
|
||||||
cd m3u8d-qt && qmake && ls -alh && make
|
cd m3u8d-qt && qmake && ls -alh && make
|
||||||
|
|
|
||||||
|
|
@ -18,26 +18,10 @@ jobs:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Cache Primes
|
- name: install qt static
|
||||||
id: cache-primes
|
uses: orestonce/install-qt-static@v0.4.2
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: qt-static/
|
version: Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104
|
||||||
key: ${{ runner.os }}_qt-static
|
|
||||||
|
|
||||||
# Runs a set of commands using the runners shell
|
|
||||||
- name: Run a multi-line script
|
|
||||||
if: steps.cache-primes.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
mkdir download/
|
|
||||||
curl -L https://osdn.net/downloads/users/30/30723/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z -o download/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z
|
|
||||||
curl -L https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.4/threads-posix/dwarf/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z -o download/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z
|
|
||||||
7z x download/Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104-ManualPatchSslLib.7z
|
|
||||||
7z x download/i686-4.9.4-release-posix-dwarf-rt_v5-rev0.7z
|
|
||||||
mkdir qt-static
|
|
||||||
move Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104 qt-static\
|
|
||||||
move mingw32 qt-static\
|
|
||||||
rm -Force -r .\download\
|
|
||||||
- name: Setup Go environment
|
- name: Setup Go environment
|
||||||
uses: actions/setup-go@v3.2.1
|
uses: actions/setup-go@v3.2.1
|
||||||
with:
|
with:
|
||||||
|
|
@ -45,8 +29,6 @@ jobs:
|
||||||
go-version: 1.18 # optional
|
go-version: 1.18 # optional
|
||||||
- name: set env
|
- name: set env
|
||||||
run: |
|
run: |
|
||||||
$env:Path="$PWD\qt-static\Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104\bin;$PWD\qt-static\mingw32\bin;$env:Path"
|
|
||||||
qt-static\Qt5.6.3-Windows-x86-MinGW4.9.4-staticFull-20200104\QQtPatcher.exe
|
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go run export/main.go
|
go run export/main.go
|
||||||
cd m3u8d-qt && qmake && mingw32-make release && cd ..
|
cd m3u8d-qt && qmake && mingw32-make release && cd ..
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@
|
||||||
/bin
|
/bin
|
||||||
/m3u8d-qt/m3u8d-impl.a
|
/m3u8d-qt/m3u8d-impl.a
|
||||||
/testdata/save_dir
|
/testdata/save_dir
|
||||||
|
/m3u8d-qt/m3u8d-qt.pro.user.*
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -19,6 +19,11 @@
|
||||||
* 将M3u8Url+SkipTsCountFromHead进行hash, 得到文件下载id
|
* 将M3u8Url+SkipTsCountFromHead进行hash, 得到文件下载id
|
||||||
* 将文件下载id/文件大小/文件内容hash 储存在 m3u8_cache.cdb里面, 下载前搜索下载目录
|
* 将文件下载id/文件大小/文件内容hash 储存在 m3u8_cache.cdb里面, 下载前搜索下载目录
|
||||||
如果发现某个文件大小/文件内容hash和以前的记录相等,则认为这个文件是以前下载的文件, 跳过此次下载.
|
如果发现某个文件大小/文件内容hash和以前的记录相等,则认为这个文件是以前下载的文件, 跳过此次下载.
|
||||||
|
* **curl模式** 可以赋予使用者任意设置下载请求的Header信息的能力,方便解决只有一个m3u8的链接时无法下载视频的尴尬局面
|
||||||
|
* 例子1, 你需要下载的视频是要登陆后观看的,Cookie信息里存放了登陆状态
|
||||||
|
* 例子2, 网站开发者验证了Referer信息、Authority信息、Origin信息、User-Agent信息、各种特定的Header信息
|
||||||
|
* 以windows下的chrome为例,找到对应的m3u8请求记录,然后右键选择 "Copy - Copy as cURL(bash)",
|
||||||
|
然后打开 windows-qt版本的 m3u8d, 点击 "curl 模式",将复制出来的请求粘贴上去即可
|
||||||
* 已有功能列表
|
* 已有功能列表
|
||||||
* 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载
|
* 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载
|
||||||
* windows、linux、mac都支持转换、合并ts格式为mp4
|
* windows、linux、mac都支持转换、合并ts格式为mp4
|
||||||
|
|
@ -28,9 +33,12 @@
|
||||||
* 支持从curl命令解析出需要的信息,正如 [cxjava/m3u8-downloader](https://github.com/cxjava/m3u8-downloader) 一样
|
* 支持从curl命令解析出需要的信息,正如 [cxjava/m3u8-downloader](https://github.com/cxjava/m3u8-downloader) 一样
|
||||||
* 显示下载速度、合并ts的速度
|
* 显示下载速度、合并ts的速度
|
||||||
* 提供macos的图形化界面
|
* 提供macos的图形化界面
|
||||||
|
* 支持下载aes加密的m3u8
|
||||||
|
* 内部使用多线程下载ts文件
|
||||||
* 支持设置代理: http/socks5
|
* 支持设置代理: http/socks5
|
||||||
* http代理解释: 要访问的真实url是http协议, 使用代理服务器可见的GET形式; 如果要访问的真实url是https协议, 使用代理服务器不可见的CONNECT形式
|
* http代理解释: 要访问的真实url是http协议, 使用代理服务器可见的GET/POST/HEAD...形式; 如果要访问的真实url是https协议, 使用代理服务器不可见的CONNECT形式
|
||||||
## TODO:
|
## TODO:
|
||||||
|
* [ ] 多线程修改为自适应模式,在下载过程中动态调整线程池大小,以便达到最快的下载速度
|
||||||
* [ ] 支持多国语言
|
* [ ] 支持多国语言
|
||||||
* [ ] 支持从一个txt里读取下载列表,批量下载
|
* [ ] 支持从一个txt里读取下载列表,批量下载
|
||||||
## 二次开发操作手册:
|
## 二次开发操作手册:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package m3u8d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gMergeStatus SpeedStatus
|
||||||
|
var gMergeCancel context.CancelFunc
|
||||||
|
var gMergeCancelLocker sync.Mutex
|
||||||
|
|
||||||
|
type MergeTsDir_Resp struct {
|
||||||
|
ErrMsg string
|
||||||
|
IsCancel bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var gMergeIsRunning bool
|
||||||
|
var gMergeIsRunningLocker sync.Mutex
|
||||||
|
|
||||||
|
func beginMerge() bool {
|
||||||
|
gMergeIsRunningLocker.Lock()
|
||||||
|
defer gMergeIsRunningLocker.Unlock()
|
||||||
|
|
||||||
|
if gMergeIsRunning != false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
gMergeIsRunning = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeTsDir(InputTsDir string, OutputMp4Name string) (resp MergeTsDir_Resp) {
|
||||||
|
if !beginMerge() {
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
gMergeIsRunningLocker.Lock()
|
||||||
|
gMergeIsRunning = false
|
||||||
|
gMergeIsRunningLocker.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
fList, err := ioutil.ReadDir(InputTsDir)
|
||||||
|
if err != nil {
|
||||||
|
resp.ErrMsg = "读取目录失败 " + err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var tsFileList []string
|
||||||
|
for _, f := range fList {
|
||||||
|
if f.Mode().IsRegular() && strings.HasSuffix(strings.ToLower(f.Name()), ".ts") {
|
||||||
|
tsFileList = append(tsFileList, filepath.Join(InputTsDir, f.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(tsFileList) == 0 {
|
||||||
|
resp.ErrMsg = "目录下不存在ts文件: " + InputTsDir
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Strings(tsFileList) // 按照字典顺序排序
|
||||||
|
if OutputMp4Name == "" {
|
||||||
|
OutputMp4Name = filepath.Join(InputTsDir, "all.mp4")
|
||||||
|
} else if !filepath.IsAbs(OutputMp4Name) {
|
||||||
|
OutputMp4Name = filepath.Join(InputTsDir, OutputMp4Name)
|
||||||
|
}
|
||||||
|
ctx, cancelFn := context.WithCancel(context.Background())
|
||||||
|
defer cancelFn()
|
||||||
|
|
||||||
|
gMergeCancelLocker.Lock()
|
||||||
|
if gMergeCancel != nil {
|
||||||
|
gMergeCancel()
|
||||||
|
}
|
||||||
|
gMergeCancel = cancelFn
|
||||||
|
gMergeCancelLocker.Unlock()
|
||||||
|
|
||||||
|
err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{
|
||||||
|
TsFileList: tsFileList,
|
||||||
|
OutputMp4: OutputMp4Name,
|
||||||
|
Ctx: ctx,
|
||||||
|
Status: &gMergeStatus,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
resp.ErrMsg = "合并错误: " + err.Error()
|
||||||
|
resp.IsCancel = isContextCancel(ctx)
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
type MergeGetProgressPercent_Resp struct {
|
||||||
|
Percent int
|
||||||
|
SpeedText string
|
||||||
|
IsRunning bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeGetProgressPercent() (resp MergeGetProgressPercent_Resp) {
|
||||||
|
gMergeIsRunningLocker.Lock()
|
||||||
|
resp.IsRunning = gMergeIsRunning
|
||||||
|
gMergeIsRunningLocker.Unlock()
|
||||||
|
if resp.IsRunning {
|
||||||
|
resp.Percent = gMergeStatus.GetPercent()
|
||||||
|
resp.SpeedText = gMergeStatus.SpeedRecent5sGetAndUpdate()
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeStop() {
|
||||||
|
gMergeCancelLocker.Lock()
|
||||||
|
if gMergeCancel != nil {
|
||||||
|
gMergeCancel()
|
||||||
|
}
|
||||||
|
gMergeCancelLocker.Unlock()
|
||||||
|
}
|
||||||
|
|
@ -96,6 +96,7 @@ var mergeCmd = &cobra.Command{
|
||||||
TsFileList: tsFileList,
|
TsFileList: tsFileList,
|
||||||
OutputMp4: gMergeReq.OutputMp4Name,
|
OutputMp4: gMergeReq.OutputMp4Name,
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
|
Status: nil,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("合并失败", err)
|
log.Fatalln("合并失败", err)
|
||||||
|
|
@ -113,6 +114,7 @@ func init() {
|
||||||
downloadCmd.Flags().IntVarP(&gRunReq.SkipTsCountFromHead, "SkipTsCountFromHead", "", 0, "跳过前面几个ts")
|
downloadCmd.Flags().IntVarP(&gRunReq.SkipTsCountFromHead, "SkipTsCountFromHead", "", 0, "跳过前面几个ts")
|
||||||
downloadCmd.Flags().StringVarP(&gRunReq.SetProxy, "SetProxy", "", "", "代理设置, http://127.0.0.1:8080 socks5://127.0.0.1:1089")
|
downloadCmd.Flags().StringVarP(&gRunReq.SetProxy, "SetProxy", "", "", "代理设置, http://127.0.0.1:8080 socks5://127.0.0.1:1089")
|
||||||
downloadCmd.Flags().BoolVarP(&gRunReq.SkipRemoveTs, "SkipRemoveTs", "", false, "不删除下载的ts文件")
|
downloadCmd.Flags().BoolVarP(&gRunReq.SkipRemoveTs, "SkipRemoveTs", "", false, "不删除下载的ts文件")
|
||||||
|
downloadCmd.Flags().IntVarP(&gRunReq.ThreadCount, "ThreadCount", "", 8, "下载线程数")
|
||||||
rootCmd.AddCommand(downloadCmd)
|
rootCmd.AddCommand(downloadCmd)
|
||||||
curlCmd.DisableFlagParsing = true
|
curlCmd.DisableFlagParsing = true
|
||||||
rootCmd.AddCommand(curlCmd)
|
rootCmd.AddCommand(curlCmd)
|
||||||
|
|
|
||||||
244
download.go
244
download.go
|
|
@ -1,20 +1,22 @@
|
||||||
package m3u8d
|
package m3u8d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/orestonce/gopool"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -22,12 +24,15 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/orestonce/gopool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TsInfo 用于保存 ts 文件的下载地址和文件名
|
// TsInfo 用于保存 ts 文件的下载地址和文件名
|
||||||
type TsInfo struct {
|
type TsInfo struct {
|
||||||
Name string
|
Name string
|
||||||
Url string
|
Url string
|
||||||
|
Seq uint64 // 如果是aes加密并且没有iv, 这个seq需要充当iv
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProgress_Resp struct {
|
type GetProgress_Resp struct {
|
||||||
|
|
@ -36,6 +41,8 @@ type GetProgress_Resp struct {
|
||||||
StatusBar string
|
StatusBar string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var PNG_SIGN = []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
|
||||||
|
|
||||||
func GetProgress() (resp GetProgress_Resp) {
|
func GetProgress() (resp GetProgress_Resp) {
|
||||||
var sleepTh int32
|
var sleepTh int32
|
||||||
var speedV string
|
var speedV string
|
||||||
|
|
@ -43,14 +50,14 @@ func GetProgress() (resp GetProgress_Resp) {
|
||||||
gOldEnvLocker.Lock()
|
gOldEnvLocker.Lock()
|
||||||
if gOldEnv != nil {
|
if gOldEnv != nil {
|
||||||
sleepTh = atomic.LoadInt32(&gOldEnv.sleepTh)
|
sleepTh = atomic.LoadInt32(&gOldEnv.sleepTh)
|
||||||
gOldEnv.progressLocker.Lock()
|
resp.Percent = gOldEnv.status.GetPercent()
|
||||||
resp.Percent = gOldEnv.progressPercent
|
resp.Title = gOldEnv.status.GetTitle()
|
||||||
resp.Title = gOldEnv.progressBarTitle
|
|
||||||
gOldEnv.progressLocker.Unlock()
|
|
||||||
if resp.Title == "" {
|
if resp.Title == "" {
|
||||||
resp.Title = "正在下载"
|
resp.Title = "正在下载"
|
||||||
}
|
}
|
||||||
speedV = gOldEnv.speedRecent5sGetAndUpdate()
|
if !gOldEnv.GetIsCancel() {
|
||||||
|
speedV = gOldEnv.status.SpeedRecent5sGetAndUpdate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gOldEnvLocker.Unlock()
|
gOldEnvLocker.Unlock()
|
||||||
resp.StatusBar = speedV
|
resp.StatusBar = speedV
|
||||||
|
|
@ -63,12 +70,6 @@ func GetProgress() (resp GetProgress_Resp) {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) SetProgressBarTitle(title string) {
|
|
||||||
this.progressLocker.Lock()
|
|
||||||
this.progressBarTitle = title
|
|
||||||
this.progressLocker.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunDownload_Resp struct {
|
type RunDownload_Resp struct {
|
||||||
ErrMsg string
|
ErrMsg string
|
||||||
IsSkipped bool
|
IsSkipped bool
|
||||||
|
|
@ -86,6 +87,7 @@ type RunDownload_Req struct {
|
||||||
HeaderMap map[string][]string
|
HeaderMap map[string][]string
|
||||||
SkipRemoveTs bool
|
SkipRemoveTs bool
|
||||||
ProgressBarShow bool
|
ProgressBarShow bool
|
||||||
|
ThreadCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
type downloadEnv struct {
|
type downloadEnv struct {
|
||||||
|
|
@ -94,13 +96,7 @@ type downloadEnv struct {
|
||||||
nowClient *http.Client
|
nowClient *http.Client
|
||||||
header http.Header
|
header http.Header
|
||||||
sleepTh int32
|
sleepTh int32
|
||||||
progressLocker sync.Mutex
|
status SpeedStatus
|
||||||
progressBarTitle string
|
|
||||||
progressPercent int
|
|
||||||
progressBarShow bool
|
|
||||||
speedBytesLocker sync.Mutex
|
|
||||||
speedBeginTime time.Time
|
|
||||||
speedBytesMap map[time.Time]int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp) {
|
func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp) {
|
||||||
|
|
@ -113,7 +109,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.FileName == "" {
|
if req.FileName == "" {
|
||||||
req.FileName = "all"
|
req.FileName = GetFileNameFromUrl(req.M3u8Url)
|
||||||
}
|
}
|
||||||
if req.SkipTsCountFromHead < 0 {
|
if req.SkipTsCountFromHead < 0 {
|
||||||
req.SkipTsCountFromHead = 0
|
req.SkipTsCountFromHead = 0
|
||||||
|
|
@ -134,7 +130,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
for key, valueList := range req.HeaderMap {
|
for key, valueList := range req.HeaderMap {
|
||||||
this.header[key] = valueList
|
this.header[key] = valueList
|
||||||
}
|
}
|
||||||
this.SetProgressBarTitle("[1/6]嗅探m3u8")
|
this.status.SetProgressBarTitle("[1/6]嗅探m3u8")
|
||||||
var m3u8Body []byte
|
var m3u8Body []byte
|
||||||
var errMsg string
|
var errMsg string
|
||||||
req.M3u8Url, m3u8Body, errMsg = this.sniffM3u8(req.M3u8Url)
|
req.M3u8Url, m3u8Body, errMsg = this.sniffM3u8(req.M3u8Url)
|
||||||
|
|
@ -154,7 +150,7 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
if info != nil {
|
if info != nil {
|
||||||
this.SetProgressBarTitle("[2/6]检查是否已下载")
|
this.status.SetProgressBarTitle("[2/6]检查是否已下载")
|
||||||
latestNameFullPath, found := info.SearchVideoInDir(req.SaveDir)
|
latestNameFullPath, found := info.SearchVideoInDir(req.SaveDir)
|
||||||
if found {
|
if found {
|
||||||
resp.IsSkipped = true
|
resp.IsSkipped = true
|
||||||
|
|
@ -174,15 +170,26 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 获取m3u8地址的内容体
|
|
||||||
tsKey, err := this.getM3u8Key(req.M3u8Url, string(m3u8Body))
|
downloadingFilePath := filepath.Join(downloadDir, "downloading.txt")
|
||||||
|
if !isFileExists(downloadingFilePath) {
|
||||||
|
err = ioutil.WriteFile(downloadingFilePath, []byte(req.M3u8Url), 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "getM3u8Key: " + err.Error()
|
resp.ErrMsg = "os.WriteUrl error: " + err.Error()
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beginSeq := parseBeginSeq(m3u8Body)
|
||||||
|
// 获取m3u8地址的内容体
|
||||||
|
encInfo, err := this.getEncryptInfo(req.M3u8Url, string(m3u8Body))
|
||||||
|
if err != nil {
|
||||||
|
resp.ErrMsg = "getEncryptInfo: " + err.Error()
|
||||||
resp.IsCancel = this.GetIsCancel()
|
resp.IsCancel = this.GetIsCancel()
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
this.SetProgressBarTitle("[3/6]获取ts列表")
|
this.status.SetProgressBarTitle("[3/6]获取ts列表")
|
||||||
tsList, errMsg := getTsList(req.M3u8Url, string(m3u8Body))
|
tsList, errMsg := getTsList(beginSeq, req.M3u8Url, string(m3u8Body))
|
||||||
if errMsg != "" {
|
if errMsg != "" {
|
||||||
resp.ErrMsg = "获取ts列表错误: " + errMsg
|
resp.ErrMsg = "获取ts列表错误: " + errMsg
|
||||||
return resp
|
return resp
|
||||||
|
|
@ -193,16 +200,16 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
}
|
}
|
||||||
tsList = tsList[req.SkipTsCountFromHead:]
|
tsList = tsList[req.SkipTsCountFromHead:]
|
||||||
// 下载ts
|
// 下载ts
|
||||||
this.SetProgressBarTitle("[4/6]下载ts")
|
this.status.SetProgressBarTitle("[4/6]下载ts")
|
||||||
this.speedSetBegin()
|
this.status.SpeedResetBytes()
|
||||||
err = this.downloader(tsList, downloadDir, tsKey)
|
err = this.downloader(tsList, downloadDir, encInfo, req.ThreadCount)
|
||||||
this.speedClearBytes()
|
this.status.SpeedResetBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "下载ts文件错误: " + err.Error()
|
resp.ErrMsg = "下载ts文件错误: " + err.Error()
|
||||||
resp.IsCancel = this.GetIsCancel()
|
resp.IsCancel = this.GetIsCancel()
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
this.DrawProgressBar(1, 1)
|
this.status.DrawProgressBar(1, 1)
|
||||||
var tsFileList []string
|
var tsFileList []string
|
||||||
for _, one := range tsList {
|
for _, one := range tsList {
|
||||||
tsFileList = append(tsFileList, filepath.Join(downloadDir, one.Name))
|
tsFileList = append(tsFileList, filepath.Join(downloadDir, one.Name))
|
||||||
|
|
@ -211,19 +218,19 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
var contentHash string
|
var contentHash string
|
||||||
tmpOutputName = filepath.Join(downloadDir, "all.merge.mp4")
|
tmpOutputName = filepath.Join(downloadDir, "all.merge.mp4")
|
||||||
|
|
||||||
this.SetProgressBarTitle("[5/6]合并ts为mp4")
|
this.status.SetProgressBarTitle("[5/6]合并ts为mp4")
|
||||||
this.speedSetBegin()
|
|
||||||
err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{
|
err = MergeTsFileListToSingleMp4(MergeTsFileListToSingleMp4_Req{
|
||||||
TsFileList: tsFileList,
|
TsFileList: tsFileList,
|
||||||
OutputMp4: tmpOutputName,
|
OutputMp4: tmpOutputName,
|
||||||
Ctx: this.ctx,
|
Ctx: this.ctx,
|
||||||
|
Status: &this.status,
|
||||||
})
|
})
|
||||||
this.speedClearBytes()
|
this.status.SpeedResetBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ErrMsg = "合并错误: " + err.Error()
|
resp.ErrMsg = "合并错误: " + err.Error()
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
this.SetProgressBarTitle("[6/6]计算文件hash")
|
this.status.SetProgressBarTitle("[6/6]计算文件hash")
|
||||||
contentHash = getFileSha256(tmpOutputName)
|
contentHash = getFileSha256(tmpOutputName)
|
||||||
if contentHash == "" {
|
if contentHash == "" {
|
||||||
resp.ErrMsg = "无法计算摘要信息: " + tmpOutputName
|
resp.ErrMsg = "无法计算摘要信息: " + tmpOutputName
|
||||||
|
|
@ -272,6 +279,16 @@ func (this *downloadEnv) RunDownload(req RunDownload_Req) (resp RunDownload_Resp
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBeginSeq(body []byte) uint64 {
|
||||||
|
data := M3u8Parse(string(body))
|
||||||
|
seq := data.GetPart(`#EXT-X-MEDIA-SEQUENCE`).TextFull
|
||||||
|
u, err := strconv.ParseUint(seq, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
var gOldEnv *downloadEnv
|
var gOldEnv *downloadEnv
|
||||||
var gOldEnvLocker sync.Mutex
|
var gOldEnvLocker sync.Mutex
|
||||||
|
|
||||||
|
|
@ -291,8 +308,9 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) {
|
||||||
Proxy: http.ProxyURL(proxyUrlObj),
|
Proxy: http.ProxyURL(proxyUrlObj),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
speedBytesMap: map[time.Time]int64{},
|
status: SpeedStatus{
|
||||||
progressBarShow: req.ProgressBarShow,
|
progressBarShow: req.ProgressBarShow,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
env.ctx, env.cancelFn = context.WithCancel(context.Background())
|
env.ctx, env.cancelFn = context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
|
@ -303,8 +321,8 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) {
|
||||||
gOldEnv = env
|
gOldEnv = env
|
||||||
gOldEnvLocker.Unlock()
|
gOldEnvLocker.Unlock()
|
||||||
resp = env.RunDownload(req)
|
resp = env.RunDownload(req)
|
||||||
env.SetProgressBarTitle("下载进度")
|
env.status.SetProgressBarTitle("下载进度")
|
||||||
env.DrawProgressBar(1, 0)
|
env.status.DrawProgressBar(1, 0)
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,31 +355,42 @@ func GetWd() string {
|
||||||
return wd
|
return wd
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取m3u8加密的密钥
|
// 获取m3u8加密的密钥, 可能存在iv
|
||||||
func (this *downloadEnv) getM3u8Key(m3u8Url string, html string) (key string, err error) {
|
func (this *downloadEnv) getEncryptInfo(m3u8Url string, html string) (info *EncryptInfo, err error) {
|
||||||
key = ""
|
keyPart := M3u8Parse(html).GetPart("#EXT-X-KEY")
|
||||||
for _, line := range splitLineWithTrimSpace(html) {
|
uri := keyPart.KeyValue["URI"]
|
||||||
if strings.Contains(line, "#EXT-X-KEY") == false {
|
if uri == "" {
|
||||||
continue
|
return nil, nil
|
||||||
}
|
}
|
||||||
uriPos := strings.Index(line, "URI")
|
method := keyPart.KeyValue["METHOD"]
|
||||||
quotationMarkPos := strings.LastIndex(line, "\"")
|
if method == EncryptMethod_NONE {
|
||||||
keyUrl := strings.Split(line[uriPos:quotationMarkPos], "\"")[1]
|
return nil, nil
|
||||||
if !strings.Contains(line, "http") {
|
}
|
||||||
var errMsg string
|
keyUrl, errMsg := resolveRefUrl(m3u8Url, uri)
|
||||||
keyUrl, errMsg = resolveRefUrl(m3u8Url, line)
|
|
||||||
if errMsg != "" {
|
if errMsg != "" {
|
||||||
return "", errors.New(errMsg)
|
return nil, errors.New(errMsg)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var res []byte
|
var res []byte
|
||||||
res, err = this.doGetRequest(keyUrl)
|
res, err = this.doGetRequest(keyUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return string(res), nil
|
if method == EncryptMethod_AES128 && len(res) != 16 { // Aes 128
|
||||||
|
return nil, errors.New("getEncryptInfo invalid key " + strconv.Quote(string(res)))
|
||||||
}
|
}
|
||||||
return "", nil
|
var iv []byte
|
||||||
|
ivs := keyPart.KeyValue["IV"]
|
||||||
|
if ivs != "" {
|
||||||
|
iv, err = hex.DecodeString(strings.TrimPrefix(ivs, "0x"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &EncryptInfo{
|
||||||
|
Method: method,
|
||||||
|
Key: res,
|
||||||
|
Iv: iv,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitLineWithTrimSpace(s string) []string {
|
func splitLineWithTrimSpace(s string) []string {
|
||||||
|
|
@ -373,7 +402,7 @@ func splitLineWithTrimSpace(s string) []string {
|
||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTsList(m38uUrl string, body string) (tsList []TsInfo, errMsg string) {
|
func getTsList(beginSeq uint64, m38uUrl string, body string) (tsList []TsInfo, errMsg string) {
|
||||||
index := 0
|
index := 0
|
||||||
|
|
||||||
for _, line := range splitLineWithTrimSpace(body) {
|
for _, line := range splitLineWithTrimSpace(body) {
|
||||||
|
|
@ -388,6 +417,7 @@ func getTsList(m38uUrl string, body string) (tsList []TsInfo, errMsg string) {
|
||||||
tsList = append(tsList, TsInfo{
|
tsList = append(tsList, TsInfo{
|
||||||
Name: fmt.Sprintf("%05d.ts", index), // ts视频片段命名规则
|
Name: fmt.Sprintf("%05d.ts", index), // ts视频片段命名规则
|
||||||
Url: after,
|
Url: after,
|
||||||
|
Seq: beginSeq + uint64(index-1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +426,7 @@ func getTsList(m38uUrl string, body string) (tsList []TsInfo, errMsg string) {
|
||||||
|
|
||||||
// 下载ts文件
|
// 下载ts文件
|
||||||
// @modify: 2020-08-13 修复ts格式SyncByte合并不能播放问题
|
// @modify: 2020-08-13 修复ts格式SyncByte合并不能播放问题
|
||||||
func (this *downloadEnv) downloadTsFile(ts TsInfo, download_dir, key string) (err error) {
|
func (this *downloadEnv) downloadTsFile(ts TsInfo, download_dir string, encInfo *EncryptInfo) (err error) {
|
||||||
currPath := fmt.Sprintf("%s/%s", download_dir, ts.Name)
|
currPath := fmt.Sprintf("%s/%s", download_dir, ts.Name)
|
||||||
if isFileExists(currPath) {
|
if isFileExists(currPath) {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -409,13 +439,19 @@ func (this *downloadEnv) downloadTsFile(ts TsInfo, download_dir, key string) (er
|
||||||
var origData []byte
|
var origData []byte
|
||||||
origData = data
|
origData = data
|
||||||
// 解密出视频 ts 源文件
|
// 解密出视频 ts 源文件
|
||||||
if key != "" {
|
if encInfo != nil {
|
||||||
//解密 ts 文件,算法:aes 128 cbc pack5
|
//解密 ts 文件,算法:aes 128 cbc pack5
|
||||||
origData, err = AesDecrypt(origData, []byte(key))
|
origData, err = AesDecrypt(ts.Seq, origData, encInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect Fake png file
|
||||||
|
if bytes.HasPrefix(origData, PNG_SIGN) {
|
||||||
|
origData = origData[8:]
|
||||||
|
}
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/MPEG_transport_stream
|
// https://en.wikipedia.org/wiki/MPEG_transport_stream
|
||||||
// Some TS files do not start with SyncByte 0x47, they can not be played after merging,
|
// Some TS files do not start with SyncByte 0x47, they can not be played after merging,
|
||||||
// Need to remove the bytes before the SyncByte 0x47(71).
|
// Need to remove the bytes before the SyncByte 0x47(71).
|
||||||
|
|
@ -436,7 +472,7 @@ func (this *downloadEnv) downloadTsFile(ts TsInfo, download_dir, key string) (er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
this.speedAddBytes(len(origData))
|
this.status.SpeedAddBytes(len(origData))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -447,8 +483,11 @@ func (this *downloadEnv) SleepDur(d time.Duration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) downloader(tsList []TsInfo, downloadDir string, key string) (err error) {
|
func (this *downloadEnv) downloader(tsList []TsInfo, downloadDir string, encInfo *EncryptInfo, threadCount int) (err error) {
|
||||||
task := gopool.NewThreadPool(1)
|
if threadCount <= 0 || threadCount > 1000 {
|
||||||
|
return errors.New("downloadEnv.threadCount invalid: " + strconv.Itoa(threadCount))
|
||||||
|
}
|
||||||
|
task := gopool.NewThreadPool(threadCount)
|
||||||
tsLen := len(tsList)
|
tsLen := len(tsList)
|
||||||
downloadCount := 0
|
downloadCount := 0
|
||||||
var locker sync.Mutex
|
var locker sync.Mutex
|
||||||
|
|
@ -469,7 +508,7 @@ func (this *downloadEnv) downloader(tsList []TsInfo, downloadDir string, key str
|
||||||
this.SleepDur(time.Second * time.Duration(i))
|
this.SleepDur(time.Second * time.Duration(i))
|
||||||
atomic.AddInt32(&this.sleepTh, -1)
|
atomic.AddInt32(&this.sleepTh, -1)
|
||||||
}
|
}
|
||||||
lastErr = this.downloadTsFile(ts, downloadDir, key)
|
lastErr = this.downloadTsFile(ts, downloadDir, encInfo)
|
||||||
if lastErr == nil {
|
if lastErr == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -486,7 +525,7 @@ func (this *downloadEnv) downloader(tsList []TsInfo, downloadDir string, key str
|
||||||
}
|
}
|
||||||
locker.Lock()
|
locker.Lock()
|
||||||
downloadCount++
|
downloadCount++
|
||||||
this.DrawProgressBar(tsLen, downloadCount)
|
this.status.DrawProgressBar(tsLen, downloadCount)
|
||||||
locker.Unlock()
|
locker.Unlock()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -495,24 +534,6 @@ func (this *downloadEnv) downloader(tsList []TsInfo, downloadDir string, key str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 进度条
|
|
||||||
func (this *downloadEnv) DrawProgressBar(total int, current int) {
|
|
||||||
if total == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
proportion := float32(current) / float32(total)
|
|
||||||
|
|
||||||
this.progressLocker.Lock()
|
|
||||||
this.progressPercent = int(proportion * 100)
|
|
||||||
title := this.progressBarTitle
|
|
||||||
if this.progressBarShow {
|
|
||||||
width := 50
|
|
||||||
pos := int(proportion * float32(width))
|
|
||||||
fmt.Printf(title+" %s%*s %6.2f%%\r", strings.Repeat("■", pos), width-pos, "", proportion*100)
|
|
||||||
}
|
|
||||||
this.progressLocker.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFileExists(path string) bool {
|
func isFileExists(path string) bool {
|
||||||
stat, err := os.Stat(path)
|
stat, err := os.Stat(path)
|
||||||
return err == nil && stat.Mode().IsRegular()
|
return err == nil && stat.Mode().IsRegular()
|
||||||
|
|
@ -525,23 +546,29 @@ func isDirExists(path string) bool {
|
||||||
|
|
||||||
// ============================== 加解密相关 ==============================
|
// ============================== 加解密相关 ==============================
|
||||||
|
|
||||||
func PKCS7UnPadding(origData []byte) []byte {
|
func AesDecrypt(seq uint64, crypted []byte, encInfo *EncryptInfo) ([]byte, error) {
|
||||||
length := len(origData)
|
block, err := aes.NewCipher(encInfo.Key)
|
||||||
unpadding := int(origData[length-1])
|
|
||||||
return origData[:(length - unpadding)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func AesDecrypt(crypted, key []byte) ([]byte, error) {
|
|
||||||
block, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
blockSize := block.BlockSize()
|
iv := encInfo.Iv
|
||||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
if len(iv) == 0 {
|
||||||
|
if encInfo.Method == EncryptMethod_AES128 {
|
||||||
|
iv = make([]byte, 16)
|
||||||
|
binary.BigEndian.PutUint64(iv[8:], seq)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("AesDecrypt method not support " + strconv.Quote(encInfo.Method))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||||
origData := make([]byte, len(crypted))
|
origData := make([]byte, len(crypted))
|
||||||
blockMode.CryptBlocks(origData, crypted)
|
blockMode.CryptBlocks(origData, crypted)
|
||||||
origData = PKCS7UnPadding(origData)
|
length := len(origData)
|
||||||
return origData, nil
|
unpadding := int(origData[length-1])
|
||||||
|
if length-unpadding < 0 {
|
||||||
|
return nil, fmt.Errorf(`invalid length of unpadding %v - %v`, length, unpadding)
|
||||||
|
}
|
||||||
|
return origData[:(length - unpadding)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileSha256(targetFile string) (v string) {
|
func getFileSha256(targetFile string) (v string) {
|
||||||
|
|
@ -583,6 +610,11 @@ func (this *downloadEnv) sniffM3u8(urlS string) (afterUrl string, content []byte
|
||||||
containsTs = true
|
containsTs = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// Support fake png ts
|
||||||
|
if UrlHasSuffix(line, ".png") {
|
||||||
|
containsTs = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if containsTs {
|
if containsTs {
|
||||||
return urlS, content, ""
|
return urlS, content, ""
|
||||||
|
|
@ -649,10 +681,30 @@ func (this *downloadEnv) doGetRequest(urlS string) (data []byte, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) GetIsCancel() bool {
|
func (this *downloadEnv) GetIsCancel() bool {
|
||||||
|
return isContextCancel(this.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isContextCancel(ctx context.Context) bool {
|
||||||
select {
|
select {
|
||||||
case <-this.ctx.Done():
|
case <-ctx.Done():
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFileNameFromUrl(u string) string {
|
||||||
|
urlObj, err := url.Parse(u)
|
||||||
|
if err != nil || urlObj.Scheme == "" {
|
||||||
|
return "all"
|
||||||
|
}
|
||||||
|
name := path.Base(urlObj.Path)
|
||||||
|
if name == "" {
|
||||||
|
return "all"
|
||||||
|
}
|
||||||
|
ext := path.Ext(name)
|
||||||
|
if len(name) > len(ext) {
|
||||||
|
return strings.TrimSuffix(name, ext)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package m3u8d
|
package m3u8d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -50,7 +51,7 @@ func TestGetTsList(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tGetTsList(m3u8Url string, m3u8Content string, expectTs0Url string) {
|
func tGetTsList(m3u8Url string, m3u8Content string, expectTs0Url string) {
|
||||||
list, errMsg := getTsList(m3u8Url, m3u8Content)
|
list, errMsg := getTsList(0, m3u8Url, m3u8Content)
|
||||||
if errMsg != "" {
|
if errMsg != "" {
|
||||||
panic(errMsg)
|
panic(errMsg)
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +89,7 @@ func TestFull(t *testing.T) {
|
||||||
M3u8Url: m3u8Url,
|
M3u8Url: m3u8Url,
|
||||||
SaveDir: saveDir,
|
SaveDir: saveDir,
|
||||||
FileName: "all",
|
FileName: "all",
|
||||||
|
ThreadCount: 8,
|
||||||
})
|
})
|
||||||
if resp2.ErrMsg != "" {
|
if resp2.ErrMsg != "" {
|
||||||
panic(resp2.ErrMsg)
|
panic(resp2.ErrMsg)
|
||||||
|
|
@ -100,3 +102,35 @@ func TestFull(t *testing.T) {
|
||||||
panic("state error")
|
panic("state error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetFileName(t *testing.T) {
|
||||||
|
u1 := "https://example.com/video.m3u8"
|
||||||
|
u2 := "https://example.com/video.m3u8?query=1"
|
||||||
|
u3 := "https://example.com/video-name"
|
||||||
|
|
||||||
|
if GetFileNameFromUrl(u1) != "video" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if GetFileNameFromUrl(u2) != "video" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if GetFileNameFromUrl(u3) != "video-name" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseOldEnv(t *testing.T) {
|
||||||
|
encInfo := EncryptInfo{
|
||||||
|
Method: EncryptMethod_AES128,
|
||||||
|
Key: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6},
|
||||||
|
Iv: nil,
|
||||||
|
}
|
||||||
|
before := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 4}
|
||||||
|
after, err := AesDecrypt(1, before, &encInfo)
|
||||||
|
checkErr(err)
|
||||||
|
if bytes.Equal(after, []byte{69, 46, 52, 180, 68, 205, 99, 220, 193, 44, 116, 174, 96, 196, 199, 87, 214, 77, 67, 5, 37, 8, 139, 146, 229, 120, 164, 76, 107, 0, 204, 0}) == false {
|
||||||
|
panic("expect bytes failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ func main() {
|
||||||
BuildCliBinary() // 编译命令行版本
|
BuildCliBinary() // 编译命令行版本
|
||||||
if os.Getenv("GITHUB_ACTIONS") == "" { // 本地编译
|
if os.Getenv("GITHUB_ACTIONS") == "" { // 本地编译
|
||||||
CreateLibForQtUi("amd64-static") // 创建Qt需要使用的.a库文件
|
CreateLibForQtUi("amd64-static") // 创建Qt需要使用的.a库文件
|
||||||
WriteVersionDotRc("1.5.6")
|
WriteVersionDotRc("1.5.20")
|
||||||
} else { // github actions 编译
|
} else { // github actions 编译
|
||||||
if runtime.GOOS == "darwin" { // 编译darwin版本的dmg
|
if runtime.GOOS == "darwin" { // 编译darwin版本的dmg
|
||||||
CreateLibForQtUi("amd64-shared")
|
CreateLibForQtUi("amd64-shared")
|
||||||
|
|
@ -94,6 +94,10 @@ func CreateLibForQtUi(mode string) {
|
||||||
ctx.Generate1(m3u8d.GetWd)
|
ctx.Generate1(m3u8d.GetWd)
|
||||||
ctx.Generate1(m3u8d.ParseCurlStr)
|
ctx.Generate1(m3u8d.ParseCurlStr)
|
||||||
ctx.Generate1(m3u8d.RunDownload_Req_ToCurlStr)
|
ctx.Generate1(m3u8d.RunDownload_Req_ToCurlStr)
|
||||||
|
ctx.Generate1(m3u8d.GetFileNameFromUrl)
|
||||||
|
ctx.Generate1(m3u8d.MergeTsDir)
|
||||||
|
ctx.Generate1(m3u8d.MergeStop)
|
||||||
|
ctx.Generate1(m3u8d.MergeGetProgressPercent)
|
||||||
if mode == "amd64-static" {
|
if mode == "amd64-static" {
|
||||||
ctx.MustCreateAmd64LibraryInDir("m3u8d-qt")
|
ctx.MustCreateAmd64LibraryInDir("m3u8d-qt")
|
||||||
} else if mode == "386-static" {
|
} else if mode == "386-static" {
|
||||||
|
|
|
||||||
30
go.mod
30
go.mod
|
|
@ -6,13 +6,33 @@ require (
|
||||||
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2
|
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2
|
||||||
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07
|
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07
|
||||||
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171
|
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171
|
||||||
github.com/spf13/cobra v1.4.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/yapingcat/gomedia v0.0.0-20220731053213-04782526c441
|
github.com/yapingcat/gomedia v0.0.0-20221023155149-c5f2f0f45ca5
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
|
golang.org/x/text v0.3.3
|
||||||
golang.org/x/text v0.3.7
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/cilium/ebpf v0.12.3 // indirect
|
||||||
|
github.com/cosiner/argv v0.1.0 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||||
|
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d // indirect
|
||||||
|
github.com/go-delve/delve v1.21.2 // indirect
|
||||||
|
github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d // indirect
|
||||||
|
github.com/google/go-dap v0.11.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
go.starlark.net v0.0.0-20231101134539-556fd59b42f6 // indirect
|
||||||
|
golang.org/x/arch v0.6.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
|
golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
77
go.sum
77
go.sum
|
|
@ -1,26 +1,85 @@
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||||
|
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||||
|
github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
|
||||||
|
github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d h1:hUWoLdw5kvo2xCsqlsIBMvWUc1QCSsCYD2J2+Fg6YoU=
|
||||||
|
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d/go.mod h1:C7Es+DLenIpPc9J6IYw4jrK0h7S9bKj4DNl8+KxGEXU=
|
||||||
|
github.com/go-delve/delve v1.21.2 h1:eaS+ziJo+660mi3D2q/VP8RxW5GcF4Y1zyKSi82alsU=
|
||||||
|
github.com/go-delve/delve v1.21.2/go.mod h1:FgTAiRUe43RS5EexL06RPyMtP8AMZVL/t9Qqgy3qUe4=
|
||||||
|
github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d h1:pxjSLshkZJGLVm0wv20f/H0oTWiq/egkoJQ2ja6LEvo=
|
||||||
|
github.com/go-delve/liner v1.2.3-0.20220127212407-d32d89dd2a5d/go.mod h1:biJCRbqp51wS+I92HMqn5H8/A0PAhxn2vyOT+JqhiGI=
|
||||||
|
github.com/google/go-dap v0.11.0 h1:SpAZJL41rOOvd85PuLCCLE1dteTQOyKNnn0H3DBHywo=
|
||||||
|
github.com/google/go-dap v0.11.0/go.mod h1:HAeyoSd2WIfTfg+0GRXcFrb+RnojAtGNh+k+XTIxJDE=
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2 h1:AmGkuxSIOW0gA/jetY8d1DVV0cyQ08FMCxa1arkI6HQ=
|
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2 h1:AmGkuxSIOW0gA/jetY8d1DVV0cyQ08FMCxa1arkI6HQ=
|
||||||
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2/go.mod h1:HMNNdA1LMQFJRwobtCzVevWcInFSo9rfs1fYeVYwU+c=
|
github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2/go.mod h1:HMNNdA1LMQFJRwobtCzVevWcInFSo9rfs1fYeVYwU+c=
|
||||||
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07 h1:nyt0GDiskod5Y9uNVrXgK7PZHeL7Ab1uVc3LhLg7/gk=
|
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07 h1:nyt0GDiskod5Y9uNVrXgK7PZHeL7Ab1uVc3LhLg7/gk=
|
||||||
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07/go.mod h1:1fsOAZftk08/dOTRqlp6f/MVwaEKOhrnPUg0RtWiSdY=
|
github.com/orestonce/go2cpp v0.0.0-20220802140809-b2a921a62a07/go.mod h1:1fsOAZftk08/dOTRqlp6f/MVwaEKOhrnPUg0RtWiSdY=
|
||||||
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171 h1:NnOl6HTrhrlTT7aaAybVOtq+fEztGFMoQtegckgwLdk=
|
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171 h1:NnOl6HTrhrlTT7aaAybVOtq+fEztGFMoQtegckgwLdk=
|
||||||
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171/go.mod h1:MCQUrAPiG9/PTjHJuGqWLasKbIaG6z62KO6kfp90byM=
|
github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171/go.mod h1:MCQUrAPiG9/PTjHJuGqWLasKbIaG6z62KO6kfp90byM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||||
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||||
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
|
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/yapingcat/gomedia v0.0.0-20220731053213-04782526c441 h1:SAohYbjWwtDtpjW9GmV6IFDou2CW4MvUXxJtz3FiuR0=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/yapingcat/gomedia v0.0.0-20220731053213-04782526c441/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=
|
github.com/yapingcat/gomedia v0.0.0-20221023155149-c5f2f0f45ca5 h1:dswmlgf8sSrjaQJCGDpmI14sIkHqyIZdP6Jwzl7gVDY=
|
||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
github.com/yapingcat/gomedia v0.0.0-20221023155149-c5f2f0f45ca5/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
go.starlark.net v0.0.0-20231101134539-556fd59b42f6 h1:+eC0F/k4aBLC4szgOcjd7bDTEnpxADJyWJE0yowgM3E=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
go.starlark.net v0.0.0-20231101134539-556fd59b42f6/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||||
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||||
|
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c h1:3kC/TjQ+xzIblQv39bCOyRk8fbEeJcDHwbyxPUU2BpA=
|
||||||
|
golang.org/x/sys v0.14.1-0.20231108175955-e4099bfacb8c/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package m3u8d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EncryptInfo struct {
|
||||||
|
Method string
|
||||||
|
Key []byte
|
||||||
|
Iv []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.2.4
|
||||||
|
const (
|
||||||
|
EncryptMethod_NONE = `NONE`
|
||||||
|
EncryptMethod_AES128 = `AES-128`
|
||||||
|
EncryptMethod_SIMPLE_AES = `SAMPLE-AES` // TODO
|
||||||
|
)
|
||||||
|
|
||||||
|
type M3u8Content struct {
|
||||||
|
PartList []M3u8Part `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type M3u8Part struct {
|
||||||
|
Tag string `json:",omitempty"`
|
||||||
|
TextFull string `json:",omitempty"`
|
||||||
|
KeyValue map[string]string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info M3u8Content) GetPart(tag string) M3u8Part {
|
||||||
|
for _, one := range info.PartList {
|
||||||
|
if one.Tag == tag {
|
||||||
|
return one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return M3u8Part{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func M3u8Parse(content string) (info M3u8Content) {
|
||||||
|
for _, line := range splitLineWithTrimSpace(content) {
|
||||||
|
if strings.HasPrefix(line, "#") == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmp := strings.SplitN(line, ":", 2)
|
||||||
|
if len(tmp) < 2 {
|
||||||
|
info.PartList = append(info.PartList, M3u8Part{
|
||||||
|
Tag: tmp[0],
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var p M3u8Part
|
||||||
|
p.Tag = tmp[0]
|
||||||
|
p.TextFull = strings.TrimSpace(tmp[1])
|
||||||
|
for _, kv := range strings.Split(tmp[1], ",") {
|
||||||
|
data := strings.SplitN(kv, "=", 2)
|
||||||
|
if len(data) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := data[0]
|
||||||
|
value := strings.TrimSpace(data[1])
|
||||||
|
if strings.HasPrefix(value, "\"") {
|
||||||
|
var err error
|
||||||
|
value, err = strconv.Unquote(value)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.KeyValue == nil {
|
||||||
|
p.KeyValue = map[string]string{}
|
||||||
|
}
|
||||||
|
p.KeyValue[key] = value
|
||||||
|
}
|
||||||
|
info.PartList = append(info.PartList, p)
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package m3u8d
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestM3u8Parse(t *testing.T) {
|
||||||
|
info := M3u8Parse(`#EXTM3U
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-TARGETDURATION:6
|
||||||
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:0
|
||||||
|
#EXT-X-KEY:METHOD=AES-128,URI="/20230502/xthms/2000kb/hls/key.key"
|
||||||
|
`)
|
||||||
|
part := info.GetPart("#EXT-X-KEY")
|
||||||
|
if part.KeyValue["METHOD"] != EncryptMethod_AES128 {
|
||||||
|
panic("method")
|
||||||
|
}
|
||||||
|
if part.KeyValue["URI"] != "/20230502/xthms/2000kb/hls/key.key" {
|
||||||
|
panic("uri")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFileNameFromUrl(t *testing.T) {
|
||||||
|
{
|
||||||
|
part := M3u8Parse(`#EXT-X-KEY:IV=0x10c27a9e3fa363dfe4c44b59b67304b3`).GetPart("#EXT-X-KEY")
|
||||||
|
iv, err := hex.DecodeString(strings.TrimPrefix(part.KeyValue["IV"], "0x"))
|
||||||
|
checkErr(err)
|
||||||
|
if len(iv) != 16 {
|
||||||
|
panic("iv " + strconv.Quote(string(iv)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
part := M3u8Parse(`#EXT-X-KEY:nothing`).GetPart("#EXT-X-KEY")
|
||||||
|
iv, err := hex.DecodeString(strings.TrimPrefix(part.KeyValue["IV"], "0x"))
|
||||||
|
checkErr(err)
|
||||||
|
if len(iv) != 0 {
|
||||||
|
panic("iv " + strconv.Quote(string(iv)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkErr(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestM3u8Parse2(t *testing.T) {
|
||||||
|
seq1 := parseBeginSeq([]byte(`#EXTM3U
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-TARGETDURATION:6
|
||||||
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:0`))
|
||||||
|
if seq1 != 0 {
|
||||||
|
panic(seq1)
|
||||||
|
}
|
||||||
|
seq2 := parseBeginSeq([]byte(`#EXTM3U
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-TARGETDURATION:6
|
||||||
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:2`))
|
||||||
|
if seq2 != 2 {
|
||||||
|
panic(seq2)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#line 1 "cgo-builtin-export-prolog"
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
#include <stddef.h> /* for ptrdiff_t below */
|
#include <stddef.h>
|
||||||
|
|
||||||
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
#define GO_CGO_EXPORT_PROLOGUE_H
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
@ -41,11 +41,17 @@ typedef long long GoInt64;
|
||||||
typedef unsigned long long GoUint64;
|
typedef unsigned long long GoUint64;
|
||||||
typedef GoInt64 GoInt;
|
typedef GoInt64 GoInt;
|
||||||
typedef GoUint64 GoUint;
|
typedef GoUint64 GoUint;
|
||||||
typedef __SIZE_TYPE__ GoUintptr;
|
typedef size_t GoUintptr;
|
||||||
typedef float GoFloat32;
|
typedef float GoFloat32;
|
||||||
typedef double GoFloat64;
|
typedef double GoFloat64;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <complex.h>
|
||||||
|
typedef _Fcomplex GoComplex64;
|
||||||
|
typedef _Dcomplex GoComplex128;
|
||||||
|
#else
|
||||||
typedef float _Complex GoComplex64;
|
typedef float _Complex GoComplex64;
|
||||||
typedef double _Complex GoComplex128;
|
typedef double _Complex GoComplex128;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static assertion to make sure the file is being used on architecture
|
static assertion to make sure the file is being used on architecture
|
||||||
|
|
@ -75,6 +81,10 @@ extern __declspec(dllexport) void Go2cppFn_GetProgress(char* in, int inLen, char
|
||||||
extern __declspec(dllexport) void Go2cppFn_GetWd(char* in, int inLen, char** out, int* outLen);
|
extern __declspec(dllexport) void Go2cppFn_GetWd(char* in, int inLen, char** out, int* outLen);
|
||||||
extern __declspec(dllexport) void Go2cppFn_ParseCurlStr(char* in, int inLen, char** out, int* outLen);
|
extern __declspec(dllexport) void Go2cppFn_ParseCurlStr(char* in, int inLen, char** out, int* outLen);
|
||||||
extern __declspec(dllexport) void Go2cppFn_RunDownload_Req_ToCurlStr(char* in, int inLen, char** out, int* outLen);
|
extern __declspec(dllexport) void Go2cppFn_RunDownload_Req_ToCurlStr(char* in, int inLen, char** out, int* outLen);
|
||||||
|
extern __declspec(dllexport) void Go2cppFn_GetFileNameFromUrl(char* in, int inLen, char** out, int* outLen);
|
||||||
|
extern __declspec(dllexport) void Go2cppFn_MergeTsDir(char* in, int inLen, char** out, int* outLen);
|
||||||
|
extern __declspec(dllexport) void Go2cppFn_MergeStop(char* in, int inLen, char** out, int* outLen);
|
||||||
|
extern __declspec(dllexport) void Go2cppFn_MergeGetProgressPercent(char* in, int inLen, char** out, int* outLen);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
@ -85,97 +95,106 @@ RunDownload_Resp RunDownload(RunDownload_Req in0){
|
||||||
std::string in;
|
std::string in;
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
uint32_t tmp34 = in0.M3u8Url.length();
|
uint32_t tmp36 = in0.M3u8Url.length();
|
||||||
char tmp35[4];
|
|
||||||
tmp35[0] = (uint32_t(tmp34) >> 24) & 0xFF;
|
|
||||||
tmp35[1] = (uint32_t(tmp34) >> 16) & 0xFF;
|
|
||||||
tmp35[2] = (uint32_t(tmp34) >> 8) & 0xFF;
|
|
||||||
tmp35[3] = (uint32_t(tmp34) >> 0) & 0xFF;
|
|
||||||
in.append(tmp35, 4);
|
|
||||||
in.append(in0.M3u8Url);
|
|
||||||
}
|
|
||||||
in.append((char*)(&in0.Insecure), 1);
|
|
||||||
{
|
|
||||||
uint32_t tmp36 = in0.SaveDir.length();
|
|
||||||
char tmp37[4];
|
char tmp37[4];
|
||||||
tmp37[0] = (uint32_t(tmp36) >> 24) & 0xFF;
|
tmp37[0] = (uint32_t(tmp36) >> 24) & 0xFF;
|
||||||
tmp37[1] = (uint32_t(tmp36) >> 16) & 0xFF;
|
tmp37[1] = (uint32_t(tmp36) >> 16) & 0xFF;
|
||||||
tmp37[2] = (uint32_t(tmp36) >> 8) & 0xFF;
|
tmp37[2] = (uint32_t(tmp36) >> 8) & 0xFF;
|
||||||
tmp37[3] = (uint32_t(tmp36) >> 0) & 0xFF;
|
tmp37[3] = (uint32_t(tmp36) >> 0) & 0xFF;
|
||||||
in.append(tmp37, 4);
|
in.append(tmp37, 4);
|
||||||
in.append(in0.SaveDir);
|
in.append(in0.M3u8Url);
|
||||||
}
|
}
|
||||||
|
in.append((char*)(&in0.Insecure), 1);
|
||||||
{
|
{
|
||||||
uint32_t tmp38 = in0.FileName.length();
|
uint32_t tmp38 = in0.SaveDir.length();
|
||||||
char tmp39[4];
|
char tmp39[4];
|
||||||
tmp39[0] = (uint32_t(tmp38) >> 24) & 0xFF;
|
tmp39[0] = (uint32_t(tmp38) >> 24) & 0xFF;
|
||||||
tmp39[1] = (uint32_t(tmp38) >> 16) & 0xFF;
|
tmp39[1] = (uint32_t(tmp38) >> 16) & 0xFF;
|
||||||
tmp39[2] = (uint32_t(tmp38) >> 8) & 0xFF;
|
tmp39[2] = (uint32_t(tmp38) >> 8) & 0xFF;
|
||||||
tmp39[3] = (uint32_t(tmp38) >> 0) & 0xFF;
|
tmp39[3] = (uint32_t(tmp38) >> 0) & 0xFF;
|
||||||
in.append(tmp39, 4);
|
in.append(tmp39, 4);
|
||||||
|
in.append(in0.SaveDir);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t tmp40 = in0.FileName.length();
|
||||||
|
char tmp41[4];
|
||||||
|
tmp41[0] = (uint32_t(tmp40) >> 24) & 0xFF;
|
||||||
|
tmp41[1] = (uint32_t(tmp40) >> 16) & 0xFF;
|
||||||
|
tmp41[2] = (uint32_t(tmp40) >> 8) & 0xFF;
|
||||||
|
tmp41[3] = (uint32_t(tmp40) >> 0) & 0xFF;
|
||||||
|
in.append(tmp41, 4);
|
||||||
in.append(in0.FileName);
|
in.append(in0.FileName);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
char tmp40[4];
|
|
||||||
tmp40[0] = (uint32_t(in0.SkipTsCountFromHead) >> 24) & 0xFF;
|
|
||||||
tmp40[1] = (uint32_t(in0.SkipTsCountFromHead) >> 16) & 0xFF;
|
|
||||||
tmp40[2] = (uint32_t(in0.SkipTsCountFromHead) >> 8) & 0xFF;
|
|
||||||
tmp40[3] = (uint32_t(in0.SkipTsCountFromHead) >> 0) & 0xFF;
|
|
||||||
in.append(tmp40, 4);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
uint32_t tmp41 = in0.SetProxy.length();
|
|
||||||
char tmp42[4];
|
char tmp42[4];
|
||||||
tmp42[0] = (uint32_t(tmp41) >> 24) & 0xFF;
|
tmp42[0] = (uint32_t(in0.SkipTsCountFromHead) >> 24) & 0xFF;
|
||||||
tmp42[1] = (uint32_t(tmp41) >> 16) & 0xFF;
|
tmp42[1] = (uint32_t(in0.SkipTsCountFromHead) >> 16) & 0xFF;
|
||||||
tmp42[2] = (uint32_t(tmp41) >> 8) & 0xFF;
|
tmp42[2] = (uint32_t(in0.SkipTsCountFromHead) >> 8) & 0xFF;
|
||||||
tmp42[3] = (uint32_t(tmp41) >> 0) & 0xFF;
|
tmp42[3] = (uint32_t(in0.SkipTsCountFromHead) >> 0) & 0xFF;
|
||||||
in.append(tmp42, 4);
|
in.append(tmp42, 4);
|
||||||
in.append(in0.SetProxy);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp43 = in0.HeaderMap.size();
|
uint32_t tmp43 = in0.SetProxy.length();
|
||||||
char tmp44[4];
|
char tmp44[4];
|
||||||
tmp44[0] = (uint32_t(tmp43) >> 24) & 0xFF;
|
tmp44[0] = (uint32_t(tmp43) >> 24) & 0xFF;
|
||||||
tmp44[1] = (uint32_t(tmp43) >> 16) & 0xFF;
|
tmp44[1] = (uint32_t(tmp43) >> 16) & 0xFF;
|
||||||
tmp44[2] = (uint32_t(tmp43) >> 8) & 0xFF;
|
tmp44[2] = (uint32_t(tmp43) >> 8) & 0xFF;
|
||||||
tmp44[3] = (uint32_t(tmp43) >> 0) & 0xFF;
|
tmp44[3] = (uint32_t(tmp43) >> 0) & 0xFF;
|
||||||
in.append(tmp44, 4);
|
in.append(tmp44, 4);
|
||||||
for(std::map<std::string, std::vector<std::string>>::iterator tmp45 = in0.HeaderMap.begin(); tmp45 != in0.HeaderMap.end(); ++tmp45) {
|
in.append(in0.SetProxy);
|
||||||
{
|
|
||||||
uint32_t tmp46 = tmp45->first.length();
|
|
||||||
char tmp47[4];
|
|
||||||
tmp47[0] = (uint32_t(tmp46) >> 24) & 0xFF;
|
|
||||||
tmp47[1] = (uint32_t(tmp46) >> 16) & 0xFF;
|
|
||||||
tmp47[2] = (uint32_t(tmp46) >> 8) & 0xFF;
|
|
||||||
tmp47[3] = (uint32_t(tmp46) >> 0) & 0xFF;
|
|
||||||
in.append(tmp47, 4);
|
|
||||||
in.append(tmp45->first);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp48 = tmp45->second.size();
|
uint32_t tmp45 = in0.HeaderMap.size();
|
||||||
|
char tmp46[4];
|
||||||
|
tmp46[0] = (uint32_t(tmp45) >> 24) & 0xFF;
|
||||||
|
tmp46[1] = (uint32_t(tmp45) >> 16) & 0xFF;
|
||||||
|
tmp46[2] = (uint32_t(tmp45) >> 8) & 0xFF;
|
||||||
|
tmp46[3] = (uint32_t(tmp45) >> 0) & 0xFF;
|
||||||
|
in.append(tmp46, 4);
|
||||||
|
for(std::map<std::string, std::vector<std::string>>::iterator tmp47 = in0.HeaderMap.begin(); tmp47 != in0.HeaderMap.end(); ++tmp47) {
|
||||||
|
{
|
||||||
|
uint32_t tmp48 = tmp47->first.length();
|
||||||
char tmp49[4];
|
char tmp49[4];
|
||||||
tmp49[0] = (uint32_t(tmp48) >> 24) & 0xFF;
|
tmp49[0] = (uint32_t(tmp48) >> 24) & 0xFF;
|
||||||
tmp49[1] = (uint32_t(tmp48) >> 16) & 0xFF;
|
tmp49[1] = (uint32_t(tmp48) >> 16) & 0xFF;
|
||||||
tmp49[2] = (uint32_t(tmp48) >> 8) & 0xFF;
|
tmp49[2] = (uint32_t(tmp48) >> 8) & 0xFF;
|
||||||
tmp49[3] = (uint32_t(tmp48) >> 0) & 0xFF;
|
tmp49[3] = (uint32_t(tmp48) >> 0) & 0xFF;
|
||||||
in.append(tmp49, 4);
|
in.append(tmp49, 4);
|
||||||
for (uint32_t tmp50=0; tmp50 < tmp48; ++tmp50) {
|
in.append(tmp47->first);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp51 = tmp45->second[tmp50].length();
|
uint32_t tmp50 = tmp47->second.size();
|
||||||
char tmp52[4];
|
char tmp51[4];
|
||||||
tmp52[0] = (uint32_t(tmp51) >> 24) & 0xFF;
|
tmp51[0] = (uint32_t(tmp50) >> 24) & 0xFF;
|
||||||
tmp52[1] = (uint32_t(tmp51) >> 16) & 0xFF;
|
tmp51[1] = (uint32_t(tmp50) >> 16) & 0xFF;
|
||||||
tmp52[2] = (uint32_t(tmp51) >> 8) & 0xFF;
|
tmp51[2] = (uint32_t(tmp50) >> 8) & 0xFF;
|
||||||
tmp52[3] = (uint32_t(tmp51) >> 0) & 0xFF;
|
tmp51[3] = (uint32_t(tmp50) >> 0) & 0xFF;
|
||||||
in.append(tmp52, 4);
|
in.append(tmp51, 4);
|
||||||
in.append(tmp45->second[tmp50]);
|
for (uint32_t tmp52=0; tmp52 < tmp50; ++tmp52) {
|
||||||
|
{
|
||||||
|
uint32_t tmp53 = tmp47->second[tmp52].length();
|
||||||
|
char tmp54[4];
|
||||||
|
tmp54[0] = (uint32_t(tmp53) >> 24) & 0xFF;
|
||||||
|
tmp54[1] = (uint32_t(tmp53) >> 16) & 0xFF;
|
||||||
|
tmp54[2] = (uint32_t(tmp53) >> 8) & 0xFF;
|
||||||
|
tmp54[3] = (uint32_t(tmp53) >> 0) & 0xFF;
|
||||||
|
in.append(tmp54, 4);
|
||||||
|
in.append(tmp47->second[tmp52]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.append((char*)(&in0.SkipRemoveTs), 1);
|
in.append((char*)(&in0.SkipRemoveTs), 1);
|
||||||
|
in.append((char*)(&in0.ProgressBarShow), 1);
|
||||||
|
{
|
||||||
|
char tmp55[4];
|
||||||
|
tmp55[0] = (uint32_t(in0.ThreadCount) >> 24) & 0xFF;
|
||||||
|
tmp55[1] = (uint32_t(in0.ThreadCount) >> 16) & 0xFF;
|
||||||
|
tmp55[2] = (uint32_t(in0.ThreadCount) >> 8) & 0xFF;
|
||||||
|
tmp55[3] = (uint32_t(in0.ThreadCount) >> 0) & 0xFF;
|
||||||
|
in.append(tmp55, 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
char *out = NULL;
|
char *out = NULL;
|
||||||
int outLen = 0;
|
int outLen = 0;
|
||||||
|
|
@ -184,30 +203,30 @@ RunDownload_Resp RunDownload(RunDownload_Req in0){
|
||||||
int outIdx = 0;
|
int outIdx = 0;
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
uint32_t tmp53 = 0;
|
uint32_t tmp56 = 0;
|
||||||
uint32_t tmp54 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp57 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp55 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp58 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp56 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp59 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp57 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp60 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp53 = tmp54 | tmp55 | tmp56 | tmp57;
|
tmp56 = tmp57 | tmp58 | tmp59 | tmp60;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.ErrMsg = std::string(out+outIdx, out+outIdx+tmp53);
|
retValue.ErrMsg = std::string(out+outIdx, out+outIdx+tmp56);
|
||||||
outIdx+=tmp53;
|
outIdx+=tmp56;
|
||||||
}
|
}
|
||||||
retValue.IsSkipped = (bool) out[outIdx];
|
retValue.IsSkipped = (bool) out[outIdx];
|
||||||
outIdx++;
|
outIdx++;
|
||||||
retValue.IsCancel = (bool) out[outIdx];
|
retValue.IsCancel = (bool) out[outIdx];
|
||||||
outIdx++;
|
outIdx++;
|
||||||
{
|
{
|
||||||
uint32_t tmp58 = 0;
|
uint32_t tmp61 = 0;
|
||||||
uint32_t tmp59 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp62 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp60 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp63 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp61 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp64 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp62 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp65 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp58 = tmp59 | tmp60 | tmp61 | tmp62;
|
tmp61 = tmp62 | tmp63 | tmp64 | tmp65;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.SaveFileTo = std::string(out+outIdx, out+outIdx+tmp58);
|
retValue.SaveFileTo = std::string(out+outIdx, out+outIdx+tmp61);
|
||||||
outIdx+=tmp58;
|
outIdx+=tmp61;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
|
@ -298,13 +317,13 @@ std::string GetWd(){
|
||||||
ParseCurl_Resp ParseCurlStr(std::string in0){
|
ParseCurl_Resp ParseCurlStr(std::string in0){
|
||||||
std::string in;
|
std::string in;
|
||||||
{
|
{
|
||||||
uint32_t tmp19 = in0.length();
|
uint32_t tmp21 = in0.length();
|
||||||
char tmp20[4];
|
char tmp22[4];
|
||||||
tmp20[0] = (uint32_t(tmp19) >> 24) & 0xFF;
|
tmp22[0] = (uint32_t(tmp21) >> 24) & 0xFF;
|
||||||
tmp20[1] = (uint32_t(tmp19) >> 16) & 0xFF;
|
tmp22[1] = (uint32_t(tmp21) >> 16) & 0xFF;
|
||||||
tmp20[2] = (uint32_t(tmp19) >> 8) & 0xFF;
|
tmp22[2] = (uint32_t(tmp21) >> 8) & 0xFF;
|
||||||
tmp20[3] = (uint32_t(tmp19) >> 0) & 0xFF;
|
tmp22[3] = (uint32_t(tmp21) >> 0) & 0xFF;
|
||||||
in.append(tmp20, 4);
|
in.append(tmp22, 4);
|
||||||
in.append(in0);
|
in.append(in0);
|
||||||
}
|
}
|
||||||
char *out = NULL;
|
char *out = NULL;
|
||||||
|
|
@ -314,122 +333,132 @@ ParseCurl_Resp ParseCurlStr(std::string in0){
|
||||||
int outIdx = 0;
|
int outIdx = 0;
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
uint32_t tmp21 = 0;
|
uint32_t tmp23 = 0;
|
||||||
uint32_t tmp22 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp24 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp23 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp25 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp24 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp26 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp25 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp27 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp21 = tmp22 | tmp23 | tmp24 | tmp25;
|
tmp23 = tmp24 | tmp25 | tmp26 | tmp27;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.ErrMsg = std::string(out+outIdx, out+outIdx+tmp21);
|
retValue.ErrMsg = std::string(out+outIdx, out+outIdx+tmp23);
|
||||||
outIdx+=tmp21;
|
outIdx+=tmp23;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
uint32_t tmp26 = 0;
|
uint32_t tmp28 = 0;
|
||||||
uint32_t tmp27 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp29 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp28 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp30 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp29 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp31 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp30 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp32 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp26 = tmp27 | tmp28 | tmp29 | tmp30;
|
tmp28 = tmp29 | tmp30 | tmp31 | tmp32;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.DownloadReq.M3u8Url = std::string(out+outIdx, out+outIdx+tmp26);
|
retValue.DownloadReq.M3u8Url = std::string(out+outIdx, out+outIdx+tmp28);
|
||||||
outIdx+=tmp26;
|
outIdx+=tmp28;
|
||||||
}
|
}
|
||||||
retValue.DownloadReq.Insecure = (bool) out[outIdx];
|
retValue.DownloadReq.Insecure = (bool) out[outIdx];
|
||||||
outIdx++;
|
outIdx++;
|
||||||
{
|
{
|
||||||
uint32_t tmp31 = 0;
|
uint32_t tmp33 = 0;
|
||||||
uint32_t tmp32 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp34 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp33 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp35 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp34 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp36 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp35 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp37 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp31 = tmp32 | tmp33 | tmp34 | tmp35;
|
tmp33 = tmp34 | tmp35 | tmp36 | tmp37;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.DownloadReq.SaveDir = std::string(out+outIdx, out+outIdx+tmp31);
|
retValue.DownloadReq.SaveDir = std::string(out+outIdx, out+outIdx+tmp33);
|
||||||
outIdx+=tmp31;
|
outIdx+=tmp33;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp36 = 0;
|
uint32_t tmp38 = 0;
|
||||||
uint32_t tmp37 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp39 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp38 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp40 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp39 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp41 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp40 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp42 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp36 = tmp37 | tmp38 | tmp39 | tmp40;
|
tmp38 = tmp39 | tmp40 | tmp41 | tmp42;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.DownloadReq.FileName = std::string(out+outIdx, out+outIdx+tmp36);
|
retValue.DownloadReq.FileName = std::string(out+outIdx, out+outIdx+tmp38);
|
||||||
outIdx+=tmp36;
|
outIdx+=tmp38;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp41 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp43 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp42 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp44 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp43 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp45 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp44 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp46 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
retValue.DownloadReq.SkipTsCountFromHead = tmp41 | tmp42 | tmp43 | tmp44;
|
retValue.DownloadReq.SkipTsCountFromHead = tmp43 | tmp44 | tmp45 | tmp46;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp45 = 0;
|
uint32_t tmp47 = 0;
|
||||||
uint32_t tmp46 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp48 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp47 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp49 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp48 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp50 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp49 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp51 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp45 = tmp46 | tmp47 | tmp48 | tmp49;
|
tmp47 = tmp48 | tmp49 | tmp50 | tmp51;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue.DownloadReq.SetProxy = std::string(out+outIdx, out+outIdx+tmp45);
|
retValue.DownloadReq.SetProxy = std::string(out+outIdx, out+outIdx+tmp47);
|
||||||
outIdx+=tmp45;
|
outIdx+=tmp47;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp50 = 0;
|
uint32_t tmp52 = 0;
|
||||||
uint32_t tmp51 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp53 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp52 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp54 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp53 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp55 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp54 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp56 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp50 = tmp51 | tmp52 | tmp53 | tmp54;
|
tmp52 = tmp53 | tmp54 | tmp55 | tmp56;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
for (uint32_t tmp55 = 0; tmp55 < tmp50; tmp55++) {
|
for (uint32_t tmp57 = 0; tmp57 < tmp52; tmp57++) {
|
||||||
std::string tmp56;
|
std::string tmp58;
|
||||||
{
|
{
|
||||||
uint32_t tmp57 = 0;
|
uint32_t tmp59 = 0;
|
||||||
uint32_t tmp58 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp60 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp59 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp61 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp60 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp62 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp61 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp63 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp57 = tmp58 | tmp59 | tmp60 | tmp61;
|
tmp59 = tmp60 | tmp61 | tmp62 | tmp63;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
tmp56 = std::string(out+outIdx, out+outIdx+tmp57);
|
tmp58 = std::string(out+outIdx, out+outIdx+tmp59);
|
||||||
outIdx+=tmp57;
|
outIdx+=tmp59;
|
||||||
}
|
}
|
||||||
std::vector<std::string> tmp62;
|
std::vector<std::string> tmp64;
|
||||||
{
|
{
|
||||||
uint32_t tmp63 = 0;
|
uint32_t tmp65 = 0;
|
||||||
uint32_t tmp64 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp66 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp65 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp67 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp66 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp68 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp67 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp69 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp63 = tmp64 | tmp65 | tmp66 | tmp67;
|
tmp65 = tmp66 | tmp67 | tmp68 | tmp69;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
for (uint32_t tmp68 = 0; tmp68 < tmp63; tmp68++) {
|
for (uint32_t tmp70 = 0; tmp70 < tmp65; tmp70++) {
|
||||||
std::string tmp69;
|
std::string tmp71;
|
||||||
{
|
{
|
||||||
uint32_t tmp70 = 0;
|
uint32_t tmp72 = 0;
|
||||||
uint32_t tmp71 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp73 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp72 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp74 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp73 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp75 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp74 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp76 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp70 = tmp71 | tmp72 | tmp73 | tmp74;
|
tmp72 = tmp73 | tmp74 | tmp75 | tmp76;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
tmp69 = std::string(out+outIdx, out+outIdx+tmp70);
|
tmp71 = std::string(out+outIdx, out+outIdx+tmp72);
|
||||||
outIdx+=tmp70;
|
outIdx+=tmp72;
|
||||||
}
|
}
|
||||||
tmp62.push_back(tmp69);
|
tmp64.push_back(tmp71);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retValue.DownloadReq.HeaderMap[tmp56] = tmp62;
|
retValue.DownloadReq.HeaderMap[tmp58] = tmp64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retValue.DownloadReq.SkipRemoveTs = (bool) out[outIdx];
|
retValue.DownloadReq.SkipRemoveTs = (bool) out[outIdx];
|
||||||
outIdx++;
|
outIdx++;
|
||||||
|
retValue.DownloadReq.ProgressBarShow = (bool) out[outIdx];
|
||||||
|
outIdx++;
|
||||||
|
{
|
||||||
|
uint32_t tmp77 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
|
uint32_t tmp78 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
|
uint32_t tmp79 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
|
uint32_t tmp80 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
|
retValue.DownloadReq.ThreadCount = tmp77 | tmp78 | tmp79 | tmp80;
|
||||||
|
outIdx+=4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
|
@ -442,97 +471,106 @@ std::string RunDownload_Req_ToCurlStr(RunDownload_Req in0){
|
||||||
std::string in;
|
std::string in;
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
uint32_t tmp31 = in0.M3u8Url.length();
|
uint32_t tmp33 = in0.M3u8Url.length();
|
||||||
char tmp32[4];
|
|
||||||
tmp32[0] = (uint32_t(tmp31) >> 24) & 0xFF;
|
|
||||||
tmp32[1] = (uint32_t(tmp31) >> 16) & 0xFF;
|
|
||||||
tmp32[2] = (uint32_t(tmp31) >> 8) & 0xFF;
|
|
||||||
tmp32[3] = (uint32_t(tmp31) >> 0) & 0xFF;
|
|
||||||
in.append(tmp32, 4);
|
|
||||||
in.append(in0.M3u8Url);
|
|
||||||
}
|
|
||||||
in.append((char*)(&in0.Insecure), 1);
|
|
||||||
{
|
|
||||||
uint32_t tmp33 = in0.SaveDir.length();
|
|
||||||
char tmp34[4];
|
char tmp34[4];
|
||||||
tmp34[0] = (uint32_t(tmp33) >> 24) & 0xFF;
|
tmp34[0] = (uint32_t(tmp33) >> 24) & 0xFF;
|
||||||
tmp34[1] = (uint32_t(tmp33) >> 16) & 0xFF;
|
tmp34[1] = (uint32_t(tmp33) >> 16) & 0xFF;
|
||||||
tmp34[2] = (uint32_t(tmp33) >> 8) & 0xFF;
|
tmp34[2] = (uint32_t(tmp33) >> 8) & 0xFF;
|
||||||
tmp34[3] = (uint32_t(tmp33) >> 0) & 0xFF;
|
tmp34[3] = (uint32_t(tmp33) >> 0) & 0xFF;
|
||||||
in.append(tmp34, 4);
|
in.append(tmp34, 4);
|
||||||
in.append(in0.SaveDir);
|
in.append(in0.M3u8Url);
|
||||||
}
|
}
|
||||||
|
in.append((char*)(&in0.Insecure), 1);
|
||||||
{
|
{
|
||||||
uint32_t tmp35 = in0.FileName.length();
|
uint32_t tmp35 = in0.SaveDir.length();
|
||||||
char tmp36[4];
|
char tmp36[4];
|
||||||
tmp36[0] = (uint32_t(tmp35) >> 24) & 0xFF;
|
tmp36[0] = (uint32_t(tmp35) >> 24) & 0xFF;
|
||||||
tmp36[1] = (uint32_t(tmp35) >> 16) & 0xFF;
|
tmp36[1] = (uint32_t(tmp35) >> 16) & 0xFF;
|
||||||
tmp36[2] = (uint32_t(tmp35) >> 8) & 0xFF;
|
tmp36[2] = (uint32_t(tmp35) >> 8) & 0xFF;
|
||||||
tmp36[3] = (uint32_t(tmp35) >> 0) & 0xFF;
|
tmp36[3] = (uint32_t(tmp35) >> 0) & 0xFF;
|
||||||
in.append(tmp36, 4);
|
in.append(tmp36, 4);
|
||||||
|
in.append(in0.SaveDir);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t tmp37 = in0.FileName.length();
|
||||||
|
char tmp38[4];
|
||||||
|
tmp38[0] = (uint32_t(tmp37) >> 24) & 0xFF;
|
||||||
|
tmp38[1] = (uint32_t(tmp37) >> 16) & 0xFF;
|
||||||
|
tmp38[2] = (uint32_t(tmp37) >> 8) & 0xFF;
|
||||||
|
tmp38[3] = (uint32_t(tmp37) >> 0) & 0xFF;
|
||||||
|
in.append(tmp38, 4);
|
||||||
in.append(in0.FileName);
|
in.append(in0.FileName);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
char tmp37[4];
|
|
||||||
tmp37[0] = (uint32_t(in0.SkipTsCountFromHead) >> 24) & 0xFF;
|
|
||||||
tmp37[1] = (uint32_t(in0.SkipTsCountFromHead) >> 16) & 0xFF;
|
|
||||||
tmp37[2] = (uint32_t(in0.SkipTsCountFromHead) >> 8) & 0xFF;
|
|
||||||
tmp37[3] = (uint32_t(in0.SkipTsCountFromHead) >> 0) & 0xFF;
|
|
||||||
in.append(tmp37, 4);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
uint32_t tmp38 = in0.SetProxy.length();
|
|
||||||
char tmp39[4];
|
char tmp39[4];
|
||||||
tmp39[0] = (uint32_t(tmp38) >> 24) & 0xFF;
|
tmp39[0] = (uint32_t(in0.SkipTsCountFromHead) >> 24) & 0xFF;
|
||||||
tmp39[1] = (uint32_t(tmp38) >> 16) & 0xFF;
|
tmp39[1] = (uint32_t(in0.SkipTsCountFromHead) >> 16) & 0xFF;
|
||||||
tmp39[2] = (uint32_t(tmp38) >> 8) & 0xFF;
|
tmp39[2] = (uint32_t(in0.SkipTsCountFromHead) >> 8) & 0xFF;
|
||||||
tmp39[3] = (uint32_t(tmp38) >> 0) & 0xFF;
|
tmp39[3] = (uint32_t(in0.SkipTsCountFromHead) >> 0) & 0xFF;
|
||||||
in.append(tmp39, 4);
|
in.append(tmp39, 4);
|
||||||
in.append(in0.SetProxy);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp40 = in0.HeaderMap.size();
|
uint32_t tmp40 = in0.SetProxy.length();
|
||||||
char tmp41[4];
|
char tmp41[4];
|
||||||
tmp41[0] = (uint32_t(tmp40) >> 24) & 0xFF;
|
tmp41[0] = (uint32_t(tmp40) >> 24) & 0xFF;
|
||||||
tmp41[1] = (uint32_t(tmp40) >> 16) & 0xFF;
|
tmp41[1] = (uint32_t(tmp40) >> 16) & 0xFF;
|
||||||
tmp41[2] = (uint32_t(tmp40) >> 8) & 0xFF;
|
tmp41[2] = (uint32_t(tmp40) >> 8) & 0xFF;
|
||||||
tmp41[3] = (uint32_t(tmp40) >> 0) & 0xFF;
|
tmp41[3] = (uint32_t(tmp40) >> 0) & 0xFF;
|
||||||
in.append(tmp41, 4);
|
in.append(tmp41, 4);
|
||||||
for(std::map<std::string, std::vector<std::string>>::iterator tmp42 = in0.HeaderMap.begin(); tmp42 != in0.HeaderMap.end(); ++tmp42) {
|
in.append(in0.SetProxy);
|
||||||
{
|
|
||||||
uint32_t tmp43 = tmp42->first.length();
|
|
||||||
char tmp44[4];
|
|
||||||
tmp44[0] = (uint32_t(tmp43) >> 24) & 0xFF;
|
|
||||||
tmp44[1] = (uint32_t(tmp43) >> 16) & 0xFF;
|
|
||||||
tmp44[2] = (uint32_t(tmp43) >> 8) & 0xFF;
|
|
||||||
tmp44[3] = (uint32_t(tmp43) >> 0) & 0xFF;
|
|
||||||
in.append(tmp44, 4);
|
|
||||||
in.append(tmp42->first);
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp45 = tmp42->second.size();
|
uint32_t tmp42 = in0.HeaderMap.size();
|
||||||
|
char tmp43[4];
|
||||||
|
tmp43[0] = (uint32_t(tmp42) >> 24) & 0xFF;
|
||||||
|
tmp43[1] = (uint32_t(tmp42) >> 16) & 0xFF;
|
||||||
|
tmp43[2] = (uint32_t(tmp42) >> 8) & 0xFF;
|
||||||
|
tmp43[3] = (uint32_t(tmp42) >> 0) & 0xFF;
|
||||||
|
in.append(tmp43, 4);
|
||||||
|
for(std::map<std::string, std::vector<std::string>>::iterator tmp44 = in0.HeaderMap.begin(); tmp44 != in0.HeaderMap.end(); ++tmp44) {
|
||||||
|
{
|
||||||
|
uint32_t tmp45 = tmp44->first.length();
|
||||||
char tmp46[4];
|
char tmp46[4];
|
||||||
tmp46[0] = (uint32_t(tmp45) >> 24) & 0xFF;
|
tmp46[0] = (uint32_t(tmp45) >> 24) & 0xFF;
|
||||||
tmp46[1] = (uint32_t(tmp45) >> 16) & 0xFF;
|
tmp46[1] = (uint32_t(tmp45) >> 16) & 0xFF;
|
||||||
tmp46[2] = (uint32_t(tmp45) >> 8) & 0xFF;
|
tmp46[2] = (uint32_t(tmp45) >> 8) & 0xFF;
|
||||||
tmp46[3] = (uint32_t(tmp45) >> 0) & 0xFF;
|
tmp46[3] = (uint32_t(tmp45) >> 0) & 0xFF;
|
||||||
in.append(tmp46, 4);
|
in.append(tmp46, 4);
|
||||||
for (uint32_t tmp47=0; tmp47 < tmp45; ++tmp47) {
|
in.append(tmp44->first);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
uint32_t tmp48 = tmp42->second[tmp47].length();
|
uint32_t tmp47 = tmp44->second.size();
|
||||||
char tmp49[4];
|
char tmp48[4];
|
||||||
tmp49[0] = (uint32_t(tmp48) >> 24) & 0xFF;
|
tmp48[0] = (uint32_t(tmp47) >> 24) & 0xFF;
|
||||||
tmp49[1] = (uint32_t(tmp48) >> 16) & 0xFF;
|
tmp48[1] = (uint32_t(tmp47) >> 16) & 0xFF;
|
||||||
tmp49[2] = (uint32_t(tmp48) >> 8) & 0xFF;
|
tmp48[2] = (uint32_t(tmp47) >> 8) & 0xFF;
|
||||||
tmp49[3] = (uint32_t(tmp48) >> 0) & 0xFF;
|
tmp48[3] = (uint32_t(tmp47) >> 0) & 0xFF;
|
||||||
in.append(tmp49, 4);
|
in.append(tmp48, 4);
|
||||||
in.append(tmp42->second[tmp47]);
|
for (uint32_t tmp49=0; tmp49 < tmp47; ++tmp49) {
|
||||||
|
{
|
||||||
|
uint32_t tmp50 = tmp44->second[tmp49].length();
|
||||||
|
char tmp51[4];
|
||||||
|
tmp51[0] = (uint32_t(tmp50) >> 24) & 0xFF;
|
||||||
|
tmp51[1] = (uint32_t(tmp50) >> 16) & 0xFF;
|
||||||
|
tmp51[2] = (uint32_t(tmp50) >> 8) & 0xFF;
|
||||||
|
tmp51[3] = (uint32_t(tmp50) >> 0) & 0xFF;
|
||||||
|
in.append(tmp51, 4);
|
||||||
|
in.append(tmp44->second[tmp49]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.append((char*)(&in0.SkipRemoveTs), 1);
|
in.append((char*)(&in0.SkipRemoveTs), 1);
|
||||||
|
in.append((char*)(&in0.ProgressBarShow), 1);
|
||||||
|
{
|
||||||
|
char tmp52[4];
|
||||||
|
tmp52[0] = (uint32_t(in0.ThreadCount) >> 24) & 0xFF;
|
||||||
|
tmp52[1] = (uint32_t(in0.ThreadCount) >> 16) & 0xFF;
|
||||||
|
tmp52[2] = (uint32_t(in0.ThreadCount) >> 8) & 0xFF;
|
||||||
|
tmp52[3] = (uint32_t(in0.ThreadCount) >> 0) & 0xFF;
|
||||||
|
in.append(tmp52, 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
char *out = NULL;
|
char *out = NULL;
|
||||||
int outLen = 0;
|
int outLen = 0;
|
||||||
|
|
@ -540,15 +578,143 @@ std::string RunDownload_Req_ToCurlStr(RunDownload_Req in0){
|
||||||
std::string retValue;
|
std::string retValue;
|
||||||
int outIdx = 0;
|
int outIdx = 0;
|
||||||
{
|
{
|
||||||
uint32_t tmp50 = 0;
|
uint32_t tmp53 = 0;
|
||||||
uint32_t tmp51 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
uint32_t tmp54 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
uint32_t tmp52 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
uint32_t tmp55 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
uint32_t tmp53 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
uint32_t tmp56 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
uint32_t tmp54 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
uint32_t tmp57 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
tmp50 = tmp51 | tmp52 | tmp53 | tmp54;
|
tmp53 = tmp54 | tmp55 | tmp56 | tmp57;
|
||||||
outIdx+=4;
|
outIdx+=4;
|
||||||
retValue = std::string(out+outIdx, out+outIdx+tmp50);
|
retValue = std::string(out+outIdx, out+outIdx+tmp53);
|
||||||
outIdx+=tmp50;
|
outIdx+=tmp53;
|
||||||
|
}
|
||||||
|
if (out != NULL) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetFileNameFromUrl(std::string in0){
|
||||||
|
std::string in;
|
||||||
|
{
|
||||||
|
uint32_t tmp5 = in0.length();
|
||||||
|
char tmp6[4];
|
||||||
|
tmp6[0] = (uint32_t(tmp5) >> 24) & 0xFF;
|
||||||
|
tmp6[1] = (uint32_t(tmp5) >> 16) & 0xFF;
|
||||||
|
tmp6[2] = (uint32_t(tmp5) >> 8) & 0xFF;
|
||||||
|
tmp6[3] = (uint32_t(tmp5) >> 0) & 0xFF;
|
||||||
|
in.append(tmp6, 4);
|
||||||
|
in.append(in0);
|
||||||
|
}
|
||||||
|
char *out = NULL;
|
||||||
|
int outLen = 0;
|
||||||
|
Go2cppFn_GetFileNameFromUrl((char *)in.data(), in.length(), &out, &outLen);
|
||||||
|
std::string retValue;
|
||||||
|
int outIdx = 0;
|
||||||
|
{
|
||||||
|
uint32_t tmp7 = 0;
|
||||||
|
uint32_t tmp8 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
|
uint32_t tmp9 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
|
uint32_t tmp10 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
|
uint32_t tmp11 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
|
tmp7 = tmp8 | tmp9 | tmp10 | tmp11;
|
||||||
|
outIdx+=4;
|
||||||
|
retValue = std::string(out+outIdx, out+outIdx+tmp7);
|
||||||
|
outIdx+=tmp7;
|
||||||
|
}
|
||||||
|
if (out != NULL) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeTsDir_Resp MergeTsDir(std::string in0, std::string in1){
|
||||||
|
std::string in;
|
||||||
|
{
|
||||||
|
uint32_t tmp9 = in0.length();
|
||||||
|
char tmp10[4];
|
||||||
|
tmp10[0] = (uint32_t(tmp9) >> 24) & 0xFF;
|
||||||
|
tmp10[1] = (uint32_t(tmp9) >> 16) & 0xFF;
|
||||||
|
tmp10[2] = (uint32_t(tmp9) >> 8) & 0xFF;
|
||||||
|
tmp10[3] = (uint32_t(tmp9) >> 0) & 0xFF;
|
||||||
|
in.append(tmp10, 4);
|
||||||
|
in.append(in0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t tmp11 = in1.length();
|
||||||
|
char tmp12[4];
|
||||||
|
tmp12[0] = (uint32_t(tmp11) >> 24) & 0xFF;
|
||||||
|
tmp12[1] = (uint32_t(tmp11) >> 16) & 0xFF;
|
||||||
|
tmp12[2] = (uint32_t(tmp11) >> 8) & 0xFF;
|
||||||
|
tmp12[3] = (uint32_t(tmp11) >> 0) & 0xFF;
|
||||||
|
in.append(tmp12, 4);
|
||||||
|
in.append(in1);
|
||||||
|
}
|
||||||
|
char *out = NULL;
|
||||||
|
int outLen = 0;
|
||||||
|
Go2cppFn_MergeTsDir((char *)in.data(), in.length(), &out, &outLen);
|
||||||
|
MergeTsDir_Resp retValue;
|
||||||
|
int outIdx = 0;
|
||||||
|
{
|
||||||
|
{
|
||||||
|
uint32_t tmp13 = 0;
|
||||||
|
uint32_t tmp14 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
|
uint32_t tmp15 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
|
uint32_t tmp16 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
|
uint32_t tmp17 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
|
tmp13 = tmp14 | tmp15 | tmp16 | tmp17;
|
||||||
|
outIdx+=4;
|
||||||
|
retValue.ErrMsg = std::string(out+outIdx, out+outIdx+tmp13);
|
||||||
|
outIdx+=tmp13;
|
||||||
|
}
|
||||||
|
retValue.IsCancel = (bool) out[outIdx];
|
||||||
|
outIdx++;
|
||||||
|
}
|
||||||
|
if (out != NULL) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeStop(){
|
||||||
|
std::string in;
|
||||||
|
char *out = NULL;
|
||||||
|
int outLen = 0;
|
||||||
|
Go2cppFn_MergeStop((char *)in.data(), in.length(), &out, &outLen);
|
||||||
|
if (out != NULL) {
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeGetProgressPercent_Resp MergeGetProgressPercent(){
|
||||||
|
std::string in;
|
||||||
|
char *out = NULL;
|
||||||
|
int outLen = 0;
|
||||||
|
Go2cppFn_MergeGetProgressPercent((char *)in.data(), in.length(), &out, &outLen);
|
||||||
|
MergeGetProgressPercent_Resp retValue;
|
||||||
|
int outIdx = 0;
|
||||||
|
{
|
||||||
|
{
|
||||||
|
uint32_t tmp4 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
|
uint32_t tmp5 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
|
uint32_t tmp6 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
|
uint32_t tmp7 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
|
retValue.Percent = tmp4 | tmp5 | tmp6 | tmp7;
|
||||||
|
outIdx+=4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32_t tmp8 = 0;
|
||||||
|
uint32_t tmp9 = uint32_t(uint8_t(out[outIdx+0]) << 24);
|
||||||
|
uint32_t tmp10 = uint32_t(uint8_t(out[outIdx+1]) << 16);
|
||||||
|
uint32_t tmp11 = uint32_t(uint8_t(out[outIdx+2]) << 8);
|
||||||
|
uint32_t tmp12 = uint32_t(uint8_t(out[outIdx+3]) << 0);
|
||||||
|
tmp8 = tmp9 | tmp10 | tmp11 | tmp12;
|
||||||
|
outIdx+=4;
|
||||||
|
retValue.SpeedText = std::string(out+outIdx, out+outIdx+tmp8);
|
||||||
|
outIdx+=tmp8;
|
||||||
|
}
|
||||||
|
retValue.IsRunning = (bool) out[outIdx];
|
||||||
|
outIdx++;
|
||||||
}
|
}
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
free(out);
|
free(out);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ struct RunDownload_Req{
|
||||||
std::string SetProxy;
|
std::string SetProxy;
|
||||||
std::map<std::string, std::vector<std::string>> HeaderMap;
|
std::map<std::string, std::vector<std::string>> HeaderMap;
|
||||||
bool SkipRemoveTs;
|
bool SkipRemoveTs;
|
||||||
RunDownload_Req(): Insecure(false),SkipTsCountFromHead(0),SkipRemoveTs(false){}
|
bool ProgressBarShow;
|
||||||
|
int32_t ThreadCount;
|
||||||
|
RunDownload_Req(): Insecure(false),SkipTsCountFromHead(0),SkipRemoveTs(false),ProgressBarShow(false),ThreadCount(0){}
|
||||||
};
|
};
|
||||||
struct RunDownload_Resp{
|
struct RunDownload_Resp{
|
||||||
std::string ErrMsg;
|
std::string ErrMsg;
|
||||||
|
|
@ -42,6 +44,21 @@ struct ParseCurl_Resp{
|
||||||
};
|
};
|
||||||
ParseCurl_Resp ParseCurlStr(std::string in0);
|
ParseCurl_Resp ParseCurlStr(std::string in0);
|
||||||
std::string RunDownload_Req_ToCurlStr(RunDownload_Req in0);
|
std::string RunDownload_Req_ToCurlStr(RunDownload_Req in0);
|
||||||
|
std::string GetFileNameFromUrl(std::string in0);
|
||||||
|
struct MergeTsDir_Resp{
|
||||||
|
std::string ErrMsg;
|
||||||
|
bool IsCancel;
|
||||||
|
MergeTsDir_Resp(): IsCancel(false){}
|
||||||
|
};
|
||||||
|
MergeTsDir_Resp MergeTsDir(std::string in0, std::string in1);
|
||||||
|
void MergeStop();
|
||||||
|
struct MergeGetProgressPercent_Resp{
|
||||||
|
int32_t Percent;
|
||||||
|
std::string SpeedText;
|
||||||
|
bool IsRunning;
|
||||||
|
MergeGetProgressPercent_Resp(): Percent(0),IsRunning(false){}
|
||||||
|
};
|
||||||
|
MergeGetProgressPercent_Resp MergeGetProgressPercent();
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QIntValidator>
|
#include <QIntValidator>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
#include "curldialog.h"
|
#include "curldialog.h"
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) :
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
|
@ -18,25 +22,33 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
ui->lineEdit_SkipTsCountFromHead->setValidator(vd);
|
ui->lineEdit_SkipTsCountFromHead->setValidator(vd);
|
||||||
ui->lineEdit_SkipTsCountFromHead->setPlaceholderText("[0,9999]");
|
ui->lineEdit_SkipTsCountFromHead->setPlaceholderText("[0,9999]");
|
||||||
ui->lineEdit_SaveDir->setPlaceholderText(QString::fromStdString(GetWd()));
|
ui->lineEdit_SaveDir->setPlaceholderText(QString::fromStdString(GetWd()));
|
||||||
m_syncUi.AddRunFnOn_OtherThread([this](){
|
m_timer = new QTimer(this);
|
||||||
while(true)
|
connect(m_timer, &QTimer::timeout, [this](){
|
||||||
|
//更新ui1
|
||||||
{
|
{
|
||||||
QThread::msleep(50);
|
|
||||||
if (this->m_syncUi.Get_Done()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_syncUi.AddRunFnOn_UiThread([this](){
|
|
||||||
GetProgress_Resp resp = GetProgress();
|
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(!resp.StatusBar.empty())
|
||||||
ui->statusBar->showMessage(QString::fromStdString(resp.StatusBar), 5*1000);
|
ui->statusBar->showMessage(QString::fromStdString(resp.StatusBar), 5*1000);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
//更新ui2
|
||||||
|
{
|
||||||
|
auto resp = MergeGetProgressPercent();
|
||||||
|
ui->progressBar_merge->setValue(resp.Percent);
|
||||||
|
if(!resp.SpeedText.empty())
|
||||||
|
ui->statusBar->showMessage(QString::fromStdString(resp.SpeedText), 5*1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
m_timer->start(50);
|
||||||
|
this->updateDownloadUi(false);
|
||||||
|
this->updateMergeUi(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
|
m_timer->stop();
|
||||||
CloseOldEnv();
|
CloseOldEnv();
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
@ -46,18 +58,7 @@ void MainWindow::on_pushButton_RunDownload_clicked()
|
||||||
if (ui->lineEdit_M3u8Url->isEnabled()==false) {
|
if (ui->lineEdit_M3u8Url->isEnabled()==false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ui->lineEdit_M3u8Url->setEnabled(false);
|
updateDownloadUi(true);
|
||||||
ui->lineEdit_SaveDir->setEnabled(false);
|
|
||||||
ui->pushButton_SaveDir->setEnabled(false);
|
|
||||||
ui->lineEdit_FileName->setEnabled(false);
|
|
||||||
ui->lineEdit_SkipTsCountFromHead->setEnabled(false);
|
|
||||||
ui->pushButton_RunDownload->setEnabled(false);
|
|
||||||
ui->checkBox_Insecure->setEnabled(false);
|
|
||||||
ui->progressBar->setValue(0);
|
|
||||||
ui->lineEdit_SetProxy->setEnabled(false);
|
|
||||||
ui->pushButton_curlMode->setEnabled(false);
|
|
||||||
ui->checkBox_SkipRemoveTs->setEnabled(false);
|
|
||||||
ui->pushButton_StopDownload->setEnabled(true);
|
|
||||||
|
|
||||||
RunDownload_Req req;
|
RunDownload_Req req;
|
||||||
req.M3u8Url = ui->lineEdit_M3u8Url->text().toStdString();
|
req.M3u8Url = ui->lineEdit_M3u8Url->text().toStdString();
|
||||||
|
|
@ -68,28 +69,18 @@ void MainWindow::on_pushButton_RunDownload_clicked()
|
||||||
req.SetProxy = ui->lineEdit_SetProxy->text().toStdString();
|
req.SetProxy = ui->lineEdit_SetProxy->text().toStdString();
|
||||||
req.HeaderMap = m_HeaderMap;
|
req.HeaderMap = m_HeaderMap;
|
||||||
req.SkipRemoveTs = ui->checkBox_SkipRemoveTs->isChecked();
|
req.SkipRemoveTs = ui->checkBox_SkipRemoveTs->isChecked();
|
||||||
|
req.ThreadCount = ui->lineEdit_ThreadCount->text().toInt();
|
||||||
|
|
||||||
m_syncUi.AddRunFnOn_OtherThread([req, this](){
|
m_syncUi.AddRunFnOn_OtherThread([req, this](){
|
||||||
RunDownload_Resp resp = RunDownload(req);
|
RunDownload_Resp resp = RunDownload(req);
|
||||||
m_syncUi.AddRunFnOn_UiThread([req, this, resp](){
|
m_syncUi.AddRunFnOn_UiThread([req, this, resp](){
|
||||||
ui->lineEdit_M3u8Url->setEnabled(true);
|
this->updateDownloadUi(false);
|
||||||
ui->lineEdit_SaveDir->setEnabled(true);
|
|
||||||
ui->pushButton_SaveDir->setEnabled(true);
|
|
||||||
ui->lineEdit_FileName->setEnabled(true);
|
|
||||||
ui->lineEdit_SkipTsCountFromHead->setEnabled(true);
|
|
||||||
ui->pushButton_RunDownload->setEnabled(true);
|
|
||||||
ui->checkBox_Insecure->setEnabled(true);
|
|
||||||
ui->pushButton_RunDownload->setText("开始下载");
|
|
||||||
ui->lineEdit_SetProxy->setEnabled(true);
|
|
||||||
ui->pushButton_StopDownload->setEnabled(false);
|
|
||||||
ui->pushButton_curlMode->setEnabled(true);
|
|
||||||
ui->checkBox_SkipRemoveTs->setEnabled(true);
|
|
||||||
if (resp.IsCancel) {
|
if (resp.IsCancel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resp.ErrMsg.empty()) {
|
if (!resp.ErrMsg.empty()) {
|
||||||
Toast::Instance()->SetError(QString::fromStdString(resp.ErrMsg));
|
QMessageBox::warning(this, "下载错误", QString::fromStdString(resp.ErrMsg));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (resp.IsSkipped) {
|
if (resp.IsSkipped) {
|
||||||
|
|
@ -129,3 +120,93 @@ void MainWindow::on_pushButton_curlMode_clicked()
|
||||||
ui->checkBox_Insecure->setChecked(resp.DownloadReq.Insecure);
|
ui->checkBox_Insecure->setChecked(resp.DownloadReq.Insecure);
|
||||||
this->m_HeaderMap = resp.DownloadReq.HeaderMap;
|
this->m_HeaderMap = resp.DownloadReq.HeaderMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_lineEdit_M3u8Url_textChanged(const QString &arg1)
|
||||||
|
{
|
||||||
|
if (ui->lineEdit_FileName->text().isEmpty()==false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString fileName = QString::fromStdString(GetFileNameFromUrl(arg1.toStdString()));
|
||||||
|
if (fileName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui->lineEdit_FileName->setPlaceholderText(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_pushButton_returnDownload_clicked()
|
||||||
|
{
|
||||||
|
ui->stackedWidget->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_pushButton_gotoMergeTs_clicked()
|
||||||
|
{
|
||||||
|
ui->stackedWidget->setCurrentIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_pushButton_startMerge_clicked()
|
||||||
|
{
|
||||||
|
QString fileName = ui->lineEdit_mergeFileName->text();
|
||||||
|
if(fileName.isEmpty())
|
||||||
|
fileName = ui->lineEdit_mergeFileName->placeholderText();
|
||||||
|
QString dir = ui->lineEdit_mergeDir->text();
|
||||||
|
|
||||||
|
this->updateMergeUi(true);
|
||||||
|
|
||||||
|
m_syncUi.AddRunFnOn_OtherThread([=](){
|
||||||
|
auto resp = MergeTsDir(dir.toStdString(), fileName.toStdString());
|
||||||
|
|
||||||
|
m_syncUi.AddRunFnOn_UiThread([=](){
|
||||||
|
this->updateMergeUi(false);
|
||||||
|
if(resp.ErrMsg.empty())
|
||||||
|
Toast::Instance()->SetSuccess("合并成功!");
|
||||||
|
else if(!resp.IsCancel)
|
||||||
|
QMessageBox::warning(this, "下载错误", QString::fromStdString(resp.ErrMsg));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_pushButton_stopMerge_clicked()
|
||||||
|
{
|
||||||
|
MergeStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_toolButton_selectMergeDir_clicked()
|
||||||
|
{
|
||||||
|
QString dir = QFileDialog::getExistingDirectory(this);
|
||||||
|
if(dir.isEmpty())
|
||||||
|
return;
|
||||||
|
ui->lineEdit_mergeDir->setText(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateDownloadUi(bool runing)
|
||||||
|
{
|
||||||
|
ui->lineEdit_M3u8Url->setEnabled(!runing);
|
||||||
|
ui->lineEdit_SaveDir->setEnabled(!runing);
|
||||||
|
ui->pushButton_SaveDir->setEnabled(!runing);
|
||||||
|
ui->lineEdit_FileName->setEnabled(!runing);
|
||||||
|
ui->lineEdit_SkipTsCountFromHead->setEnabled(!runing);
|
||||||
|
ui->pushButton_RunDownload->setEnabled(!runing);
|
||||||
|
ui->checkBox_Insecure->setEnabled(!runing);
|
||||||
|
if(runing == false)
|
||||||
|
ui->pushButton_RunDownload->setText("开始下载");
|
||||||
|
ui->lineEdit_SetProxy->setEnabled(!runing);
|
||||||
|
ui->pushButton_StopDownload->setEnabled(runing);
|
||||||
|
ui->pushButton_curlMode->setEnabled(!runing);
|
||||||
|
ui->checkBox_SkipRemoveTs->setEnabled(!runing);
|
||||||
|
ui->lineEdit_ThreadCount->setEnabled(!runing);
|
||||||
|
|
||||||
|
if(runing == false)
|
||||||
|
ui->progressBar->setValue(0);
|
||||||
|
|
||||||
|
ui->pushButton_gotoMergeTs->setEnabled(!runing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateMergeUi(bool runing)
|
||||||
|
{
|
||||||
|
ui->lineEdit_mergeDir->setEnabled(!runing);
|
||||||
|
ui->toolButton_selectMergeDir->setEnabled(!runing);
|
||||||
|
ui->pushButton_stopMerge->setEnabled(runing);
|
||||||
|
ui->pushButton_startMerge->setEnabled(!runing);
|
||||||
|
ui->lineEdit_mergeFileName->setEnabled(!runing);
|
||||||
|
ui->pushButton_returnDownload->setEnabled(!runing);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,24 @@ private slots:
|
||||||
|
|
||||||
void on_pushButton_curlMode_clicked();
|
void on_pushButton_curlMode_clicked();
|
||||||
|
|
||||||
|
void on_lineEdit_M3u8Url_textChanged(const QString &arg1);
|
||||||
|
|
||||||
|
void on_pushButton_returnDownload_clicked();
|
||||||
|
|
||||||
|
void on_pushButton_gotoMergeTs_clicked();
|
||||||
|
|
||||||
|
void on_pushButton_startMerge_clicked();
|
||||||
|
|
||||||
|
void on_pushButton_stopMerge_clicked();
|
||||||
|
|
||||||
|
void on_toolButton_selectMergeDir_clicked();
|
||||||
|
private:
|
||||||
|
void updateDownloadUi(bool runing);
|
||||||
|
void updateMergeUi(bool runing);
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
RunOnUiThread m_syncUi;
|
RunOnUiThread m_syncUi;
|
||||||
|
QTimer *m_timer = nullptr;
|
||||||
std::map<std::string, std::vector<std::string>> m_HeaderMap;
|
std::map<std::string, std::vector<std::string>> m_HeaderMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>762</width>
|
<width>857</width>
|
||||||
<height>253</height>
|
<height>297</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
|
@ -15,12 +15,25 @@
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralWidget">
|
<widget class="QWidget" name="centralWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="page">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="4" column="0">
|
<item row="4" column="1">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLineEdit" name="lineEdit_SkipTsCountFromHead"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEdit_ThreadCount">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>代理设置</string>
|
<string>8</string>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>[1,1000]</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -31,10 +44,24 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="lineEdit_M3u8Url">
|
<widget class="QLineEdit" name="lineEdit_FileName">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string>m3u8的url</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="pushButton_curlMode">
|
||||||
|
<property name="text">
|
||||||
|
<string>curl模式</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -45,6 +72,34 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>跳过前面几个TS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>下载线程数</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>代理设置</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEdit_M3u8Url">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="lineEdit_SaveDir"/>
|
<widget class="QLineEdit" name="lineEdit_SaveDir"/>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -73,38 +128,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QLineEdit" name="lineEdit_FileName">
|
|
||||||
<property name="placeholderText">
|
|
||||||
<string>all</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QPushButton" name="pushButton_curlMode">
|
|
||||||
<property name="text">
|
|
||||||
<string>curl模式</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>跳过前面几个TS</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_SkipTsCountFromHead"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>m3u8的url</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLineEdit" name="lineEdit_SetProxy">
|
<widget class="QLineEdit" name="lineEdit_SetProxy">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
|
|
@ -144,7 +168,7 @@
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,0">
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,0,0">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_progressBar">
|
<widget class="QLabel" name="label_progressBar">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -159,6 +183,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_gotoMergeTs">
|
||||||
|
<property name="text">
|
||||||
|
<string>合并ts</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="pushButton_RunDownload">
|
<widget class="QPushButton" name="pushButton_RunDownload">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -180,6 +211,166 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>文件夹:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_mergeDir"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="toolButton_selectMergeDir">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,1,0">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>输出文件名:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit_mergeFileName">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>all.mp4</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>80</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>进度</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar_merge">
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_returnDownload">
|
||||||
|
<property name="text">
|
||||||
|
<string>返回</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_startMerge">
|
||||||
|
<property name="text">
|
||||||
|
<string>开始合并</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton_stopMerge">
|
||||||
|
<property name="text">
|
||||||
|
<string>停止合并</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusBar"/>
|
<widget class="QStatusBar" name="statusBar"/>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ IDI_ICON1 ICON "favicon.ico"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,5,6,0
|
FILEVERSION 1,5,20,0
|
||||||
PRODUCTVERSION 1,5,6,0
|
PRODUCTVERSION 1,5,20,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS VS_FF_DEBUG
|
FILEFLAGS VS_FF_DEBUG
|
||||||
|
|
@ -23,7 +23,7 @@ VS_VERSION_INFO VERSIONINFO
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "080404b0"
|
BLOCK "080404b0"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "ProductVersion", "1.5.6.0\0"
|
VALUE "ProductVersion", "1.5.20.0\0"
|
||||||
VALUE "ProductName", "m3u8 downloader\0"
|
VALUE "ProductName", "m3u8 downloader\0"
|
||||||
VALUE "LegalCopyright", "https://github.com/orestonce/m3u8d\0"
|
VALUE "LegalCopyright", "https://github.com/orestonce/m3u8d\0"
|
||||||
VALUE "FileDescription", "m3u8 downloader\0"
|
VALUE "FileDescription", "m3u8 downloader\0"
|
||||||
|
|
|
||||||
19
merge.go
19
merge.go
|
|
@ -15,6 +15,7 @@ import (
|
||||||
type MergeTsFileListToSingleMp4_Req struct {
|
type MergeTsFileListToSingleMp4_Req struct {
|
||||||
TsFileList []string
|
TsFileList []string
|
||||||
OutputMp4 string
|
OutputMp4 string
|
||||||
|
Status *SpeedStatus
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,6 +26,10 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
}
|
}
|
||||||
defer mp4file.Close()
|
defer mp4file.Close()
|
||||||
|
|
||||||
|
if req.Status != nil {
|
||||||
|
req.Status.SpeedResetBytes()
|
||||||
|
}
|
||||||
|
|
||||||
muxer, err := mp4.CreateMp4Muxer(mp4file)
|
muxer, err := mp4.CreateMp4Muxer(mp4file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -56,7 +61,7 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if cid == mpeg2.TS_STREAM_H264 {
|
} else if cid == mpeg2.TS_STREAM_H264 {
|
||||||
err = muxer.Write(vtid, frame, uint64(pts), uint64(dts))
|
err = muxer.Write(vtid, frame, pts, dts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
OnFrameErr = err
|
OnFrameErr = err
|
||||||
return
|
return
|
||||||
|
|
@ -67,8 +72,6 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
env := getOldEnv()
|
|
||||||
|
|
||||||
for idx, tsFile := range req.TsFileList {
|
for idx, tsFile := range req.TsFileList {
|
||||||
select {
|
select {
|
||||||
case <-req.Ctx.Done():
|
case <-req.Ctx.Done():
|
||||||
|
|
@ -87,9 +90,9 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
if OnFrameErr != nil {
|
if OnFrameErr != nil {
|
||||||
return OnFrameErr
|
return OnFrameErr
|
||||||
}
|
}
|
||||||
if env != nil {
|
if req.Status != nil {
|
||||||
env.DrawProgressBar(len(req.TsFileList), idx)
|
req.Status.DrawProgressBar(len(req.TsFileList), idx)
|
||||||
env.speedAddBytes(len(buf))
|
req.Status.SpeedAddBytes(len(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,8 +104,8 @@ func MergeTsFileListToSingleMp4(req MergeTsFileListToSingleMp4_Req) (err error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if env != nil {
|
if req.Status != nil {
|
||||||
env.DrawProgressBar(1, 1)
|
req.Status.DrawProgressBar(1, 1)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
82
speed.go
82
speed.go
|
|
@ -1,38 +1,67 @@
|
||||||
package m3u8d
|
package m3u8d
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (this *downloadEnv) speedSetBegin() {
|
type SpeedStatus struct {
|
||||||
|
speedBytesLocker sync.Mutex
|
||||||
|
speedBeginTime time.Time
|
||||||
|
speedBytesMap map[time.Time]int64
|
||||||
|
|
||||||
|
progressLocker sync.Mutex
|
||||||
|
progressPercent int
|
||||||
|
progressBarTitle string
|
||||||
|
progressBarShow bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SpeedStatus) DrawProgressBar(total int, current int) {
|
||||||
|
if total == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proportion := float32(current) / float32(total)
|
||||||
|
|
||||||
|
this.progressLocker.Lock()
|
||||||
|
this.progressPercent = int(proportion * 100)
|
||||||
|
title := this.progressBarTitle
|
||||||
|
if this.progressBarShow {
|
||||||
|
width := 50
|
||||||
|
pos := int(proportion * float32(width))
|
||||||
|
fmt.Printf(title+" %s%*s %6.2f%%\r", strings.Repeat("■", pos), width-pos, "", proportion*100)
|
||||||
|
}
|
||||||
|
this.progressLocker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SpeedStatus) SpeedAddBytes(a int) {
|
||||||
|
this.speedBytesLocker.Lock()
|
||||||
|
defer this.speedBytesLocker.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
this.speedBytesMap[now] += int64(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SpeedStatus) SpeedResetBytes() {
|
||||||
this.speedBytesLocker.Lock()
|
this.speedBytesLocker.Lock()
|
||||||
defer this.speedBytesLocker.Unlock()
|
defer this.speedBytesLocker.Unlock()
|
||||||
|
|
||||||
this.speedBeginTime = time.Now()
|
this.speedBeginTime = time.Now()
|
||||||
}
|
if this.speedBytesMap == nil {
|
||||||
|
this.speedBytesMap = map[time.Time]int64{}
|
||||||
func (this *downloadEnv) speedAddBytes(a int) {
|
}
|
||||||
this.speedBytesLocker.Lock()
|
|
||||||
defer this.speedBytesLocker.Unlock()
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
this.speedBytesMap[now] += int64(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *downloadEnv) speedClearBytes() {
|
|
||||||
this.speedBytesLocker.Lock()
|
|
||||||
defer this.speedBytesLocker.Unlock()
|
|
||||||
|
|
||||||
this.speedBytesMap = map[time.Time]int64{}
|
this.speedBytesMap = map[time.Time]int64{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *downloadEnv) speedRecent5sGetAndUpdate() string {
|
func (this *SpeedStatus) SpeedRecent5sGetAndUpdate() string {
|
||||||
this.speedBytesLocker.Lock()
|
this.speedBytesLocker.Lock()
|
||||||
defer this.speedBytesLocker.Unlock()
|
defer this.speedBytesLocker.Unlock()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if this.GetIsCancel() || this.speedBeginTime.IsZero() || now.Sub(this.speedBeginTime) < time.Second { // 1s以内, 暂时不计算速度
|
if this.speedBeginTime.IsZero() || now.Sub(this.speedBeginTime) < time.Second { // 1s以内, 暂时不计算速度
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,3 +93,22 @@ func (this *downloadEnv) speedRecent5sGetAndUpdate() string {
|
||||||
v = v / 1024
|
v = v / 1024
|
||||||
return strconv.FormatFloat(v, 'f', 2, 64) + " MB/s"
|
return strconv.FormatFloat(v, 'f', 2, 64) + " MB/s"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *SpeedStatus) GetPercent() (percent int) {
|
||||||
|
this.progressLocker.Lock()
|
||||||
|
defer this.progressLocker.Unlock()
|
||||||
|
|
||||||
|
return this.progressPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SpeedStatus) GetTitle() (title string) {
|
||||||
|
this.progressLocker.Lock()
|
||||||
|
defer this.progressLocker.Unlock()
|
||||||
|
|
||||||
|
return this.progressBarTitle
|
||||||
|
}
|
||||||
|
func (this *SpeedStatus) SetProgressBarTitle(title string) {
|
||||||
|
this.progressLocker.Lock()
|
||||||
|
this.progressBarTitle = title
|
||||||
|
this.progressLocker.Unlock()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package m3u8d
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSpeedStatus_GetPercent(t *testing.T) {
|
||||||
|
var status SpeedStatus
|
||||||
|
status.SpeedResetBytes()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue