diff options
| author | TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> | 2026-05-12 01:47:07 +0200 |
|---|---|---|
| committer | TheMelmacian <76712303+TheMelmacian@users.noreply.github.com> | 2026-05-12 02:12:14 +0200 |
| commit | 39049a726e1a88e8acf1d8cc5c217bc8d86be9ae (patch) | |
| tree | 5239b36e5511781bbb4abea70054b3c7600bbb92 | |
| parent | 5701cdce684dbbcdfdd5cc4c79586fe623e9f2d0 (diff) | |
move language filters from QueryFiltersLegacy to QueryFilters
10 files changed, 82 insertions, 44 deletions
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 11f1496086..15d51cf35f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -87,6 +87,7 @@ namespace Emby.Server.Implementations.Library private readonly IPathManager _pathManager; private readonly FastConcurrentLru<Guid, BaseItem> _cache; private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule; + private readonly IMediaStreamRepository _mediaStreamRepository; /// <summary> /// The _root folder sync lock. @@ -129,6 +130,7 @@ namespace Emby.Server.Implementations.Library /// <param name="peopleRepository">The people repository.</param> /// <param name="pathManager">The path manager.</param> /// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param> + /// <param name="mediaStreamRepository">The media stream repository.</param> public LibraryManager( IServerApplicationHost appHost, ILoggerFactory loggerFactory, @@ -151,7 +153,8 @@ namespace Emby.Server.Implementations.Library IDirectoryService directoryService, IPeopleRepository peopleRepository, IPathManager pathManager, - DotIgnoreIgnoreRule dotIgnoreIgnoreRule) + DotIgnoreIgnoreRule dotIgnoreIgnoreRule, + IMediaStreamRepository mediaStreamRepository) { _appHost = appHost; _logger = loggerFactory.CreateLogger<LibraryManager>(); @@ -181,6 +184,8 @@ namespace Emby.Server.Implementations.Library _configurationManager.ConfigurationUpdated += ConfigurationUpdated; + _mediaStreamRepository = mediaStreamRepository; + RecordConfigurationValues(_configurationManager.Configuration); } @@ -3800,5 +3805,11 @@ namespace Emby.Server.Implementations.Library SetTopParentOrAncestorIds(query); return _itemRepository.GetQueryFiltersLegacy(query); } + + /// <inheritdoc /> + public IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType) + { + return _mediaStreamRepository.GetMediaStreamLanguages(mediaStreamType); + } } } diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index 2f53784db1..740423ef04 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -8,6 +8,8 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -24,16 +26,19 @@ public class FilterController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; + private readonly ILocalizationManager _localization; /// <summary> /// Initializes a new instance of the <see cref="FilterController"/> class. /// </summary> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param> - public FilterController(ILibraryManager libraryManager, IUserManager userManager) + /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public FilterController(ILibraryManager libraryManager, IUserManager userManager, ILocalizationManager localization) { _libraryManager = libraryManager; _userManager = userManager; + _localization = localization; } /// <summary> @@ -183,6 +188,36 @@ public class FilterController : BaseJellyfinApiController }).ToArray(); } + if (includeItemTypes.Contains(BaseItemKind.Movie) || includeItemTypes.Contains(BaseItemKind.Series)) + { + filters.AudioLanguages = _libraryManager + .GetMediaStreamLanguages(MediaStreamType.Audio) + .Select(language => + { + var culture = _localization.FindLanguageInfo(language); + return new NameValuePair + { + Name = culture != null ? $"{culture.DisplayName} ({language})" : language, + Value = language + }; + }) + .OrderBy(l => l.Name) + .ToArray(); + filters.SubtitleLanguages = _libraryManager + .GetMediaStreamLanguages(MediaStreamType.Subtitle) + .Select(language => + { + var culture = _localization.FindLanguageInfo(language); + return new NameValuePair + { + Name = culture != null ? $"{culture.DisplayName} ({language})" : language, + Value = language + }; + }) + .OrderBy(l => l.Name) + .ToArray(); + } + return filters; } } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index a813109c96..8eca2787b1 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -421,7 +421,7 @@ public class ItemsController : BaseJellyfinApiController } else { - // if we want to know if an item has no subtitles we don't need to check for subtitles of a specific language + // if we search for items without subtitles, we don't need to check for subtitles of a specific language query.SubtitleLanguages = []; } } diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.Querying.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.Querying.cs index 71b46b3cb5..dc16c3b1b3 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.Querying.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.Querying.cs @@ -535,46 +535,12 @@ public sealed partial class BaseItemRepository .OrderBy(g => g) .ToArray(); - // At the moment language filters are only available for video types (Movie and Series libraries). - // They are fetched directly from the MediaStreamInfos table and only filtered by StreamType. - // This is the fastest and most perfomant way to get the list of available languages, - // but the filter values can include language tags that are not linked to any item in the current library. - var subtitleLanguages = IncludesVideoTypes(filter) - ? context.MediaStreamInfos - .Where(s => s.StreamType == MediaStreamTypeEntity.Subtitle) - .Select(s => string.IsNullOrEmpty(s.Language) ? "und" : s.Language) // und = undetermined - .Distinct() - .OrderBy(l => l) - .ToArray() - : []; - - var audioLanguages = IncludesVideoTypes(filter) - ? context.MediaStreamInfos - .Where(s => s.StreamType == MediaStreamTypeEntity.Audio) - .Select(s => string.IsNullOrEmpty(s.Language) ? "und" : s.Language) // und = undetermined - .Distinct() - .OrderBy(l => l) - .ToArray() - : []; - return new QueryFiltersLegacy { Years = years, OfficialRatings = officialRatings, Tags = tags, - Genres = genres, - SubtitleLanguages = subtitleLanguages, - AudioLanguages = audioLanguages + Genres = genres }; } - - private bool IncludesVideoTypes(InternalItemsQuery filter) - { - return filter.IncludeItemTypes.Contains(BaseItemKind.Movie) - || filter.IncludeItemTypes.Contains(BaseItemKind.Video) - || filter.IncludeItemTypes.Contains(BaseItemKind.Series) - || filter.IncludeItemTypes.Contains(BaseItemKind.Season) - || filter.IncludeItemTypes.Contains(BaseItemKind.Episode) - || filter.IncludeItemTypes.Contains(BaseItemKind.Trailer); - } } diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs index b58e7fffe3..3d1aafd72e 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.TranslateQuery.cs @@ -1078,6 +1078,7 @@ public sealed partial class BaseItemRepository { // Dvds and Blu-rays can either be stored in a folder structure or as an iso file // => to find all matches we need to check both: VideoType and IsoType + // alternatively, we could provide specific IsoType filters var videoTypeBs = filter.VideoTypes.Select(vt => $"\"VideoType\":\"{vt}\"").ToArray(); var isoTypeBs = filter.VideoTypes.Select(vt => $"\"IsoType\":\"{vt}\"").ToArray(); Expression<Func<BaseItemEntity, bool>> hasVideoType = e => videoTypeBs.Any(f => e.Data!.Contains(f)) || isoTypeBs.Any(f => e.Data!.Contains(f)); diff --git a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs index dd0446f49a..7fa33c8639 100644 --- a/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs +++ b/Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs @@ -55,6 +55,17 @@ public class MediaStreamRepository : IMediaStreamRepository return TranslateQuery(context.MediaStreamInfos.AsNoTracking(), filter).AsEnumerable().Select(Map).ToArray(); } + /// <inheritdoc /> + public IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType) + { + using var context = _dbProvider.CreateDbContext(); + return context.MediaStreamInfos + .Where(e => e.StreamType == (MediaStreamTypeEntity)mediaStreamType) + .Select(s => string.IsNullOrEmpty(s.Language) ? "und" : s.Language) // und = undetermined + .Distinct() + .ToArray(); + } + private string? GetPathToSave(string? path) { if (path is null) diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index f5e3d7034e..f4c2196400 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -784,5 +784,12 @@ namespace MediaBrowser.Controller.Library /// <param name="query">The query filter.</param> /// <returns>Aggregated filter values.</returns> QueryFiltersLegacy GetQueryFiltersLegacy(InternalItemsQuery query); + + /// <summary> + /// Gets a list of all language codes of the provided stream type. + /// </summary> + /// <param name="mediaStreamType">The stream type.</param> + /// <returns>List of language codes.</returns> + IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType); } } diff --git a/MediaBrowser.Controller/Persistence/IMediaStreamRepository.cs b/MediaBrowser.Controller/Persistence/IMediaStreamRepository.cs index 665129eafd..de04ff021d 100644 --- a/MediaBrowser.Controller/Persistence/IMediaStreamRepository.cs +++ b/MediaBrowser.Controller/Persistence/IMediaStreamRepository.cs @@ -22,6 +22,13 @@ public interface IMediaStreamRepository IReadOnlyList<MediaStream> GetMediaStreams(MediaStreamQuery filter); /// <summary> + /// Gets all language codes of the provided stream type. + /// </summary> + /// <param name="mediaStreamType">The type of the media stream.</param> + /// <returns>IEnumerable{string}.</returns> + IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType); + + /// <summary> /// Saves the media streams. /// </summary> /// <param name="id">The identifier.</param> diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs index 73b27a7b06..b877af71c6 100644 --- a/MediaBrowser.Model/Querying/QueryFilters.cs +++ b/MediaBrowser.Model/Querying/QueryFilters.cs @@ -12,10 +12,16 @@ namespace MediaBrowser.Model.Querying { Tags = Array.Empty<string>(); Genres = Array.Empty<NameGuidPair>(); + AudioLanguages = Array.Empty<NameValuePair>(); + SubtitleLanguages = Array.Empty<NameValuePair>(); } public NameGuidPair[] Genres { get; set; } public string[] Tags { get; set; } + + public NameValuePair[] AudioLanguages { get; set; } + + public NameValuePair[] SubtitleLanguages { get; set; } } } diff --git a/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs b/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs index aa1ca85cad..fcb450ed30 100644 --- a/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs +++ b/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs @@ -13,8 +13,6 @@ namespace MediaBrowser.Model.Querying Tags = Array.Empty<string>(); OfficialRatings = Array.Empty<string>(); Years = Array.Empty<int>(); - AudioLanguages = Array.Empty<string>(); - SubtitleLanguages = Array.Empty<string>(); } public string[] Genres { get; set; } @@ -24,9 +22,5 @@ namespace MediaBrowser.Model.Querying public string[] OfficialRatings { get; set; } public int[] Years { get; set; } - - public string[] AudioLanguages { get; set; } - - public string[] SubtitleLanguages { get; set; } } } |
