Downloading releases via Manual Search are now processed via unique id to allow caching more Release details.

This commit is contained in:
Taloth Saldono 2014-08-06 19:29:34 +02:00
parent 9b5bf374a6
commit 518a75ea5c
7 changed files with 71 additions and 22 deletions

View File

@ -14,6 +14,8 @@ using Omu.ValueInjecter;
using System.Linq; using System.Linq;
using Nancy.ModelBinding; using Nancy.ModelBinding;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NzbDrone.Common.Cache;
using System.Threading;
namespace NzbDrone.Api.Indexers namespace NzbDrone.Api.Indexers
{ {
@ -27,12 +29,15 @@ namespace NzbDrone.Api.Indexers
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
private readonly Logger _logger; private readonly Logger _logger;
private readonly ICached<RemoteEpisode> _remoteEpisodeCache;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
ISearchForNzb nzbSearchService, ISearchForNzb nzbSearchService,
IMakeDownloadDecision downloadDecisionMaker, IMakeDownloadDecision downloadDecisionMaker,
IPrioritizeDownloadDecision prioritizeDownloadDecision, IPrioritizeDownloadDecision prioritizeDownloadDecision,
IDownloadService downloadService, IDownloadService downloadService,
IParsingService parsingService, IParsingService parsingService,
ICacheManager cacheManager,
Logger logger) Logger logger)
{ {
_rssFetcherAndParser = rssFetcherAndParser; _rssFetcherAndParser = rssFetcherAndParser;
@ -43,15 +48,24 @@ namespace NzbDrone.Api.Indexers
_parsingService = parsingService; _parsingService = parsingService;
_logger = logger; _logger = logger;
GetResourceAll = GetReleases; GetResourceAll = GetReleases;
Post["/"] = x=> DownloadRelease(this.Bind<ReleaseResource>()); Post["/"] = x => DownloadRelease(this.Bind<ReleaseResource>());
PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true); PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true);
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
} }
private Response DownloadRelease(ReleaseResource release) private Response DownloadRelease(ReleaseResource release)
{ {
var remoteEpisode = _parsingService.Map(release.InjectTo<ParsedEpisodeInfo>(), release.TvRageId); var remoteEpisode = _remoteEpisodeCache.Find(release.Guid);
remoteEpisode.Release = release.InjectTo<ReleaseInfo>();
if (remoteEpisode == null)
{
_logger.Debug("Couldn't find requested release in cache, cache timeout probably expired.");
return new NotFoundResponse();
}
_downloadService.DownloadReport(remoteEpisode); _downloadService.DownloadReport(remoteEpisode);
return release.AsResponse(); return release.AsResponse();
@ -93,12 +107,14 @@ namespace NzbDrone.Api.Indexers
return MapDecisions(prioritizedDecisions); return MapDecisions(prioritizedDecisions);
} }
private static List<ReleaseResource> MapDecisions(IEnumerable<DownloadDecision> decisions) private List<ReleaseResource> MapDecisions(IEnumerable<DownloadDecision> decisions)
{ {
var result = new List<ReleaseResource>(); var result = new List<ReleaseResource>();
foreach (var downloadDecision in decisions) foreach (var downloadDecision in decisions)
{ {
_remoteEpisodeCache.Set(downloadDecision.RemoteEpisode.Release.Guid, downloadDecision.RemoteEpisode, TimeSpan.FromMinutes(30));
var release = new ReleaseResource(); var release = new ReleaseResource();
release.InjectFrom(downloadDecision.RemoteEpisode.Release); release.InjectFrom(downloadDecision.RemoteEpisode.Release);

View File

@ -9,6 +9,7 @@ namespace NzbDrone.Api.Indexers
{ {
public class ReleaseResource : RestResource public class ReleaseResource : RestResource
{ {
public String Guid { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public Int32 QualityWeight { get; set; } public Int32 QualityWeight { get; set; }
public Int32 Age { get; set; } public Int32 Age { get; set; }

View File

@ -174,6 +174,8 @@ namespace NzbDrone.Core.Indexers
} }
} }
result = result.DistinctBy(v => v.Guid).ToList();
result.ForEach(c => result.ForEach(c =>
{ {
c.Indexer = indexer.Definition.Name; c.Indexer = indexer.Definition.Name;

View File

@ -47,8 +47,6 @@ namespace NzbDrone.Core.Indexers
if (reportInfo != null) if (reportInfo != null)
{ {
reportInfo.DownloadUrl = GetNzbUrl(item);
reportInfo.InfoUrl = GetNzbInfoUrl(item);
result.Add(reportInfo); result.Add(reportInfo);
} }
} }
@ -65,11 +63,10 @@ namespace NzbDrone.Core.Indexers
private ReleaseInfo ParseFeedItem(XElement item, string url) private ReleaseInfo ParseFeedItem(XElement item, string url)
{ {
var title = GetTitle(item);
var reportInfo = CreateNewReleaseInfo(); var reportInfo = CreateNewReleaseInfo();
reportInfo.Title = title; reportInfo.Guid = GetGuid(item);
reportInfo.Title = GetTitle(item);
reportInfo.PublishDate = GetPublishDate(item); reportInfo.PublishDate = GetPublishDate(item);
reportInfo.DownloadUrl = GetNzbUrl(item); reportInfo.DownloadUrl = GetNzbUrl(item);
reportInfo.InfoUrl = GetNzbInfoUrl(item); reportInfo.InfoUrl = GetNzbInfoUrl(item);
@ -83,11 +80,16 @@ namespace NzbDrone.Core.Indexers
throw new SizeParsingException("Unable to parse size from: {0} [{1}]", reportInfo.Title, url); throw new SizeParsingException("Unable to parse size from: {0} [{1}]", reportInfo.Title, url);
} }
_logger.Trace("Parsed: {0}", item.Title()); _logger.Trace("Parsed: {0}", reportInfo.Title);
return PostProcessor(item, reportInfo); return PostProcessor(item, reportInfo);
} }
protected virtual String GetGuid(XElement item)
{
return item.TryGetValue("guid", Guid.NewGuid().ToString());
}
protected virtual string GetTitle(XElement item) protected virtual string GetTitle(XElement item)
{ {
return item.Title(); return item.Title();

View File

@ -5,14 +5,15 @@ namespace NzbDrone.Core.Parser.Model
{ {
public class ReleaseInfo public class ReleaseInfo
{ {
public string Title { get; set; } public String Guid { get; set; }
public long Size { get; set; } public String Title { get; set; }
public string DownloadUrl { get; set; } public Int64 Size { get; set; }
public string InfoUrl { get; set; } public String DownloadUrl { get; set; }
public string CommentUrl { get; set; } public String InfoUrl { get; set; }
public String CommentUrl { get; set; }
public String Indexer { get; set; } public String Indexer { get; set; }
public DownloadProtocol DownloadProtocol { get; set; } public DownloadProtocol DownloadProtocol { get; set; }
public int TvRageId { get; set; } public Int32 TvRageId { get; set; }
public DateTime PublishDate { get; set; } public DateTime PublishDate { get; set; }
public Int32 Age public Int32 Age

View File

@ -48,18 +48,18 @@ namespace NzbDrone.Integration.Test.Client
} }
public TResource Post(TResource body) public TResource Post(TResource body, HttpStatusCode statusCode = HttpStatusCode.Created)
{ {
var request = BuildRequest(); var request = BuildRequest();
request.AddBody(body); request.AddBody(body);
return Post<TResource>(request); return Post<TResource>(request, statusCode);
} }
public TResource Put(TResource body) public TResource Put(TResource body, HttpStatusCode statusCode = HttpStatusCode.Accepted)
{ {
var request = BuildRequest(); var request = BuildRequest();
request.AddBody(body); request.AddBody(body);
return Put<TResource>(request); return Put<TResource>(request, statusCode);
} }
public TResource Get(int id, HttpStatusCode statusCode = HttpStatusCode.OK) public TResource Get(int id, HttpStatusCode statusCode = HttpStatusCode.OK)

View File

@ -1,7 +1,11 @@
using System.Linq; using FluentAssertions;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Api.Indexers; using NzbDrone.Api.Indexers;
using NzbDrone.Api.Series;
using NzbDrone.Test.Common;
using System.Linq;
using System.Net;
using System.Threading;
namespace NzbDrone.Integration.Test namespace NzbDrone.Integration.Test
{ {
@ -18,8 +22,31 @@ namespace NzbDrone.Integration.Test
releases.Should().OnlyContain(c => BeValidRelease(c)); releases.Should().OnlyContain(c => BeValidRelease(c));
} }
[Test]
public void should_reject_unknown_release()
{
var result = Releases.Post(new ReleaseResource { Guid = "unknown" }, HttpStatusCode.NotFound);
result.Id.Should().Be(0);
}
[Test]
public void should_accept_request_with_only_guid_supplied()
{
var releases = Releases.All();
// InternalServerError is caused by the Release being invalid for download (no Series).
// But if it didn't accept it, it would return NotFound.
// TODO: Maybe we should create a full mock Newznab server endpoint.
//var result = Releases.Post(new ReleaseResource { Guid = releases.First().Guid });
//result.Guid.Should().Be(releases.First().Guid);
var result = Releases.Post(new ReleaseResource { Guid = releases.First().Guid }, HttpStatusCode.InternalServerError);
}
private bool BeValidRelease(ReleaseResource releaseResource) private bool BeValidRelease(ReleaseResource releaseResource)
{ {
releaseResource.Guid.Should().NotBeNullOrEmpty();
releaseResource.Age.Should().BeGreaterOrEqualTo(-1); releaseResource.Age.Should().BeGreaterOrEqualTo(-1);
releaseResource.Title.Should().NotBeNullOrWhiteSpace(); releaseResource.Title.Should().NotBeNullOrWhiteSpace();
releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace(); releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace();