diff --git a/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs b/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs index 2842403..131741e 100644 --- a/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs +++ b/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs @@ -54,6 +54,14 @@ namespace N_m3u8DL_RE.Common.Entity public Playlist? Playlist { get; set; } + public int SegmentsCount + { + get + { + return Playlist != null ? Playlist.MediaParts.Sum(x => x.MediaSegments.Count) : 0; + } + } + public string ToShortString() { var prefixStr = ""; @@ -93,8 +101,7 @@ namespace N_m3u8DL_RE.Common.Entity var prefixStr = ""; var returnStr = ""; var encStr = string.Empty; - var segmentsCount = Playlist != null ? Playlist.MediaParts.Sum(x => x.MediaSegments.Count) : 0; - var segmentsCountStr = segmentsCount == 0 ? "" : (segmentsCount > 1 ? $"{segmentsCount} Segments" : $"{segmentsCount} Segment"); + var segmentsCountStr = SegmentsCount == 0 ? "" : (SegmentsCount > 1 ? $"{SegmentsCount} Segments" : $"{SegmentsCount} Segment"); //增加加密标志 if (Playlist != null && Playlist.MediaParts.Any(m => m.MediaSegments.Any(s => s.EncryptInfo.Method != EncryptMethod.NONE))) diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index f53a07b..a717a86 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -32,10 +32,13 @@ namespace N_m3u8DL_RE.Common.Resource public static string cmd_muxImport { get => GetText("cmd_muxImport"); } public static string cmd_muxImport_more { get => GetText("cmd_muxImport_more"); } public static string cmd_selectVideo { get => GetText("cmd_selectVideo"); } + public static string cmd_dropVideo { get => GetText("cmd_dropVideo"); } public static string cmd_selectVideo_more { get => GetText("cmd_selectVideo_more"); } public static string cmd_selectAudio { get => GetText("cmd_selectAudio"); } + public static string cmd_dropAudio { get => GetText("cmd_dropAudio"); } public static string cmd_selectAudio_more { get => GetText("cmd_selectAudio_more"); } public static string cmd_selectSubtitle { get => GetText("cmd_selectSubtitle"); } + public static string cmd_dropSubtitle { get => GetText("cmd_dropSubtitle"); } 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"); } diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index d6cc83f..6675761 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -334,11 +334,17 @@ namespace N_m3u8DL_RE.Common.Resource zhTW: "通過正則表達式選擇符合要求的影片軌. 輸入 \"--morehelp select-video\" 以查看詳細訊息", enUS: "Select video streams by regular expressions. Use \"--morehelp select-video\" for more details" ), + ["cmd_dropVideo"] = new TextContainer + ( + zhCN: "通过正则表达式去除符合要求的视频流.", + zhTW: "通過正則表達式去除符合要求的影片串流.", + enUS: "Drop video streams by regular expressions." + ), ["cmd_selectVideo_more"] = new TextContainer ( zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" + - "id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX\r\n" + - "frame=REGEX:ch=REGEX:range=REGEX:url=REGEX:for=FOR\r\n\r\n" + + "id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX\r\n" + + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX:for=FOR\r\n\r\n" + "* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" + "例如: \r\n" + "# 选择最佳视频\r\n" + @@ -370,6 +376,12 @@ namespace N_m3u8DL_RE.Common.Resource zhTW: "通過正則表達式選擇符合要求的音軌. 輸入 \"--morehelp select-audio\" 以查看詳細訊息", enUS: "Select audio streams by regular expressions. Use \"--morehelp select-audio\" for more details" ), + ["cmd_dropAudio"] = new TextContainer + ( + zhCN: "通过正则表达式去除符合要求的音频流.", + zhTW: "通過正則表達式去除符合要求的音軌.", + enUS: "Drop audio streams by regular expressions." + ), ["cmd_selectAudio_more"] = new TextContainer ( zhCN: "通过正则表达式选择符合要求的音频流. 参考 --select-video\r\n\r\n" + @@ -403,6 +415,12 @@ namespace N_m3u8DL_RE.Common.Resource zhTW: "通過正則表達式選擇符合要求的字幕流. 輸入 \"--morehelp select-subtitle\" 以查看詳細訊息", enUS: "Select subtitle streams by regular expressions. Use \"--morehelp select-subtitle\" for more details" ), + ["cmd_dropSubtitle"] = new TextContainer + ( + zhCN: "通过正则表达式去除符合要求的字幕流.", + zhTW: "通過正則表達式去除符合要求的字幕流.", + enUS: "Drop subtitle streams by regular expressions." + ), ["cmd_selectSubtitle_more"] = new TextContainer ( zhCN: "通过正则表达式选择符合要求的字幕流. 参考 --select-video\r\n\r\n" + diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 2749601..45f1e12 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -83,6 +83,10 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option VideoFilter = new(new string[] { "-sv", "--select-video" }, description: ResString.cmd_selectVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; 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" }; + + private readonly static Option DropVideoFilter = new(new string[] { "-dv", "--drop-video" }, description: ResString.cmd_dropVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; + private readonly static Option DropAudioFilter = new(new string[] { "-da", "--drop-audio" }, description: ResString.cmd_dropAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; + private readonly static Option DropSubtitleFilter = new(new string[] { "-ds", "--drop-subtitle" }, description: ResString.cmd_dropSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; /// /// 解析用户代理 @@ -236,6 +240,14 @@ namespace N_m3u8DL_RE.CommandLine if (!string.IsNullOrEmpty(url)) streamFilter.UrlReg = new Regex(url); + var segsMin = p.GetValue("segsMin"); + if (!string.IsNullOrEmpty(segsMin)) + streamFilter.SegmentsMinCount = long.Parse(segsMin); + + var segsMax = p.GetValue("segsMax"); + if (!string.IsNullOrEmpty(segsMax)) + streamFilter.SegmentsMaxCount = long.Parse(segsMax); + return streamFilter; } @@ -373,6 +385,9 @@ namespace N_m3u8DL_RE.CommandLine VideoFilter = bindingContext.ParseResult.GetValueForOption(VideoFilter), AudioFilter = bindingContext.ParseResult.GetValueForOption(AudioFilter), SubtitleFilter = bindingContext.ParseResult.GetValueForOption(SubtitleFilter), + DropVideoFilter = bindingContext.ParseResult.GetValueForOption(DropVideoFilter), + DropAudioFilter = bindingContext.ParseResult.GetValueForOption(DropAudioFilter), + DropSubtitleFilter = bindingContext.ParseResult.GetValueForOption(DropSubtitleFilter), LiveRealTimeMerge = bindingContext.ParseResult.GetValueForOption(LiveRealTimeMerge), LiveKeepSegments = bindingContext.ParseResult.GetValueForOption(LiveKeepSegments), LiveRecordLimit = bindingContext.ParseResult.GetValueForOption(LiveRecordLimit), @@ -447,7 +462,7 @@ namespace N_m3u8DL_RE.CommandLine MuxAfterDone, CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LiveRecordLimit, LiveWaitTime, - MuxImports, VideoFilter, AudioFilter, SubtitleFilter, MoreHelp + MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, MoreHelp }; rootCommand.TreatUnmatchedTokensAsErrors = true; diff --git a/src/N_m3u8DL-RE/Entity/StreamFilter.cs b/src/N_m3u8DL-RE/Entity/StreamFilter.cs index 6536613..a9168ce 100644 --- a/src/N_m3u8DL-RE/Entity/StreamFilter.cs +++ b/src/N_m3u8DL-RE/Entity/StreamFilter.cs @@ -19,6 +19,8 @@ namespace N_m3u8DL_RE.Entity public Regex? ChannelsReg { get; set; } public Regex? VideoRangeReg { get; set; } public Regex? UrlReg { get; set; } + public long? SegmentsMinCount { get; set; } + public long? SegmentsMaxCount { get; set; } public string For { get; set; } = "best"; } diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs index 372f3a5..df19cf7 100644 --- a/src/N_m3u8DL-RE/Program.cs +++ b/src/N_m3u8DL-RE/Program.cs @@ -198,6 +198,14 @@ namespace N_m3u8DL_RE } var selectedStreams = new List(); + if (option.DropVideoFilter != null || option.DropAudioFilter != null || option.DropSubtitleFilter != null) + { + basicStreams = FilterUtil.DoFilterDrop(basicStreams, option.DropVideoFilter); + audios = FilterUtil.DoFilterDrop(audios, option.DropAudioFilter); + subs = FilterUtil.DoFilterDrop(subs, option.DropSubtitleFilter); + lists = basicStreams.Concat(audios).Concat(subs).OrderBy(x => true); + } + if (option.AutoSelect) { if (basicStreams.Any()) @@ -215,9 +223,9 @@ namespace N_m3u8DL_RE } else if (option.VideoFilter != null || option.AudioFilter != null || option.SubtitleFilter != null) { - basicStreams = FilterUtil.DoFilter(basicStreams, option.VideoFilter); - audios = FilterUtil.DoFilter(audios, option.AudioFilter); - subs = FilterUtil.DoFilter(subs, option.SubtitleFilter); + basicStreams = FilterUtil.DoFilterKeep(basicStreams, option.VideoFilter); + audios = FilterUtil.DoFilterKeep(audios, option.AudioFilter); + subs = FilterUtil.DoFilterKeep(subs, option.SubtitleFilter); selectedStreams = basicStreams.Concat(audios).Concat(subs).ToList(); } else diff --git a/src/N_m3u8DL-RE/Util/FilterUtil.cs b/src/N_m3u8DL-RE/Util/FilterUtil.cs index 3634f03..187bcb4 100644 --- a/src/N_m3u8DL-RE/Util/FilterUtil.cs +++ b/src/N_m3u8DL-RE/Util/FilterUtil.cs @@ -13,7 +13,7 @@ namespace N_m3u8DL_RE.Util { public class FilterUtil { - public static List DoFilter(IEnumerable lists, StreamFilter? filter) + public static List DoFilterKeep(IEnumerable lists, StreamFilter? filter) { if (filter == null) return new List(); @@ -36,6 +36,10 @@ namespace N_m3u8DL_RE.Util inputs = inputs.Where(i => i.VideoRange != null && filter.VideoRangeReg.IsMatch(i.VideoRange)); if (filter.UrlReg != null) inputs = inputs.Where(i => i.Url != null && filter.UrlReg.IsMatch(i.Url)); + if (filter.SegmentsMaxCount != null && inputs.All(i => i.SegmentsCount > 0)) + inputs = inputs.Where(i => i.SegmentsCount < filter.SegmentsMaxCount); + if (filter.SegmentsMinCount != null && inputs.All(i => i.SegmentsCount > 0)) + inputs = inputs.Where(i => i.SegmentsCount > filter.SegmentsMinCount); var bestNumberStr = filter.For.Replace("best", ""); var worstNumberStr = filter.For.Replace("worst", ""); @@ -52,6 +56,18 @@ namespace N_m3u8DL_RE.Util return inputs.ToList(); } + public static List DoFilterDrop(IEnumerable lists, StreamFilter? filter) + { + if (filter == null) return new List(); + + var inputs = lists.Where(_ => true); + var selected = DoFilterKeep(lists, filter); + + inputs = inputs.SkipWhile(i => selected.Any(s => s.ToString() == i.ToString())); + + return inputs.ToList(); + } + public static List SelectStreams(IEnumerable lists) { if (lists.Count() == 1)