diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs index 0be33b033..d20f0c178 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs @@ -12,10 +12,13 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public override bool IsRssSearch => SearchTerm.IsNullOrWhiteSpace() && - Author.IsNullOrWhiteSpace() && - Title.IsNullOrWhiteSpace() && - Publisher.IsNullOrWhiteSpace() && - Genre.IsNullOrWhiteSpace() && - !Year.HasValue; + !IsIdSearch; + + public override bool IsIdSearch => + Author.IsNotNullOrWhiteSpace() || + Title.IsNotNullOrWhiteSpace() || + Publisher.IsNotNullOrWhiteSpace() || + Genre.IsNotNullOrWhiteSpace() || + Year.HasValue; } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs index 5882d5113..c3145bc77 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs @@ -15,12 +15,15 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public override bool IsRssSearch => SearchTerm.IsNullOrWhiteSpace() && - ImdbId.IsNullOrWhiteSpace() && - Genre.IsNullOrWhiteSpace() && - !TmdbId.HasValue && - !TraktId.HasValue && - !DoubanId.HasValue && - !Year.HasValue; + !IsIdSearch; + + public override bool IsIdSearch => + ImdbId.IsNotNullOrWhiteSpace() || + Genre.IsNotNullOrWhiteSpace() || + TmdbId.HasValue || + TraktId.HasValue || + DoubanId.HasValue || + Year.HasValue; public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId); diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs index 8f29a00f0..ac4f69c82 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs @@ -13,11 +13,14 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public override bool IsRssSearch => SearchTerm.IsNullOrWhiteSpace() && - Album.IsNullOrWhiteSpace() && - Artist.IsNullOrWhiteSpace() && - Label.IsNullOrWhiteSpace() && - Genre.IsNullOrWhiteSpace() && - Track.IsNullOrWhiteSpace() && - !Year.HasValue; + !IsIdSearch; + + public override bool IsIdSearch => + Album.IsNotNullOrWhiteSpace() || + Artist.IsNotNullOrWhiteSpace() || + Label.IsNotNullOrWhiteSpace() || + Genre.IsNotNullOrWhiteSpace() || + Track.IsNotNullOrWhiteSpace() || + Year.HasValue; } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 8a5e12318..339abff86 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -26,6 +26,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public virtual bool IsRssSearch => SearchTerm.IsNullOrWhiteSpace(); + public virtual bool IsIdSearch => false; + public string SanitizedSearchTerm => GetSanitizedTerm(SearchTerm); private static string GetSanitizedTerm(string term) diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs index 6d8ffeee5..1e4c35e5b 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs @@ -28,15 +28,18 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public override bool IsRssSearch => SearchTerm.IsNullOrWhiteSpace() && - Episode.IsNullOrWhiteSpace() && - ImdbId.IsNullOrWhiteSpace() && - !Season.HasValue && - !TvdbId.HasValue && - !RId.HasValue && - !TraktId.HasValue && - !TvMazeId.HasValue && - !TmdbId.HasValue && - !DoubanId.HasValue; + !IsIdSearch; + + public override bool IsIdSearch => + Episode.IsNotNullOrWhiteSpace() || + ImdbId.IsNotNullOrWhiteSpace() || + Season.HasValue || + TvdbId.HasValue || + RId.HasValue || + TraktId.HasValue || + TvMazeId.HasValue || + TmdbId.HasValue || + DoubanId.HasValue; public override string SearchQuery { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs index 5edfd8b5f..d6337ad2b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/Cardigann.cs @@ -7,8 +7,10 @@ using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerVersions; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; @@ -77,6 +79,18 @@ namespace NzbDrone.Core.Indexers.Cardigann }; } + protected override IList CleanupReleases(IEnumerable releases, SearchCriteriaBase searchCriteria) + { + var cleanReleases = base.CleanupReleases(releases, searchCriteria); + + if (_definitionService.GetCachedDefinition(Settings.DefinitionFile).Search?.Rows?.Filters?.Any(x => x.Name == "andmatch") ?? false) + { + cleanReleases = FilterReleasesByQuery(releases, searchCriteria).ToList(); + } + + return cleanReleases; + } + protected override IDictionary GetCookies() { if (Settings.ExtraFieldData.TryGetValue("cookie", out var cookies)) diff --git a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs index c1dfc58ba..d89837432 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SpeedApp.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; +using System.Linq; using NLog; using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions { @@ -19,6 +22,13 @@ namespace NzbDrone.Core.Indexers.Definitions { } + protected override IList CleanupReleases(IEnumerable releases, SearchCriteriaBase searchCriteria) + { + var cleanReleases = base.CleanupReleases(releases, searchCriteria); + + return FilterReleasesByQuery(cleanReleases, searchCriteria).ToList(); + } + protected override IndexerCapabilities SetCapabilities() { var caps = new IndexerCapabilities diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index e1d61c2b6..29f819c99 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers return Task.FromResult(new IndexerPageableQueryResult()); } - return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); + return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } public override Task Fetch(MusicSearchCriteria searchCriteria) @@ -70,7 +70,7 @@ namespace NzbDrone.Core.Indexers return Task.FromResult(new IndexerPageableQueryResult()); } - return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); + return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } public override Task Fetch(TvSearchCriteria searchCriteria) @@ -80,7 +80,7 @@ namespace NzbDrone.Core.Indexers return Task.FromResult(new IndexerPageableQueryResult()); } - return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); + return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } public override Task Fetch(BookSearchCriteria searchCriteria) @@ -90,7 +90,7 @@ namespace NzbDrone.Core.Indexers return Task.FromResult(new IndexerPageableQueryResult()); } - return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); + return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } public override Task Fetch(BasicSearchCriteria searchCriteria) @@ -100,7 +100,7 @@ namespace NzbDrone.Core.Indexers return Task.FromResult(new IndexerPageableQueryResult()); } - return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria)); + return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria), searchCriteria); } public override async Task Download(Uri link) @@ -233,7 +233,7 @@ namespace NzbDrone.Core.Indexers _indexerStatusService.UpdateCookies(Definition.Id, cookies, expiration); } - protected virtual async Task FetchReleases(Func pageableRequestChainSelector, bool isRecent = false) + protected virtual async Task FetchReleases(Func pageableRequestChainSelector, SearchCriteriaBase searchCriteria, bool isRecent = false) { var releases = new List(); var result = new IndexerPageableQueryResult(); @@ -367,7 +367,7 @@ namespace NzbDrone.Core.Indexers _logger.Error(ex, "An error occurred while processing indexer feed. {0}", url); } - result.Releases = CleanupReleases(releases); + result.Releases = CleanupReleases(releases, searchCriteria); return result; } diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index e56121013..f565fd96f 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentValidation.Results; using NLog; @@ -104,7 +105,7 @@ namespace NzbDrone.Core.Indexers public abstract IndexerCapabilities GetCapabilities(); - protected virtual IList CleanupReleases(IEnumerable releases) + protected virtual IList CleanupReleases(IEnumerable releases, SearchCriteriaBase searchCriteria) { var result = releases.ToList(); @@ -146,6 +147,24 @@ namespace NzbDrone.Core.Indexers return result.DistinctBy(v => v.Guid).ToList(); } + protected IEnumerable FilterReleasesByQuery(IEnumerable releases, SearchCriteriaBase searchCriteria) + { + var commonWords = new[] { "and", "the", "an", "of" }; + + if (!searchCriteria.IsRssSearch && !searchCriteria.IsIdSearch) + { + var splitRegex = new Regex("[^\\w]+"); + + // split search term to individual terms for less aggressive filtering, filter common terms + var terms = splitRegex.Split(searchCriteria.SearchTerm).Where(t => t.IsNotNullOrWhiteSpace() && t.Length > 1 && !commonWords.ContainsIgnoreCase(t)); + + // check in title and description for any term searched for + releases = releases.Where(r => terms.Any(t => r.Title.ContainsIgnoreCase(t) || r.Description.ContainsIgnoreCase(t))).ToList(); + } + + return releases; + } + protected virtual TSettings GetDefaultBaseUrl(TSettings settings) { var defaultLink = IndexerUrls.FirstOrDefault();