支持通过时长筛选 `plistDurMin`

This commit is contained in:
nilaoda 2023-06-14 22:45:53 +08:00
parent 724a5c9fa2
commit 43cb264374
5 changed files with 54 additions and 6 deletions

View File

@ -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
(

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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", "");

View File

@ -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);
}
/// <summary>
/// 从1h3m20s解析出总秒数
/// </summary>
/// <param name="timeStr"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
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)
{