diff --git a/Jellyfin.Server.Implementations/Item/OrderMapper.cs b/Jellyfin.Server.Implementations/Item/OrderMapper.cs index 1ae7cc6c4a..91b4f58f41 100644 --- a/Jellyfin.Server.Implementations/Item/OrderMapper.cs +++ b/Jellyfin.Server.Implementations/Item/OrderMapper.cs @@ -1,4 +1,7 @@ #pragma warning disable RS0030 // Do not use banned APIs +#pragma warning disable CA1304 // Specify CultureInfo +#pragma warning disable CA1311 // Specify a culture or use an invariant version +#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons using System; using System.Linq; @@ -57,10 +60,18 @@ public static class OrderMapper (ItemSortBy.SeriesDatePlayed, not null) => e => jellyfinDbContext.BaseItems .Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey) - .Join(jellyfinDbContext.UserData.Where(w => w.UserId == query.User.Id && w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate) + .LeftJoin( + jellyfinDbContext.UserData.Where(w => w.UserId == query.User.Id && w.Played), + item => item.Id, + userData => userData.ItemId, + (item, userData) => userData == null ? (DateTime?)null : userData.LastPlayedDate) .Max(f => f), (ItemSortBy.SeriesDatePlayed, null) => e => jellyfinDbContext.BaseItems.Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey) - .Join(jellyfinDbContext.UserData.Where(w => w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate) + .LeftJoin( + jellyfinDbContext.UserData.Where(w => w.Played), + item => item.Id, + userData => userData.ItemId, + (item, userData) => userData == null ? (DateTime?)null : userData.LastPlayedDate) .Max(f => f), // ItemSortBy.SeriesDatePlayed => e => jellyfinDbContext.UserData // .Where(u => u.Item!.SeriesPresentationUniqueKey == e.PresentationUniqueKey && u.Played) @@ -73,6 +84,7 @@ public static class OrderMapper /// /// Creates an expression to order search results by match quality. /// Prioritizes: exact match (0) > prefix match with word boundary (1) > prefix match (2) > contains (3). + /// Considers both CleanName and OriginalTitle for matching. /// /// The search term to match against. /// An expression that returns an integer representing match quality (lower is better). @@ -80,10 +92,15 @@ public static class OrderMapper { var cleanSearchTerm = GetCleanValue(searchTerm); var searchPrefix = cleanSearchTerm + " "; + var originalSearchLower = searchTerm.ToLowerInvariant(); + var originalSearchPrefix = originalSearchLower + " "; return e => - e.CleanName == cleanSearchTerm ? 0 : - e.CleanName!.StartsWith(searchPrefix) ? 1 : - e.CleanName!.StartsWith(cleanSearchTerm) ? 2 : 3; + // Exact match on CleanName or OriginalTitle + (e.CleanName == cleanSearchTerm || (e.OriginalTitle != null && e.OriginalTitle.ToLower() == originalSearchLower)) ? 0 : + // Prefix match with word boundary + (e.CleanName!.StartsWith(searchPrefix) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().StartsWith(originalSearchPrefix))) ? 1 : + // Prefix match + (e.CleanName!.StartsWith(cleanSearchTerm) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().StartsWith(originalSearchLower))) ? 2 : 3; } private static string GetCleanValue(string value) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7586b99e77..498b7d19e4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1334,6 +1334,7 @@ namespace MediaBrowser.Controller.Entities return false; } + var parents = GetParents().ToList(); if (GetParents().Any(i => !i.IsVisible(user, true))) { return false; @@ -1341,7 +1342,7 @@ namespace MediaBrowser.Controller.Entities if (checkFolders) { - var topParent = GetParents().LastOrDefault() ?? this; + var topParent = parents.Count > 0 ? parents[^1] : this; if (string.IsNullOrEmpty(topParent.Path)) { @@ -1670,8 +1671,16 @@ namespace MediaBrowser.Controller.Entities private bool IsVisibleViaTags(User user, bool skipAllowedTagsCheck) { + var allowedTagsPreference = user.GetPreference(PreferenceKind.AllowedTags); + var blockedTagsPreference = user.GetPreference(PreferenceKind.BlockedTags); + var needsTagCheck = allowedTagsPreference.Length > 0 || blockedTagsPreference.Length > 0; + if (!needsTagCheck) + { + return true; + } + var allTags = GetInheritedTags(); - if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase))) + if (blockedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase))) { return false; } @@ -1682,8 +1691,7 @@ namespace MediaBrowser.Controller.Entities return true; } - var allowedTagsPreference = user.GetPreference(PreferenceKind.AllowedTags); - if (!skipAllowedTagsCheck && allowedTagsPreference.Length != 0 && !allowedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase))) + if (!skipAllowedTagsCheck && !allowedTagsPreference.Any(i => allTags.Contains(i, StringComparison.OrdinalIgnoreCase))) { return false; }