aboutsummaryrefslogtreecommitdiff
path: root/Emby.Naming
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Naming')
-rw-r--r--Emby.Naming/Audio/AlbumParser.cs9
-rw-r--r--Emby.Naming/AudioBook/AudioBookFilePathParser.cs6
-rw-r--r--Emby.Naming/AudioBook/AudioBookListResolver.cs24
-rw-r--r--Emby.Naming/AudioBook/AudioBookNameParser.cs6
-rw-r--r--Emby.Naming/Common/NamingOptions.cs52
-rw-r--r--Emby.Naming/Emby.Naming.csproj14
-rw-r--r--Emby.Naming/ExternalFiles/ExternalPathParser.cs4
-rw-r--r--Emby.Naming/TV/EpisodePathParser.cs16
-rw-r--r--Emby.Naming/TV/SeriesPathParser.cs2
-rw-r--r--Emby.Naming/TV/SeriesResolver.cs2
-rw-r--r--Emby.Naming/Video/CleanDateTimeParser.cs2
-rw-r--r--Emby.Naming/Video/ExtraRuleResolver.cs4
-rw-r--r--Emby.Naming/Video/FileStackRule.cs2
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs62
-rw-r--r--Emby.Naming/Video/VideoResolver.cs3
15 files changed, 97 insertions, 111 deletions
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
index bbfdccc90..86a564153 100644
--- a/Emby.Naming/Audio/AlbumParser.cs
+++ b/Emby.Naming/Audio/AlbumParser.cs
@@ -3,6 +3,7 @@ using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
+using Jellyfin.Extensions;
namespace Emby.Naming.Audio
{
@@ -58,13 +59,7 @@ namespace Emby.Naming.Audio
var tmp = trimmedFilename.Slice(prefix.Length).Trim();
- int index = tmp.IndexOf(' ');
- if (index != -1)
- {
- tmp = tmp.Slice(0, index);
- }
-
- if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
+ if (int.TryParse(tmp.LeftPart(' '), CultureInfo.InvariantCulture, out _))
{
return true;
}
diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
index 7b4429ab1..75fdedfea 100644
--- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
+++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs
@@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook
var fileName = Path.GetFileNameWithoutExtension(path);
foreach (var expression in _options.AudioBookPartsExpressions)
{
- var match = new Regex(expression, RegexOptions.IgnoreCase).Match(fileName);
+ var match = Regex.Match(fileName, expression, RegexOptions.IgnoreCase);
if (match.Success)
{
if (!result.ChapterNumber.HasValue)
@@ -40,7 +40,7 @@ namespace Emby.Naming.AudioBook
var value = match.Groups["chapter"];
if (value.Success)
{
- if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
{
result.ChapterNumber = intValue;
}
@@ -52,7 +52,7 @@ namespace Emby.Naming.AudioBook
var value = match.Groups["part"];
if (value.Success)
{
- if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
{
result.PartNumber = intValue;
}
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
index 6e491185d..ca304102f 100644
--- a/Emby.Naming/AudioBook/AudioBookListResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs
@@ -69,35 +69,35 @@ namespace Emby.Naming.AudioBook
extras = new List<AudioBookFileInfo>();
alternativeVersions = new List<AudioBookFileInfo>();
- var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber != null || x.PartNumber != null);
+ var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber is not null || x.PartNumber is not null);
var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
foreach (var group in groupedBy)
{
- if (group.Key.ChapterNumber == null && group.Key.PartNumber == null)
+ if (group.Key.ChapterNumber is null && group.Key.PartNumber is null)
{
if (group.Count() > 1 || haveChaptersOrPages)
{
- var ex = new List<AudioBookFileInfo>();
- var alt = new List<AudioBookFileInfo>();
+ List<AudioBookFileInfo>? ex = null;
+ List<AudioBookFileInfo>? alt = null;
foreach (var audioFile in group)
{
- var name = Path.GetFileNameWithoutExtension(audioFile.Path);
- if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) ||
- name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) ||
- name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
+ var name = Path.GetFileNameWithoutExtension(audioFile.Path.AsSpan());
+ if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase)
+ || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase)
+ || name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
{
- alt.Add(audioFile);
+ (alt ??= new()).Add(audioFile);
}
else
{
- ex.Add(audioFile);
+ (ex ??= new()).Add(audioFile);
}
}
- if (ex.Count > 0)
+ if (ex is not null)
{
var extra = ex
.OrderBy(x => x.Container)
@@ -108,7 +108,7 @@ namespace Emby.Naming.AudioBook
extras.AddRange(extra);
}
- if (alt.Count > 0)
+ if (alt is not null)
{
var alternatives = alt
.OrderBy(x => x.Container)
diff --git a/Emby.Naming/AudioBook/AudioBookNameParser.cs b/Emby.Naming/AudioBook/AudioBookNameParser.cs
index 120482bc2..5ea649dbf 100644
--- a/Emby.Naming/AudioBook/AudioBookNameParser.cs
+++ b/Emby.Naming/AudioBook/AudioBookNameParser.cs
@@ -30,10 +30,10 @@ namespace Emby.Naming.AudioBook
AudioBookNameParserResult result = default;
foreach (var expression in _options.AudioBookNamesExpressions)
{
- var match = new Regex(expression, RegexOptions.IgnoreCase).Match(name);
+ var match = Regex.Match(name, expression, RegexOptions.IgnoreCase);
if (match.Success)
{
- if (result.Name == null)
+ if (result.Name is null)
{
var value = match.Groups["name"];
if (value.Success)
@@ -47,7 +47,7 @@ namespace Emby.Naming.AudioBook
var value = match.Groups["year"];
if (value.Success)
{
- if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
{
result.Year = intValue;
}
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index 0119fa38c..a069da102 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -141,8 +141,7 @@ namespace Emby.Naming.Common
VideoFileStackingRules = new[]
{
new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[0-9]+)[\)\]]?(?:\.[^.]+)?$", true),
- new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[a-d])[\)\]]?(?:\.[^.]+)?$", false),
- new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]?)(?<number>[a-d])(?:\.[^.]+)?$", false)
+ new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[a-d])[\)\]]?(?:\.[^.]+)?$", false)
};
CleanDateTimes = new[]
@@ -153,11 +152,12 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
- @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multi|subs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"^(?<cleaned>.+?)(\[.*\])",
@"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
@"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)",
- @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$"
+ @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$",
+ @"^\s*(?<cleaned>.+?)(([-._ ](trailer|sample))|-(scene|clip|behindthescenes|deleted|deletedscene|featurette|short|interview|other|extra))$"
};
SubtitleFileExtensions = new[]
@@ -169,6 +169,7 @@ namespace Emby.Naming.Common
".srt",
".ssa",
".sub",
+ ".sup",
".vtt",
};
@@ -269,7 +270,6 @@ namespace Emby.Naming.Common
".sfx",
".shn",
".sid",
- ".spc",
".stm",
".strm",
".ult",
@@ -337,7 +337,15 @@ namespace Emby.Naming.Common
}
},
- // This isn't a Kodi naming rule, but the expression below causes false positives,
+ // This isn't a Kodi naming rule, but the expression below causes false episode numbers for
+ // Title Season X Episode X naming schemes.
+ // "Series Season X Episode X - Title.avi", "Series S03 E09.avi", "s3 e9 - Title.avi"
+ new EpisodeExpression(@".*[\\\/]((?<seriesname>[^\\/]+?)\s)?[Ss](?:eason)?\s*(?<seasonnumber>[0-9]+)\s+[Ee](?:pisode)?\s*(?<epnumber>[0-9]+).*$")
+ {
+ IsNamed = true
+ },
+
+ // Not a Kodi rule as well, but the expression below also causes false positives,
// so we make sure this one gets tested first.
// "Foo Bar 889"
new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,4})(-(?<endingepnumber>[0-9]{2,4}))*[^\\\/x]*$")
@@ -452,16 +460,6 @@ namespace Emby.Naming.Common
},
};
- EpisodeWithoutSeasonExpressions = new[]
- {
- @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
- };
-
- EpisodeMultiPartExpressions = new[]
- {
- @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
- };
-
VideoExtraRules = new[]
{
new ExtraRule(
@@ -797,16 +795,6 @@ namespace Emby.Naming.Common
public EpisodeExpression[] EpisodeExpressions { get; set; }
/// <summary>
- /// Gets or sets list of raw episode without season regular expressions strings.
- /// </summary>
- public string[] EpisodeWithoutSeasonExpressions { get; set; }
-
- /// <summary>
- /// Gets or sets list of raw multi-part episodes regular expressions strings.
- /// </summary>
- public string[] EpisodeMultiPartExpressions { get; set; }
-
- /// <summary>
/// Gets or sets list of video file extensions.
/// </summary>
public string[] VideoFileExtensions { get; set; }
@@ -877,24 +865,12 @@ namespace Emby.Naming.Common
public Regex[] CleanStringRegexes { get; private set; } = Array.Empty<Regex>();
/// <summary>
- /// Gets list of episode without season regular expressions.
- /// </summary>
- public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } = Array.Empty<Regex>();
-
- /// <summary>
- /// Gets list of multi-part episode regular expressions.
- /// </summary>
- public Regex[] EpisodeMultiPartRegexes { get; private set; } = Array.Empty<Regex>();
-
- /// <summary>
/// Compiles raw regex strings into regexes.
/// </summary>
public void Compile()
{
CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
- EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
- EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
}
private Regex Compile(string exp)
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index ca002b981..f3973dad9 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -6,7 +6,7 @@
</PropertyGroup>
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net7.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -16,7 +16,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
- <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+ <CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Stability)'=='Unstable'">
@@ -42,18 +42,18 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
+ <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
</Project>
diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
index 1fa4fa537..953129671 100644
--- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs
+++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
@@ -94,12 +94,12 @@ namespace Emby.Naming.ExternalFiles
// Try to translate to three character code
var culture = _localizationManager.FindLanguageInfo(currentSliceWithoutSeparator);
- if (culture != null && pathInfo.Language == null)
+ if (culture is not null && pathInfo.Language is null)
{
pathInfo.Language = culture.ThreeLetterISOLanguageName;
extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase);
}
- else if (culture != null && pathInfo.Language == "hin")
+ else if (culture is not null && pathInfo.Language == "hin")
{
// Hindi language code "hi" collides with a hearing impaired flag - use as Hindi only if no other language is set
pathInfo.IsHearingImpaired = true;
diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs
index 6d0597356..8cd5a126e 100644
--- a/Emby.Naming/TV/EpisodePathParser.cs
+++ b/Emby.Naming/TV/EpisodePathParser.cs
@@ -76,7 +76,7 @@ namespace Emby.Naming.TV
}
}
- if (result != null && fillExtendedInfo)
+ if (result is not null && fillExtendedInfo)
{
FillAdditional(path, result);
@@ -113,7 +113,7 @@ namespace Emby.Naming.TV
if (expression.DateTimeFormats.Length > 0)
{
if (DateTime.TryParseExact(
- match.Groups[0].Value,
+ match.Groups[0].ValueSpan,
expression.DateTimeFormats,
CultureInfo.InvariantCulture,
DateTimeStyles.None,
@@ -125,7 +125,7 @@ namespace Emby.Naming.TV
result.Success = true;
}
}
- else if (DateTime.TryParse(match.Groups[0].Value, out date))
+ else if (DateTime.TryParse(match.Groups[0].ValueSpan, out date))
{
result.Year = date.Year;
result.Month = date.Month;
@@ -138,12 +138,12 @@ namespace Emby.Naming.TV
}
else if (expression.IsNamed)
{
- if (int.TryParse(match.Groups["seasonnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(match.Groups["seasonnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
{
result.SeasonNumber = num;
}
- if (int.TryParse(match.Groups["epnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ if (int.TryParse(match.Groups["epnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
result.EpisodeNumber = num;
}
@@ -158,7 +158,7 @@ namespace Emby.Naming.TV
if (nextIndex >= name.Length
|| !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal))
{
- if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ if (int.TryParse(endingNumberGroup.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
result.EndingEpisodeNumber = num;
}
@@ -170,12 +170,12 @@ namespace Emby.Naming.TV
}
else
{
- if (int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(match.Groups[1].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
{
result.SeasonNumber = num;
}
- if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
+ if (int.TryParse(match.Groups[2].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
result.EpisodeNumber = num;
}
diff --git a/Emby.Naming/TV/SeriesPathParser.cs b/Emby.Naming/TV/SeriesPathParser.cs
index 23067e6a4..94b4b5c82 100644
--- a/Emby.Naming/TV/SeriesPathParser.cs
+++ b/Emby.Naming/TV/SeriesPathParser.cs
@@ -28,7 +28,7 @@ namespace Emby.Naming.TV
}
}
- if (result != null)
+ if (result is not null)
{
if (!string.IsNullOrEmpty(result.SeriesName))
{
diff --git a/Emby.Naming/TV/SeriesResolver.cs b/Emby.Naming/TV/SeriesResolver.cs
index 156a03c9e..307a84096 100644
--- a/Emby.Naming/TV/SeriesResolver.cs
+++ b/Emby.Naming/TV/SeriesResolver.cs
@@ -14,7 +14,7 @@ namespace Emby.Naming.TV
/// Used for removing separators between words, i.e turns "The_show" into "The show" while
/// preserving namings like "S.H.O.W".
/// </summary>
- private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))");
+ private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))", RegexOptions.Compiled);
/// <summary>
/// Resolve information about series from path.
diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs
index 0ee633dcc..9a6c6e978 100644
--- a/Emby.Naming/Video/CleanDateTimeParser.cs
+++ b/Emby.Naming/Video/CleanDateTimeParser.cs
@@ -43,7 +43,7 @@ namespace Emby.Naming.Video
&& match.Groups.Count == 5
&& match.Groups[1].Success
&& match.Groups[2].Success
- && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
+ && int.TryParse(match.Groups[2].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
{
result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year);
return true;
diff --git a/Emby.Naming/Video/ExtraRuleResolver.cs b/Emby.Naming/Video/ExtraRuleResolver.cs
index 0970e509a..3219472ef 100644
--- a/Emby.Naming/Video/ExtraRuleResolver.cs
+++ b/Emby.Naming/Video/ExtraRuleResolver.cs
@@ -56,7 +56,7 @@ namespace Emby.Naming.Video
}
else if (rule.RuleType == ExtraRuleType.Regex)
{
- var filename = Path.GetFileName(path);
+ var filename = Path.GetFileName(path.AsSpan());
var isMatch = Regex.IsMatch(filename, rule.Token, RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -76,7 +76,7 @@ namespace Emby.Naming.Video
}
}
- if (result.ExtraType != null)
+ if (result.ExtraType is not null)
{
return result;
}
diff --git a/Emby.Naming/Video/FileStackRule.cs b/Emby.Naming/Video/FileStackRule.cs
index 76b487f42..be0f79d33 100644
--- a/Emby.Naming/Video/FileStackRule.cs
+++ b/Emby.Naming/Video/FileStackRule.cs
@@ -17,7 +17,7 @@ public class FileStackRule
/// <param name="isNumerical">Whether the file stack rule uses numerical or alphabetical numbering.</param>
public FileStackRule(string token, bool isNumerical)
{
- _tokenRegex = new Regex(token, RegexOptions.IgnoreCase);
+ _tokenRegex = new Regex(token, RegexOptions.IgnoreCase | RegexOptions.Compiled);
IsNumerical = isNumerical;
}
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index 11f82525f..6209cd46f 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
+using Jellyfin.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Naming.Video
@@ -13,6 +14,8 @@ namespace Emby.Naming.Video
/// </summary>
public static class VideoListResolver
{
+ private static readonly Regex _resolutionRegex = new Regex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
/// <summary>
/// Resolves alternative versions and extras from list of video files.
/// </summary>
@@ -26,7 +29,7 @@ namespace Emby.Naming.Video
// Filter out all extras, otherwise they could cause stacks to not be resolved
// See the unit test TestStackedWithTrailer
var nonExtras = videoInfos
- .Where(i => i.ExtraType == null)
+ .Where(i => i.ExtraType is null)
.Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory });
var stackResult = StackResolver.Resolve(nonExtras, namingOptions).ToList();
@@ -42,7 +45,7 @@ namespace Emby.Naming.Video
continue;
}
- if (current.ExtraType == null)
+ if (current.ExtraType is null)
{
standaloneMedia.Add(current);
}
@@ -106,37 +109,52 @@ namespace Emby.Naming.Video
}
// Cannot use Span inside local functions and delegates thus we cannot use LINQ here nor merge with the above [if]
+ VideoInfo? primary = null;
for (var i = 0; i < videos.Count; i++)
{
var video = videos[i];
- if (video.ExtraType != null)
+ if (video.ExtraType is not null)
{
continue;
}
- if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions))
+ if (!IsEligibleForMultiVersion(folderName, video.Files[0].FileNameWithoutExtension, namingOptions))
{
return videos;
}
+
+ if (folderName.Equals(video.Files[0].FileNameWithoutExtension, StringComparison.Ordinal))
+ {
+ primary = video;
+ }
+ }
+
+ if (videos.Count > 1)
+ {
+ var groups = videos.GroupBy(x => _resolutionRegex.IsMatch(x.Files[0].FileNameWithoutExtension)).ToList();
+ videos.Clear();
+ foreach (var group in groups)
+ {
+ if (group.Key)
+ {
+ videos.InsertRange(0, group.OrderByDescending(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator()));
+ }
+ else
+ {
+ videos.AddRange(group.OrderBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator()));
+ }
+ }
}
- // The list is created and overwritten in the caller, so we are allowed to do in-place sorting
- videos.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
+ primary ??= videos[0];
+ videos.Remove(primary);
var list = new List<VideoInfo>
{
- videos[0]
+ primary
};
- var alternateVersionsLen = videos.Count - 1;
- var alternateVersions = new VideoFileInfo[alternateVersionsLen];
- for (int i = 0; i < alternateVersionsLen; i++)
- {
- var video = videos[i + 1];
- alternateVersions[i] = video.Files[0];
- }
-
- list[0].AlternateVersions = alternateVersions;
+ list[0].AlternateVersions = videos.Select(x => x.Files[0]).ToArray();
list[0].Name = folderName.ToString();
return list;
@@ -161,9 +179,8 @@ namespace Emby.Naming.Video
return true;
}
- private static bool IsEligibleForMultiVersion(ReadOnlySpan<char> folderName, string testFilePath, NamingOptions namingOptions)
+ private static bool IsEligibleForMultiVersion(ReadOnlySpan<char> folderName, ReadOnlySpan<char> testFilename, NamingOptions namingOptions)
{
- var testFilename = Path.GetFileNameWithoutExtension(testFilePath.AsSpan());
if (!testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
{
return false;
@@ -176,16 +193,15 @@ namespace Emby.Naming.Video
}
// There are no span overloads for regex unfortunately
- var tmpTestFilename = testFilename.ToString();
- if (CleanStringParser.TryClean(tmpTestFilename, namingOptions.CleanStringRegexes, out var cleanName))
+ if (CleanStringParser.TryClean(testFilename.ToString(), namingOptions.CleanStringRegexes, out var cleanName))
{
- tmpTestFilename = cleanName.Trim();
+ testFilename = cleanName.AsSpan().Trim();
}
// The CleanStringParser should have removed common keywords etc.
- return string.IsNullOrEmpty(tmpTestFilename)
+ return testFilename.IsEmpty
|| testFilename[0] == '-'
- || Regex.IsMatch(tmpTestFilename, @"^\[([^]]*)\]", RegexOptions.Compiled);
+ || Regex.IsMatch(testFilename, @"^\[([^]]*)\]", RegexOptions.Compiled);
}
}
}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index de8e177d8..db5bfdbf9 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -87,8 +87,7 @@ namespace Emby.Naming.Video
name = cleanDateTimeResult.Name;
year = cleanDateTimeResult.Year;
- if (extraResult.ExtraType == null
- && TryCleanString(name, namingOptions, out var newName))
+ if (TryCleanString(name, namingOptions, out var newName))
{
name = newName;
}