diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
index 50610ca..01b7f9b 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
@@ -36,6 +36,9 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_selectAudio_more { get => GetText("cmd_selectAudio_more"); }
public static string cmd_selectSubtitle { get => GetText("cmd_selectSubtitle"); }
public static string cmd_selectSubtitle_more { get => GetText("cmd_selectSubtitle_more"); }
+ public static string cmd_customHLSMethod { get => GetText("cmd_customHLSMethod"); }
+ public static string cmd_customHLSKey { get => GetText("cmd_customHLSKey"); }
+ public static string cmd_customHLSIv { get => GetText("cmd_customHLSIv"); }
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"); }
@@ -57,6 +60,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
public static string cmd_concurrentDownload { get => GetText("cmd_concurrentDownload"); }
+ public static string cmd_useSystemProxy { get => GetText("cmd_useSystemProxy"); }
public static string cmd_liveKeepSegments { get => GetText("cmd_liveKeepSegments"); }
public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); }
public static string cmd_liveRealTimeMerge { get => GetText("cmd_liveRealTimeMerge"); }
diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
index ca22b31..674bcdf 100644
--- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
@@ -244,17 +244,41 @@ namespace N_m3u8DL_RE.Common.Resource
zhTW: "錄製直播時即時合併",
enUS: "Real-time merge into file when recording live"
),
+ ["cmd_useSystemProxy"] = new TextContainer
+ (
+ zhCN: "使用系统默认代理",
+ zhTW: "使用系統默認代理",
+ enUS: "Use system default proxy"
+ ),
["cmd_livePerformAsVod"] = new TextContainer
(
zhCN: "以点播方式下载直播流",
zhTW: "以點播方式下載直播流",
enUS: "Download live streams as vod"
),
+ ["cmd_customHLSMethod"] = new TextContainer
+ (
+ zhCN: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
+ zhTW: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
+ enUS: "Set HLS encryption method (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)"
+ ),
+ ["cmd_customHLSKey"] = new TextContainer
+ (
+ zhCN: "指定HLS解密KEY. 可以是文件, HEX或Base64",
+ zhTW: "指定HLS解密KEY. 可以是文件, HEX或Base64",
+ enUS: "Set the HLS decryption key. Can be file, HEX or Base64"
+ ),
+ ["cmd_customHLSIv"] = new TextContainer
+ (
+ zhCN: "指定HLS解密IV. 可以是文件, HEX或Base64",
+ zhTW: "指定HLS解密IV. 可以是文件, HEX或Base64",
+ enUS: "Set the HLS decryption iv. Can be file, HEX or Base64"
+ ),
["cmd_liveKeepSegments"] = new TextContainer
(
zhCN: "录制直播并开启实时合并时依然保留分片",
zhTW: "錄製直播並開啟即時合併時依然保留分片",
- enUS: "Keep segments when recording a live broadcast and enable liveRealTimeMerge"
+ enUS: "Keep segments when recording a live (liveRealTimeMerge enabled)"
),
["cmd_liveRecordLimit"] = new TextContainer
(
@@ -276,9 +300,9 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_selectVideo"] = new TextContainer
(
- zhCN: "通过正则表达式选择符合要求的视频流. 输入 \"--morehelp select-video\" 以查看详细信息.",
- zhTW: "通過正則表達式選擇符合要求的影片軌. 輸入 \"--morehelp select-video\" 以查看詳細訊息.",
- enUS: "Select video streams by regular expressions. Use \"--morehelp select-video\" for more details."
+ zhCN: "通过正则表达式选择符合要求的视频流. 输入 \"--morehelp select-video\" 以查看详细信息",
+ zhTW: "通過正則表達式選擇符合要求的影片軌. 輸入 \"--morehelp select-video\" 以查看詳細訊息",
+ enUS: "Select video streams by regular expressions. Use \"--morehelp select-video\" for more details"
),
["cmd_selectVideo_more"] = new TextContainer
(
@@ -312,9 +336,9 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_selectAudio"] = new TextContainer
(
- zhCN: "通过正则表达式选择符合要求的音频流. 输入 \"--morehelp select-audio\" 以查看详细信息.",
- zhTW: "通過正則表達式選擇符合要求的音軌. 輸入 \"--morehelp select-audio\" 以查看詳細訊息.",
- enUS: "Select audio streams by regular expressions. Use \"--morehelp select-audio\" for more details."
+ zhCN: "通过正则表达式选择符合要求的音频流. 输入 \"--morehelp select-audio\" 以查看详细信息",
+ zhTW: "通過正則表達式選擇符合要求的音軌. 輸入 \"--morehelp select-audio\" 以查看詳細訊息",
+ enUS: "Select audio streams by regular expressions. Use \"--morehelp select-audio\" for more details"
),
["cmd_selectAudio_more"] = new TextContainer
(
@@ -345,9 +369,9 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_selectSubtitle"] = new TextContainer
(
- zhCN: "通过正则表达式选择符合要求的字幕流. 输入 \"--morehelp select-subtitle\" 以查看详细信息.",
- zhTW: "通過正則表達式選擇符合要求的字幕流. 輸入 \"--morehelp select-subtitle\" 以查看詳細訊息.",
- enUS: "Select subtitle streams by regular expressions. Use \"--morehelp select-subtitle\" for more details."
+ zhCN: "通过正则表达式选择符合要求的字幕流. 输入 \"--morehelp select-subtitle\" 以查看详细信息",
+ zhTW: "通過正則表達式選擇符合要求的字幕流. 輸入 \"--morehelp select-subtitle\" 以查看詳細訊息",
+ enUS: "Select subtitle streams by regular expressions. Use \"--morehelp select-subtitle\" for more details"
),
["cmd_selectSubtitle_more"] = new TextContainer
(
@@ -411,15 +435,15 @@ namespace N_m3u8DL_RE.Common.Resource
),
["cmd_muxAfterDone"] = new TextContainer
(
- zhCN: "所有工作完成时尝试混流分离的音视频. 输入 \"--morehelp mux-after-done\" 以查看详细信息.",
- zhTW: "所有工作完成時嘗試混流分離的影音. 輸入 \"--morehelp mux-after-done\" 以查看詳細訊息.",
- enUS: "When all works is done, try to mux the downloaded streams. Use \"--morehelp mux-after-done\" for more details."
+ zhCN: "所有工作完成时尝试混流分离的音视频. 输入 \"--morehelp mux-after-done\" 以查看详细信息",
+ zhTW: "所有工作完成時嘗試混流分離的影音. 輸入 \"--morehelp mux-after-done\" 以查看詳細訊息",
+ enUS: "When all works is done, try to mux the downloaded streams. Use \"--morehelp mux-after-done\" for more details"
),
["cmd_muxImport"] = new TextContainer
(
- zhCN: "混流时引入外部媒体文件. 输入 \"--morehelp mux-import\" 以查看详细信息.",
- zhTW: "混流時引入外部媒體檔案. 輸入 \"--morehelp mux-import\" 以查看詳細訊息.",
- enUS: "When MuxAfterDone enabled, allow to import local media files. Use \"--morehelp mux-import\" for more details."
+ zhCN: "混流时引入外部媒体文件. 输入 \"--morehelp mux-import\" 以查看详细信息",
+ zhTW: "混流時引入外部媒體檔案. 輸入 \"--morehelp mux-import\" 以查看詳細訊息",
+ enUS: "When MuxAfterDone enabled, allow to import local media files. Use \"--morehelp mux-import\" for more details"
),
["cmd_muxImport_more"] = new TextContainer
(
diff --git a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
index 46ee563..70098db 100644
--- a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
+++ b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
@@ -23,13 +23,14 @@ namespace N_m3u8DL_RE.Common.Util
{
public class HTTPUtil
{
-
- public static readonly HttpClient AppHttpClient = new(new HttpClientHandler
+ public static readonly HttpClientHandler HttpClientHandler = new()
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.All,
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
- })
+ };
+
+ public static readonly HttpClient AppHttpClient = new(HttpClientHandler)
{
Timeout = TimeSpan.FromMinutes(2)
};
diff --git a/src/N_m3u8DL-RE.Common/Util/HexUtil.cs b/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
index e5f0b0b..20944d9 100644
--- a/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
+++ b/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
@@ -13,6 +13,25 @@ namespace N_m3u8DL_RE.Common.Util
return BitConverter.ToString(data).Replace("-", split);
}
+ ///
+ /// 判断是不是HEX字符串
+ ///
+ ///
+ ///
+ public static bool TryParseHexString(string input, out byte[]? bytes)
+ {
+ bytes = null;
+ input = input.ToUpper();
+ if (input.StartsWith("0X"))
+ input = input[2..];
+ if (input.Length % 2 != 0)
+ return false;
+ if (input.Any(c => !"0123456789ABCDEF".Contains(c)))
+ return false;
+ bytes = HexToBytes(input);
+ return true;
+ }
+
public static byte[] HexToBytes(string hex)
{
hex = hex.Trim();
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
index 2073674..95eae91 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
@@ -318,18 +318,6 @@ namespace N_m3u8DL_RE.Parser.Extractor
//解析KEY
else if (line.StartsWith(HLSTags.ext_x_key))
{
- //自定义KEY情况 不读取当前行的KEY信息.
- //对于IV,没自定义且当前行有IV的话 就用
- if (ParserConfig.CustomeKey != null)
- {
- currentEncryptInfo.Key = ParserConfig.CustomeKey;
- if (ParserConfig.CustomeIV == null && line.Contains("IV=0x"))
- currentEncryptInfo.IV = HexUtil.HexToBytes(ParserUtil.GetAttribute(line, "IV"));
- continue;
- }
-
- var iv = ParserUtil.GetAttribute(line, "IV");
- var method = ParserUtil.GetAttribute(line, "METHOD");
var uri = ParserUtil.GetAttribute(line, "URI");
var uri_last = ParserUtil.GetAttribute(lastKeyLine, "URI");
diff --git a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
index 68d72f8..d1c7db7 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
@@ -28,12 +28,21 @@ namespace N_m3u8DL_RE.Parser.Processor.HLS
Logger.Debug("METHOD:{},URI:{},IV:{}", method, uri, iv);
var encryptInfo = new EncryptInfo(method);
+
+ //处理自定义加密方式
+ if (parserConfig.CustomMethod != null)
+ {
+ encryptInfo.Method = parserConfig.CustomMethod.Value;
+ Logger.Warn("METHOD changed to {}", method, encryptInfo.Method);
+ }
+
//IV
if (!string.IsNullOrEmpty(iv))
{
encryptInfo.IV = HexUtil.HexToBytes(iv);
}
- if (parserConfig.CustomeIV != null)
+ //自定义IV
+ if (parserConfig.CustomeIV != null && parserConfig.CustomeIV.Length > 0)
{
encryptInfo.IV = parserConfig.CustomeIV;
}
@@ -41,7 +50,7 @@ namespace N_m3u8DL_RE.Parser.Processor.HLS
//KEY
try
{
- if (parserConfig.CustomeKey != null)
+ if (parserConfig.CustomeKey != null && parserConfig.CustomeKey.Length > 0)
{
encryptInfo.Key = parserConfig.CustomeKey;
}
@@ -75,7 +84,7 @@ namespace N_m3u8DL_RE.Parser.Processor.HLS
{
Logger.WarnMarkUp($"[grey]{_ex.Message.EscapeMarkup()} retryCount: {retryCount}[/]");
Thread.Sleep(1000);
- if (retryCount > 0) goto getHttpKey;
+ if (retryCount-- > 0) goto getHttpKey;
else throw;
}
}
diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
index 4a8e8b3..d75a850 100644
--- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
+++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
@@ -1,5 +1,7 @@
-using N_m3u8DL_RE.Common.Log;
+using N_m3u8DL_RE.Common.Enum;
+using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Common.Resource;
+using N_m3u8DL_RE.Common.Util;
using N_m3u8DL_RE.Entity;
using N_m3u8DL_RE.Enum;
using N_m3u8DL_RE.Util;
@@ -48,9 +50,18 @@ namespace N_m3u8DL_RE.CommandLine
private readonly static Option BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl);
private readonly static Option ConcurrentDownload = new(new string[] { "-mt", "--concurrent-download" }, description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
+ //代理选项
+ private readonly static Option UseSystemProxy = new(new string[] { "--use-system-proxy" }, description: ResString.cmd_useSystemProxy, getDefaultValue: () => true);
+
//morehelp
private readonly static Option MoreHelp = new(new string[] { "--morehelp" }, description: ResString.cmd_moreHelp) { ArgumentHelpName = "OPTION" };
+ //自定义KEY等
+ private readonly static Option CustomHLSMethod = new(name: "--custom-hls-method", description: ResString.cmd_customHLSMethod) { ArgumentHelpName = "METHOD" };
+ private readonly static Option CustomHLSKey = new(name: "--custom-hls-key", description: ResString.cmd_customHLSKey, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
+ private readonly static Option CustomHLSIv = new(name: "--custom-hls-iv", description: ResString.cmd_customHLSIv, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
+
+
//直播相关
private readonly static Option LivePerformAsVod = new(new string[] { "--live-perform-as-vod" }, description: ResString.cmd_livePerformAsVod, getDefaultValue: () => false);
private readonly static Option LiveRealTimeMerge = new(new string[] { "--live-real-time-merge" }, description: ResString.cmd_liveRealTimeMerge, getDefaultValue: () => false);
@@ -65,6 +76,32 @@ namespace N_m3u8DL_RE.CommandLine
private readonly static Option AudioFilter = new(new string[] { "-sa", "--select-audio" }, description: ResString.cmd_selectAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
private readonly static Option SubtitleFilter = new(new string[] { "-ss", "--select-subtitle" }, description: ResString.cmd_selectSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+ ///
+ /// 解析自定义KEY
+ ///
+ ///
+ ///
+ private static byte[]? ParseHLSCustomKey(ArgumentResult result)
+ {
+ var input = result.Tokens.First().Value;
+ try
+ {
+ if (string.IsNullOrEmpty(input))
+ return null;
+ if (File.Exists(input))
+ return File.ReadAllBytes(input);
+ else if (HexUtil.TryParseHexString(input, out byte[]? bytes))
+ return bytes;
+ else
+ return Convert.FromBase64String(input);
+ }
+ catch (Exception)
+ {
+ result.ErrorMessage = "error in parse hls custom key: " + input;
+ return null;
+ }
+ }
+
///
/// 解析录制直播时长限制
///
@@ -302,6 +339,10 @@ namespace N_m3u8DL_RE.CommandLine
LiveKeepSegments = bindingContext.ParseResult.GetValueForOption(LiveKeepSegments),
LiveRecordLimit = bindingContext.ParseResult.GetValueForOption(LiveRecordLimit),
LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod),
+ UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy),
+ CustomHLSMethod = bindingContext.ParseResult.GetValueForOption(CustomHLSMethod),
+ CustomHLSKey = bindingContext.ParseResult.GetValueForOption(CustomHLSKey),
+ CustomHLSIv = bindingContext.ParseResult.GetValueForOption(CustomHLSIv),
};
var parsedHeaders = bindingContext.ParseResult.GetValueForOption(Headers);
@@ -362,6 +403,7 @@ namespace N_m3u8DL_RE.CommandLine
FFmpegBinaryPath,
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
MuxAfterDone,
+ CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy,
LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LiveRecordLimit,
MuxImports, VideoFilter, AudioFilter, SubtitleFilter, MoreHelp
};
diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
index dc05bea..1b23b58 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.Common.Enum;
+using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Entity;
using N_m3u8DL_RE.Enum;
@@ -123,6 +124,10 @@ namespace N_m3u8DL_RE.CommandLine
///
public bool LivePerformAsVod { get; set; }
///
+ /// See: .
+ ///
+ public bool UseSystemProxy { get; set; }
+ ///
/// See: .
///
public SubtitleFormat SubtitleFormat { get; set; }
@@ -174,6 +179,18 @@ namespace N_m3u8DL_RE.CommandLine
/// See: .
///
public StreamFilter? SubtitleFilter { get; set; }
+ ///
+ /// See: .
+ ///
+ public EncryptMethod? CustomHLSMethod { get; set; }
+ ///
+ /// See: .
+ ///
+ public byte[]? CustomHLSKey { get; set; }
+ ///
+ /// See: .
+ ///
+ public byte[]? CustomHLSIv { get; set; }
public bool MuxKeepFiles { get; set; }
}
}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs
index 646a59c..d4730b7 100644
--- a/src/N_m3u8DL-RE/Program.cs
+++ b/src/N_m3u8DL-RE/Program.cs
@@ -58,6 +58,11 @@ namespace N_m3u8DL_RE
{
Logger.LogLevel = option.LogLevel;
+ if (option.UseSystemProxy == false)
+ {
+ HTTPUtil.HttpClientHandler.UseProxy = false;
+ }
+
try
{
//检查互斥的选项
@@ -130,7 +135,10 @@ namespace N_m3u8DL_RE
AppendUrlParams = option.AppendUrlParams,
UrlProcessorArgs = option.UrlProcessorArgs,
BaseUrl = option.BaseUrl!,
- Headers = headers
+ Headers = headers,
+ CustomMethod = option.CustomHLSMethod,
+ CustomeKey = option.CustomHLSKey,
+ CustomeIV = option.CustomHLSIv,
};
//demo1
diff --git a/src/N_m3u8DL-RE/Util/DownloadUtil.cs b/src/N_m3u8DL-RE/Util/DownloadUtil.cs
index 8d1a4ea..70b2dae 100644
--- a/src/N_m3u8DL-RE/Util/DownloadUtil.cs
+++ b/src/N_m3u8DL-RE/Util/DownloadUtil.cs
@@ -1,5 +1,6 @@
using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Common.Resource;
+using N_m3u8DL_RE.Common.Util;
using N_m3u8DL_RE.Entity;
using System;
using System.Collections.Generic;
@@ -14,15 +15,7 @@ namespace N_m3u8DL_RE.Util
{
internal class DownloadUtil
{
- private static readonly HttpClient AppHttpClient = new(new HttpClientHandler
- {
- AllowAutoRedirect = false,
- AutomaticDecompression = DecompressionMethods.All,
- ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
- })
- {
- Timeout = TimeSpan.FromMinutes(2)
- };
+ private static readonly HttpClient AppHttpClient = HTTPUtil.AppHttpClient;
public static async Task DownloadToFileAsync(string url, string path, SpeedContainer speedContainer, Dictionary? headers = null, long? fromPosition = null, long? toPosition = null)
{
diff --git a/src/N_m3u8DL-RE/Util/OtherUtil.cs b/src/N_m3u8DL-RE/Util/OtherUtil.cs
index f7091a8..59b729c 100644
--- a/src/N_m3u8DL-RE/Util/OtherUtil.cs
+++ b/src/N_m3u8DL-RE/Util/OtherUtil.cs
@@ -8,9 +8,9 @@ namespace N_m3u8DL_RE.Util
{
internal class OtherUtil
{
- public static Dictionary SplitHeaderArrayToDic(string[]? headers)
+ public static Dictionary SplitHeaderArrayToDic(string[]? headers)
{
- Dictionary dic = new();
+ Dictionary dic = new();
if (headers != null)
{