diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs index f45cb03ec..dcb40c9f5 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs @@ -10,17 +10,6 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public int? Year { get; set; } public string Genre { get; set; } - public override bool RssSearch - { - get - { - if (SearchTerm.IsNullOrWhiteSpace() && Author.IsNullOrWhiteSpace() && Title.IsNullOrWhiteSpace()) - { - return true; - } - - return false; - } - } + public override bool RssSearch => SearchTerm.IsNullOrWhiteSpace() && Author.IsNullOrWhiteSpace() && Title.IsNullOrWhiteSpace(); } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs index f69d88a3b..6dbbff82c 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs @@ -13,18 +13,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public int? Year { get; set; } public string Genre { get; set; } - public override bool RssSearch - { - get - { - if (SearchTerm.IsNullOrWhiteSpace() && ImdbId.IsNullOrWhiteSpace() && !TmdbId.HasValue && !TraktId.HasValue) - { - return true; - } - - return false; - } - } + public override bool RssSearch => SearchTerm.IsNullOrWhiteSpace() && ImdbId.IsNullOrWhiteSpace() && !TmdbId.HasValue && !TraktId.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 4026367cc..85db08428 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs @@ -11,17 +11,6 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public string Track { get; set; } public int? Year { get; set; } - public override bool RssSearch - { - get - { - if (SearchTerm.IsNullOrWhiteSpace() && Album.IsNullOrWhiteSpace() && Artist.IsNullOrWhiteSpace() && Label.IsNullOrWhiteSpace()) - { - return true; - } - - return false; - } - } + public override bool RssSearch => SearchTerm.IsNullOrWhiteSpace() && Album.IsNullOrWhiteSpace() && Artist.IsNullOrWhiteSpace() && Label.IsNullOrWhiteSpace(); } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 3b4d86e42..fb76ed829 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -7,9 +7,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions { public abstract class SearchCriteriaBase { - private static readonly Regex SpecialCharacter = new Regex(@"[`'.]", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex NonWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex StandardizeDashesRegex = new (@"\p{Pd}+", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex StandardizeSingleQuotesRegex = new (@"[\u0060\u00B4\u2018\u2019]", RegexOptions.IgnoreCase | RegexOptions.Compiled); public virtual bool InteractiveSearch { get; set; } public List IndexerIds { get; set; } @@ -21,58 +20,24 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public string Source { get; set; } public string Host { get; set; } - public virtual string SearchQuery - { - get - { - return $"Term: [{SearchTerm}]"; - } - } + public override string ToString() => $"{SearchQuery}, Offset: {Offset ?? 0}, Limit: {Limit ?? 0}, Categories: [{string.Join(", ", Categories)}]"; - public override string ToString() - { - return $"{SearchQuery}, Offset: {Offset ?? 0}, Limit: {Limit ?? 0}, Categories: [{string.Join(", ", Categories)}]"; - } + public virtual string SearchQuery => $"Term: [{SearchTerm}]"; - public virtual bool RssSearch - { - get - { - if (SearchTerm.IsNullOrWhiteSpace()) - { - return true; - } + public virtual bool RssSearch => SearchTerm.IsNullOrWhiteSpace(); - return false; - } - } + public string SanitizedSearchTerm => GetSanitizedTerm(SearchTerm); - public string SanitizedSearchTerm + private static string GetSanitizedTerm(string term) { - get - { - var term = SearchTerm; - if (SearchTerm == null) - { - term = ""; - } + term ??= ""; + + term = StandardizeDashesRegex.Replace(term, "-"); + term = StandardizeSingleQuotesRegex.Replace(term, "'"); + + var safeTitle = term.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c) || c is '-' or '.' or '_' or '(' or ')' or '@' or '/' or '\'' or '[' or ']' or '+' or '%'); - var safeTitle = term.Where(c => (char.IsLetterOrDigit(c) - || char.IsWhiteSpace(c) - || c == '-' - || c == '.' - || c == '_' - || c == '(' - || c == ')' - || c == '@' - || c == '/' - || c == '\'' - || c == '[' - || c == ']' - || c == '+' - || c == '%')); - return string.Concat(safeTitle); - } + return string.Concat(safeTitle); } } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs index f04761c3d..f2d0192c9 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs @@ -21,23 +21,12 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public int? Year { get; set; } public string Genre { get; set; } - public string SanitizedTvSearchString => (SanitizedSearchTerm + " " + EpisodeSearchString).Trim(); + public string SanitizedTvSearchString => $"{SanitizedSearchTerm} {EpisodeSearchString}".Trim(); public string EpisodeSearchString => GetEpisodeSearchString(); public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId); - public override bool RssSearch - { - get - { - if (SearchTerm.IsNullOrWhiteSpace() && ImdbId.IsNullOrWhiteSpace() && !TvdbId.HasValue && !RId.HasValue && !TraktId.HasValue && !TvMazeId.HasValue) - { - return true; - } - - return false; - } - } + public override bool RssSearch => SearchTerm.IsNullOrWhiteSpace() && ImdbId.IsNullOrWhiteSpace() && !TvdbId.HasValue && !RId.HasValue && !TraktId.HasValue && !TvMazeId.HasValue; public override string SearchQuery { diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs index b4f3a3a6b..b21ae489e 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs @@ -46,7 +46,7 @@ public class GazelleRequestGenerator : IIndexerRequestGenerator { var pageableRequests = new IndexerPageableRequestChain(); - var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + var parameters = GetBasicSearchParameters(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories); if (searchCriteria.ImdbId != null) { @@ -62,9 +62,9 @@ public class GazelleRequestGenerator : IIndexerRequestGenerator { var pageableRequests = new IndexerPageableRequestChain(); - var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + var parameters = GetBasicSearchParameters(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories); - if (searchCriteria.Artist.IsNotNullOrWhiteSpace()) + if (searchCriteria.Artist.IsNotNullOrWhiteSpace() && searchCriteria.Artist != "VA") { parameters.Set("artistname", searchCriteria.Artist); } @@ -104,7 +104,7 @@ public class GazelleRequestGenerator : IIndexerRequestGenerator { var pageableRequests = new IndexerPageableRequestChain(); - var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + var parameters = GetBasicSearchParameters(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories); pageableRequests.Add(GetRequest(parameters)); return pageableRequests; @@ -114,7 +114,7 @@ public class GazelleRequestGenerator : IIndexerRequestGenerator { var pageableRequests = new IndexerPageableRequestChain(); - var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories); + var parameters = GetBasicSearchParameters(searchCriteria.SanitizedSearchTerm, searchCriteria.Categories); pageableRequests.Add(GetRequest(parameters)); return pageableRequests; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Libble.cs b/src/NzbDrone.Core/Indexers/Definitions/Libble.cs index 58dfc6c3a..e6a806780 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Libble.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Libble.cs @@ -128,14 +128,17 @@ public class LibbleRequestGenerator : IIndexerRequestGenerator var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - if (searchCriteria.Artist.IsNotNullOrWhiteSpace()) + if (searchCriteria.Artist.IsNotNullOrWhiteSpace() && searchCriteria.Artist != "VA") { parameters.Set("artistname", searchCriteria.Artist); } if (searchCriteria.Album.IsNotNullOrWhiteSpace()) { - parameters.Set("groupname", searchCriteria.Album); + // Remove year + var album = Regex.Replace(searchCriteria.Album, @"(.+)\b\d{4}$", "$1"); + + parameters.Set("groupname", album.Trim()); } if (searchCriteria.Label.IsNotNullOrWhiteSpace()) @@ -188,9 +191,14 @@ public class LibbleRequestGenerator : IIndexerRequestGenerator { var term = searchCriteria.SanitizedSearchTerm.Trim(); + parameters.Set("action", "advanced"); parameters.Set("order_by", "time"); parameters.Set("order_way", "desc"); - parameters.Set("searchstr", term); + + if (term.IsNotNullOrWhiteSpace()) + { + parameters.Set("searchstr", term); + } var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); if (queryCats.Any()) @@ -276,7 +284,9 @@ public class LibbleParser : IParseIndexerResponse Guid = infoUrl, InfoUrl = infoUrl, DownloadUrl = downloadLink, - Title = $"{releaseArtist} - {releaseAlbumName} {releaseAlbumYear} {releaseTags}".Trim(' ', '-'), + Title = $"{releaseArtist} - {releaseAlbumName} {releaseAlbumYear.Value} {releaseTags}".Trim(' ', '-'), + Artist = releaseArtist, + Album = releaseAlbumName, Categories = ParseCategories(group), Description = releaseDescription, Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(4)").TextContent.Trim()), diff --git a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs index dc0b39e1e..43927d995 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Orpheus.cs @@ -135,22 +135,22 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - if (searchCriteria.Artist.IsNotNullOrWhiteSpace()) + if (searchCriteria.Artist.IsNotNullOrWhiteSpace() && searchCriteria.Artist != "VA") { - parameters.Add("artistname", searchCriteria.Artist); + parameters.Set("artistname", searchCriteria.Artist); } if (searchCriteria.Album.IsNotNullOrWhiteSpace()) { - parameters.Add("groupname", searchCriteria.Album); + parameters.Set("groupname", searchCriteria.Album); } if (searchCriteria.Year.HasValue) { - parameters.Add("year", searchCriteria.Year.ToString()); + parameters.Set("year", searchCriteria.Year.ToString()); } - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } @@ -160,7 +160,7 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } @@ -180,28 +180,28 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } - private IEnumerable GetRequest(SearchCriteriaBase searchCriteria, NameValueCollection parameters) + private IEnumerable GetPagedRequests(SearchCriteriaBase searchCriteria, NameValueCollection parameters) { var term = searchCriteria.SanitizedSearchTerm.Trim(); - parameters.Add("action", "browse"); - parameters.Add("order_by", "time"); - parameters.Add("order_way", "desc"); - parameters.Add("searchstr", term); + parameters.Set("action", "browse"); + parameters.Set("order_by", "time"); + parameters.Set("order_way", "desc"); - var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); + if (term.IsNotNullOrWhiteSpace()) + { + parameters.Set("searchstr", term); + } - if (queryCats.Count > 0) + var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); + if (queryCats.Any()) { - foreach (var cat in queryCats) - { - parameters.Add($"filter_cat[{cat}]", "1"); - } + queryCats.ForEach(cat => parameters.Set($"filter_cat[{cat}]", "1")); } var request = RequestBuilder() @@ -267,12 +267,14 @@ namespace NzbDrone.Core.Indexers.Definitions var release = new GazelleInfo { Guid = infoUrl, + InfoUrl = infoUrl, + DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken), Title = WebUtility.HtmlDecode(title), + Artist = WebUtility.HtmlDecode(result.Artist), + Album = WebUtility.HtmlDecode(result.GroupName), Container = torrent.Encoding, Codec = torrent.Format, Size = long.Parse(torrent.Size), - DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken), - InfoUrl = infoUrl, Seeders = int.Parse(torrent.Seeders), Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), PublishDate = torrent.Time.ToUniversalTime(), diff --git a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs index 53c46f390..505a173e0 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs @@ -117,22 +117,22 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - if (searchCriteria.Artist.IsNotNullOrWhiteSpace()) + if (searchCriteria.Artist.IsNotNullOrWhiteSpace() && searchCriteria.Artist != "VA") { - parameters.Add("artistname", searchCriteria.Artist); + parameters.Set("artistname", searchCriteria.Artist); } if (searchCriteria.Album.IsNotNullOrWhiteSpace()) { - parameters.Add("groupname", searchCriteria.Album); + parameters.Set("groupname", searchCriteria.Album); } if (searchCriteria.Year.HasValue) { - parameters.Add("year", searchCriteria.Year.ToString()); + parameters.Set("year", searchCriteria.Year.ToString()); } - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } @@ -142,7 +142,7 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } @@ -162,27 +162,28 @@ namespace NzbDrone.Core.Indexers.Definitions var pageableRequests = new IndexerPageableRequestChain(); var parameters = new NameValueCollection(); - pageableRequests.Add(GetRequest(searchCriteria, parameters)); + pageableRequests.Add(GetPagedRequests(searchCriteria, parameters)); return pageableRequests; } - private IEnumerable GetRequest(SearchCriteriaBase searchCriteria, NameValueCollection parameters) + private IEnumerable GetPagedRequests(SearchCriteriaBase searchCriteria, NameValueCollection parameters) { var term = searchCriteria.SanitizedSearchTerm.Trim(); - parameters.Add("action", "browse"); - parameters.Add("order_by", "time"); - parameters.Add("order_way", "desc"); - parameters.Add("searchstr", term); + parameters.Set("action", "browse"); + parameters.Set("order_by", "time"); + parameters.Set("order_way", "desc"); + + if (term.IsNotNullOrWhiteSpace()) + { + parameters.Set("searchstr", term); + } var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories); if (queryCats.Any()) { - foreach (var cat in queryCats) - { - parameters.Add($"filter_cat[{cat}]", "1"); - } + queryCats.ForEach(cat => parameters.Set($"filter_cat[{cat}]", "1")); } var request = RequestBuilder() @@ -248,12 +249,14 @@ namespace NzbDrone.Core.Indexers.Definitions var release = new GazelleInfo { Guid = infoUrl, + InfoUrl = infoUrl, + DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken), Title = WebUtility.HtmlDecode(title), + Artist = WebUtility.HtmlDecode(result.Artist), + Album = WebUtility.HtmlDecode(result.GroupName), Container = torrent.Encoding, Codec = torrent.Format, Size = long.Parse(torrent.Size), - DownloadUrl = GetDownloadUrl(id, torrent.CanUseToken), - InfoUrl = infoUrl, Seeders = int.Parse(torrent.Seeders), Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), PublishDate = torrent.Time.ToUniversalTime(), diff --git a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs index de01a3a3a..e230fe5f5 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/SecretCinema.cs @@ -146,13 +146,15 @@ public class SecretCinemaParser : IParseIndexerResponse release.Title = $"{title} ({result.GroupYear}) {torrent.Media}"; // Replace media formats with standards - release.Title = Regex.Replace(release.Title, "BDMV", "COMPLETE BLURAY", RegexOptions.IgnoreCase); - release.Title = Regex.Replace(release.Title, "SD", "DVDRip", RegexOptions.IgnoreCase); + release.Title = Regex.Replace(release.Title, @"\bBDMV\b", "COMPLETE BLURAY", RegexOptions.IgnoreCase); + release.Title = Regex.Replace(release.Title, @"\bSD\b", "DVDRip", RegexOptions.IgnoreCase); } else { // SC API currently doesn't return anything but title. release.Title = $"{artist} - {title} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]"; + release.Artist = artist; + release.Album = title; } if (torrent.HasCue)