From 9bcb6ff19aebfec82e80b5f4d25c98746abea6fa Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Wed, 12 Aug 2015 20:45:44 +0200 Subject: [PATCH] New: Sonarr can now update series to use another tvdbid in case when tvdb removes a duplicate and Skyhook detects it. --- .../Http/HttpClientFixture.cs | 19 +++++++--- src/NzbDrone.Common/Http/HttpClient.cs | 7 +--- src/NzbDrone.Common/Http/HttpRequest.cs | 6 ++++ .../SkyHook/SkyHookProxyFixture.cs | 5 ++- .../TvTests/RefreshSeriesServiceFixture.cs | 35 +++++++++++++++++-- .../Exceptions/SeriesNotFoundException.cs | 29 +++++++++++++++ .../MetadataSource/SkyHook/SkyHookProxy.cs | 22 +++++++++--- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + src/NzbDrone.Core/Tv/RefreshSeriesService.cs | 20 ++++++++++- 9 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 src/NzbDrone.Core/Exceptions/SeriesNotFoundException.cs diff --git a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs index 0ef1b75d5..df63ee85a 100644 --- a/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs +++ b/src/NzbDrone.Common.Test/Http/HttpClientFixture.cs @@ -83,16 +83,27 @@ public void should_throw_on_unsuccessful_status_codes(int statusCode) ExceptionVerification.IgnoreWarns(); } - [TestCase(HttpStatusCode.MovedPermanently)] - public void should_not_follow_redirects_when_not_in_production(HttpStatusCode statusCode) + [Test] + public void should_not_follow_redirects_when_not_in_production() { - var request = new HttpRequest("http://eu.httpbin.org/status/" + (int)statusCode); + var request = new HttpRequest("http://eu.httpbin.org/redirect/1"); - Subject.Get(request); + Subject.Get(request); ExceptionVerification.ExpectedErrors(1); } + [Test] + public void should_follow_redirects() + { + var request = new HttpRequest("http://eu.httpbin.org/redirect/1"); + request.AllowAutoRedirect = true; + + Subject.Get(request); + + ExceptionVerification.ExpectedErrors(0); + } + [Test] public void should_send_user_agent() { diff --git a/src/NzbDrone.Common/Http/HttpClient.cs b/src/NzbDrone.Common/Http/HttpClient.cs index da1d3379e..d12263f8a 100644 --- a/src/NzbDrone.Common/Http/HttpClient.cs +++ b/src/NzbDrone.Common/Http/HttpClient.cs @@ -61,11 +61,6 @@ public HttpResponse Execute(HttpRequest request) webRequest.AllowAutoRedirect = request.AllowAutoRedirect; webRequest.ContentLength = 0; - if (!RuntimeInfoBase.IsProduction) - { - webRequest.AllowAutoRedirect = false; - } - var stopWatch = Stopwatch.StartNew(); if (request.Headers != null) @@ -83,7 +78,7 @@ public HttpResponse Execute(HttpRequest request) _logger.Trace("{0} ({1:n0} ms)", response, stopWatch.ElapsedMilliseconds); - if (request.AllowAutoRedirect && !RuntimeInfoBase.IsProduction && + if (!RuntimeInfoBase.IsProduction && (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.MovedPermanently || response.StatusCode == HttpStatusCode.Found)) diff --git a/src/NzbDrone.Common/Http/HttpRequest.cs b/src/NzbDrone.Common/Http/HttpRequest.cs index 890099a91..6e8d1d5b3 100644 --- a/src/NzbDrone.Common/Http/HttpRequest.cs +++ b/src/NzbDrone.Common/Http/HttpRequest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Common.Http { @@ -16,6 +17,11 @@ public HttpRequest(string url, HttpAccept httpAccept = null) AllowAutoRedirect = true; Cookies = new Dictionary(); + if (!RuntimeInfoBase.IsProduction) + { + AllowAutoRedirect = false; + } + if (httpAccept != null) { Headers.Accept = httpAccept.Value; diff --git a/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs b/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs index 16a66a4f4..be15000f9 100644 --- a/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs +++ b/src/NzbDrone.Core.Test/MetadataSource/SkyHook/SkyHookProxyFixture.cs @@ -3,6 +3,7 @@ using System.Linq; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.Exceptions; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource.SkyHook; using NzbDrone.Core.Test.Framework; @@ -38,9 +39,7 @@ public void should_be_able_to_get_series_detail(int tvdbId, string title) [Test] public void getting_details_of_invalid_series() { - Assert.Throws(() => Subject.GetSeriesInfo(Int32.MaxValue)); - - ExceptionVerification.ExpectedWarns(1); + Assert.Throws(() => Subject.GetSeriesInfo(Int32.MaxValue)); } [Test] diff --git a/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs b/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs index d847c2ce7..aed900762 100644 --- a/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs +++ b/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs @@ -5,10 +5,12 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Exceptions; using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Commands; +using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.TvTests { @@ -34,14 +36,16 @@ public void Setup() Mocker.GetMock() .Setup(s => s.GetSeries(_series.Id)) .Returns(_series); - + Mocker.GetMock() + .Setup(s => s.GetSeriesInfo(It.IsAny())) + .Callback(p => { throw new SeriesNotFoundException(p); }); } private void GivenNewSeriesInfo(Series series) { Mocker.GetMock() - .Setup(s => s.GetSeriesInfo(It.IsAny())) + .Setup(s => s.GetSeriesInfo(_series.TvdbId)) .Returns(new Tuple>(series, new List())); } @@ -90,5 +94,32 @@ public void should_update_tvrage_id_if_changed() Mocker.GetMock() .Verify(v => v.UpdateSeries(It.Is(s => s.TvRageId == newSeriesInfo.TvRageId))); } + + [Test] + public void should_log_error_if_tvdb_id_not_found() + { + Subject.Execute(new RefreshSeriesCommand(_series.Id)); + + Mocker.GetMock() + .Verify(v => v.UpdateSeries(It.IsAny()), Times.Never()); + + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_update_if_tvdb_id_changed() + { + var newSeriesInfo = _series.JsonClone(); + newSeriesInfo.TvdbId = _series.TvdbId + 1; + + GivenNewSeriesInfo(newSeriesInfo); + + Subject.Execute(new RefreshSeriesCommand(_series.Id)); + + Mocker.GetMock() + .Verify(v => v.UpdateSeries(It.Is(s => s.TvdbId == newSeriesInfo.TvdbId))); + + ExceptionVerification.ExpectedWarns(1); + } } } diff --git a/src/NzbDrone.Core/Exceptions/SeriesNotFoundException.cs b/src/NzbDrone.Core/Exceptions/SeriesNotFoundException.cs new file mode 100644 index 000000000..7dc2e458e --- /dev/null +++ b/src/NzbDrone.Core/Exceptions/SeriesNotFoundException.cs @@ -0,0 +1,29 @@ +using System; +using NzbDrone.Common.Exceptions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Exceptions +{ + public class SeriesNotFoundException : NzbDroneException + { + public int TvdbSeriesId { get; set; } + + public SeriesNotFoundException(int tvdbSeriesId) + : base(string.Format("Series with tvdbid {0} was not found, it may have been removed from TheTVDB.", tvdbSeriesId)) + { + TvdbSeriesId = tvdbSeriesId; + } + + public SeriesNotFoundException(int tvdbSeriesId, string message, params object[] args) + : base(message, args) + { + TvdbSeriesId = tvdbSeriesId; + } + + public SeriesNotFoundException(int tvdbSeriesId, string message) + : base(message) + { + TvdbSeriesId = tvdbSeriesId; + } + } +} diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index 9af5a0cc7..13a9cf5e8 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Text.RegularExpressions; -using System.Web; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; +using NzbDrone.Core.Exceptions; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource.SkyHook.Resource; using NzbDrone.Core.Tv; @@ -31,8 +30,23 @@ public Tuple> GetSeriesInfo(int tvdbSeriesId) { var httpRequest = _requestBuilder.Build(tvdbSeriesId.ToString()); httpRequest.AddSegment("route", "shows"); + httpRequest.AllowAutoRedirect = true; + httpRequest.SuppressHttpError = true; var httpResponse = _httpClient.Get(httpRequest); + + if (httpResponse.HasHttpError) + { + if (httpResponse.StatusCode == HttpStatusCode.NotFound) + { + throw new SeriesNotFoundException(tvdbSeriesId); + } + else + { + throw new HttpException(httpRequest, httpResponse); + } + } + var episodes = httpResponse.Resource.Episodes.Select(MapEpisode); var series = MapSeries(httpResponse.Resource); @@ -71,7 +85,7 @@ public List SearchForNewSeries(string title) } } - var term = HttpUtility.UrlEncode((title.ToLower().Trim())); + var term = System.Web.HttpUtility.UrlEncode((title.ToLower().Trim())); var httpRequest = _requestBuilder.Build("?term={term}"); httpRequest.AddSegment("route", "search"); httpRequest.AddSegment("term", term); @@ -80,7 +94,7 @@ public List SearchForNewSeries(string title) return httpResponse.Resource.SelectList(MapSeries); } - catch (Common.Http.HttpException) + catch (HttpException) { throw new SkyHookException("Search for '{0}' failed. Unable to communicate with SkyHook.", title); } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index f10c85961..be79e3c29 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -431,6 +431,7 @@ + diff --git a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs index f44ccd5e5..ec7af2fe9 100644 --- a/src/NzbDrone.Core/Tv/RefreshSeriesService.cs +++ b/src/NzbDrone.Core/Tv/RefreshSeriesService.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DataAugmentation.DailySeries; +using NzbDrone.Core.Exceptions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; @@ -48,10 +49,27 @@ public RefreshSeriesService(IProvideSeriesInfo seriesInfo, private void RefreshSeriesInfo(Series series) { _logger.ProgressInfo("Updating Info for {0}", series.Title); - var tuple = _seriesInfo.GetSeriesInfo(series.TvdbId); + + Tuple> tuple; + + try + { + tuple = _seriesInfo.GetSeriesInfo(series.TvdbId); + } + catch (SeriesNotFoundException) + { + _logger.Error("Series '{0}' (tvdbid {1}) was not found, it may have been removed from TheTVDB.", series.Title, series.TvdbId); + return; + } var seriesInfo = tuple.Item1; + if (series.TvdbId != seriesInfo.TvdbId) + { + _logger.Warn("Series '{0}' (tvdbid {1}) was replaced with '{2}' (tvdbid {3}), because the original was a duplicate.", series.Title, series.TvdbId, seriesInfo.Title, seriesInfo.TvdbId); + series.TvdbId = seriesInfo.TvdbId; + } + series.Title = seriesInfo.Title; series.TitleSlug = seriesInfo.TitleSlug; series.TvRageId = seriesInfo.TvRageId;