From 518a75ea5c23e8bb8ac73cc5921190b28408ebda Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Wed, 6 Aug 2014 19:29:34 +0200 Subject: [PATCH] Downloading releases via Manual Search are now processed via unique id to allow caching more Release details. --- src/NzbDrone.Api/Indexers/ReleaseModule.cs | 24 +++++++++++--- src/NzbDrone.Api/Indexers/ReleaseResource.cs | 1 + .../Indexers/IndexerFetchService.cs | 2 ++ src/NzbDrone.Core/Indexers/RssParserBase.cs | 14 +++++---- src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs | 13 ++++---- .../Client/ClientBase.cs | 8 ++--- .../ReleaseIntegrationTest.cs | 31 +++++++++++++++++-- 7 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/NzbDrone.Api/Indexers/ReleaseModule.cs b/src/NzbDrone.Api/Indexers/ReleaseModule.cs index a64222b0b..4723d2728 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -14,6 +14,8 @@ using Omu.ValueInjecter; using System.Linq; using Nancy.ModelBinding; using NzbDrone.Api.Extensions; +using NzbDrone.Common.Cache; +using System.Threading; namespace NzbDrone.Api.Indexers { @@ -27,12 +29,15 @@ namespace NzbDrone.Api.Indexers private readonly IParsingService _parsingService; private readonly Logger _logger; + private readonly ICached _remoteEpisodeCache; + public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, ISearchForNzb nzbSearchService, IMakeDownloadDecision downloadDecisionMaker, IPrioritizeDownloadDecision prioritizeDownloadDecision, IDownloadService downloadService, IParsingService parsingService, + ICacheManager cacheManager, Logger logger) { _rssFetcherAndParser = rssFetcherAndParser; @@ -43,15 +48,24 @@ namespace NzbDrone.Api.Indexers _parsingService = parsingService; _logger = logger; GetResourceAll = GetReleases; - Post["/"] = x=> DownloadRelease(this.Bind()); + Post["/"] = x => DownloadRelease(this.Bind()); PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true); + + _remoteEpisodeCache = cacheManager.GetCache(GetType(), "remoteEpisodes"); } private Response DownloadRelease(ReleaseResource release) { - var remoteEpisode = _parsingService.Map(release.InjectTo(), release.TvRageId); - remoteEpisode.Release = release.InjectTo(); + var remoteEpisode = _remoteEpisodeCache.Find(release.Guid); + + if (remoteEpisode == null) + { + _logger.Debug("Couldn't find requested release in cache, cache timeout probably expired."); + + return new NotFoundResponse(); + } + _downloadService.DownloadReport(remoteEpisode); return release.AsResponse(); @@ -93,12 +107,14 @@ namespace NzbDrone.Api.Indexers return MapDecisions(prioritizedDecisions); } - private static List MapDecisions(IEnumerable decisions) + private List MapDecisions(IEnumerable decisions) { var result = new List(); foreach (var downloadDecision in decisions) { + _remoteEpisodeCache.Set(downloadDecision.RemoteEpisode.Release.Guid, downloadDecision.RemoteEpisode, TimeSpan.FromMinutes(30)); + var release = new ReleaseResource(); release.InjectFrom(downloadDecision.RemoteEpisode.Release); diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs index a07861eb0..ddf800331 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Api.Indexers { public class ReleaseResource : RestResource { + public String Guid { get; set; } public QualityModel Quality { get; set; } public Int32 QualityWeight { get; set; } public Int32 Age { get; set; } diff --git a/src/NzbDrone.Core/Indexers/IndexerFetchService.cs b/src/NzbDrone.Core/Indexers/IndexerFetchService.cs index 1bf6626d3..253ee8386 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -174,6 +174,8 @@ namespace NzbDrone.Core.Indexers } } + result = result.DistinctBy(v => v.Guid).ToList(); + result.ForEach(c => { c.Indexer = indexer.Definition.Name; diff --git a/src/NzbDrone.Core/Indexers/RssParserBase.cs b/src/NzbDrone.Core/Indexers/RssParserBase.cs index 0af4a50b5..befce4f50 100644 --- a/src/NzbDrone.Core/Indexers/RssParserBase.cs +++ b/src/NzbDrone.Core/Indexers/RssParserBase.cs @@ -47,8 +47,6 @@ namespace NzbDrone.Core.Indexers if (reportInfo != null) { - reportInfo.DownloadUrl = GetNzbUrl(item); - reportInfo.InfoUrl = GetNzbInfoUrl(item); result.Add(reportInfo); } } @@ -65,11 +63,10 @@ namespace NzbDrone.Core.Indexers private ReleaseInfo ParseFeedItem(XElement item, string url) { - var title = GetTitle(item); - var reportInfo = CreateNewReleaseInfo(); - reportInfo.Title = title; + reportInfo.Guid = GetGuid(item); + reportInfo.Title = GetTitle(item); reportInfo.PublishDate = GetPublishDate(item); reportInfo.DownloadUrl = GetNzbUrl(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); } - _logger.Trace("Parsed: {0}", item.Title()); + _logger.Trace("Parsed: {0}", reportInfo.Title); return PostProcessor(item, reportInfo); } + protected virtual String GetGuid(XElement item) + { + return item.TryGetValue("guid", Guid.NewGuid().ToString()); + } + protected virtual string GetTitle(XElement item) { return item.Title(); diff --git a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs index 3aa30a432..91fb3acdb 100644 --- a/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -5,14 +5,15 @@ namespace NzbDrone.Core.Parser.Model { public class ReleaseInfo { - public string Title { get; set; } - public long Size { get; set; } - public string DownloadUrl { get; set; } - public string InfoUrl { get; set; } - public string CommentUrl { get; set; } + public String Guid { get; set; } + public String Title { get; set; } + public Int64 Size { get; set; } + public String DownloadUrl { get; set; } + public String InfoUrl { get; set; } + public String CommentUrl { get; set; } public String Indexer { get; set; } public DownloadProtocol DownloadProtocol { get; set; } - public int TvRageId { get; set; } + public Int32 TvRageId { get; set; } public DateTime PublishDate { get; set; } public Int32 Age diff --git a/src/NzbDrone.Integration.Test/Client/ClientBase.cs b/src/NzbDrone.Integration.Test/Client/ClientBase.cs index 6e6fa91e8..68c738009 100644 --- a/src/NzbDrone.Integration.Test/Client/ClientBase.cs +++ b/src/NzbDrone.Integration.Test/Client/ClientBase.cs @@ -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(); request.AddBody(body); - return Post(request); + return Post(request, statusCode); } - public TResource Put(TResource body) + public TResource Put(TResource body, HttpStatusCode statusCode = HttpStatusCode.Accepted) { var request = BuildRequest(); request.AddBody(body); - return Put(request); + return Put(request, statusCode); } public TResource Get(int id, HttpStatusCode statusCode = HttpStatusCode.OK) diff --git a/src/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs b/src/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs index 884039de6..8a5433bb0 100644 --- a/src/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs @@ -1,7 +1,11 @@ -using System.Linq; -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; 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 { @@ -18,8 +22,31 @@ namespace NzbDrone.Integration.Test 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) { + releaseResource.Guid.Should().NotBeNullOrEmpty(); releaseResource.Age.Should().BeGreaterOrEqualTo(-1); releaseResource.Title.Should().NotBeNullOrWhiteSpace(); releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace();