增加ffmpeg合并且默认启用
This commit is contained in:
parent
9864b2560e
commit
7ef1f21c51
|
@ -60,6 +60,15 @@ namespace N_m3u8DL_RE.Common.Resource {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 fMP4 is detected, binary merging is automatically enabled 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string autoBinaryMerge {
|
||||
get {
|
||||
return ResourceManager.GetString("autoBinaryMerge", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Bad m3u8 的本地化字符串。
|
||||
/// </summary>
|
||||
|
@ -141,6 +150,15 @@ namespace N_m3u8DL_RE.Common.Resource {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Full path to the ffmpeg binary, like C:\Tools\ffmpeg.exe 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string cmd_ffmpegBinaryPath {
|
||||
get {
|
||||
return ResourceManager.GetString("cmd_ffmpegBinaryPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Pass custom header(s) to server, Example:
|
||||
///-H "Cookie: mycookie" -H "User-Agent: iOS" 的本地化字符串。
|
||||
|
@ -332,6 +350,15 @@ namespace N_m3u8DL_RE.Common.Resource {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 ffmpeg merging... 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ffmpegMerge {
|
||||
get {
|
||||
return ResourceManager.GetString("ffmpegMerge", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Extracting TTML(raw) subtitle... 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
|
@ -249,4 +249,13 @@
|
|||
<data name="cmd_loadKeyFailed" xml:space="preserve">
|
||||
<value>Failed to get KEY, ignore.</value>
|
||||
</data>
|
||||
<data name="autoBinaryMerge" xml:space="preserve">
|
||||
<value>fMP4 is detected, binary merging is automatically enabled</value>
|
||||
</data>
|
||||
<data name="cmd_ffmpegBinaryPath" xml:space="preserve">
|
||||
<value>Full path to the ffmpeg binary, like C:\Tools\ffmpeg.exe</value>
|
||||
</data>
|
||||
<data name="ffmpegMerge" xml:space="preserve">
|
||||
<value>ffmpeg merging...</value>
|
||||
</data>
|
||||
</root>
|
|
@ -269,4 +269,13 @@
|
|||
<data name="cmd_loadKeyFailed" xml:space="preserve">
|
||||
<value>获取KEY失败,忽略读取.</value>
|
||||
</data>
|
||||
<data name="autoBinaryMerge" xml:space="preserve">
|
||||
<value>检测到fMP4,自动开启二进制合并</value>
|
||||
</data>
|
||||
<data name="cmd_ffmpegBinaryPath" xml:space="preserve">
|
||||
<value>ffmpeg可执行程序全路径, 例如 C:\Tools\ffmpeg.exe</value>
|
||||
</data>
|
||||
<data name="ffmpegMerge" xml:space="preserve">
|
||||
<value>调用ffmpeg合并中...</value>
|
||||
</data>
|
||||
</root>
|
|
@ -246,4 +246,13 @@
|
|||
<data name="cmd_loadKeyFailed" xml:space="preserve">
|
||||
<value>獲取KEY失敗,忽略讀取.</value>
|
||||
</data>
|
||||
<data name="autoBinaryMerge" xml:space="preserve">
|
||||
<value>檢測到fMP4,自動開啟二進位制合併</value>
|
||||
</data>
|
||||
<data name="cmd_ffmpegBinaryPath" xml:space="preserve">
|
||||
<value>ffmpeg可執行程序全路徑, 例如 C:\Tools\ffmpeg.exe</value>
|
||||
</data>
|
||||
<data name="ffmpegMerge" xml:space="preserve">
|
||||
<value>調用ffmpeg合併中...</value>
|
||||
</data>
|
||||
</root>
|
|
@ -2,14 +2,8 @@
|
|||
using N_m3u8DL_RE.Common.Entity;
|
||||
using N_m3u8DL_RE.Common.Log;
|
||||
using N_m3u8DL_RE.Common.Resource;
|
||||
using N_m3u8DL_RE.Parser.Util;
|
||||
using N_m3u8DL_RE.Parser.Constants;
|
||||
using N_m3u8DL_RE.Parser.Extractor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using N_m3u8DL_RE.Common.Util;
|
||||
|
||||
namespace N_m3u8DL_RE.Parser
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
private readonly static Option<int> ThreadCount = new(new string[] { "--thread-count" }, description: ResString.cmd_threadCount, getDefaultValue: () => 8);
|
||||
private readonly static Option<bool> SkipMerge = new(new string[] { "--skip-merge" }, description: ResString.cmd_skipMerge, getDefaultValue: () => false);
|
||||
private readonly static Option<bool> SkipDownload = new(new string[] { "--skip-download" }, description: ResString.cmd_skipDownload, getDefaultValue: () => false);
|
||||
private readonly static Option<bool> BinaryMerge = new(new string[] { "--binary-merge" }, description: ResString.cmd_binaryMerge, getDefaultValue: () => true);
|
||||
private readonly static Option<bool> BinaryMerge = new(new string[] { "--binary-merge" }, description: ResString.cmd_binaryMerge, getDefaultValue: () => false);
|
||||
private readonly static Option<bool> DelAfterDone = new(new string[] { "--del-after-done" }, description: ResString.cmd_delAfterDone, getDefaultValue: () => true);
|
||||
private readonly static Option<bool> AutoSubtitleFix = new(new string[] { "--auto-subtitle-fix" }, description: ResString.cmd_subtitleFix, getDefaultValue: () => true);
|
||||
private readonly static Option<bool> CheckSegmentsCount = new(new string[] { "--check-segments-count" }, description: ResString.cmd_checkSegmentsCount, getDefaultValue: () => true);
|
||||
|
@ -35,6 +35,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
private readonly static Option<bool> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
|
||||
private readonly static Option<bool> UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
|
||||
private readonly static Option<string?> DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath);
|
||||
private readonly static Option<string?> FFmpegBinaryPath = new(new string[] { "--ffmpeg-binary-path" }, description: ResString.cmd_ffmpegBinaryPath);
|
||||
|
||||
class MyOptionBinder : BinderBase<MyOption>
|
||||
{
|
||||
|
@ -67,6 +68,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
|
||||
UseShakaPackager = bindingContext.ParseResult.GetValueForOption(UseShakaPackager),
|
||||
DecryptionBinaryPath = bindingContext.ParseResult.GetValueForOption(DecryptionBinaryPath),
|
||||
FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
|
||||
};
|
||||
|
||||
//在这里设置语言
|
||||
|
@ -91,10 +93,11 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
|
||||
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
||||
{
|
||||
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220726")
|
||||
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220808")
|
||||
{
|
||||
Input, TmpDir, SaveDir, SaveName, ThreadCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
||||
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
||||
FFmpegBinaryPath,
|
||||
LogLevel, UILanguage, UrlProcessorArgs, Keys, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
|
||||
};
|
||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||
|
|
|
@ -105,5 +105,9 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
/// See: <see cref="CommandInvoker.DecryptionBinaryPath"/>.
|
||||
/// </summary>
|
||||
public string? DecryptionBinaryPath { get; set; }
|
||||
/// <summary>
|
||||
/// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
|
||||
/// </summary>
|
||||
public string? FFmpegBinaryPath { get; set; }
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ namespace N_m3u8DL_RE.Config
|
|||
MP4RealTimeDecryption = option.MP4RealTimeDecryption;
|
||||
UseShakaPackager = option.UseShakaPackager;
|
||||
DecryptionBinaryPath = option.DecryptionBinaryPath;
|
||||
FFmpegBinaryPath = option.FFmpegBinaryPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,5 +104,9 @@ namespace N_m3u8DL_RE.Config
|
|||
/// 解密KEYs
|
||||
/// </summary>
|
||||
public string[]? Keys { get; set; }
|
||||
/// <summary>
|
||||
/// ffmpeg路径
|
||||
/// </summary>
|
||||
public string? FFmpegBinaryPath { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Mp4SubtitleParser;
|
||||
using N_m3u8DL_RE.Common.Entity;
|
||||
using N_m3u8DL_RE.Common.Enum;
|
||||
using N_m3u8DL_RE.Common.Log;
|
||||
using N_m3u8DL_RE.Common.Resource;
|
||||
using N_m3u8DL_RE.Config;
|
||||
|
@ -335,15 +336,24 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
//合并
|
||||
if (!DownloaderConfig.SkipMerge)
|
||||
{
|
||||
//对于fMP4,自动开启二进制合并
|
||||
if (mp4InitFile != "")
|
||||
{
|
||||
DownloaderConfig.BinaryMerge = true;
|
||||
Logger.WarnMarkUp($"[white on darkorange3_1]{ResString.autoBinaryMerge}[/]");
|
||||
}
|
||||
|
||||
if (DownloaderConfig.BinaryMerge)
|
||||
{
|
||||
Logger.InfoMarkUp(ResString.binaryMerge);
|
||||
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
||||
DownloadUtil.CombineMultipleFilesIntoSingleFile(files, output);
|
||||
MergeUtil.CombineMultipleFilesIntoSingleFile(files, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
||||
Logger.InfoMarkUp(ResString.ffmpegMerge);
|
||||
MergeUtil.MergeByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, files, Path.ChangeExtension(output, null), "mp4");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,16 @@ namespace N_m3u8DL_RE
|
|||
|
||||
try
|
||||
{
|
||||
//预先检查ffmpeg
|
||||
if (!option.BinaryMerge)
|
||||
{
|
||||
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
||||
if (string.IsNullOrEmpty(option.FFmpegBinaryPath))
|
||||
{
|
||||
throw new FileNotFoundException(option.FFmpegBinaryPath);
|
||||
}
|
||||
}
|
||||
|
||||
//预先检查
|
||||
if (option.Keys != null && option.Keys.Length > 0)
|
||||
{
|
||||
|
|
|
@ -67,38 +67,5 @@ namespace N_m3u8DL_RE.Util
|
|||
ActualFilePath = path
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输入一堆已存在的文件,合并到新文件
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="outputFilePath"></param>
|
||||
public static void CombineMultipleFilesIntoSingleFile(string[] files, string outputFilePath)
|
||||
{
|
||||
if (files.Length == 0) return;
|
||||
if (files.Length == 1)
|
||||
{
|
||||
FileInfo fi = new FileInfo(files[0]);
|
||||
fi.CopyTo(outputFilePath, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(outputFilePath)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)!);
|
||||
|
||||
string[] inputFilePaths = files;
|
||||
using (var outputStream = File.Create(outputFilePath))
|
||||
{
|
||||
foreach (var inputFilePath in inputFilePaths)
|
||||
{
|
||||
if (inputFilePath == "")
|
||||
continue;
|
||||
using (var inputStream = File.OpenRead(inputFilePath))
|
||||
{
|
||||
inputStream.CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace N_m3u8DL_RE.Util
|
|||
if (init != "")
|
||||
{
|
||||
tmpFile = Path.ChangeExtension(source, ".itmp");
|
||||
DownloadUtil.CombineMultipleFilesIntoSingleFile(new string[] { init, source }, tmpFile);
|
||||
MergeUtil.CombineMultipleFilesIntoSingleFile(new string[] { init, source }, tmpFile);
|
||||
enc = tmpFile;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace N_m3u8DL_RE.Util
|
||||
{
|
||||
internal class MergeUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 输入一堆已存在的文件,合并到新文件
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <param name="outputFilePath"></param>
|
||||
public static void CombineMultipleFilesIntoSingleFile(string[] files, string outputFilePath)
|
||||
{
|
||||
if (files.Length == 0) return;
|
||||
if (files.Length == 1)
|
||||
{
|
||||
FileInfo fi = new FileInfo(files[0]);
|
||||
fi.CopyTo(outputFilePath, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(Path.GetDirectoryName(outputFilePath)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)!);
|
||||
|
||||
string[] inputFilePaths = files;
|
||||
using (var outputStream = File.Create(outputFilePath))
|
||||
{
|
||||
foreach (var inputFilePath in inputFilePaths)
|
||||
{
|
||||
if (inputFilePath == "")
|
||||
continue;
|
||||
using (var inputStream = File.OpenRead(inputFilePath))
|
||||
{
|
||||
inputStream.CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void MergeByFFmpeg(string binary, string[] files, string outputPath, string muxFormat,
|
||||
bool fastStart = false,
|
||||
bool writeDate = true, string poster = "", string audioName = "", string title = "",
|
||||
string copyright = "", string comment = "", string encodingTool = "", string recTime = "")
|
||||
{
|
||||
string dateString = string.IsNullOrEmpty(recTime) ? DateTime.Now.ToString("o") : recTime;
|
||||
|
||||
bool useAACFilter = true;
|
||||
StringBuilder command = new StringBuilder("-loglevel warning -i concat:\"");
|
||||
string ddpAudio = string.Empty;
|
||||
string addPoster = "-map 1 -c:v:1 copy -disposition:v:1 attached_pic";
|
||||
ddpAudio = (File.Exists($"{Path.GetFileNameWithoutExtension(outputPath + ".mp4")}.txt") ? File.ReadAllText($"{Path.GetFileNameWithoutExtension(outputPath + ".mp4")}.txt") : "");
|
||||
if (!string.IsNullOrEmpty(ddpAudio)) useAACFilter = false;
|
||||
|
||||
foreach (string t in files)
|
||||
{
|
||||
command.Append(Path.GetFileName(t) + "|");
|
||||
}
|
||||
|
||||
switch (muxFormat.ToUpper())
|
||||
{
|
||||
case ("MP4"):
|
||||
command.Append("\" " + (string.IsNullOrEmpty(poster) ? "" : "-i \"" + poster + "\""));
|
||||
command.Append(" " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\""));
|
||||
command.Append(
|
||||
$" -map 0:v? {(string.IsNullOrEmpty(ddpAudio) ? "-map 0:a?" : $"-map {(string.IsNullOrEmpty(poster) ? "1" : "2")}:a -map 0:a?")} -map 0:s? " + (string.IsNullOrEmpty(poster) ? "" : addPoster)
|
||||
+ (writeDate ? " -metadata date=\"" + dateString + "\"" : "") +
|
||||
" -metadata encoding_tool=\"" + encodingTool + "\" -metadata title=\"" + title +
|
||||
"\" -metadata copyright=\"" + copyright + "\" -metadata comment=\"" + comment +
|
||||
$"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler_name=\"" + audioName + $"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler=\"" + audioName + "\" ");
|
||||
command.Append(string.IsNullOrEmpty(ddpAudio) ? "" : " -metadata:s:a:0 handler_name=\"DD+\" -metadata:s:a:0 handler=\"DD+\" ");
|
||||
if (fastStart)
|
||||
command.Append("-movflags +faststart");
|
||||
command.Append(" -c copy -y " + (useAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + outputPath + ".mp4\"");
|
||||
break;
|
||||
case ("MKV"):
|
||||
command.Append("\" -map 0 -c copy -y " + (useAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + outputPath + ".mkv\"");
|
||||
break;
|
||||
case ("FLV"):
|
||||
command.Append("\" -map 0 -c copy -y " + (useAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + outputPath + ".flv\"");
|
||||
break;
|
||||
case ("TS"):
|
||||
command.Append("\" -map 0 -c copy -y -f mpegts -bsf:v h264_mp4toannexb \"" + outputPath + ".ts\"");
|
||||
break;
|
||||
case ("VTT"):
|
||||
command.Append("\" -map 0 -y \"" + outputPath + ".srt\""); //Convert To Srt
|
||||
break;
|
||||
case ("EAC3"):
|
||||
command.Append("\" -map 0:a -c copy -y \"" + outputPath + ".eac3\"");
|
||||
break;
|
||||
case ("AAC"):
|
||||
command.Append("\" -map 0:a -c copy -y \"" + outputPath + ".m4a\"");
|
||||
break;
|
||||
case ("AC3"):
|
||||
command.Append("\" -map 0:a -c copy -y \"" + outputPath + ".ac3\"");
|
||||
break;
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
WorkingDirectory = Path.GetDirectoryName(files[0]),
|
||||
FileName = binary,
|
||||
Arguments = command.ToString(),
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false
|
||||
})!.WaitForExit();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue