diff --git a/CHANGELOG.md b/CHANGELOG.md index af48c224..3f2b4856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix adding items to empty playlists - Fix filler preset editor and deco dead air fallback editor to only show supported collection types - Fix infinite loop caused by impossible schedule (all collection items longer than schedule item duration) +- Fix selecting audio and subtitle streams with two-letter language codes ### Changed - Remove some unnecessary API calls related to media server scanning and paging diff --git a/ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs b/ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs index 9850542b..98daa4c2 100644 --- a/ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs +++ b/ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs @@ -24,7 +24,7 @@ public class GetArtistByIdHandler : IRequestHandler { List mediaCodes = await _searchRepository.GetLanguagesForArtist(artist); - List languageCodes = await _searchRepository.GetAllLanguageCodes(mediaCodes); + List languageCodes = await _searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes); return ProjectToViewModel(artist, languageCodes); }, () => Task.FromResult(Option.None)); diff --git a/ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs b/ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs index 73867ae7..b198049c 100644 --- a/ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs +++ b/ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs @@ -35,7 +35,7 @@ public class GetTelevisionShowByIdHandler : IRequestHandler list.HeadOrNone()); List mediaCodes = await _searchRepository.GetLanguagesForShow(show); - List languageCodes = await _searchRepository.GetAllLanguageCodes(mediaCodes); + List languageCodes = await _searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes); return ProjectToViewModel(show, languageCodes, maybeJellyfin, maybeEmby); }, () => Task.FromResult(Option.None)); diff --git a/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs b/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs index 4c056794..5ff8c864 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.Metadata; @@ -69,7 +70,8 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector }); } - List allLanguageCodes = await _searchRepository.GetAllLanguageCodes(new List { language }); + List allLanguageCodes = await _searchRepository.GetAllThreeLetterLanguageCodes([language]) + .Map(GetTwoAndThreeLetterLanguageCodes); if (allLanguageCodes.Count > 1) { _logger.LogDebug("Preferred audio language has multiple codes {Codes}", allLanguageCodes); @@ -178,7 +180,8 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector else { // filter to preferred language - allCodes = await _searchRepository.GetAllLanguageCodes(new List { language }); + allCodes = await _searchRepository.GetAllThreeLetterLanguageCodes([language]) + .Map(GetTwoAndThreeLetterLanguageCodes); if (allCodes.Count > 1) { _logger.LogDebug("Preferred subtitle language has multiple codes {Codes}", allCodes); @@ -402,6 +405,26 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector return Option.None; } + private static List GetTwoAndThreeLetterLanguageCodes(List threeLetterLanguageCodes) + { + CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures); + var result = new System.Collections.Generic.HashSet(); + + foreach (string code in threeLetterLanguageCodes) + { + IEnumerable cultures = allCultures + .Filter(ci => string.Equals(ci.ThreeLetterISOLanguageName, code, StringComparison.OrdinalIgnoreCase)); + + foreach (CultureInfo culture in cultures) + { + result.Add(culture.ThreeLetterISOLanguageName); + result.Add(culture.TwoLetterISOLanguageName); + } + } + + return result.ToList(); + } + private static AudioStream[] GetAudioStreamsForScript(MediaVersion version) => version.Streams .Filter(s => s.MediaStreamKind == MediaStreamKind.Audio) .Map(a => new AudioStream(a.Index, a.Channels, a.Codec, a.Default, a.Forced, a.Language, a.Title)) diff --git a/ErsatzTV.Core/Interfaces/Repositories/ISearchRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ISearchRepository.cs index 97e9c200..df66eb27 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ISearchRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ISearchRepository.cs @@ -11,6 +11,6 @@ public interface ISearchRepository Task> GetSubLanguagesForSeason(Season season); Task> GetLanguagesForArtist(Artist artist); Task> GetSubLanguagesForArtist(Artist artist); - Task> GetAllLanguageCodes(List mediaCodes); + Task> GetAllThreeLetterLanguageCodes(List mediaCodes); IAsyncEnumerable GetAllMediaItems(); } diff --git a/ErsatzTV.Infrastructure.Tests/Data/Repositories/Caching/CachingSearchRepositoryTests.cs b/ErsatzTV.Infrastructure.Tests/Data/Repositories/Caching/CachingSearchRepositoryTests.cs index 4e123625..4d84b124 100644 --- a/ErsatzTV.Infrastructure.Tests/Data/Repositories/Caching/CachingSearchRepositoryTests.cs +++ b/ErsatzTV.Infrastructure.Tests/Data/Repositories/Caching/CachingSearchRepositoryTests.cs @@ -19,15 +19,15 @@ public class CachingSearchRepositoryTests var frenchResult = new List { "french_result" }; ISearchRepository searchRepo = Substitute.For(); - searchRepo.GetAllLanguageCodes(englishMediaCodes).Returns(englishResult.AsTask()); - searchRepo.GetAllLanguageCodes(frenchMediaCodes).Returns(frenchResult.AsTask()); + searchRepo.GetAllThreeLetterLanguageCodes(englishMediaCodes).Returns(englishResult.AsTask()); + searchRepo.GetAllThreeLetterLanguageCodes(frenchMediaCodes).Returns(frenchResult.AsTask()); var repo = new CachingSearchRepository(searchRepo); - List result1 = await repo.GetAllLanguageCodes(englishMediaCodes); + List result1 = await repo.GetAllThreeLetterLanguageCodes(englishMediaCodes); result1.Should().BeEquivalentTo(englishResult); - List result2 = await repo.GetAllLanguageCodes(frenchMediaCodes); + List result2 = await repo.GetAllThreeLetterLanguageCodes(frenchMediaCodes); result2.Should().BeEquivalentTo(frenchResult); } } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/Caching/CachingSearchRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/Caching/CachingSearchRepository.cs index 96abe50a..27479152 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/Caching/CachingSearchRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/Caching/CachingSearchRepository.cs @@ -29,14 +29,14 @@ public class CachingSearchRepository : ICachingSearchRepository public Task> GetSubLanguagesForArtist(Artist artist) => _searchRepository.GetSubLanguagesForArtist(artist); - public async Task> GetAllLanguageCodes(List mediaCodes) + public async Task> GetAllThreeLetterLanguageCodes(List mediaCodes) { if (!_cache.ContainsKey(mediaCodes)) { await _slim.WaitAsync(); try { - _cache.TryAdd(mediaCodes, await _searchRepository.GetAllLanguageCodes(mediaCodes)); + _cache.TryAdd(mediaCodes, await _searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes)); } finally { diff --git a/ErsatzTV.Infrastructure/Data/Repositories/SearchRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/SearchRepository.cs index 56bb88a8..1bcd3ab5 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/SearchRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/SearchRepository.cs @@ -231,7 +231,7 @@ public class SearchRepository : ISearchRepository new { ArtistId = artist.Id }).Map(result => result.ToList()); } - public virtual async Task> GetAllLanguageCodes(List mediaCodes) + public virtual async Task> GetAllThreeLetterLanguageCodes(List mediaCodes) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await dbContext.LanguageCodes.GetAllLanguageCodes(mediaCodes); diff --git a/ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs b/ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs index 267d2bd5..20f9c980 100644 --- a/ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs +++ b/ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs @@ -852,7 +852,7 @@ public class ElasticSearchIndex : ISearchIndex private async Task> GetLanguages(ISearchRepository searchRepository, List mediaCodes) { var englishNames = new System.Collections.Generic.HashSet(); - foreach (string code in await searchRepository.GetAllLanguageCodes(mediaCodes)) + foreach (string code in await searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes)) { Option maybeCultureInfo = _cultureInfos.Find( ci => string.Equals(ci.ThreeLetterISOLanguageName, code, StringComparison.OrdinalIgnoreCase)); diff --git a/ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs b/ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs index 82d149bf..26862638 100644 --- a/ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs +++ b/ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs @@ -540,7 +540,7 @@ public sealed class LuceneSearchIndex : ISearchIndex } var englishNames = new System.Collections.Generic.HashSet(); - foreach (string code in await searchRepository.GetAllLanguageCodes(mediaCodes)) + foreach (string code in await searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes)) { Option maybeCultureInfo = _cultureInfos.Find( ci => string.Equals(ci.ThreeLetterISOLanguageName, code, StringComparison.OrdinalIgnoreCase)); @@ -564,7 +564,7 @@ public sealed class LuceneSearchIndex : ISearchIndex } var englishNames = new System.Collections.Generic.HashSet(); - foreach (string code in await searchRepository.GetAllLanguageCodes(mediaCodes)) + foreach (string code in await searchRepository.GetAllThreeLetterLanguageCodes(mediaCodes)) { Option maybeCultureInfo = _cultureInfos.Find( ci => string.Equals(ci.ThreeLetterISOLanguageName, code, StringComparison.OrdinalIgnoreCase)); diff --git a/ErsatzTV/Program.cs b/ErsatzTV/Program.cs index edc4a6c1..9c5fb047 100644 --- a/ErsatzTV/Program.cs +++ b/ErsatzTV/Program.cs @@ -80,6 +80,9 @@ public class Program .MinimumLevel.Override( "ErsatzTV.Core.FFmpeg.FFmpegLibraryProcessService", LoggingLevelSwitches.StreamingLevelSwitch) + .MinimumLevel.Override( + "ErsatzTV.Core.FFmpeg.FFmpegStreamSelector", + LoggingLevelSwitches.StreamingLevelSwitch) .MinimumLevel.Override("ErsatzTV.Controllers.IptvController", LoggingLevelSwitches.StreamingLevelSwitch) .MinimumLevel.Override("ErsatzTV.Controllers.InternalController", LoggingLevelSwitches.StreamingLevelSwitch)