public void InitCardigannIndexers(string path) { logger.Info("Loading Cardigann definitions from: " + path); try { if (!Directory.Exists(path)) { return; } DirectoryInfo d = new DirectoryInfo(path); foreach (var file in d.GetFiles("*.yml")) { logger.Info("Loading Cardigann definition " + file.FullName); string DefinitionString = File.ReadAllText(file.FullName); CardigannIndexer idx = new CardigannIndexer(this, container.Resolve <IWebClient>(), logger, container.Resolve <IProtectionService>(), DefinitionString); if (indexers.ContainsKey(idx.ID)) { logger.Debug(string.Format("Ignoring definition ID={0}, file={1}: Indexer already exists", idx.ID, file.FullName)); } else { indexers.Add(idx.ID, idx); LoadIndexerConfig(idx); } } } catch (Exception ex) { logger.Error(ex, "Error while loading Cardigann definitions: " + ex.Message); } }
private void InitCardigannIndexers(IEnumerable <FileInfo> files) { var deserializer = new DeserializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) // .IgnoreUnmatchedProperties() .Build(); try { var definitions = files.Select(file => { logger.Debug("Loading Cardigann definition " + file.FullName); try { var DefinitionString = File.ReadAllText(file.FullName); var definition = deserializer.Deserialize <IndexerDefinition>(DefinitionString); return(definition); } catch (Exception ex) { logger.Error(ex, "Error while parsing Cardigann definition " + file.FullName + ": " + ex.Message); return(null); } }).Where(definition => definition != null); var cardigannIndexers = definitions.Select(definition => { try { // create own webClient instance for each indexer (seperate cookies stores, etc.) var indexerWebClientInstance = (WebClient)Activator.CreateInstance(webClient.GetType(), processService, logger, globalConfigService, serverConfig); IIndexer indexer = new CardigannIndexer(configService, indexerWebClientInstance, logger, protectionService, definition); configService.Load(indexer); return(indexer); } catch (Exception ex) { logger.Error(ex, "Error while creating Cardigann instance from Definition: " + ex.Message); return(null); } }).Where(cardigannIndexer => cardigannIndexer != null).ToList(); // Explicit conversion to list to avoid repeated resource loading foreach (var indexer in cardigannIndexers) { if (indexers.ContainsKey(indexer.Id)) { logger.Debug(string.Format("Ignoring definition ID={0}: Indexer already exists", indexer.Id)); continue; } indexers.Add(indexer.Id, indexer); } logger.Info("Cardigann definitions loaded (" + indexers.Count + "): " + string.Join(", ", indexers.Keys)); } catch (Exception ex) { logger.Error(ex, "Error while loading Cardigann definitions: " + ex.Message); } }
public async Task TestCardigannJsonAsync() { _webClient.RegisterRequestCallback("https://jsondefinition1.com/api/torrents/filter?api_token=&name=1080p&sortField=created_at&sortDirection=desc&perPage=100&page=1", "json-response1.json"); var definition = LoadTestDefinition("json-definition1.yml"); var indexer = new CardigannIndexer(null, _webClient, _logger, null, _cacheService, definition); var query = new TorznabQuery { QueryType = "search", SearchTerm = "1080p", }; var result = await indexer.ResultsForQuery(query, false); Assert.AreEqual(false, result.IsFromCache); var releases = result.Releases.ToList(); Assert.AreEqual(78, releases.Count); var firstRelease = releases.First(); Assert.AreEqual(2, firstRelease.Category.Count); Assert.AreEqual(2000, firstRelease.Category.First()); Assert.AreEqual(100001, firstRelease.Category.Last()); Assert.AreEqual("The Eyes of Tammy Faye (2021) BDRip 1080p AVC ES DD+ 5.1 EN DTSSS 5.1 Subs] HDO", firstRelease.Title); Assert.AreEqual("https://jsondefinition1.com/torrents/24804", firstRelease.Details.ToString()); Assert.AreEqual("https://jsondefinition1.com/torrent/download/24804.01c887e14d0845f195bc12b31ea27d38", firstRelease.Link.ToString()); Assert.AreEqual("https://jsondefinition1.com/torrent/download/24804.01c887e14d0845f195bc12b31ea27d38", firstRelease.Guid.ToString()); Assert.AreEqual(null, firstRelease.MagnetUri); Assert.AreEqual(null, firstRelease.InfoHash); Assert.AreEqual("https://image.tmdb.org/t/p/w92/iBjkm6oxTPrvNkzr63cmnrpsQPR.jpg", firstRelease.Poster.ToString()); Assert.AreEqual(2021, firstRelease.PublishDate.Year); Assert.AreEqual(17964744704, firstRelease.Size); Assert.AreEqual(27, firstRelease.Seeders); Assert.AreEqual(30, firstRelease.Peers); Assert.AreEqual(1, firstRelease.Files); Assert.AreEqual(29, firstRelease.Grabs); Assert.AreEqual(1, firstRelease.DownloadVolumeFactor); Assert.AreEqual(1, firstRelease.UploadVolumeFactor); Assert.AreEqual(null, firstRelease.MinimumRatio); Assert.AreEqual(345600, firstRelease.MinimumSeedTime); Assert.AreEqual(451.73625183105469, firstRelease.Gain); Assert.AreEqual(9115530, firstRelease.Imdb); Assert.AreEqual(null, firstRelease.RageID); Assert.AreEqual(601470, firstRelease.TMDb); Assert.AreEqual(0, firstRelease.TVDBId); }
private void InitCardigannIndexers(IEnumerable <string> path) { logger.Info("Loading Cardigann definitions from: " + string.Join(", ", path)); var deserializer = new DeserializerBuilder() .WithNamingConvention(new CamelCaseNamingConvention()) .IgnoreUnmatchedProperties() .Build(); try { var directoryInfos = path.Select(p => new DirectoryInfo(p)); var existingDirectories = directoryInfos.Where(d => d.Exists); var files = existingDirectories.SelectMany(d => d.GetFiles("*.yml")); var definitions = files.Select(file => { logger.Info("Loading Cardigann definition " + file.FullName); string DefinitionString = File.ReadAllText(file.FullName); var definition = deserializer.Deserialize <IndexerDefinition>(DefinitionString); return(definition); }); var cardigannIndexers = definitions.Select(definition => { // create own webClient instance for each indexer (seperate cookies stores, etc.) var indexerWebClientInstance = (IWebClient)Activator.CreateInstance(webClient.GetType(), processService, logger, globalConfigService); IIndexer indexer = new CardigannIndexer(configService, indexerWebClientInstance, logger, protectionService, definition); configService.Load(indexer); return(indexer); }).ToList(); // Explicit conversion to list to avoid repeated resource loading foreach (var indexer in cardigannIndexers) { if (indexers.ContainsKey(indexer.ID)) { logger.Debug(string.Format("Ignoring definition ID={0}: Indexer already exists", indexer.ID)); continue; } indexers.Add(indexer.ID, indexer); } } catch (Exception ex) { logger.Error(ex, "Error while loading Cardigann definitions: " + ex.Message); } }
public void DeleteIndexer(string name) { var indexer = GetIndexer(name); var configPath = GetIndexerConfigFilePath(indexer); File.Delete(configPath); if (indexer.GetType() == typeof(CardigannIndexer)) { indexers[name] = new CardigannIndexer(this, container.Resolve <IWebClient>(), logger, container.Resolve <IProtectionService>(), ((CardigannIndexer)indexer).DefinitionString); } else { indexers[name] = container.ResolveNamed <IIndexer>(indexer.ID); } }
public async Task TestCardigannHtmlAsync() { _webClient.RegisterRequestCallback("https://www.testdefinition1.cc/search?query=ubuntu&sort=created", "html-response1.html"); var definition = LoadTestDefinition("html-definition1.yml"); var indexer = new CardigannIndexer(null, _webClient, _logger, null, _cacheService, definition); var query = new TorznabQuery { QueryType = "search", SearchTerm = "ubuntu", }; var result = await indexer.ResultsForQuery(query, false); Assert.AreEqual(false, result.IsFromCache); var releases = result.Releases.ToList(); Assert.AreEqual(25, releases.Count); var firstRelease = releases.First(); Assert.AreEqual(1, firstRelease.Category.Count); Assert.AreEqual(8000, firstRelease.Category.First()); Assert.AreEqual("ubuntu-19.04-desktop-amd64.iso", firstRelease.Title); Assert.AreEqual("https://www.testdefinition1.cc/torrent/d540fc48eb12f2833163eed6421d449dd8f1ce1f", firstRelease.Details.ToString()); Assert.AreEqual("http://itorrents.org/torrent/d540fc48eb12f2833163eed6421d449dd8f1ce1f.torrent", firstRelease.Link.ToString()); Assert.AreEqual("http://itorrents.org/torrent/d540fc48eb12f2833163eed6421d449dd8f1ce1f.torrent", firstRelease.Guid.ToString()); Assert.AreEqual("magnet:?xt=urn:btih:d540fc48eb12f2833163eed6421d449dd8f1ce1f&dn=ubuntu-19.04-desktop-amd64.iso", firstRelease.MagnetUri.ToString().Split(new[] { "&tr" }, StringSplitOptions.None).First()); Assert.AreEqual("d540fc48eb12f2833163eed6421d449dd8f1ce1f", firstRelease.InfoHash); Assert.AreEqual(2019, firstRelease.PublishDate.Year); Assert.AreEqual(2097152000, firstRelease.Size); Assert.AreEqual(12, firstRelease.Seeders); Assert.AreEqual(13, firstRelease.Peers); Assert.AreEqual(1, firstRelease.DownloadVolumeFactor); Assert.AreEqual(2, firstRelease.UploadVolumeFactor); Assert.AreEqual(23.4375, firstRelease.Gain); }
public void TestCardigannTorznabCategories() { var definition = new IndexerDefinition // minimun indexer definition { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> >() }, Search = new searchBlock() }; var indexer = new CardigannIndexer(null, null, null, null, definition); // by default all indexers have 0 categories Assert.AreEqual(0, indexer.TorznabCaps.Categories.Count); // TODO: make sure TVSearchAvailable is false by default Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.True(indexer.TorznabCaps.TVSearchAvailable); Assert.False(indexer.TorznabCaps.MovieSearchAvailable); Assert.False(indexer.TorznabCaps.MusicSearchAvailable); Assert.False(indexer.TorznabCaps.BookSearchAvailable); Assert.False(indexer.TorznabCaps.SupportsImdbMovieSearch); Assert.False(indexer.TorznabCaps.SupportsImdbTVSearch); Assert.False(indexer.TorznabCaps.SupportsTvdbSearch); Assert.False(indexer.TorznabCaps.SupportsTmdbMovieSearch); Assert.False(indexer.TorznabCaps.SupportsTVRageSearch); Assert.AreEqual(0, indexer.TorznabCaps.SupportedMusicSearchParamsList.Count); definition = new IndexerDefinition // test categories (same as in C# indexer) { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> >(), Categories = new Dictionary <string, string> { { "1", TorznabCatType.Movies.Name }, // integer cat (has children) { "mov_sd", TorznabCatType.MoviesSD.Name }, // string cat (child cat) { "33", TorznabCatType.BooksComics.Name } // integer cat (child cat) }, Categorymappings = new List <CategorymappingBlock> { new CategorymappingBlock // integer cat with description (child cat) => generates custom cat 100044 { id = "44", cat = TorznabCatType.ConsoleXbox.Name, desc = "Console/Xbox_c" }, new CategorymappingBlock // string cat with description (child cat) { id = "con_wii", cat = TorznabCatType.ConsoleWii.Name, desc = "Console/Wii_c" }, new CategorymappingBlock // duplicate category (2 indexer cats => 1 toznab cat) { id = "45", cat = TorznabCatType.ConsoleXbox.Name, desc = "Console/Xbox_c2" }, } }, Search = new searchBlock() }; indexer = new CardigannIndexer(null, null, null, null, definition); // TODO: movies category enables MovieSearchAvailable but other categories like tv or books don't // TODO: test duplicates Assert.True(indexer.TorznabCaps.MovieSearchAvailable); 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(8020, 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); // 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" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> > { { "search", new List <string> { "q" } }, { "tv-search", new List <string> { "q", "season", "ep", "rid", "tvdbid", "imdbid" } }, { "movie-search", new List <string> { "q", "imdbid", "tmdbid" } }, { "music-search", new List <string> { "q", "album", "artist", "label", "year" } }, { "book-search", new List <string> { "q", "author", "title" } } }, Categories = new Dictionary <string, string>() }, Search = new searchBlock() }; indexer = new CardigannIndexer(null, null, null, null, definition); Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.True(indexer.TorznabCaps.TVSearchAvailable); // TODO: movie search can't be enabled with the mode, only with movies categories Assert.False(indexer.TorznabCaps.MovieSearchAvailable); Assert.True(indexer.TorznabCaps.MusicSearchAvailable); Assert.True(indexer.TorznabCaps.BookSearchAvailable); Assert.True(indexer.TorznabCaps.SupportsImdbMovieSearch); // TODO: this is disabled in Jackett.Common\Indexers\CardigannIndexer.cs : 114 Assert.False(indexer.TorznabCaps.SupportsImdbTVSearch); Assert.True(indexer.TorznabCaps.SupportsTvdbSearch); Assert.True(indexer.TorznabCaps.SupportsTmdbMovieSearch); // TODO: TVRage search is not implemented in Cardigann Assert.False(indexer.TorznabCaps.SupportsTVRageSearch); Assert.AreEqual(5, indexer.TorznabCaps.SupportedMusicSearchParamsList.Count); // test Jackett UI categories (internal JSON) => same code path as C# indexer // test Torznab caps (XML) => same code path as C# indexer }
private void InitCardigannIndexers(IEnumerable <string> path) { logger.Info("Loading Cardigann indexers from: " + string.Join(", ", path)); var deserializer = new DeserializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) //.IgnoreUnmatchedProperties() .Build(); try { var directoryInfos = path.Select(p => new DirectoryInfo(p)); var existingDirectories = directoryInfos.Where(d => d.Exists); var files = existingDirectories.SelectMany(d => d.GetFiles("*.yml")); var definitions = files.Select(file => { logger.Debug("Loading Cardigann definition " + file.FullName); try { var definitionString = File.ReadAllText(file.FullName); var definition = deserializer.Deserialize <IndexerDefinition>(definitionString); return(definition); } catch (Exception e) { logger.Error($"Error while parsing Cardigann definition {file.FullName}\n{e}"); return(null); } }).Where(definition => definition != null); var cardigannIndexers = definitions.Select(definition => { try { // create own webClient instance for each indexer (seperate cookies stores, etc.) var indexerWebClientInstance = (WebClient)Activator.CreateInstance(webClient.GetType(), processService, logger, globalConfigService, serverConfig); IIndexer indexer = new CardigannIndexer(configService, indexerWebClientInstance, logger, protectionService, definition); configService.Load(indexer); return(indexer); } catch (Exception e) { logger.Error($"Error while creating Cardigann instance from definition ID={definition.Id}: {e}"); return(null); } }).Where(cardigannIndexer => cardigannIndexer != null).ToList(); // Explicit conversion to list to avoid repeated resource loading var cardigannCounter = 0; var cardiganIds = new List <string>(); foreach (var indexer in cardigannIndexers) { if (indexers.ContainsKey(indexer.Id)) { logger.Warn($"Ignoring definition ID={indexer.Id}: Indexer already exists"); continue; } indexers.Add(indexer.Id, indexer); cardigannCounter++; cardiganIds.Add(indexer.Id); } logger.Info($"Loaded {cardigannCounter} Cardigann indexers: {string.Join(", ", cardiganIds)}"); } catch (Exception e) { logger.Error($"Error while loading Cardigann definitions: {e}"); } logger.Info($"Loaded {indexers.Count} indexers in total"); }
public void TestCardigannTorznabCategories() { var definition = new IndexerDefinition // minimun indexer definition { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> >() }, Search = new searchBlock() }; var indexer = new CardigannIndexer(null, null, null, null, definition); Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.IsEmpty(indexer.TorznabCaps.TvSearchParams); Assert.False(indexer.TorznabCaps.TvSearchAvailable); Assert.False(indexer.TorznabCaps.TvSearchSeasonAvailable); Assert.False(indexer.TorznabCaps.TvSearchEpAvailable); Assert.False(indexer.TorznabCaps.TvSearchImdbAvailable); Assert.False(indexer.TorznabCaps.TvSearchTvdbAvailable); Assert.False(indexer.TorznabCaps.TvSearchTvRageAvailable); Assert.IsEmpty(indexer.TorznabCaps.MovieSearchParams); Assert.False(indexer.TorznabCaps.MovieSearchAvailable); Assert.False(indexer.TorznabCaps.MovieSearchImdbAvailable); Assert.False(indexer.TorznabCaps.MovieSearchTmdbAvailable); Assert.IsEmpty(indexer.TorznabCaps.MusicSearchParams); Assert.False(indexer.TorznabCaps.MusicSearchAvailable); Assert.False(indexer.TorznabCaps.MusicSearchAlbumAvailable); Assert.False(indexer.TorznabCaps.MusicSearchArtistAvailable); Assert.False(indexer.TorznabCaps.MusicSearchLabelAvailable); Assert.False(indexer.TorznabCaps.MusicSearchYearAvailable); Assert.False(indexer.TorznabCaps.BookSearchAvailable); Assert.AreEqual(0, indexer.TorznabCaps.Categories.Count); definition = new IndexerDefinition // test categories (same as in C# indexer) { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> >(), Categories = new Dictionary <string, string> { { "1", TorznabCatType.Movies.Name }, // integer cat (has children) { "mov_sd", TorznabCatType.MoviesSD.Name }, // string cat (child cat) { "33", TorznabCatType.BooksComics.Name } // integer cat (child cat) }, Categorymappings = new List <CategorymappingBlock> { new CategorymappingBlock // integer cat with description (child cat) => generates custom cat 100044 { id = "44", cat = TorznabCatType.ConsoleXbox.Name, desc = "Console/Xbox_c" }, new CategorymappingBlock // string cat with description (child cat) { id = "con_wii", cat = TorznabCatType.ConsoleWii.Name, desc = "Console/Wii_c" }, new CategorymappingBlock // duplicate category (2 indexer cats => 1 toznab cat) { id = "45", cat = TorznabCatType.ConsoleXbox.Name, desc = "Console/Xbox_c2" }, } }, Search = new searchBlock() }; 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(8020, 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); // 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" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> > { { "search", new List <string> { "q" } }, { "tv-search", new List <string> { "q", "season", "ep", "imdbid", "tvdbid", "rid" } }, { "movie-search", new List <string> { "q", "imdbid", "tmdbid" } }, { "music-search", new List <string> { "q", "album", "artist", "label", "year" } }, { "book-search", new List <string> { "q", "author", "title" } } }, Categories = new Dictionary <string, string>() }, Search = new searchBlock() }; indexer = new CardigannIndexer(null, null, null, null, definition); Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.True(indexer.TorznabCaps.TvSearchAvailable); // TODO: SupportsImdbTVSearch is disabled in Jackett.Common.Models.TorznabCapabilities.TvSearchImdbAvailable Assert.False(indexer.TorznabCaps.TvSearchImdbAvailable); Assert.True(indexer.TorznabCaps.TvSearchTvdbAvailable); Assert.True(indexer.TorznabCaps.TvSearchTvRageAvailable); Assert.AreEqual( new List <MovieSearchParam> { MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId }, indexer.TorznabCaps.MovieSearchParams ); Assert.True(indexer.TorznabCaps.MovieSearchAvailable); Assert.True(indexer.TorznabCaps.MovieSearchImdbAvailable); Assert.True(indexer.TorznabCaps.MovieSearchTmdbAvailable); // TODO: improve this assert Assert.AreEqual(5, indexer.TorznabCaps.MusicSearchParams.Count); Assert.True(indexer.TorznabCaps.MusicSearchAvailable); Assert.True(indexer.TorznabCaps.BookSearchAvailable); // test Jackett UI categories (internal JSON) => same code path as C# indexer // test Torznab caps (XML) => same code path as C# indexer }
public void TestCardigannTorznabCategories() { var definition = new IndexerDefinition // minimun indexer definition { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> > { { "search", new List <string> { "q" } } } }, Search = new searchBlock() }; var indexer = new CardigannIndexer(null, null, null, null, definition); Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.IsEmpty(indexer.TorznabCaps.TvSearchParams); Assert.False(indexer.TorznabCaps.TvSearchAvailable); Assert.False(indexer.TorznabCaps.TvSearchSeasonAvailable); Assert.False(indexer.TorznabCaps.TvSearchEpAvailable); Assert.False(indexer.TorznabCaps.TvSearchImdbAvailable); Assert.False(indexer.TorznabCaps.TvSearchTvdbAvailable); Assert.False(indexer.TorznabCaps.TvSearchTvRageAvailable); Assert.IsEmpty(indexer.TorznabCaps.MovieSearchParams); Assert.False(indexer.TorznabCaps.MovieSearchAvailable); Assert.False(indexer.TorznabCaps.MovieSearchImdbAvailable); Assert.False(indexer.TorznabCaps.MovieSearchTmdbAvailable); Assert.IsEmpty(indexer.TorznabCaps.MusicSearchParams); Assert.False(indexer.TorznabCaps.MusicSearchAvailable); Assert.False(indexer.TorznabCaps.MusicSearchAlbumAvailable); Assert.False(indexer.TorznabCaps.MusicSearchArtistAvailable); Assert.False(indexer.TorznabCaps.MusicSearchLabelAvailable); Assert.False(indexer.TorznabCaps.MusicSearchYearAvailable); Assert.IsEmpty(indexer.TorznabCaps.BookSearchParams); Assert.False(indexer.TorznabCaps.BookSearchAvailable); Assert.False(indexer.TorznabCaps.BookSearchTitleAvailable); Assert.False(indexer.TorznabCaps.BookSearchAuthorAvailable); Assert.AreEqual(0, indexer.TorznabCaps.Categories.GetTorznabCategoryTree().Count); definition = new IndexerDefinition // test categories (same as in C# indexer) { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> > { { "search", new List <string> { "q" } } }, Categories = new Dictionary <string, string> { { "1", TorznabCatType.Movies.Name }, // integer cat (has children) { "mov_sd", TorznabCatType.MoviesSD.Name }, // string cat (child cat) { "33", TorznabCatType.BooksComics.Name } // integer cat (child cat) }, Categorymappings = new List <CategorymappingBlock> { new CategorymappingBlock // integer cat with description (child cat) => generates custom cat 100044 { id = "44", cat = TorznabCatType.ConsoleXBox.Name, desc = "Console/Xbox_c" }, new CategorymappingBlock // string cat with description (child cat) { id = "con_wii", cat = TorznabCatType.ConsoleWii.Name, desc = "Console/Wii_c" }, new CategorymappingBlock // duplicate category (2 indexer cats => 1 toznab cat) { id = "45", cat = TorznabCatType.ConsoleXBox.Name, desc = "Console/Xbox_c2" }, } }, Search = new searchBlock() }; indexer = new CardigannIndexer(null, null, null, null, definition); // test categories var expected = new List <TorznabCategory> { TorznabCatType.Movies.CopyWithoutSubCategories(), TorznabCatType.Books.CopyWithoutSubCategories(), TorznabCatType.Console.CopyWithoutSubCategories(), new TorznabCategory(100044, "Console/Xbox_c"), new TorznabCategory(137107, "Console/Wii_c"), new TorznabCategory(100045, "Console/Xbox_c2") }; expected[0].SubCategories.Add(TorznabCatType.MoviesSD.CopyWithoutSubCategories()); expected[1].SubCategories.Add(TorznabCatType.BooksComics.CopyWithoutSubCategories()); expected[2].SubCategories.Add(TorznabCatType.ConsoleXBox.CopyWithoutSubCategories()); expected[2].SubCategories.Add(TorznabCatType.ConsoleWii.CopyWithoutSubCategories()); TestCategories.CompareCategoryTrees(expected, indexer.TorznabCaps.Categories.GetTorznabCategoryTree()); definition = new IndexerDefinition // test search modes { Links = new List <string> { "https://example.com" }, Caps = new capabilitiesBlock { Modes = new Dictionary <string, List <string> > { { "search", new List <string> { "q" } }, { "tv-search", new List <string> { "q", "season", "ep", "imdbid", "tvdbid", "rid" } }, { "movie-search", new List <string> { "q", "imdbid", "tmdbid" } }, { "music-search", new List <string> { "q", "album", "artist", "label", "year" } }, { "book-search", new List <string> { "q", "title", "author" } } }, Categories = new Dictionary <string, string>() }, Search = new searchBlock() }; indexer = new CardigannIndexer(null, null, null, null, definition); Assert.True(indexer.TorznabCaps.SearchAvailable); Assert.AreEqual( new List <TvSearchParam> { TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId, TvSearchParam.RId }, indexer.TorznabCaps.TvSearchParams ); Assert.True(indexer.TorznabCaps.TvSearchAvailable); Assert.True(indexer.TorznabCaps.TvSearchSeasonAvailable); Assert.True(indexer.TorznabCaps.TvSearchEpAvailable); // TODO: SupportsImdbTVSearch is disabled in Jackett.Common.Models.TorznabCapabilities.TvSearchImdbAvailable Assert.False(indexer.TorznabCaps.TvSearchImdbAvailable); Assert.True(indexer.TorznabCaps.TvSearchTvdbAvailable); Assert.True(indexer.TorznabCaps.TvSearchTvRageAvailable); Assert.AreEqual( new List <MovieSearchParam> { MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId }, indexer.TorznabCaps.MovieSearchParams ); Assert.True(indexer.TorznabCaps.MovieSearchAvailable); Assert.True(indexer.TorznabCaps.MovieSearchImdbAvailable); Assert.True(indexer.TorznabCaps.MovieSearchTmdbAvailable); Assert.AreEqual( new List <MusicSearchParam> { MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year }, indexer.TorznabCaps.MusicSearchParams ); Assert.True(indexer.TorznabCaps.MusicSearchAvailable); Assert.True(indexer.TorznabCaps.MusicSearchAlbumAvailable); Assert.True(indexer.TorznabCaps.MusicSearchArtistAvailable); Assert.True(indexer.TorznabCaps.MusicSearchLabelAvailable); Assert.True(indexer.TorznabCaps.MusicSearchYearAvailable); Assert.AreEqual( new List <BookSearchParam> { BookSearchParam.Q, BookSearchParam.Title, BookSearchParam.Author }, indexer.TorznabCaps.BookSearchParams ); Assert.True(indexer.TorznabCaps.BookSearchAvailable); Assert.True(indexer.TorznabCaps.BookSearchTitleAvailable); Assert.True(indexer.TorznabCaps.BookSearchAuthorAvailable); // test Jackett UI categories (internal JSON) => same code path as C# indexer // test Torznab caps (XML) => same code path as C# indexer }