diff --git a/src/N_m3u8DL-RE.Common/N_m3u8DL-RE.Common.csproj b/src/N_m3u8DL-RE.Common/N_m3u8DL-RE.Common.csproj
index 6360901..bbd9d1d 100644
--- a/src/N_m3u8DL-RE.Common/N_m3u8DL-RE.Common.csproj
+++ b/src/N_m3u8DL-RE.Common/N_m3u8DL-RE.Common.csproj
@@ -22,16 +22,19 @@
-
- PublicResXFileCodeGenerator
-
PublicResXFileCodeGenerator
- ResString.Designer.cs
+ true
+ ResString.Designer.cs
-
+
PublicResXFileCodeGenerator
+ true
+
+
+ PublicResXFileCodeGenerator
+ true
-
+
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.Designer.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.Designer.cs
index d7e568b..e491cb7 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.Designer.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.Designer.cs
@@ -61,7 +61,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 错误的m3u8 的本地化字符串。
+ /// 查找类似 Bad m3u8 的本地化字符串。
///
public static string badM3u8 {
get {
@@ -70,7 +70,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 二进制合并中... 的本地化字符串。
+ /// 查找类似 Binary merging... 的本地化字符串。
///
public static string binaryMerge {
get {
@@ -79,7 +79,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 验证最后一个分片有效性 的本地化字符串。
+ /// 查找类似 Verifying the validity of the last segment 的本地化字符串。
///
public static string checkingLast {
get {
@@ -88,7 +88,161 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 获取: 的本地化字符串。
+ /// 查找类似 Automatically selects the best tracks of all types 的本地化字符串。
+ ///
+ public static string cmd_autoSelect {
+ get {
+ return ResourceManager.GetString("cmd_autoSelect", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Binary merge 的本地化字符串。
+ ///
+ public static string cmd_binaryMerge {
+ get {
+ return ResourceManager.GetString("cmd_binaryMerge", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Check if the actual number of segments downloaded matches the expected number 的本地化字符串。
+ ///
+ public static string cmd_checkSegmentsCount {
+ get {
+ return ResourceManager.GetString("cmd_checkSegmentsCount", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Delete temporary files when done 的本地化字符串。
+ ///
+ public static string cmd_delAfterDone {
+ get {
+ return ResourceManager.GetString("cmd_delAfterDone", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Pass custom header(s) to server, Example:
+ /// -H "Cookie: mycookie" -H "User-Agent: iOS" 的本地化字符串。
+ ///
+ public static string cmd_header {
+ get {
+ return ResourceManager.GetString("cmd_header", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Input Url or File 的本地化字符串。
+ ///
+ public static string cmd_Input {
+ get {
+ return ResourceManager.GetString("cmd_Input", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set log level 的本地化字符串。
+ ///
+ public static string cmd_logLevel {
+ get {
+ return ResourceManager.GetString("cmd_logLevel", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set output directory 的本地化字符串。
+ ///
+ public static string cmd_saveDir {
+ get {
+ return ResourceManager.GetString("cmd_saveDir", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set output filename 的本地化字符串。
+ ///
+ public static string cmd_saveName {
+ get {
+ return ResourceManager.GetString("cmd_saveName", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Skip download 的本地化字符串。
+ ///
+ public static string cmd_skipDownload {
+ get {
+ return ResourceManager.GetString("cmd_skipDownload", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Skip segments merge 的本地化字符串。
+ ///
+ public static string cmd_skipMerge {
+ get {
+ return ResourceManager.GetString("cmd_skipMerge", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Subtitle output format 的本地化字符串。
+ ///
+ public static string cmd_subFormat {
+ get {
+ return ResourceManager.GetString("cmd_subFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Select only subtitle tracks 的本地化字符串。
+ ///
+ public static string cmd_subOnly {
+ get {
+ return ResourceManager.GetString("cmd_subOnly", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Automatically fix subtitles 的本地化字符串。
+ ///
+ public static string cmd_subtitleFix {
+ get {
+ return ResourceManager.GetString("cmd_subtitleFix", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set download thread count 的本地化字符串。
+ ///
+ public static string cmd_threadCount {
+ get {
+ return ResourceManager.GetString("cmd_threadCount", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set temporary file directory 的本地化字符串。
+ ///
+ public static string cmd_tmpDir {
+ get {
+ return ResourceManager.GetString("cmd_tmpDir", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Set UI language 的本地化字符串。
+ ///
+ public static string cmd_uiLanguage {
+ get {
+ return ResourceManager.GetString("cmd_uiLanguage", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Fetch: 的本地化字符串。
///
public static string fetch {
get {
@@ -97,7 +251,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 正在提取TTML(raw)字幕... 的本地化字符串。
+ /// 查找类似 Extracting TTML(raw) subtitle... 的本地化字符串。
///
public static string fixingTTML {
get {
@@ -106,7 +260,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 正在提取TTML(mp4)字幕... 的本地化字符串。
+ /// 查找类似 Extracting TTML(mp4) subtitle... 的本地化字符串。
///
public static string fixingTTMLmp4 {
get {
@@ -115,7 +269,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 正在提取VTT(raw)字幕... 的本地化字符串。
+ /// 查找类似 Extracting VTT(raw) subtitle... 的本地化字符串。
///
public static string fixingVTT {
get {
@@ -124,7 +278,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 正在提取VTT(mp4)字幕... 的本地化字符串。
+ /// 查找类似 Extracting VTT(mp4) subtitle... 的本地化字符串。
///
public static string fixingVTTmp4 {
get {
@@ -133,7 +287,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 找不到支持的Processor 的本地化字符串。
+ /// 查找类似 No Processor matched 的本地化字符串。
///
public static string keyProcessorNotFound {
get {
@@ -142,7 +296,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 检测到直播流 的本地化字符串。
+ /// 查找类似 Live stream found 的本地化字符串。
///
public static string liveFound {
get {
@@ -151,7 +305,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 加载URL: 的本地化字符串。
+ /// 查找类似 Loading URL: 的本地化字符串。
///
public static string loadingUrl {
get {
@@ -160,7 +314,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 检测到Master列表,开始解析全部流信息 的本地化字符串。
+ /// 查找类似 Master List detected, try parse all streams 的本地化字符串。
///
public static string masterM3u8Found {
get {
@@ -169,7 +323,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 内容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/] 的本地化字符串。
+ /// 查找类似 Content Matched: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/] 的本地化字符串。
///
public static string matchDASH {
get {
@@ -178,7 +332,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 内容匹配: [white on deepskyblue1]HTTP Live Streaming[/] 的本地化字符串。
+ /// 查找类似 Content Matched: [white on deepskyblue1]HTTP Live Streaming[/] 的本地化字符串。
///
public static string matchHLS {
get {
@@ -187,7 +341,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 当前输入不受支持: 的本地化字符串。
+ /// 查找类似 Input not supported: 的本地化字符串。
///
public static string notSupported {
get {
@@ -196,7 +350,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 正在解析媒体信息... 的本地化字符串。
+ /// 查找类似 Parsing streams... 的本地化字符串。
///
public static string parsingStream {
get {
@@ -205,7 +359,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 [grey](按键盘上下键以浏览更多内容)[/] 的本地化字符串。
+ /// 查找类似 [grey](Move up and down to reveal more streams)[/] 的本地化字符串。
///
public static string promptChoiceText {
get {
@@ -214,7 +368,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 (按 [blue]空格键[/] 选择流, [green]回车键[/] 完成选择) 的本地化字符串。
+ /// 查找类似 (Press [blue]<space>[/] to toggle a stream, [green]<enter>[/] to accept) 的本地化字符串。
///
public static string promptInfo {
get {
@@ -223,7 +377,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 请选择 [green]你要下载的内容[/]: 的本地化字符串。
+ /// 查找类似 Please select [green]what you want to download[/]: 的本地化字符串。
///
public static string promptTitle {
get {
@@ -232,7 +386,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 分片数量校验不通过, 共{}个,已下载{}. 的本地化字符串。
+ /// 查找类似 Segment count check not pass, total: {}, downloaded: {}. 的本地化字符串。
///
public static string segmentCountCheckNotPass {
get {
@@ -241,7 +395,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 已选择的流: 的本地化字符串。
+ /// 查找类似 Selected streams: 的本地化字符串。
///
public static string selectedStream {
get {
@@ -250,7 +404,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 开始下载... 的本地化字符串。
+ /// 查找类似 Start downloading... 的本地化字符串。
///
public static string startDownloading {
get {
@@ -259,7 +413,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 已解析, 共计 {} 条媒体流, 基本流 {} 条, 可选音频流 {} 条, 可选字幕流 {} 条 的本地化字符串。
+ /// 查找类似 Extracted, there are {} streams, with {} basic streams, {} audio streams, {} subtitle streams 的本地化字符串。
///
public static string streamsInfo {
get {
@@ -268,7 +422,7 @@ namespace N_m3u8DL_RE.Common.Resource {
}
///
- /// 查找类似 写出meta.json 的本地化字符串。
+ /// 查找类似 Writing meta.json 的本地化字符串。
///
public static string writeJson {
get {
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.en-US.resx b/src/N_m3u8DL-RE.Common/Resource/ResString.en-US.resx
deleted file mode 100644
index 68cb5fb..0000000
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.en-US.resx
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- Bad m3u8
-
-
- Input not supported:
-
-
- Loading URL:
-
-
- Content Matched: [white on deepskyblue1]HTTP Live Streaming[/]
-
-
- Master List detected, try parse all streams
-
-
- Fetch:
-
-
- Please select [green]what you want to download[/]:
-
-
- [grey](Move up and down to reveal more streams)[/]
-
-
- (Press [blue]<space>[/] to toggle a stream, [green]<enter>[/] to accept)
-
-
- Extracted, there are {} streams, with {} basic streams, {} audio streams, {} subtitle streams
-
-
- Live stream found
-
-
- Selected streams:
-
-
- Writing meta.json
-
-
- Content Matched: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]
-
-
- Verifying the validity of the last segment
-
-
- Parsing streams...
-
-
- No Processor matched
-
-
- Start downloading...
-
-
- Segment count check not pass, total: {}, downloaded: {}.
-
-
- Extracting VTT(raw) subtitle...
-
-
- Extracting VTT(mp4) subtitle...
-
-
- Binary merging...
-
-
- Extracting TTML(mp4) subtitle...
-
-
- Extracting TTML(raw) subtitle...
-
-
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.resx b/src/N_m3u8DL-RE.Common/Resource/ResString.resx
index 9b4e647..69ef495 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.resx
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.resx
@@ -98,75 +98,127 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- 错误的m3u8
+ Bad m3u8
- 当前输入不受支持:
+ Input not supported:
- 加载URL:
+ Loading URL:
- 内容匹配: [white on deepskyblue1]HTTP Live Streaming[/]
+ Content Matched: [white on deepskyblue1]HTTP Live Streaming[/]
- 检测到Master列表,开始解析全部流信息
+ Master List detected, try parse all streams
- 获取:
+ Fetch:
- 请选择 [green]你要下载的内容[/]:
+ Please select [green]what you want to download[/]:
- [grey](按键盘上下键以浏览更多内容)[/]
+ [grey](Move up and down to reveal more streams)[/]
- (按 [blue]空格键[/] 选择流, [green]回车键[/] 完成选择)
+ (Press [blue]<space>[/] to toggle a stream, [green]<enter>[/] to accept)
- 已解析, 共计 {} 条媒体流, 基本流 {} 条, 可选音频流 {} 条, 可选字幕流 {} 条
+ Extracted, there are {} streams, with {} basic streams, {} audio streams, {} subtitle streams
- 检测到直播流
+ Live stream found
- 已选择的流:
+ Selected streams:
- 写出meta.json
+ Writing meta.json
- 内容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]
+ Content Matched: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]
- 验证最后一个分片有效性
+ Verifying the validity of the last segment
- 正在解析媒体信息...
+ Parsing streams...
- 找不到支持的Processor
+ No Processor matched
- 开始下载...
+ Start downloading...
- 分片数量校验不通过, 共{}个,已下载{}.
+ Segment count check not pass, total: {}, downloaded: {}.
- 正在提取VTT(raw)字幕...
+ Extracting VTT(raw) subtitle...
- 正在提取VTT(mp4)字幕...
+ Extracting VTT(mp4) subtitle...
- 二进制合并中...
+ Binary merging...
- 正在提取TTML(mp4)字幕...
+ Extracting TTML(mp4) subtitle...
- 正在提取TTML(raw)字幕...
+ Extracting TTML(raw) subtitle...
+
+
+ Input Url or File
+
+
+ Pass custom header(s) to server, Example:
+ -H "Cookie: mycookie" -H "User-Agent: iOS"
+
+
+ Set log level
+
+
+ Automatically selects the best tracks of all types
+
+
+ Select only subtitle tracks
+
+
+ Set download thread count
+
+
+ Skip segments merge
+
+
+ Binary merge
+
+
+ Delete temporary files when done
+
+
+ Automatically fix subtitles
+
+
+ Check if the actual number of segments downloaded matches the expected number
+
+
+ Subtitle output format
+
+
+ Set temporary file directory
+
+
+ Set output directory
+
+
+ Set output filename
+
+
+ Set UI language
+
+
+ Skip download
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hans.resx b/src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hans.resx
new file mode 100644
index 0000000..52094e4
--- /dev/null
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hans.resx
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 错误的m3u8
+
+
+ 当前输入不受支持:
+
+
+ 加载URL:
+
+
+ 内容匹配: [white on deepskyblue1]HTTP Live Streaming[/]
+
+
+ 检测到Master列表,开始解析全部流信息
+
+
+ 获取:
+
+
+ 请选择 [green]你要下载的内容[/]:
+
+
+ [grey](按键盘上下键以浏览更多内容)[/]
+
+
+ (按 [blue]空格键[/] 选择流, [green]回车键[/] 完成选择)
+
+
+ 已解析, 共计 {} 条媒体流, 基本流 {} 条, 可选音频流 {} 条, 可选字幕流 {} 条
+
+
+ 检测到直播流
+
+
+ 已选择的流:
+
+
+ 写出meta.json
+
+
+ 内容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]
+
+
+ 验证最后一个分片有效性
+
+
+ 正在解析媒体信息...
+
+
+ 找不到支持的Processor
+
+
+ 开始下载...
+
+
+ 分片数量校验不通过, 共{}个,已下载{}.
+
+
+ 正在提取VTT(raw)字幕...
+
+
+ 正在提取VTT(mp4)字幕...
+
+
+ 二进制合并中...
+
+
+ 正在提取TTML(mp4)字幕...
+
+
+ 正在提取TTML(raw)字幕...
+
+
+ 链接或文件
+
+
+ 为HTTP请求设置特定的请求头, 例如:
+ -H "Cookie: mycookie" -H "User-Agent: iOS"
+
+
+ 设置日志级别
+
+
+ 自动选择所有类型的最佳轨道
+
+
+ 只选取字幕轨道
+
+
+ 设置下载线程数
+
+
+ 跳过合并分片
+
+
+ 二进制合并
+
+
+ 完成后删除临时文件
+
+
+ 自动修正字幕
+
+
+ 检测实际下载的分片数量和预期数量是否匹配
+
+
+ 字幕输出类型
+
+
+ 设置临时文件存储目录
+
+
+ 设置输出目录
+
+
+ 设置保存文件名
+
+
+ 设置UI语言
+
+
+ 跳过下载
+
+
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.zh-TW.resx b/src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hant.resx
similarity index 79%
rename from src/N_m3u8DL-RE.Common/Resource/ResString.zh-TW.resx
rename to src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hant.resx
index 2cda1c5..7952de7 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.zh-TW.resx
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.zh-Hant.resx
@@ -169,4 +169,56 @@
正在提取TTML(raw)字幕...
+
+ 鏈接或文件
+
+
+ 為HTTP請求設置特定的請求頭, 例如:
+ -H "Cookie: mycookie" -H "User-Agent: iOS"
+
+
+ 設置日誌級別
+
+
+ 自動選擇所有類型的最佳軌道
+
+
+ 只選取字幕軌道
+
+
+ 設置下載線程數
+
+
+ 跳過合併分片
+
+
+ 二進制合併
+
+
+ 完成後刪除臨時文件
+
+
+ 自動修正字幕
+
+
+ 檢測實際下載的分片數量和預期數量是否匹配
+
+
+ 字幕輸出類型
+
+
+ 設置臨時文件存儲目錄
+
+
+ 設置輸出目錄
+
+
+ 設置保存文件名
+
+
+ 設置UI語言
+
+
+ 跳過下載
+
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.sln b/src/N_m3u8DL-RE.sln
index 7f69da1..8954741 100644
--- a/src/N_m3u8DL-RE.sln
+++ b/src/N_m3u8DL-RE.sln
@@ -32,6 +32,7 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
+ RESX_NeutralResourcesLanguage = en-US
SolutionGuid = {87F963D4-EA06-413D-9372-C726711C32B5}
EndGlobalSection
EndGlobal
diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
new file mode 100644
index 0000000..65ae774
--- /dev/null
+++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
@@ -0,0 +1,89 @@
+using N_m3u8DL_RE.Common.Log;
+using N_m3u8DL_RE.Common.Resource;
+using N_m3u8DL_RE.Enum;
+using System.CommandLine;
+using System.CommandLine.Binding;
+using System.Globalization;
+
+namespace N_m3u8DL_RE.CommandLine
+{
+
+ internal class CommandInvoker
+ {
+ private readonly static Argument Input = new(name: "input", description: ResString.cmd_Input);
+ private readonly static Option TmpDir = new(new string[] { "--tmp-dir" }, description: ResString.cmd_tmpDir);
+ private readonly static Option SaveDir = new(new string[] { "--save-dir" }, description: ResString.cmd_saveDir);
+ private readonly static Option SaveName = new(new string[] { "--save-name" }, description: ResString.cmd_saveName);
+ private readonly static Option UILanguage = new(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage);
+ private readonly static Option Headers = new(new string[] { "--header", "-H" }, description: ResString.cmd_header) { Arity = ArgumentArity.ZeroOrMore, 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);
+ private readonly static Option SubOnly = new(new string[] { "--sub-only" }, description: ResString.cmd_subOnly, getDefaultValue: () => false);
+ private readonly static Option ThreadCount = new(new string[] { "--thread-count" }, description: ResString.cmd_threadCount, getDefaultValue: () => 8);
+ private readonly static Option SkipMerge = new(new string[] { "--skip-merge" }, description: ResString.cmd_skipMerge, getDefaultValue: () => false);
+ private readonly static Option SkipDownload = new(new string[] { "--skip-download" }, description: ResString.cmd_skipDownload, getDefaultValue: () => false);
+ private readonly static Option BinaryMerge = new(new string[] { "--binary-merge" }, description: ResString.cmd_binaryMerge, getDefaultValue: () => true);
+ private readonly static Option DelAfterDone = new(new string[] { "--del-after-done" }, description: ResString.cmd_delAfterDone, getDefaultValue: () => true);
+ private readonly static Option AutoSubtitleFix = new(new string[] { "--auto-subtitle-fix" }, description: ResString.cmd_subtitleFix, getDefaultValue: () => true);
+ private readonly static Option CheckSegmentsCount = new(new string[] { "--check-segments-count" }, description: ResString.cmd_checkSegmentsCount, getDefaultValue: () => true);
+
+ class MyOptionBinder : BinderBase
+ {
+ protected override MyOption GetBoundValue(BindingContext bindingContext)
+ {
+ var option = new MyOption
+ {
+ Input = bindingContext.ParseResult.GetValueForArgument(Input),
+ Headers = bindingContext.ParseResult.GetValueForOption(Headers),
+ LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel),
+ AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect),
+ SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge),
+ BinaryMerge = bindingContext.ParseResult.GetValueForOption(BinaryMerge),
+ DelAfterDone = bindingContext.ParseResult.GetValueForOption(DelAfterDone),
+ AutoSubtitleFix = bindingContext.ParseResult.GetValueForOption(AutoSubtitleFix),
+ CheckSegmentsCount = bindingContext.ParseResult.GetValueForOption(CheckSegmentsCount),
+ SubtitleFormat = bindingContext.ParseResult.GetValueForOption(SubtitleFormat),
+ SubOnly = bindingContext.ParseResult.GetValueForOption(SubOnly),
+ TmpDir = bindingContext.ParseResult.GetValueForOption(TmpDir),
+ SaveDir = bindingContext.ParseResult.GetValueForOption(SaveDir),
+ SaveName = bindingContext.ParseResult.GetValueForOption(SaveName),
+ ThreadCount = bindingContext.ParseResult.GetValueForOption(ThreadCount),
+ UILanguage = bindingContext.ParseResult.GetValueForOption(UILanguage),
+ SkipDownload = bindingContext.ParseResult.GetValueForOption(SkipDownload),
+ };
+
+ //在这里设置语言
+
+ string loc = "en-US";
+ string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
+ if (currLoc == "zh-CN" || currLoc == "zh-SG") loc = "zh-Hans";
+ else if (currLoc.StartsWith("zh-")) loc = "zh-Hant";
+
+ //以用户选择优先
+ loc = option.UILanguage ?? loc;
+
+ CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(loc);
+ Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(loc);
+ Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(loc);
+ ResString.Culture = CultureInfo.GetCultureInfo(loc);
+
+ return option;
+ }
+ }
+
+
+ public static async Task InvokeArgs(string[] args, Func action)
+ {
+ var rootCommand = new RootCommand("N_m3u8DL-RE Beta version: 20220719")
+ {
+ Input, TmpDir, SaveDir, SaveName, ThreadCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
+ BinaryMerge, DelAfterDone, Headers, SubOnly, SubtitleFormat, AutoSubtitleFix, LogLevel, UILanguage
+ };
+ rootCommand.TreatUnmatchedTokensAsErrors = true;
+ rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
+
+ return await rootCommand.InvokeAsync(args);
+ }
+ }
+}
diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
new file mode 100644
index 0000000..e02bdda
--- /dev/null
+++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
@@ -0,0 +1,77 @@
+using N_m3u8DL_RE.Common.Log;
+using N_m3u8DL_RE.Enum;
+
+namespace N_m3u8DL_RE.CommandLine
+{
+ internal class MyOption
+ {
+ ///
+ /// See: .
+ ///
+ public string Input { get; set; } = default!;
+ ///
+ /// See: .
+ ///
+ public string[]? Headers { get; set; }
+ ///
+ /// See: .
+ ///
+ public LogLevel LogLevel { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool AutoSelect { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool SubOnly { get; set; }
+ ///
+ /// See: .
+ ///
+ public int ThreadCount { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool SkipMerge { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool BinaryMerge { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool DelAfterDone { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool AutoSubtitleFix { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool CheckSegmentsCount { get; set; }
+ ///
+ /// See: .
+ ///
+ public bool SkipDownload { get; set; }
+ ///
+ /// See: .
+ ///
+ public SubtitleFormat SubtitleFormat { get; set; }
+ ///
+ /// See: .
+ ///
+ public string? TmpDir { get; set; }
+ ///
+ /// See: .
+ ///
+ public string? SaveDir { get; set; }
+ ///
+ /// See: .
+ ///
+ public string? SaveName { get; set; }
+ ///
+ /// See: .
+ ///
+ public string? UILanguage { 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 4f23a9d..7b368f0 100644
--- a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs
+++ b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs
@@ -1,4 +1,7 @@
-using System;
+using N_m3u8DL_RE.CommandLine;
+using N_m3u8DL_RE.Enum;
+using N_m3u8DL_RE.Parser.Config;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -8,6 +11,22 @@ namespace N_m3u8DL_RE.Config
{
internal class DownloaderConfig
{
+ public DownloaderConfig() { }
+
+ 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;
+ }
+
///
/// 临时文件存储目录
///
@@ -49,6 +68,10 @@ namespace N_m3u8DL_RE.Config
///
public bool AutoSubtitleFix { get; set; } = true;
///
+ /// 字幕格式
+ ///
+ public SubtitleFormat SubtitleFormat { get; set; } = SubtitleFormat.VTT;
+ ///
/// 请求头
///
public Dictionary Headers { get; set; } = new Dictionary()
diff --git a/src/N_m3u8DL-RE/Directory.Build.props b/src/N_m3u8DL-RE/Directory.Build.props
index ad71932..630e98d 100644
--- a/src/N_m3u8DL-RE/Directory.Build.props
+++ b/src/N_m3u8DL-RE/Directory.Build.props
@@ -5,6 +5,11 @@
true
true
full
+ link
+ true
+ false
+ true
+ zh-Hans;zh-Hant;en-US
7.0.0-*
diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
index 2d85386..32955f8 100644
--- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
+++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
@@ -40,7 +40,7 @@ namespace N_m3u8DL_RE.DownloadManager
if (segments == null) return false;
var dirName = $"{NowDateTime:yyyy-MM-dd_HH-mm-ss}_{streamSpec.GroupId}_{streamSpec.Codecs}_{streamSpec.Language}";
- var tmpDir = DownloaderConfig.TmpDir ?? Path.Combine(Environment.CurrentDirectory, dirName);
+ var tmpDir = Path.Combine(DownloaderConfig.TmpDir ?? Environment.CurrentDirectory, dirName);
var saveDir = DownloaderConfig.SaveDir ?? Environment.CurrentDirectory;
var saveName = DownloaderConfig.SaveName ?? dirName;
var headers = DownloaderConfig.Headers;
@@ -152,12 +152,19 @@ namespace N_m3u8DL_RE.DownloadManager
foreach (var item in files) File.Delete(item);
FileDic.Clear();
var index = 0;
- var path = Path.Combine(tmpDir, index.ToString(pad) + $".fix.{streamSpec.Extension ?? "clip"}");
- var vttContentFixed = finalVtt.ToStringWithHeader();
- await File.WriteAllTextAsync(path, vttContentFixed, new UTF8Encoding(false));
+ var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
+ var subContentFixed = finalVtt.ToStringWithHeader();
+ //转换字幕格式
+ if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
+ {
+ path = Path.ChangeExtension(path, ".srt");
+ subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
+ output = Path.ChangeExtension(output, ".srt");
+ }
+ await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
FileDic[keys.First()] = new DownloadResult()
{
- ActualContentLength = vttContentFixed.Length,
+ ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
}
@@ -181,15 +188,22 @@ namespace N_m3u8DL_RE.DownloadManager
FileDic.Clear();
var index = 0;
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
- var vttContentFixed = finalVtt.ToStringWithHeader();
- await File.WriteAllTextAsync(path, vttContentFixed, new UTF8Encoding(false));
+ var subContentFixed = finalVtt.ToStringWithHeader();
+ //转换字幕格式
+ if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
+ {
+ path = Path.ChangeExtension(path, ".srt");
+ subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
+ output = Path.ChangeExtension(output, ".srt");
+ }
+ await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
FileDic[firstKey] = new DownloadResult()
{
- ActualContentLength = vttContentFixed.Length,
+ ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
//修改输出后缀
- output = Path.ChangeExtension(output, ".vtt");
+ output = Path.ChangeExtension(output, Path.GetExtension(path));
}
}
@@ -207,15 +221,22 @@ namespace N_m3u8DL_RE.DownloadManager
FileDic.Clear();
var index = 0;
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
- var vttContentFixed = finalVtt.ToStringWithHeader();
- await File.WriteAllTextAsync(path, vttContentFixed, new UTF8Encoding(false));
+ var subContentFixed = finalVtt.ToStringWithHeader();
+ //转换字幕格式
+ if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
+ {
+ path = Path.ChangeExtension(path, ".srt");
+ subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
+ output = Path.ChangeExtension(output, ".srt");
+ }
+ await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
FileDic[firstKey] = new DownloadResult()
{
- ActualContentLength = vttContentFixed.Length,
+ ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
//修改输出后缀
- output = Path.ChangeExtension(output, ".vtt");
+ output = Path.ChangeExtension(output, Path.GetExtension(path));
}
//自动修复TTML mp4字幕
@@ -237,15 +258,22 @@ namespace N_m3u8DL_RE.DownloadManager
FileDic.Clear();
var index = 0;
var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
- var vttContentFixed = finalVtt.ToStringWithHeader();
- await File.WriteAllTextAsync(path, vttContentFixed, new UTF8Encoding(false));
+ var subContentFixed = finalVtt.ToStringWithHeader();
+ //转换字幕格式
+ if (DownloaderConfig.SubtitleFormat != Enum.SubtitleFormat.VTT)
+ {
+ path = Path.ChangeExtension(path, ".srt");
+ subContentFixed = ConvertUtil.WebVtt2Other(finalVtt, DownloaderConfig.SubtitleFormat);
+ output = Path.ChangeExtension(output, ".srt");
+ }
+ await File.WriteAllTextAsync(path, subContentFixed, new UTF8Encoding(false));
FileDic[firstKey] = new DownloadResult()
{
- ActualContentLength = vttContentFixed.Length,
+ ActualContentLength = subContentFixed.Length,
ActualFilePath = path
};
//修改输出后缀
- output = Path.ChangeExtension(output, ".vtt");
+ output = Path.ChangeExtension(output, Path.GetExtension(path));
}
//合并
@@ -262,6 +290,7 @@ namespace N_m3u8DL_RE.DownloadManager
throw new NotImplementedException();
}
}
+
//删除临时文件夹
if (DownloaderConfig.DelAfterDone)
{
diff --git a/src/N_m3u8DL-RE/Enum/SubtitleFormat.cs b/src/N_m3u8DL-RE/Enum/SubtitleFormat.cs
new file mode 100644
index 0000000..e1c75e7
--- /dev/null
+++ b/src/N_m3u8DL-RE/Enum/SubtitleFormat.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace N_m3u8DL_RE.Enum
+{
+ internal enum SubtitleFormat
+ {
+ VTT,
+ SRT
+ }
+}
diff --git a/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj b/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj
index bd6cbb6..924f5c5 100644
--- a/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj
+++ b/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj
@@ -7,18 +7,15 @@
enable
preview
enable
+ 0.0.1
-
-
-
-
-
+
diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs
index a4c53a9..b722547 100644
--- a/src/N_m3u8DL-RE/Program.cs
+++ b/src/N_m3u8DL-RE/Program.cs
@@ -19,26 +19,32 @@ using N_m3u8DL_RE.Config;
using N_m3u8DL_RE.Util;
using System.Diagnostics;
using N_m3u8DL_RE.DownloadManager;
+using N_m3u8DL_RE.CommandLine;
+using System.Reflection;
namespace N_m3u8DL_RE
{
internal class Program
{
-
static async Task Main(string[] args)
{
- string loc = "en-US";
- string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
- if (currLoc == "zh-TW" || currLoc == "zh-HK" || currLoc == "zh-MO") loc = "zh-TW";
- else if (currLoc == "zh-CN" || currLoc == "zh-SG") loc = "zh-CN";
- //设置语言
- CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(loc);
- Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(loc);
- //Logger.LogLevel = LogLevel.DEBUG;
+ await CommandInvoker.InvokeArgs(args, DoWorkAsync);
+ }
+
+ static async Task DoWorkAsync(MyOption option)
+ {
+ Logger.LogLevel = option.LogLevel;
try
{
var parserConfig = new ParserConfig();
+
+ //设置Headers
+ foreach (var item in ConvertUtil.SplitHeaderArrayToDic(option.Headers))
+ {
+ parserConfig.Headers[item.Key] = item.Value;
+ }
+
//demo1
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
//demo2
@@ -53,19 +59,29 @@ namespace N_m3u8DL_RE
//url = "https://rest-as.ott.kaltura.com/api_v3/service/assetFile/action/playManifest/partnerId/147/assetId/1304099/assetType/media/assetFileId/16136929/contextType/PLAYBACK/isAltUrl/False/ks/djJ8MTQ3fMusTFH6PCZpcrfKLQwI-pPm9ex6b6r49wioe32WH2udXeM4reyWIkSDpi7HhvhxBHAHAKiHrcnkmIJQpyAt4MuDBG0ywGQ-jOeqQFcTRQ8BGJGw6g-smSBLwSbo4CCx9M9vWNJX3GkOfhoMAY4yRU-ur3okHiVq1mUJ82XBd_iVqLuzodnc9sJEtcHH0zc5CoPiTq2xor-dq3yDURnZm3isfSN3t9uLIJEW09oE-SJ84DM5GUuFUdbnIV8bdcWUsPicUg-Top1G2D3WcWXq4EvPnwvD8jrC_vsiOpLHf5akAwtdGsJ6__cXUmT7a-QlfjdvaZ5T8UhDLnttHmsxYs2E5c0lh4uOvvJou8dD8iYxUexlPI2j4QUkBRxqOEVLSNV3Y82-5TTRqgnK_uGYXHwk7EAmDws7hbLj2-DJ1heXDcye3OJYdunJgAS-9ma5zmQQNiY_HYh6wj2N1HpCTNAtWWga6R9fC0VgBTZbidW-YwMSGzIvMQfIfWKe15X7Oc_hCs-zGfW9XeRJZrutcWKK_D_HlzpQVBF2vIF3XgaI/a.mpd";
//url = "https://dash.akamaized.net/dash264/TestCases/2c/qualcomm/1/MultiResMPEG2.mpd";
//url = "http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8";
- //url = "https://cmaf.lln.latam.hbomaxcdn.com/videos/GYPGKMQjoDkVLBQEAAAAo/1/1b5ad5/1_single_J8sExA_1080hi.mpd";
+ url = "https://cmaf.lln.latam.hbomaxcdn.com/videos/GYPGKMQjoDkVLBQEAAAAo/1/1b5ad5/1_single_J8sExA_1080hi.mpd";
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/multi_subs.mpd"; //ttml + mp4
//url = "http://media.axprod.net/TestVectors/v6-Clear/Manifest_1080p.mpd"; //vtt + mp4
- url = "https://livesim.dashif.org/dash/vod/testpic_2s/xml_subs.mpd"; //ttml
+ //url = "https://livesim.dashif.org/dash/vod/testpic_2s/xml_subs.mpd"; //ttml
+ //url = "https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8"; //HLS vtt
+ //url = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8"; //高级HLS fMP4+VTT
+ //url = "https://events-delivery.apple.com/0205eyyhwbbqexozkwmgccegwnjyrktg/m3u8/vod_index-dpyfrsVksFWjneFiptbXnAMYBtGYbXeZ.m3u8"; //高级HLS fMP4+VTT
+ //url = "https://apionvod5.seezntv.com/ktmain1/cold/CP/55521/202207/media/MIAM61RPSGL150000100_DRM/MIAM61RPSGL150000100_H.m3u8?sid=0000000F50000040000A700000020000";
+ //url = "https://ewcdn12.nowe.com/session/16-5-72579e3-2103014898783810281/Content/DASH_VOS3/VOD/6908/19585/d2afa5fe-e9c8-40f0-8d18-648aaaf292b6/f677841a-9d8f-2ff5-3517-674ba49ef192/manifest.mpd?token=894db5d69931835f82dd8e393974ef9f_1658146180";
+ //url = "https://ols-ww100-cp.akamaized.net/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/65794a72596d6c30496a6f7a4e6a67324e4441774d444173496e42735958526d62334a74496a6f695a47567a6133527663434973496d526c646d6c6a5a565235634755694f694a335a5749694c434a746232526c62434936496e6470626d527664334d694c434a7663315235634755694f694a6a61484a76625755694c434a7663794936496a45774d6934774c6a41694c434a68634841694f69497a4c6a416966513d3d/dash.mpd?cpatoken=exp=1658223027~acl=/manifest/master/06ee6f68-ee80-11ea-9bc5-02b68fb543c4/*~hmac=644c608aac361f688e9b24b0f345c801d0f2d335819431d1873ff7aeac46d6b2&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2VfaWQiOm51bGwsIndhdGNoX3R5cGUiOiJQUkVNSVVNIiwicHJvZ3JhbV9pZCI6ImUwMWRmYjAyLTM1YmItMTFlOS1hNDI3LTA2YTA0MTdjMWQxZSIsImFkX3RhZyI6ZmFsc2UsInBhcmVudF9wcm9ncmFtX2lkIjoiZmJmMDc2MDYtMzNmYi0xMWU5LWE0MjctMDZhMDQxN2MxZDFlIiwiY2xpZW50X2lkIjoiNGQ3MDViZTQtYTQ5ZS0xMWVhLWJiMzctMDI0MmFjMTMwMDAyIiwidmlkZW9fdHlwZSI6InZvZCIsImdyYW50X3R5cGUiOiJwbGF5X3ZpZGVvIiwidXNlcl9pZCI6ImFhNTMxZWQ2LWM2NTMtNDliYS04NGI1LWFkZDRmNGIzNGMyNyIsImN1cnJlbnRfc2Vjb25kIjowLCJyZXBvcnRfaWQiOiJOU1RHIiwic2NvcGUiOlsicHVibGljOi4qIiwibWU6LioiXSwiZXhwIjoxNjU4Mzk1ODI2LCJkZXRlY3Rpb25faWQiOm51bGwsInZpZGVvX2lkIjoiODc0Yjk0ZDItNzZiYi00YzliLTgzODQtNzJlMTA0NWVjOGMxIiwiaXNzIjoiQXNpYXBsYXktT0F1dGgtU2VydmVyIiwiaWF0IjoxNjU4MTM2NjI2LCJ0ZXJyaXRvcnkiOiJUVyJ9.1juciYIyMNzykXKu-nGLR_cYWvPMEAE9ub-ny7RzFnM";
+ //url = "https://a38avoddashs3ww-a.akamaihd.net/ondemand/iad_2/8e91/f2f2/ec5a/430f-bd7a-0779f4a0189d/685cda75-609c-41c1-86bb-688f4cdb5521_corrected.mpd";
+ url = "https://dcs-vod.mp.lura.live/vod/p/session/manifest.mpd?i=i177610817-nb45239a2-e962-4137-bc70-1790359619e6";
+ //url = "https://theater.kktv.com.tw/98/04000198010001_584b26392f7f7f11fc62299214a55fb7/16113081449d8d5e9960_sub_dash.mpd"; //MPD+VTT
+ //url = "";
- if (args.Length > 0)
+ if (!string.IsNullOrEmpty(option.Input))
{
- url = args[0];
+ url = option.Input;
}
if (string.IsNullOrEmpty(url))
{
- url = AnsiConsole.Ask("请输入 [green]URL[/]: ");
+ url = AnsiConsole.Ask("Input [green]URL[/]: ");
}
//流提取器配置
@@ -95,7 +111,27 @@ namespace N_m3u8DL_RE
}
//展示交互式选择框
- var selectedStreams = PromptUtil.SelectStreams(lists);
+ //var selectedStreams = PromptUtil.SelectStreams(lists);
+ var selectedStreams = new List();
+ if (option.AutoSelect)
+ {
+ if (basicStreams.Any())
+ selectedStreams.Add(basicStreams.First());
+ var langs = audios.DistinctBy(a => a.Language).Select(a => a.Language);
+ foreach (var lang in langs)
+ {
+ selectedStreams.Add(audios.Where(a => a.Language == lang).OrderByDescending(a => a.Bandwidth).First());
+ }
+ selectedStreams.AddRange(subs);
+ }
+ else if (option.SubOnly)
+ {
+ selectedStreams.AddRange(subs);
+ }
+ else
+ {
+ selectedStreams = PromptUtil.SelectStreams(lists);
+ }
//一个以上的话,需要手动重新加载playlist
if (lists.Count() > 1)
await extractor.FetchPlayListAsync(selectedStreams);
@@ -107,29 +143,33 @@ namespace N_m3u8DL_RE
Logger.InfoMarkUp(item.ToString());
}
+ if (option.SkipDownload)
+ {
+ return;
+ }
+
+#if DEBUG
Console.ReadKey();
+#endif
//下载配置
- var downloadConfig = new DownloaderConfig()
+ var downloadConfig = new DownloaderConfig(option)
{
Headers = parserConfig.Headers,
- BinaryMerge = true,
- DelAfterDone = true,
- CheckSegmentsCount = true
};
//开始下载
var sdm = new SimpleDownloadManager(downloadConfig);
var result = await sdm.StartDownloadAsync(selectedStreams);
if (result)
- Logger.InfoMarkUp("[white on green]成功[/]");
+ Logger.InfoMarkUp("[white on green]Done[/]");
else
- Logger.ErrorMarkUp("[white on red]失败[/]");
+ Logger.ErrorMarkUp("[white on red]Faild[/]");
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
+ await Task.Delay(3000);
}
- //Console.ReadKey();
}
}
}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Util/ConvertUtil.cs b/src/N_m3u8DL-RE/Util/ConvertUtil.cs
new file mode 100644
index 0000000..02076c3
--- /dev/null
+++ b/src/N_m3u8DL-RE/Util/ConvertUtil.cs
@@ -0,0 +1,55 @@
+using N_m3u8DL_RE.Common.Entity;
+using N_m3u8DL_RE.Common.Log;
+using N_m3u8DL_RE.Enum;
+using System.Text;
+
+namespace N_m3u8DL_RE.Util
+{
+ internal class ConvertUtil
+ {
+ public static Dictionary SplitHeaderArrayToDic(string[]? headers)
+ {
+ Dictionary dic = new();
+
+ if (headers != null)
+ {
+ foreach (string header in headers)
+ {
+ var index = header.IndexOf(':');
+ if (index != -1)
+ {
+ dic[header[..index].Trim()] = header[(index + 1)..].Trim();
+ }
+ }
+ }
+
+ return dic;
+ }
+
+ private static string WebVtt2Srt(WebVttSub vtt)
+ {
+ StringBuilder sb = new StringBuilder();
+ int index = 1;
+ foreach (var c in vtt.Cues)
+ {
+ sb.AppendLine($"{index++}");
+ sb.AppendLine(c.StartTime.ToString(@"hh\:mm\:ss\,fff") + " --> " + c.EndTime.ToString(@"hh\:mm\:ss\,fff"));
+ sb.AppendLine(c.Payload);
+ sb.AppendLine();
+ }
+ sb.AppendLine();
+ return sb.ToString();
+ }
+
+ public static string WebVtt2Other(WebVttSub vtt, SubtitleFormat toFormat)
+ {
+ Logger.Debug($"Convert {SubtitleFormat.VTT} ==> {toFormat}");
+ return toFormat switch
+ {
+ SubtitleFormat.VTT => vtt.ToStringWithHeader(),
+ SubtitleFormat.SRT => WebVtt2Srt(vtt),
+ _ => throw new NotSupportedException($"{toFormat} not supported!")
+ };
+ }
+ }
+}
diff --git a/src/N_m3u8DL-RE/rd.xml b/src/N_m3u8DL-RE/rd.xml
index ef7a25e..a9352dc 100644
--- a/src/N_m3u8DL-RE/rd.xml
+++ b/src/N_m3u8DL-RE/rd.xml
@@ -1,7 +1,7 @@
-
+
+
\ No newline at end of file