Rewrote the RequestGenerator to support paging and other refactorings.

pull/3113/head
Taloth Saldono 9 years ago
parent 58b01b91d5
commit fc75783fbe

@ -1,14 +1,14 @@
using Moq; using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.TitansOfTv;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using System;
using System.Linq;
using FluentAssertions;
using NzbDrone.Core.Indexers.TitansOfTv;
namespace NzbDrone.Core.Test.IndexerTests.TitansOfTvTests namespace NzbDrone.Core.Test.IndexerTests.TitansOfTvTests
{ {
@ -21,12 +21,12 @@ namespace NzbDrone.Core.Test.IndexerTests.TitansOfTvTests
Subject.Definition = new IndexerDefinition Subject.Definition = new IndexerDefinition
{ {
Name = "TitansOfTV", Name = "TitansOfTV",
Settings = new TitansOfTvSettings { ApiKey = "abc", BaseUrl = "https://titansof.tv/api/torrents" } Settings = new TitansOfTvSettings { ApiKey = "abc", BaseUrl = "https://titansof.tv/api" }
}; };
} }
[Test] [Test]
public void should_parse_recent_feed_from_BroadcastheNet() public void should_parse_recent_feed_from_TitansOfTv()
{ {
var recentFeed = ReadAllText(@"Files/Indexers/TitansOfTv/RecentFeed.json"); var recentFeed = ReadAllText(@"Files/Indexers/TitansOfTv/RecentFeed.json");
@ -59,7 +59,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TitansOfTvTests
private void VerifyBackOff() private void VerifyBackOff()
{ {
// TODO How to detect (and implement) back-off logic. Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once());
} }
[Test] [Test]

@ -8,38 +8,30 @@ namespace NzbDrone.Core.Indexers.TitansOfTv
{ {
public class TitansOfTv : HttpIndexerBase<TitansOfTvSettings> public class TitansOfTv : HttpIndexerBase<TitansOfTvSettings>
{ {
public TitansOfTv(IHttpClient httpClient, IConfigService configService, IParsingService parsingService, Logger logger) public TitansOfTv(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
: base(httpClient, configService, parsingService, logger) : base(httpClient, indexerStatusService, configService, parsingService, logger)
{ {
} }
public override string Name public override string Name
{ {
get get { return "Titans of TV"; }
{
return "Titans of TV";
}
} }
public override DownloadProtocol Protocol public override DownloadProtocol Protocol { get { return DownloadProtocol.Torrent; } }
{ public override bool SupportsRss { get { return true; } }
get public override bool SupportsSearch { get { return true; } }
{ public override int PageSize { get { return 100; } }
return DownloadProtocol.Torrent;
}
}
public override IIndexerRequestGenerator GetRequestGenerator() public override IIndexerRequestGenerator GetRequestGenerator()
{ {
return new TitansOfTvRequestGenerator() { Settings = Settings }; return new TitansOfTvRequestGenerator() { Settings = Settings, PageSize = PageSize };
} }
public override IParseIndexerResponse GetParser() public override IParseIndexerResponse GetParser()
{ {
return new TitansOfTvParser(); return new TitansOfTvParser();
} }
public override Boolean SupportsSearch { get { return true; } }
} }
} }

@ -3,16 +3,26 @@ using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.TitansOfTv namespace NzbDrone.Core.Indexers.TitansOfTv
{ {
public class Result public class TitansOfTvApiResult
{
public string code { get; set; }
public int http_code { get; set; }
public int total { get; set; }
public int offset { get; set; }
public int limit { get; set; }
public List<TitansOfTvTorrent> results { get; set; }
}
public class TitansOfTvTorrent
{ {
public string id { get; set; } public string id { get; set; }
public string series_id { get; set; } public string series_id { get; set; }
public string episode_id { get; set; } public string episode_id { get; set; }
public string season_id { get; set; } public string season_id { get; set; }
public string seeders { get; set; } public int? seeders { get; set; }
public string leechers { get; set; } public int? leechers { get; set; }
public string size { get; set; } public long size { get; set; }
public string snatched { get; set; } public int? snatched { get; set; }
public int user_id { get; set; } public int user_id { get; set; }
public string anonymous { get; set; } public string anonymous { get; set; }
public string container { get; set; } public string container { get; set; }
@ -29,17 +39,10 @@ namespace NzbDrone.Core.Indexers.TitansOfTv
public string episode { get; set; } public string episode { get; set; }
public string series { get; set; } public string series { get; set; }
public string network { get; set; } public string network { get; set; }
public string mediainfo { get; set; }
public string download { get; set; } public string download { get; set; }
public string additional { get; set; }
public string episodeUrl { get; set; } public string episodeUrl { get; set; }
} public string commentUrl { get; set; }
public class ApiResult
{
public string code { get; set; }
public int http_code { get; set; }
public int total { get; set; }
public int offset { get; set; }
public int limit { get; set; }
public List<Result> results { get; set; }
} }
} }

@ -1,8 +1,8 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Newtonsoft.Json; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -33,20 +33,24 @@ namespace NzbDrone.Core.Indexers.TitansOfTv
} }
var content = indexerResponse.HttpResponse.Content; var content = indexerResponse.HttpResponse.Content;
var parsed = JsonConvert.DeserializeObject<ApiResult>(content); var parsed = Json.Deserialize<TitansOfTvApiResult>(content);
var protocol = indexerResponse.HttpRequest.Url.Scheme + ":"; var protocol = indexerResponse.HttpRequest.Url.Scheme + ":";
foreach (var parsedItem in parsed.results) foreach (var parsedItem in parsed.results)
{ {
var release = new TorrentInfo(); var release = new TorrentInfo();
release.Guid = String.Format("ToTV-{0}", parsedItem.id); release.Guid = string.Format("ToTV-{0}", parsedItem.id);
release.DownloadUrl = RegexProtocol.Replace(parsedItem.download, protocol); release.DownloadUrl = RegexProtocol.Replace(parsedItem.download, protocol);
release.InfoUrl = RegexProtocol.Replace(parsedItem.episodeUrl, protocol); release.InfoUrl = RegexProtocol.Replace(parsedItem.episodeUrl, protocol);
if (parsedItem.commentUrl.IsNotNullOrWhiteSpace())
{
release.CommentUrl = RegexProtocol.Replace(parsedItem.commentUrl, protocol);
}
release.DownloadProtocol = DownloadProtocol.Torrent; release.DownloadProtocol = DownloadProtocol.Torrent;
release.Title = parsedItem.release_name; release.Title = parsedItem.release_name;
release.Size = Convert.ToInt64(parsedItem.size); release.Size = parsedItem.size;
release.Seeders = Convert.ToInt32(parsedItem.seeders); release.Seeders = parsedItem.seeders;
release.Peers = Convert.ToInt32(parsedItem.leechers) + release.Seeders; release.Peers = parsedItem.leechers + release.Seeders;
release.PublishDate = parsedItem.created_at; release.PublishDate = parsedItem.created_at;
results.Add(release); results.Add(release);
} }

@ -1,111 +1,119 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http; using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.TitansOfTv namespace NzbDrone.Core.Indexers.TitansOfTv
{ {
public class TitansOfTvRequestGenerator : IIndexerRequestGenerator public class TitansOfTvRequestGenerator : IIndexerRequestGenerator
{ {
public int MaxPages { get; set; }
public int PageSize { get; set; }
public TitansOfTvSettings Settings { get; set; } public TitansOfTvSettings Settings { get; set; }
public TitansOfTvRequestGenerator()
{
MaxPages = 30;
PageSize = 100;
}
public IList<IEnumerable<IndexerRequest>> GetRecentRequests() public IList<IEnumerable<IndexerRequest>> GetRecentRequests()
{ {
var pageableRequests = new List<IEnumerable<IndexerRequest>>(); var pageableRequests = new List<IEnumerable<IndexerRequest>>();
var innerList = new List<IndexerRequest>();
var httpRequest = BuildHttpRequest(GetBaseUrl());
innerList.Add(new IndexerRequest(httpRequest)); pageableRequests.AddIfNotNull(GetPagedRequests(MaxPages));
pageableRequests.Add(innerList);
return pageableRequests; return pageableRequests;
} }
private HttpRequest BuildHttpRequest(string url) public IList<IEnumerable<IndexerRequest>> GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria)
{ {
var httpRequest = new HttpRequest(url, HttpAccept.Json); var pageableRequests = new List<IEnumerable<IndexerRequest>>();
httpRequest.Headers["X-Authorization"] = Settings.ApiKey;
return httpRequest;
}
public IList<IEnumerable<IndexerRequest>> GetSearchRequests(IndexerSearch.Definitions.SingleEpisodeSearchCriteria searchCriteria)
{
var url = GetBaseUrl() + "&series_id={series}&episode={episode}";
var requests = new List<IEnumerable<IndexerRequest>>();
var innerList = new List<IndexerRequest>();
requests.Add(innerList);
var httpRequest = BuildHttpRequest(url); pageableRequests.AddIfNotNull(GetPagedRequests(MaxPages,
var episodeString = String.Format("S{0:00}E{1:00}", searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); series_id: searchCriteria.Series.TvdbId,
httpRequest.AddSegment("series", searchCriteria.Series.TvdbId.ToString(CultureInfo.InvariantCulture)); episode: string.Format("S{0:00}E{1:00}", searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber)));
httpRequest.AddSegment("episode", episodeString);
var request = new IndexerRequest(httpRequest); pageableRequests.AddIfNotNull(GetPagedRequests(MaxPages,
innerList.Add(request); series_id: searchCriteria.Series.TvdbId,
season: string.Format("Season {0:00}", searchCriteria.SeasonNumber)));
return requests; return pageableRequests;
} }
public IList<IEnumerable<IndexerRequest>> GetSearchRequests(IndexerSearch.Definitions.SeasonSearchCriteria searchCriteria) public IList<IEnumerable<IndexerRequest>> GetSearchRequests(SeasonSearchCriteria searchCriteria)
{ {
var url = GetBaseUrl() + "&series_id={series}&season={season}"; var pageableRequests = new List<IEnumerable<IndexerRequest>>();
var requests = new List<IEnumerable<IndexerRequest>>();
var innerList = new List<IndexerRequest>();
requests.Add(innerList);
var httpRequest = BuildHttpRequest(url);
var seasonString = String.Format("Season {0:00}", searchCriteria.SeasonNumber);
httpRequest.AddSegment("series", searchCriteria.Series.TvdbId.ToString(CultureInfo.InvariantCulture));
httpRequest.AddSegment("season", seasonString);
var request = new IndexerRequest(httpRequest);
innerList.Add(request);
httpRequest = BuildHttpRequest(url); // TODO: Search for all episodes?!?
seasonString = String.Format("Season {0}", searchCriteria.SeasonNumber);
httpRequest.AddSegment("series", searchCriteria.Series.TvdbId.ToString(CultureInfo.InvariantCulture));
httpRequest.AddSegment("season", seasonString);
request = new IndexerRequest(httpRequest); pageableRequests.AddIfNotNull(GetPagedRequests(MaxPages,
innerList.Add(request); series_id: searchCriteria.Series.TvdbId,
season: string.Format("Season {0:00}", searchCriteria.SeasonNumber)));
return requests; return pageableRequests;
} }
public IList<IEnumerable<IndexerRequest>> GetSearchRequests(IndexerSearch.Definitions.DailyEpisodeSearchCriteria searchCriteria) public IList<IEnumerable<IndexerRequest>> GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria)
{ {
var url = GetBaseUrl() + "&series_id={series}&air_date={air_date}"; var pageableRequests = new List<IEnumerable<IndexerRequest>>();
var requests = new List<IEnumerable<IndexerRequest>>();
var innerList = new List<IndexerRequest>();
requests.Add(innerList);
var httpRequest = BuildHttpRequest(url);
var airDate = searchCriteria.AirDate.ToString("yyyy-MM-dd");
httpRequest.AddSegment("series", searchCriteria.Series.TvdbId.ToString(CultureInfo.InvariantCulture));
httpRequest.AddSegment("air_date", airDate);
var request = new IndexerRequest(httpRequest); pageableRequests.AddIfNotNull(GetPagedRequests(MaxPages,
innerList.Add(request); series_id: searchCriteria.Series.TvdbId,
air_date: searchCriteria.AirDate));
return requests; return pageableRequests;
} }
public IList<IEnumerable<IndexerRequest>> GetSearchRequests(IndexerSearch.Definitions.AnimeEpisodeSearchCriteria searchCriteria) public IList<IEnumerable<IndexerRequest>> GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria)
{ {
return new List<IEnumerable<IndexerRequest>>(); return new List<IEnumerable<IndexerRequest>>();
} }
public IList<IEnumerable<IndexerRequest>> GetSearchRequests(IndexerSearch.Definitions.SpecialEpisodeSearchCriteria searchCriteria) public IList<IEnumerable<IndexerRequest>> GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria)
{ {
return new List<IEnumerable<IndexerRequest>>(); return new List<IEnumerable<IndexerRequest>>();
} }
private string GetBaseUrl() private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, int? series_id = null, string episode = null, string season = null, DateTime? air_date = null)
{ {
return Settings.BaseUrl + "?limit=100"; var pageSize = PageSize;
if (pageSize == 0)
{
maxPages = 1;
pageSize = 100;
}
for (var page = 0; page < maxPages; page++)
{
var request = new IndexerRequest(string.Format("{0}/torrents?offset={1}&limit={2}", Settings.BaseUrl.TrimEnd('/'), page * pageSize, pageSize), HttpAccept.Json);
request.HttpRequest.Headers.Add("X-Authorization", Settings.ApiKey);
if (series_id.HasValue)
{
request.HttpRequest.AddQueryParam("series_id", series_id.Value.ToString(CultureInfo.InvariantCulture));
}
if (season != null)
{
request.HttpRequest.AddQueryParam("season", season);
}
if (episode != null)
{
request.HttpRequest.AddQueryParam("episode", episode);
}
if (air_date.HasValue)
{
request.HttpRequest.AddQueryParam("air_date", air_date.Value.ToString("yyyy-MM-dd"));
}
yield return request;
}
} }
} }
} }

@ -5,8 +5,7 @@ using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.TitansOfTv namespace NzbDrone.Core.Indexers.TitansOfTv
{ {
public class TitansOfTvSettingsValidator : AbstractValidator<TitansOfTvSettings>
public class TitansOfTvSettingsValidator : AbstractValidator<TitansOfTvSettings>
{ {
public TitansOfTvSettingsValidator() public TitansOfTvSettingsValidator()
{ {
@ -20,7 +19,7 @@ namespace NzbDrone.Core.Indexers.TitansOfTv
public TitansOfTvSettings() public TitansOfTvSettings()
{ {
BaseUrl = "http://titansof.tv/api/torrents"; BaseUrl = "http://titansof.tv/api";
} }
[FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")] [FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")]

Loading…
Cancel
Save