From 00e4555736ee63459946ec8e80271c9df0f4876c Mon Sep 17 00:00:00 2001 From: ta264 Date: Mon, 10 May 2021 21:54:50 +0100 Subject: [PATCH] New: Add min pages and ignored terms to metadata profiles --- .../EditMetadataProfileModalContent.js | 39 ++++++++++++++++++- .../Migration/008_extend_metadata_profiles.cs | 15 +++++++ src/NzbDrone.Core/Localization/Core/en.json | 2 + .../Profiles/Metadata/MetadataProfile.cs | 2 + .../Metadata/MetadataProfileService.cs | 27 ++++++++++++- .../Metadata/MetadataProfileResource.cs | 10 ++++- 6 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/NzbDrone.Core/Datastore/Migration/008_extend_metadata_profiles.cs diff --git a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js index 65a36bd39..71fc7a7a0 100644 --- a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js @@ -15,6 +15,9 @@ import { inputTypes, kinds } from 'Helpers/Props'; import translate from 'Utilities/String/translate'; import styles from './EditMetadataProfileModalContent.css'; +// Tab, enter, and comma +const tagInputDelimiters = [9, 13, 188]; + function EditMetadataProfileModalContent(props) { const { isFetching, @@ -38,7 +41,9 @@ function EditMetadataProfileModalContent(props) { skipMissingIsbn, skipPartsAndSets, skipSeriesSecondary, - allowedLanguages + allowedLanguages, + ignored, + minPages } = item; return ( @@ -92,6 +97,21 @@ function EditMetadataProfileModalContent(props) { /> + + + {translate('MinimumPages')} + + + + + {translate('SkipBooksWithMissingReleaseDate')} @@ -157,6 +177,23 @@ function EditMetadataProfileModalContent(props) { /> + + + {translate('MustNotContain')} + + + + + } diff --git a/src/NzbDrone.Core/Datastore/Migration/008_extend_metadata_profiles.cs b/src/NzbDrone.Core/Datastore/Migration/008_extend_metadata_profiles.cs new file mode 100644 index 000000000..ab2bd3420 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/008_extend_metadata_profiles.cs @@ -0,0 +1,15 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(008)] + public class extend_metadata_profiles : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("MetadataProfiles").AddColumn("MinPages").AsInt32().NotNullable().WithDefaultValue(0); + Alter.Table("MetadataProfiles").AddColumn("Ignored").AsString().Nullable(); + } + } +} diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index b36956e24..1f3029113 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -349,6 +349,8 @@ "MinimumFreeSpace": "Minimum Free Space", "MinimumFreeSpaceWhenImportingHelpText": "Prevent import if it would leave less than this amount of disk space available", "MinimumLimits": "Minimum Limits", + "MinimumPages": "Minimum Pages", + "MinPagesHelpText": "Ignore books with fewer pages than this", "MinimumPopularity": "Minimum Popularity", "Missing": "Missing", "MissingBooks": "Missing Books", diff --git a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs index de7689d67..2c5fb0537 100644 --- a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs +++ b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs @@ -11,5 +11,7 @@ namespace NzbDrone.Core.Profiles.Metadata public bool SkipPartsAndSets { get; set; } public bool SkipSeriesSecondary { get; set; } public string AllowedLanguages { get; set; } + public int MinPages { get; set; } + public string Ignored { get; set; } } } diff --git a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs index 4fa3b4997..c121ae16e 100644 --- a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs +++ b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.ImportLists; using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Profiles.Releases; using NzbDrone.Core.RootFolders; namespace NzbDrone.Core.Profiles.Metadata @@ -36,6 +37,7 @@ namespace NzbDrone.Core.Profiles.Metadata private readonly IMediaFileService _mediaFileService; private readonly IImportListFactory _importListFactory; private readonly IRootFolderService _rootFolderService; + private readonly ITermMatcherService _termMatcherService; private readonly Logger _logger; public MetadataProfileService(IMetadataProfileRepository profileRepository, @@ -44,6 +46,7 @@ namespace NzbDrone.Core.Profiles.Metadata IMediaFileService mediaFileService, IImportListFactory importListFactory, IRootFolderService rootFolderService, + ITermMatcherService termMatcherService, Logger logger) { _profileRepository = profileRepository; @@ -52,6 +55,7 @@ namespace NzbDrone.Core.Profiles.Metadata _mediaFileService = mediaFileService; _importListFactory = importListFactory; _rootFolderService = rootFolderService; + _termMatcherService = termMatcherService; _logger = logger; } @@ -129,6 +133,8 @@ namespace NzbDrone.Core.Profiles.Metadata FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => !p.SkipMissingDate || x.ReleaseDate.HasValue, "release date is missing"); FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => !p.SkipPartsAndSets || !IsPartOrSet(x, seriesLinks.GetValueOrDefault(x), titles), "book is part of set"); FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => !p.SkipSeriesSecondary || !seriesLinks.ContainsKey(x) || seriesLinks[x].Any(y => y.IsPrimary), "book is a secondary series item"); + FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => x.Editions.Value.Any(e => e.PageCount > p.MinPages) || x.Editions.Value.All(e => e.PageCount == 0), "minimum page count not met"); + FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => !MatchesTerms(x.Title, p.Ignored), "contains ignored terms"); foreach (var book in hash) { @@ -137,7 +143,7 @@ namespace NzbDrone.Core.Profiles.Metadata book.Editions = FilterEditions(book.Editions.Value, localEditions, localFiles, profile); } - FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => x.Editions.Value.Any(), "all editions filterd out"); + FilterByPredicate(hash, x => x.ForeignBookId, localHash, profile, (x, p) => x.Editions.Value.Any(), "all editions filtered out"); return hash.ToList(); } @@ -153,6 +159,7 @@ namespace NzbDrone.Core.Profiles.Metadata 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) => !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"); return hash.ToList(); } @@ -195,6 +202,24 @@ namespace NzbDrone.Core.Profiles.Metadata return false; } + private bool MatchesTerms(string value, string terms) + { + if (terms.IsNullOrWhiteSpace() || value.IsNullOrWhiteSpace()) + { + return false; + } + + var split = terms.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var foundTerms = ContainsAny(split, value); + + return foundTerms.Any(); + } + + private List ContainsAny(List terms, string title) + { + return terms.Where(t => _termMatcherService.IsMatch(t, title)).ToList(); + } + public void Handle(ApplicationStartedEvent message) { var profiles = All(); diff --git a/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs b/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs index 8a3c84249..a5864391e 100644 --- a/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs +++ b/src/Readarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs @@ -14,6 +14,8 @@ namespace Readarr.Api.V1.Profiles.Metadata public bool SkipPartsAndSets { get; set; } public bool SkipSeriesSecondary { get; set; } public string AllowedLanguages { get; set; } + public int MinPages { get; set; } + public string Ignored { get; set; } } public static class MetadataProfileResourceMapper @@ -34,7 +36,9 @@ namespace Readarr.Api.V1.Profiles.Metadata SkipMissingIsbn = model.SkipMissingIsbn, SkipPartsAndSets = model.SkipPartsAndSets, SkipSeriesSecondary = model.SkipSeriesSecondary, - AllowedLanguages = model.AllowedLanguages + AllowedLanguages = model.AllowedLanguages, + MinPages = model.MinPages, + Ignored = model.Ignored }; } @@ -54,7 +58,9 @@ namespace Readarr.Api.V1.Profiles.Metadata SkipMissingIsbn = resource.SkipMissingIsbn, SkipPartsAndSets = resource.SkipPartsAndSets, SkipSeriesSecondary = resource.SkipSeriesSecondary, - AllowedLanguages = resource.AllowedLanguages + AllowedLanguages = resource.AllowedLanguages, + MinPages = resource.MinPages, + Ignored = resource.Ignored }; }