From 7939f3b009e830e38a3f37455418011043429ee3 Mon Sep 17 00:00:00 2001 From: TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> Date: Mon, 25 May 2026 23:40:40 +0200 Subject: [PATCH 1/4] only fetch language codes for the requested library when generating filter values --- .../Library/LibraryManager.cs | 12 ++++++ Jellyfin.Api/Controllers/FilterController.cs | 38 +++++++++++++++-- .../Item/BaseItemRepository.ByName.cs | 41 +++++++++++++++++++ .../Library/ILibraryManager.cs | 8 ++++ .../Persistence/IItemRepository.cs | 9 ++++ 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index cc85f09d23..f075d2f649 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -3862,5 +3862,17 @@ namespace Emby.Server.Implementations.Library { return _mediaStreamRepository.GetMediaStreamLanguages(mediaStreamType); } + + /// + public IReadOnlyList GetMediaStreamLanguages(MediaStreamType mediaStreamType, InternalItemsQuery query) + { + if (query.User is not null) + { + AddUserToQuery(query, query.User); + } + + SetTopParentOrAncestorIds(query); + return _itemRepository.GetMediaStreamLanguages(query, mediaStreamType); + } } } diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index cfc8be28ae..07ebe83e86 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -158,13 +158,42 @@ public class FilterController : BaseJellyfinApiController IsSeries = isSeries }; + var streamLanguageQuery = new InternalItemsQuery(user) + { + // It's possible that different langauges are only available on alternative versions. + // To fetch them all, owned items are inlcluded. + IncludeOwnedItems = true, + IncludeItemTypes = includeItemTypes, + DtoOptions = new DtoOptions + { + Fields = Array.Empty(), + EnableImages = false, + EnableUserData = false + }, + IsAiring = isAiring, + IsMovie = isMovie, + IsSports = isSports, + IsKids = isKids, + IsNews = isNews, + IsSeries = isSeries + }; + if ((recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder) { genreQuery.AncestorIds = parentItem is null ? Array.Empty() : new[] { parentItem.Id }; + streamLanguageQuery.AncestorIds = parentItem is null ? Array.Empty() : new[] { parentItem.Id }; } else { genreQuery.Parent = parentItem; + streamLanguageQuery.Parent = parentItem; + } + + if ((includeItemTypes.Contains(BaseItemKind.Series) || includeItemTypes.Contains(BaseItemKind.Season)) + && !includeItemTypes.Contains(BaseItemKind.Episode)) + { + // streams are joined on epsiodes not shows or seasons + streamLanguageQuery.IncludeItemTypes = [..includeItemTypes, BaseItemKind.Episode]; } if (includeItemTypes.Length == 1 @@ -188,10 +217,13 @@ public class FilterController : BaseJellyfinApiController }).ToArray(); } - if (includeItemTypes.Contains(BaseItemKind.Movie) || includeItemTypes.Contains(BaseItemKind.Series)) + if (includeItemTypes.Contains(BaseItemKind.Movie) + || includeItemTypes.Contains(BaseItemKind.Series) + || includeItemTypes.Contains(BaseItemKind.Season) + || includeItemTypes.Contains(BaseItemKind.Episode)) { filters.AudioLanguages = _libraryManager - .GetMediaStreamLanguages(MediaStreamType.Audio) + .GetMediaStreamLanguages(MediaStreamType.Audio, streamLanguageQuery) .Select(language => { var culture = _localization.FindLanguageInfo(language); @@ -204,7 +236,7 @@ public class FilterController : BaseJellyfinApiController .OrderBy(l => l.Name) .ToArray(); filters.SubtitleLanguages = _libraryManager - .GetMediaStreamLanguages(MediaStreamType.Subtitle) + .GetMediaStreamLanguages(MediaStreamType.Subtitle, streamLanguageQuery) .Select(language => { var culture = _localization.FindLanguageInfo(language); diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs index e4fd3204e1..00ffd984f5 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs @@ -7,6 +7,7 @@ using Jellyfin.Data.Enums; using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using Microsoft.EntityFrameworkCore; using BaseItemDto = MediaBrowser.Controller.Entities.BaseItem; @@ -81,6 +82,46 @@ public sealed partial class BaseItemRepository _itemTypeLookup.MusicGenreTypes); } + /// + public IReadOnlyList GetMediaStreamLanguages(InternalItemsQuery filter, MediaStreamType mediaStreamType) + { + ArgumentNullException.ThrowIfNull(filter); + + if (!filter.Limit.HasValue) + { + filter.EnableTotalRecordCount = false; + } + + using var context = _dbProvider.CreateDbContext(); + + return TranslateQuery( + context.BaseItems.Include(e => e.MediaStreams).Where(e => e.Id != EF.Constant(PlaceholderId)), + context, + new InternalItemsQuery(filter.User) + { + IncludeOwnedItems = filter.IncludeOwnedItems, + ExcludeItemTypes = filter.ExcludeItemTypes, + IncludeItemTypes = filter.IncludeItemTypes, + MediaTypes = filter.MediaTypes, + AncestorIds = filter.AncestorIds, + ItemIds = filter.ItemIds, + TopParentIds = filter.TopParentIds, + ParentId = filter.ParentId, + IsAiring = filter.IsAiring, + IsMovie = filter.IsMovie, + IsSports = filter.IsSports, + IsKids = filter.IsKids, + IsNews = filter.IsNews, + IsSeries = filter.IsSeries + }) + .Where(e => e.MediaStreams != null) + .SelectMany(e => e.MediaStreams!) + .Where(e => e.StreamType == (MediaStreamTypeEntity)mediaStreamType) + .Select(s => string.IsNullOrEmpty(s.Language) ? "und" : s.Language) // und = undetermined + .Distinct() + .ToArray(); + } + private string[] GetItemValueNames(IReadOnlyList itemValueTypes, IReadOnlyList withItemTypes, IReadOnlyList excludeItemTypes) { using var context = _dbProvider.CreateDbContext(); diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index c23eba75ef..c37b13ea4f 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -800,5 +800,13 @@ namespace MediaBrowser.Controller.Library /// The stream type. /// List of language codes. IReadOnlyList GetMediaStreamLanguages(MediaStreamType mediaStreamType); + + /// + /// Gets a list of all language codes for the matching items and the the provided stream type. + /// + /// The stream type. + /// The query filter. + /// List of language codes. + IReadOnlyList GetMediaStreamLanguages(MediaStreamType mediaStreamType, InternalItemsQuery query); } } diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 291916ab25..d44fe57bed 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -7,6 +7,7 @@ using Jellyfin.Data.Enums; using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Persistence; @@ -119,6 +120,14 @@ public interface IItemRepository /// The list of genre names. IReadOnlyList GetGenreNames(); + /// + /// Gets all language codes of the matching base items and the provided stream type. + /// + /// The query filter. + /// The type of the media stream. + /// List of language codes. + public IReadOnlyList GetMediaStreamLanguages(InternalItemsQuery filter, MediaStreamType mediaStreamType); + /// /// Gets all artist names. /// From 2a44c35224e29498c0d75fd1091e043496b04865 Mon Sep 17 00:00:00 2001 From: TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> Date: Mon, 13 Jul 2026 20:01:01 +0200 Subject: [PATCH 2/4] Apply review suggestions --- Jellyfin.Api/Controllers/FilterController.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index 07ebe83e86..9964a2f4e4 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -180,8 +180,9 @@ public class FilterController : BaseJellyfinApiController if ((recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder) { - genreQuery.AncestorIds = parentItem is null ? Array.Empty() : new[] { parentItem.Id }; - streamLanguageQuery.AncestorIds = parentItem is null ? Array.Empty() : new[] { parentItem.Id }; + var ancestorIds = parentItem is null ? Array.Empty() : new[] { parentItem.Id }; + genreQuery.AncestorIds = ancestorIds; + streamLanguageQuery.AncestorIds = ancestorIds; } else { From 7d83779b6feea56f6f1fa5ebfc2aaf772d32e86e Mon Sep 17 00:00:00 2001 From: TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> Date: Sat, 18 Jul 2026 10:57:14 +0200 Subject: [PATCH 3/4] fix code style --- Jellyfin.Api/Controllers/FilterController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index 9964a2f4e4..b458bd90f3 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -161,7 +161,7 @@ public class FilterController : BaseJellyfinApiController var streamLanguageQuery = new InternalItemsQuery(user) { // It's possible that different langauges are only available on alternative versions. - // To fetch them all, owned items are inlcluded. + // To fetch them all, owned items are included. IncludeOwnedItems = true, IncludeItemTypes = includeItemTypes, DtoOptions = new DtoOptions @@ -194,7 +194,7 @@ public class FilterController : BaseJellyfinApiController && !includeItemTypes.Contains(BaseItemKind.Episode)) { // streams are joined on epsiodes not shows or seasons - streamLanguageQuery.IncludeItemTypes = [..includeItemTypes, BaseItemKind.Episode]; + streamLanguageQuery.IncludeItemTypes = [.. includeItemTypes, BaseItemKind.Episode]; } if (includeItemTypes.Length == 1 From b317af0d307065d4657f8db4771ef89e36fdff10 Mon Sep 17 00:00:00 2001 From: TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> Date: Tue, 21 Jul 2026 22:11:50 +0200 Subject: [PATCH 4/4] fix: remove obsolete code --- .../Item/BaseItemRepository.ByName.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs index 00ffd984f5..3131b027e6 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.ByName.cs @@ -87,11 +87,6 @@ public sealed partial class BaseItemRepository { ArgumentNullException.ThrowIfNull(filter); - if (!filter.Limit.HasValue) - { - filter.EnableTotalRecordCount = false; - } - using var context = _dbProvider.CreateDbContext(); return TranslateQuery(