From 8e72c7153db6a4622e871aebc1d73a8690b7605a Mon Sep 17 00:00:00 2001 From: Qstick Date: Thu, 19 Nov 2020 16:32:15 -0500 Subject: [PATCH] Lots of workings to search processing, UI Categories --- .../Indexer/Index/Table/CapabilitiesLabel.js | 50 ++------ .../Indexer/Index/Table/MovieIndexHeader.css | 2 +- .../src/Indexer/Index/Table/MovieIndexRow.css | 2 +- frontend/src/Search/Table/CategoryLabel.js | 23 ++++ .../src/Search/Table/SearchIndexHeader.css | 1 + frontend/src/Search/Table/SearchIndexRow.css | 1 + frontend/src/Search/Table/SearchIndexRow.js | 16 +++ frontend/src/Store/Actions/releaseActions.js | 6 + .../HDBitsRequestGeneratorFixture.cs | 19 --- .../NewznabRequestGeneratorFixture.cs | 24 ---- .../Applications/Lidarr/Lidarr.cs | 2 +- .../Applications/Radarr/Radarr.cs | 2 +- .../Applications/Readarr/Readarr.cs | 2 +- .../Applications/Sonarr/Sonarr.cs | 2 +- .../Definitions/BasicSearchCriteria.cs | 12 ++ .../Definitions/BookSearchCriteria.cs | 12 ++ .../Definitions/MovieSearchCriteria.cs | 8 +- .../Definitions/MusicSearchCriteria.cs | 15 +++ .../Definitions/SearchCriteriaBase.cs | 3 + .../Definitions/TvSearchCriteria.cs | 20 +++ .../IndexerSearch/NewznabRequest.cs | 18 +-- .../IndexerSearch/NewznabResults.cs | 6 +- .../IndexerSearch/NzbSearchService.cs | 65 +++++++++- .../AwesomeHD/AwesomeHDRequestGenerator.cs | 20 +++ .../Definitions/Cardigann/CardigannBase.cs | 116 +++++++++++++----- .../Definitions/Cardigann/CardigannParser.cs | 5 +- .../Cardigann/CardigannReleaseInfo.cs | 2 +- .../Cardigann/CardigannRequestGenerator.cs | 115 +++++++++++++---- .../FileList/FileListRequestGenerator.cs | 20 +++ .../HDBits/HDBitsRequestGenerator.cs | 27 ++-- .../IPTorrents/IPTorrentsRequestGenerator.cs | 20 +++ .../Newznab/NewznabRequestGenerator.cs | 47 ++++--- .../Definitions/Nyaa/NyaaRequestGenerator.cs | 29 +++-- .../PassThePopcornRequestGenerator.cs | 29 +++-- .../Rarbg/RarbgRequestGenerator.cs | 20 +++ .../TorrentPotatoRequestGenerator.cs | 20 +++ .../TorrentRssIndexerRequestGenerator.cs | 20 +++ src/NzbDrone.Core/Indexers/HttpIndexerBase.cs | 42 ++++++- src/NzbDrone.Core/Indexers/IIndexer.cs | 4 + .../Indexers/IIndexerRequestGenerator.cs | 4 + src/NzbDrone.Core/Indexers/IndexerBase.cs | 4 + .../Indexers/IndexerCapabilities.cs | 10 +- .../Indexers/RssIndexerRequestGenerator.cs | 20 +++ src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs | 4 +- src/NzbDrone.Core/Parser/Model/TorrentInfo.cs | 1 + .../Indexers/IndexerCapabilityResource.cs | 41 +++++++ src/Prowlarr.Api.V1/Indexers/IndexerModule.cs | 1 + .../Indexers/IndexerResource.cs | 4 +- src/Prowlarr.Api.V1/Search/SearchModule.cs | 3 +- src/Prowlarr.Api.V1/Search/SearchRequest.cs | 1 + src/Prowlarr.Api.V1/Search/SearchResource.cs | 4 +- 51 files changed, 720 insertions(+), 224 deletions(-) create mode 100644 frontend/src/Search/Table/CategoryLabel.js create mode 100644 src/NzbDrone.Core/IndexerSearch/Definitions/BasicSearchCriteria.cs create mode 100644 src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs create mode 100644 src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs create mode 100644 src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs create mode 100644 src/Prowlarr.Api.V1/Indexers/IndexerCapabilityResource.cs diff --git a/frontend/src/Indexer/Index/Table/CapabilitiesLabel.js b/frontend/src/Indexer/Index/Table/CapabilitiesLabel.js index 9fb216193..698bc6221 100644 --- a/frontend/src/Indexer/Index/Table/CapabilitiesLabel.js +++ b/frontend/src/Indexer/Index/Table/CapabilitiesLabel.js @@ -4,48 +4,25 @@ import Label from 'Components/Label'; function CapabilitiesLabel(props) { const { - movieSearchAvailable, - tvSearchAvailable, - musicSearchAvailable, - bookSearchAvailable + categories } = props.capabilities; + const filteredList = categories.filter((item) => item.id < 100000).map((item) => item.name).sort(); + return ( { - bookSearchAvailable ? - : - null - } - - { - movieSearchAvailable ? - : - null - } - - { - musicSearchAvailable ? - : - null - } - - { - tvSearchAvailable ? - : - null + filteredList.map((category) => { + return ( + + ); + }) } { - !tvSearchAvailable && !musicSearchAvailable && !movieSearchAvailable && !bookSearchAvailable ? + filteredList.length === 0 ? : @@ -61,10 +38,7 @@ CapabilitiesLabel.propTypes = { CapabilitiesLabel.defaultProps = { capabilities: { - movieSearchAvailable: false, - tvSearchAvailable: false, - musicSearchAvailable: false, - bookSearchAvailable: false + categories: [] } }; diff --git a/frontend/src/Indexer/Index/Table/MovieIndexHeader.css b/frontend/src/Indexer/Index/Table/MovieIndexHeader.css index a5cfffcc1..665955ea3 100644 --- a/frontend/src/Indexer/Index/Table/MovieIndexHeader.css +++ b/frontend/src/Indexer/Index/Table/MovieIndexHeader.css @@ -21,7 +21,7 @@ .capabilities { composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; - flex: 0 0 180px; + flex: 0 0 350px; } .added { diff --git a/frontend/src/Indexer/Index/Table/MovieIndexRow.css b/frontend/src/Indexer/Index/Table/MovieIndexRow.css index 3e33a1775..308bbab2b 100644 --- a/frontend/src/Indexer/Index/Table/MovieIndexRow.css +++ b/frontend/src/Indexer/Index/Table/MovieIndexRow.css @@ -28,7 +28,7 @@ .capabilities { composes: cell; - flex: 0 0 180px; + flex: 0 0 350px; } .added { diff --git a/frontend/src/Search/Table/CategoryLabel.js b/frontend/src/Search/Table/CategoryLabel.js new file mode 100644 index 000000000..e43c32899 --- /dev/null +++ b/frontend/src/Search/Table/CategoryLabel.js @@ -0,0 +1,23 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import Label from 'Components/Label'; + +function CategoryLabel({ categories }) { + let catName = ''; + + if (categories && categories.length > 0) { + catName = categories[0].name; + } + + return ( + + ); +} + +CategoryLabel.propTypes = { + categories: PropTypes.arrayOf(PropTypes.object).isRequired +}; + +export default CategoryLabel; diff --git a/frontend/src/Search/Table/SearchIndexHeader.css b/frontend/src/Search/Table/SearchIndexHeader.css index c7be5e061..284d8f597 100644 --- a/frontend/src/Search/Table/SearchIndexHeader.css +++ b/frontend/src/Search/Table/SearchIndexHeader.css @@ -10,6 +10,7 @@ flex: 4 0 110px; } +.category, .indexer { composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; diff --git a/frontend/src/Search/Table/SearchIndexRow.css b/frontend/src/Search/Table/SearchIndexRow.css index 2ca871cd5..a5f8415a7 100644 --- a/frontend/src/Search/Table/SearchIndexRow.css +++ b/frontend/src/Search/Table/SearchIndexRow.css @@ -17,6 +17,7 @@ flex: 4 0 110px; } +.category, .indexer { composes: cell; diff --git a/frontend/src/Search/Table/SearchIndexRow.js b/frontend/src/Search/Table/SearchIndexRow.js index 654135593..eb52ad126 100644 --- a/frontend/src/Search/Table/SearchIndexRow.js +++ b/frontend/src/Search/Table/SearchIndexRow.js @@ -10,6 +10,7 @@ import formatDateTime from 'Utilities/Date/formatDateTime'; import formatAge from 'Utilities/Number/formatAge'; import formatBytes from 'Utilities/Number/formatBytes'; import translate from 'Utilities/String/translate'; +import CategoryLabel from './CategoryLabel'; import Peers from './Peers'; import ProtocolLabel from './ProtocolLabel'; import styles from './SearchIndexRow.css'; @@ -22,6 +23,7 @@ class SearchIndexRow extends Component { render() { const { protocol, + categories, age, ageHours, ageMinutes, @@ -132,6 +134,19 @@ class SearchIndexRow extends Component { ); } + if (column.name === 'category') { + return ( + + + + ); + } + if (column.name === 'indexerFlags') { return ( (body); - - query.Category.Should().HaveCount(2); - query.Username.Should().Be(Subject.Settings.Username); - query.Passkey.Should().Be(Subject.Settings.ApiKey); - } - [Test] public void should_search_by_imdbid_if_supported() { diff --git a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs index afc9da611..29a80099f 100644 --- a/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerTests/NewznabTests/NewznabRequestGeneratorFixture.cs @@ -36,30 +36,6 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests .Returns(_capabilities); } - [Test] - public void should_use_all_categories_for_feed() - { - var results = Subject.GetRecentRequests(); - - results.GetAllTiers().Should().HaveCount(1); - - var page = results.GetAllTiers().First().First(); - - page.Url.Query.Should().Contain("&cat=1,2&"); - } - - [Test] - public void should_not_have_duplicate_categories() - { - var results = Subject.GetRecentRequests(); - - results.GetAllTiers().Should().HaveCount(1); - - var page = results.GetAllTiers().First().First(); - - page.Url.FullUri.Should().Contain("&cat=1,2,3&"); - } - [Test] public void should_return_subsequent_pages() { diff --git a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs index 4df3e9d64..0fc08aed3 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs @@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Lidarr Fields = schema.Fields, }; - lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/1/"; + lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; diff --git a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs index d4ba871bf..fd0915ae5 100644 --- a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs +++ b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs @@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Radarr Fields = schema.Fields, }; - radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/1/"; + radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; diff --git a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs index 375242cdc..632d171de 100644 --- a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs +++ b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs @@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Readarr Fields = schema.Fields, }; - readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/1/"; + readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; diff --git a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs index 63178269e..6f5fd37d9 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs @@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Sonarr Fields = schema.Fields, }; - sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/1/"; + sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/BasicSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/BasicSearchCriteria.cs new file mode 100644 index 000000000..2795b36f3 --- /dev/null +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/BasicSearchCriteria.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class BasicSearchCriteria : SearchCriteriaBase + { + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs new file mode 100644 index 000000000..467ae4a9d --- /dev/null +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/BookSearchCriteria.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class BookSearchCriteria : SearchCriteriaBase + { + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs index e5302f2bc..275118213 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MovieSearchCriteria.cs @@ -3,10 +3,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public class MovieSearchCriteria : SearchCriteriaBase { public string ImdbId { get; set; } - public int TmdbId { get; set; } - public override string ToString() - { - return string.Format("[{0}]", ImdbId); - } + public int? TmdbId { get; set; } + public int? Year { get; set; } + public int? TraktId { get; set; } } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs new file mode 100644 index 000000000..0b6b2e607 --- /dev/null +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/MusicSearchCriteria.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class MusicSearchCriteria : SearchCriteriaBase + { + public string Album { get; set; } + public string Artist { get; set; } + public string Label { get; set; } + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 81c44d6f0..bf189a728 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -16,5 +16,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions public List IndexerIds { get; set; } public string SearchTerm { get; set; } public int[] Categories { get; set; } + public string SearchType { get; set; } + public int? Limit { get; set; } + public int? Offset { get; set; } } } diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs new file mode 100644 index 000000000..a1bd431a3 --- /dev/null +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/TvSearchCriteria.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.IndexerSearch.Definitions +{ + public class TvSearchCriteria : SearchCriteriaBase + { + public int? Season { get; set; } + public int? Ep { get; set; } + + public string ImdbId { get; set; } + public int? TvdbId { get; set; } + public int? RId { get; set; } + public int? TvMazeId { get; set; } + public int? TraktId { get; set; } + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/NewznabRequest.cs b/src/NzbDrone.Core/IndexerSearch/NewznabRequest.cs index 81f900959..0cdae0c8b 100644 --- a/src/NzbDrone.Core/IndexerSearch/NewznabRequest.cs +++ b/src/NzbDrone.Core/IndexerSearch/NewznabRequest.cs @@ -7,19 +7,21 @@ namespace NzbDrone.Core.IndexerSearch public string q { get; set; } public string cat { get; set; } public string imdbid { get; set; } - public string tmdbid { get; set; } + public int? tmdbid { get; set; } public string extended { get; set; } - public string limit { get; set; } - public string offset { get; set; } - public string rid { get; set; } - public string tvdbid { get; set; } - public string season { get; set; } - public string ep { get; set; } + public int? limit { get; set; } + public int? offset { get; set; } + public int? rid { get; set; } + public int? tvmazeid { get; set; } + public int? traktid { get; set; } + public int? tvdbid { get; set; } + public int? season { get; set; } + public int? ep { get; set; } public string album { get; set; } public string artist { get; set; } public string label { get; set; } public string track { get; set; } - public string year { get; set; } + public int? year { get; set; } public string genre { get; set; } public string author { get; set; } public string title { get; set; } diff --git a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs index a7a465b4e..54f8feb51 100644 --- a/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs +++ b/src/NzbDrone.Core/IndexerSearch/NewznabResults.cs @@ -72,12 +72,14 @@ namespace NzbDrone.Core.IndexerSearch new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory new XElement("prowlarrindexer", new XAttribute("id", r.IndexerId), r.Indexer), r.PublishDate == DateTime.MinValue ? new XElement("pubDate", XmlDateFormat(DateTime.Now)) : new XElement("pubDate", XmlDateFormat(r.PublishDate)), - r.Category == null ? null : from c in r.Category select new XElement("category", c), new XElement("size", r.Size), + r.Category == null ? null : from c in r.Category select new XElement("category", c.Id), new XElement( "enclosure", - new XAttribute("length", r.Size), + new XAttribute("url", r.DownloadUrl ?? t.MagnetUrl ?? string.Empty), + r.Size == null ? null : new XAttribute("length", r.Size), new XAttribute("type", "application/x-bittorrent")), + r.Category == null ? null : from c in r.Category select GetTorznabElement("category", c.Id), GetTorznabElement("rageid", r.TvRageId), GetTorznabElement("thetvdb", r.TvdbId), GetTorznabElement("imdb", r.ImdbId.ToString("D7")), diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 9aef16a8b..579a6053c 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -16,7 +16,6 @@ namespace NzbDrone.Core.IndexerSearch { public interface ISearchForNzb { - List Search(string query, List indexerIds, bool interactiveSearch); NewznabResults Search(NewznabRequest request, List indexerIds, bool interactiveSearch); } @@ -35,17 +34,70 @@ namespace NzbDrone.Core.IndexerSearch _logger = logger; } - public List Search(string query, List indexerIds, bool interactiveSearch) + public NewznabResults Search(NewznabRequest request, List indexerIds, bool interactiveSearch) { - var searchSpec = Get(new NewznabRequest { q = query }, indexerIds, interactiveSearch); + var results = new NewznabResults(); - return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec); + switch (request.t) + { + case "movie": + return MovieSearch(request, indexerIds, interactiveSearch); + case "music": + return MusicSearch(request, indexerIds, interactiveSearch); + case "tvsearch": + return TvSearch(request, indexerIds, interactiveSearch); + case "book": + return BookSearch(request, indexerIds, interactiveSearch); + default: + return BasicSearch(request, indexerIds, interactiveSearch); + } } - public NewznabResults Search(NewznabRequest request, List indexerIds, bool interactiveSearch) + private NewznabResults MovieSearch(NewznabRequest request, List indexerIds, bool interactiveSearch) { var searchSpec = Get(request, indexerIds, interactiveSearch); + searchSpec.ImdbId = request.imdbid; + searchSpec.TmdbId = request.tmdbid; + searchSpec.TraktId = request.traktid; + searchSpec.Year = request.year; + + return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; + } + + private NewznabResults MusicSearch(NewznabRequest request, List indexerIds, bool interactiveSearch) + { + var searchSpec = Get(request, indexerIds, interactiveSearch); + + return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; + } + + private NewznabResults TvSearch(NewznabRequest request, List indexerIds, bool interactiveSearch) + { + var searchSpec = Get(request, indexerIds, interactiveSearch); + + searchSpec.Season = request.season; + searchSpec.Ep = request.ep; + searchSpec.TvdbId = request.tvdbid; + searchSpec.ImdbId = request.imdbid; + searchSpec.TraktId = request.traktid; + searchSpec.RId = request.rid; + searchSpec.TvMazeId = request.tvmazeid; + + return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; + } + + private NewznabResults BookSearch(NewznabRequest request, List indexerIds, bool interactiveSearch) + { + var searchSpec = Get(request, indexerIds, interactiveSearch); + + return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; + } + + private NewznabResults BasicSearch(NewznabRequest request, List indexerIds, bool interactiveSearch) + { + var searchSpec = Get(request, indexerIds, interactiveSearch); + return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; } @@ -67,6 +119,9 @@ namespace NzbDrone.Core.IndexerSearch } spec.SearchTerm = query.q; + spec.SearchType = query.t; + spec.Limit = query.limit; + spec.Offset = query.offset; spec.IndexerIds = indexerIds; diff --git a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs index 3e24e591f..e07cdfbe6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/AwesomeHD/AwesomeHDRequestGenerator.cs @@ -37,5 +37,25 @@ namespace NzbDrone.Core.Indexers.AwesomeHD yield return new IndexerRequest($"{Settings.BaseUrl.Trim().TrimEnd('/')}/searchapi.php?action=latestmovies&passkey={Settings.Passkey.Trim()}", HttpAccept.Rss); } } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs index c30cd5b7e..2621202cc 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannBase.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using AngleSharp.Dom; @@ -9,7 +10,6 @@ using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Serializer; -using NzbDrone.Core.IndexerSearch.Definitions; namespace NzbDrone.Core.Indexers.Cardigann { @@ -22,7 +22,8 @@ namespace NzbDrone.Core.Indexers.Cardigann protected string SiteLink { get; private set; } - /* protected readonly List categoryMapping = new List(); */ + protected readonly List _categoryMapping = new List(); + protected readonly List _defaultCategories = new List(); protected readonly string[] OptionalFields = new string[] { "imdb", "rageid", "tvdbid", "banner" }; @@ -54,6 +55,70 @@ namespace NzbDrone.Core.Indexers.Cardigann _logger = logger; SiteLink = definition.Links.First(); + + if (_definition.Caps.Categories != null) + { + foreach (var category in _definition.Caps.Categories) + { + var cat = TorznabCatType.GetCatByName(category.Value); + if (cat == null) + { + _logger.Error(string.Format("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, category.Key, category.Value)); + continue; + } + + AddCategoryMapping(category.Key, cat); + } + } + + if (_definition.Caps.Categorymappings != null) + { + foreach (var categorymapping in _definition.Caps.Categorymappings) + { + IndexerCategory torznabCat = null; + + if (categorymapping.cat != null) + { + torznabCat = TorznabCatType.GetCatByName(categorymapping.cat); + if (torznabCat == null) + { + _logger.Error(string.Format("CardigannIndexer ({0}): invalid Torznab category for id {1}: {2}", _definition.Id, categorymapping.id, categorymapping.cat)); + continue; + } + } + + AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc); + + if (categorymapping.Default) + { + _defaultCategories.Add(categorymapping.id); + } + } + } + } + + public void AddCategoryMapping(string trackerCategory, IndexerCategory torznabCategory, string trackerCategoryDesc = null) + { + _categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, torznabCategory.Id)); + + if (trackerCategoryDesc == null) + { + return; + } + + // create custom cats (1:1 categories) if trackerCategoryDesc is defined + // - if trackerCategory is "integer" we use that number to generate custom category id + // - if trackerCategory is "string" we compute a hash to generate fixed integer id for the custom category + // the hash is not perfect but it should work in most cases. we can't use sequential numbers because + // categories are updated frequently and the id must be fixed to work in 3rd party apps + if (!int.TryParse(trackerCategory, out var trackerCategoryInt)) + { + var hashed = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(trackerCategory)); + trackerCategoryInt = BitConverter.ToUInt16(hashed, 0); // id between 0 and 65535 < 100000 + } + + var customCat = new IndexerCategory(trackerCategoryInt + 100000, trackerCategoryDesc); + _categoryMapping.Add(new CategoryMapping(trackerCategory, trackerCategoryDesc, customCat.Id)); } protected IElement QuerySelector(IElement element, string selector) @@ -186,53 +251,36 @@ namespace NzbDrone.Core.Indexers.Cardigann return variables; } - protected ICollection MapTrackerCatToNewznab(string input) + protected ICollection MapTrackerCatToNewznab(string input) { - if (input == null) - { - return new List(); - } - - var cats = _definition.Caps.Categorymappings.Where(m => m.id != null && m.id.ToLowerInvariant() == input.ToLowerInvariant()).Select(c => TorznabCatType.GetCatByName(c.cat).Id).ToList(); - - // 1:1 category mapping - try + if (string.IsNullOrWhiteSpace(input)) { - var trackerCategoryInt = int.Parse(input); - cats.Add(trackerCategoryInt + 100000); - } - catch (FormatException) - { - // input is not an integer, continue + return new List(); } + var cats = _categoryMapping + .Where(m => + !string.IsNullOrWhiteSpace(m.TrackerCategory) && + string.Equals(m.TrackerCategory, input, StringComparison.InvariantCultureIgnoreCase)) + .Select(c => TorznabCatType.AllCats.FirstOrDefault(n => n.Id == c.NewzNabCategory) ?? new IndexerCategory { Id = c.NewzNabCategory }) + .ToList(); return cats; } public List MapTorznabCapsToTrackers(int[] searchCategories, bool mapChildrenCatsToParent = false) { - var queryCats = new List(); - if (searchCategories == null) { - return queryCats; + return new List(); } - foreach (var searchCat in searchCategories) - { - var match = TorznabCatType.AllCats.FirstOrDefault(c => c.Id == searchCat); - - if (match != null) - { - queryCats.Add(match.Name); - } - } + var results = new List(); - var result = _definition.Caps.Categorymappings - .Where(c => queryCats.Contains(c.cat)) - .Select(mapping => mapping.id).Distinct().ToList(); + results.AddRange(_categoryMapping + .Where(c => searchCategories.Contains(c.NewzNabCategory)) + .Select(mapping => mapping.TrackerCategory).Distinct().ToList()); - return result; + return results; } protected delegate string TemplateTextModifier(string str); diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs index c06192816..1151ea880 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannParser.cs @@ -472,13 +472,14 @@ namespace NzbDrone.Core.Indexers.Cardigann result.AddRange(releases.Select(x => new TorrentInfo { + PublishDate = x.PublishDate, Guid = x.Guid.ToString(), Title = x.Title, Size = x.Size.Value, - DownloadUrl = x.Link?.ToString(), + DownloadUrl = x.Link.AbsoluteUri, CommentUrl = x.Comments?.ToString(), InfoUrl = x.Link?.ToString(), - MagnetUrl = x.MagnetUri?.ToString(), + MagnetUrl = x.MagnetUri?.AbsoluteUri, InfoHash = x.InfoHash, Seeders = (int?)x.Seeders, Peers = (int?)x.Peers, diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannReleaseInfo.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannReleaseInfo.cs index e1cad172f..f4ecd0399 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannReleaseInfo.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannReleaseInfo.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Core.Indexers.Cardigann public Uri Link { get; set; } public Uri Comments { get; set; } public DateTime PublishDate { get; set; } - public ICollection Category { get; set; } + public ICollection Category { get; set; } public long? Size { get; set; } public long? Files { get; set; } public long? Grabs { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs index 6638dee8a..6f74ae742 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Cardigann/CardigannRequestGenerator.cs @@ -10,8 +10,6 @@ namespace NzbDrone.Core.Indexers.Cardigann { public class CardigannRequestGenerator : CardigannBase, IIndexerRequestGenerator { - private List _defaultCategories = new List(); - public CardigannRequestGenerator(CardigannDefinition definition, CardigannSettings settings, Logger logger) @@ -22,71 +20,134 @@ namespace NzbDrone.Core.Indexers.Cardigann public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } - public virtual IndexerPageableRequestChain GetRecentRequests() + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { - _logger.Trace("Getting recent"); + _logger.Trace("Getting search"); var pageableRequests = new IndexerPageableRequestChain(); - var variables = GetBaseTemplateVariables(); + var variables = GetQueryVariableDefaults(searchCriteria); - variables[".Query.Type"] = null; - variables[".Query.Q"] = null; - variables[".Query.Categories"] = null; - variables[".Query.IMDBID"] = null; - variables[".Query.IMDBIDShort"] = null; - variables[".Query.TMDBID"] = null; + variables[".Query.Movie"] = null; + variables[".Query.Year"] = searchCriteria.Year; + variables[".Query.IMDBID"] = searchCriteria.ImdbId; + variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId.Replace("tt", ""); + variables[".Query.TMDBID"] = searchCriteria.TmdbId; + variables[".Query.TraktID"] = searchCriteria.TraktId; pageableRequests.Add(GetRequest(variables)); return pageableRequests; } - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) { - _logger.Trace("Getting search"); + var pageableRequests = new IndexerPageableRequestChain(); + + var variables = GetQueryVariableDefaults(searchCriteria); + + variables[".Query.Album"] = searchCriteria.Album; + variables[".Query.Artist"] = searchCriteria.Artist; + variables[".Query.Label"] = searchCriteria.Label; + variables[".Query.Track"] = null; + + pageableRequests.Add(GetRequest(variables)); + + return pageableRequests; + } + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { var pageableRequests = new IndexerPageableRequestChain(); - var variables = GetBaseTemplateVariables(); + var variables = GetQueryVariableDefaults(searchCriteria); - variables[".Query.Type"] = "movie"; - variables[".Query.Q"] = searchCriteria.SearchTerm; - variables[".Query.Categories"] = searchCriteria.Categories; + variables[".Query.Series"] = null; + variables[".Query.Ep"] = searchCriteria.Ep; + variables[".Query.Season"] = searchCriteria.Season; variables[".Query.IMDBID"] = searchCriteria.ImdbId; - variables[".Query.IMDBIDShort"] = null; - variables[".Query.TMDBID"] = searchCriteria.TmdbId; + variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId.Replace("tt", ""); + variables[".Query.TVDBID"] = searchCriteria.TvdbId; + variables[".Query.TVRageID"] = searchCriteria.RId; + variables[".Query.TVMazeID"] = searchCriteria.TvMazeId; + variables[".Query.TraktID"] = searchCriteria.TraktId; pageableRequests.Add(GetRequest(variables)); return pageableRequests; } - private IEnumerable GetRequest(Dictionary variables) + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) { - var search = _definition.Search; + var pageableRequests = new IndexerPageableRequestChain(); + + var variables = GetQueryVariableDefaults(searchCriteria); + variables[".Query.Author"] = null; + variables[".Query.Title"] = null; + + pageableRequests.Add(GetRequest(variables)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + var variables = GetQueryVariableDefaults(searchCriteria); + + pageableRequests.Add(GetRequest(variables)); + + return pageableRequests; + } + + private Dictionary GetQueryVariableDefaults(SearchCriteriaBase searchCriteria) + { + var variables = GetBaseTemplateVariables(); + + variables[".Query.Type"] = searchCriteria.SearchType; + variables[".Query.Q"] = searchCriteria.SearchTerm; + variables[".Query.Categories"] = searchCriteria.Categories; + variables[".Query.Limit"] = searchCriteria.Limit; + variables[".Query.Offset"] = searchCriteria.Offset; + variables[".Query.Extended"] = null; + variables[".Query.APIKey"] = null; + + //Movie + variables[".Query.Movie"] = null; + variables[".Query.Year"] = null; + variables[".Query.IMDBID"] = null; + variables[".Query.IMDBIDShort"] = null; + variables[".Query.TMDBID"] = null; + + //Tv variables[".Query.Series"] = null; variables[".Query.Ep"] = null; variables[".Query.Season"] = null; - variables[".Query.Movie"] = null; - variables[".Query.Year"] = null; - variables[".Query.Limit"] = null; - variables[".Query.Offset"] = null; - variables[".Query.Extended"] = null; - variables[".Query.APIKey"] = null; variables[".Query.TVDBID"] = null; variables[".Query.TVRageID"] = null; variables[".Query.TVMazeID"] = null; variables[".Query.TraktID"] = null; + + //Music variables[".Query.Album"] = null; variables[".Query.Artist"] = null; variables[".Query.Label"] = null; variables[".Query.Track"] = null; variables[".Query.Episode"] = null; + + //Book variables[".Query.Author"] = null; variables[".Query.Title"] = null; + return variables; + } + + private IEnumerable GetRequest(Dictionary variables) + { + var search = _definition.Search; + var mappedCategories = MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]); if (mappedCategories.Count == 0) { diff --git a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs index ed84d22ba..6f46ea751 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/FileList/FileListRequestGenerator.cs @@ -34,6 +34,26 @@ namespace NzbDrone.Core.Indexers.FileList return pageableRequests; } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + private IEnumerable GetRequest(string searchType, string parameters) { var categoriesQuery = string.Join(",", Settings.Categories.Distinct()); diff --git a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs index 91d7c15fc..c4a1217c6 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/HDBits/HDBitsRequestGenerator.cs @@ -12,13 +12,6 @@ namespace NzbDrone.Core.Indexers.HDBits { public HDBitsSettings Settings { get; set; } - public virtual IndexerPageableRequestChain GetRecentRequests() - { - var pageableRequests = new IndexerPageableRequestChain(); - pageableRequests.Add(GetRequest(new TorrentQuery())); - return pageableRequests; - } - public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); @@ -76,5 +69,25 @@ namespace NzbDrone.Core.Indexers.HDBits yield return new IndexerRequest(request); } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs index fbc810067..eea397476 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/IPTorrents/IPTorrentsRequestGenerator.cs @@ -28,6 +28,26 @@ namespace NzbDrone.Core.Indexers.IPTorrents yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss); } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs index d22379ac7..e26a25e50 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Newznab/NewznabRequestGenerator.cs @@ -66,30 +66,38 @@ namespace NzbDrone.Core.Indexers.Newznab } } - public virtual IndexerPageableRequestChain GetRecentRequests() + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); - var capabilities = _capabilitiesProvider.GetCapabilities(Settings); - - // Some indexers might forget to enable movie search, but normal search still works fine. Thus we force a normal search. - if (capabilities.MovieSearchParams != null) - { - pageableRequests.Add(GetPagedRequests(MaxPages, new int[] { 2000 }, "movie", "")); - } - else if (capabilities.SearchParams != null) - { - pageableRequests.Add(GetPagedRequests(MaxPages, new int[] { 2000 }, "search", "")); - } + AddMovieIdPageableRequests(pageableRequests, MaxPages, searchCriteria.Categories, searchCriteria); return pageableRequests; } - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); - AddMovieIdPageableRequests(pageableRequests, MaxPages, searchCriteria.Categories, searchCriteria); + pageableRequests.Add(GetPagedRequests(MaxPages, + searchCriteria.Categories, + "search", + string.Format("&q={0}", NewsnabifyTitle(searchCriteria.SearchTerm)))); return pageableRequests; } @@ -153,15 +161,14 @@ namespace NzbDrone.Core.Indexers.Newznab private IEnumerable GetPagedRequests(int maxPages, IEnumerable categories, string searchType, string parameters) { - if (categories.Empty()) + var baseUrl = string.Format("{0}{1}?t={2}&extended=1", Settings.BaseUrl.TrimEnd('/'), Settings.ApiPath.TrimEnd('/'), searchType); + + if (categories != null && categories.Any()) { - yield break; + var categoriesQuery = string.Join(",", categories.Distinct()); + baseUrl += string.Format("&cats={0}", categoriesQuery); } - var categoriesQuery = string.Join(",", categories.Distinct()); - - var baseUrl = string.Format("{0}{1}?t={2}&cat={3}&extended=1{4}", Settings.BaseUrl.TrimEnd('/'), Settings.ApiPath.TrimEnd('/'), searchType, categoriesQuery, Settings.AdditionalParameters); - if (Settings.ApiKey.IsNotNullOrWhiteSpace()) { baseUrl += "&apikey=" + Settings.ApiKey; diff --git a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs index 98f34878c..8e6d9b25a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Nyaa/NyaaRequestGenerator.cs @@ -18,15 +18,6 @@ namespace NzbDrone.Core.Indexers.Nyaa PageSize = 100; } - public virtual IndexerPageableRequestChain GetRecentRequests() - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests(MaxPages, null)); - - return pageableRequests; - } - private IEnumerable GetPagedRequests(int maxPages, string term) { var baseUrl = string.Format("{0}/?page=rss{1}", Settings.BaseUrl.TrimEnd('/'), Settings.AdditionalParameters); @@ -65,6 +56,26 @@ namespace NzbDrone.Core.Indexers.Nyaa return pageableRequests; } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs index acffa2eb3..3a5911ead 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/PassThePopcorn/PassThePopcornRequestGenerator.cs @@ -16,15 +16,6 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn public IHttpClient HttpClient { get; set; } public Logger Logger { get; set; } - public virtual IndexerPageableRequestChain GetRecentRequests() - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetRequest(null)); - - return pageableRequests; - } - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); @@ -66,5 +57,25 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn yield return request; } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs index eeafb2ffc..145978df8 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Rarbg/RarbgRequestGenerator.cs @@ -116,6 +116,26 @@ namespace NzbDrone.Core.Indexers.Rarbg yield return new IndexerRequest(requestBuilder.Build()); } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs index fc7a4f965..5e45956fd 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs @@ -80,6 +80,26 @@ namespace NzbDrone.Core.Indexers.TorrentPotato return pageableRequests; } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs index fbdad0635..a9bc900c7 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentRss/TorrentRssIndexerRequestGenerator.cs @@ -39,6 +39,26 @@ namespace NzbDrone.Core.Indexers.TorrentRss yield return request; } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index ddd27678b..afad08fa7 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -49,6 +49,46 @@ namespace NzbDrone.Core.Indexers return FetchReleases(g => g.GetSearchRequests(searchCriteria)); } + public override IList Fetch(MusicSearchCriteria searchCriteria) + { + if (!SupportsSearch) + { + return new List(); + } + + return FetchReleases(g => g.GetSearchRequests(searchCriteria)); + } + + public override IList Fetch(TvSearchCriteria searchCriteria) + { + if (!SupportsSearch) + { + return new List(); + } + + return FetchReleases(g => g.GetSearchRequests(searchCriteria)); + } + + public override IList Fetch(BookSearchCriteria searchCriteria) + { + if (!SupportsSearch) + { + return new List(); + } + + return FetchReleases(g => g.GetSearchRequests(searchCriteria)); + } + + public override IList Fetch(BasicSearchCriteria searchCriteria) + { + if (!SupportsSearch) + { + return new List(); + } + + return FetchReleases(g => g.GetSearchRequests(searchCriteria)); + } + protected IndexerPageableRequestChain GetRequestChain(SearchCriteriaBase searchCriteria = null) { var generator = GetRequestGenerator(); @@ -254,7 +294,7 @@ namespace NzbDrone.Core.Indexers protected virtual bool IsValidRelease(ReleaseInfo release) { - if (release.DownloadUrl.IsNullOrWhiteSpace()) + if (release.DownloadUrl == null) { return false; } diff --git a/src/NzbDrone.Core/Indexers/IIndexer.cs b/src/NzbDrone.Core/Indexers/IIndexer.cs index a2e7e995b..27e44d6a2 100644 --- a/src/NzbDrone.Core/Indexers/IIndexer.cs +++ b/src/NzbDrone.Core/Indexers/IIndexer.cs @@ -15,5 +15,9 @@ namespace NzbDrone.Core.Indexers IndexerPrivacy Privacy { get; } IList Fetch(MovieSearchCriteria searchCriteria); + IList Fetch(MusicSearchCriteria searchCriteria); + IList Fetch(TvSearchCriteria searchCriteria); + IList Fetch(BookSearchCriteria searchCriteria); + IList Fetch(BasicSearchCriteria searchCriteria); } } diff --git a/src/NzbDrone.Core/Indexers/IIndexerRequestGenerator.cs b/src/NzbDrone.Core/Indexers/IIndexerRequestGenerator.cs index e80c54d39..450412aaf 100644 --- a/src/NzbDrone.Core/Indexers/IIndexerRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/IIndexerRequestGenerator.cs @@ -7,6 +7,10 @@ namespace NzbDrone.Core.Indexers public interface IIndexerRequestGenerator { IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria); + IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria); + IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria); + IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria); + IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria); Func> GetCookies { get; set; } Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Indexers/IndexerBase.cs b/src/NzbDrone.Core/Indexers/IndexerBase.cs index e04e638c2..115723a30 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBase.cs @@ -66,6 +66,10 @@ namespace NzbDrone.Core.Indexers protected TSettings Settings => (TSettings)Definition.Settings; public abstract IList Fetch(MovieSearchCriteria searchCriteria); + public abstract IList Fetch(MusicSearchCriteria searchCriteria); + public abstract IList Fetch(TvSearchCriteria searchCriteria); + public abstract IList Fetch(BookSearchCriteria searchCriteria); + public abstract IList Fetch(BasicSearchCriteria searchCriteria); protected virtual IList CleanupReleases(IEnumerable releases) { diff --git a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs index c8ed18784..99f9234c9 100644 --- a/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs +++ b/src/NzbDrone.Core/Indexers/IndexerCapabilities.cs @@ -114,6 +114,7 @@ namespace NzbDrone.Core.Indexers throw new Exception("In search mode 'search' only 'q' parameter is supported and it's mandatory"); } + SearchParams.Add(SearchParam.Q); break; case "tv-search": ParseTvSearchParams(entry.Value); @@ -277,6 +278,13 @@ namespace NzbDrone.Core.Indexers return string.Join(",", parameters); } + private string SupportedSearchParams() + { + var parameters = new List { "q" }; // q is always enabled + + return string.Join(",", parameters); + } + private string SupportedMovieSearchParams() { var parameters = new List { "q" }; // q is always enabled @@ -350,7 +358,7 @@ namespace NzbDrone.Core.Indexers new XElement("searching", new XElement("search", new XAttribute("available", SearchAvailable ? "yes" : "no"), - new XAttribute("supportedParams", "q")), + new XAttribute("supportedParams", SupportedSearchParams())), new XElement("tv-search", new XAttribute("available", TvSearchAvailable ? "yes" : "no"), new XAttribute("supportedParams", SupportedTvSearchParams())), diff --git a/src/NzbDrone.Core/Indexers/RssIndexerRequestGenerator.cs b/src/NzbDrone.Core/Indexers/RssIndexerRequestGenerator.cs index 7897af38f..eead09944 100644 --- a/src/NzbDrone.Core/Indexers/RssIndexerRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/RssIndexerRequestGenerator.cs @@ -28,6 +28,26 @@ namespace NzbDrone.Core.Indexers return new IndexerPageableRequestChain(); } + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + public Func> GetCookies { get; set; } public Action, DateTime?> CookiesUpdater { get; set; } } diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index 2ccf2336a..40c2d0722 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Parser.Model { public string Guid { get; set; } public string Title { get; set; } - public long Size { get; set; } + public long? Size { get; set; } public string DownloadUrl { get; set; } public string InfoUrl { get; set; } public string CommentUrl { get; set; } @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Parser.Model public string Container { get; set; } public string Codec { get; set; } public string Resolution { get; set; } - public ICollection Category { get; set; } + public ICollection Category { get; set; } public IndexerFlags IndexerFlags { get; set; } diff --git a/src/NzbDrone.Core/Parser/Model/TorrentInfo.cs b/src/NzbDrone.Core/Parser/Model/TorrentInfo.cs index 9f76fc9ee..7594334b0 100644 --- a/src/NzbDrone.Core/Parser/Model/TorrentInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/TorrentInfo.cs @@ -1,3 +1,4 @@ +using System; using System.Text; namespace NzbDrone.Core.Parser.Model diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerCapabilityResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerCapabilityResource.cs new file mode 100644 index 000000000..4e137a8f2 --- /dev/null +++ b/src/Prowlarr.Api.V1/Indexers/IndexerCapabilityResource.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NzbDrone.Core.Indexers; +using Prowlarr.Http.REST; + +namespace Prowlarr.Api.V1.Indexers +{ + public class IndexerCapabilityResource : RestResource + { + public int? LimitsMax { get; set; } + public int? LimitsDefault { get; set; } + + public List Categories; + } + + public static class IndexerCapabilitiesResourceMapper + { + public static IndexerCapabilityResource ToResource(this IndexerCapabilities model) + { + if (model == null) + { + return null; + } + + return new IndexerCapabilityResource + { + LimitsMax = model.LimitsMax, + LimitsDefault = model.LimitsDefault, + Categories = model.Categories.GetTorznabCategoryTree() + }; + } + + public static List ToResource(this IEnumerable models) + { + return models.Select(ToResource).ToList(); + } + } +} diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerModule.cs b/src/Prowlarr.Api.V1/Indexers/IndexerModule.cs index 9521c8126..f24bad915 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerModule.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerModule.cs @@ -64,6 +64,7 @@ namespace Prowlarr.Api.V1.Indexers return response; case "tvsearch": case "music": + case "book": case "movie": Response movieResponse = _nzbSearchService.Search(request, new List { indexer.Id }, false).ToXml(); movieResponse.ContentType = "application/rss+xml"; diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs index fec61a0bf..189552847 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs @@ -17,7 +17,7 @@ namespace Prowlarr.Api.V1.Indexers public bool SupportsSearch { get; set; } public DownloadProtocol Protocol { get; set; } public IndexerPrivacy Privacy { get; set; } - public IndexerCapabilities Capabilities { get; set; } + public IndexerCapabilityResource Capabilities { get; set; } public int Priority { get; set; } public DateTime Added { get; set; } } @@ -59,7 +59,7 @@ namespace Prowlarr.Api.V1.Indexers resource.EnableInteractiveSearch = definition.EnableInteractiveSearch; resource.SupportsRss = definition.SupportsRss; resource.SupportsSearch = definition.SupportsSearch; - resource.Capabilities = definition.Capabilities; + resource.Capabilities = definition.Capabilities.ToResource(); resource.Protocol = definition.Protocol; resource.Privacy = definition.Privacy; resource.Priority = definition.Priority; diff --git a/src/Prowlarr.Api.V1/Search/SearchModule.cs b/src/Prowlarr.Api.V1/Search/SearchModule.cs index 81621448c..8208c21d6 100644 --- a/src/Prowlarr.Api.V1/Search/SearchModule.cs +++ b/src/Prowlarr.Api.V1/Search/SearchModule.cs @@ -6,6 +6,7 @@ using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Exceptions; using NzbDrone.Core.IndexerSearch; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; using Prowlarr.Http; @@ -49,7 +50,7 @@ namespace Prowlarr.Api.V1.Search { try { - var decisions = _nzbSearhService.Search(query, indexerIds, true); + var decisions = _nzbSearhService.Search(new NewznabRequest { q = query, t = "search" }, indexerIds, true).Releases; return MapDecisions(decisions); } diff --git a/src/Prowlarr.Api.V1/Search/SearchRequest.cs b/src/Prowlarr.Api.V1/Search/SearchRequest.cs index d8acfdfca..d320e5f76 100644 --- a/src/Prowlarr.Api.V1/Search/SearchRequest.cs +++ b/src/Prowlarr.Api.V1/Search/SearchRequest.cs @@ -6,5 +6,6 @@ namespace Prowlarr.Api.V1.Search { public List IndexerIds { get; set; } public string Query { get; set; } + public int[] Categories { get; set; } } } diff --git a/src/Prowlarr.Api.V1/Search/SearchResource.cs b/src/Prowlarr.Api.V1/Search/SearchResource.cs index 47b480297..e1a35ae0b 100644 --- a/src/Prowlarr.Api.V1/Search/SearchResource.cs +++ b/src/Prowlarr.Api.V1/Search/SearchResource.cs @@ -26,6 +26,7 @@ namespace Prowlarr.Api.V1.Search public string DownloadUrl { get; set; } public string InfoUrl { get; set; } public IEnumerable IndexerFlags { get; set; } + public ICollection Categories { get; set; } public string MagnetUrl { get; set; } public string InfoHash { get; set; } @@ -51,7 +52,7 @@ namespace Prowlarr.Api.V1.Search Age = releaseInfo.Age, AgeHours = releaseInfo.AgeHours, AgeMinutes = releaseInfo.AgeMinutes, - Size = releaseInfo.Size, + Size = releaseInfo.Size ?? 0, IndexerId = releaseInfo.IndexerId, Indexer = releaseInfo.Indexer, Title = releaseInfo.Title, @@ -60,6 +61,7 @@ namespace Prowlarr.Api.V1.Search CommentUrl = releaseInfo.CommentUrl, DownloadUrl = releaseInfo.DownloadUrl, InfoUrl = releaseInfo.InfoUrl, + Categories = releaseInfo.Category, //ReleaseWeight MagnetUrl = torrentInfo.MagnetUrl,