增加复杂混流选项配置;增加支持引入外部文件混流

This commit is contained in:
nilaoda 2022-08-25 21:44:13 +08:00
parent df63b42356
commit 78473cebb8
12 changed files with 360 additions and 212 deletions

View File

@ -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"); }

View File

@ -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
(

View File

@ -11,10 +11,7 @@ namespace N_m3u8DL_RE.Parser.Config
public string BaseUrl { get; set; }
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"
};
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
/// <summary>
/// 内容前置处理器. 调用顺序与列表顺序相同

View File

@ -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<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?> 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[]?> 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<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);
@ -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> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
private readonly static Option<bool> UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
private readonly static Option<bool> MuxAfterDone = new (new string[] { "--mux-after-done" }, description: ResString.cmd_muxAfterDone, getDefaultValue: () => false);
private readonly static Option<bool> 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?> 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<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>
{
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<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,
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());

View File

@ -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!");
}
}
}
}

View File

@ -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
/// <summary>
/// See: <see cref="CommandInvoker.Headers"/>.
/// </summary>
public string[]? Headers { get; set; }
public Dictionary<string,string> Headers { get; set; } = new Dictionary<string,string>();
/// <summary>
/// See: <see cref="CommandInvoker.Keys"/>.
/// </summary>
@ -137,5 +138,10 @@ namespace N_m3u8DL_RE.CommandLine
/// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
/// </summary>
public string? MkvmergeBinaryPath { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.MuxImports"/>.
/// </summary>
public List<OutputFile>? MuxImports { get; set; }
public bool MuxKeepFiles { get; set; }
}
}

View File

@ -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;
}
/// <summary>
/// 临时文件存储目录
/// </summary>
public string? TmpDir { get; set; }
/// <summary>
/// 文件存储目录
/// </summary>
public string? SaveDir { get; set; }
/// <summary>
/// 文件名
/// </summary>
public string? SaveName { get; set; }
/// <summary>
/// 文件名模板
/// </summary>
public string? SavePattern { get; set; }
/// <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>
public bool CheckContentLength { get; set; } = true;
/// <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>
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; }
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
}
}

View File

@ -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<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}[/]");
}
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");

View File

@ -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)
{
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)

View File

@ -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; }
}
}

View File

@ -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);

View File

@ -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();
}
}
}