新增`--allow-hls-multi-ext-map` (#503)
This commit is contained in:
parent
0c73b730bb
commit
e8e92b6337
|
@ -65,6 +65,7 @@ public class ResString
|
||||||
public static string cmd_skipDownload => GetText("cmd_skipDownload");
|
public static string cmd_skipDownload => GetText("cmd_skipDownload");
|
||||||
public static string cmd_noDateInfo => GetText("cmd_noDateInfo");
|
public static string cmd_noDateInfo => GetText("cmd_noDateInfo");
|
||||||
public static string cmd_noLog => GetText("cmd_noLog");
|
public static string cmd_noLog => GetText("cmd_noLog");
|
||||||
|
public static string cmd_allowHlsMultiExtMap => GetText("cmd_allowHlsMultiExtMap");
|
||||||
public static string cmd_skipMerge => GetText("cmd_skipMerge");
|
public static string cmd_skipMerge => GetText("cmd_skipMerge");
|
||||||
public static string cmd_subFormat => GetText("cmd_subFormat");
|
public static string cmd_subFormat => GetText("cmd_subFormat");
|
||||||
public static string cmd_subOnly => GetText("cmd_subOnly");
|
public static string cmd_subOnly => GetText("cmd_subOnly");
|
||||||
|
@ -111,6 +112,7 @@ public class ResString
|
||||||
public static string liveFound => GetText("liveFound");
|
public static string liveFound => GetText("liveFound");
|
||||||
public static string loadingUrl => GetText("loadingUrl");
|
public static string loadingUrl => GetText("loadingUrl");
|
||||||
public static string masterM3u8Found => GetText("masterM3u8Found");
|
public static string masterM3u8Found => GetText("masterM3u8Found");
|
||||||
|
public static string allowHlsMultiExtMap => GetText("allowHlsMultiExtMap");
|
||||||
public static string matchDASH => GetText("matchDASH");
|
public static string matchDASH => GetText("matchDASH");
|
||||||
public static string matchMSS => GetText("matchMSS");
|
public static string matchMSS => GetText("matchMSS");
|
||||||
public static string matchTS => GetText("matchTS");
|
public static string matchTS => GetText("matchTS");
|
||||||
|
|
|
@ -172,6 +172,12 @@ internal class StaticText
|
||||||
zhTW: "關閉日誌文件輸出",
|
zhTW: "關閉日誌文件輸出",
|
||||||
enUS: "Disable log file output"
|
enUS: "Disable log file output"
|
||||||
),
|
),
|
||||||
|
["cmd_allowHlsMultiExtMap"] = new TextContainer
|
||||||
|
(
|
||||||
|
zhCN: "允许HLS中的多个#EXT-X-MAP(实验性)",
|
||||||
|
zhTW: "允許HLS中的多個#EXT-X-MAP(實驗性)",
|
||||||
|
enUS: "Allow multiple #EXT-X-MAP in HLS (experimental)"
|
||||||
|
),
|
||||||
["cmd_appendUrlParams"] = new TextContainer
|
["cmd_appendUrlParams"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "将输入Url的Params添加至分片, 对某些网站很有用, 例如 kakao.com",
|
zhCN: "将输入Url的Params添加至分片, 对某些网站很有用, 例如 kakao.com",
|
||||||
|
@ -820,6 +826,12 @@ internal class StaticText
|
||||||
zhTW: "檢測到Master列表,開始解析全部流訊息",
|
zhTW: "檢測到Master列表,開始解析全部流訊息",
|
||||||
enUS: "Master List detected, try parse all streams"
|
enUS: "Master List detected, try parse all streams"
|
||||||
),
|
),
|
||||||
|
["allowHlsMultiExtMap"] = new TextContainer
|
||||||
|
(
|
||||||
|
zhCN: "已经允许识别多个#EXT-X-MAP标签, 本软件可能无法正确处理, 请手动确认内容完整性",
|
||||||
|
zhTW: "已經允許識別多個#EXT-X-MAP標籤, 本軟件可能無法正確處理, 請手動確認內容完整性",
|
||||||
|
enUS: "Multiple #EXT-X-MAP tags are now allowed for detection. However, this software may not handle them correctly. Please manually verify the content's integrity"
|
||||||
|
),
|
||||||
["matchTS"] = new TextContainer
|
["matchTS"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "内容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
|
zhCN: "内容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
|
||||||
|
|
|
@ -12,8 +12,10 @@ public class ParserConfig
|
||||||
public string OriginalUrl { get; set; }
|
public string OriginalUrl { get; set; }
|
||||||
|
|
||||||
public string BaseUrl { get; set; }
|
public string BaseUrl { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> CustomParserArgs { get; } = new();
|
||||||
|
|
||||||
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
|
public Dictionary<string, string> Headers { get; init; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内容前置处理器. 调用顺序与列表顺序相同
|
/// 内容前置处理器. 调用顺序与列表顺序相同
|
||||||
|
|
|
@ -221,7 +221,13 @@ internal class HLSExtractor : IExtractor
|
||||||
{
|
{
|
||||||
// 标记是否已清除广告分片
|
// 标记是否已清除广告分片
|
||||||
bool hasAd = false;
|
bool hasAd = false;
|
||||||
|
;
|
||||||
|
bool allowHlsMultiExtMap = ParserConfig.CustomParserArgs.TryGetValue("AllowHlsMultiExtMap", out var allMultiExtMap) && allMultiExtMap == "true";
|
||||||
|
if (allowHlsMultiExtMap)
|
||||||
|
{
|
||||||
|
Logger.WarnMarkUp($"[darkorange3_1]{ResString.allowHlsMultiExtMap}[/]");
|
||||||
|
}
|
||||||
|
|
||||||
using StringReader sr = new StringReader(M3u8Content);
|
using StringReader sr = new StringReader(M3u8Content);
|
||||||
string? line;
|
string? line;
|
||||||
bool expectSegment = false;
|
bool expectSegment = false;
|
||||||
|
@ -395,8 +401,11 @@ internal class HLSExtractor : IExtractor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
segments = new();
|
segments = new();
|
||||||
isEndlist = true;
|
if (!allowHlsMultiExtMap)
|
||||||
break;
|
{
|
||||||
|
isEndlist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 评论行不解析
|
// 评论行不解析
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace N_m3u8DL_RE.CommandLine;
|
||||||
|
|
||||||
internal partial class CommandInvoker
|
internal partial class CommandInvoker
|
||||||
{
|
{
|
||||||
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20241117";
|
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20241123";
|
||||||
|
|
||||||
[GeneratedRegex("((best|worst)\\d*|all)")]
|
[GeneratedRegex("((best|worst)\\d*|all)")]
|
||||||
private static partial Regex ForStrRegex();
|
private static partial Regex ForStrRegex();
|
||||||
|
@ -61,6 +61,7 @@ internal partial class CommandInvoker
|
||||||
private static readonly Option<string?> BaseUrl = new(["--base-url"], description: ResString.cmd_baseUrl);
|
private static readonly Option<string?> BaseUrl = new(["--base-url"], description: ResString.cmd_baseUrl);
|
||||||
private static readonly Option<bool> ConcurrentDownload = new(["-mt", "--concurrent-download"], description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
|
private static readonly Option<bool> ConcurrentDownload = new(["-mt", "--concurrent-download"], description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
|
||||||
private static readonly Option<bool> NoLog = new(["--no-log"], description: ResString.cmd_noLog, getDefaultValue: () => false);
|
private static readonly Option<bool> NoLog = new(["--no-log"], description: ResString.cmd_noLog, getDefaultValue: () => false);
|
||||||
|
private static readonly Option<bool> AllowHlsMultiExtMap = new(["--allow-hls-multi-ext-map"], description: ResString.cmd_allowHlsMultiExtMap, getDefaultValue: () => false);
|
||||||
private static readonly Option<string[]?> AdKeywords = new(["--ad-keyword"], description: ResString.cmd_adKeyword) { ArgumentHelpName = "REG" };
|
private static readonly Option<string[]?> AdKeywords = new(["--ad-keyword"], description: ResString.cmd_adKeyword) { ArgumentHelpName = "REG" };
|
||||||
private static readonly Option<long?> MaxSpeed = new(["-R", "--max-speed"], description: ResString.cmd_maxSpeed, parseArgument: ParseSpeedLimit) { ArgumentHelpName = "SPEED" };
|
private static readonly Option<long?> MaxSpeed = new(["-R", "--max-speed"], description: ResString.cmd_maxSpeed, parseArgument: ParseSpeedLimit) { ArgumentHelpName = "SPEED" };
|
||||||
|
|
||||||
|
@ -550,6 +551,7 @@ internal partial class CommandInvoker
|
||||||
LiveTakeCount = bindingContext.ParseResult.GetValueForOption(LiveTakeCount),
|
LiveTakeCount = bindingContext.ParseResult.GetValueForOption(LiveTakeCount),
|
||||||
NoDateInfo = bindingContext.ParseResult.GetValueForOption(NoDateInfo),
|
NoDateInfo = bindingContext.ParseResult.GetValueForOption(NoDateInfo),
|
||||||
NoLog = bindingContext.ParseResult.GetValueForOption(NoLog),
|
NoLog = bindingContext.ParseResult.GetValueForOption(NoLog),
|
||||||
|
AllowHlsMultiExtMap = bindingContext.ParseResult.GetValueForOption(AllowHlsMultiExtMap),
|
||||||
AdKeywords = bindingContext.ParseResult.GetValueForOption(AdKeywords),
|
AdKeywords = bindingContext.ParseResult.GetValueForOption(AdKeywords),
|
||||||
MaxSpeed = bindingContext.ParseResult.GetValueForOption(MaxSpeed),
|
MaxSpeed = bindingContext.ParseResult.GetValueForOption(MaxSpeed),
|
||||||
};
|
};
|
||||||
|
@ -618,7 +620,7 @@ internal partial class CommandInvoker
|
||||||
MuxAfterDone,
|
MuxAfterDone,
|
||||||
CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt,
|
CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt,
|
||||||
LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, LiveTakeCount,
|
LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, LiveTakeCount,
|
||||||
MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, AdKeywords, DisableUpdateCheck, MoreHelp
|
MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, AdKeywords, DisableUpdateCheck, AllowHlsMultiExtMap, MoreHelp
|
||||||
};
|
};
|
||||||
|
|
||||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||||
|
|
|
@ -53,6 +53,10 @@ internal class MyOption
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool NoLog { get; set; }
|
public bool NoLog { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.AllowHlsMultiExtMap"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowHlsMultiExtMap { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.AutoSelect"/>.
|
/// See: <see cref="CommandInvoker.AutoSelect"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoSelect { get; set; }
|
public bool AutoSelect { get; set; }
|
||||||
|
|
|
@ -106,21 +106,20 @@ internal class Program
|
||||||
|
|
||||||
// 检查互斥的选项
|
// 检查互斥的选项
|
||||||
|
|
||||||
if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
|
if (option is { MuxAfterDone: false, MuxImports.Count: > 0 })
|
||||||
{
|
{
|
||||||
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
|
throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// LivePipeMux开启时 LiveRealTimeMerge必须开启
|
// LivePipeMux开启时 LiveRealTimeMerge必须开启
|
||||||
if (option.LivePipeMux && !option.LiveRealTimeMerge)
|
if (option is { LivePipeMux: true, LiveRealTimeMerge: false })
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp("LivePipeMux detected, forced enable LiveRealTimeMerge");
|
Logger.WarnMarkUp("LivePipeMux detected, forced enable LiveRealTimeMerge");
|
||||||
option.LiveRealTimeMerge = true;
|
option.LiveRealTimeMerge = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预先检查ffmpeg
|
// 预先检查ffmpeg
|
||||||
if (option.FFmpegBinaryPath == null)
|
option.FFmpegBinaryPath ??= GlobalUtil.FindExecutable("ffmpeg");
|
||||||
option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
|
if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
|
||||||
{
|
{
|
||||||
|
@ -132,8 +131,7 @@ internal class Program
|
||||||
// 预先检查mkvmerge
|
// 预先检查mkvmerge
|
||||||
if (option.MuxOptions != null && option.MuxOptions.UseMkvmerge && option.MuxAfterDone)
|
if (option.MuxOptions != null && option.MuxOptions.UseMkvmerge && option.MuxAfterDone)
|
||||||
{
|
{
|
||||||
if (option.MkvmergeBinaryPath == null)
|
option.MkvmergeBinaryPath ??= GlobalUtil.FindExecutable("mkvmerge");
|
||||||
option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
|
|
||||||
if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
|
if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException("mkvmerge not found");
|
throw new FileNotFoundException("mkvmerge not found");
|
||||||
|
@ -142,7 +140,7 @@ internal class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预先检查
|
// 预先检查
|
||||||
if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
|
if (option.Keys is { Length: > 0 } || option.KeyTextFile != null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(option.DecryptionBinaryPath))
|
if (string.IsNullOrEmpty(option.DecryptionBinaryPath))
|
||||||
{
|
{
|
||||||
|
@ -193,6 +191,11 @@ internal class Program
|
||||||
CustomeIV = option.CustomHLSIv,
|
CustomeIV = option.CustomHLSIv,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (option.AllowHlsMultiExtMap)
|
||||||
|
{
|
||||||
|
parserConfig.CustomParserArgs.Add("AllowHlsMultiExtMap", "true");
|
||||||
|
}
|
||||||
|
|
||||||
// demo1
|
// demo1
|
||||||
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
|
||||||
// demo2
|
// demo2
|
||||||
|
@ -225,13 +228,13 @@ internal class Program
|
||||||
|
|
||||||
|
|
||||||
// 全部媒体
|
// 全部媒体
|
||||||
var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder);
|
var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder).ToList();
|
||||||
// 基本流
|
// 基本流
|
||||||
var basicStreams = lists.Where(x => x.MediaType == null || x.MediaType == MediaType.VIDEO);
|
var basicStreams = lists.Where(x => x.MediaType is null or MediaType.VIDEO).ToList();
|
||||||
// 可选音频轨道
|
// 可选音频轨道
|
||||||
var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
|
var audios = lists.Where(x => x.MediaType == MediaType.AUDIO).ToList();
|
||||||
// 可选字幕轨道
|
// 可选字幕轨道
|
||||||
var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
|
var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES).ToList();
|
||||||
|
|
||||||
// 尝试从URL或文件读取文件名
|
// 尝试从URL或文件读取文件名
|
||||||
if (string.IsNullOrEmpty(option.SaveName))
|
if (string.IsNullOrEmpty(option.SaveName))
|
||||||
|
@ -246,7 +249,7 @@ internal class Program
|
||||||
// 写出文件
|
// 写出文件
|
||||||
await WriteRawFilesAsync(option, extractor, tmpDir);
|
await WriteRawFilesAsync(option, extractor, tmpDir);
|
||||||
|
|
||||||
Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count());
|
Logger.Info(ResString.streamsInfo, lists.Count, basicStreams.Count, audios.Count, subs.Count);
|
||||||
|
|
||||||
foreach (var item in lists)
|
foreach (var item in lists)
|
||||||
{
|
{
|
||||||
|
@ -259,7 +262,7 @@ internal class Program
|
||||||
basicStreams = FilterUtil.DoFilterDrop(basicStreams, option.DropVideoFilter);
|
basicStreams = FilterUtil.DoFilterDrop(basicStreams, option.DropVideoFilter);
|
||||||
audios = FilterUtil.DoFilterDrop(audios, option.DropAudioFilter);
|
audios = FilterUtil.DoFilterDrop(audios, option.DropAudioFilter);
|
||||||
subs = FilterUtil.DoFilterDrop(subs, option.DropSubtitleFilter);
|
subs = FilterUtil.DoFilterDrop(subs, option.DropSubtitleFilter);
|
||||||
lists = basicStreams.Concat(audios).Concat(subs).OrderBy(x => true);
|
lists = basicStreams.Concat(audios).Concat(subs).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.DropVideoFilter != null) Logger.Extra($"DropVideoFilter => {option.DropVideoFilter}");
|
if (option.DropVideoFilter != null) Logger.Extra($"DropVideoFilter => {option.DropVideoFilter}");
|
||||||
|
|
Loading…
Reference in New Issue