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 @@ namespace NzbDrone.Common.Test.Http 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 @@ namespace NzbDrone.Common.Http 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 @@ namespace NzbDrone.Common.Http _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 @@ namespace NzbDrone.Common.Http 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.Collections.Generic; 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 @@ namespace NzbDrone.Core.Test.MetadataSource.SkyHook [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 FizzWare.NBuilder; 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 @@ namespace NzbDrone.Core.Test.TvTests 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 @@ namespace NzbDrone.Core.Test.TvTests 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 @@ namespace NzbDrone.Core.MetadataSource.SkyHook { 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 @@ namespace NzbDrone.Core.MetadataSource.SkyHook } } - 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 @@ namespace NzbDrone.Core.MetadataSource.SkyHook 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 NLog; 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 @@ namespace NzbDrone.Core.Tv 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;