core: follow torznab specs about categories. resolves #10120 (#10147)

This commit is contained in:
Diego Heras 2020-11-08 23:27:54 +01:00 committed by GitHub
parent 10c8e33715
commit 2030d9cf13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 61 additions and 60 deletions

View File

@ -317,15 +317,11 @@ namespace Jackett.Common.Indexers
return true;
var caps = TorznabCaps;
if (query.HasSpecifiedCategories)
if (!caps.Categories.SupportsCategories(query.Categories))
return false;
if (caps.TvSearchImdbAvailable && query.IsImdbQuery && query.IsTVSearch)
return true;
if (caps.MovieSearchImdbAvailable && query.IsImdbQuery && query.IsMovieSearch)
return true;
else if (!caps.MovieSearchImdbAvailable && query.IsImdbQuery && query.QueryType != "TorrentPotato") // potato query should always contain imdb+search term
if (!caps.MovieSearchImdbAvailable && query.IsImdbQuery && query.QueryType != "TorrentPotato") // potato query should always contain imdb+search term
return false;
if (caps.SearchAvailable && query.IsSearch)
return true;
@ -349,6 +345,27 @@ namespace Jackett.Common.Indexers
return false;
}
protected bool CanHandleCategories(TorznabQuery query, bool isMetaIndexer = false)
{
// https://torznab.github.io/spec-1.3-draft/torznab/Specification-v1.3.html#cat-parameter
if (query.HasSpecifiedCategories)
{
var supportedCats = TorznabCaps.Categories.SupportedCategories(query.Categories);
if (supportedCats.Length == 0)
{
if (!isMetaIndexer)
logger.Error($"All categories provided are unsupported in {DisplayName}: {string.Join(",", query.Categories)}");
return false;
}
if (supportedCats.Length != query.Categories.Length && !isMetaIndexer)
{
var unsupportedCats = query.Categories.Except(supportedCats);
logger.Warn($"Some of the categories provided are unsupported in {DisplayName}: {string.Join(",", unsupportedCats)}");
}
}
return true;
}
public void Unconfigure()
{
IsConfigured = false;
@ -358,9 +375,9 @@ namespace Jackett.Common.Indexers
public abstract Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson);
public virtual async Task<IndexerResult> ResultsForQuery(TorznabQuery query)
public virtual async Task<IndexerResult> ResultsForQuery(TorznabQuery query, bool isMetaIndexer)
{
if (!CanHandleQuery(query))
if (!CanHandleQuery(query) || !CanHandleCategories(query, isMetaIndexer))
return new IndexerResult(this, new ReleaseInfo[0]);
try
@ -637,9 +654,9 @@ namespace Jackett.Common.Indexers
return releases;
}
public override async Task<IndexerResult> ResultsForQuery(TorznabQuery query)
public override async Task<IndexerResult> ResultsForQuery(TorznabQuery query, bool isMetaIndexer)
{
var result = await base.ResultsForQuery(query);
var result = await base.ResultsForQuery(query, isMetaIndexer);
result.Releases = CleanLinks(result.Releases);
return result;

View File

@ -51,7 +51,7 @@ namespace Jackett.Common.Indexers
void Unconfigure();
Task<IndexerResult> ResultsForQuery(TorznabQuery query);
Task<IndexerResult> ResultsForQuery(TorznabQuery query, bool isMetaIndexer=false);
bool CanHandleQuery(TorznabQuery query);
}

View File

@ -46,9 +46,9 @@ namespace Jackett.Common.Indexers.Meta
public override Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson) => Task.FromResult(IndexerConfigurationStatus.Completed);
public override async Task<IndexerResult> ResultsForQuery(TorznabQuery query)
public override async Task<IndexerResult> ResultsForQuery(TorznabQuery query, bool isMetaIndexer)
{
if (!CanHandleQuery(query))
if (!CanHandleQuery(query) || !CanHandleCategories(query, true))
return new IndexerResult(this, new ReleaseInfo[0]);
try
@ -66,11 +66,11 @@ namespace Jackett.Common.Indexers.Meta
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var indexers = validIndexers;
IEnumerable<Task<IndexerResult>> supportedTasks = indexers.Where(i => i.CanHandleQuery(query)).Select(i => i.ResultsForQuery(query)).ToList(); // explicit conversion to List to execute LINQ query
IEnumerable<Task<IndexerResult>> supportedTasks = indexers.Where(i => i.CanHandleQuery(query)).Select(i => i.ResultsForQuery(query, true)).ToList(); // explicit conversion to List to execute LINQ query
var fallbackStrategies = fallbackStrategyProvider.FallbackStrategiesForQuery(query);
var fallbackQueries = fallbackStrategies.Select(async f => await f.FallbackQueries()).SelectMany(t => t.Result);
var fallbackTasks = fallbackQueries.SelectMany(q => indexers.Where(i => !i.CanHandleQuery(query) && i.CanHandleQuery(q)).Select(i => i.ResultsForQuery(q.Clone())));
var fallbackTasks = fallbackQueries.SelectMany(q => indexers.Where(i => !i.CanHandleQuery(query) && i.CanHandleQuery(q)).Select(i => i.ResultsForQuery(q.Clone(), true)));
var tasks = supportedTasks.Concat(fallbackTasks.ToList()); // explicit conversion to List to execute LINQ query
// When there are many indexers used by a metaindexer querying each and every one of them can take very very

View File

@ -41,7 +41,6 @@ namespace Jackett.Common.Models.DTO
stringQuery.SearchTerm = queryStr;
stringQuery.Categories = request.Category ?? new int[0];
stringQuery.ExpandCatsToSubCats();
// try to build an IMDB Query (tt plus 6 to 8 digits)
if (stringQuery.SanitizedSearchTerm.StartsWith("tt") && stringQuery.SanitizedSearchTerm.Length <= 10)
@ -57,7 +56,6 @@ namespace Jackett.Common.Models.DTO
Season = stringQuery.Season,
Episode = stringQuery.Episode,
};
imdbQuery.ExpandCatsToSubCats();
return imdbQuery;
}

View File

@ -15,8 +15,6 @@ namespace Jackett.Common.Models.DTO
ImdbID = request.Imdbid,
QueryType = "TorrentPotato"
};
torznabQuery.ExpandCatsToSubCats();
return torznabQuery;
}
}

View File

@ -85,8 +85,6 @@ namespace Jackett.Common.Models.DTO
if (!string.IsNullOrWhiteSpace(request.author))
query.Author = request.author;
query.ExpandCatsToSubCats();
return query;
}
}

View File

@ -104,14 +104,13 @@ namespace Jackett.Common.Models
return cats;
}
public bool SupportsCategories(int[] categories)
public int[] SupportedCategories(int[] categories)
{
if (categories == null)
return false;
if (categories == null || categories.Length == 0)
return new int[0];
var subCategories = _torznabCategoryTree.SelectMany(c => c.SubCategories);
var allCategories = _torznabCategoryTree.Concat(subCategories);
var supportsCategory = allCategories.Any(i => categories.Any(c => c == i.ID));
return supportsCategory;
return allCategories.Where(c => categories.Contains(c.ID)).Select(c => c.ID).ToArray();
}
public void Concat(TorznabCapabilitiesCategories rhs)

View File

@ -210,24 +210,5 @@ namespace Jackett.Common.Models
}
return episodeString;
}
public void ExpandCatsToSubCats()
{
if (Categories.Count() == 0)
return;
var newCatList = new List<int>();
newCatList.AddRange(Categories);
foreach (var cat in Categories)
{
var majorCat = TorznabCatType.AllCats.Where(c => c.ID == cat).FirstOrDefault();
// If we search for TV we should also search for all sub cats
if (majorCat != null)
{
newCatList.AddRange(majorCat.SubCategories.Select(s => s.ID));
}
}
Categories = newCatList.Distinct().ToArray();
}
}
}

View File

@ -139,8 +139,9 @@ namespace Jackett.Server.Controllers
if (!resultController.CurrentIndexer.CanHandleQuery(resultController.CurrentQuery))
{
context.Result = ResultsController.GetErrorActionResult(context.RouteData, HttpStatusCode.BadRequest, 201, $"{resultController.CurrentIndexer.Id} " +
$"does not support the requested query. Please check the capabilities (t=caps) and make sure the search mode and categories are supported.");
context.Result = ResultsController.GetErrorActionResult(context.RouteData, HttpStatusCode.BadRequest, 201,
$"{resultController.CurrentIndexer.Id} does not support the requested query. " +
"Please check the capabilities (t=caps) and make sure the search mode and parameters are supported.");
}
}
@ -211,13 +212,11 @@ namespace Jackett.Server.Controllers
var manualResult = new ManualSearchResult();
var trackers = IndexerService.GetAllIndexers().ToList().Where(t => t.IsConfigured);
if (request.Tracker != null)
{
trackers = trackers.Where(t => request.Tracker.Contains(t.Id));
}
trackers = trackers.Where(t => t.CanHandleQuery(CurrentQuery));
var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery)).ToList();
var isMetaIndexer = request.Tracker == null || request.Tracker.Length > 1;
var tasks = trackers.ToList().Select(t => t.ResultsForQuery(CurrentQuery, isMetaIndexer)).ToList();
try
{
var aggregateTask = Task.WhenAll(tasks);

View File

@ -326,19 +326,30 @@ namespace Jackett.Test.Common.Models
}
[Test]
public void TestSupportsCategories()
public void TestSupportedCategories()
{
var tcc = CreateTestDataset();
Assert.True(tcc.SupportsCategories(new []{ TorznabCatType.Movies.ID })); // parent cat
Assert.True(tcc.SupportsCategories(new []{ TorznabCatType.MoviesSD.ID })); // child cat
Assert.True(tcc.SupportsCategories(new []{ TorznabCatType.Movies.ID, TorznabCatType.MoviesSD.ID })); // parent & child
Assert.True(tcc.SupportsCategories(new []{ 100040 })); // custom cat
Assert.False(tcc.SupportsCategories(new []{ TorznabCatType.Movies3D.ID })); // not supported child cat
Assert.False(tcc.SupportsCategories(new []{ 9999 })); // unknown cat
Assert.False(tcc.SupportsCategories(new []{ 100001 })); // unknown custom cat
Assert.False(tcc.SupportsCategories(new int[]{})); // empty list
Assert.False(tcc.SupportsCategories(null)); // null
Assert.AreEqual( new[] { TorznabCatType.Movies.ID }, // parent cat
tcc.SupportedCategories(new []{ TorznabCatType.Movies.ID }));
Assert.AreEqual( new[] { TorznabCatType.MoviesSD.ID }, // child cat
tcc.SupportedCategories(new []{ TorznabCatType.MoviesSD.ID }));
Assert.AreEqual( new[] { TorznabCatType.Movies.ID, TorznabCatType.MoviesSD.ID }, // parent & child cat
tcc.SupportedCategories(new []{ TorznabCatType.Movies.ID, TorznabCatType.MoviesSD.ID }));
Assert.AreEqual( new[] { 100040 }, // custom cat
tcc.SupportedCategories(new []{ 100040 }));
Assert.AreEqual( new[] { TorznabCatType.Movies.ID }, // mixed good and bad
tcc.SupportedCategories(new []{ TorznabCatType.Movies.ID, 9999 }));
Assert.AreEqual( new int[] {}, // not supported child cat
tcc.SupportedCategories(new []{ TorznabCatType.Movies3D.ID }));
Assert.AreEqual( new int[] {}, // unknown cat
tcc.SupportedCategories(new []{ 9999 }));
Assert.AreEqual( new int[] {}, // unknown custom cat
tcc.SupportedCategories(new []{ 100001 }));
Assert.AreEqual( new int[]{}, // empty list
tcc.SupportedCategories(new int[]{}));
Assert.AreEqual( new int[] {}, // null
tcc.SupportedCategories(null));
}
[Test]