Merge pull request #16980 from TheMelmacian/feature/library_specific_language_filter_values

Improve language filters to only fetch language codes that match the requested items/libraries (follow up to #9787)
This commit is contained in:
Cody Robibero
2026-07-21 20:43:31 -04:00
committed by GitHub
5 changed files with 102 additions and 4 deletions
@@ -3886,5 +3886,17 @@ namespace Emby.Server.Implementations.Library
{
return _mediaStreamRepository.GetMediaStreamLanguages(mediaStreamType);
}
/// <inheritdoc />
public IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType, InternalItemsQuery query)
{
if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
SetTopParentOrAncestorIds(query);
return _itemRepository.GetMediaStreamLanguages(query, mediaStreamType);
}
}
}
+37 -4
View File
@@ -158,13 +158,43 @@ 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 included.
IncludeOwnedItems = true,
IncludeItemTypes = includeItemTypes,
DtoOptions = new DtoOptions
{
Fields = Array.Empty<ItemFields>(),
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<Guid>() : new[] { parentItem.Id };
var ancestorIds = parentItem is null ? Array.Empty<Guid>() : new[] { parentItem.Id };
genreQuery.AncestorIds = ancestorIds;
streamLanguageQuery.AncestorIds = ancestorIds;
}
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 +218,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 +237,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);
@@ -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,41 @@ public sealed partial class BaseItemRepository
_itemTypeLookup.MusicGenreTypes);
}
/// <inheritdoc />
public IReadOnlyList<string> GetMediaStreamLanguages(InternalItemsQuery filter, MediaStreamType mediaStreamType)
{
ArgumentNullException.ThrowIfNull(filter);
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<ItemValueType> itemValueTypes, IReadOnlyList<string> withItemTypes, IReadOnlyList<string> excludeItemTypes)
{
using var context = _dbProvider.CreateDbContext();
@@ -799,5 +799,13 @@ namespace MediaBrowser.Controller.Library
/// <param name="mediaStreamType">The stream type.</param>
/// <returns>List of language codes.</returns>
IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType);
/// <summary>
/// Gets a list of all language codes for the matching items and the the provided stream type.
/// </summary>
/// <param name="mediaStreamType">The stream type.</param>
/// <param name="query">The query filter.</param>
/// <returns>List of language codes.</returns>
IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType, InternalItemsQuery query);
}
}
@@ -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
/// <returns>The list of genre names.</returns>
IReadOnlyList<string> GetGenreNames();
/// <summary>
/// Gets all language codes of the matching base items and the provided stream type.
/// </summary>
/// <param name="filter">The query filter.</param>
/// <param name="mediaStreamType">The type of the media stream.</param>
/// <returns>List of language codes.</returns>
public IReadOnlyList<string> GetMediaStreamLanguages(InternalItemsQuery filter, MediaStreamType mediaStreamType);
/// <summary>
/// Gets all artist names.
/// </summary>