增加复杂混流选项配置;增加支持引入外部文件混流
This commit is contained in:
parent
df63b42356
commit
78473cebb8
|
@ -27,6 +27,7 @@ namespace N_m3u8DL_RE.Common.Resource
|
||||||
public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); }
|
public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); }
|
||||||
public static string cmd_baseUrl { get => GetText("cmd_baseUrl"); }
|
public static string cmd_baseUrl { get => GetText("cmd_baseUrl"); }
|
||||||
public static string cmd_header { get => GetText("cmd_header"); }
|
public static string cmd_header { get => GetText("cmd_header"); }
|
||||||
|
public static string cmd_muxImport { get => GetText("cmd_muxImport"); }
|
||||||
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"); }
|
||||||
public static string cmd_keyText { get => GetText("cmd_keyText"); }
|
public static string cmd_keyText { get => GetText("cmd_keyText"); }
|
||||||
|
|
|
@ -246,9 +246,72 @@ namespace N_m3u8DL_RE.Common.Resource
|
||||||
),
|
),
|
||||||
["cmd_muxAfterDone"] = new TextContainer
|
["cmd_muxAfterDone"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "所有工作完成时尝试使用ffmpeg混流分离的音视频(默认容器: mkv)",
|
zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
|
||||||
zhTW: "所有工作完成時嘗試使用ffmpeg混流分離的影音(默認容器: mkv)",
|
"* format=FORMAT: 指定混流容器 mkv, mp4\r\n" +
|
||||||
enUS: "When all works is done, try to use ffmpeg to mux the separated streams.(Default container: mkv)"
|
"* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默认: ffmpeg)\r\n" +
|
||||||
|
"* bin_path=PATH: 指定程序路径 (默认: 自动寻找)\r\n" +
|
||||||
|
"* keep=BOOL: 混流完成是否删除文件 true, false (默认: true)\r\n\r\n" +
|
||||||
|
"例如: \r\n" +
|
||||||
|
"\r\n#混流为mp4容器\r\n" +
|
||||||
|
"-M format=mp4\r\n" +
|
||||||
|
"\r\n#使用mkvmerge, 自动寻找程序\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge\r\n" +
|
||||||
|
"\r\n#使用mkvmerge, 自定义程序路径\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
|
||||||
|
zhTW: "所有工作完成時嘗試混流分離的影音. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
|
||||||
|
"* format=FORMAT: 指定混流容器 mkv, mp4\r\n" +
|
||||||
|
"* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默認: ffmpeg)\r\n" +
|
||||||
|
"* bin_path=PATH: 指定程序路徑 (默認: 自動尋找)\r\n" +
|
||||||
|
"* keep=BOOL: 混流完成是否刪除文件 true, false (默認: true)\r\n\r\n" +
|
||||||
|
"例如: \r\n" +
|
||||||
|
"\r\n#混流為mp4容器\r\n" +
|
||||||
|
"-M format=mp4\r\n" +
|
||||||
|
"\r\n#使用mkvmerge, 自動尋找程序\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge\r\n" +
|
||||||
|
"\r\n#使用mkvmerge, 自訂程序路徑\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
|
||||||
|
enUS: "When all works is done, try to mux the downloaded streams. OPTIONS is a colon separated list of:\r\n\r\n" +
|
||||||
|
"* format=FORMAT: set container. mkv, mp4\r\n" +
|
||||||
|
"* muxer=MUXER: set muxer. ffmpeg, mkvmerge (Default: ffmpeg)\r\n" +
|
||||||
|
"* bin_path=PATH: set binary file path. (Default: auto)\r\n" +
|
||||||
|
"* keep=BOOL: set whether or not delete files. true, false (Default: true)\r\n\r\n" +
|
||||||
|
"Examples: \r\n" +
|
||||||
|
"\r\n#mux to mp4\r\n" +
|
||||||
|
"-M format=mp4\r\n" +
|
||||||
|
"\r\n#use mkvmerge, auto detect bin path\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge\r\n" +
|
||||||
|
"\r\n#use mkvmerge, set bin path\r\n" +
|
||||||
|
"-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n"
|
||||||
|
),
|
||||||
|
["cmd_muxImport"] = new TextContainer
|
||||||
|
(
|
||||||
|
zhCN: "混流时引入外部媒体文件. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
|
||||||
|
"* path=PATH: 指定媒体文件路径\r\n" +
|
||||||
|
"* lang=CODE: 指定媒体文件语言代码 (非必须)\r\n" +
|
||||||
|
"* name=NAME: 指定媒体文件描述信息 (非必须)\r\n\r\n" +
|
||||||
|
"例如: \r\n" +
|
||||||
|
"\r\n#引入外部字幕\r\n" +
|
||||||
|
"--mux-import path=zh-Hans.srt:lang=chi:name=\"中文 (简体)\"\r\n" +
|
||||||
|
"\r\n#引入外部音轨+字幕\r\n" +
|
||||||
|
"--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
|
||||||
|
zhTW: "混流時引入外部媒體檔案. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
|
||||||
|
"* path=PATH: 指定媒體檔案路徑\r\n" +
|
||||||
|
"* lang=CODE: 指定媒體檔案語言代碼 (非必須)\r\n" +
|
||||||
|
"* name=NAME: 指定媒體檔案描述訊息 (非必須)\r\n\r\n" +
|
||||||
|
"例如: \r\n" +
|
||||||
|
"\r\n#引入外部字幕\r\n" +
|
||||||
|
"--mux-import path=zh-Hant.srt:lang=chi:name=\"中文 (繁體)\"\r\n" +
|
||||||
|
"\r\n#引入外部音軌+字幕\r\n" +
|
||||||
|
"--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
|
||||||
|
enUS: "When MuxAfterDone enabled, allow to import local media files. OPTIONS is a colon separated list of:\r\n\r\n" +
|
||||||
|
"* path=PATH: set file path\r\n" +
|
||||||
|
"* lang=CODE: set media language code (not required)\r\n" +
|
||||||
|
"* name=NAME: set description (not required)\r\n\r\n" +
|
||||||
|
"Examples: \r\n" +
|
||||||
|
"\r\n#import subtitle\r\n" +
|
||||||
|
"--mux-import path=en-US.srt:lang=eng:name=\"English (Original)\"\r\n" +
|
||||||
|
"\r\n#import audio and subtitle\r\n" +
|
||||||
|
"--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\""
|
||||||
),
|
),
|
||||||
["cmd_muxToMp4"] = new TextContainer
|
["cmd_muxToMp4"] = new TextContainer
|
||||||
(
|
(
|
||||||
|
|
|
@ -11,10 +11,7 @@ namespace N_m3u8DL_RE.Parser.Config
|
||||||
|
|
||||||
public string BaseUrl { get; set; }
|
public string BaseUrl { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>()
|
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
|
||||||
{
|
|
||||||
["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容前置处理器. 调用顺序与列表顺序相同
|
/// 内容前置处理器. 调用顺序与列表顺序相同
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
using N_m3u8DL_RE.Common.Log;
|
using N_m3u8DL_RE.Common.Log;
|
||||||
using N_m3u8DL_RE.Common.Resource;
|
using N_m3u8DL_RE.Common.Resource;
|
||||||
|
using N_m3u8DL_RE.Entity;
|
||||||
using N_m3u8DL_RE.Enum;
|
using N_m3u8DL_RE.Enum;
|
||||||
|
using N_m3u8DL_RE.Util;
|
||||||
using System.CommandLine;
|
using System.CommandLine;
|
||||||
using System.CommandLine.Binding;
|
using System.CommandLine.Binding;
|
||||||
|
using System.CommandLine.Parsing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -17,9 +20,9 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
private readonly static Option<string?> SavePattern = new(new string[] { "--save-pattern" }, description: ResString.cmd_savePattern, getDefaultValue: () => "<SaveName>_<Id>_<Codecs>_<Language>_<Ext>");
|
private readonly static Option<string?> SavePattern = new(new string[] { "--save-pattern" }, description: ResString.cmd_savePattern, getDefaultValue: () => "<SaveName>_<Id>_<Codecs>_<Language>_<Ext>");
|
||||||
private readonly static Option<string?> UILanguage = new Option<string?>(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage).FromAmong("en-US", "zh-CN", "zh-TW");
|
private readonly static Option<string?> UILanguage = new Option<string?>(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage).FromAmong("en-US", "zh-CN", "zh-TW");
|
||||||
private readonly static Option<string?> UrlProcessorArgs = new(new string[] { "--urlprocessor-args" }, description: ResString.cmd_urlProcessorArgs);
|
private readonly static Option<string?> UrlProcessorArgs = new(new string[] { "--urlprocessor-args" }, description: ResString.cmd_urlProcessorArgs);
|
||||||
private readonly static Option<string[]?> Keys = new(new string[] { "--key" }, description: ResString.cmd_keys) { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = false };
|
private readonly static Option<string[]?> Keys = new(new string[] { "--key" }, description: ResString.cmd_keys) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
|
||||||
private readonly static Option<string> KeyTextFile = new(new string[] { "--key-text-file" }, description: ResString.cmd_keyText);
|
private readonly static Option<string> KeyTextFile = new(new string[] { "--key-text-file" }, description: ResString.cmd_keyText);
|
||||||
private readonly static Option<string[]?> Headers = new(new string[] { "-H", "--header" }, description: ResString.cmd_header) { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = false };
|
private readonly static Option<Dictionary<string, string>> Headers = new(new string[] { "-H", "--header" }, description: ResString.cmd_header, parseArgument: ParseHeaders) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
|
||||||
private readonly static Option<LogLevel> LogLevel = new(name: "--log-level", description: ResString.cmd_logLevel, getDefaultValue: () => Common.Log.LogLevel.INFO);
|
private readonly static Option<LogLevel> LogLevel = new(name: "--log-level", description: ResString.cmd_logLevel, getDefaultValue: () => Common.Log.LogLevel.INFO);
|
||||||
private readonly static Option<SubtitleFormat> SubtitleFormat = new(name: "--sub-format", description: ResString.cmd_subFormat, getDefaultValue: () => Enum.SubtitleFormat.VTT);
|
private readonly static Option<SubtitleFormat> SubtitleFormat = new(name: "--sub-format", description: ResString.cmd_subFormat, getDefaultValue: () => Enum.SubtitleFormat.VTT);
|
||||||
private readonly static Option<bool> AutoSelect = new(new string[] { "--auto-select" }, description: ResString.cmd_autoSelect, getDefaultValue: () => false);
|
private readonly static Option<bool> AutoSelect = new(new string[] { "--auto-select" }, description: ResString.cmd_autoSelect, getDefaultValue: () => false);
|
||||||
|
@ -36,14 +39,118 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
private readonly static Option<bool> AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
|
private readonly static Option<bool> AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, 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> 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> MuxToMp4 = new (new string[] { "--mux-to-mp4" }, description: ResString.cmd_muxToMp4, 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);
|
|
||||||
private readonly static Option<string?> BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl);
|
private readonly static Option<string?> BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl);
|
||||||
|
|
||||||
|
//复杂命令行如下
|
||||||
|
private readonly static Option<MuxOptions?> MuxAfterDone = new(new string[] { "-M", "--mux-after-done" }, description: ResString.cmd_muxAfterDone, parseArgument: ParseMuxAfterDone) { ArgumentHelpName = "OPTIONS" };
|
||||||
|
private readonly static Option<List<OutputFile>> MuxImports = new("--mux-import", description: ResString.cmd_muxImport, parseArgument: ParseImports) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false, ArgumentHelpName = "OPTIONS" };
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分割Header
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static Dictionary<string, string> ParseHeaders(ArgumentResult result)
|
||||||
|
{
|
||||||
|
//默认的Headers
|
||||||
|
var headers = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
|
||||||
|
};
|
||||||
|
var array = result.Tokens.Select(t => t.Value).ToArray();
|
||||||
|
var otherHeaders = ConvertUtil.SplitHeaderArrayToDic(array);
|
||||||
|
foreach (var h in otherHeaders)
|
||||||
|
{
|
||||||
|
headers[h.Key] = h.Value;
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析混流引入的外部文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static List<OutputFile> ParseImports(ArgumentResult result)
|
||||||
|
{
|
||||||
|
var imports = new List<OutputFile>();
|
||||||
|
|
||||||
|
foreach (var item in result.Tokens)
|
||||||
|
{
|
||||||
|
var p = new ComplexParamParser(item.Value);
|
||||||
|
var path = p.GetValue("path");
|
||||||
|
var lang = p.GetValue("lang") ?? "und";
|
||||||
|
var name = p.GetValue("name");
|
||||||
|
if (string.IsNullOrEmpty(path) || !File.Exists(path))
|
||||||
|
{
|
||||||
|
result.ErrorMessage = "path empty or file not exists!";
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
imports.Add(new OutputFile()
|
||||||
|
{
|
||||||
|
FilePath = path,
|
||||||
|
LangCode = lang,
|
||||||
|
Description = name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return imports;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析混流选项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static MuxOptions? ParseMuxAfterDone(ArgumentResult result)
|
||||||
|
{
|
||||||
|
var p = new ComplexParamParser(result.Tokens.First().Value);
|
||||||
|
//混流格式
|
||||||
|
var format = p.GetValue("format");
|
||||||
|
if (format != "mp4" && format != "mkv")
|
||||||
|
{
|
||||||
|
result.ErrorMessage = $"format={format} not valid";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//混流器
|
||||||
|
var muxer = p.GetValue("muxer") ?? "ffmpeg";
|
||||||
|
if (muxer != "ffmpeg" && muxer != "mkvmerge")
|
||||||
|
{
|
||||||
|
result.ErrorMessage = $"muxer={muxer} not valid";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//混流器路径
|
||||||
|
var bin_path = p.GetValue("bin_path") ?? "auto";
|
||||||
|
if (string.IsNullOrEmpty(bin_path))
|
||||||
|
{
|
||||||
|
result.ErrorMessage = $"bin_path={bin_path} not valid";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//是否删除
|
||||||
|
var keep = p.GetValue("keep") ?? "false";
|
||||||
|
if (keep != "true" && keep != "false")
|
||||||
|
{
|
||||||
|
result.ErrorMessage = $"keep={keep} not valid";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//冲突检测
|
||||||
|
if (muxer == "mkvmerge" && format == "mp4")
|
||||||
|
{
|
||||||
|
result.ErrorMessage = $"mkvmerge can not do mp4";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new MuxOptions()
|
||||||
|
{
|
||||||
|
UseMkvmerge = muxer == "mkvmerge",
|
||||||
|
MuxToMp4 = format == "mp4",
|
||||||
|
KeepFiles = keep == "true",
|
||||||
|
BinPath = bin_path == "auto" ? null : bin_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class MyOptionBinder : BinderBase<MyOption>
|
class MyOptionBinder : BinderBase<MyOption>
|
||||||
{
|
{
|
||||||
protected override MyOption GetBoundValue(BindingContext bindingContext)
|
protected override MyOption GetBoundValue(BindingContext bindingContext)
|
||||||
|
@ -51,7 +158,7 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
var option = new MyOption
|
var option = new MyOption
|
||||||
{
|
{
|
||||||
Input = bindingContext.ParseResult.GetValueForArgument(Input),
|
Input = bindingContext.ParseResult.GetValueForArgument(Input),
|
||||||
Headers = bindingContext.ParseResult.GetValueForOption(Headers),
|
Headers = bindingContext.ParseResult.GetValueForOption(Headers)!,
|
||||||
LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel),
|
LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel),
|
||||||
AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect),
|
AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect),
|
||||||
SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge),
|
SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge),
|
||||||
|
@ -78,10 +185,8 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
|
FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
|
||||||
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),
|
|
||||||
UseMkvmerge = bindingContext.ParseResult.GetValueForOption(UseMkvmerge),
|
|
||||||
BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
|
BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
|
||||||
MuxToMp4 = bindingContext.ParseResult.GetValueForOption(MuxToMp4),
|
MuxImports = bindingContext.ParseResult.GetValueForOption(MuxImports),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,6 +198,19 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(option.UILanguage);
|
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(option.UILanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//混流设置
|
||||||
|
var muxAfterDoneValue = bindingContext.ParseResult.GetValueForOption(MuxAfterDone);
|
||||||
|
if (muxAfterDoneValue != null)
|
||||||
|
{
|
||||||
|
option.MuxAfterDone = true;
|
||||||
|
option.MuxKeepFiles = muxAfterDoneValue.KeepFiles;
|
||||||
|
option.MuxToMp4 = muxAfterDoneValue.MuxToMp4;
|
||||||
|
option.UseMkvmerge = muxAfterDoneValue.UseMkvmerge;
|
||||||
|
if (option.UseMkvmerge) option.MkvmergeBinaryPath = muxAfterDoneValue.BinPath;
|
||||||
|
else option.FFmpegBinaryPath = muxAfterDoneValue.BinPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,12 +218,13 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
|
|
||||||
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
||||||
{
|
{
|
||||||
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220823")
|
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220825")
|
||||||
{
|
{
|
||||||
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
||||||
BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, MuxToMp4, UseMkvmerge, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
||||||
FFmpegBinaryPath, MkvmergeBinaryPath,
|
FFmpegBinaryPath,
|
||||||
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption
|
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
|
||||||
|
MuxAfterDone, MuxImports
|
||||||
};
|
};
|
||||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||||
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_RE.CommandLine
|
||||||
|
{
|
||||||
|
internal class ComplexParamParser
|
||||||
|
{
|
||||||
|
private string _arg;
|
||||||
|
public ComplexParamParser(string arg)
|
||||||
|
{
|
||||||
|
_arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? GetValue(string key)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(_arg)) return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var index = _arg.IndexOf(key + "=");
|
||||||
|
if (index == -1) return _arg.IndexOf(key) != -1 ? "true" : null;
|
||||||
|
|
||||||
|
var chars = _arg[(index + key.Length + 1)..].ToCharArray();
|
||||||
|
var result = new StringBuilder();
|
||||||
|
char last = '\0';
|
||||||
|
for (int i = 0; i < chars.Length; i++)
|
||||||
|
{
|
||||||
|
if (chars[i] == ':')
|
||||||
|
{
|
||||||
|
if (last == '\\')
|
||||||
|
{
|
||||||
|
result.Replace("\\", "");
|
||||||
|
last = chars[i];
|
||||||
|
result.Append(chars[i]);
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last = chars[i];
|
||||||
|
result.Append(chars[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultStr = result.ToString().Trim().Trim('\"').Trim('\'');
|
||||||
|
|
||||||
|
//不应该有引号出现
|
||||||
|
if (resultStr.Contains('\"') || resultStr.Contains('\'')) throw new Exception();
|
||||||
|
|
||||||
|
return resultStr;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Parse Argument [{key}] failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using N_m3u8DL_RE.Common.Log;
|
using N_m3u8DL_RE.Common.Log;
|
||||||
|
using N_m3u8DL_RE.Entity;
|
||||||
using N_m3u8DL_RE.Enum;
|
using N_m3u8DL_RE.Enum;
|
||||||
|
|
||||||
namespace N_m3u8DL_RE.CommandLine
|
namespace N_m3u8DL_RE.CommandLine
|
||||||
|
@ -12,7 +13,7 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.Headers"/>.
|
/// See: <see cref="CommandInvoker.Headers"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[]? Headers { get; set; }
|
public Dictionary<string,string> Headers { get; set; } = new Dictionary<string,string>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.Keys"/>.
|
/// See: <see cref="CommandInvoker.Keys"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -137,5 +138,10 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
|
/// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? MkvmergeBinaryPath { get; set; }
|
public string? MkvmergeBinaryPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.MuxImports"/>.
|
||||||
|
/// </summary>
|
||||||
|
public List<OutputFile>? MuxImports { get; set; }
|
||||||
|
public bool MuxKeepFiles { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,132 +11,19 @@ namespace N_m3u8DL_RE.Config
|
||||||
{
|
{
|
||||||
internal class DownloaderConfig
|
internal class DownloaderConfig
|
||||||
{
|
{
|
||||||
public DownloaderConfig() { }
|
public required MyOption MyOptions { get; set; }
|
||||||
|
|
||||||
public DownloaderConfig(MyOption option)
|
|
||||||
{
|
|
||||||
AutoSubtitleFix = option.AutoSubtitleFix;
|
|
||||||
SkipMerge = option.SkipMerge;
|
|
||||||
BinaryMerge = option.BinaryMerge;
|
|
||||||
DelAfterDone = option.DelAfterDone;
|
|
||||||
CheckSegmentsCount = option.CheckSegmentsCount;
|
|
||||||
SubtitleFormat = option.SubtitleFormat;
|
|
||||||
TmpDir = option.TmpDir;
|
|
||||||
SaveName = option.SaveName;
|
|
||||||
SaveDir = option.SaveDir;
|
|
||||||
ThreadCount = option.ThreadCount;
|
|
||||||
SavePattern = option.SavePattern;
|
|
||||||
Keys = option.Keys;
|
|
||||||
MP4RealTimeDecryption = option.MP4RealTimeDecryption;
|
|
||||||
UseShakaPackager = option.UseShakaPackager;
|
|
||||||
DecryptionBinaryPath = option.DecryptionBinaryPath;
|
|
||||||
FFmpegBinaryPath = option.FFmpegBinaryPath;
|
|
||||||
KeyTextFile = option.KeyTextFile;
|
|
||||||
DownloadRetryCount = option.DownloadRetryCount;
|
|
||||||
MuxAfterDone = option.MuxAfterDone;
|
|
||||||
UseMkvmerge = option.UseMkvmerge;
|
|
||||||
MkvmergeBinaryPath = option.MkvmergeBinaryPath;
|
|
||||||
MuxToMp4 = option.MuxToMp4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 临时文件存储目录
|
|
||||||
/// </summary>
|
|
||||||
public string? TmpDir { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 文件存储目录
|
|
||||||
/// </summary>
|
|
||||||
public string? SaveDir { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 文件名
|
|
||||||
/// </summary>
|
|
||||||
public string? SaveName { get; set; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件名模板
|
/// 文件名模板
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? SavePattern { get; set; }
|
public string? SavePattern { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 线程数
|
|
||||||
/// </summary>
|
|
||||||
public int ThreadCount { get; set; } = 8;
|
|
||||||
/// <summary>
|
|
||||||
/// 每个分片的重试次数
|
|
||||||
/// </summary>
|
|
||||||
public int DownloadRetryCount { get; set; } = 3;
|
|
||||||
/// <summary>
|
|
||||||
/// 跳过合并
|
|
||||||
/// </summary>
|
|
||||||
public bool SkipMerge { get; set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// 二进制合并
|
|
||||||
/// </summary>
|
|
||||||
public bool BinaryMerge { get; set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// 完成后是否删除临时文件
|
|
||||||
/// </summary>
|
|
||||||
public bool DelAfterDone { get; set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// 校验有没有下完全部分片
|
|
||||||
/// </summary>
|
|
||||||
public bool CheckSegmentsCount { get; set; } = true;
|
|
||||||
/// <summary>
|
|
||||||
/// 校验响应头的文件大小和实际大小
|
/// 校验响应头的文件大小和实际大小
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CheckContentLength { get; set; } = true;
|
public bool CheckContentLength { get; set; } = true;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自动修复字幕
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoSubtitleFix { get; set; } = true;
|
|
||||||
/// <summary>
|
|
||||||
/// MP4实时解密
|
|
||||||
/// </summary>
|
|
||||||
public bool MP4RealTimeDecryption { get; set; } = true;
|
|
||||||
/// <summary>
|
|
||||||
/// 使用shaka-packager替代mp4decrypt
|
|
||||||
/// </summary>
|
|
||||||
public bool UseShakaPackager { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 自动混流音视频
|
|
||||||
/// </summary>
|
|
||||||
public bool MuxAfterDone { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 自动混流音视频容器使用mp4
|
|
||||||
/// </summary>
|
|
||||||
public bool MuxToMp4 { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 使用mkvmerge混流
|
|
||||||
/// </summary>
|
|
||||||
public bool UseMkvmerge { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// MP4解密所用工具的全路径
|
|
||||||
/// </summary>
|
|
||||||
public string? DecryptionBinaryPath { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 字幕格式
|
|
||||||
/// </summary>
|
|
||||||
public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT;
|
|
||||||
/// <summary>
|
|
||||||
/// 请求头
|
/// 请求头
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>()
|
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
|
||||||
{
|
|
||||||
["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
|
|
||||||
};
|
|
||||||
/// <summary>
|
|
||||||
/// 解密KEYs
|
|
||||||
/// </summary>
|
|
||||||
public string[]? Keys { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// KID-KEY文件
|
|
||||||
/// </summary>
|
|
||||||
public string? KeyTextFile { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// ffmpeg路径
|
|
||||||
/// </summary>
|
|
||||||
public string? FFmpegBinaryPath { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// mkvmerge路径
|
|
||||||
/// </summary>
|
|
||||||
public string? MkvmergeBinaryPath { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,27 +69,27 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
//从文件读取KEY
|
//从文件读取KEY
|
||||||
private async Task SearchKeyAsync(string? currentKID)
|
private async Task SearchKeyAsync(string? currentKID)
|
||||||
{
|
{
|
||||||
var _key = await MP4DecryptUtil.SearchKeyFromFile(DownloaderConfig.KeyTextFile, currentKID);
|
var _key = await MP4DecryptUtil.SearchKeyFromFile(DownloaderConfig.MyOptions.KeyTextFile, currentKID);
|
||||||
if (_key != null)
|
if (_key != null)
|
||||||
{
|
{
|
||||||
if (DownloaderConfig.Keys == null)
|
if (DownloaderConfig.MyOptions.Keys == null)
|
||||||
DownloaderConfig.Keys = new string[] { _key };
|
DownloaderConfig.MyOptions.Keys = new string[] { _key };
|
||||||
else
|
else
|
||||||
DownloaderConfig.Keys = DownloaderConfig.Keys.Concat(new string[] { _key }).ToArray();
|
DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
|
private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
|
||||||
{
|
{
|
||||||
if (!DownloaderConfig.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
|
if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
|
||||||
{
|
{
|
||||||
DownloaderConfig.BinaryMerge = true;
|
DownloaderConfig.MyOptions.BinaryMerge = true;
|
||||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
|
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DownloaderConfig.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
|
if (!DownloaderConfig.MyOptions.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
|
||||||
{
|
{
|
||||||
DownloaderConfig.MuxAfterDone = false;
|
DownloaderConfig.MyOptions.MuxAfterDone = false;
|
||||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
|
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,16 +121,16 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (segments.Count() == 1) speedContainer.SingleSegment = true;
|
if (segments.Count() == 1) speedContainer.SingleSegment = true;
|
||||||
|
|
||||||
var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
|
var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
|
||||||
var dirName = $"{DownloaderConfig.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{streamSpec.GroupId}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
|
var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{streamSpec.GroupId}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
|
||||||
//去除非法字符
|
//去除非法字符
|
||||||
dirName = ConvertUtil.GetValidFileName(dirName, filterSlash: true);
|
dirName = ConvertUtil.GetValidFileName(dirName, filterSlash: true);
|
||||||
var tmpDir = Path.Combine(DownloaderConfig.TmpDir ?? Environment.CurrentDirectory, dirName);
|
var tmpDir = Path.Combine(DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory, dirName);
|
||||||
var saveDir = DownloaderConfig.SaveDir ?? Environment.CurrentDirectory;
|
var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
|
||||||
var saveName = DownloaderConfig.SaveName != null ? $"{DownloaderConfig.SaveName}.{type}.{streamSpec.Language}".TrimEnd('.') : dirName;
|
var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{type}.{streamSpec.Language}".TrimEnd('.') : dirName;
|
||||||
var headers = DownloaderConfig.Headers;
|
var headers = DownloaderConfig.Headers;
|
||||||
|
|
||||||
//mp4decrypt
|
//mp4decrypt
|
||||||
var mp4decrypt = DownloaderConfig.DecryptionBinaryPath!;
|
var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
|
||||||
var mp4InitFile = "";
|
var mp4InitFile = "";
|
||||||
var currentKID = "";
|
var currentKID = "";
|
||||||
var readInfo = false; //是否读取过
|
var readInfo = false; //是否读取过
|
||||||
|
@ -154,9 +154,9 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
Logger.InfoMarkUp(ResString.startDownloading + streamSpec.ToShortString());
|
Logger.InfoMarkUp(ResString.startDownloading + streamSpec.ToShortString());
|
||||||
|
|
||||||
//对于CENC,全部自动开启二进制合并
|
//对于CENC,全部自动开启二进制合并
|
||||||
if (!DownloaderConfig.BinaryMerge && totalCount >= 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
|
if (!DownloaderConfig.MyOptions.BinaryMerge && totalCount >= 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
|
||||||
{
|
{
|
||||||
DownloaderConfig.BinaryMerge = true;
|
DownloaderConfig.MyOptions.BinaryMerge = true;
|
||||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge4}[/]");
|
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge4}[/]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +164,9 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (streamSpec.Playlist?.MediaInit != null)
|
if (streamSpec.Playlist?.MediaInit != null)
|
||||||
{
|
{
|
||||||
//对于fMP4,自动开启二进制合并
|
//对于fMP4,自动开启二进制合并
|
||||||
if (!DownloaderConfig.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
|
if (!DownloaderConfig.MyOptions.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
|
||||||
{
|
{
|
||||||
DownloaderConfig.BinaryMerge = true;
|
DownloaderConfig.MyOptions.BinaryMerge = true;
|
||||||
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
|
Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,11 +188,11 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
//从文件读取KEY
|
//从文件读取KEY
|
||||||
await SearchKeyAsync(currentKID);
|
await SearchKeyAsync(currentKID);
|
||||||
//实时解密
|
//实时解密
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && streamSpec.Playlist.MediaInit.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
|
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && streamSpec.Playlist.MediaInit.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
|
||||||
{
|
{
|
||||||
var enc = result.ActualFilePath;
|
var enc = result.ActualFilePath;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID);
|
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
|
||||||
if (dResult)
|
if (dResult)
|
||||||
{
|
{
|
||||||
FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
|
FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
|
||||||
|
@ -202,7 +202,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (!readInfo)
|
if (!readInfo)
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.readingInfo);
|
Logger.WarnMarkUp(ResString.readingInfo);
|
||||||
var mediainfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.FFmpegBinaryPath!, result.ActualFilePath);
|
var mediainfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
|
||||||
mediainfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
|
mediainfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
|
||||||
ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter);
|
ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter);
|
||||||
readInfo = true;
|
readInfo = true;
|
||||||
|
@ -225,7 +225,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
FileDic[seg] = result;
|
FileDic[seg] = result;
|
||||||
task.Increment(1);
|
task.Increment(1);
|
||||||
//实时解密
|
//实时解密
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && seg.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC && result != null)
|
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && seg.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC && result != null)
|
||||||
{
|
{
|
||||||
//读取init信息
|
//读取init信息
|
||||||
if (string.IsNullOrEmpty(currentKID))
|
if (string.IsNullOrEmpty(currentKID))
|
||||||
|
@ -236,7 +236,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
await SearchKeyAsync(currentKID);
|
await SearchKeyAsync(currentKID);
|
||||||
var enc = result.ActualFilePath;
|
var enc = result.ActualFilePath;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID, mp4InitFile);
|
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
|
||||||
if (dResult)
|
if (dResult)
|
||||||
{
|
{
|
||||||
File.Delete(enc);
|
File.Delete(enc);
|
||||||
|
@ -245,7 +245,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
//ffmpeg读取信息
|
//ffmpeg读取信息
|
||||||
Logger.WarnMarkUp(ResString.readingInfo);
|
Logger.WarnMarkUp(ResString.readingInfo);
|
||||||
var mediainfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.FFmpegBinaryPath!, result!.ActualFilePath);
|
var mediainfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
|
||||||
mediainfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
|
mediainfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
|
||||||
ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter);
|
ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter);
|
||||||
readInfo = true;
|
readInfo = true;
|
||||||
|
@ -254,7 +254,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
//开始下载
|
//开始下载
|
||||||
var options = new ParallelOptions()
|
var options = new ParallelOptions()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = DownloaderConfig.ThreadCount
|
MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount
|
||||||
};
|
};
|
||||||
await Parallel.ForEachAsync(segments, options, async (seg, _) =>
|
await Parallel.ForEachAsync(segments, options, async (seg, _) =>
|
||||||
{
|
{
|
||||||
|
@ -264,11 +264,11 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
FileDic[seg] = result;
|
FileDic[seg] = result;
|
||||||
task.Increment(1);
|
task.Increment(1);
|
||||||
//实时解密
|
//实时解密
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && seg.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC && result != null)
|
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && seg.EncryptInfo.Method == Common.Enum.EncryptMethod.CENC && result != null)
|
||||||
{
|
{
|
||||||
var enc = result.ActualFilePath;
|
var enc = result.ActualFilePath;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID, mp4InitFile);
|
var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
|
||||||
if (dResult)
|
if (dResult)
|
||||||
{
|
{
|
||||||
File.Delete(enc);
|
File.Delete(enc);
|
||||||
|
@ -291,18 +291,18 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
|
Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "")
|
if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && mp4InitFile != "")
|
||||||
{
|
{
|
||||||
File.Delete(mp4InitFile);
|
File.Delete(mp4InitFile);
|
||||||
//shaka实时解密不需要init文件用于合并
|
//shaka实时解密不需要init文件用于合并
|
||||||
if (DownloaderConfig.UseShakaPackager)
|
if (DownloaderConfig.MyOptions.UseShakaPackager)
|
||||||
{
|
{
|
||||||
FileDic!.Remove(streamSpec.Playlist!.MediaInit, out _);
|
FileDic!.Remove(streamSpec.Playlist!.MediaInit, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//校验分片数量
|
//校验分片数量
|
||||||
if (DownloaderConfig.CheckSegmentsCount && FileDic.Values.Any(s => s == null))
|
if (DownloaderConfig.MyOptions.CheckSegmentsCount && FileDic.Values.Any(s => s == null))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.segmentCountCheckNotPass, totalCount, FileDic.Values.Where(s => s != null).Count());
|
Logger.WarnMarkUp(ResString.segmentCountCheckNotPass, totalCount, FileDic.Values.Where(s => s != null).Count());
|
||||||
return false;
|
return false;
|
||||||
|
@ -322,7 +322,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复VTT raw字幕
|
//自动修复VTT raw字幕
|
||||||
if (DownloaderConfig.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingVTT);
|
Logger.WarnMarkUp(ResString.fixingVTT);
|
||||||
|
@ -357,10 +357,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
||||||
var subContentFixed = finalVtt.ToStringWithHeader();
|
var subContentFixed = finalVtt.ToStringWithHeader();
|
||||||
//转换字幕格式
|
//转换字幕格式
|
||||||
if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
||||||
{
|
{
|
||||||
path = Path.ChangeExtension(path, ".srt");
|
path = Path.ChangeExtension(path, ".srt");
|
||||||
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
|
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat);
|
||||||
output = Path.ChangeExtension(output, ".srt");
|
output = Path.ChangeExtension(output, ".srt");
|
||||||
}
|
}
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
||||||
|
@ -372,7 +372,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复VTT mp4字幕
|
//自动修复VTT mp4字幕
|
||||||
if (DownloaderConfig.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
|
&& streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
|
||||||
{
|
{
|
||||||
var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
|
var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
|
||||||
|
@ -392,10 +392,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
||||||
var subContentFixed = finalVtt.ToStringWithHeader();
|
var subContentFixed = finalVtt.ToStringWithHeader();
|
||||||
//转换字幕格式
|
//转换字幕格式
|
||||||
if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
||||||
{
|
{
|
||||||
path = Path.ChangeExtension(path, ".srt");
|
path = Path.ChangeExtension(path, ".srt");
|
||||||
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
|
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat);
|
||||||
output = Path.ChangeExtension(output, ".srt");
|
output = Path.ChangeExtension(output, ".srt");
|
||||||
}
|
}
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
||||||
|
@ -410,7 +410,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复TTML raw字幕
|
//自动修复TTML raw字幕
|
||||||
if (DownloaderConfig.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingTTML);
|
Logger.WarnMarkUp(ResString.fixingTTML);
|
||||||
|
@ -425,10 +425,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
||||||
var subContentFixed = finalVtt.ToStringWithHeader();
|
var subContentFixed = finalVtt.ToStringWithHeader();
|
||||||
//转换字幕格式
|
//转换字幕格式
|
||||||
if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
||||||
{
|
{
|
||||||
path = Path.ChangeExtension(path, ".srt");
|
path = Path.ChangeExtension(path, ".srt");
|
||||||
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
|
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat);
|
||||||
output = Path.ChangeExtension(output, ".srt");
|
output = Path.ChangeExtension(output, ".srt");
|
||||||
}
|
}
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
||||||
|
@ -442,7 +442,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复TTML mp4字幕
|
//自动修复TTML mp4字幕
|
||||||
if (DownloaderConfig.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
|
||||||
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
|
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
|
||||||
{
|
{
|
||||||
|
@ -462,10 +462,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
|
||||||
var subContentFixed = finalVtt.ToStringWithHeader();
|
var subContentFixed = finalVtt.ToStringWithHeader();
|
||||||
//转换字幕格式
|
//转换字幕格式
|
||||||
if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
|
||||||
{
|
{
|
||||||
path = Path.ChangeExtension(path, ".srt");
|
path = Path.ChangeExtension(path, ".srt");
|
||||||
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
|
subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat);
|
||||||
output = Path.ChangeExtension(output, ".srt");
|
output = Path.ChangeExtension(output, ".srt");
|
||||||
}
|
}
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
|
||||||
|
@ -480,10 +480,10 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
|
|
||||||
bool mergeSuccess = false;
|
bool mergeSuccess = false;
|
||||||
//合并
|
//合并
|
||||||
if (!DownloaderConfig.SkipMerge)
|
if (!DownloaderConfig.MyOptions.SkipMerge)
|
||||||
{
|
{
|
||||||
//字幕也使用二进制合并
|
//字幕也使用二进制合并
|
||||||
if (DownloaderConfig.BinaryMerge || streamSpec.MediaType == MediaType.SUBTITLES)
|
if (DownloaderConfig.MyOptions.BinaryMerge || streamSpec.MediaType == MediaType.SUBTITLES)
|
||||||
{
|
{
|
||||||
Logger.InfoMarkUp(ResString.binaryMerge);
|
Logger.InfoMarkUp(ResString.binaryMerge);
|
||||||
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
var files = FileDic.Values.Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
||||||
|
@ -502,13 +502,13 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp($"{Path.GetFileName(ffOut)} => {Path.GetFileName(ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut)))}");
|
Logger.WarnMarkUp($"{Path.GetFileName(ffOut)} => {Path.GetFileName(ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut)))}");
|
||||||
}
|
}
|
||||||
mergeSuccess = MergeUtil.MergeByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, files, Path.ChangeExtension(ffOut, null), ext, useAACFilter);
|
mergeSuccess = MergeUtil.MergeByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, files, Path.ChangeExtension(ffOut, null), ext, useAACFilter);
|
||||||
if (mergeSuccess) output = ffOut;
|
if (mergeSuccess) output = ffOut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//删除临时文件夹
|
//删除临时文件夹
|
||||||
if (!DownloaderConfig.SkipMerge && DownloaderConfig.DelAfterDone && mergeSuccess)
|
if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && mergeSuccess)
|
||||||
{
|
{
|
||||||
var files = FileDic.Values.Select(v => v!.ActualFilePath);
|
var files = FileDic.Values.Select(v => v!.ActualFilePath);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
|
@ -527,12 +527,12 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
|
|
||||||
//调用mp4decrypt解密
|
//调用mp4decrypt解密
|
||||||
if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MP4RealTimeDecryption && DownloaderConfig.Keys != null && DownloaderConfig.Keys.Length > 0)
|
if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
|
||||||
{
|
{
|
||||||
var enc = output;
|
var enc = output;
|
||||||
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
|
||||||
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
|
Logger.InfoMarkUp($"[grey]Decrypting...[/]");
|
||||||
var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.UseShakaPackager, mp4decrypt, DownloaderConfig.Keys, enc, dec, currentKID);
|
var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
File.Delete(enc);
|
File.Delete(enc);
|
||||||
|
@ -585,20 +585,26 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var success = Results.Values.All(v => v == true);
|
var success = Results.Values.All(v => v == true);
|
||||||
|
|
||||||
//混流
|
//混流
|
||||||
if (success && DownloaderConfig.MuxAfterDone && OutputFiles.Count > 0)
|
if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0)
|
||||||
{
|
{
|
||||||
var saveDir = DownloaderConfig.SaveDir ?? Environment.CurrentDirectory;
|
if (DownloaderConfig.MyOptions.MuxImports != null)
|
||||||
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()}.{(DownloaderConfig.MuxToMp4 ? "mp4" : "mkv")}[/]");
|
|
||||||
var result = false;
|
|
||||||
if (DownloaderConfig.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
|
|
||||||
else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MuxToMp4);
|
|
||||||
//完成后删除各轨道文件
|
|
||||||
if (result)
|
|
||||||
{
|
{
|
||||||
|
OutputFiles.AddRange(DownloaderConfig.MyOptions.MuxImports);
|
||||||
|
}
|
||||||
|
OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]"));
|
||||||
|
var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
|
||||||
|
var outName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}";
|
||||||
|
var outPath = Path.Combine(saveDir, outName);
|
||||||
|
Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}.{(DownloaderConfig.MyOptions.MuxToMp4 ? "mp4" : "mkv")}[/]");
|
||||||
|
var result = false;
|
||||||
|
if (DownloaderConfig.MyOptions.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MyOptions.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
|
||||||
|
else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MyOptions.MuxToMp4);
|
||||||
|
//完成后删除各轨道文件
|
||||||
|
if (result && !DownloaderConfig.MyOptions.MuxKeepFiles)
|
||||||
|
{
|
||||||
|
Logger.WarnMarkUp("[grey]Cleaning files...[/]");
|
||||||
OutputFiles.ForEach(f => File.Delete(f.FilePath));
|
OutputFiles.ForEach(f => File.Delete(f.FilePath));
|
||||||
var tmpDir = DownloaderConfig.TmpDir ?? Environment.CurrentDirectory;
|
var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory;
|
||||||
SafeDeleteDir(tmpDir);
|
SafeDeleteDir(tmpDir);
|
||||||
}
|
}
|
||||||
else Logger.ErrorMarkUp($"Mux failed");
|
else Logger.ErrorMarkUp($"Mux failed");
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace N_m3u8DL_RE.Downloader
|
||||||
public async Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null)
|
public async Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null)
|
||||||
{
|
{
|
||||||
var url = segment.Url;
|
var url = segment.Url;
|
||||||
var dResult = await DownClipAsync(url, savePath, speedContainer, segment.StartRange, segment.StopRange, headers, DownloaderConfig.DownloadRetryCount);
|
var dResult = await DownClipAsync(url, savePath, speedContainer, segment.StartRange, segment.StopRange, headers, DownloaderConfig.MyOptions.DownloadRetryCount);
|
||||||
if (dResult != null && dResult.Success && segment.EncryptInfo != null)
|
if (dResult != null && dResult.Success && segment.EncryptInfo != null)
|
||||||
{
|
{
|
||||||
if (segment.EncryptInfo.Method == EncryptMethod.AES_128)
|
if (segment.EncryptInfo.Method == EncryptMethod.AES_128)
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace N_m3u8DL_RE.Entity
|
||||||
|
{
|
||||||
|
internal class MuxOptions
|
||||||
|
{
|
||||||
|
public bool UseMkvmerge { get; set; } = false;
|
||||||
|
public bool MuxToMp4 { get; set; } = false;
|
||||||
|
public bool KeepFiles { get; set; } = false;
|
||||||
|
public string? BinPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,14 +50,10 @@ namespace N_m3u8DL_RE
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//检查互斥的选项
|
//检查互斥的选项
|
||||||
if (option.UseMkvmerge && option.MuxToMp4)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Can't use mkvmerge to make mp4!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.MuxToMp4 && !option.MuxAfterDone)
|
if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Can't enable MuxToMp4 when MuxAfterDone is false!");
|
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
//预先检查ffmpeg
|
//预先检查ffmpeg
|
||||||
|
@ -111,15 +107,10 @@ namespace N_m3u8DL_RE
|
||||||
{
|
{
|
||||||
AppendUrlParams = option.AppendUrlParams,
|
AppendUrlParams = option.AppendUrlParams,
|
||||||
UrlProcessorArgs = option.UrlProcessorArgs,
|
UrlProcessorArgs = option.UrlProcessorArgs,
|
||||||
BaseUrl = option.BaseUrl!
|
BaseUrl = option.BaseUrl!,
|
||||||
|
Headers = option.Headers
|
||||||
};
|
};
|
||||||
|
|
||||||
//设置Headers
|
|
||||||
foreach (var item in ConvertUtil.SplitHeaderArrayToDic(option.Headers))
|
|
||||||
{
|
|
||||||
parserConfig.Headers[item.Key] = item.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//demo1
|
//demo1
|
||||||
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
||||||
//demo2
|
//demo2
|
||||||
|
@ -254,9 +245,10 @@ namespace N_m3u8DL_RE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//下载配置
|
//下载配置
|
||||||
var downloadConfig = new DownloaderConfig(option)
|
var downloadConfig = new DownloaderConfig()
|
||||||
{
|
{
|
||||||
Headers = parserConfig.Headers,
|
MyOptions = option,
|
||||||
|
Headers = parserConfig.Headers, //使用命令行解析得到的Headers
|
||||||
};
|
};
|
||||||
//开始下载
|
//开始下载
|
||||||
var sdm = new SimpleDownloadManager(downloadConfig);
|
var sdm = new SimpleDownloadManager(downloadConfig);
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.Util
|
||||||
var index = header.IndexOf(':');
|
var index = header.IndexOf(':');
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
dic[header[..index].Trim()] = header[(index + 1)..].Trim();
|
dic[header[..index].Trim().ToLower()] = header[(index + 1)..].Trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue