fix missing audio and subtitle language codes (#1822)
This commit is contained in:
@@ -44,6 +44,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Fix adding pad filler to content that is less than one minute in duration
|
||||
- Generate unique identifier for virtual HDHomeRun tuner by @raknam
|
||||
- This allows a single Plex server to connect to multiple ETV instances
|
||||
- Include *all* language codes from media library in preferred audio and subtitle language options
|
||||
- Language codes where an English name cannot be found will be at the bottom of the list
|
||||
|
||||
### Changed
|
||||
- Remove some unnecessary API calls related to media server scanning and paging
|
||||
|
||||
@@ -39,7 +39,6 @@ public class CreateChannelHandler(
|
||||
private static async Task<Validation<BaseError, Channel>> Validate(TvContext dbContext, CreateChannel request) =>
|
||||
(ValidateName(request), await ValidateNumber(dbContext, request),
|
||||
await FFmpegProfileMustExist(dbContext, request),
|
||||
ValidatePreferredAudioLanguage(request),
|
||||
ValidatePreferredSubtitleLanguage(request),
|
||||
await WatermarkMustExist(dbContext, request),
|
||||
await FillerPresetMustExist(dbContext, request))
|
||||
@@ -48,7 +47,6 @@ public class CreateChannelHandler(
|
||||
name,
|
||||
number,
|
||||
ffmpegProfileId,
|
||||
preferredAudioLanguageCode,
|
||||
preferredSubtitleLanguageCode,
|
||||
watermarkId,
|
||||
fillerPresetId) =>
|
||||
@@ -76,7 +74,7 @@ public class CreateChannelHandler(
|
||||
ProgressMode = request.ProgressMode,
|
||||
StreamingMode = request.StreamingMode,
|
||||
Artwork = artwork,
|
||||
PreferredAudioLanguageCode = preferredAudioLanguageCode,
|
||||
PreferredAudioLanguageCode = request.PreferredAudioLanguageCode,
|
||||
PreferredAudioTitle = request.PreferredAudioTitle,
|
||||
PreferredSubtitleLanguageCode = preferredSubtitleLanguageCode,
|
||||
SubtitleMode = request.SubtitleMode,
|
||||
@@ -101,13 +99,6 @@ public class CreateChannelHandler(
|
||||
createChannel.NotEmpty(c => c.Name)
|
||||
.Bind(_ => createChannel.NotLongerThan(50)(c => c.Name));
|
||||
|
||||
private static Validation<BaseError, string> ValidatePreferredAudioLanguage(CreateChannel createChannel) =>
|
||||
Optional(createChannel.PreferredAudioLanguageCode ?? string.Empty)
|
||||
.Filter(
|
||||
lc => string.IsNullOrWhiteSpace(lc) || CultureInfo.GetCultures(CultureTypes.NeutralCultures).Any(
|
||||
ci => string.Equals(ci.ThreeLetterISOLanguageName, lc, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToValidation<BaseError>("Preferred audio language code is invalid");
|
||||
|
||||
private static Validation<BaseError, string> ValidatePreferredSubtitleLanguage(CreateChannel createChannel) =>
|
||||
Optional(createChannel.PreferredSubtitleLanguageCode ?? string.Empty)
|
||||
.Filter(
|
||||
|
||||
@@ -93,9 +93,8 @@ public class UpdateChannelHandler(
|
||||
|
||||
private static async Task<Validation<BaseError, Channel>> Validate(TvContext dbContext, UpdateChannel request) =>
|
||||
(await ChannelMustExist(dbContext, request), ValidateName(request),
|
||||
await ValidateNumber(dbContext, request),
|
||||
ValidatePreferredAudioLanguage(request))
|
||||
.Apply((channelToUpdate, _, _, _) => channelToUpdate);
|
||||
await ValidateNumber(dbContext, request))
|
||||
.Apply((channelToUpdate, _, _) => channelToUpdate);
|
||||
|
||||
private static Task<Validation<BaseError, Channel>> ChannelMustExist(
|
||||
TvContext dbContext,
|
||||
@@ -130,11 +129,4 @@ public class UpdateChannelHandler(
|
||||
|
||||
return BaseError.New("Channel number must be unique");
|
||||
}
|
||||
|
||||
private static Validation<BaseError, string> ValidatePreferredAudioLanguage(UpdateChannel updateChannel) =>
|
||||
Optional(updateChannel.PreferredAudioLanguageCode ?? string.Empty)
|
||||
.Filter(
|
||||
lc => string.IsNullOrWhiteSpace(lc) || CultureInfo.GetCultures(CultureTypes.NeutralCultures).Any(
|
||||
ci => string.Equals(ci.ThreeLetterISOLanguageName, lc, StringComparison.OrdinalIgnoreCase)))
|
||||
.ToValidation<BaseError>("Preferred audio language code is invalid");
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using System.Globalization;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Metadata;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems;
|
||||
|
||||
public class GetAllLanguageCodesHandler : IRequestHandler<GetAllLanguageCodes, List<LanguageCodeViewModel>>
|
||||
public class GetAllLanguageCodesHandler(IMediaItemRepository mediaItemRepository)
|
||||
: IRequestHandler<GetAllLanguageCodes, List<LanguageCodeViewModel>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public GetAllLanguageCodesHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public async Task<List<LanguageCodeViewModel>> Handle(
|
||||
GetAllLanguageCodes request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
List<CultureInfo> cultures = await _mediaItemRepository.GetAllLanguageCodeCultures();
|
||||
return cultures.Map(c => new LanguageCodeViewModel(c.ThreeLetterISOLanguageName, c.EnglishName)).ToList();
|
||||
List<LanguageCodeAndName> languageCodes = await mediaItemRepository.GetAllLanguageCodesAndNames();
|
||||
return languageCodes.Map(c => new LanguageCodeViewModel(c.Code, c.Name)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Metadata;
|
||||
|
||||
namespace ErsatzTV.Core.Interfaces.Repositories;
|
||||
|
||||
public interface IMediaItemRepository
|
||||
{
|
||||
Task<List<CultureInfo>> GetAllKnownCultures();
|
||||
Task<List<CultureInfo>> GetAllLanguageCodeCultures();
|
||||
Task<List<LanguageCodeAndName>> GetAllLanguageCodesAndNames();
|
||||
Task<List<int>> FlagFileNotFound(LibraryPath libraryPath, string path);
|
||||
Task<Unit> FlagNormal(MediaItem mediaItem);
|
||||
Task<Either<BaseError, Unit>> DeleteItems(List<int> mediaItemIds);
|
||||
|
||||
3
ErsatzTV.Core/Metadata/LanguageCodeAndName.cs
Normal file
3
ErsatzTV.Core/Metadata/LanguageCodeAndName.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Core.Metadata;
|
||||
|
||||
public record LanguageCodeAndName(string Code, string Name);
|
||||
@@ -5,6 +5,7 @@ using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Extensions;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Metadata;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -41,27 +42,38 @@ public class MediaItemRepository : IMediaItemRepository
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
public async Task<List<CultureInfo>> GetAllLanguageCodeCultures()
|
||||
public async Task<List<LanguageCodeAndName>> GetAllLanguageCodesAndNames()
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
var result = new System.Collections.Generic.HashSet<CultureInfo>();
|
||||
var result = new System.Collections.Generic.HashSet<LanguageCodeAndName>();
|
||||
|
||||
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
|
||||
List<string> mediaCodes = await GetAllLanguageCodes();
|
||||
var unseenCodes = new System.Collections.Generic.HashSet<string>(mediaCodes);
|
||||
foreach (string mediaCode in mediaCodes)
|
||||
{
|
||||
foreach (string code in await dbContext.LanguageCodes.GetAllLanguageCodes(mediaCode))
|
||||
{
|
||||
Option<CultureInfo> maybeCulture = allCultures.Find(
|
||||
c => string.Equals(code, c.ThreeLetterISOLanguageName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
foreach (CultureInfo culture in maybeCulture)
|
||||
{
|
||||
result.Add(culture);
|
||||
unseenCodes.Remove(mediaCode);
|
||||
unseenCodes.Remove(code);
|
||||
result.Add(new LanguageCodeAndName(culture.ThreeLetterISOLanguageName, culture.EnglishName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// every language code from the db must appear in the results
|
||||
// entries that have no culture info (and thus english name) will just use the code twice
|
||||
foreach (string mediaCode in unseenCodes.Where(c => !string.IsNullOrWhiteSpace(c)))
|
||||
{
|
||||
result.Add(new LanguageCodeAndName(mediaCode, mediaCode));
|
||||
}
|
||||
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,16 +15,5 @@ public class ChannelEditViewModelValidator : AbstractValidator<ChannelEditViewMo
|
||||
RuleFor(x => x.Name).NotEmpty();
|
||||
RuleFor(x => x.Group).NotEmpty();
|
||||
RuleFor(x => x.FFmpegProfileId).GreaterThan(0);
|
||||
|
||||
RuleFor(x => x.PreferredAudioLanguageCode)
|
||||
.Must(
|
||||
languageCode => CultureInfo.GetCultures(CultureTypes.NeutralCultures)
|
||||
.Any(
|
||||
ci => string.Equals(
|
||||
ci.ThreeLetterISOLanguageName,
|
||||
languageCode,
|
||||
StringComparison.OrdinalIgnoreCase)))
|
||||
.When(vm => !string.IsNullOrWhiteSpace(vm.PreferredAudioLanguageCode))
|
||||
.WithMessage("Preferred audio language code is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user