mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-01-15 08:23:28 -03:00
Rework season folder parsing (#11748)
This commit is contained in:
@@ -1,43 +1,35 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Emby.Naming.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to parse season paths.
|
||||
/// </summary>
|
||||
public static class SeasonPathParser
|
||||
public static partial class SeasonPathParser
|
||||
{
|
||||
/// <summary>
|
||||
/// A season folder must contain one of these somewhere in the name.
|
||||
/// </summary>
|
||||
private static readonly string[] _seasonFolderNames =
|
||||
{
|
||||
"season",
|
||||
"sæson",
|
||||
"temporada",
|
||||
"saison",
|
||||
"staffel",
|
||||
"series",
|
||||
"сезон",
|
||||
"stagione"
|
||||
};
|
||||
[GeneratedRegex(@"^\s*((?<seasonnumber>(?>\d+))(?:st|nd|rd|th|\.)*(?!\s*[Ee]\d+))\s*(?:[[시즌]*|[シーズン]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<rightpart>.*)$")]
|
||||
private static partial Regex ProcessPre();
|
||||
|
||||
private static readonly char[] _splitChars = ['.', '_', ' ', '-'];
|
||||
[GeneratedRegex(@"^\s*(?:[[시즌]*|[シーズン]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<seasonnumber>(?>\d+)(?!\s*[Ee]\d+))(?<rightpart>.*)$")]
|
||||
private static partial Regex ProcessPost();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to parse season number from path.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to season.</param>
|
||||
/// <param name="parentPath">Folder name of the parent.</param>
|
||||
/// <param name="supportSpecialAliases">Support special aliases when parsing.</param>
|
||||
/// <param name="supportNumericSeasonFolders">Support numeric season folders when parsing.</param>
|
||||
/// <returns>Returns <see cref="SeasonPathParserResult"/> object.</returns>
|
||||
public static SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
|
||||
public static SeasonPathParserResult Parse(string path, string? parentPath, bool supportSpecialAliases, bool supportNumericSeasonFolders)
|
||||
{
|
||||
var result = new SeasonPathParserResult();
|
||||
var parentFolderName = parentPath is null ? null : new DirectoryInfo(parentPath).Name;
|
||||
|
||||
var (seasonNumber, isSeasonFolder) = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders);
|
||||
var (seasonNumber, isSeasonFolder) = GetSeasonNumberFromPath(path, parentFolderName, supportSpecialAliases, supportNumericSeasonFolders);
|
||||
|
||||
result.SeasonNumber = seasonNumber;
|
||||
|
||||
@@ -54,15 +46,24 @@ namespace Emby.Naming.TV
|
||||
/// Gets the season number from path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="parentFolderName">The parent folder name.</param>
|
||||
/// <param name="supportSpecialAliases">if set to <c>true</c> [support special aliases].</param>
|
||||
/// <param name="supportNumericSeasonFolders">if set to <c>true</c> [support numeric season folders].</param>
|
||||
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||
private static (int? SeasonNumber, bool IsSeasonFolder) GetSeasonNumberFromPath(
|
||||
string path,
|
||||
string? parentFolderName,
|
||||
bool supportSpecialAliases,
|
||||
bool supportNumericSeasonFolders)
|
||||
{
|
||||
string filename = Path.GetFileName(path);
|
||||
filename = Regex.Replace(filename, "[ ._-]", string.Empty);
|
||||
|
||||
if (parentFolderName is not null)
|
||||
{
|
||||
parentFolderName = Regex.Replace(parentFolderName, "[ ._-]", string.Empty);
|
||||
filename = filename.Replace(parentFolderName, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (supportSpecialAliases)
|
||||
{
|
||||
@@ -85,53 +86,38 @@ namespace Emby.Naming.TV
|
||||
}
|
||||
}
|
||||
|
||||
if (TryGetSeasonNumberFromPart(filename, out int seasonNumber))
|
||||
if (filename.StartsWith('s'))
|
||||
{
|
||||
var testFilename = filename.AsSpan()[1..];
|
||||
|
||||
if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
return (val, true);
|
||||
}
|
||||
}
|
||||
|
||||
var preMatch = ProcessPre().Match(filename);
|
||||
if (preMatch.Success)
|
||||
{
|
||||
return CheckMatch(preMatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
var postMatch = ProcessPost().Match(filename);
|
||||
return CheckMatch(postMatch);
|
||||
}
|
||||
}
|
||||
|
||||
private static (int? SeasonNumber, bool IsSeasonFolder) CheckMatch(Match match)
|
||||
{
|
||||
var numberString = match.Groups["seasonnumber"];
|
||||
if (numberString.Success)
|
||||
{
|
||||
var seasonNumber = int.Parse(numberString.Value, CultureInfo.InvariantCulture);
|
||||
return (seasonNumber, true);
|
||||
}
|
||||
|
||||
// Look for one of the season folder names
|
||||
foreach (var name in _seasonFolderNames)
|
||||
{
|
||||
if (filename.Contains(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var result = GetSeasonNumberFromPathSubstring(filename.Replace(name, " ", StringComparison.OrdinalIgnoreCase));
|
||||
if (result.SeasonNumber.HasValue)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var parts = filename.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (TryGetSeasonNumberFromPart(part, out seasonNumber))
|
||||
{
|
||||
return (seasonNumber, true);
|
||||
}
|
||||
}
|
||||
|
||||
return (null, true);
|
||||
}
|
||||
|
||||
private static bool TryGetSeasonNumberFromPart(ReadOnlySpan<char> part, out int seasonNumber)
|
||||
{
|
||||
seasonNumber = 0;
|
||||
if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (int.TryParse(part.Slice(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
||||
{
|
||||
seasonNumber = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (null, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user