mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -04:00
Improve alternate ordering (#9336)
This commit is contained in:
parent
e7a7edbac0
commit
5b493e14ac
@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook
|
|||||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||||
foreach (var expression in _options.AudioBookPartsExpressions)
|
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 (match.Success)
|
||||||
{
|
{
|
||||||
if (!result.ChapterNumber.HasValue)
|
if (!result.ChapterNumber.HasValue)
|
||||||
|
@ -30,7 +30,7 @@ namespace Emby.Naming.AudioBook
|
|||||||
AudioBookNameParserResult result = default;
|
AudioBookNameParserResult result = default;
|
||||||
foreach (var expression in _options.AudioBookNamesExpressions)
|
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 (match.Success)
|
||||||
{
|
{
|
||||||
if (result.Name is null)
|
if (result.Name is null)
|
||||||
|
@ -14,7 +14,7 @@ namespace Emby.Naming.TV
|
|||||||
/// Used for removing separators between words, i.e turns "The_show" into "The show" while
|
/// Used for removing separators between words, i.e turns "The_show" into "The show" while
|
||||||
/// preserving namings like "S.H.O.W".
|
/// preserving namings like "S.H.O.W".
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Resolve information about series from path.
|
/// Resolve information about series from path.
|
||||||
|
@ -17,7 +17,7 @@ public class FileStackRule
|
|||||||
/// <param name="isNumerical">Whether the file stack rule uses numerical or alphabetical numbering.</param>
|
/// <param name="isNumerical">Whether the file stack rule uses numerical or alphabetical numbering.</param>
|
||||||
public FileStackRule(string token, bool isNumerical)
|
public FileStackRule(string token, bool isNumerical)
|
||||||
{
|
{
|
||||||
_tokenRegex = new Regex(token, RegexOptions.IgnoreCase);
|
_tokenRegex = new Regex(token, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
IsNumerical = isNumerical;
|
IsNumerical = isNumerical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
@ -13,6 +14,8 @@ namespace Emby.Naming.Video
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class VideoListResolver
|
public static class VideoListResolver
|
||||||
{
|
{
|
||||||
|
private static readonly Regex _resolutionRegex = new Regex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves alternative versions and extras from list of video files.
|
/// Resolves alternative versions and extras from list of video files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -115,19 +118,34 @@ namespace Emby.Naming.Video
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions))
|
if (!IsEligibleForMultiVersion(folderName, video.Files[0].FileNameWithoutExtension, namingOptions))
|
||||||
{
|
{
|
||||||
return videos;
|
return videos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderName.Equals(Path.GetFileNameWithoutExtension(video.Files[0].Path.AsSpan()), StringComparison.Ordinal))
|
if (folderName.Equals(video.Files[0].FileNameWithoutExtension, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
primary = video;
|
primary = video;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The list is created and overwritten in the caller, so we are allowed to do in-place sorting
|
if (videos.Count > 1)
|
||||||
videos.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
|
{
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
primary ??= videos[0];
|
primary ??= videos[0];
|
||||||
videos.Remove(primary);
|
videos.Remove(primary);
|
||||||
|
|
||||||
@ -161,9 +179,8 @@ namespace Emby.Naming.Video
|
|||||||
return true;
|
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))
|
if (!testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -13,8 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
public LegacyHdHomerunChannelCommands(string url)
|
public LegacyHdHomerunChannelCommands(string url)
|
||||||
{
|
{
|
||||||
// parse url for channel and program
|
// parse url for channel and program
|
||||||
var regExp = new Regex(@"\/ch([0-9]+)-?([0-9]*)");
|
var match = Regex.Match(url, @"\/ch([0-9]+)-?([0-9]*)");
|
||||||
var match = regExp.Match(url);
|
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
_channel = match.Groups[1].Value;
|
_channel = match.Groups[1].Value;
|
||||||
|
@ -308,8 +308,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
{
|
{
|
||||||
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
|
var matches = Regex.Matches(line, @"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
|
||||||
var matches = reg.Matches(line);
|
|
||||||
|
|
||||||
remaining = line;
|
remaining = line;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace Jellyfin.Extensions
|
|||||||
{
|
{
|
||||||
// Matches non-conforming unicode chars
|
// Matches non-conforming unicode chars
|
||||||
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
|
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
|
||||||
private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)");
|
private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)", RegexOptions.Compiled);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the diacritics character from the strings.
|
/// Removes the diacritics character from the strings.
|
||||||
|
@ -188,8 +188,7 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
@"/movies/Iron Man/Iron Man-bluray.mkv",
|
@"/movies/Iron Man/Iron Man-bluray.mkv",
|
||||||
@"/movies/Iron Man/Iron Man-3d.mkv",
|
@"/movies/Iron Man/Iron Man-3d.mkv",
|
||||||
@"/movies/Iron Man/Iron Man-3d-hsbs.mkv",
|
@"/movies/Iron Man/Iron Man-3d-hsbs.mkv",
|
||||||
@"/movies/Iron Man/Iron Man-3d.hsbs.mkv",
|
@"/movies/Iron Man/Iron Man[test].mkv"
|
||||||
@"/movies/Iron Man/Iron Man[test].mkv",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = VideoListResolver.Resolve(
|
var result = VideoListResolver.Resolve(
|
||||||
@ -197,10 +196,14 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
_namingOptions).ToList();
|
_namingOptions).ToList();
|
||||||
|
|
||||||
Assert.Single(result);
|
Assert.Single(result);
|
||||||
Assert.Equal(7, result[0].AlternateVersions.Count);
|
Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path);
|
||||||
Assert.False(result[0].AlternateVersions[2].Is3D);
|
Assert.Equal(6, result[0].AlternateVersions.Count);
|
||||||
Assert.True(result[0].AlternateVersions[3].Is3D);
|
Assert.Equal("/movies/Iron Man/Iron Man-720p.mkv", result[0].AlternateVersions[0].Path);
|
||||||
Assert.True(result[0].AlternateVersions[4].Is3D);
|
Assert.Equal("/movies/Iron Man/Iron Man-3d.mkv", result[0].AlternateVersions[1].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man-3d-hsbs.mkv", result[0].AlternateVersions[2].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man-bluray.mkv", result[0].AlternateVersions[3].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man-test.mkv", result[0].AlternateVersions[4].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man[test].mkv", result[0].AlternateVersions[5].Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -214,7 +217,6 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
@"/movies/Iron Man/Iron Man - bluray.mkv",
|
@"/movies/Iron Man/Iron Man - bluray.mkv",
|
||||||
@"/movies/Iron Man/Iron Man - 3d.mkv",
|
@"/movies/Iron Man/Iron Man - 3d.mkv",
|
||||||
@"/movies/Iron Man/Iron Man - 3d-hsbs.mkv",
|
@"/movies/Iron Man/Iron Man - 3d-hsbs.mkv",
|
||||||
@"/movies/Iron Man/Iron Man - 3d.hsbs.mkv",
|
|
||||||
@"/movies/Iron Man/Iron Man [test].mkv"
|
@"/movies/Iron Man/Iron Man [test].mkv"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -223,10 +225,14 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
_namingOptions).ToList();
|
_namingOptions).ToList();
|
||||||
|
|
||||||
Assert.Single(result);
|
Assert.Single(result);
|
||||||
Assert.Equal(7, result[0].AlternateVersions.Count);
|
Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path);
|
||||||
Assert.False(result[0].AlternateVersions[3].Is3D);
|
Assert.Equal(6, result[0].AlternateVersions.Count);
|
||||||
Assert.True(result[0].AlternateVersions[4].Is3D);
|
Assert.Equal("/movies/Iron Man/Iron Man - 720p.mkv", result[0].AlternateVersions[0].Path);
|
||||||
Assert.True(result[0].AlternateVersions[5].Is3D);
|
Assert.Equal("/movies/Iron Man/Iron Man - 3d.mkv", result[0].AlternateVersions[1].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man - 3d-hsbs.mkv", result[0].AlternateVersions[2].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man - bluray.mkv", result[0].AlternateVersions[3].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man - test.mkv", result[0].AlternateVersions[4].Path);
|
||||||
|
Assert.Equal("/movies/Iron Man/Iron Man [test].mkv", result[0].AlternateVersions[5].Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -328,8 +334,12 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
{
|
{
|
||||||
var files = new[]
|
var files = new[]
|
||||||
{
|
{
|
||||||
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv",
|
||||||
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv",
|
||||||
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv",
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv",
|
||||||
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv"
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv",
|
||||||
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv",
|
||||||
|
@"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv",
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = VideoListResolver.Resolve(
|
var result = VideoListResolver.Resolve(
|
||||||
@ -338,8 +348,12 @@ namespace Jellyfin.Naming.Tests.Video
|
|||||||
|
|
||||||
Assert.Single(result);
|
Assert.Single(result);
|
||||||
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", result[0].Files[0].Path);
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", result[0].Files[0].Path);
|
||||||
Assert.Single(result[0].AlternateVersions);
|
Assert.Equal(5, result[0].AlternateVersions.Count);
|
||||||
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[0].Path);
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv", result[0].AlternateVersions[0].Path);
|
||||||
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[1].Path);
|
||||||
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv", result[0].AlternateVersions[2].Path);
|
||||||
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv", result[0].AlternateVersions[3].Path);
|
||||||
|
Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv", result[0].AlternateVersions[4].Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user