From 78473cebb808661407de03d6bd8a029e651e36ba Mon Sep 17 00:00:00 2001 From: nilaoda Date: Thu, 25 Aug 2022 21:44:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=8D=E6=9D=82=E6=B7=B7?= =?UTF-8?q?=E6=B5=81=E9=80=89=E9=A1=B9=E9=85=8D=E7=BD=AE;=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=94=AF=E6=8C=81=E5=BC=95=E5=85=A5=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=B7=B7=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/N_m3u8DL-RE.Common/Resource/ResString.cs | 1 + src/N_m3u8DL-RE.Common/Resource/StaticText.cs | 69 +++++++- src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs | 5 +- src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs | 147 ++++++++++++++++-- .../CommandLine/ComplexParamParser.cs | 61 ++++++++ src/N_m3u8DL-RE/CommandLine/MyOption.cs | 8 +- src/N_m3u8DL-RE/Config/DownloaderConfig.cs | 117 +------------- .../DownloadManager/SimpleDownloadManager.cs | 122 ++++++++------- .../Downloader/SimpleDownloader.cs | 2 +- src/N_m3u8DL-RE/Entity/MuxOptions.cs | 16 ++ src/N_m3u8DL-RE/Program.cs | 22 +-- src/N_m3u8DL-RE/Util/ConvertUtil.cs | 2 +- 12 files changed, 360 insertions(+), 212 deletions(-) create mode 100644 src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs create mode 100644 src/N_m3u8DL-RE/Entity/MuxOptions.cs diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index 22487db..7fe79eb 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -27,6 +27,7 @@ namespace N_m3u8DL_RE.Common.Resource public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); } public static string cmd_baseUrl { get => GetText("cmd_baseUrl"); } 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_keys { get => GetText("cmd_keys"); } public static string cmd_keyText { get => GetText("cmd_keyText"); } diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index b787c5a..6cb03e9 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -246,9 +246,72 @@ namespace N_m3u8DL_RE.Common.Resource ), ["cmd_muxAfterDone"] = new TextContainer ( - zhCN: "所有工作完成时尝试使用ffmpeg混流分离的音视频(默认容器: mkv)", - zhTW: "所有工作完成時嘗試使用ffmpeg混流分離的影音(默認容器: mkv)", - enUS: "When all works is done, try to use ffmpeg to mux the separated streams.(Default container: mkv)" + zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\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", + 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 ( diff --git a/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs b/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs index fb5f8c6..5bacc9e 100644 --- a/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs +++ b/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs @@ -11,10 +11,7 @@ namespace N_m3u8DL_RE.Parser.Config public string BaseUrl { get; set; } - public Dictionary Headers { get; set; } = new Dictionary() - { - ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" - }; + public Dictionary Headers { get; set; } = new Dictionary(); /// /// 内容前置处理器. 调用顺序与列表顺序相同 diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 546c4f4..c176804 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -1,8 +1,11 @@ using N_m3u8DL_RE.Common.Log; using N_m3u8DL_RE.Common.Resource; +using N_m3u8DL_RE.Entity; using N_m3u8DL_RE.Enum; +using N_m3u8DL_RE.Util; using System.CommandLine; using System.CommandLine.Binding; +using System.CommandLine.Parsing; using System.Globalization; using System.Linq; @@ -17,9 +20,9 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option SavePattern = new(new string[] { "--save-pattern" }, description: ResString.cmd_savePattern, getDefaultValue: () => "____"); private readonly static Option UILanguage = new Option(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage).FromAmong("en-US", "zh-CN", "zh-TW"); private readonly static Option UrlProcessorArgs = new(new string[] { "--urlprocessor-args" }, description: ResString.cmd_urlProcessorArgs); - private readonly static Option Keys = new(new string[] { "--key" }, description: ResString.cmd_keys) { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = false }; + private readonly static Option Keys = new(new string[] { "--key" }, description: ResString.cmd_keys) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false }; private readonly static Option KeyTextFile = new(new string[] { "--key-text-file" }, description: ResString.cmd_keyText); - private readonly static Option Headers = new(new string[] { "-H", "--header" }, description: ResString.cmd_header) { Arity = ArgumentArity.ZeroOrMore, AllowMultipleArgumentsPerToken = false }; + private readonly static Option> Headers = new(new string[] { "-H", "--header" }, description: ResString.cmd_header, parseArgument: ParseHeaders) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false }; private readonly static Option LogLevel = new(name: "--log-level", description: ResString.cmd_logLevel, getDefaultValue: () => Common.Log.LogLevel.INFO); private readonly static Option SubtitleFormat = new(name: "--sub-format", description: ResString.cmd_subFormat, getDefaultValue: () => Enum.SubtitleFormat.VTT); private readonly static Option 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 AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false); private readonly static Option MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false); private readonly static Option UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false); - private readonly static Option MuxAfterDone = new (new string[] { "--mux-after-done" }, description: ResString.cmd_muxAfterDone, getDefaultValue: () => false); - private readonly static Option MuxToMp4 = new (new string[] { "--mux-to-mp4" }, description: ResString.cmd_muxToMp4, getDefaultValue: () => false); - private readonly static Option UseMkvmerge = new(new string[] { "--use-mkvmerge" }, description: ResString.cmd_useMkvmerge, getDefaultValue: () => false); private readonly static Option DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath); private readonly static Option FFmpegBinaryPath = new(new string[] { "--ffmpeg-binary-path" }, description: ResString.cmd_ffmpegBinaryPath); - private readonly static Option MkvmergeBinaryPath = new(new string[] { "--mkvmerge-binary-path" }, description: ResString.cmd_mkvmergeBinaryPath); private readonly static Option BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl); + //复杂命令行如下 + private readonly static Option MuxAfterDone = new(new string[] { "-M", "--mux-after-done" }, description: ResString.cmd_muxAfterDone, parseArgument: ParseMuxAfterDone) { ArgumentHelpName = "OPTIONS" }; + private readonly static Option> MuxImports = new("--mux-import", description: ResString.cmd_muxImport, parseArgument: ParseImports) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false, ArgumentHelpName = "OPTIONS" }; + + + /// + /// 分割Header + /// + /// + /// + private static Dictionary ParseHeaders(ArgumentResult result) + { + //默认的Headers + var headers = new Dictionary() + { + ["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; + } + + /// + /// 解析混流引入的外部文件 + /// + /// + /// + private static List ParseImports(ArgumentResult result) + { + var imports = new List(); + + 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; + } + + /// + /// 解析混流选项 + /// + /// + /// + 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 { protected override MyOption GetBoundValue(BindingContext bindingContext) @@ -51,7 +158,7 @@ namespace N_m3u8DL_RE.CommandLine var option = new MyOption { Input = bindingContext.ParseResult.GetValueForArgument(Input), - Headers = bindingContext.ParseResult.GetValueForOption(Headers), + Headers = bindingContext.ParseResult.GetValueForOption(Headers)!, LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel), AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect), SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge), @@ -78,10 +185,8 @@ namespace N_m3u8DL_RE.CommandLine FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath), KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile), DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount), - MuxAfterDone = bindingContext.ParseResult.GetValueForOption(MuxAfterDone), - UseMkvmerge = bindingContext.ParseResult.GetValueForOption(UseMkvmerge), 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); } + //混流设置 + 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; } } @@ -100,12 +218,13 @@ namespace N_m3u8DL_RE.CommandLine public static async Task InvokeArgs(string[] args, Func 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, - BinaryMerge, DelAfterDone, WriteMetaJson, MuxAfterDone, MuxToMp4, UseMkvmerge, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix, - FFmpegBinaryPath, MkvmergeBinaryPath, - LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption + BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix, + FFmpegBinaryPath, + LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption, + MuxAfterDone, MuxImports }; rootCommand.TreatUnmatchedTokensAsErrors = true; rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder()); diff --git a/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs b/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs new file mode 100644 index 0000000..4fa3b82 --- /dev/null +++ b/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs @@ -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!"); + } + } + } +} diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs index f649bfe..13ef95f 100644 --- a/src/N_m3u8DL-RE/CommandLine/MyOption.cs +++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs @@ -1,4 +1,5 @@ using N_m3u8DL_RE.Common.Log; +using N_m3u8DL_RE.Entity; using N_m3u8DL_RE.Enum; namespace N_m3u8DL_RE.CommandLine @@ -12,7 +13,7 @@ namespace N_m3u8DL_RE.CommandLine /// /// See: . /// - public string[]? Headers { get; set; } + public Dictionary Headers { get; set; } = new Dictionary(); /// /// See: . /// @@ -137,5 +138,10 @@ namespace N_m3u8DL_RE.CommandLine /// See: . /// public string? MkvmergeBinaryPath { get; set; } + /// + /// See: . + /// + public List? MuxImports { get; set; } + public bool MuxKeepFiles { get; set; } } } \ No newline at end of file diff --git a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs index 776c0c1..0f70597 100644 --- a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs +++ b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs @@ -11,132 +11,19 @@ namespace N_m3u8DL_RE.Config { 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; - } - - /// - /// 临时文件存储目录 - /// - public string? TmpDir { get; set; } - /// - /// 文件存储目录 - /// - public string? SaveDir { get; set; } - /// - /// 文件名 - /// - public string? SaveName { get; set; } /// /// 文件名模板 /// public string? SavePattern { get; set; } /// - /// 线程数 - /// - public int ThreadCount { get; set; } = 8; - /// - /// 每个分片的重试次数 - /// - public int DownloadRetryCount { get; set; } = 3; - /// - /// 跳过合并 - /// - public bool SkipMerge { get; set; } = false; - /// - /// 二进制合并 - /// - public bool BinaryMerge { get; set; } = false; - /// - /// 完成后是否删除临时文件 - /// - public bool DelAfterDone { get; set; } = false; - /// - /// 校验有没有下完全部分片 - /// - public bool CheckSegmentsCount { get; set; } = true; - /// /// 校验响应头的文件大小和实际大小 /// public bool CheckContentLength { get; set; } = true; /// - /// 自动修复字幕 - /// - public bool AutoSubtitleFix { get; set; } = true; - /// - /// MP4实时解密 - /// - public bool MP4RealTimeDecryption { get; set; } = true; - /// - /// 使用shaka-packager替代mp4decrypt - /// - public bool UseShakaPackager { get; set; } - /// - /// 自动混流音视频 - /// - public bool MuxAfterDone { get; set; } - /// - /// 自动混流音视频容器使用mp4 - /// - public bool MuxToMp4 { get; set; } - /// - /// 使用mkvmerge混流 - /// - public bool UseMkvmerge { get; set; } - /// - /// MP4解密所用工具的全路径 - /// - public string? DecryptionBinaryPath { get; set; } - /// - /// 字幕格式 - /// - public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT; - /// /// 请求头 /// - public Dictionary Headers { get; set; } = new Dictionary() - { - ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" - }; - /// - /// 解密KEYs - /// - public string[]? Keys { get; set; } - /// - /// KID-KEY文件 - /// - public string? KeyTextFile { get; set; } - /// - /// ffmpeg路径 - /// - public string? FFmpegBinaryPath { get; set; } - /// - /// mkvmerge路径 - /// - public string? MkvmergeBinaryPath { get; set; } + public Dictionary Headers { get; set; } = new Dictionary(); } } diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs index 442f847..c28bcdc 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs @@ -69,27 +69,27 @@ namespace N_m3u8DL_RE.DownloadManager //从文件读取KEY 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 (DownloaderConfig.Keys == null) - DownloaderConfig.Keys = new string[] { _key }; + if (DownloaderConfig.MyOptions.Keys == null) + DownloaderConfig.MyOptions.Keys = new string[] { _key }; 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 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}[/]"); } - 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}[/]"); } @@ -121,16 +121,16 @@ namespace N_m3u8DL_RE.DownloadManager if (segments.Count() == 1) speedContainer.SingleSegment = true; 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); - var tmpDir = Path.Combine(DownloaderConfig.TmpDir ?? Environment.CurrentDirectory, dirName); - var saveDir = DownloaderConfig.SaveDir ?? Environment.CurrentDirectory; - var saveName = DownloaderConfig.SaveName != null ? $"{DownloaderConfig.SaveName}.{type}.{streamSpec.Language}".TrimEnd('.') : dirName; + var tmpDir = Path.Combine(DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory, dirName); + var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory; + var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{type}.{streamSpec.Language}".TrimEnd('.') : dirName; var headers = DownloaderConfig.Headers; //mp4decrypt - var mp4decrypt = DownloaderConfig.DecryptionBinaryPath!; + var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!; var mp4InitFile = ""; var currentKID = ""; var readInfo = false; //是否读取过 @@ -154,9 +154,9 @@ namespace N_m3u8DL_RE.DownloadManager Logger.InfoMarkUp(ResString.startDownloading + streamSpec.ToShortString()); //对于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}[/]"); } @@ -164,9 +164,9 @@ namespace N_m3u8DL_RE.DownloadManager if (streamSpec.Playlist?.MediaInit != null) { //对于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}[/]"); } @@ -188,11 +188,11 @@ namespace N_m3u8DL_RE.DownloadManager //从文件读取KEY 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 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) { FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec; @@ -202,7 +202,7 @@ namespace N_m3u8DL_RE.DownloadManager if (!readInfo) { 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())); ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter); readInfo = true; @@ -225,7 +225,7 @@ namespace N_m3u8DL_RE.DownloadManager FileDic[seg] = result; 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信息 if (string.IsNullOrEmpty(currentKID)) @@ -236,7 +236,7 @@ namespace N_m3u8DL_RE.DownloadManager await SearchKeyAsync(currentKID); var enc = result.ActualFilePath; 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) { File.Delete(enc); @@ -245,7 +245,7 @@ namespace N_m3u8DL_RE.DownloadManager } //ffmpeg读取信息 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())); ChangeSpecInfo(streamSpec, mediainfos, ref useAACFilter); readInfo = true; @@ -254,7 +254,7 @@ namespace N_m3u8DL_RE.DownloadManager //开始下载 var options = new ParallelOptions() { - MaxDegreeOfParallelism = DownloaderConfig.ThreadCount + MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount }; await Parallel.ForEachAsync(segments, options, async (seg, _) => { @@ -264,11 +264,11 @@ namespace N_m3u8DL_RE.DownloadManager FileDic[seg] = result; 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 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) { 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)))}"); } - if (DownloaderConfig.MP4RealTimeDecryption && mp4InitFile != "") + if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && mp4InitFile != "") { File.Delete(mp4InitFile); //shaka实时解密不需要init文件用于合并 - if (DownloaderConfig.UseShakaPackager) + if (DownloaderConfig.MyOptions.UseShakaPackager) { 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()); return false; @@ -322,7 +322,7 @@ namespace N_m3u8DL_RE.DownloadManager } //自动修复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")) { Logger.WarnMarkUp(ResString.fixingVTT); @@ -357,10 +357,10 @@ namespace N_m3u8DL_RE.DownloadManager var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt"); var subContentFixed = finalVtt.ToStringWithHeader(); //转换字幕格式 - if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT) + if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT) { path = Path.ChangeExtension(path, ".srt"); - subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat); + subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat); output = Path.ChangeExtension(output, ".srt"); } await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false)); @@ -372,7 +372,7 @@ namespace N_m3u8DL_RE.DownloadManager } //自动修复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")) { 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 subContentFixed = finalVtt.ToStringWithHeader(); //转换字幕格式 - if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT) + if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT) { path = Path.ChangeExtension(path, ".srt"); - subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat); + subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat); output = Path.ChangeExtension(output, ".srt"); } await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false)); @@ -410,7 +410,7 @@ namespace N_m3u8DL_RE.DownloadManager } //自动修复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")) { Logger.WarnMarkUp(ResString.fixingTTML); @@ -425,10 +425,10 @@ namespace N_m3u8DL_RE.DownloadManager var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt"); var subContentFixed = finalVtt.ToStringWithHeader(); //转换字幕格式 - if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT) + if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT) { path = Path.ChangeExtension(path, ".srt"); - subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat); + subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat); output = Path.ChangeExtension(output, ".srt"); } await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false)); @@ -442,7 +442,7 @@ namespace N_m3u8DL_RE.DownloadManager } //自动修复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.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 subContentFixed = finalVtt.ToStringWithHeader(); //转换字幕格式 - if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT) + if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT) { path = Path.ChangeExtension(path, ".srt"); - subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat); + subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.MyOptions.SubtitleFormat); output = Path.ChangeExtension(output, ".srt"); } await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false)); @@ -480,10 +480,10 @@ namespace N_m3u8DL_RE.DownloadManager 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); 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)))}"); } - 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 (!DownloaderConfig.SkipMerge && DownloaderConfig.DelAfterDone && mergeSuccess) + if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && mergeSuccess) { var files = FileDic.Values.Select(v => v!.ActualFilePath); foreach (var file in files) @@ -527,12 +527,12 @@ namespace N_m3u8DL_RE.DownloadManager } //调用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 dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc)); 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) { File.Delete(enc); @@ -585,20 +585,26 @@ namespace N_m3u8DL_RE.DownloadManager 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; - 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) + if (DownloaderConfig.MyOptions.MuxImports != null) { + 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)); - var tmpDir = DownloaderConfig.TmpDir ?? Environment.CurrentDirectory; + var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory; SafeDeleteDir(tmpDir); } else Logger.ErrorMarkUp($"Mux failed"); diff --git a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs index f9f8c5b..772d886 100644 --- a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs +++ b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs @@ -30,7 +30,7 @@ namespace N_m3u8DL_RE.Downloader public async Task DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary? headers = null) { 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 (segment.EncryptInfo.Method == EncryptMethod.AES_128) diff --git a/src/N_m3u8DL-RE/Entity/MuxOptions.cs b/src/N_m3u8DL-RE/Entity/MuxOptions.cs new file mode 100644 index 0000000..2761832 --- /dev/null +++ b/src/N_m3u8DL-RE/Entity/MuxOptions.cs @@ -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; } + } +} diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs index b8e6d1a..02e5e70 100644 --- a/src/N_m3u8DL-RE/Program.cs +++ b/src/N_m3u8DL-RE/Program.cs @@ -50,14 +50,10 @@ namespace N_m3u8DL_RE 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 @@ -111,15 +107,10 @@ namespace N_m3u8DL_RE { AppendUrlParams = option.AppendUrlParams, 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 parserConfig.ContentProcessors.Insert(0, new DemoProcessor()); //demo2 @@ -254,9 +245,10 @@ namespace N_m3u8DL_RE #endif //下载配置 - var downloadConfig = new DownloaderConfig(option) + var downloadConfig = new DownloaderConfig() { - Headers = parserConfig.Headers, + MyOptions = option, + Headers = parserConfig.Headers, //使用命令行解析得到的Headers }; //开始下载 var sdm = new SimpleDownloadManager(downloadConfig); diff --git a/src/N_m3u8DL-RE/Util/ConvertUtil.cs b/src/N_m3u8DL-RE/Util/ConvertUtil.cs index 4cc675e..b42fd36 100644 --- a/src/N_m3u8DL-RE/Util/ConvertUtil.cs +++ b/src/N_m3u8DL-RE/Util/ConvertUtil.cs @@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.Util var index = header.IndexOf(':'); if (index != -1) { - dic[header[..index].Trim()] = header[(index + 1)..].Trim(); + dic[header[..index].Trim().ToLower()] = header[(index + 1)..].Trim(); } } }