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)
{