diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index e22cc4c..9cffb7c 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -386,31 +386,40 @@ namespace N_m3u8DL_RE.Common.Resource ( zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\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" + + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + + "plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" + "* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" + "例如: \r\n" + "# 选择最佳视频\r\n" + "-sv best\r\n" + "# 选择4K+HEVC视频\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n", + "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "# 选择长度大于1小时20分钟30秒的视频\r\n" + + "-sv plistDurMin=\"1h20m30s\":for=best\r\n", zhTW: "通過正則表達式選擇符合要求的影片軌. 你能夠以:分隔形式指定如下參數:\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" + + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + + "plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" + "* for=FOR: 選擇方式. best[number], worst[number], all (默認: best)\r\n\r\n" + "例如: \r\n" + "# 選擇最佳影片\r\n" + "-sv best\r\n" + "# 選擇4K+HEVC影片\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n", + "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "# 選擇長度大於1小時20分鐘30秒的影片\r\n" + + "-sv plistDurMin=\"1h20m30s\":for=best\r\n", enUS: "Select video streams by regular expressions. OPTIONS is a colon separated list of:\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" + + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + + "plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" + "* for=FOR: Select type. best[number], worst[number], all (Default: best)\r\n\r\n" + "Examples: \r\n" + "# select best video\r\n" + "-sv best\r\n" + "# select 4K+HEVC video\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "# Select best video with duration longer than 1 hour 20 minutes 30 seconds\r\n" + + "-sv plistDurMin=\"1h20m30s\":for=best\r\n" ), ["cmd_selectAudio"] = new TextContainer ( diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 1bd460a..59c3271 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -274,6 +274,14 @@ namespace N_m3u8DL_RE.CommandLine if (!string.IsNullOrEmpty(segsMax)) streamFilter.SegmentsMaxCount = long.Parse(segsMax); + var plistDurMin = p.GetValue("plistDurMin"); + if (!string.IsNullOrEmpty(plistDurMin)) + streamFilter.PlaylistMinDur = OtherUtil.ParseSeconds(plistDurMin); + + var plistDurMax = p.GetValue("plistDurMax"); + if (!string.IsNullOrEmpty(plistDurMax)) + streamFilter.PlaylistMaxDur = OtherUtil.ParseSeconds(plistDurMax); + return streamFilter; } diff --git a/src/N_m3u8DL-RE/Entity/StreamFilter.cs b/src/N_m3u8DL-RE/Entity/StreamFilter.cs index a9168ce..1092bc7 100644 --- a/src/N_m3u8DL-RE/Entity/StreamFilter.cs +++ b/src/N_m3u8DL-RE/Entity/StreamFilter.cs @@ -21,6 +21,8 @@ namespace N_m3u8DL_RE.Entity public Regex? UrlReg { get; set; } public long? SegmentsMinCount { get; set; } public long? SegmentsMaxCount { get; set; } + public double? PlaylistMinDur { get; set; } + public double? PlaylistMaxDur { get; set; } public string For { get; set; } = "best"; } diff --git a/src/N_m3u8DL-RE/Util/FilterUtil.cs b/src/N_m3u8DL-RE/Util/FilterUtil.cs index cec0913..b9f3629 100644 --- a/src/N_m3u8DL-RE/Util/FilterUtil.cs +++ b/src/N_m3u8DL-RE/Util/FilterUtil.cs @@ -40,6 +40,10 @@ namespace N_m3u8DL_RE.Util 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); + if (filter.PlaylistMinDur != null) + inputs = inputs.Where(i => i.Playlist?.TotalDuration > filter.PlaylistMinDur); + if (filter.PlaylistMaxDur != null) + inputs = inputs.Where(i => i.Playlist?.TotalDuration < filter.PlaylistMaxDur); var bestNumberStr = filter.For.Replace("best", ""); var worstNumberStr = filter.For.Replace("worst", ""); diff --git a/src/N_m3u8DL-RE/Util/OtherUtil.cs b/src/N_m3u8DL-RE/Util/OtherUtil.cs index 953d578..cce15b2 100644 --- a/src/N_m3u8DL-RE/Util/OtherUtil.cs +++ b/src/N_m3u8DL-RE/Util/OtherUtil.cs @@ -4,6 +4,7 @@ using N_m3u8DL_RE.Enum; using System.CommandLine; using System.IO.Compression; using System.Text; +using System.Text.RegularExpressions; namespace N_m3u8DL_RE.Util { @@ -94,6 +95,30 @@ namespace N_m3u8DL_RE.Util return new TimeSpan(days, hours, mins, secs); } + /// + /// 从1h3m20s解析出总秒数 + /// + /// + /// + /// + public static double ParseSeconds(string timeStr) + { + var pattern = new Regex(@"^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$"); + + var match = pattern.Match(timeStr); + + if (!match.Success) + { + throw new ArgumentException("时间格式无效"); + } + + int hours = match.Groups[1].Success ? int.Parse(match.Groups[1].Value) : 0; + int minutes = match.Groups[2].Success ? int.Parse(match.Groups[2].Value) : 0; + int seconds = match.Groups[3].Success ? int.Parse(match.Groups[3].Value) : 0; + + return hours * 3600 + minutes * 60 + seconds; + } + //若该文件夹为空,删除,同时判断其父文件夹,直到遇到根目录或不为空的目录 public static void SafeDeleteDir(string dirPath) {