repo creation with initial code after cloning public repo
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Emby.Naming.Common;
|
||||
|
||||
namespace Emby.Naming.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to parse information about episode from path.
|
||||
/// </summary>
|
||||
public class EpisodePathParser
|
||||
{
|
||||
private readonly NamingOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EpisodePathParser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options"><see cref="NamingOptions"/> object containing EpisodeExpressions and MultipleEpisodeExpressions.</param>
|
||||
public EpisodePathParser(NamingOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses information about episode from path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path.</param>
|
||||
/// <param name="isDirectory">Is path for a directory or file.</param>
|
||||
/// <param name="isNamed">Do we want to use IsNamed expressions.</param>
|
||||
/// <param name="isOptimistic">Do we want to use Optimistic expressions.</param>
|
||||
/// <param name="supportsAbsoluteNumbers">Do we want to use expressions supporting absolute episode numbers.</param>
|
||||
/// <param name="fillExtendedInfo">Should we attempt to retrieve extended information.</param>
|
||||
/// <returns>Returns <see cref="EpisodePathParserResult"/> object.</returns>
|
||||
public EpisodePathParserResult Parse(
|
||||
string path,
|
||||
bool isDirectory,
|
||||
bool? isNamed = null,
|
||||
bool? isOptimistic = null,
|
||||
bool? supportsAbsoluteNumbers = null,
|
||||
bool fillExtendedInfo = true)
|
||||
{
|
||||
// Added to be able to use regex patterns which require a file extension.
|
||||
// There were no failed tests without this block, but to be safe, we can keep it until
|
||||
// the regex which require file extensions are modified so that they don't need them.
|
||||
if (isDirectory)
|
||||
{
|
||||
path += ".mp4";
|
||||
}
|
||||
|
||||
EpisodePathParserResult? result = null;
|
||||
|
||||
foreach (var expression in _options.EpisodeExpressions)
|
||||
{
|
||||
if (supportsAbsoluteNumbers.HasValue
|
||||
&& expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isNamed.HasValue && expression.IsNamed != isNamed.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isOptimistic.HasValue && expression.IsOptimistic != isOptimistic.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentResult = Parse(path, expression);
|
||||
if (currentResult.Success)
|
||||
{
|
||||
result = currentResult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result is not null && fillExtendedInfo)
|
||||
{
|
||||
FillAdditional(path, result);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.SeriesName))
|
||||
{
|
||||
result.SeriesName = result.SeriesName
|
||||
.Trim()
|
||||
.Trim('_', '.', '-')
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return result ?? new EpisodePathParserResult();
|
||||
}
|
||||
|
||||
private static EpisodePathParserResult Parse(string name, EpisodeExpression expression)
|
||||
{
|
||||
var result = new EpisodePathParserResult();
|
||||
|
||||
// This is a hack to handle wmc naming
|
||||
if (expression.IsByDate)
|
||||
{
|
||||
name = name.Replace('_', '-');
|
||||
}
|
||||
|
||||
var match = expression.Regex.Match(name);
|
||||
|
||||
// (Full)(Season)(Episode)(Extension)
|
||||
if (match.Success && match.Groups.Count >= 3)
|
||||
{
|
||||
if (expression.IsByDate)
|
||||
{
|
||||
DateTime date;
|
||||
if (expression.DateTimeFormats.Length > 0)
|
||||
{
|
||||
if (DateTime.TryParseExact(
|
||||
match.Groups[0].ValueSpan,
|
||||
expression.DateTimeFormats,
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.None,
|
||||
out date))
|
||||
{
|
||||
result.Year = date.Year;
|
||||
result.Month = date.Month;
|
||||
result.Day = date.Day;
|
||||
result.Success = true;
|
||||
}
|
||||
}
|
||||
else if (DateTime.TryParse(match.Groups[0].ValueSpan, out date))
|
||||
{
|
||||
result.Year = date.Year;
|
||||
result.Month = date.Month;
|
||||
result.Day = date.Day;
|
||||
result.Success = true;
|
||||
}
|
||||
|
||||
// TODO: Only consider success if date successfully parsed?
|
||||
result.Success = true;
|
||||
}
|
||||
else if (expression.IsNamed)
|
||||
{
|
||||
if (int.TryParse(match.Groups["seasonnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
|
||||
{
|
||||
result.SeasonNumber = num;
|
||||
}
|
||||
|
||||
if (int.TryParse(match.Groups["epnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
|
||||
{
|
||||
result.EpisodeNumber = num;
|
||||
}
|
||||
|
||||
var endingNumberGroup = match.Groups["endingepnumber"];
|
||||
if (endingNumberGroup.Success)
|
||||
{
|
||||
// Will only set EndingEpisodeNumber if the captured number is not followed by additional numbers
|
||||
// or a 'p' or 'i' as what you would get with a pixel resolution specification.
|
||||
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
|
||||
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
|
||||
if (nextIndex >= name.Length
|
||||
|| !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal))
|
||||
{
|
||||
if (int.TryParse(endingNumberGroup.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
|
||||
{
|
||||
result.EndingEpisodeNumber = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.SeriesName = match.Groups["seriesname"].Value;
|
||||
result.Success = result.EpisodeNumber.HasValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int.TryParse(match.Groups[1].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
|
||||
{
|
||||
result.SeasonNumber = num;
|
||||
}
|
||||
|
||||
if (int.TryParse(match.Groups[2].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
|
||||
{
|
||||
result.EpisodeNumber = num;
|
||||
}
|
||||
|
||||
result.Success = result.EpisodeNumber.HasValue;
|
||||
}
|
||||
|
||||
// Invalidate match when the season is 200 through 1927 or above 2500
|
||||
// because it is an error unless the TV show is intentionally using false season numbers.
|
||||
// It avoids erroneous parsing of something like "Series Special (1920x1080).mkv" as being season 1920 episode 1080.
|
||||
if ((result.SeasonNumber >= 200 && result.SeasonNumber < 1928)
|
||||
|| result.SeasonNumber > 2500)
|
||||
{
|
||||
result.Success = false;
|
||||
}
|
||||
|
||||
result.IsByDate = expression.IsByDate;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void FillAdditional(string path, EpisodePathParserResult info)
|
||||
{
|
||||
var expressions = _options.MultipleEpisodeExpressions.Where(i => i.IsNamed).ToList();
|
||||
|
||||
if (string.IsNullOrEmpty(info.SeriesName))
|
||||
{
|
||||
expressions.InsertRange(0, _options.EpisodeExpressions.Where(i => i.IsNamed));
|
||||
}
|
||||
|
||||
FillAdditional(path, info, expressions);
|
||||
}
|
||||
|
||||
private void FillAdditional(string path, EpisodePathParserResult info, IEnumerable<EpisodeExpression> expressions)
|
||||
{
|
||||
foreach (var i in expressions)
|
||||
{
|
||||
var result = Parse(path, i);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(info.SeriesName))
|
||||
{
|
||||
info.SeriesName = result.SeriesName;
|
||||
}
|
||||
|
||||
if (!info.EndingEpisodeNumber.HasValue && info.EpisodeNumber.HasValue)
|
||||
{
|
||||
info.EndingEpisodeNumber = result.EndingEpisodeNumber;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(info.SeriesName)
|
||||
&& (!info.EpisodeNumber.HasValue || info.EndingEpisodeNumber.HasValue))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user