增加支持使用mkvmerge混流

This commit is contained in:
nilaoda 2022-08-21 20:55:39 +08:00
parent ef43b70409
commit 279d2b0e5e
9 changed files with 83 additions and 11 deletions

View File

@ -31,6 +31,8 @@ namespace N_m3u8DL_RE.Common.Entity
public string? VideoId { get; set; }
public string? SubtitleId { get; set; }
public string? PeriodId { get; set; }
public string Url { get; set; }
public Playlist? Playlist { get; set; }

View File

@ -23,6 +23,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_decryptionBinaryPath { get => GetText("cmd_decryptionBinaryPath"); }
public static string cmd_delAfterDone { get => GetText("cmd_delAfterDone"); }
public static string cmd_ffmpegBinaryPath { get => GetText("cmd_ffmpegBinaryPath"); }
public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); }
public static string cmd_header { get => GetText("cmd_header"); }
public static string cmd_Input { get => GetText("cmd_Input"); }
public static string cmd_keys { get => GetText("cmd_keys"); }
@ -44,6 +45,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_uiLanguage { get => GetText("cmd_uiLanguage"); }
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
public static string cmd_useMkvmerge { get => GetText("cmd_useMkvmerge"); }
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
public static string cmd_writeMetaJson { get => GetText("cmd_writeMetaJson"); }
public static string fetch { get => GetText("fetch"); }

View File

@ -100,6 +100,12 @@ namespace N_m3u8DL_RE.Common.Resource
zhTW: "ffmpeg可執行程序全路徑, 例如 C:\\Tools\\ffmpeg.exe",
enUS: "Full path to the ffmpeg binary, like C:\\Tools\\ffmpeg.exe"
),
["cmd_mkvmergeBinaryPath"] = new TextContainer
(
zhCN: "mkvmerge可执行程序全路径, 例如 C:\\Tools\\mkvmerge.exe",
zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe",
enUS: "Full path to the mkvmerge binary, like C:\\Tools\\mkvmerge.exe"
),
["cmd_header"] = new TextContainer
(
zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
@ -216,9 +222,15 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_useShakaPackager"] = new TextContainer
(
zhCN: "使用shaka-packager替代mp4decrypt",
zhTW: "使用shaka-packager替代mp4decrypt",
enUS: "Use shaka-packager instead of mp4decrypt"
zhCN: "解密时使用shaka-packager替代mp4decrypt",
zhTW: "解密时使用shaka-packager替代mp4decrypt",
enUS: "Use shaka-packager instead of mp4decrypt to decrypt"
),
["cmd_useMkvmerge"] = new TextContainer
(
zhCN: "混流时使用mkvmerge替代ffmpeg",
zhTW: "混流时使用mkvmerge替代ffmpeg",
enUS: "Use mkvmerge instead of ffmpeg to mux"
),
["cmd_muxAfterDone"] = new TextContainer
(

View File

@ -37,8 +37,10 @@ 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<bool> MuxAfterDone = new (new string[] { "--mux-after-done" }, description: ResString.cmd_muxAfterDone, getDefaultValue: () => false);
private readonly static Option<bool> UseMkvmerge = new(new string[] { "--use-mkvmerge" }, description: ResString.cmd_useMkvmerge, 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);
private readonly static Option<string?> MkvmergeBinaryPath = new(new string[] { "--mkvmerge-binary-path" }, description: ResString.cmd_mkvmergeBinaryPath);
class MyOptionBinder : BinderBase<MyOption>
{
@ -75,6 +77,7 @@ namespace N_m3u8DL_RE.CommandLine
KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
MuxAfterDone = bindingContext.ParseResult.GetValueForOption(MuxAfterDone),
UseMkvmerge = bindingContext.ParseResult.GetValueForOption(UseMkvmerge),
};
@ -97,8 +100,8 @@ namespace N_m3u8DL_RE.CommandLine
{
Input, TmpDir, SaveDir, SaveName, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
FFmpegBinaryPath,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
FFmpegBinaryPath, MkvmergeBinaryPath,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, UseMkvmerge, MP4RealTimeDecryption
};
rootCommand.TreatUnmatchedTokensAsErrors = true;
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());

View File

@ -90,6 +90,10 @@ namespace N_m3u8DL_RE.CommandLine
/// </summary>
public bool MuxAfterDone { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.UseMkvmerge"/>.
/// </summary>
public bool UseMkvmerge { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
/// </summary>
public SubtitleFormat SubtitleFormat { get; set; }
@ -121,5 +125,9 @@ namespace N_m3u8DL_RE.CommandLine
/// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
/// </summary>
public string? FFmpegBinaryPath { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
/// </summary>
public string? MkvmergeBinaryPath { get; set; }
}
}

View File

@ -34,6 +34,8 @@ namespace N_m3u8DL_RE.Config
KeyTextFile = option.KeyTextFile;
DownloadRetryCount = option.DownloadRetryCount;
MuxAfterDone = option.MuxAfterDone;
UseMkvmerge = option.UseMkvmerge;
MkvmergeBinaryPath = option.MkvmergeBinaryPath;
}
/// <summary>
@ -97,6 +99,10 @@ namespace N_m3u8DL_RE.Config
/// </summary>
public bool MuxAfterDone { get; set; }
/// <summary>
/// 使用mkvmerge混流
/// </summary>
public bool UseMkvmerge { get; set; }
/// <summary>
/// MP4解密所用工具的全路径
/// </summary>
public string? DecryptionBinaryPath { get; set; }
@ -123,5 +129,9 @@ namespace N_m3u8DL_RE.Config
/// ffmpeg路径
/// </summary>
public string? FFmpegBinaryPath { get; set; }
/// <summary>
/// mkvmerge路径
/// </summary>
public string? MkvmergeBinaryPath { get; set; }
}
}

View File

@ -568,7 +568,9 @@ namespace N_m3u8DL_RE.DownloadManager
var outName = $"{DownloaderConfig.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}";
var outPath = Path.Combine(saveDir, outName);
Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}.mkv[/]");
var result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath);
var result = false;
if (DownloaderConfig.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath);
//完成后删除各轨道文件
if (result)
{

View File

@ -51,11 +51,22 @@ namespace N_m3u8DL_RE
if (option.FFmpegBinaryPath == null)
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
if (string.IsNullOrEmpty(option.FFmpegBinaryPath))
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
{
throw new FileNotFoundException(ResString.ffmpegNotFound);
}
//预先检查mkvmerge
if (option.UseMkvmerge && option.MuxAfterDone)
{
if (option.MkvmergeBinaryPath == null)
option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
{
throw new FileNotFoundException("mkvmerge not found");
}
}
//预先检查
if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
{

View File

@ -50,6 +50,8 @@ namespace N_m3u8DL_RE.Util
private static void InvokeFFmpeg(string binary, string command, string workingDirectory)
{
Logger.DebugMarkUp($"{binary}: {command}");
using var p = new Process();
p.StartInfo = new ProcessStartInfo()
{
@ -129,8 +131,6 @@ namespace N_m3u8DL_RE.Util
break;
}
Logger.DebugMarkUp($"{binary}: {command}");
InvokeFFmpeg(binary, command.ToString(), Path.GetDirectoryName(files[0])!);
if (File.Exists($"{outputPath}.{muxFormat}") && new FileInfo($"{outputPath}.{muxFormat}").Length > 0)
@ -162,8 +162,7 @@ namespace N_m3u8DL_RE.Util
//LANG and NAME
for (int i = 0; i < files.Length; i++)
{
if (!string.IsNullOrEmpty(files[i].LangCode))
command.Append($" -metadata:s:{i} language={files[i].LangCode} ");
command.Append($" -metadata:s:{i} language={files[i].LangCode ?? "und"} ");
if (!string.IsNullOrEmpty(files[i].Description))
command.Append($" -metadata:s:{i} title={files[i].Description} ");
}
@ -177,5 +176,28 @@ namespace N_m3u8DL_RE.Util
return false;
}
public static bool MuxInputsByMkvmerge(string binary, OutputFile[] files, string outputPath)
{
StringBuilder command = new StringBuilder($"-q --output \"{outputPath}.mkv\" ");
command.Append(" --no-chapters ");
//LANG and NAME
for (int i = 0; i < files.Length; i++)
{
command.Append($" --language 0:{files[i].LangCode ?? "und"} ");
if (!string.IsNullOrEmpty(files[i].Description))
command.Append($" --track-name {i}:\"{files[i].Description}\" ");
command.Append($" \"{files[i].FilePath}\" ");
}
InvokeFFmpeg(binary, command.ToString(), Environment.CurrentDirectory);
if (File.Exists($"{outputPath}.mkv") && new FileInfo($"{outputPath}.mkv").Length > 1024)
return true;
return false;
}
}
}