diff --git a/README.md b/README.md index 247386d..bb88115 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ 此次下载. ## TODO: * [x] 如果不是m3u8样子的URL,自动下载html下来、搜索其中的m3u8链接进行下载 - * [ ] windows、linux、mac都支持ffmpeg合并ts列表为mp4 + * [x] windows、linux、mac都支持ffmpeg合并ts列表为mp4 * [ ] 增加linux的图形界面支持 * [ ] 支持保存为mp3格式 ## 二次开发操作手册: diff --git a/cache.go b/cache.go index e55d8e8..fe07490 100644 --- a/cache.go +++ b/cache.go @@ -8,7 +8,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strconv" ) type DbVideoInfo struct { @@ -28,15 +27,15 @@ func (this *RunDownload_Req) getVideoId() (id string, err error) { } func cacheRead(dir string, id string) (info *DbVideoInfo, err error) { - value, err := dbRead(dir, id) + value, err := cdb.FileGetValueString(filepath.Join(dir, "m3u8d_cache.cdb"), id) if err != nil { + if err == cdb.ErrNoData { + return nil, nil + } return nil, err } - if len(value) == 0 { - return nil, nil - } var obj DbVideoInfo - err = json.Unmarshal(value, &obj) + err = json.Unmarshal([]byte(value), &obj) if err != nil { return nil, err } @@ -77,7 +76,7 @@ func cacheWrite(dir string, id string, originReq RunDownload_Req, videoNameFullP if err != nil { return err } - return dbWrite(dir, id, content) + return cdb.FileRewriteKeyValue(filepath.Join(dir, "m3u8d_cache.cdb"), id, string(content)) } func dbRead(dir string, key string) (content []byte, err error) { @@ -98,56 +97,3 @@ func dbRead(dir string, key string) (content []byte, err error) { } return content, nil } - -func dbWrite(dir string, key string, value []byte) (err error) { - cdbFileName := filepath.Join(dir, "m3u8d_cache.cdb") - - reader, err := cdb.OpenFile(cdbFileName) - if err != nil && !os.IsNotExist(err) { - return err - } - tmpCdbFileName := cdbFileName + "." + strconv.Itoa(os.Getpid()) + ".tmp" - writer, err := cdb.NewFileWriter(tmpCdbFileName) - if err != nil { - if reader != nil { - reader.Close() - } - return err - } - - if reader != nil { - for it := reader.BeginIterator(); it != nil; { - tmpKey, tmpValue, err := it.ReadNextKeyValue() - if err != nil { - if err == cdb.ErrNoData { - break - } - reader.Close() - writer.Close() - os.Remove(tmpCdbFileName) - return err - } - if string(tmpKey) == key { - continue - } - err = writer.WriteKeyValue(tmpKey, tmpValue) - if err != nil { - reader.Close() - writer.Close() - os.Remove(tmpCdbFileName) - return err - } - } - reader.Close() - } - err = writer.WriteKeyValue([]byte(key), value) - if err != nil { - writer.Close() - return err - } - err = writer.Close() - if err != nil { - return err - } - return os.Rename(tmpCdbFileName, cdbFileName) -} diff --git a/download.go b/download.go index 1d29a92..a6b0f00 100644 --- a/download.go +++ b/download.go @@ -1,7 +1,3 @@ -//@author:llychao -//@contributor: Junyi -//@date:2020-02-18 -//@功能:golang m3u8 video Downloader package m3u8d import ( @@ -12,6 +8,8 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/levigross/grequests" + "github.com/orestonce/goffmpeg" "github.com/orestonce/gopool" "io" "io/ioutil" @@ -24,8 +22,6 @@ import ( "strings" "sync" "time" - - "github.com/levigross/grequests" ) // TsInfo 用于保存 ts 文件的下载地址和文件名 @@ -104,7 +100,7 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) { } var ffmpegExe string if req.UserFfmpegMerge { - ffmpegExe, err = SetupFfmpeg() + ffmpegExe, err = goffmpeg.SetupFfmpeg() if err != nil { resp.ErrMsg = "SetupFfmpeg error: " + err.Error() return resp @@ -177,14 +173,28 @@ func RunDownload(req RunDownload_Req) (resp RunDownload_Resp) { var contentHash string if req.UserFfmpegMerge { tmpOutputName = filepath.Join(downloadDir, "all.merge.mp4") - contentHash, err = mergeTsFileList_Ffmpeg(ffmpegExe, tsFileList, tmpOutputName) + err = goffmpeg.MergeMultiToSingleMp4(goffmpeg.MergeMultiToSingleMp4_Req{ + FfmpegExePath: ffmpegExe, + TsFileList: tsFileList, + OutputMp4: tmpOutputName, + ProgressCh: nil, + }) + if err != nil { + resp.ErrMsg = "合并错误: " + err.Error() + return resp + } + contentHash = getFileSha256(tmpOutputName) + if contentHash == "" { + resp.ErrMsg = "无法计算摘要信息: " + tmpOutputName + return resp + } } else { tmpOutputName = filepath.Join(downloadDir, "all.merge.ts") contentHash, err = mergeTsFileList_Raw(tsFileList, tmpOutputName) - } - if err != nil { - resp.ErrMsg = "合并错误: " + err.Error() - return resp + if err != nil { + resp.ErrMsg = "合并错误: " + err.Error() + return resp + } } var name string for idx := 0; ; idx++ { diff --git a/export/main.go b/export/main.go index a715ae3..a845219 100644 --- a/export/main.go +++ b/export/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - BuildCliBinary() // 编译二进制 + //BuildCliBinary() // 编译二进制 CreateLibForQtUi() // 创建Qt需要使用的.a库文件 } @@ -42,6 +42,10 @@ func BuildCliBinary() { GOOS: "linux", GOARCH: "arm64", }, + { + GOOS: "linux", + GOARCH: "arm", + }, { GOOS: "darwin", GOARCH: "amd64", diff --git a/ffmpeg-2022-05-04-git-0914e3a14a-essentials_build.7z b/ffmpeg-2022-05-04-git-0914e3a14a-essentials_build.7z deleted file mode 100644 index 64ce647..0000000 Binary files a/ffmpeg-2022-05-04-git-0914e3a14a-essentials_build.7z and /dev/null differ diff --git a/ffmpeg_other.go b/ffmpeg_other.go deleted file mode 100644 index 10f8753..0000000 --- a/ffmpeg_other.go +++ /dev/null @@ -1,9 +0,0 @@ -//+build !windows - -package m3u8d - -import "errors" - -func UnzipFfmpegToLocal(exeFileDir string) (targetFile string, err error) { - return "", errors.New("UnzipFfmpegToLocal not implement.") -} diff --git a/ffmpeg_windows.go b/ffmpeg_windows.go deleted file mode 100644 index 0d90b8b..0000000 --- a/ffmpeg_windows.go +++ /dev/null @@ -1,78 +0,0 @@ -//+build windows - -package m3u8d - -import ( - "bytes" - _ "embed" - "errors" - "github.com/saracen/go7z" - "io" - "os" - "path" - "path/filepath" -) - -//go:embed ffmpeg-2022-05-04-git-0914e3a14a-essentials_build.7z -var g7zipFileContent []byte - -const FfmpegSha256Value = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - -func UnzipFfmpegToLocal(exeFileDir string) (targetFile string, err error) { - targetFile = filepath.Join(exeFileDir, "ffmpeg-2022-05-04.exe") - if isFileExists(targetFile) && isSha256Match(targetFile) { - return targetFile, nil - } - sz, err := go7z.NewReader(bytes.NewReader(g7zipFileContent), int64(len(g7zipFileContent))) - if err != nil { - return "", err - } - - for { - hdr, err := sz.Next() - if err == io.EOF { - break // End of archive - } - if err != nil { - return "", err - } - - // If empty stream (no contents) and isn't specifically an empty file... - // then it's a directory. - if hdr.IsEmptyStream && !hdr.IsEmptyFile { - continue - } - - if path.Base(hdr.Name) == "ffmpeg.exe" { - if !isDirExists(exeFileDir) { - err = os.MkdirAll(exeFileDir, 0777) - if err != nil { - return "", err - } - } - // Create file - f, err := os.Create(targetFile) - if err != nil { - return "", err - } - - if _, err := io.Copy(f, sz); err != nil { - f.Close() - return "", err - } - err = f.Close() - if err != nil { - return "", err - } - if !isSha256Match(targetFile) { - return "", errors.New("ffmpeg的sha256 校验不通过") - } - return targetFile, nil - } - } - return "", errors.New("7z文件内未找到 ffmpeg.exe") -} - -func isSha256Match(targetFile string) bool { - return getFileSha256(targetFile) == FfmpegSha256Value -} diff --git a/go.mod b/go.mod index b3f0b25..704c6bd 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.17 require ( github.com/levigross/grequests v0.0.0-20190908174114-253788527a1a - github.com/orestonce/cdb v0.0.0-20210317131130-99d93d11de21 + github.com/orestonce/cdb v0.0.0-20220528005855-d187c22240e2 github.com/orestonce/go2cpp v0.0.0-20220507123906-b66d3600c123 + github.com/orestonce/goffmpeg v0.0.0-20220528011920-92f3449b6c15 github.com/orestonce/gopool v0.0.0-20220508090328-d7d56d45b171 github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda github.com/spf13/cobra v1.4.0 diff --git a/go.sum b/go.sum index 3f29579..fd803ec 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,14 @@ github.com/levigross/grequests v0.0.0-20190908174114-253788527a1a h1:DGFy/362j92 github.com/levigross/grequests v0.0.0-20190908174114-253788527a1a/go.mod h1:jVntzcUU+2BtVohZBQmSHWUmh8B55LCNfPhcNCIvvIg= github.com/orestonce/cdb v0.0.0-20210317131130-99d93d11de21 h1:kFAgzquHOv3dmVU2HoUUzu1586bd1DKHfAz6dd5do+I= github.com/orestonce/cdb v0.0.0-20210317131130-99d93d11de21/go.mod h1:HMNNdA1LMQFJRwobtCzVevWcInFSo9rfs1fYeVYwU+c= +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/go2cpp v0.0.0-20220507123906-b66d3600c123 h1:AyAKqO7kC68OKiZk9GV4bUG+xMe1VQCy2CPbS9hvdY4= github.com/orestonce/go2cpp v0.0.0-20220507123906-b66d3600c123/go.mod h1:1fsOAZftk08/dOTRqlp6f/MVwaEKOhrnPUg0RtWiSdY= +github.com/orestonce/goffmpeg v0.0.0-20220519233347-2266c9b171d9 h1:ki+QkZANpELM4WJjjcFwwFmt6r9ni53QxIYeS6OX77Y= +github.com/orestonce/goffmpeg v0.0.0-20220519233347-2266c9b171d9/go.mod h1:QHZrzI4ewo7pYfEo8+QzrPxsCixua/TS0aE505m/nBU= +github.com/orestonce/goffmpeg v0.0.0-20220528011920-92f3449b6c15 h1:Ix2AmnSHlA2hrI1NkpmXnFeOcxtdCj66fj1KoGFq9tI= +github.com/orestonce/goffmpeg v0.0.0-20220528011920-92f3449b6c15/go.mod h1:QHZrzI4ewo7pYfEo8+QzrPxsCixua/TS0aE505m/nBU= 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/merge.go b/merge.go index b74c137..9b2c5fa 100644 --- a/merge.go +++ b/merge.go @@ -3,104 +3,10 @@ package m3u8d import ( "crypto/sha256" "encoding/hex" - "errors" "io" "os" - "os/exec" - "path/filepath" - "sync" ) -type SetupFfmpeg_Resp struct { - ErrMsg string - FfmpegPath string -} - -var gFfmpegExePath string -var gFfmpegExePathLocker sync.Mutex - -func SetupFfmpeg() (p string, err error) { - gFfmpegExePathLocker.Lock() - defer gFfmpegExePathLocker.Unlock() - - if gFfmpegExePath != "" { - return gFfmpegExePath, nil - } - - ffmpegPath, err := exec.LookPath("ffmpeg") - if err != nil { - ffmpegPath, err = UnzipFfmpegToLocal(getTempDir()) - if err != nil { - return "", err - } - gFfmpegExePath = ffmpegPath - return ffmpegPath, nil - } - gFfmpegExePath = ffmpegPath - return ffmpegPath, nil -} - -func getTempDir() string { - dir := filepath.Join(os.TempDir(), "m3u8d") - if !isDirExists(dir) { - _ = os.MkdirAll(dir, 0777) - } - return dir -} - -func mergeTsFileList_Ffmpeg(ffmpegPath string, tsFileList []string, outputMp4 string) (contentHash string, err error) { - outputMp4Temp := outputMp4 + ".tmp" - cmd := exec.Command(ffmpegPath, "-i", "-", "-acodec", "copy", "-vcodec", "copy", "-f", "mp4", outputMp4Temp) - ip, err := cmd.StdinPipe() - if err != nil { - return "", err - } - if isFileExists(outputMp4Temp) { - err = os.Remove(outputMp4Temp) - if err != nil { - return "", err - } - } - //cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - setupCmd(cmd) - err = cmd.Start() - if err != nil { - return "", err - } - for _, name := range tsFileList { - fin, err := os.Open(name) - if err != nil { - cmd.Process.Kill() - cmd.Wait() - return "", errors.New("read error: " + err.Error()) - } - _, err = io.Copy(ip, fin) - if err != nil { - cmd.Process.Kill() - cmd.Wait() - fin.Close() - return "", errors.New("write error: " + err.Error()) - } - fin.Close() - } - err = ip.Close() - if err != nil { - cmd.Process.Kill() - cmd.Wait() - return "", errors.New("ip.Close error: " + err.Error()) - } - err = cmd.Wait() - if err != nil { - return "", err - } - err = os.Rename(outputMp4Temp, outputMp4) - if err != nil { - return "", err - } - return getFileSha256(outputMp4), nil -} - func mergeTsFileList_Raw(tsFileList []string, outputTs string) (hash string, err error) { fout, err := os.Create(outputTs) if err != nil { diff --git a/merge_other.go b/merge_other.go deleted file mode 100644 index a9cb4a7..0000000 --- a/merge_other.go +++ /dev/null @@ -1,9 +0,0 @@ -//+build !windows - -package m3u8d - -import "os/exec" - -func setupCmd(cmd *exec.Cmd) { - -} diff --git a/merge_windows.go b/merge_windows.go deleted file mode 100644 index 05fb383..0000000 --- a/merge_windows.go +++ /dev/null @@ -1,14 +0,0 @@ -//+build windows - -package m3u8d - -import ( - "os/exec" - "syscall" -) - -func setupCmd(cmd *exec.Cmd) { - cmd.SysProcAttr = &syscall.SysProcAttr{ - HideWindow: true, - } -} \ No newline at end of file