mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-10-31 10:37:22 -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