New: Add option to search for anime using standard episode numbers
Closes #4153
This commit is contained in:
parent
05b1581b7d
commit
cee17483d9
|
@ -0,0 +1,85 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers.Fanzub;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.FanzubTests
|
||||
{
|
||||
public class FanzubRequestGeneratorFixture : CoreTest<FanzubRequestGenerator>
|
||||
{
|
||||
private SeasonSearchCriteria _seasonSearchCriteria;
|
||||
private AnimeEpisodeSearchCriteria _animeSearchCriteria;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Subject.Settings = new FanzubSettings()
|
||||
{
|
||||
BaseUrl = "http://127.0.0.1:1234/",
|
||||
};
|
||||
|
||||
_seasonSearchCriteria = new SeasonSearchCriteria()
|
||||
{
|
||||
SceneTitles = new List<string>() { "Naruto Shippuuden" },
|
||||
SeasonNumber = 1,
|
||||
};
|
||||
|
||||
_animeSearchCriteria = new AnimeEpisodeSearchCriteria()
|
||||
{
|
||||
SceneTitles = new List<string>() { "Naruto Shippuuden" },
|
||||
AbsoluteEpisodeNumber = 9,
|
||||
SeasonNumber = 1,
|
||||
EpisodeNumber = 9
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_season()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_seasonSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_search_season()
|
||||
{
|
||||
Subject.Settings.AnimeStandardFormatSearch = true;
|
||||
var results = Subject.GetSearchRequests(_seasonSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.FullUri.Should().Contain("q=\"Naruto+Shippuuden%20S01\"|\"Naruto+Shippuuden%20-%20S01\"");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_only_absolute_numbering_for_anime_search()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.FullUri.Should().Contain("q=\"Naruto+Shippuuden%2009\"|\"Naruto+Shippuuden%20-%2009\"");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_also_use_standard_numbering_for_anime_search()
|
||||
{
|
||||
Subject.Settings.AnimeStandardFormatSearch = true;
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.FullUri.Should().Contain("q=\"Naruto+Shippuuden%2009\"|\"Naruto+Shippuuden%20-%2009\"|\"Naruto+Shippuuden%20S01E09\"|\"Naruto+Shippuuden%20-%20S01E09\"");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
_animeSearchCriteria = new AnimeEpisodeSearchCriteria()
|
||||
{
|
||||
SceneTitles = new List<string>() { "Monkey+Island" },
|
||||
AbsoluteEpisodeNumber = 100
|
||||
AbsoluteEpisodeNumber = 100,
|
||||
SeasonNumber = 5,
|
||||
EpisodeNumber = 4
|
||||
};
|
||||
|
||||
_capabilities = new NewznabCapabilities();
|
||||
|
@ -123,6 +125,31 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
pages.Count.Should().BeLessThan(500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_only_absolute_numbering_for_anime_search()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.FullUri.Should().Contain("q=Monkey%20Island+100");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_also_use_standard_numbering_for_anime_search()
|
||||
{
|
||||
Subject.Settings.AnimeStandardFormatSearch = true;
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetTier(0).Should().HaveCount(2);
|
||||
var pages = results.GetTier(0).Take(2).Select(t => t.First()).ToList();
|
||||
|
||||
pages[0].Url.FullUri.Should().Contain("q=Monkey%20Island+100");
|
||||
pages[1].Url.FullUri.Should().Contain("q=Monkey%20Island+s05e04");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_by_rid_if_not_supported()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers.Nyaa;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.NyaaTests
|
||||
{
|
||||
public class NyaaRequestGeneratorFixture : CoreTest<NyaaRequestGenerator>
|
||||
{
|
||||
private SeasonSearchCriteria _seasonSearchCriteria;
|
||||
private AnimeEpisodeSearchCriteria _animeSearchCriteria;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Subject.Settings = new NyaaSettings()
|
||||
{
|
||||
BaseUrl = "http://127.0.0.1:1234/",
|
||||
};
|
||||
|
||||
_seasonSearchCriteria = new SeasonSearchCriteria()
|
||||
{
|
||||
SceneTitles = new List<string>() { "Naruto Shippuuden" },
|
||||
SeasonNumber = 1,
|
||||
};
|
||||
|
||||
_animeSearchCriteria = new AnimeEpisodeSearchCriteria()
|
||||
{
|
||||
SceneTitles = new List<string>() { "Naruto Shippuuden" },
|
||||
AbsoluteEpisodeNumber = 9,
|
||||
SeasonNumber = 1,
|
||||
EpisodeNumber = 9
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_search_season()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_seasonSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_search_season()
|
||||
{
|
||||
Subject.Settings.AnimeStandardFormatSearch = true;
|
||||
var results = Subject.GetSearchRequests(_seasonSearchCriteria);
|
||||
|
||||
results.GetAllTiers().Should().HaveCount(1);
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.FullUri.Should().Contain("term=Naruto+Shippuuden+s01");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_only_absolute_numbering_for_anime_search()
|
||||
{
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetTier(0).Should().HaveCount(2);
|
||||
var pages = results.GetTier(0).Take(2).Select(t => t.First()).ToList();
|
||||
|
||||
pages[0].Url.FullUri.Should().Contain("term=Naruto+Shippuuden+9");
|
||||
pages[1].Url.FullUri.Should().Contain("term=Naruto+Shippuuden+09");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_also_use_standard_numbering_for_anime_search()
|
||||
{
|
||||
Subject.Settings.AnimeStandardFormatSearch = true;
|
||||
var results = Subject.GetSearchRequests(_animeSearchCriteria);
|
||||
|
||||
results.GetTier(0).Should().HaveCount(3);
|
||||
var pages = results.GetTier(0).Take(3).Select(t => t.First()).ToList();
|
||||
|
||||
pages[0].Url.FullUri.Should().Contain("term=Naruto+Shippuuden+9");
|
||||
pages[1].Url.FullUri.Should().Contain("term=Naruto+Shippuuden+09");
|
||||
pages[2].Url.FullUri.Should().Contain("term=Naruto+Shippuuden+s01e09");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,11 +3,13 @@
|
|||
public class AnimeEpisodeSearchCriteria : SearchCriteriaBase
|
||||
{
|
||||
public int AbsoluteEpisodeNumber { get; set; }
|
||||
public int EpisodeNumber { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public bool IsSeasonSearch { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0} : {1:00}]", Series.Title, AbsoluteEpisodeNumber);
|
||||
return $"[{Series.Title} : S{SeasonNumber:00}E{EpisodeNumber:00} ({AbsoluteEpisodeNumber:00})]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -349,19 +349,9 @@ namespace NzbDrone.Core.IndexerSearch
|
|||
|
||||
searchSpec.IsSeasonSearch = isSeasonSearch;
|
||||
|
||||
if (episode.SceneAbsoluteEpisodeNumber.HasValue)
|
||||
{
|
||||
searchSpec.AbsoluteEpisodeNumber = episode.SceneAbsoluteEpisodeNumber.Value;
|
||||
}
|
||||
else if (episode.AbsoluteEpisodeNumber.HasValue)
|
||||
{
|
||||
searchSpec.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error($"Can not search for {series.Title} - S{episode.SeasonNumber:00}E{episode.EpisodeNumber:00} it does not have an absolute episode number");
|
||||
throw new SearchFailedException("Absolute episode number is missing");
|
||||
}
|
||||
searchSpec.SeasonNumber = episode.SceneSeasonNumber ?? episode.SeasonNumber;
|
||||
searchSpec.EpisodeNumber = episode.SceneEpisodeNumber ?? episode.EpisodeNumber;
|
||||
searchSpec.AbsoluteEpisodeNumber = episode.SceneAbsoluteEpisodeNumber ?? episode.AbsoluteEpisodeNumber ?? 0;
|
||||
|
||||
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,15 @@ namespace NzbDrone.Core.Indexers.Fanzub
|
|||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
if (Settings.AnimeStandardFormatSearch && searchCriteria.SeasonNumber > 0)
|
||||
{
|
||||
var searchTitles = searchCriteria.CleanSceneTitles.SelectMany(v => GetSeasonSearchStrings(v, searchCriteria.SeasonNumber)).ToList();
|
||||
pageableRequests.Add(GetPagedRequests(string.Join("|", searchTitles)));
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
|
||||
|
@ -55,6 +63,11 @@ namespace NzbDrone.Core.Indexers.Fanzub
|
|||
|
||||
var searchTitles = searchCriteria.CleanSceneTitles.SelectMany(v => GetTitleSearchStrings(v, searchCriteria.AbsoluteEpisodeNumber)).ToList();
|
||||
|
||||
if (Settings.AnimeStandardFormatSearch && searchCriteria.SeasonNumber > 0 && searchCriteria.EpisodeNumber > 0)
|
||||
{
|
||||
searchTitles.AddRange(searchCriteria.CleanSceneTitles.SelectMany(v => GetTitleSearchStrings(v, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber)).ToList());
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Join("|", searchTitles)));
|
||||
|
||||
return pageableRequests;
|
||||
|
@ -78,6 +91,13 @@ namespace NzbDrone.Core.Indexers.Fanzub
|
|||
yield return new IndexerRequest(url.ToString(), HttpAccept.Rss);
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSeasonSearchStrings(string title, int seasonNumber)
|
||||
{
|
||||
var formats = new[] { "{0}%20S{1:00}", "{0}%20-%20S{1:00}" };
|
||||
|
||||
return formats.Select(s => "\"" + string.Format(s, CleanTitle(title), seasonNumber) + "\"");
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetTitleSearchStrings(string title, int absoluteEpisodeNumber)
|
||||
{
|
||||
var formats = new[] { "{0}%20{1:00}", "{0}%20-%20{1:00}" };
|
||||
|
@ -85,6 +105,13 @@ namespace NzbDrone.Core.Indexers.Fanzub
|
|||
return formats.Select(s => "\"" + string.Format(s, CleanTitle(title), absoluteEpisodeNumber) + "\"");
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetTitleSearchStrings(string title, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
var formats = new[] { "{0}%20S{1:00}E{2:00}", "{0}%20-%20S{1:00}E{2:00}" };
|
||||
|
||||
return formats.Select(s => "\"" + string.Format(s, CleanTitle(title), seasonNumber, episodeNumber) + "\"");
|
||||
}
|
||||
|
||||
private string CleanTitle(string title)
|
||||
{
|
||||
return RemoveCharactersRegex.Replace(title, "");
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace NzbDrone.Core.Indexers.Fanzub
|
|||
[FieldDefinition(0, Label = "Rss URL", HelpText = "Enter to URL to an Fanzub compatible RSS feed")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Anime Standard Format Search", Type = FieldType.Checkbox, HelpText = "Also search for anime using the standard numbering")]
|
||||
public bool AnimeStandardFormatSearch { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
|
|
@ -321,6 +321,15 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
string.Format("&q={0}+{1:00}",
|
||||
NewsnabifyTitle(queryTitle),
|
||||
searchCriteria.AbsoluteEpisodeNumber)));
|
||||
|
||||
if (Settings.AnimeStandardFormatSearch && searchCriteria.SeasonNumber > 0 && searchCriteria.EpisodeNumber > 0)
|
||||
{
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, Settings.AnimeCategories, "search",
|
||||
string.Format("&q={0}+s{1:00}e{2:00}",
|
||||
NewsnabifyTitle(queryTitle),
|
||||
searchCriteria.SeasonNumber,
|
||||
searchCriteria.EpisodeNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,10 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
[FieldDefinition(4, Label = "Anime Categories", Type = FieldType.Select, SelectOptionsProviderAction = "newznabCategories", HelpText = "Drop down list, leave blank to disable anime")]
|
||||
public IEnumerable<int> AnimeCategories { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)]
|
||||
[FieldDefinition(5, Label = "Anime Standard Format Search", Type = FieldType.Checkbox, HelpText = "Also search for anime using the standard numbering")]
|
||||
public bool AnimeStandardFormatSearch { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)]
|
||||
public string AdditionalParameters { get; set; }
|
||||
|
||||
// Field 6 is used by TorznabSettings MinimumSeeders
|
||||
|
|
|
@ -33,7 +33,19 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
|||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria)
|
||||
{
|
||||
return new IndexerPageableRequestChain();
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
if (Settings.AnimeStandardFormatSearch && searchCriteria.SeasonNumber > 0)
|
||||
{
|
||||
foreach (var queryTitle in searchCriteria.SceneTitles)
|
||||
{
|
||||
var searchTitle = PrepareQuery(queryTitle);
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+s{searchCriteria.SeasonNumber:00}"));
|
||||
}
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
|
||||
|
@ -54,11 +66,19 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
|||
{
|
||||
var searchTitle = PrepareQuery(queryTitle);
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+{searchCriteria.AbsoluteEpisodeNumber:0}"));
|
||||
|
||||
if (searchCriteria.AbsoluteEpisodeNumber < 10)
|
||||
if (searchCriteria.AbsoluteEpisodeNumber > 0)
|
||||
{
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+{searchCriteria.AbsoluteEpisodeNumber:00}"));
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+{searchCriteria.AbsoluteEpisodeNumber:0}"));
|
||||
|
||||
if (searchCriteria.AbsoluteEpisodeNumber < 10)
|
||||
{
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+{searchCriteria.AbsoluteEpisodeNumber:00}"));
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.AnimeStandardFormatSearch && searchCriteria.SeasonNumber > 0 && searchCriteria.EpisodeNumber > 0)
|
||||
{
|
||||
pageableRequests.Add(GetPagedRequests(MaxPages, $"{searchTitle}+s{searchCriteria.SeasonNumber:00}e{searchCriteria.EpisodeNumber:00}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,13 +30,16 @@ namespace NzbDrone.Core.Indexers.Nyaa
|
|||
[FieldDefinition(0, Label = "Website URL")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")]
|
||||
[FieldDefinition(1, Label = "Anime Standard Format Search", Type = FieldType.Checkbox, HelpText = "Also search for anime using the standard numbering")]
|
||||
public bool AnimeStandardFormatSearch { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")]
|
||||
public string AdditionalParameters { get; set; }
|
||||
|
||||
[FieldDefinition(2, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
[FieldDefinition(3, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
[FieldDefinition(3)]
|
||||
[FieldDefinition(4)]
|
||||
public SeedCriteriaSettings SeedCriteria { get; } = new SeedCriteriaSettings();
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
|
|
@ -57,10 +57,10 @@ namespace NzbDrone.Core.Indexers.Torznab
|
|||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
[FieldDefinition(7, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
[FieldDefinition(7)]
|
||||
[FieldDefinition(8)]
|
||||
public SeedCriteriaSettings SeedCriteria { get; } = new SeedCriteriaSettings();
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
|
|
Loading…
Reference in New Issue