core: move categories code into a new class (#10005)

This commit is contained in:
Diego Heras
2020-10-27 22:17:03 +01:00
committed by GitHub
parent 4b743e4040
commit 861655395b
7 changed files with 438 additions and 296 deletions

View File

@@ -283,7 +283,7 @@ namespace Jackett.Common.Indexers
var caps = TorznabCaps;
if (query.HasSpecifiedCategories)
if (!caps.SupportsCategories(query.Categories))
if (!caps.Categories.SupportsCategories(query.Categories))
return false;
if (caps.TvSearchImdbAvailable && query.IsImdbQuery && query.IsTVSearch)
return true;
@@ -577,127 +577,29 @@ namespace Jackett.Common.Indexers
}
}
protected List<string> GetAllTrackerCategories() => categoryMapping.Select(x => x.TrackerCategory).ToList();
protected List<string> GetAllTrackerCategories() =>
TorznabCaps.Categories.GetTrackerCategories();
protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory, string trackerCategoryDesc = null)
{
categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, newznabCategory.ID));
if (!TorznabCaps.Categories.Contains(newznabCategory))
TorznabCaps.Categories.Add(newznabCategory);
protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory, string trackerCategoryDesc = null) =>
TorznabCaps.Categories.AddCategoryMapping(trackerCategory, newznabCategory, trackerCategoryDesc);
// add 1:1 categories
if (trackerCategoryDesc != null && trackerCategory != null)
{
//TODO convert to int.TryParse() to avoid using throw as flow control
try
{
var trackerCategoryInt = int.Parse(trackerCategory);
var CustomCat = new TorznabCategory(trackerCategoryInt + 100000, trackerCategoryDesc);
if (!TorznabCaps.Categories.Contains(CustomCat))
TorznabCaps.Categories.Add(CustomCat);
}
catch (FormatException)
{
// trackerCategory is not an integer, continue
}
}
}
protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory, string trackerCategoryDesc = null) => AddCategoryMapping(trackerCategory.ToString(), newznabCategory, trackerCategoryDesc);
protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory, string trackerCategoryDesc = null) =>
AddCategoryMapping(trackerCategory.ToString(), newznabCategory, trackerCategoryDesc);
protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories)
{
foreach (var trackerCat in trackerCategories)
{
AddCategoryMapping(trackerCat, newznabCategory);
}
}
protected virtual List<string> MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false)
{
var result = new List<string>();
foreach (var cat in query.Categories)
{
// use 1:1 mapping to tracker categories for newznab categories >= 100000
if (cat >= 100000)
{
result.Add((cat - 100000).ToString());
continue;
}
protected List<string> MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false) =>
TorznabCaps.Categories.MapTorznabCapsToTrackers(query, mapChildrenCatsToParent);
var queryCats = new List<int> { cat };
var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat);
if (newznabCat != null)
{
queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID));
}
protected ICollection<int> MapTrackerCatToNewznab(string input) =>
TorznabCaps.Categories.MapTrackerCatToNewznab(input);
if (mapChildrenCatsToParent)
{
var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat));
if (parentNewznabCat != null)
{
queryCats.Add(parentNewznabCat.ID);
}
}
foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory)))
{
result.Add(mapping.TrackerCategory);
}
}
return result.Distinct().ToList();
}
protected ICollection<int> MapTrackerCatToNewznab(string input)
{
if (input == null)
return new List<int>();
var cats = categoryMapping.Where(m => m.TrackerCategory != null && m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant()).Select(c => c.NewzNabCategory).ToList();
// 1:1 category mapping
try
{
var trackerCategoryInt = int.Parse(input);
cats.Add(trackerCategoryInt + 100000);
}
catch (FormatException)
{
// input is not an integer, continue
}
return cats;
}
protected ICollection<int> MapTrackerCatDescToNewznab(string input)
{
var cats = new List<int>();
if (null != input)
{
var mapping = categoryMapping.Where(m => m.TrackerCategoryDesc != null && m.TrackerCategoryDesc.ToLowerInvariant() == input.ToLowerInvariant()).FirstOrDefault();
if (mapping != null)
{
cats.Add(mapping.NewzNabCategory);
if (mapping.TrackerCategory != null)
{
// 1:1 category mapping
try
{
var trackerCategoryInt = int.Parse(mapping.TrackerCategory);
cats.Add(trackerCategoryInt + 100000);
}
catch (FormatException)
{
// mapping.TrackerCategory is not an integer, continue
}
}
}
}
return cats;
}
protected ICollection<int> MapTrackerCatDescToNewznab(string input) =>
TorznabCaps.Categories.MapTrackerCatDescToNewznab(input);
private IEnumerable<ReleaseInfo> CleanLinks(IEnumerable<ReleaseInfo> releases)
{
@@ -748,7 +650,6 @@ namespace Jackett.Common.Indexers
public override TorznabCapabilities TorznabCaps { get; protected set; }
private readonly List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
protected WebClient webclient;
protected readonly string downloadUrlBase = "";
}

View File

@@ -51,11 +51,11 @@ namespace Jackett.Common.Models.DTO
site_link = indexer.SiteLink;
language = indexer.Language;
last_error = indexer.LastError;
potatoenabled = indexer.TorznabCaps.Categories.Any(i => TorznabCatType.Movies.Contains(i));
potatoenabled = indexer.TorznabCaps.Categories.GetTorznabCategories().Any(i => TorznabCatType.Movies.Contains(i));
alternativesitelinks = indexer.AlternativeSiteLinks;
caps = indexer.TorznabCaps.Categories
caps = indexer.TorznabCaps.Categories.GetTorznabCategories()
.GroupBy(p => p.ID)
.Select(g => g.First())
.OrderBy(c => c.ID < 100000 ? "z" + c.ID.ToString() : c.Name)

View File

@@ -71,7 +71,7 @@ namespace Jackett.Common.Models
public bool BookSearchTitleAvailable => (BookSearchParams.Contains(BookSearchParam.Title));
public bool BookSearchAuthorAvailable => (BookSearchParams.Contains(BookSearchParam.Author));
public List<TorznabCategory> Categories { get; set; }
public readonly TorznabCapabilitiesCategories Categories;
public TorznabCapabilities()
{
@@ -80,7 +80,7 @@ namespace Jackett.Common.Models
MovieSearchParams = new List<MovieSearchParam>();
MusicSearchParams = new List<MusicSearchParam>();
BookSearchParams = new List<BookSearchParam>();
Categories = new List<TorznabCategory>();
Categories = new TorznabCapabilitiesCategories();
}
public void ParseCardigannSearchModes(Dictionary<string,List<string>> modes)
@@ -219,14 +219,6 @@ namespace Jackett.Common.Models
return string.Join(",", parameters);
}
public bool SupportsCategories(int[] categories)
{
var subCategories = Categories.SelectMany(c => c.SubCategories);
var allCategories = Categories.Concat(subCategories);
var supportsCategory = allCategories.Any(i => categories.Any(c => c == i.ID));
return supportsCategory;
}
public XDocument GetXDocument()
{
var xdoc = new XDocument(
@@ -269,7 +261,7 @@ namespace Jackett.Common.Models
)
),
new XElement("categories",
from c in Categories.OrderBy(x => x.ID < 100000 ? "z" + x.ID.ToString() : x.Name)
from c in Categories.GetTorznabCategories().OrderBy(x => x.ID < 100000 ? "z" + x.ID.ToString() : x.Name)
select new XElement("category",
new XAttribute("id", c.ID),
new XAttribute("name", c.Name),
@@ -295,7 +287,7 @@ namespace Jackett.Common.Models
lhs.MovieSearchParams = lhs.MovieSearchParams.Union(rhs.MovieSearchParams).ToList();
lhs.MusicSearchParams = lhs.MusicSearchParams.Union(rhs.MusicSearchParams).ToList();
lhs.BookSearchParams = lhs.BookSearchParams.Union(rhs.BookSearchParams).ToList();
lhs.Categories.AddRange(rhs.Categories.Where(x => x.ID < 100000).Except(lhs.Categories)); // exclude indexer specific categories (>= 100000)
lhs.Categories.Concat(rhs.Categories);
return lhs;
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Jackett.Common.Models
{
public class TorznabCapabilitiesCategories
{
private readonly List<TorznabCategory> _categories = new List<TorznabCategory>();
private readonly List<CategoryMapping> _categoryMapping = new List<CategoryMapping>();
public List<TorznabCategory> GetTorznabCategories() => _categories;
public List<string> GetTrackerCategories() => _categoryMapping.Select(x => x.TrackerCategory).ToList();
public void AddCategoryMapping(string trackerCategory, TorznabCategory torznabCategory, string trackerCategoryDesc = null)
{
_categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, torznabCategory.ID));
if (!_categories.Contains(torznabCategory))
_categories.Add(torznabCategory);
// add 1:1 categories
if (trackerCategoryDesc != null && trackerCategory != null)
{
//TODO convert to int.TryParse() to avoid using throw as flow control
try
{
var trackerCategoryInt = int.Parse(trackerCategory);
var customCat = new TorznabCategory(trackerCategoryInt + 100000, trackerCategoryDesc);
if (!_categories.Contains(customCat))
_categories.Add(customCat);
}
catch (FormatException)
{
// trackerCategory is not an integer, continue
}
}
}
public List<string> MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false)
{
var result = new List<string>();
foreach (var cat in query.Categories)
{
// use 1:1 mapping to tracker categories for newznab categories >= 100000
if (cat >= 100000)
{
result.Add((cat - 100000).ToString());
continue;
}
var queryCats = new List<int> { cat };
var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat);
if (newznabCat != null)
{
queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID));
}
if (mapChildrenCatsToParent)
{
var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat));
if (parentNewznabCat != null)
{
queryCats.Add(parentNewznabCat.ID);
}
}
foreach (var mapping in _categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory)))
{
result.Add(mapping.TrackerCategory);
}
}
return result.Distinct().ToList();
}
public ICollection<int> MapTrackerCatToNewznab(string input)
{
if (input == null)
return new List<int>();
var cats = _categoryMapping
.Where(m => m.TrackerCategory != null && m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant())
.Select(c => c.NewzNabCategory).ToList();
// 1:1 category mapping
try
{
var trackerCategoryInt = int.Parse(input);
cats.Add(trackerCategoryInt + 100000);
}
catch (FormatException)
{
// input is not an integer, continue
}
return cats;
}
public ICollection<int> MapTrackerCatDescToNewznab(string input)
{
var cats = new List<int>();
if (null != input)
{
var mapping = _categoryMapping
.FirstOrDefault(m => m.TrackerCategoryDesc != null && m.TrackerCategoryDesc.ToLowerInvariant() == input.ToLowerInvariant());
if (mapping != null)
{
cats.Add(mapping.NewzNabCategory);
if (mapping.TrackerCategory != null)
{
// 1:1 category mapping
try
{
var trackerCategoryInt = int.Parse(mapping.TrackerCategory);
cats.Add(trackerCategoryInt + 100000);
}
catch (FormatException)
{
// mapping.TrackerCategory is not an integer, continue
}
}
}
}
return cats;
}
public bool SupportsCategories(int[] categories)
{
if (categories == null)
return false;
var subCategories = _categories.SelectMany(c => c.SubCategories);
var allCategories = _categories.Concat(subCategories);
var supportsCategory = allCategories.Any(i => categories.Any(c => c == i.ID));
return supportsCategory;
}
public void Concat(TorznabCapabilitiesCategories rhs) =>
// exclude indexer specific categories (>= 100000)
// we don't concat _categoryMapping because it makes no sense for the aggregate indexer
_categories.AddRange(rhs._categories.Where(x => x.ID < 100000).Except(_categories));
}
}

View File

@@ -0,0 +1,232 @@
using System.Linq;
using Jackett.Common.Models;
using NUnit.Framework;
using Assert = NUnit.Framework.Assert;
namespace Jackett.Test.Common.Models
{
[TestFixture]
public class TorznabCapabilitiesCategoriesTests
{
[Test]
public void TestConstructor()
{
var tcc = new TorznabCapabilitiesCategories();
Assert.IsEmpty(tcc.GetTorznabCategories());
Assert.IsEmpty(tcc.GetTrackerCategories());
}
[Test]
public void TestGetTorznabCategories()
{
var tcc = CreateTestDataset();
var cats = tcc.GetTorznabCategories();
Assert.AreEqual(7, cats.Count);
Assert.AreEqual(2000, cats[0].ID);
}
[Test]
public void TestGetTrackerCategories()
{
var tcc = CreateTestDataset();
var trackerCats = tcc.GetTrackerCategories();
Assert.AreEqual(6, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]);
}
[Test]
public void TestAddCategoryMapping()
{
var tcc = new TorznabCapabilitiesCategories();
var cats = tcc.GetTorznabCategories();
// add "int" category (parent category)
tcc.AddCategoryMapping("1", TorznabCatType.Movies);
Assert.AreEqual(1, cats.Count);
Assert.AreEqual(2000, cats[0].ID);
// add "string" category (child category)
tcc.AddCategoryMapping("mov_sd", TorznabCatType.MoviesSD);
Assert.AreEqual(2, cats.Count);
Assert.AreEqual(2030, cats[1].ID);
// add subcategory of books (child category)
tcc.AddCategoryMapping("33", TorznabCatType.BooksComics);
Assert.AreEqual(3, cats.Count);
Assert.AreEqual(7020, cats[2].ID);
// add int category with description => custom category. it's converted into 2 different categories
tcc.AddCategoryMapping("44", TorznabCatType.ConsoleXbox, "Console/Xbox_c");
Assert.AreEqual(5, cats.Count);
Assert.AreEqual(1040, cats[3].ID);
Assert.AreEqual(100044, cats[4].ID);
// TODO: we should add a way to add custom categories for string categories
// https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer#caps-endpoint
// add string category with description. it's converted into 1 category
tcc.AddCategoryMapping("con_wii", TorznabCatType.ConsoleWii, "Console/Wii_c");
Assert.AreEqual(6, cats.Count);
Assert.AreEqual(1030, cats[5].ID);
// add another int category with description that maps to ConsoleXbox (there are 2 tracker cats => 1 torznab cat)
tcc.AddCategoryMapping("45", TorznabCatType.ConsoleXbox, "Console/Xbox_c2");
Assert.AreEqual(7, cats.Count);
Assert.AreEqual(100045, cats[6].ID); // 1040 is duplicated and it is not added
}
[Test]
public void TestMapTorznabCapsToTrackers()
{
// MapTorznabCapsToTrackers: maps TorznabQuery cats => Tracker cats
var tcc = CreateTestDataset();
var query = new TorznabQuery(); // no cats
var trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(0, trackerCats.Count);
query = new TorznabQuery // int category with subcategories (parent cat)
{
Categories = new [] { TorznabCatType.Movies.ID }
};
trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
Assert.AreEqual("mov_sd", trackerCats[1]); // Movies SD
query = new TorznabQuery // string child category
{
Categories = new [] { TorznabCatType.MoviesSD.ID }
};
trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(1, trackerCats.Count);
Assert.AreEqual("mov_sd", trackerCats[0]); // Movies SD
trackerCats = tcc.MapTorznabCapsToTrackers(query, true); // get parent
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
Assert.AreEqual("mov_sd", trackerCats[1]); // Movies SD
query = new TorznabQuery // duplicate category (1 toznab cat => 2 indexer cats)
{
Categories = new [] { TorznabCatType.ConsoleXbox.ID }
};
trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("44", trackerCats[0]);
Assert.AreEqual("45", trackerCats[1]);
query = new TorznabQuery // custom cat
{
Categories = new [] { 100001 } // Movies
};
trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(1, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
query = new TorznabQuery // unknown category
{
Categories = new [] { 9999 }
};
trackerCats = tcc.MapTorznabCapsToTrackers(query);
Assert.AreEqual(0, trackerCats.Count);
}
[Test]
public void TestMapTrackerCatToNewznab()
{
// MapTrackerCatToNewznab: maps Tracker cat ID => Torznab cats
var tcc = CreateTestDataset();
// TODO: this is wrong, custom cat 100001 doesn't exists (it's not defined by us)
var torznabCats = tcc.MapTrackerCatToNewznab("1").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(2000, torznabCats[0]);
Assert.AreEqual(100001, torznabCats[1]);
torznabCats = tcc.MapTrackerCatToNewznab("mov_sd").ToList();
Assert.AreEqual(1, torznabCats.Count);
Assert.AreEqual(2030, torznabCats[0]);
torznabCats = tcc.MapTrackerCatToNewznab("44").ToList(); // 44 and 45 maps to ConsoleXbox but different custom cat
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100044, torznabCats[1]);
torznabCats = tcc.MapTrackerCatToNewznab("45").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100045, torznabCats[1]);
// TODO: this is wrong, we are returning cat 109999 which doesn't exist
//torznabCats = tcc.MapTrackerCatToNewznab("9999").ToList(); // unknown cat
//Assert.AreEqual(0, torznabCats.Count);
torznabCats = tcc.MapTrackerCatToNewznab(null).ToList(); // null
Assert.AreEqual(0, torznabCats.Count);
}
[Test]
public void TestMapTrackerCatDescToNewznab()
{
// MapTrackerCatDescToNewznab: maps Tracker cat Description => Torznab cats
var tcc = CreateTestDataset();
var torznabCats = tcc.MapTrackerCatDescToNewznab("Console/Xbox_c").ToList(); // Console/Xbox_c and Console/Xbox_c2 maps to ConsoleXbox but different custom cat
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100044, torznabCats[1]);
torznabCats = tcc.MapTrackerCatDescToNewznab("Console/Xbox_c2").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100045, torznabCats[1]);
torznabCats = tcc.MapTrackerCatDescToNewznab("Console/Wii_c").ToList();
Assert.AreEqual(1, torznabCats.Count);
Assert.AreEqual(1030, torznabCats[0]);
torznabCats = tcc.MapTrackerCatDescToNewznab("9999").ToList(); // unknown cat
Assert.AreEqual(0, torznabCats.Count);
torznabCats = tcc.MapTrackerCatDescToNewznab(null).ToList(); // null
Assert.AreEqual(0, torznabCats.Count);
}
[Test]
public void TestSupportsCategories()
{
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 []{ 100044 })); // custom cat
// TODO: fix this
//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 int[]{})); // empty list
Assert.False(tcc.SupportsCategories(null)); // null
}
[Test]
public void TestConcat()
{
var lhs = new TorznabCapabilitiesCategories();
var rhs = CreateTestDataset();
lhs.Concat(rhs);
Assert.AreEqual(5, lhs.GetTorznabCategories().Count); // removed custom cats
Assert.AreEqual(0, lhs.GetTrackerCategories().Count); // removed tracker mapping
}
private static TorznabCapabilitiesCategories CreateTestDataset()
{
var tcc = new TorznabCapabilitiesCategories();
tcc.AddCategoryMapping("1", TorznabCatType.Movies);
tcc.AddCategoryMapping("mov_sd", TorznabCatType.MoviesSD);
tcc.AddCategoryMapping("33", TorznabCatType.BooksComics);
tcc.AddCategoryMapping("44", TorznabCatType.ConsoleXbox, "Console/Xbox_c");
tcc.AddCategoryMapping("con_wii", TorznabCatType.ConsoleWii, "Console/Wii_c");
tcc.AddCategoryMapping("45", TorznabCatType.ConsoleXbox, "Console/Xbox_c2");
return tcc;
}
}
}

View File

@@ -41,7 +41,8 @@ namespace Jackett.Test.Common.Models
Assert.False(torznabCaps.BookSearchTitleAvailable);
Assert.False(torznabCaps.BookSearchAuthorAvailable);
Assert.IsEmpty(torznabCaps.Categories);
Assert.IsEmpty(torznabCaps.Categories.GetTorznabCategories());
Assert.IsEmpty(torznabCaps.Categories.GetTrackerCategories());
}
[Test]
@@ -403,10 +404,8 @@ namespace Jackett.Test.Common.Models
Assert.AreEqual("q,title,author", xDoumentSearching?.Element("book-search")?.Attribute("supportedParams")?.Value);
// test categories
torznabCaps = new TorznabCapabilities
{
Categories = {TorznabCatType.MoviesSD} // child category
};
torznabCaps = new TorznabCapabilities();
torznabCaps.Categories.AddCategoryMapping("c1", TorznabCatType.MoviesSD); // child category
xDocument = torznabCaps.GetXDocument();
var xDoumentCategories = xDocument.Root?.Element("categories")?.Elements("category").ToList();
Assert.AreEqual(1, xDoumentCategories?.Count);
@@ -414,10 +413,9 @@ namespace Jackett.Test.Common.Models
Assert.AreEqual(TorznabCatType.MoviesSD.Name, xDoumentCategories?.First().Attribute("name")?.Value);
// TODO: child category is duplicated. should we add just parent and child without other subcats?
torznabCaps = new TorznabCapabilities
{
Categories = {TorznabCatType.Movies, TorznabCatType.MoviesSD} // parent and child category
};
torznabCaps = new TorznabCapabilities();
torznabCaps.Categories.AddCategoryMapping("c1", TorznabCatType.Movies); // parent and child category
torznabCaps.Categories.AddCategoryMapping("c2", TorznabCatType.MoviesSD);
xDocument = torznabCaps.GetXDocument();
xDoumentCategories = xDocument.Root?.Element("categories")?.Elements("category").ToList();
Assert.AreEqual(2, xDoumentCategories?.Count);
@@ -430,10 +428,9 @@ namespace Jackett.Test.Common.Models
Assert.AreEqual(TorznabCatType.MoviesForeign.ID.ToString(), xDoumentSubCategories?.First().Attribute("id")?.Value);
Assert.AreEqual(TorznabCatType.MoviesForeign.Name, xDoumentSubCategories?.First().Attribute("name")?.Value);
// TODO: review Torznab spec about custom cats => https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer#caps-endpoint
torznabCaps = new TorznabCapabilities{
Categories = {new TorznabCategory(100001, "CustomCat"), TorznabCatType.MoviesSD} // custom category
};
torznabCaps = new TorznabCapabilities();
torznabCaps.Categories.AddCategoryMapping("c1", new TorznabCategory(100001, "CustomCat")); // custom category
torznabCaps.Categories.AddCategoryMapping("c2", TorznabCatType.MoviesSD);
xDocument = torznabCaps.GetXDocument();
xDoumentCategories = xDocument.Root?.Element("categories")?.Elements("category").ToList();
Assert.AreEqual(2, xDoumentCategories?.Count);
@@ -455,7 +452,7 @@ namespace Jackett.Test.Common.Models
Assert.IsEmpty(res.MovieSearchParams);
Assert.IsEmpty(res.MusicSearchParams);
Assert.IsEmpty(res.BookSearchParams);
Assert.IsEmpty(res.Categories);
Assert.IsEmpty(res.Categories.GetTorznabCategories());
torznabCaps1 = new TorznabCapabilities
{
@@ -463,18 +460,20 @@ namespace Jackett.Test.Common.Models
TvSearchParams = new List<TvSearchParam> {TvSearchParam.Q},
MovieSearchParams = new List<MovieSearchParam> {MovieSearchParam.Q},
MusicSearchParams = new List<MusicSearchParam> {MusicSearchParam.Q},
BookSearchParams = new List<BookSearchParam> {BookSearchParam.Q},
Categories = new List<TorznabCategory>{TorznabCatType.Movies, new TorznabCategory(100001, "CustomCat1")}
BookSearchParams = new List<BookSearchParam> {BookSearchParam.Q}
};
torznabCaps1.Categories.AddCategoryMapping("1", TorznabCatType.Movies);
torznabCaps1.Categories.AddCategoryMapping("c1", new TorznabCategory(100001, "CustomCat1"));
torznabCaps2 = new TorznabCapabilities
{
SearchAvailable = false,
TvSearchParams = new List<TvSearchParam> {TvSearchParam.Season},
MovieSearchParams = new List<MovieSearchParam> {MovieSearchParam.ImdbId},
MusicSearchParams = new List<MusicSearchParam> {MusicSearchParam.Artist},
BookSearchParams = new List<BookSearchParam> {BookSearchParam.Title},
Categories = new List<TorznabCategory>{TorznabCatType.TVAnime, new TorznabCategory(100002, "CustomCat2")}
BookSearchParams = new List<BookSearchParam> {BookSearchParam.Title}
};
torznabCaps2.Categories.AddCategoryMapping("2", TorznabCatType.TVAnime);
torznabCaps2.Categories.AddCategoryMapping("c2", new TorznabCategory(100002, "CustomCat2"));
res = TorznabCapabilities.Concat(torznabCaps1, torznabCaps2);
Assert.False(res.SearchAvailable);
@@ -482,10 +481,7 @@ namespace Jackett.Test.Common.Models
Assert.True(res.MovieSearchParams.Count == 2);
Assert.True(res.MusicSearchParams.Count == 2);
Assert.True(res.BookSearchParams.Count == 2);
Assert.True(res.Categories.Count == 3); // only CustomCat2 is removed
Assert.True(res.Categories.GetTorznabCategories().Count == 3); // only CustomCat2 is removed
}
// TODO: test SupportsCategories
// TODO: test categories in GetXDocument
}
}

View File

@@ -30,7 +30,6 @@ namespace Jackett.Test.Torznab
{
}
public override TorznabCapabilities TorznabCaps { get; protected set; }
public override Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson) => throw new NotImplementedException();
protected override Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query) => throw new NotImplementedException();
@@ -59,162 +58,39 @@ namespace Jackett.Test.Torznab
Assert.False(TorznabCaps.BookSearchAvailable);
Assert.False(TorznabCaps.BookSearchTitleAvailable);
Assert.False(TorznabCaps.BookSearchAuthorAvailable);
Assert.AreEqual(0, TorznabCaps.Categories.Count);
Assert.AreEqual(0, TorznabCaps.Categories.GetTorznabCategories().Count);
// add "int" category (parent category)
AddCategoryMapping(1, TorznabCatType.Movies);
Assert.AreEqual(1, TorznabCaps.Categories.Count);
Assert.AreEqual(2000, TorznabCaps.Categories[0].ID);
// add "string" category (child category)
// simple category tests
AddCategoryMapping("1", TorznabCatType.Movies);
AddCategoryMapping("mov_sd", TorznabCatType.MoviesSD);
Assert.AreEqual(2, TorznabCaps.Categories.Count);
Assert.AreEqual(2030, TorznabCaps.Categories[1].ID);
// add subcategory of books (child category)
AddCategoryMapping(33, TorznabCatType.BooksComics);
Assert.AreEqual(3, TorznabCaps.Categories.Count);
Assert.AreEqual(7020, TorznabCaps.Categories[2].ID);
// add int category with description => custom category. it's converted into 2 different categories
AddCategoryMapping(44, TorznabCatType.ConsoleXbox, "Console/Xbox_c");
Assert.AreEqual(5, TorznabCaps.Categories.Count);
Assert.AreEqual(1040, TorznabCaps.Categories[3].ID);
Assert.AreEqual(100044, TorznabCaps.Categories[4].ID);
// TODO: we should add a way to add custom categories for string categories
// https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer#caps-endpoint
// add string category with description. it's converted into 1 category
AddCategoryMapping("33", TorznabCatType.BooksComics);
AddCategoryMapping("44", TorznabCatType.ConsoleXbox, "Console/Xbox_c");
AddCategoryMapping("con_wii", TorznabCatType.ConsoleWii, "Console/Wii_c");
Assert.AreEqual(6, TorznabCaps.Categories.Count);
Assert.AreEqual(1030, TorznabCaps.Categories[5].ID);
AddCategoryMapping("45", TorznabCatType.ConsoleXbox, "Console/Xbox_c2");
// add another int category with description that maps to ConsoleXbox (there are 2 tracker cats => 1 torznab cat)
AddCategoryMapping(45, TorznabCatType.ConsoleXbox, "Console/Xbox_c2");
Assert.AreEqual(7, TorznabCaps.Categories.Count);
Assert.AreEqual(100045, TorznabCaps.Categories[6].ID); // 1040 is duplicated and it is not added
// TODO: test AddMultiCategoryMapping
// TODO: add duplicates: different trackerCat but same newznabCat
// TODO: duplicates are not working well because we keep 2 internal lists with categories. One is deduplicated
// and the other doesn't
// add duplicate
//AddCategoryMapping(1, TorznabCatType.Movies, "Movies");
//Assert.AreEqual(6, TorznabCaps.Categories.Count);
// test MapTorznabCapsToTrackers: maps TorznazQuery cats => Tracker cats
var query = new TorznabQuery(); // no cats
var query = new TorznabQuery // int category with subcategories (parent cat)
{
Categories = new [] { TorznabCatType.Movies.ID }
};
var trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(0, trackerCats.Count);
query = new TorznabQuery // a lot of cats (mixed types)
{
Categories = TorznabCaps.Categories.Select(c => c.ID).ToArray()
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(6, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]);
Assert.AreEqual("mov_sd", trackerCats[1]);
Assert.AreEqual("33", trackerCats[2]);
Assert.AreEqual("44", trackerCats[3]);
Assert.AreEqual("45", trackerCats[4]);
Assert.AreEqual("con_wii", trackerCats[5]);
query = new TorznabQuery // int category with subcategories (parent cat)
{
Categories = new [] { 2000 } // Movies
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
Assert.AreEqual("mov_sd", trackerCats[1]); // Movies SD
query = new TorznabQuery // string child category
{
Categories = new [] { 2030 } // Movies SD
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(1, trackerCats.Count);
Assert.AreEqual("mov_sd", trackerCats[0]); // Movies SD
trackerCats = MapTorznabCapsToTrackers(query, true); // get parent
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
Assert.AreEqual("mov_sd", trackerCats[1]); // Movies SD
query = new TorznabQuery // duplicate category (1 toznab cat => 2 indexer cats)
{
Categories = new [] { 1040 } // ConsoleXbox
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(2, trackerCats.Count);
Assert.AreEqual("44", trackerCats[0]);
Assert.AreEqual("45", trackerCats[1]);
query = new TorznabQuery // custom cat
{
Categories = new [] { 100001 } // Movies
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(1, trackerCats.Count);
Assert.AreEqual("1", trackerCats[0]); // Movies
query = new TorznabQuery // unknown category
{
Categories = new [] { 9999 }
};
trackerCats = MapTorznabCapsToTrackers(query);
Assert.AreEqual(0, trackerCats.Count);
// TODO: this is wrong, custom cat 100001 doesn't exists (it's not defined by us)
// test MapTrackerCatToNewznab: maps Tracker cat ID => Torznab cats
var torznabCats = MapTrackerCatToNewznab("1").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(2000, torznabCats[0]);
Assert.AreEqual(100001, torznabCats[1]);
torznabCats = MapTrackerCatToNewznab("mov_sd").ToList();
Assert.AreEqual(1, torznabCats.Count);
Assert.AreEqual(2030, torznabCats[0]);
torznabCats = MapTrackerCatToNewznab("44").ToList(); // 44 and 45 maps to ConsoleXbox but different custom cat
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100044, torznabCats[1]);
torznabCats = MapTrackerCatToNewznab("45").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100045, torznabCats[1]);
// TODO: this is wrong, we are returning cat 109999 which doesn't exist
//torznabCats = MapTrackerCatToNewznab("9999").ToList(); // unknown cat
//Assert.AreEqual(0, torznabCats.Count);
torznabCats = MapTrackerCatToNewznab(null).ToList(); // null
Assert.AreEqual(0, torznabCats.Count);
// TODO: I think this method should be removed because description can be non-unique
// test MapTrackerCatDescToNewznab: maps Tracker cat Description => Torznab cats
torznabCats = MapTrackerCatDescToNewznab("Console/Xbox_c").ToList(); // Console/Xbox_c and Console/Xbox_c2 maps to ConsoleXbox but different custom cat
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100044, torznabCats[1]);
torznabCats = MapTrackerCatDescToNewznab("Console/Xbox_c2").ToList();
Assert.AreEqual(2, torznabCats.Count);
Assert.AreEqual(1040, torznabCats[0]);
Assert.AreEqual(100045, torznabCats[1]);
torznabCats = MapTrackerCatDescToNewznab("Console/Wii_c").ToList();
Assert.AreEqual(1, torznabCats.Count);
Assert.AreEqual(1030, torznabCats[0]);
torznabCats = MapTrackerCatDescToNewznab("9999").ToList(); // unknown cat
Assert.AreEqual(0, torznabCats.Count);
torznabCats = MapTrackerCatDescToNewznab(null).ToList(); // null
Assert.AreEqual(0, torznabCats.Count);
// TODO: move these methods to TorznabCaps or TorznabQuery classess
// TODO: test AddMultiCategoryMapping
// TODO: add duplicates: different trackerCat but same newznabCat
// TODO: duplicates are not working well because we keep 2 internal lists with categories. One is deduplicated
// and the other doesn't
// test Jackett UI categories (internal JSON)
var dto = new Jackett.Common.Models.DTO.Indexer(this);
@@ -281,7 +157,7 @@ namespace Jackett.Test.Torznab
Assert.False(indexer.TorznabCaps.BookSearchAvailable);
Assert.False(indexer.TorznabCaps.BookSearchTitleAvailable);
Assert.False(indexer.TorznabCaps.BookSearchAuthorAvailable);
Assert.AreEqual(0, indexer.TorznabCaps.Categories.Count);
Assert.AreEqual(0, indexer.TorznabCaps.Categories.GetTorznabCategories().Count);
definition = new IndexerDefinition // test categories (same as in C# indexer)
{
@@ -325,16 +201,16 @@ namespace Jackett.Test.Torznab
indexer = new CardigannIndexer(null, null, null, null, definition);
// TODO: test duplicates
Assert.AreEqual(7, indexer.TorznabCaps.Categories.Count);
Assert.AreEqual(2000, indexer.TorznabCaps.Categories[0].ID);
Assert.AreEqual(2030, indexer.TorznabCaps.Categories[1].ID);
Assert.AreEqual(7020, indexer.TorznabCaps.Categories[2].ID);
Assert.AreEqual(1040, indexer.TorznabCaps.Categories[3].ID);
Assert.AreEqual(100044, indexer.TorznabCaps.Categories[4].ID);
Assert.AreEqual(1030, indexer.TorznabCaps.Categories[5].ID);
Assert.AreEqual(100045, indexer.TorznabCaps.Categories[6].ID);
var cats = indexer.TorznabCaps.Categories.GetTorznabCategories();
Assert.AreEqual(7, cats.Count);
Assert.AreEqual(2000, cats[0].ID);
Assert.AreEqual(2030, cats[1].ID);
Assert.AreEqual(7020, cats[2].ID);
Assert.AreEqual(1040, cats[3].ID);
Assert.AreEqual(100044, cats[4].ID);
Assert.AreEqual(1030, cats[5].ID);
Assert.AreEqual(100045, cats[6].ID);
// TODO: we are not validating modes or params in each mode. ie: search is not required/supported and it's used
definition = new IndexerDefinition // test search modes
{
Links = new List<string>{ "https://example.com" },