diff --git a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js index 71fc7a7a0..1099e9949 100644 --- a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js @@ -173,6 +173,7 @@ function EditMetadataProfileModalContent(props) { type={inputTypes.TEXT} name="allowedLanguages" {...allowedLanguages} + helpText={translate('Iso639-3')} onChange={onInputChange} /> diff --git a/src/NzbDrone.Core/Books/Calibre/Extensions.cs b/src/NzbDrone.Core/Books/Calibre/Extensions.cs index 8b6c0048b..9b3aa10a9 100644 --- a/src/NzbDrone.Core/Books/Calibre/Extensions.cs +++ b/src/NzbDrone.Core/Books/Calibre/Extensions.cs @@ -13,12 +13,16 @@ namespace NzbDrone.Core.Books.Calibre private static readonly Dictionary ByThree; private static readonly Dictionary NameMap; + public static HashSet KnownLanguages { get; } + static Extensions() { var assembly = Assembly.GetExecutingAssembly(); TwoToThree = InitializeDictionary(assembly, "2to3.json"); ByThree = InitializeDictionary(assembly, "by3.json"); NameMap = InitializeDictionary(assembly, "name_map.json"); + + KnownLanguages = ByThree.Keys.ToHashSet(); } private static Dictionary InitializeDictionary(Assembly assembly, string resource) diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 0d3ebd1b3..e3e025fb1 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -305,6 +305,7 @@ "IsExpandedShowFileInfo": "Show file info", "IsInUseCantDeleteAMetadataProfileThatIsAttachedToAnAuthorOrImportList": "Can't delete a metadata profile that is attached to an author or import list", "IsInUseCantDeleteAQualityProfileThatIsAttachedToAnAuthorOrImportList": "Can't delete a quality profile that is attached to an author or import list", + "Iso639-3": "ISO 639-3 language codes, or 'null', comma separated", "IsShowingMonitoredMonitorSelected": "Monitor Selected", "IsShowingMonitoredUnmonitorSelected": "Unmonitor Selected", "IsTagUsedCannotBeDeletedWhileInUse": "Cannot be deleted while in use", diff --git a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs index 53afee3d1..c494f9244 100644 --- a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs +++ b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Books; +using NzbDrone.Core.Books.Calibre; using NzbDrone.Core.ImportLists; using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles; @@ -173,14 +174,14 @@ namespace NzbDrone.Core.Profiles.Metadata private List FilterEditions(IEnumerable editions, List localEditions, List localFiles, MetadataProfile profile) { - var allowedLanguages = profile.AllowedLanguages.IsNotNullOrWhiteSpace() ? new HashSet(profile.AllowedLanguages.Split(',').Select(x => x.Trim().ToLower())) : new HashSet(); + var allowedLanguages = profile.AllowedLanguages.IsNotNullOrWhiteSpace() ? new HashSet(profile.AllowedLanguages.Trim(',').Split(',').Select(x => x.CanonicalizeLanguage())) : new HashSet(); var hash = new HashSet(editions); var localHash = new HashSet(localEditions.Where(x => x.ManualAdd).Select(x => x.ForeignEditionId)); localHash.UnionWith(localFiles.Select(x => x.Edition.Value.ForeignEditionId)); - FilterByPredicate(hash, x => x.ForeignEditionId, localHash, profile, (x, p) => !allowedLanguages.Any() || allowedLanguages.Contains(x.Language?.ToLower() ?? "null"), "edition language not allowed"); + FilterByPredicate(hash, x => x.ForeignEditionId, localHash, profile, (x, p) => !allowedLanguages.Any() || allowedLanguages.Contains(x.Language?.CanonicalizeLanguage() ?? "null"), "edition language not allowed"); FilterByPredicate(hash, x => x.ForeignEditionId, localHash, profile, (x, p) => !p.SkipMissingIsbn || x.Isbn13.IsNotNullOrWhiteSpace() || x.Asin.IsNotNullOrWhiteSpace(), "isbn and asin is missing"); FilterByPredicate(hash, x => x.ForeignEditionId, localHash, profile, (x, p) => !MatchesTerms(x.Title, p.Ignored), "contains ignored terms"); @@ -272,7 +273,7 @@ namespace NzbDrone.Core.Profiles.Metadata MinPopularity = 350, SkipMissingDate = true, SkipPartsAndSets = true, - AllowedLanguages = "eng, en-US, en-GB, null" + AllowedLanguages = "eng, null" }); } diff --git a/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileController.cs b/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileController.cs index 785d7a202..aad55ecad 100644 --- a/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileController.cs +++ b/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileController.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; +using System.Linq; using FluentValidation; using Microsoft.AspNetCore.Mvc; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Profiles.Metadata; using NzbDrone.Http.REST.Attributes; using Readarr.Http; @@ -17,6 +19,14 @@ namespace Readarr.Api.V1.Profiles.Metadata { _profileService = profileService; SharedValidator.RuleFor(c => c.Name).NotEqual("None").WithMessage("'None' is a reserved profile name").NotEmpty(); + SharedValidator.RuleFor(c => c.AllowedLanguages) + .Must(x => x + .Trim(',') + .Split(',') + .Select(y => y.Trim()) + .All(y => y == "null" || NzbDrone.Core.Books.Calibre.Extensions.KnownLanguages.Contains(y))) + .When(x => x.AllowedLanguages.IsNotNullOrWhiteSpace()) + .WithMessage("Unknown languages"); } [RestPostById]