diff --git a/src/Jackett.Common/Indexers/XSpeeds.cs b/src/Jackett.Common/Indexers/XSpeeds.cs index d4984a6d7..4f73e80af 100644 --- a/src/Jackett.Common/Indexers/XSpeeds.cs +++ b/src/Jackett.Common/Indexers/XSpeeds.cs @@ -247,7 +247,6 @@ namespace Jackett.Common.Indexers protected override async Task> PerformQuery(TorznabQuery query) { - var searchString = query.GetQueryString(); var prevCook = CookieHeader + ""; var categoryMapping = MapTorznabCapsToTrackers(query); @@ -261,12 +260,14 @@ namespace Jackett.Common.Indexers { "order", GetOrder } }; + var searchString = Regex.Replace(query.GetQueryString(), @"[ -._]+", " ").Trim(); + if (query.IsImdbQuery) { searchParams.Add("keywords", query.ImdbID); searchParams.Add("search_type", "t_both"); } - else + else if (!string.IsNullOrWhiteSpace(searchString)) { searchParams.Add("keywords", searchString); searchParams.Add("search_type", "t_name"); diff --git a/src/Jackett.Common/Models/DTO/ApiSearch.cs b/src/Jackett.Common/Models/DTO/ApiSearch.cs index 5760dc24d..e98e85faf 100644 --- a/src/Jackett.Common/Models/DTO/ApiSearch.cs +++ b/src/Jackett.Common/Models/DTO/ApiSearch.cs @@ -1,3 +1,4 @@ +using System; using System.Text.RegularExpressions; using Jackett.Common.Utils; @@ -16,48 +17,53 @@ namespace Jackett.Common.Models.DTO QueryType = "search" }; - var queryStr = request.Query; - if (queryStr != null) + var queryStr = $"{request.Query}".Trim(); + + if (!string.IsNullOrWhiteSpace(queryStr)) { - var seasonMatch = Regex.Match(queryStr, @"S(\d{2,4})"); - if (seasonMatch.Success) + var seasonEpisodeMatch = Regex.Match(queryStr, @"\bS(\d{2,4})E(\d{2,4}[A-Za-z]?)$"); + if (seasonEpisodeMatch.Success) { - stringQuery.Season = int.Parse(seasonMatch.Groups[1].Value); - queryStr = queryStr.Remove(seasonMatch.Index, seasonMatch.Length); + stringQuery.Season = int.Parse(seasonEpisodeMatch.Groups[1].Value); + stringQuery.Episode = seasonEpisodeMatch.Groups[2].Value.TrimStart('0'); + queryStr = queryStr.Remove(seasonEpisodeMatch.Index, seasonEpisodeMatch.Length).Trim(); + } + else + { + var episodeMatch = Regex.Match(queryStr, @"\bE(\d{2,4}[A-Za-z]?)$"); + if (episodeMatch.Success) + { + stringQuery.Episode = episodeMatch.Groups[1].Value.TrimStart('0'); + queryStr = queryStr.Remove(episodeMatch.Index, episodeMatch.Length).Trim(); + } + + var seasonMatch = Regex.Match(queryStr, @"\bS(\d{2,4})$"); + if (seasonMatch.Success) + { + stringQuery.Season = int.Parse(seasonMatch.Groups[1].Value); + queryStr = queryStr.Remove(seasonMatch.Index, seasonMatch.Length).Trim(); + } } - var episodeMatch = Regex.Match(queryStr, @"E(\d{2,4}[A-Za-z]?)"); - if (episodeMatch.Success) - { - stringQuery.Episode = episodeMatch.Groups[1].Value.TrimStart(new char[] { '0' }); - queryStr = queryStr.Remove(episodeMatch.Index, episodeMatch.Length); - } queryStr = queryStr.Trim(); } - else - { - queryStr = ""; // empty string search is interpreted as null - } stringQuery.SearchTerm = queryStr; - stringQuery.Categories = request.Category ?? new int[0]; + stringQuery.Categories = request.Category ?? Array.Empty(); // try to build an IMDB Query (tt plus 6 to 8 digits) if (stringQuery.SanitizedSearchTerm.StartsWith("tt") && stringQuery.SanitizedSearchTerm.Length <= 10) { - var imdbID = ParseUtil.GetFullImdbID(stringQuery.SanitizedSearchTerm); - TorznabQuery imdbQuery = null; - if (imdbID != null) + var imdbId = ParseUtil.GetFullImdbID(stringQuery.SanitizedSearchTerm); + if (imdbId != null) { - imdbQuery = new TorznabQuery() + return new TorznabQuery { - ImdbID = imdbID, + ImdbID = imdbId, Categories = stringQuery.Categories, Season = stringQuery.Season, Episode = stringQuery.Episode, }; - - return imdbQuery; } } diff --git a/src/Jackett.Test/Common/Models/DTO/ApiSearchTests.cs b/src/Jackett.Test/Common/Models/DTO/ApiSearchTests.cs new file mode 100644 index 000000000..e57b05e92 --- /dev/null +++ b/src/Jackett.Test/Common/Models/DTO/ApiSearchTests.cs @@ -0,0 +1,32 @@ +using Jackett.Common.Models.DTO; +using NUnit.Framework; + +namespace Jackett.Test.Common.Models.DTO +{ + [TestFixture] + public class ApiSearchTests + { + [TestCase("The.Good.Lord.S01E05.720p.WEB.H264-CAKES", "The.Good.Lord.S01E05.720p.WEB.H264-CAKES", 0, null)] + [TestCase("The.Good.Lord.S01E05.", "The.Good.Lord.S01E05.", 0, null)] + [TestCase("The.Good.Lord.S01E05", "The.Good.Lord. S01E05", 1, "5")] + [TestCase("The Good Lord S01E05", "The Good Lord S01E05", 1, "5")] + [TestCase("The Good Lord S01 E05", "The Good Lord S01E05", 1, "5")] + [TestCase("The Good Lord S01", "The Good Lord S01", 1, null)] + [TestCase("The Good Lord E05", "The Good Lord", 0, "5")] + [TestCase("The.Good.Lord.s01e05", "The.Good.Lord.s01e05", 0, null)] + [TestCase("The.Good.Lord.S01e05", "The.Good.Lord.S01e05", 0, null)] + [TestCase("The.Good.Lord.s01E05", "The.Good.Lord.s01E05", 0, null)] + [TestCase("The.Good.Lord.S1E5", "The.Good.Lord.S1E5", 0, null)] + [TestCase("The.Good.Lord.S11E5", "The.Good.Lord.S11E5", 0, null)] + [TestCase("The.Good.Lord.S1E15", "The.Good.Lord.S1E15", 0, null)] + public void TestToTorznabQuery(string query, string expected, int season, string episode) + { + var request = new ApiSearch { Query = query }; + var currentQuery = ApiSearch.ToTorznabQuery(request); + + Assert.AreEqual(expected, currentQuery.GetQueryString()); + Assert.AreEqual(season, currentQuery.Season); + Assert.AreEqual(episode, currentQuery.Episode); + } + } +}