增加支持使用mkvmerge混流
This commit is contained in:
parent
ef43b70409
commit
279d2b0e5e
|
@ -31,6 +31,8 @@ namespace N_m3u8DL_RE.Common.Entity
|
||||||
public string? VideoId { get; set; }
|
public string? VideoId { get; set; }
|
||||||
public string? SubtitleId { get; set; }
|
public string? SubtitleId { get; set; }
|
||||||
|
|
||||||
|
public string? PeriodId { get; set; }
|
||||||
|
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
public Playlist? Playlist { get; set; }
|
public Playlist? Playlist { get; set; }
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace N_m3u8DL_RE.Common.Resource
|
||||||
public static string cmd_decryptionBinaryPath { get => GetText("cmd_decryptionBinaryPath"); }
|
public static string cmd_decryptionBinaryPath { get => GetText("cmd_decryptionBinaryPath"); }
|
||||||
public static string cmd_delAfterDone { get => GetText("cmd_delAfterDone"); }
|
public static string cmd_delAfterDone { get => GetText("cmd_delAfterDone"); }
|
||||||
public static string cmd_ffmpegBinaryPath { get => GetText("cmd_ffmpegBinaryPath"); }
|
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_header { get => GetText("cmd_header"); }
|
||||||
public static string cmd_Input { get => GetText("cmd_Input"); }
|
public static string cmd_Input { get => GetText("cmd_Input"); }
|
||||||
public static string cmd_keys { get => GetText("cmd_keys"); }
|
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_uiLanguage { get => GetText("cmd_uiLanguage"); }
|
||||||
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
|
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
|
||||||
public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
|
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_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
|
||||||
public static string cmd_writeMetaJson { get => GetText("cmd_writeMetaJson"); }
|
public static string cmd_writeMetaJson { get => GetText("cmd_writeMetaJson"); }
|
||||||
public static string fetch { get => GetText("fetch"); }
|
public static string fetch { get => GetText("fetch"); }
|
||||||
|
|
|
@ -100,6 +100,12 @@ namespace N_m3u8DL_RE.Common.Resource
|
||||||
zhTW: "ffmpeg可執行程序全路徑, 例如 C:\\Tools\\ffmpeg.exe",
|
zhTW: "ffmpeg可執行程序全路徑, 例如 C:\\Tools\\ffmpeg.exe",
|
||||||
enUS: "Full path to the ffmpeg binary, like 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
|
["cmd_header"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
|
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
|
["cmd_useShakaPackager"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "使用shaka-packager替代mp4decrypt",
|
zhCN: "解密时使用shaka-packager替代mp4decrypt",
|
||||||
zhTW: "使用shaka-packager替代mp4decrypt",
|
zhTW: "解密时使用shaka-packager替代mp4decrypt",
|
||||||
enUS: "Use shaka-packager instead of 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
|
["cmd_muxAfterDone"] = new TextContainer
|
||||||
(
|
(
|
||||||
|
|
|
@ -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> 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> 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> 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?> 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?> 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>
|
class MyOptionBinder : BinderBase<MyOption>
|
||||||
{
|
{
|
||||||
|
@ -75,6 +77,7 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
|
KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
|
||||||
DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
|
DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
|
||||||
MuxAfterDone = bindingContext.ParseResult.GetValueForOption(MuxAfterDone),
|
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,
|
Input, TmpDir, SaveDir, SaveName, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
||||||
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
||||||
FFmpegBinaryPath,
|
FFmpegBinaryPath, MkvmergeBinaryPath,
|
||||||
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
|
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, UseMkvmerge, MP4RealTimeDecryption
|
||||||
};
|
};
|
||||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||||
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
||||||
|
|
|
@ -90,6 +90,10 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MuxAfterDone { get; set; }
|
public bool MuxAfterDone { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.UseMkvmerge"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseMkvmerge { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SubtitleFormat SubtitleFormat { get; set; }
|
public SubtitleFormat SubtitleFormat { get; set; }
|
||||||
|
@ -121,5 +125,9 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
|
/// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? FFmpegBinaryPath { get; set; }
|
public string? FFmpegBinaryPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string? MkvmergeBinaryPath { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,6 +34,8 @@ namespace N_m3u8DL_RE.Config
|
||||||
KeyTextFile = option.KeyTextFile;
|
KeyTextFile = option.KeyTextFile;
|
||||||
DownloadRetryCount = option.DownloadRetryCount;
|
DownloadRetryCount = option.DownloadRetryCount;
|
||||||
MuxAfterDone = option.MuxAfterDone;
|
MuxAfterDone = option.MuxAfterDone;
|
||||||
|
UseMkvmerge = option.UseMkvmerge;
|
||||||
|
MkvmergeBinaryPath = option.MkvmergeBinaryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -97,6 +99,10 @@ namespace N_m3u8DL_RE.Config
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MuxAfterDone { get; set; }
|
public bool MuxAfterDone { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// 使用mkvmerge混流
|
||||||
|
/// </summary>
|
||||||
|
public bool UseMkvmerge { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// MP4解密所用工具的全路径
|
/// MP4解密所用工具的全路径
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? DecryptionBinaryPath { get; set; }
|
public string? DecryptionBinaryPath { get; set; }
|
||||||
|
@ -123,5 +129,9 @@ namespace N_m3u8DL_RE.Config
|
||||||
/// ffmpeg路径
|
/// ffmpeg路径
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? FFmpegBinaryPath { get; set; }
|
public string? FFmpegBinaryPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// mkvmerge路径
|
||||||
|
/// </summary>
|
||||||
|
public string? MkvmergeBinaryPath { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,7 +568,9 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var outName = $"{DownloaderConfig.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}";
|
var outName = $"{DownloaderConfig.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}";
|
||||||
var outPath = Path.Combine(saveDir, outName);
|
var outPath = Path.Combine(saveDir, outName);
|
||||||
Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}.mkv[/]");
|
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)
|
if (result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,11 +51,22 @@ namespace N_m3u8DL_RE
|
||||||
if (option.FFmpegBinaryPath == null)
|
if (option.FFmpegBinaryPath == null)
|
||||||
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(option.FFmpegBinaryPath))
|
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(ResString.ffmpegNotFound);
|
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)
|
if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,6 +50,8 @@ namespace N_m3u8DL_RE.Util
|
||||||
|
|
||||||
private static void InvokeFFmpeg(string binary, string command, string workingDirectory)
|
private static void InvokeFFmpeg(string binary, string command, string workingDirectory)
|
||||||
{
|
{
|
||||||
|
Logger.DebugMarkUp($"{binary}: {command}");
|
||||||
|
|
||||||
using var p = new Process();
|
using var p = new Process();
|
||||||
p.StartInfo = new ProcessStartInfo()
|
p.StartInfo = new ProcessStartInfo()
|
||||||
{
|
{
|
||||||
|
@ -129,8 +131,6 @@ namespace N_m3u8DL_RE.Util
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.DebugMarkUp($"{binary}: {command}");
|
|
||||||
|
|
||||||
InvokeFFmpeg(binary, command.ToString(), Path.GetDirectoryName(files[0])!);
|
InvokeFFmpeg(binary, command.ToString(), Path.GetDirectoryName(files[0])!);
|
||||||
|
|
||||||
if (File.Exists($"{outputPath}.{muxFormat}") && new FileInfo($"{outputPath}.{muxFormat}").Length > 0)
|
if (File.Exists($"{outputPath}.{muxFormat}") && new FileInfo($"{outputPath}.{muxFormat}").Length > 0)
|
||||||
|
@ -162,8 +162,7 @@ namespace N_m3u8DL_RE.Util
|
||||||
//LANG and NAME
|
//LANG and NAME
|
||||||
for (int i = 0; i < files.Length; i++)
|
for (int i = 0; i < files.Length; i++)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(files[i].LangCode))
|
command.Append($" -metadata:s:{i} language={files[i].LangCode ?? "und"} ");
|
||||||
command.Append($" -metadata:s:{i} language={files[i].LangCode} ");
|
|
||||||
if (!string.IsNullOrEmpty(files[i].Description))
|
if (!string.IsNullOrEmpty(files[i].Description))
|
||||||
command.Append($" -metadata:s:{i} title={files[i].Description} ");
|
command.Append($" -metadata:s:{i} title={files[i].Description} ");
|
||||||
}
|
}
|
||||||
|
@ -177,5 +176,28 @@ namespace N_m3u8DL_RE.Util
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue