Lots of workings to search processing, UI Categories

pull/6/head
Qstick 4 years ago
parent 710b4f5ec6
commit 8e72c7153d

@ -4,48 +4,25 @@ import Label from 'Components/Label';
function CapabilitiesLabel(props) { function CapabilitiesLabel(props) {
const { const {
movieSearchAvailable, categories
tvSearchAvailable,
musicSearchAvailable,
bookSearchAvailable
} = props.capabilities; } = props.capabilities;
const filteredList = categories.filter((item) => item.id < 100000).map((item) => item.name).sort();
return ( return (
<span> <span>
{ {
bookSearchAvailable ? filteredList.map((category) => {
<Label> return (
{'Books'} <Label key={category}>
</Label> : {category}
null </Label>
} );
})
{
movieSearchAvailable ?
<Label>
{'Movies'}
</Label> :
null
}
{
musicSearchAvailable ?
<Label>
{'Music'}
</Label> :
null
}
{
tvSearchAvailable ?
<Label>
{'TV'}
</Label> :
null
} }
{ {
!tvSearchAvailable && !musicSearchAvailable && !movieSearchAvailable && !bookSearchAvailable ? filteredList.length === 0 ?
<Label> <Label>
{'None'} {'None'}
</Label> : </Label> :
@ -61,10 +38,7 @@ CapabilitiesLabel.propTypes = {
CapabilitiesLabel.defaultProps = { CapabilitiesLabel.defaultProps = {
capabilities: { capabilities: {
movieSearchAvailable: false, categories: []
tvSearchAvailable: false,
musicSearchAvailable: false,
bookSearchAvailable: false
} }
}; };

@ -21,7 +21,7 @@
.capabilities { .capabilities {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';
flex: 0 0 180px; flex: 0 0 350px;
} }
.added { .added {

@ -28,7 +28,7 @@
.capabilities { .capabilities {
composes: cell; composes: cell;
flex: 0 0 180px; flex: 0 0 350px;
} }
.added { .added {

@ -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 (
<Label>
{catName}
</Label>
);
}
CategoryLabel.propTypes = {
categories: PropTypes.arrayOf(PropTypes.object).isRequired
};
export default CategoryLabel;

@ -10,6 +10,7 @@
flex: 4 0 110px; flex: 4 0 110px;
} }
.category,
.indexer { .indexer {
composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css'; composes: headerCell from '~Components/Table/VirtualTableHeaderCell.css';

@ -17,6 +17,7 @@
flex: 4 0 110px; flex: 4 0 110px;
} }
.category,
.indexer { .indexer {
composes: cell; composes: cell;

@ -10,6 +10,7 @@ import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge'; import formatAge from 'Utilities/Number/formatAge';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate'; import translate from 'Utilities/String/translate';
import CategoryLabel from './CategoryLabel';
import Peers from './Peers'; import Peers from './Peers';
import ProtocolLabel from './ProtocolLabel'; import ProtocolLabel from './ProtocolLabel';
import styles from './SearchIndexRow.css'; import styles from './SearchIndexRow.css';
@ -22,6 +23,7 @@ class SearchIndexRow extends Component {
render() { render() {
const { const {
protocol, protocol,
categories,
age, age,
ageHours, ageHours,
ageMinutes, ageMinutes,
@ -132,6 +134,19 @@ class SearchIndexRow extends Component {
); );
} }
if (column.name === 'category') {
return (
<VirtualTableRowCell
key={column.name}
className={styles[column.name]}
>
<CategoryLabel
categories={categories}
/>
</VirtualTableRowCell>
);
}
if (column.name === 'indexerFlags') { if (column.name === 'indexerFlags') {
return ( return (
<VirtualTableRowCell <VirtualTableRowCell
@ -192,6 +207,7 @@ class SearchIndexRow extends Component {
SearchIndexRow.propTypes = { SearchIndexRow.propTypes = {
guid: PropTypes.string.isRequired, guid: PropTypes.string.isRequired,
categories: PropTypes.arrayOf(PropTypes.object).isRequired,
protocol: PropTypes.string.isRequired, protocol: PropTypes.string.isRequired,
age: PropTypes.number.isRequired, age: PropTypes.number.isRequired,
ageHours: PropTypes.number.isRequired, ageHours: PropTypes.number.isRequired,

@ -66,6 +66,12 @@ export const defaultState = {
isSortable: true, isSortable: true,
isVisible: true isVisible: true
}, },
{
name: 'category',
label: translate('Category'),
isSortable: true,
isVisible: true
},
{ {
name: 'indexerFlags', name: 'indexerFlags',
label: React.createElement(Icon, { name: icons.FLAG }), label: React.createElement(Icon, { name: icons.FLAG }),

@ -34,25 +34,6 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
_movieSearchCriteria.ImdbId = null; _movieSearchCriteria.ImdbId = null;
} }
[Test]
public void should_use_all_categories_for_feed()
{
var results = Subject.GetRecentRequests();
results.GetAllTiers().Should().HaveCount(1);
var page = results.GetAllTiers().First().First();
var encoding = HttpHeader.GetEncodingFromContentType(page.HttpRequest.Headers.ContentType);
var body = encoding.GetString(page.HttpRequest.ContentData);
var query = JsonConvert.DeserializeObject<TorrentQuery>(body);
query.Category.Should().HaveCount(2);
query.Username.Should().Be(Subject.Settings.Username);
query.Passkey.Should().Be(Subject.Settings.ApiKey);
}
[Test] [Test]
public void should_search_by_imdbid_if_supported() public void should_search_by_imdbid_if_supported()
{ {

@ -36,30 +36,6 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
.Returns(_capabilities); .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] [Test]
public void should_return_subsequent_pages() public void should_return_subsequent_pages()
{ {

@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Lidarr
Fields = schema.Fields, 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 == "apiPath").Value = "/newznab";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;

@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Radarr
Fields = schema.Fields, 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 == "apiPath").Value = "/newznab";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;

@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Readarr
Fields = schema.Fields, 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 == "apiPath").Value = "/newznab";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;

@ -108,7 +108,7 @@ namespace NzbDrone.Core.Applications.Sonarr
Fields = schema.Fields, 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 == "apiPath").Value = "/newznab";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;

@ -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
{
}
}

@ -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
{
}
}

@ -3,10 +3,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
public class MovieSearchCriteria : SearchCriteriaBase public class MovieSearchCriteria : SearchCriteriaBase
{ {
public string ImdbId { get; set; } public string ImdbId { get; set; }
public int TmdbId { get; set; } public int? TmdbId { get; set; }
public override string ToString() public int? Year { get; set; }
{ public int? TraktId { get; set; }
return string.Format("[{0}]", ImdbId);
}
} }
} }

@ -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; }
}
}

@ -16,5 +16,8 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
public List<int> IndexerIds { get; set; } public List<int> IndexerIds { get; set; }
public string SearchTerm { get; set; } public string SearchTerm { get; set; }
public int[] Categories { get; set; } public int[] Categories { get; set; }
public string SearchType { get; set; }
public int? Limit { get; set; }
public int? Offset { get; set; }
} }
} }

@ -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; }
}
}

@ -7,19 +7,21 @@ namespace NzbDrone.Core.IndexerSearch
public string q { get; set; } public string q { get; set; }
public string cat { get; set; } public string cat { get; set; }
public string imdbid { get; set; } public string imdbid { get; set; }
public string tmdbid { get; set; } public int? tmdbid { get; set; }
public string extended { get; set; } public string extended { get; set; }
public string limit { get; set; } public int? limit { get; set; }
public string offset { get; set; } public int? offset { get; set; }
public string rid { get; set; } public int? rid { get; set; }
public string tvdbid { get; set; } public int? tvmazeid { get; set; }
public string season { get; set; } public int? traktid { get; set; }
public string ep { get; set; } public int? tvdbid { get; set; }
public int? season { get; set; }
public int? ep { get; set; }
public string album { get; set; } public string album { get; set; }
public string artist { get; set; } public string artist { get; set; }
public string label { get; set; } public string label { get; set; }
public string track { get; set; } public string track { get; set; }
public string year { get; set; } public int? year { get; set; }
public string genre { get; set; } public string genre { get; set; }
public string author { get; set; } public string author { get; set; }
public string title { get; set; } public string title { get; set; }

@ -72,12 +72,14 @@ namespace NzbDrone.Core.IndexerSearch
new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory new XElement("guid", r.Guid), // GUID and (Link or Magnet) are mandatory
new XElement("prowlarrindexer", new XAttribute("id", r.IndexerId), r.Indexer), 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.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), new XElement("size", r.Size),
r.Category == null ? null : from c in r.Category select new XElement("category", c.Id),
new XElement( new XElement(
"enclosure", "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")), 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("rageid", r.TvRageId),
GetTorznabElement("thetvdb", r.TvdbId), GetTorznabElement("thetvdb", r.TvdbId),
GetTorznabElement("imdb", r.ImdbId.ToString("D7")), GetTorznabElement("imdb", r.ImdbId.ToString("D7")),

@ -16,7 +16,6 @@ namespace NzbDrone.Core.IndexerSearch
{ {
public interface ISearchForNzb public interface ISearchForNzb
{ {
List<ReleaseInfo> Search(string query, List<int> indexerIds, bool interactiveSearch);
NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool interactiveSearch); NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool interactiveSearch);
} }
@ -35,17 +34,70 @@ namespace NzbDrone.Core.IndexerSearch
_logger = logger; _logger = logger;
} }
public List<ReleaseInfo> Search(string query, List<int> indexerIds, bool interactiveSearch) public NewznabResults Search(NewznabRequest request, List<int> indexerIds, bool interactiveSearch)
{ {
var searchSpec = Get<MovieSearchCriteria>(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<int> indexerIds, bool interactiveSearch) private NewznabResults MovieSearch(NewznabRequest request, List<int> indexerIds, bool interactiveSearch)
{ {
var searchSpec = Get<MovieSearchCriteria>(request, indexerIds, interactiveSearch); var searchSpec = Get<MovieSearchCriteria>(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<int> indexerIds, bool interactiveSearch)
{
var searchSpec = Get<MusicSearchCriteria>(request, indexerIds, interactiveSearch);
return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) };
}
private NewznabResults TvSearch(NewznabRequest request, List<int> indexerIds, bool interactiveSearch)
{
var searchSpec = Get<TvSearchCriteria>(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<int> indexerIds, bool interactiveSearch)
{
var searchSpec = Get<TvSearchCriteria>(request, indexerIds, interactiveSearch);
return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) };
}
private NewznabResults BasicSearch(NewznabRequest request, List<int> indexerIds, bool interactiveSearch)
{
var searchSpec = Get<BasicSearchCriteria>(request, indexerIds, interactiveSearch);
return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) }; return new NewznabResults { Releases = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec) };
} }
@ -67,6 +119,9 @@ namespace NzbDrone.Core.IndexerSearch
} }
spec.SearchTerm = query.q; spec.SearchTerm = query.q;
spec.SearchType = query.t;
spec.Limit = query.limit;
spec.Offset = query.offset;
spec.IndexerIds = indexerIds; spec.IndexerIds = indexerIds;

@ -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); 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();
}
} }
} }

@ -2,6 +2,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AngleSharp.Dom; using AngleSharp.Dom;
@ -9,7 +10,6 @@ using Newtonsoft.Json.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.Cardigann namespace NzbDrone.Core.Indexers.Cardigann
{ {
@ -22,7 +22,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
protected string SiteLink { get; private set; } protected string SiteLink { get; private set; }
/* protected readonly List<CategoryMapping> categoryMapping = new List<CategoryMapping>(); */ protected readonly List<CategoryMapping> _categoryMapping = new List<CategoryMapping>();
protected readonly List<string> _defaultCategories = new List<string>();
protected readonly string[] OptionalFields = new string[] { "imdb", "rageid", "tvdbid", "banner" }; protected readonly string[] OptionalFields = new string[] { "imdb", "rageid", "tvdbid", "banner" };
@ -54,6 +55,70 @@ namespace NzbDrone.Core.Indexers.Cardigann
_logger = logger; _logger = logger;
SiteLink = definition.Links.First(); 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) protected IElement QuerySelector(IElement element, string selector)
@ -186,53 +251,36 @@ namespace NzbDrone.Core.Indexers.Cardigann
return variables; return variables;
} }
protected ICollection<int> MapTrackerCatToNewznab(string input) protected ICollection<IndexerCategory> MapTrackerCatToNewznab(string input)
{ {
if (input == null) if (string.IsNullOrWhiteSpace(input))
{
return new List<int>();
}
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
{ {
var trackerCategoryInt = int.Parse(input); return new List<IndexerCategory>();
cats.Add(trackerCategoryInt + 100000);
}
catch (FormatException)
{
// input is not an integer, continue
} }
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; return cats;
} }
public List<string> MapTorznabCapsToTrackers(int[] searchCategories, bool mapChildrenCatsToParent = false) public List<string> MapTorznabCapsToTrackers(int[] searchCategories, bool mapChildrenCatsToParent = false)
{ {
var queryCats = new List<string>();
if (searchCategories == null) if (searchCategories == null)
{ {
return queryCats; return new List<string>();
} }
foreach (var searchCat in searchCategories) var results = new List<string>();
{
var match = TorznabCatType.AllCats.FirstOrDefault(c => c.Id == searchCat);
if (match != null)
{
queryCats.Add(match.Name);
}
}
var result = _definition.Caps.Categorymappings results.AddRange(_categoryMapping
.Where(c => queryCats.Contains(c.cat)) .Where(c => searchCategories.Contains(c.NewzNabCategory))
.Select(mapping => mapping.id).Distinct().ToList(); .Select(mapping => mapping.TrackerCategory).Distinct().ToList());
return result; return results;
} }
protected delegate string TemplateTextModifier(string str); protected delegate string TemplateTextModifier(string str);

@ -472,13 +472,14 @@ namespace NzbDrone.Core.Indexers.Cardigann
result.AddRange(releases.Select(x => new TorrentInfo result.AddRange(releases.Select(x => new TorrentInfo
{ {
PublishDate = x.PublishDate,
Guid = x.Guid.ToString(), Guid = x.Guid.ToString(),
Title = x.Title, Title = x.Title,
Size = x.Size.Value, Size = x.Size.Value,
DownloadUrl = x.Link?.ToString(), DownloadUrl = x.Link.AbsoluteUri,
CommentUrl = x.Comments?.ToString(), CommentUrl = x.Comments?.ToString(),
InfoUrl = x.Link?.ToString(), InfoUrl = x.Link?.ToString(),
MagnetUrl = x.MagnetUri?.ToString(), MagnetUrl = x.MagnetUri?.AbsoluteUri,
InfoHash = x.InfoHash, InfoHash = x.InfoHash,
Seeders = (int?)x.Seeders, Seeders = (int?)x.Seeders,
Peers = (int?)x.Peers, Peers = (int?)x.Peers,

@ -11,7 +11,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
public Uri Link { get; set; } public Uri Link { get; set; }
public Uri Comments { get; set; } public Uri Comments { get; set; }
public DateTime PublishDate { get; set; } public DateTime PublishDate { get; set; }
public ICollection<int> Category { get; set; } public ICollection<IndexerCategory> Category { get; set; }
public long? Size { get; set; } public long? Size { get; set; }
public long? Files { get; set; } public long? Files { get; set; }
public long? Grabs { get; set; } public long? Grabs { get; set; }

@ -10,8 +10,6 @@ namespace NzbDrone.Core.Indexers.Cardigann
{ {
public class CardigannRequestGenerator : CardigannBase, IIndexerRequestGenerator public class CardigannRequestGenerator : CardigannBase, IIndexerRequestGenerator
{ {
private List<string> _defaultCategories = new List<string>();
public CardigannRequestGenerator(CardigannDefinition definition, public CardigannRequestGenerator(CardigannDefinition definition,
CardigannSettings settings, CardigannSettings settings,
Logger logger) Logger logger)
@ -22,71 +20,134 @@ namespace NzbDrone.Core.Indexers.Cardigann
public Func<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, 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 pageableRequests = new IndexerPageableRequestChain();
var variables = GetBaseTemplateVariables(); var variables = GetQueryVariableDefaults(searchCriteria);
variables[".Query.Type"] = null; variables[".Query.Movie"] = null;
variables[".Query.Q"] = null; variables[".Query.Year"] = searchCriteria.Year;
variables[".Query.Categories"] = null; variables[".Query.IMDBID"] = searchCriteria.ImdbId;
variables[".Query.IMDBID"] = null; variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId.Replace("tt", "");
variables[".Query.IMDBIDShort"] = null; variables[".Query.TMDBID"] = searchCriteria.TmdbId;
variables[".Query.TMDBID"] = null; variables[".Query.TraktID"] = searchCriteria.TraktId;
pageableRequests.Add(GetRequest(variables)); pageableRequests.Add(GetRequest(variables));
return pageableRequests; 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 pageableRequests = new IndexerPageableRequestChain();
var variables = GetBaseTemplateVariables(); var variables = GetQueryVariableDefaults(searchCriteria);
variables[".Query.Type"] = "movie"; variables[".Query.Series"] = null;
variables[".Query.Q"] = searchCriteria.SearchTerm; variables[".Query.Ep"] = searchCriteria.Ep;
variables[".Query.Categories"] = searchCriteria.Categories; variables[".Query.Season"] = searchCriteria.Season;
variables[".Query.IMDBID"] = searchCriteria.ImdbId; variables[".Query.IMDBID"] = searchCriteria.ImdbId;
variables[".Query.IMDBIDShort"] = null; variables[".Query.IMDBIDShort"] = searchCriteria.ImdbId.Replace("tt", "");
variables[".Query.TMDBID"] = searchCriteria.TmdbId; variables[".Query.TVDBID"] = searchCriteria.TvdbId;
variables[".Query.TVRageID"] = searchCriteria.RId;
variables[".Query.TVMazeID"] = searchCriteria.TvMazeId;
variables[".Query.TraktID"] = searchCriteria.TraktId;
pageableRequests.Add(GetRequest(variables)); pageableRequests.Add(GetRequest(variables));
return pageableRequests; return pageableRequests;
} }
private IEnumerable<IndexerRequest> GetRequest(Dictionary<string, object> 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<string, object> 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.Series"] = null;
variables[".Query.Ep"] = null; variables[".Query.Ep"] = null;
variables[".Query.Season"] = 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.TVDBID"] = null;
variables[".Query.TVRageID"] = null; variables[".Query.TVRageID"] = null;
variables[".Query.TVMazeID"] = null; variables[".Query.TVMazeID"] = null;
variables[".Query.TraktID"] = null; variables[".Query.TraktID"] = null;
//Music
variables[".Query.Album"] = null; variables[".Query.Album"] = null;
variables[".Query.Artist"] = null; variables[".Query.Artist"] = null;
variables[".Query.Label"] = null; variables[".Query.Label"] = null;
variables[".Query.Track"] = null; variables[".Query.Track"] = null;
variables[".Query.Episode"] = null; variables[".Query.Episode"] = null;
//Book
variables[".Query.Author"] = null; variables[".Query.Author"] = null;
variables[".Query.Title"] = null; variables[".Query.Title"] = null;
return variables;
}
private IEnumerable<IndexerRequest> GetRequest(Dictionary<string, object> variables)
{
var search = _definition.Search;
var mappedCategories = MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]); var mappedCategories = MapTorznabCapsToTrackers((int[])variables[".Query.Categories"]);
if (mappedCategories.Count == 0) if (mappedCategories.Count == 0)
{ {

@ -34,6 +34,26 @@ namespace NzbDrone.Core.Indexers.FileList
return pageableRequests; 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<IndexerRequest> GetRequest(string searchType, string parameters) private IEnumerable<IndexerRequest> GetRequest(string searchType, string parameters)
{ {
var categoriesQuery = string.Join(",", Settings.Categories.Distinct()); var categoriesQuery = string.Join(",", Settings.Categories.Distinct());

@ -12,13 +12,6 @@ namespace NzbDrone.Core.Indexers.HDBits
{ {
public HDBitsSettings Settings { get; set; } 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) public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{ {
var pageableRequests = new IndexerPageableRequestChain(); var pageableRequests = new IndexerPageableRequestChain();
@ -76,5 +69,25 @@ namespace NzbDrone.Core.Indexers.HDBits
yield return new IndexerRequest(request); 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();
}
} }
} }

@ -28,6 +28,26 @@ namespace NzbDrone.Core.Indexers.IPTorrents
yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss); 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -66,30 +66,38 @@ namespace NzbDrone.Core.Indexers.Newznab
} }
} }
public virtual IndexerPageableRequestChain GetRecentRequests() public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{ {
var pageableRequests = new IndexerPageableRequestChain(); var pageableRequests = new IndexerPageableRequestChain();
var capabilities = _capabilitiesProvider.GetCapabilities(Settings); AddMovieIdPageableRequests(pageableRequests, MaxPages, searchCriteria.Categories, searchCriteria);
// 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", ""));
}
return pageableRequests; 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(); 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; return pageableRequests;
} }
@ -153,15 +161,14 @@ namespace NzbDrone.Core.Indexers.Newznab
private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, IEnumerable<int> categories, string searchType, string parameters) private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, IEnumerable<int> 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()) if (Settings.ApiKey.IsNotNullOrWhiteSpace())
{ {
baseUrl += "&apikey=" + Settings.ApiKey; baseUrl += "&apikey=" + Settings.ApiKey;

@ -18,15 +18,6 @@ namespace NzbDrone.Core.Indexers.Nyaa
PageSize = 100; PageSize = 100;
} }
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(MaxPages, null));
return pageableRequests;
}
private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, string term) private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, string term)
{ {
var baseUrl = string.Format("{0}/?page=rss{1}", Settings.BaseUrl.TrimEnd('/'), Settings.AdditionalParameters); var baseUrl = string.Format("{0}/?page=rss{1}", Settings.BaseUrl.TrimEnd('/'), Settings.AdditionalParameters);
@ -65,6 +56,26 @@ namespace NzbDrone.Core.Indexers.Nyaa
return pageableRequests; 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -16,15 +16,6 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
public IHttpClient HttpClient { get; set; } public IHttpClient HttpClient { get; set; }
public Logger Logger { 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) public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{ {
var pageableRequests = new IndexerPageableRequestChain(); var pageableRequests = new IndexerPageableRequestChain();
@ -66,5 +57,25 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
yield return request; 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();
}
} }
} }

@ -116,6 +116,26 @@ namespace NzbDrone.Core.Indexers.Rarbg
yield return new IndexerRequest(requestBuilder.Build()); 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -80,6 +80,26 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
return pageableRequests; 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -39,6 +39,26 @@ namespace NzbDrone.Core.Indexers.TorrentRss
yield return request; 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -49,6 +49,46 @@ namespace NzbDrone.Core.Indexers
return FetchReleases(g => g.GetSearchRequests(searchCriteria)); return FetchReleases(g => g.GetSearchRequests(searchCriteria));
} }
public override IList<ReleaseInfo> Fetch(MusicSearchCriteria searchCriteria)
{
if (!SupportsSearch)
{
return new List<ReleaseInfo>();
}
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
}
public override IList<ReleaseInfo> Fetch(TvSearchCriteria searchCriteria)
{
if (!SupportsSearch)
{
return new List<ReleaseInfo>();
}
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
}
public override IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria)
{
if (!SupportsSearch)
{
return new List<ReleaseInfo>();
}
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
}
public override IList<ReleaseInfo> Fetch(BasicSearchCriteria searchCriteria)
{
if (!SupportsSearch)
{
return new List<ReleaseInfo>();
}
return FetchReleases(g => g.GetSearchRequests(searchCriteria));
}
protected IndexerPageableRequestChain GetRequestChain(SearchCriteriaBase searchCriteria = null) protected IndexerPageableRequestChain GetRequestChain(SearchCriteriaBase searchCriteria = null)
{ {
var generator = GetRequestGenerator(); var generator = GetRequestGenerator();
@ -254,7 +294,7 @@ namespace NzbDrone.Core.Indexers
protected virtual bool IsValidRelease(ReleaseInfo release) protected virtual bool IsValidRelease(ReleaseInfo release)
{ {
if (release.DownloadUrl.IsNullOrWhiteSpace()) if (release.DownloadUrl == null)
{ {
return false; return false;
} }

@ -15,5 +15,9 @@ namespace NzbDrone.Core.Indexers
IndexerPrivacy Privacy { get; } IndexerPrivacy Privacy { get; }
IList<ReleaseInfo> Fetch(MovieSearchCriteria searchCriteria); IList<ReleaseInfo> Fetch(MovieSearchCriteria searchCriteria);
IList<ReleaseInfo> Fetch(MusicSearchCriteria searchCriteria);
IList<ReleaseInfo> Fetch(TvSearchCriteria searchCriteria);
IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria);
IList<ReleaseInfo> Fetch(BasicSearchCriteria searchCriteria);
} }
} }

@ -7,6 +7,10 @@ namespace NzbDrone.Core.Indexers
public interface IIndexerRequestGenerator public interface IIndexerRequestGenerator
{ {
IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria); IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria);
Func<IDictionary<string, string>> GetCookies { get; set; } Func<IDictionary<string, string>> GetCookies { get; set; }
Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -66,6 +66,10 @@ namespace NzbDrone.Core.Indexers
protected TSettings Settings => (TSettings)Definition.Settings; protected TSettings Settings => (TSettings)Definition.Settings;
public abstract IList<ReleaseInfo> Fetch(MovieSearchCriteria searchCriteria); public abstract IList<ReleaseInfo> Fetch(MovieSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(MusicSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(TvSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(BookSearchCriteria searchCriteria);
public abstract IList<ReleaseInfo> Fetch(BasicSearchCriteria searchCriteria);
protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases) protected virtual IList<ReleaseInfo> CleanupReleases(IEnumerable<ReleaseInfo> releases)
{ {

@ -114,6 +114,7 @@ namespace NzbDrone.Core.Indexers
throw new Exception("In search mode 'search' only 'q' parameter is supported and it's mandatory"); throw new Exception("In search mode 'search' only 'q' parameter is supported and it's mandatory");
} }
SearchParams.Add(SearchParam.Q);
break; break;
case "tv-search": case "tv-search":
ParseTvSearchParams(entry.Value); ParseTvSearchParams(entry.Value);
@ -277,6 +278,13 @@ namespace NzbDrone.Core.Indexers
return string.Join(",", parameters); return string.Join(",", parameters);
} }
private string SupportedSearchParams()
{
var parameters = new List<string> { "q" }; // q is always enabled
return string.Join(",", parameters);
}
private string SupportedMovieSearchParams() private string SupportedMovieSearchParams()
{ {
var parameters = new List<string> { "q" }; // q is always enabled var parameters = new List<string> { "q" }; // q is always enabled
@ -350,7 +358,7 @@ namespace NzbDrone.Core.Indexers
new XElement("searching", new XElement("searching",
new XElement("search", new XElement("search",
new XAttribute("available", SearchAvailable ? "yes" : "no"), new XAttribute("available", SearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", "q")), new XAttribute("supportedParams", SupportedSearchParams())),
new XElement("tv-search", new XElement("tv-search",
new XAttribute("available", TvSearchAvailable ? "yes" : "no"), new XAttribute("available", TvSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedTvSearchParams())), new XAttribute("supportedParams", SupportedTvSearchParams())),

@ -28,6 +28,26 @@ namespace NzbDrone.Core.Indexers
return new IndexerPageableRequestChain(); 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<IDictionary<string, string>> GetCookies { get; set; } public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; } public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
} }

@ -9,7 +9,7 @@ namespace NzbDrone.Core.Parser.Model
{ {
public string Guid { get; set; } public string Guid { get; set; }
public string Title { get; set; } public string Title { get; set; }
public long Size { get; set; } public long? Size { get; set; }
public string DownloadUrl { get; set; } public string DownloadUrl { get; set; }
public string InfoUrl { get; set; } public string InfoUrl { get; set; }
public string CommentUrl { get; set; } public string CommentUrl { get; set; }
@ -28,7 +28,7 @@ namespace NzbDrone.Core.Parser.Model
public string Container { get; set; } public string Container { get; set; }
public string Codec { get; set; } public string Codec { get; set; }
public string Resolution { get; set; } public string Resolution { get; set; }
public ICollection<int> Category { get; set; } public ICollection<IndexerCategory> Category { get; set; }
public IndexerFlags IndexerFlags { get; set; } public IndexerFlags IndexerFlags { get; set; }

@ -1,3 +1,4 @@
using System;
using System.Text; using System.Text;
namespace NzbDrone.Core.Parser.Model namespace NzbDrone.Core.Parser.Model

@ -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<IndexerCategory> 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<IndexerCapabilityResource> ToResource(this IEnumerable<IndexerCapabilities> models)
{
return models.Select(ToResource).ToList();
}
}
}

@ -64,6 +64,7 @@ namespace Prowlarr.Api.V1.Indexers
return response; return response;
case "tvsearch": case "tvsearch":
case "music": case "music":
case "book":
case "movie": case "movie":
Response movieResponse = _nzbSearchService.Search(request, new List<int> { indexer.Id }, false).ToXml(); Response movieResponse = _nzbSearchService.Search(request, new List<int> { indexer.Id }, false).ToXml();
movieResponse.ContentType = "application/rss+xml"; movieResponse.ContentType = "application/rss+xml";

@ -17,7 +17,7 @@ namespace Prowlarr.Api.V1.Indexers
public bool SupportsSearch { get; set; } public bool SupportsSearch { get; set; }
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
public IndexerPrivacy Privacy { get; set; } public IndexerPrivacy Privacy { get; set; }
public IndexerCapabilities Capabilities { get; set; } public IndexerCapabilityResource Capabilities { get; set; }
public int Priority { get; set; } public int Priority { get; set; }
public DateTime Added { get; set; } public DateTime Added { get; set; }
} }
@ -59,7 +59,7 @@ namespace Prowlarr.Api.V1.Indexers
resource.EnableInteractiveSearch = definition.EnableInteractiveSearch; resource.EnableInteractiveSearch = definition.EnableInteractiveSearch;
resource.SupportsRss = definition.SupportsRss; resource.SupportsRss = definition.SupportsRss;
resource.SupportsSearch = definition.SupportsSearch; resource.SupportsSearch = definition.SupportsSearch;
resource.Capabilities = definition.Capabilities; resource.Capabilities = definition.Capabilities.ToResource();
resource.Protocol = definition.Protocol; resource.Protocol = definition.Protocol;
resource.Privacy = definition.Privacy; resource.Privacy = definition.Privacy;
resource.Priority = definition.Priority; resource.Priority = definition.Priority;

@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Exceptions; using NzbDrone.Core.Exceptions;
using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using Prowlarr.Http; using Prowlarr.Http;
@ -49,7 +50,7 @@ namespace Prowlarr.Api.V1.Search
{ {
try try
{ {
var decisions = _nzbSearhService.Search(query, indexerIds, true); var decisions = _nzbSearhService.Search(new NewznabRequest { q = query, t = "search" }, indexerIds, true).Releases;
return MapDecisions(decisions); return MapDecisions(decisions);
} }

@ -6,5 +6,6 @@ namespace Prowlarr.Api.V1.Search
{ {
public List<int> IndexerIds { get; set; } public List<int> IndexerIds { get; set; }
public string Query { get; set; } public string Query { get; set; }
public int[] Categories { get; set; }
} }
} }

@ -26,6 +26,7 @@ namespace Prowlarr.Api.V1.Search
public string DownloadUrl { get; set; } public string DownloadUrl { get; set; }
public string InfoUrl { get; set; } public string InfoUrl { get; set; }
public IEnumerable<string> IndexerFlags { get; set; } public IEnumerable<string> IndexerFlags { get; set; }
public ICollection<IndexerCategory> Categories { get; set; }
public string MagnetUrl { get; set; } public string MagnetUrl { get; set; }
public string InfoHash { get; set; } public string InfoHash { get; set; }
@ -51,7 +52,7 @@ namespace Prowlarr.Api.V1.Search
Age = releaseInfo.Age, Age = releaseInfo.Age,
AgeHours = releaseInfo.AgeHours, AgeHours = releaseInfo.AgeHours,
AgeMinutes = releaseInfo.AgeMinutes, AgeMinutes = releaseInfo.AgeMinutes,
Size = releaseInfo.Size, Size = releaseInfo.Size ?? 0,
IndexerId = releaseInfo.IndexerId, IndexerId = releaseInfo.IndexerId,
Indexer = releaseInfo.Indexer, Indexer = releaseInfo.Indexer,
Title = releaseInfo.Title, Title = releaseInfo.Title,
@ -60,6 +61,7 @@ namespace Prowlarr.Api.V1.Search
CommentUrl = releaseInfo.CommentUrl, CommentUrl = releaseInfo.CommentUrl,
DownloadUrl = releaseInfo.DownloadUrl, DownloadUrl = releaseInfo.DownloadUrl,
InfoUrl = releaseInfo.InfoUrl, InfoUrl = releaseInfo.InfoUrl,
Categories = releaseInfo.Category,
//ReleaseWeight //ReleaseWeight
MagnetUrl = torrentInfo.MagnetUrl, MagnetUrl = torrentInfo.MagnetUrl,

Loading…
Cancel
Save