From 554924a52266a770a390b0a200f51cb56a815483 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sun, 24 Feb 2013 11:18:48 -0800 Subject: [PATCH] post grab notification and updates are now using the new EventAggregator --- .../EventingTests/EventAggregatorTests.cs | 50 +++-- NzbDrone.Common/Eventing/EventAggregator.cs | 2 +- NzbDrone.Common/Eventing/IEvent.cs | 8 + NzbDrone.Common/Eventing/IEventAggregator.cs | 2 +- NzbDrone.Common/Eventing/IHandle.cs | 6 +- NzbDrone.Common/NzbDrone.Common.csproj | 1 + .../Configuration/ConfigCachingFixture.cs | 34 ++++ .../Configuration/ConfigServiceFixture.cs | 183 +++++++++++++++++ NzbDrone.Core.Test/EpisodeParseResultTest.cs | 120 +++++++++++ .../MoveEpisodeFileFixture.cs | 9 +- .../ContainsRecentEpisode.cs | 4 +- .../DownloadProviderFixture.cs | 192 ++---------------- .../SearchHistoryProviderTest.cs | 1 + .../SearchTests/ProcessResultsFixture.cs | 1 + .../ProviderTests/SearchTests/TestSearch.cs | 1 + .../EpisodeProviderTest.cs | 17 +- .../AlreadyInQueueSpecification.cs | 1 + NzbDrone.Core/Download/DownloadProvider.cs | 81 ++++++++ NzbDrone.Core/Download/EpisodeGrabbedEvent.cs | 15 ++ NzbDrone.Core/History/HistoryService.cs | 37 +++- NzbDrone.Core/Jobs/RssSyncJob.cs | 1 + NzbDrone.Core/Model/EpisodeParseResult.cs | 64 +++++- NzbDrone.Core/NzbDrone.Core.csproj | 3 +- NzbDrone.Core/Providers/DiskScanProvider.cs | 10 +- NzbDrone.Core/Providers/DownloadProvider.cs | 174 ---------------- .../Providers/ExternalNotification/Smtp.cs | 2 - .../Providers/ExternalNotificationProvider.cs | 21 +- .../Providers/NotificationProvider.cs | 1 - .../Providers/Search/DailyEpisodeSearch.cs | 1 + .../Providers/Search/EpisodeSearch.cs | 1 + .../Providers/Search/PartialSeasonSearch.cs | 1 + NzbDrone.Core/Providers/Search/SearchBase.cs | 1 + .../Providers/SearchHistoryProvider.cs | 1 + NzbDrone.Core/Providers/SignalRProvider.cs | 18 +- NzbDrone.Core/Providers/SmtpProvider.cs | 43 +--- NzbDrone.Core/Providers/StatsProvider.cs | 3 - NzbDrone.Core/Tv/EpisodeService.cs | 23 ++- NzbDrone.Core/Tv/Events/SeriesAddedEvent.cs | 6 +- NzbDrone.Test.Common/TestBase.cs | 11 + NzbDrone.ncrunchsolution | 2 +- 40 files changed, 670 insertions(+), 482 deletions(-) create mode 100644 NzbDrone.Common/Eventing/IEvent.cs create mode 100644 NzbDrone.Core.Test/Configuration/ConfigCachingFixture.cs create mode 100644 NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs create mode 100644 NzbDrone.Core/Download/DownloadProvider.cs create mode 100644 NzbDrone.Core/Download/EpisodeGrabbedEvent.cs delete mode 100644 NzbDrone.Core/Providers/DownloadProvider.cs diff --git a/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs index ccb6e0275..2f2f19058 100644 --- a/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs +++ b/NzbDrone.Common.Test/EventingTests/EventAggregatorTests.cs @@ -13,38 +13,56 @@ public class ServiceNameFixture : TestBase [Test] public void should_publish_event_to_handlers() { - var intHandler = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { intHandler.Object }); - aggregator.Publish(12); + var eventA = new EventA(); + - intHandler.Verify(c => c.Handle(12), Times.Once()); + var intHandler = new Mock>(); + var aggregator = new EventAggregator(TestLogger, new List { intHandler.Object }); + aggregator.Publish(eventA); + + intHandler.Verify(c => c.Handle(eventA), Times.Once()); } [Test] public void should_publish_to_more_than_one_handler() { - var intHandler1 = new Mock>(); - var intHandler2 = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { intHandler1.Object, intHandler2.Object }); - aggregator.Publish(12); + var eventA = new EventA(); - intHandler1.Verify(c => c.Handle(12), Times.Once()); - intHandler2.Verify(c => c.Handle(12), Times.Once()); + var intHandler1 = new Mock>(); + var intHandler2 = new Mock>(); + var aggregator = new EventAggregator(TestLogger, new List { intHandler1.Object, intHandler2.Object }); + aggregator.Publish(eventA); + + intHandler1.Verify(c => c.Handle(eventA), Times.Once()); + intHandler2.Verify(c => c.Handle(eventA), Times.Once()); } [Test] public void should_not_publish_to_incompatible_handlers() { - var intHandler = new Mock>(); - var stringHandler = new Mock>(); - var aggregator = new EventAggregator(TestLogger, new List { intHandler.Object, stringHandler.Object }); + var eventA = new EventA(); - aggregator.Publish(12); + var aHandler = new Mock>(); + var bHandler = new Mock>(); + var aggregator = new EventAggregator(TestLogger, new List { aHandler.Object, bHandler.Object }); - intHandler.Verify(c => c.Handle(12), Times.Once()); - stringHandler.Verify(c => c.Handle(It.IsAny()), Times.Never()); + aggregator.Publish(eventA); + + aHandler.Verify(c => c.Handle(eventA), Times.Once()); + bHandler.Verify(c => c.Handle(It.IsAny()), Times.Never()); } } + + public class EventA:IEvent + { + + } + + public class EventB : IEvent + { + + } + } \ No newline at end of file diff --git a/NzbDrone.Common/Eventing/EventAggregator.cs b/NzbDrone.Common/Eventing/EventAggregator.cs index c28ec283a..78fe8c0ad 100644 --- a/NzbDrone.Common/Eventing/EventAggregator.cs +++ b/NzbDrone.Common/Eventing/EventAggregator.cs @@ -17,7 +17,7 @@ public EventAggregator(Logger logger, IEnumerable handlers) _handlers = handlers; } - public void Publish(TEvent message) + public void Publish(TEvent message) where TEvent : IEvent { _logger.Trace("Publishing {0}", message.GetType().Name); diff --git a/NzbDrone.Common/Eventing/IEvent.cs b/NzbDrone.Common/Eventing/IEvent.cs new file mode 100644 index 000000000..eabbd3209 --- /dev/null +++ b/NzbDrone.Common/Eventing/IEvent.cs @@ -0,0 +1,8 @@ +using System.Linq; + +namespace NzbDrone.Common.Eventing +{ + public interface IEvent + { + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Eventing/IEventAggregator.cs b/NzbDrone.Common/Eventing/IEventAggregator.cs index 6ed3b8270..a1369565a 100644 --- a/NzbDrone.Common/Eventing/IEventAggregator.cs +++ b/NzbDrone.Common/Eventing/IEventAggregator.cs @@ -8,6 +8,6 @@ namespace NzbDrone.Common.Eventing /// public interface IEventAggregator { - void Publish(TEvent message); + void Publish(TEvent message) where TEvent : IEvent; } } \ No newline at end of file diff --git a/NzbDrone.Common/Eventing/IHandle.cs b/NzbDrone.Common/Eventing/IHandle.cs index afbf70fae..a47df925d 100644 --- a/NzbDrone.Common/Eventing/IHandle.cs +++ b/NzbDrone.Common/Eventing/IHandle.cs @@ -5,14 +5,14 @@ namespace NzbDrone.Common.Eventing /// /// Denotes a class which can handle a particular type of message. /// - /// The type of message to handle. - public interface IHandle : IHandle + /// The type of message to handle. + public interface IHandle : IHandle where TEvent : IEvent { /// /// Handles the message. /// /// The message. - void Handle(TMessage message); + void Handle(TEvent message); } /// diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 2f450d59c..8e721a14e 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -111,6 +111,7 @@ + diff --git a/NzbDrone.Core.Test/Configuration/ConfigCachingFixture.cs b/NzbDrone.Core.Test/Configuration/ConfigCachingFixture.cs new file mode 100644 index 000000000..19922c002 --- /dev/null +++ b/NzbDrone.Core.Test/Configuration/ConfigCachingFixture.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Test.Framework; +using PetaPoco; + +namespace NzbDrone.Core.Test.Configuration +{ + [TestFixture] + public class ConfigCachingFixture : CoreTest + { + [SetUp] + public void Setup() + { + Mocker.GetMock().Setup(c => c.All()) + .Returns(new List { new Config { Key = "Key1", Value = "Value1" } }); + + } + + [Test] + public void getting_value_more_than_once_should_hit_db_once() + { + Subject.GetValue("Key1", null).Should().Be("Value1"); + Subject.GetValue("Key1", null).Should().Be("Value1"); + Subject.GetValue("Key1", null).Should().Be("Value1"); + + Mocker.GetMock().Verify(c => c.All(), Times.Once()); + } + + } +} diff --git a/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs b/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs new file mode 100644 index 000000000..a940bbe84 --- /dev/null +++ b/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs @@ -0,0 +1,183 @@ +using System; +using System.Linq; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.Configuration +{ + [TestFixture] + public class ConfigServiceFixture : ObjectDbTest + { + [SetUp] + public void SetUp() + { + Mocker.Resolve(); + } + + [Test] + public void Add_new_value_to_database() + { + const string key = "MY_KEY"; + const string value = "MY_VALUE"; + + Subject.SetValue(key, value); + Subject.GetValue(key, "").Should().Be(value); + } + + [Test] + public void Get_value_from_database() + { + const string key = "MY_KEY"; + const string value = "MY_VALUE"; + + + Db.Insert(new Config { Key = key, Value = value }); + Db.Insert(new Config { Key = "Other Key", Value = "OtherValue" }); + + var result = Subject.GetValue(key, ""); + + result.Should().Be(value); + } + + + [Test] + public void Get_value_should_return_default_when_no_value() + { + const string key = "MY_KEY"; + const string value = "MY_VALUE"; + + var result = Subject.GetValue(key, value); + + result.Should().Be(value); + } + + [Test] + public void New_value_should_update_old_value_new_value() + { + const string key = "MY_KEY"; + const string originalValue = "OLD_VALUE"; + const string newValue = "NEW_VALUE"; + + Db.Insert(new Config { Key = key, Value = originalValue }); + + //Act + Subject.SetValue(key, newValue); + var result = Subject.GetValue(key, ""); + + //Assert + result.Should().Be(newValue); + AllStoredModels.Should().HaveCount(1); + } + + [Test] + public void New_value_should_update_old_value_same_value() + { + const string key = "MY_KEY"; + const string value = "OLD_VALUE"; + + Subject.SetValue(key, value); + Subject.SetValue(key, value); + var result = Subject.GetValue(key, ""); + + result.Should().Be(value); + AllStoredModels.Should().HaveCount(1); + } + + [Test] + public void get_value_with_persist_should_store_default_value() + { + const string key = "MY_KEY"; + string value = Guid.NewGuid().ToString(); + + Subject.GetValue(key, value, persist: true).Should().Be(value); + Subject.GetValue(key, string.Empty).Should().Be(value); + } + + [Test] + public void get_value_with_out_persist_should_not_store_default_value() + { + const string key = "MY_KEY"; + string value1 = Guid.NewGuid().ToString(); + string value2 = Guid.NewGuid().ToString(); + + Subject.GetValue(key, value1).Should().Be(value1); + Subject.GetValue(key, value2).Should().Be(value2); + } + + + + [Test] + public void uguid_should_only_be_set_once() + { + var guid1 = Subject.UGuid; + var guid2 = Subject.UGuid; + + guid1.Should().Be(guid2); + } + + [Test] + public void uguid_should_return_valid_result_on_first_call() + { + var guid = Subject.UGuid; + guid.Should().NotBeEmpty(); + } + + [Test] + public void updating_a_vakye_should_update_its_value() + { + Subject.SabHost = "Test"; + Subject.SabHost.Should().Be("Test"); + + Subject.SabHost = "Test2"; + Subject.SabHost.Should().Be("Test2"); + } + + [Test] + [Description("This test will use reflection to ensure each config property read/writes to a unique key")] + public void config_properties_should_write_and_read_using_same_key() + { + var configProvider = Subject; + var allProperties = typeof(ConfigService).GetProperties().Where(p => p.GetSetMethod() != null).ToList(); + + + //Act + foreach (var propertyInfo in allProperties) + { + object value = null; + + if (propertyInfo.PropertyType == typeof(string)) + { + value = new Guid().ToString(); + } + else if (propertyInfo.PropertyType == typeof(int)) + { + value = DateTime.Now.Millisecond; + } + else if (propertyInfo.PropertyType == typeof(bool)) + { + value = true; + } + else if (propertyInfo.PropertyType.BaseType == typeof(Enum)) + { + value = 0; + } + + propertyInfo.GetSetMethod().Invoke(configProvider, new[] { value }); + var returnValue = propertyInfo.GetGetMethod().Invoke(configProvider, null); + + if (propertyInfo.PropertyType.BaseType == typeof(Enum)) + { + returnValue = (int)returnValue; + } + + returnValue.Should().Be(value, propertyInfo.Name); + } + + AllStoredModels.Should() + .HaveSameCount(allProperties, "two different properties are writing to the same key in db. Copy/Past fail."); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/EpisodeParseResultTest.cs b/NzbDrone.Core.Test/EpisodeParseResultTest.cs index 919908d52..d962cde88 100644 --- a/NzbDrone.Core.Test/EpisodeParseResultTest.cs +++ b/NzbDrone.Core.Test/EpisodeParseResultTest.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Tv; @@ -131,5 +133,123 @@ public void tostring_daily_show_proper() parseResult.ToString().Should().Be("My Series - 2010-12-30 HDTV-720p [proper]"); } + + + public static readonly object[] SabNamingCases = + { + new object[] { 1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, "My Series Name - 1x02 - My Episode Title [DVD]" }, + new object[] { 1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, "My Series Name - 1x02 - My Episode Title [DVD] [Proper]" }, + new object[] { 1, new[] { 2 }, "", QualityTypes.DVD, true, "My Series Name - 1x02 - [DVD] [Proper]" }, + new object[] { 1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV720p, false, "My Series Name - 1x02-1x04 - My Episode Title [HDTV-720p]" }, + new object[] { 1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV720p, true, "My Series Name - 1x02-1x04 - My Episode Title [HDTV-720p] [Proper]" }, + new object[] { 1, new[] { 2, 4 }, "", QualityTypes.HDTV720p, true, "My Series Name - 1x02-1x04 - [HDTV-720p] [Proper]" }, + }; + + + [Test, TestCaseSource("SabNamingCases")] + public void create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality, bool proper, string expected) + { + var series = Builder.CreateNew() + .With(c => c.Title = "My Series Name") + .Build(); + + var fakeEpisodes = new List(); + + foreach (var episode in episodes) + fakeEpisodes.Add(Builder + .CreateNew() + .With(e => e.EpisodeNumber = episode) + .With(e => e.Title = title) + .Build()); + + var parsResult = new EpisodeParseResult() + { + AirDate = DateTime.Now, + EpisodeNumbers = episodes.ToList(), + Quality = new QualityModel(quality, proper), + SeasonNumber = seasons, + Series = series, + EpisodeTitle = title, + Episodes = fakeEpisodes + }; + + parsResult.GetDownloadTitle().Should().Be(expected); + } + + [TestCase(true, Result = "My Series Name - Season 1 [Bluray720p] [Proper]")] + [TestCase(false, Result = "My Series Name - Season 1 [Bluray720p]")] + public string create_proper_sab_season_title(bool proper) + { + var series = Builder.CreateNew() + .With(c => c.Title = "My Series Name") + .Build(); + + var parsResult = new EpisodeParseResult() + { + AirDate = DateTime.Now, + Quality = new QualityModel(QualityTypes.Bluray720p, proper), + SeasonNumber = 1, + Series = series, + EpisodeTitle = "My Episode Title", + FullSeason = true + }; + + return parsResult.GetDownloadTitle(); + } + + [TestCase(true, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p] [Proper]")] + [TestCase(false, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p]")] + public string create_proper_sab_daily_titles(bool proper) + { + var series = Builder.CreateNew() + .With(c => c.SeriesType = SeriesType.Daily) + .With(c => c.Title = "My Series Name") + .Build(); + + var episode = Builder.CreateNew() + .With(e => e.Title = "My Episode Title") + .Build(); + + var parsResult = new EpisodeParseResult + { + AirDate = new DateTime(2011, 12, 1), + Quality = new QualityModel(QualityTypes.Bluray720p, proper), + Series = series, + EpisodeTitle = "My Episode Title", + Episodes = new List { episode } + }; + + return parsResult.GetDownloadTitle(); + } + + [Test] + public void should_not_repeat_the_same_episode_title() + { + var series = Builder.CreateNew() + .With(c => c.Title = "My Series Name") + .Build(); + + var fakeEpisodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeasonNumber = 5) + .TheFirst(1) + .With(e => e.Title = "My Episode Title (1)") + .TheLast(1) + .With(e => e.Title = "My Episode Title (2)") + .Build(); + + var parsResult = new EpisodeParseResult + { + AirDate = DateTime.Now, + EpisodeNumbers = new List { 10, 11 }, + Quality = new QualityModel(QualityTypes.HDTV720p, false), + SeasonNumber = 35, + Series = series, + Episodes = fakeEpisodes + }; + + parsResult.GetDownloadTitle().Should().Be("My Series Name - 5x01-5x02 - My Episode Title [HDTV-720p]"); + } + } } diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs index e4a481d52..0816c8bf8 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs @@ -8,6 +8,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; @@ -112,10 +113,6 @@ public void should_use_EpisodeFiles_quality() .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) .Returns(fi); - Mocker.GetMock() - .Setup(s => s.GetDownloadTitle(It.Is(e => e.Quality == new QualityModel { Quality = QualityTypes.WEBDL720p, Proper = false }))) - .Returns(message); - Mocker.GetMock() .Setup(s => s.FileExists(currentFilename)) .Returns(true); @@ -175,10 +172,6 @@ public void should_log_error_and_return_null_when_source_file_does_not_exists() .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) .Returns(fi); - Mocker.GetMock() - .Setup(s => s.GetDownloadTitle(It.Is(e => e.Quality == new QualityModel { Quality = QualityTypes.WEBDL720p, Proper = false }))) - .Returns(message); - Mocker.GetMock() .Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny())); diff --git a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/ContainsRecentEpisode.cs b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/ContainsRecentEpisode.cs index 7a9fa3f22..053845d72 100644 --- a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/ContainsRecentEpisode.cs +++ b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/ContainsRecentEpisode.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests diff --git a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/DownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/DownloadProviderFixture.cs index 4bceb3682..ed3d282ae 100644 --- a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/DownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTests/DownloadProviderFixture.cs @@ -6,11 +6,9 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.Configuration; -using NzbDrone.Core.History; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.DownloadClients; using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Test.Framework; @@ -20,17 +18,9 @@ namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests { [TestFixture] - public class DownloadProviderFixture : CoreTest + public class DownloadProviderFixture : CoreTest { - public static object[] SabNamingCases = - { - new object[] { 1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, "My Series Name - 1x02 - My Episode Title [DVD]" }, - new object[] { 1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, "My Series Name - 1x02 - My Episode Title [DVD] [Proper]" }, - new object[] { 1, new[] { 2 }, "", QualityTypes.DVD, true, "My Series Name - 1x02 - [DVD] [Proper]" }, - new object[] { 1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV720p, false, "My Series Name - 1x02-1x04 - My Episode Title [HDTV-720p]" }, - new object[] { 1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV720p, true, "My Series Name - 1x02-1x04 - My Episode Title [HDTV-720p] [Proper]" }, - new object[] { 1, new[] { 2, 4 }, "", QualityTypes.HDTV720p, true, "My Series Name - 1x02-1x04 - [HDTV-720p] [Proper]" }, - }; + private void SetDownloadClient(DownloadClientType clientType) { @@ -53,7 +43,7 @@ private EpisodeParseResult SetupParseResult() return Builder.CreateNew() .With(c => c.Quality = new QualityModel(QualityTypes.DVD, false)) .With(c => c.Series = Builder.CreateNew().Build()) - .With(c => c.EpisodeNumbers = new List{2}) + .With(c => c.EpisodeNumbers = new List { 2 }) .With(c => c.Episodes = episodes) .Build(); } @@ -81,7 +71,7 @@ private void WithFailedAdd() } [Test] - public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed() + public void Download_report_should_publish_on_grab_event() { WithSuccessfullAdd(); SetDownloadClient(DownloadClientType.Sabnzbd); @@ -89,7 +79,7 @@ public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed() var parseResult = SetupParseResult(); //Act - Mocker.Resolve().DownloadReport(parseResult); + Subject.DownloadReport(parseResult); //Assert @@ -99,197 +89,39 @@ public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed() Mocker.GetMock() .Verify(s => s.DownloadNzb(It.IsAny(), It.IsAny(), true), Times.Never()); - Mocker.GetMock() - .Verify(s => s.Add(It.Is(h => h.Episode == parseResult.Episodes[0])), Times.Once()); - Mocker.GetMock() - .Verify(s => s.Add(It.Is(h => h.Episode == parseResult.Episodes[1])), Times.Once()); - - Mocker.GetMock() - .Verify(c => c.MarkEpisodeAsFetched(12)); - - Mocker.GetMock() - .Verify(c => c.MarkEpisodeAsFetched(99)); - - Mocker.GetMock() - .Verify(c => c.OnGrab(It.IsAny())); - } - - [Test] - public void should_download_nzb_to_blackhole_add_to_history_mark_as_grabbed() - { - WithSuccessfullAdd(); - SetDownloadClient(DownloadClientType.Blackhole); - - var parseResult = SetupParseResult(); - - //Act - Mocker.Resolve().DownloadReport(parseResult); - - - //Assert - Mocker.GetMock() - .Verify(s => s.DownloadNzb(It.IsAny(), It.IsAny(), true), Times.Never()); - - Mocker.GetMock() - .Verify(s => s.DownloadNzb(It.IsAny(), It.IsAny(), true), Times.Once()); - - Mocker.GetMock() - .Verify(s => s.Add(It.Is(h => h.Episode == parseResult.Episodes[0])), Times.Once()); - - Mocker.GetMock() - .Verify(s => s.Add(It.Is(h => h.Episode == parseResult.Episodes[1])), Times.Once()); - - Mocker.GetMock() - .Verify(c => c.MarkEpisodeAsFetched(12)); - - Mocker.GetMock() - .Verify(c => c.MarkEpisodeAsFetched(99)); - - Mocker.GetMock() - .Verify(c => c.OnGrab(It.IsAny())); + VerifyEventPublished(It.Is(c => c.ParseResult == parseResult)); } [TestCase(DownloadClientType.Sabnzbd)] [TestCase(DownloadClientType.Blackhole)] - public void Download_report_should_not_add_to_history_mark_as_grabbed_if_add_fails(DownloadClientType clientType) + public void Download_report_should_not_publish_grabbed_event(DownloadClientType clientType) { WithFailedAdd(); SetDownloadClient(clientType); var parseResult = SetupParseResult(); - //Act - Mocker.Resolve().DownloadReport(parseResult); - - Mocker.GetMock() - .Verify(s => s.Add(It.IsAny()), Times.Never()); + Subject.DownloadReport(parseResult); - Mocker.GetMock() - .Verify(c => c.MarkEpisodeAsFetched(It.IsAny()), Times.Never()); - - Mocker.GetMock() - .Verify(c => c.OnGrab(It.IsAny()), Times.Never()); + VerifyEventNotPublished(); } [Test] public void should_return_sab_as_active_client() { SetDownloadClient(DownloadClientType.Sabnzbd); - Mocker.Resolve().GetActiveDownloadClient().Should().BeAssignableTo(); + Subject.GetActiveDownloadClient().Should().BeAssignableTo(); } [Test] public void should_return_blackhole_as_active_client() { SetDownloadClient(DownloadClientType.Blackhole); - Mocker.Resolve().GetActiveDownloadClient().Should().BeAssignableTo(); + Subject.GetActiveDownloadClient().Should().BeAssignableTo(); } - [Test, TestCaseSource("SabNamingCases")] - public void create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality, bool proper, string expected) - { - var series = Builder.CreateNew() - .With(c => c.Title = "My Series Name") - .Build(); - var fakeEpisodes = new List(); - - foreach(var episode in episodes) - fakeEpisodes.Add(Builder - .CreateNew() - .With(e => e.EpisodeNumber = episode) - .With(e => e.Title = title) - .Build()); - - var parsResult = new EpisodeParseResult() - { - AirDate = DateTime.Now, - EpisodeNumbers = episodes.ToList(), - Quality = new QualityModel(quality, proper), - SeasonNumber = seasons, - Series = series, - EpisodeTitle = title, - Episodes = fakeEpisodes - }; - - Mocker.Resolve().GetDownloadTitle(parsResult).Should().Be(expected); - } - - [TestCase(true, Result = "My Series Name - Season 1 [Bluray720p] [Proper]")] - [TestCase(false, Result = "My Series Name - Season 1 [Bluray720p]")] - public string create_proper_sab_season_title(bool proper) - { - var series = Builder.CreateNew() - .With(c => c.Title = "My Series Name") - .Build(); - - var parsResult = new EpisodeParseResult() - { - AirDate = DateTime.Now, - Quality = new QualityModel(QualityTypes.Bluray720p, proper), - SeasonNumber = 1, - Series = series, - EpisodeTitle = "My Episode Title", - FullSeason = true - }; - - return Mocker.Resolve().GetDownloadTitle(parsResult); - } - - [TestCase(true, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p] [Proper]")] - [TestCase(false, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p]")] - public string create_proper_sab_daily_titles(bool proper) - { - var series = Builder.CreateNew() - .With(c => c.SeriesType = SeriesType.Daily) - .With(c => c.Title = "My Series Name") - .Build(); - - var episode = Builder.CreateNew() - .With(e => e.Title = "My Episode Title") - .Build(); - - var parsResult = new EpisodeParseResult - { - AirDate = new DateTime(2011, 12, 1), - Quality = new QualityModel(QualityTypes.Bluray720p, proper), - Series = series, - EpisodeTitle = "My Episode Title", - Episodes = new List{ episode } - }; - - return Mocker.Resolve().GetDownloadTitle(parsResult); - } - - [Test] - public void should_not_repeat_the_same_episode_title() - { - var series = Builder.CreateNew() - .With(c => c.Title = "My Series Name") - .Build(); - - var fakeEpisodes = Builder.CreateListOfSize(2) - .All() - .With(e => e.SeasonNumber = 5) - .TheFirst(1) - .With(e => e.Title = "My Episode Title (1)") - .TheLast(1) - .With(e => e.Title = "My Episode Title (2)") - .Build(); - - var parsResult = new EpisodeParseResult - { - AirDate = DateTime.Now, - EpisodeNumbers = new List{ 10, 11 }, - Quality = new QualityModel(QualityTypes.HDTV720p, false), - SeasonNumber = 35, - Series = series, - Episodes = fakeEpisodes - }; - - Mocker.Resolve().GetDownloadTitle(parsResult).Should().Be("My Series Name - 5x01-5x02 - My Episode Title [HDTV-720p]"); - } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/SearchHistoryProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SearchHistoryProviderTest.cs index 85e7cff13..ca013b453 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchHistoryProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchHistoryProviderTest.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; diff --git a/NzbDrone.Core.Test/ProviderTests/SearchTests/ProcessResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchTests/ProcessResultsFixture.cs index 1fc3ef8eb..acb49927f 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchTests/ProcessResultsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchTests/ProcessResultsFixture.cs @@ -6,6 +6,7 @@ using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; diff --git a/NzbDrone.Core.Test/ProviderTests/SearchTests/TestSearch.cs b/NzbDrone.Core.Test/ProviderTests/SearchTests/TestSearch.cs index 717d0c946..15bcb0bc0 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchTests/TestSearch.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchTests/TestSearch.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; diff --git a/NzbDrone.Core.Test/TvTests/EpisodeProviderTests/EpisodeProviderTest.cs b/NzbDrone.Core.Test/TvTests/EpisodeProviderTests/EpisodeProviderTest.cs index e1c25ff44..c371a7987 100644 --- a/NzbDrone.Core.Test/TvTests/EpisodeProviderTests/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/TvTests/EpisodeProviderTests/EpisodeProviderTest.cs @@ -8,6 +8,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; @@ -941,23 +942,17 @@ public void GetEpisode_by_AirDate_without_EpisodeFile() [Test] public void MarkEpisodeAsFetched() { - WithRealDb(); - var fakeEpisodes = Builder.CreateListOfSize(5) + var fakeEpisodes = Builder.CreateListOfSize(2) .All().With(e => e.GrabDate = null) .Build(); - Db.InsertMany(fakeEpisodes); + var parseResult = new EpisodeParseResult() { Episodes = fakeEpisodes }; - //Act - Mocker.Resolve().MarkEpisodeAsFetched(2); - var episodes = Db.Fetch(); + Mocker.Resolve().Handle(new EpisodeGrabbedEvent(parseResult)); - //Assert - episodes.Where(e => e.OID == 2).Single().GrabDate.Should().BeWithin(TimeSpan.FromSeconds(5)).Before( - DateTime.Now); - - episodes.Where(e => e.GrabDate == null).Should().HaveCount(4); + Mocker.GetMock().Verify(c=>c.Update(fakeEpisodes[0]),Times.Once()); + Mocker.GetMock().Verify(c=>c.Update(fakeEpisodes[1]),Times.Once()); } [Test] diff --git a/NzbDrone.Core/DecisionEngine/AlreadyInQueueSpecification.cs b/NzbDrone.Core/DecisionEngine/AlreadyInQueueSpecification.cs index 2541dde27..e5c59d6c4 100644 --- a/NzbDrone.Core/DecisionEngine/AlreadyInQueueSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/AlreadyInQueueSpecification.cs @@ -1,5 +1,6 @@ using System.Linq; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; diff --git a/NzbDrone.Core/Download/DownloadProvider.cs b/NzbDrone.Core/Download/DownloadProvider.cs new file mode 100644 index 000000000..58f3c457f --- /dev/null +++ b/NzbDrone.Core/Download/DownloadProvider.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.DownloadClients; + +namespace NzbDrone.Core.Download +{ + public class DownloadProvider + { + private readonly SabProvider _sabProvider; + private readonly IConfigService _configService; + private readonly BlackholeProvider _blackholeProvider; + private readonly PneumaticProvider _pneumaticProvider; + private readonly NzbgetProvider _nzbgetProvider; + private readonly IEventAggregator _eventAggregator; + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + public DownloadProvider(SabProvider sabProvider, IConfigService configService, BlackholeProvider blackholeProvider, PneumaticProvider pneumaticProvider, NzbgetProvider nzbgetProvider, IEventAggregator eventAggregator) + { + _sabProvider = sabProvider; + _configService = configService; + _blackholeProvider = blackholeProvider; + _pneumaticProvider = pneumaticProvider; + _nzbgetProvider = nzbgetProvider; + _eventAggregator = eventAggregator; + } + + public DownloadProvider() + { + } + + public virtual bool DownloadReport(EpisodeParseResult parseResult) + { + var downloadTitle = parseResult.OriginalString; + if (!_configService.DownloadClientUseSceneName) + { + downloadTitle = parseResult.GetDownloadTitle(); + } + + var provider = GetActiveDownloadClient(); + var recentEpisode = ContainsRecentEpisode(parseResult); + + bool success = provider.DownloadNzb(parseResult.NzbUrl, downloadTitle, recentEpisode); + + if (success) + { + logger.Trace("Download added to Queue: {0}", downloadTitle); + _eventAggregator.Publish(new EpisodeGrabbedEvent(parseResult)); + } + + return success; + } + + public virtual IDownloadClient GetActiveDownloadClient() + { + switch (_configService.DownloadClient) + { + case DownloadClientType.Blackhole: + return _blackholeProvider; + + case DownloadClientType.Pneumatic: + return _pneumaticProvider; + + case DownloadClientType.Nzbget: + return _nzbgetProvider; + + default: + return _sabProvider; + } + } + + public virtual bool ContainsRecentEpisode(EpisodeParseResult parseResult) + { + return parseResult.Episodes.Any(e => e.AirDate >= DateTime.Today.AddDays(-7)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Download/EpisodeGrabbedEvent.cs b/NzbDrone.Core/Download/EpisodeGrabbedEvent.cs new file mode 100644 index 000000000..6ca4339cb --- /dev/null +++ b/NzbDrone.Core/Download/EpisodeGrabbedEvent.cs @@ -0,0 +1,15 @@ +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Model; + +namespace NzbDrone.Core.Download +{ + public class EpisodeGrabbedEvent : IEvent + { + public EpisodeParseResult ParseResult { get; private set; } + + public EpisodeGrabbedEvent(EpisodeParseResult parseResult) + { + ParseResult = parseResult; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/History/HistoryService.cs b/NzbDrone.Core/History/HistoryService.cs index 689d42db0..59efb3389 100644 --- a/NzbDrone.Core/History/HistoryService.cs +++ b/NzbDrone.Core/History/HistoryService.cs @@ -1,12 +1,22 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; namespace NzbDrone.Core.History { + public interface IHistoryService + { + List All(); + void Purge(); + void Trim(); + QualityModel GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber); + } - public class HistoryService + public class HistoryService : IHistoryService, IHandle { private readonly IHistoryRepository _historyRepository; private readonly Logger _logger; @@ -33,15 +43,28 @@ public virtual void Trim() _historyRepository.Trim(); } - public void Add(History item) - { - - } - public virtual QualityModel GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber) { return _historyRepository.GetBestQualityInHistory(seriesId, seasonNumber, episodeNumber); } + public void Handle(EpisodeGrabbedEvent message) + { + foreach (var episode in message.ParseResult.Episodes) + { + var history = new History + { + Date = DateTime.Now, + Indexer = message.ParseResult.Indexer, + Quality = message.ParseResult.Quality, + NzbTitle = message.ParseResult.OriginalString, + Episode = episode, + NzbInfoUrl = message.ParseResult.NzbInfoUrl, + ReleaseGroup = message.ParseResult.ReleaseGroup, + }; + + _historyRepository.Insert(history); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/RssSyncJob.cs b/NzbDrone.Core/Jobs/RssSyncJob.cs index fd7f4bc18..a9c86a88c 100644 --- a/NzbDrone.Core/Jobs/RssSyncJob.cs +++ b/NzbDrone.Core/Jobs/RssSyncJob.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using NLog; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; diff --git a/NzbDrone.Core/Model/EpisodeParseResult.cs b/NzbDrone.Core/Model/EpisodeParseResult.cs index 448291a07..7e6ef882d 100644 --- a/NzbDrone.Core/Model/EpisodeParseResult.cs +++ b/NzbDrone.Core/Model/EpisodeParseResult.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Collections.Generic; +using NzbDrone.Core.Providers; using NzbDrone.Core.Tv; using NzbDrone.Core.Repository; @@ -50,11 +51,10 @@ public string CleanTitle public bool SceneSource { get; set; } - public IList Episodes { get; set; } + public IList Episodes { get; set; } public override string ToString() { - string episodeString = "[Unknown Episode]"; if (AirDate != null && EpisodeNumbers == null) @@ -67,11 +67,69 @@ public override string ToString() } else if (EpisodeNumbers != null && EpisodeNumbers.Any()) { - episodeString = string.Format("S{0:00}E{1}",SeasonNumber, String.Join("-", EpisodeNumbers.Select(c => c.ToString("00")))); + episodeString = string.Format("S{0:00}E{1}", SeasonNumber, String.Join("-", EpisodeNumbers.Select(c => c.ToString("00")))); } return string.Format("{0} - {1} {2}", SeriesTitle, episodeString, Quality); + } + + public string GetDownloadTitle() + { + var seriesTitle = MediaFileProvider.CleanFilename(Series.Title); + + //Handle Full Naming + if (FullSeason) + { + var seasonResult = String.Format("{0} - Season {1} [{2}]", seriesTitle, + SeasonNumber, Quality.Quality); + + if (Quality.Proper) + seasonResult += " [Proper]"; + + return seasonResult; + } + + if (Series.SeriesType == SeriesType.Daily) + { + var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", seriesTitle, + AirDate, Episodes.First().Title, Quality.Quality); + + if (Quality.Proper) + dailyResult += " [Proper]"; + + return dailyResult; + } + + //Show Name - 1x01-1x02 - Episode Name + //Show Name - 1x01 - Episode Name + var episodeString = new List(); + var episodeNames = new List(); + + foreach (var episode in Episodes) + { + episodeString.Add(String.Format("{0}x{1:00}", episode.SeasonNumber, episode.EpisodeNumber)); + episodeNames.Add(Parser.CleanupEpisodeTitle(episode.Title)); + } + + var epNumberString = String.Join("-", episodeString); + string episodeName; + + + if (episodeNames.Distinct().Count() == 1) + episodeName = episodeNames.First(); + + else + episodeName = String.Join(" + ", episodeNames.Distinct()); + + var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, episodeName, Quality.Quality); + + if (Quality.Proper) + { + result += " [Proper]"; + } + + return result; } } } \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index f5fee3620..113b97f2d 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -260,6 +260,7 @@ + @@ -457,7 +458,7 @@ Code - + Code diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 02713490d..b0be4a91c 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -5,10 +5,9 @@ using NLog; using NzbDrone.Common; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; -using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Repository; namespace NzbDrone.Core.Providers { @@ -19,7 +18,6 @@ public class DiskScanProvider private readonly DiskProvider _diskProvider; private readonly IEpisodeService _episodeService; private readonly MediaFileProvider _mediaFileProvider; - private readonly ISeriesService _seriesService; private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly DownloadProvider _downloadProvider; private readonly SignalRProvider _signalRProvider; @@ -28,15 +26,13 @@ public class DiskScanProvider private readonly MediaInfoProvider _mediaInfoProvider; private readonly ISeriesRepository _seriesRepository; - public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, - ISeriesService seriesService, MediaFileProvider mediaFileProvider, + public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, MediaFileProvider mediaFileProvider, ExternalNotificationProvider externalNotificationProvider, DownloadProvider downloadProvider, SignalRProvider signalRProvider, IConfigService configService, RecycleBinProvider recycleBinProvider, MediaInfoProvider mediaInfoProvider, ISeriesRepository seriesRepository) { _diskProvider = diskProvider; _episodeService = episodeService; - _seriesService = seriesService; _mediaFileProvider = mediaFileProvider; _externalNotificationProvider = externalNotificationProvider; _downloadProvider = downloadProvider; @@ -235,7 +231,7 @@ public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDown parseResult.Quality = new QualityModel { Quality = episodeFile.Quality, Proper = episodeFile.Proper }; parseResult.Episodes = episodes; - var message = _downloadProvider.GetDownloadTitle(parseResult); + var message = parseResult.GetDownloadTitle(); if (newDownload) { diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs deleted file mode 100644 index cd9b5ac02..000000000 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.History; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Providers.DownloadClients; -using NzbDrone.Core.Repository; - -namespace NzbDrone.Core.Providers -{ - public class DownloadProvider - { - private readonly SabProvider _sabProvider; - private readonly HistoryService _historyService; - private readonly IEpisodeService _episodeService; - private readonly ExternalNotificationProvider _externalNotificationProvider; - private readonly IConfigService _configService; - private readonly BlackholeProvider _blackholeProvider; - private readonly SignalRProvider _signalRProvider; - private readonly PneumaticProvider _pneumaticProvider; - private readonly NzbgetProvider _nzbgetProvider; - - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - - public DownloadProvider(SabProvider sabProvider, HistoryService historyService, - IEpisodeService episodeService, ExternalNotificationProvider externalNotificationProvider, - IConfigService configService, BlackholeProvider blackholeProvider, - SignalRProvider signalRProvider, PneumaticProvider pneumaticProvider, - NzbgetProvider nzbgetProvider) - { - _sabProvider = sabProvider; - _historyService = historyService; - _episodeService = episodeService; - _externalNotificationProvider = externalNotificationProvider; - _configService = configService; - _blackholeProvider = blackholeProvider; - _signalRProvider = signalRProvider; - _pneumaticProvider = pneumaticProvider; - _nzbgetProvider = nzbgetProvider; - } - - public DownloadProvider() - { - } - - public virtual bool DownloadReport(EpisodeParseResult parseResult) - { - var downloadTitle = GetDownloadTitle(parseResult); - var provider = GetActiveDownloadClient(); - var recentEpisode = ContainsRecentEpisode(parseResult); - - bool success = provider.DownloadNzb(parseResult.NzbUrl, downloadTitle, recentEpisode); - - if (success) - { - logger.Trace("Download added to Queue: {0}", downloadTitle); - - foreach (var episode in parseResult.Episodes) - { - var history = new History.History - { - Date = DateTime.Now, - Indexer = parseResult.Indexer, - Quality = parseResult.Quality, - NzbTitle = parseResult.OriginalString, - Episode = episode, - NzbInfoUrl = parseResult.NzbInfoUrl, - ReleaseGroup = parseResult.ReleaseGroup, - }; - - _historyService.Add(history); - _episodeService.MarkEpisodeAsFetched(episode.OID); - - _signalRProvider.UpdateEpisodeStatus(episode.OID, EpisodeStatusType.Downloading, null); - } - - _externalNotificationProvider.OnGrab(downloadTitle); - } - - return success; - } - - public virtual IDownloadClient GetActiveDownloadClient() - { - switch (_configService.DownloadClient) - { - case DownloadClientType.Blackhole: - return _blackholeProvider; - - case DownloadClientType.Pneumatic: - return _pneumaticProvider; - - case DownloadClientType.Nzbget: - return _nzbgetProvider; - - default: - return _sabProvider; - } - } - - public virtual String GetDownloadTitle(EpisodeParseResult parseResult) - { - if(_configService.DownloadClientUseSceneName) - { - logger.Trace("Using scene name: {0}", parseResult.OriginalString); - return parseResult.OriginalString; - } - - var seriesTitle = MediaFileProvider.CleanFilename(parseResult.Series.Title); - - //Handle Full Naming - if (parseResult.FullSeason) - { - var seasonResult = String.Format("{0} - Season {1} [{2}]", seriesTitle, - parseResult.SeasonNumber, parseResult.Quality.Quality); - - if (parseResult.Quality.Proper) - seasonResult += " [Proper]"; - - return seasonResult; - } - - if (parseResult.Series.SeriesType == SeriesType.Daily) - { - var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", seriesTitle, - parseResult.AirDate, parseResult.Episodes.First().Title, parseResult.Quality.Quality); - - if (parseResult.Quality.Proper) - dailyResult += " [Proper]"; - - return dailyResult; - } - - //Show Name - 1x01-1x02 - Episode Name - //Show Name - 1x01 - Episode Name - var episodeString = new List(); - var episodeNames = new List(); - - foreach (var episode in parseResult.Episodes) - { - episodeString.Add(String.Format("{0}x{1:00}", episode.SeasonNumber, episode.EpisodeNumber)); - episodeNames.Add(Parser.CleanupEpisodeTitle(episode.Title)); - } - - var epNumberString = String.Join("-", episodeString); - string episodeName; - - - if (episodeNames.Distinct().Count() == 1) - episodeName = episodeNames.First(); - - else - episodeName = String.Join(" + ", episodeNames.Distinct()); - - var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, episodeName, parseResult.Quality.Quality); - - if (parseResult.Quality.Proper) - { - result += " [Proper]"; - } - - return result; - } - - public virtual bool ContainsRecentEpisode(EpisodeParseResult parseResult) - { - return parseResult.Episodes.Any(e => e.AirDate >= DateTime.Today.AddDays(-7)); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/ExternalNotification/Smtp.cs b/NzbDrone.Core/Providers/ExternalNotification/Smtp.cs index 2813b67d7..af7811dcd 100644 --- a/NzbDrone.Core/Providers/ExternalNotification/Smtp.cs +++ b/NzbDrone.Core/Providers/ExternalNotification/Smtp.cs @@ -1,8 +1,6 @@ using System; using NzbDrone.Core.Configuration; using NzbDrone.Core.Tv; -using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Repository; namespace NzbDrone.Core.Providers.ExternalNotification { diff --git a/NzbDrone.Core/Providers/ExternalNotificationProvider.cs b/NzbDrone.Core/Providers/ExternalNotificationProvider.cs index 68474c3c5..09718ace3 100644 --- a/NzbDrone.Core/Providers/ExternalNotificationProvider.cs +++ b/NzbDrone.Core/Providers/ExternalNotificationProvider.cs @@ -2,15 +2,16 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; -using NzbDrone.Core.Model; using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Repository; using PetaPoco; namespace NzbDrone.Core.Providers { - public class ExternalNotificationProvider + public class ExternalNotificationProvider : IHandle { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IDatabase _database; @@ -86,14 +87,6 @@ private void InitializeNotifiers(IList notifiers) } } - public virtual void OnGrab(string message) - { - foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) - { - notifier.OnGrab(message); - } - } - public virtual void OnDownload(string message, Series series) { foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) @@ -117,5 +110,13 @@ public virtual void AfterRename(string message, Series series) notifier.AfterRename(message, series); } } + + public void Handle(EpisodeGrabbedEvent message) + { + foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnGrab(message.ParseResult.GetDownloadTitle()); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/NotificationProvider.cs b/NzbDrone.Core/Providers/NotificationProvider.cs index 92993abe3..ac3fe4a1d 100644 --- a/NzbDrone.Core/Providers/NotificationProvider.cs +++ b/NzbDrone.Core/Providers/NotificationProvider.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Model.Notification; diff --git a/NzbDrone.Core/Providers/Search/DailyEpisodeSearch.cs b/NzbDrone.Core/Providers/Search/DailyEpisodeSearch.cs index fcce38200..b0eaf26da 100644 --- a/NzbDrone.Core/Providers/Search/DailyEpisodeSearch.cs +++ b/NzbDrone.Core/Providers/Search/DailyEpisodeSearch.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; diff --git a/NzbDrone.Core/Providers/Search/EpisodeSearch.cs b/NzbDrone.Core/Providers/Search/EpisodeSearch.cs index e17b20749..e32abc65e 100644 --- a/NzbDrone.Core/Providers/Search/EpisodeSearch.cs +++ b/NzbDrone.Core/Providers/Search/EpisodeSearch.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; diff --git a/NzbDrone.Core/Providers/Search/PartialSeasonSearch.cs b/NzbDrone.Core/Providers/Search/PartialSeasonSearch.cs index f66c08392..573586685 100644 --- a/NzbDrone.Core/Providers/Search/PartialSeasonSearch.cs +++ b/NzbDrone.Core/Providers/Search/PartialSeasonSearch.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading.Tasks; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; diff --git a/NzbDrone.Core/Providers/Search/SearchBase.cs b/NzbDrone.Core/Providers/Search/SearchBase.cs index 4dc32738e..1de8f2725 100644 --- a/NzbDrone.Core/Providers/Search/SearchBase.cs +++ b/NzbDrone.Core/Providers/Search/SearchBase.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; diff --git a/NzbDrone.Core/Providers/SearchHistoryProvider.cs b/NzbDrone.Core/Providers/SearchHistoryProvider.cs index 5abb239bc..aa706d5f1 100644 --- a/NzbDrone.Core/Providers/SearchHistoryProvider.cs +++ b/NzbDrone.Core/Providers/SearchHistoryProvider.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using NLog; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Search; diff --git a/NzbDrone.Core/Providers/SignalRProvider.cs b/NzbDrone.Core/Providers/SignalRProvider.cs index 08c829116..adf0e9e47 100644 --- a/NzbDrone.Core/Providers/SignalRProvider.cs +++ b/NzbDrone.Core/Providers/SignalRProvider.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Text; using NLog; +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Download; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Hubs; @@ -11,7 +13,7 @@ namespace NzbDrone.Core.Providers { - public class SignalRProvider + public class SignalRProvider : IHandle { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); @@ -24,9 +26,9 @@ public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episode var context = GlobalHost.ConnectionManager.GetHubContext(); context.Clients.updatedStatus(new { - EpisodeId = episodeId, - EpisodeStatus = episodeStatus.ToString(), - Quality = (quality == null ? String.Empty : quality.Quality.ToString()) + EpisodeId = episodeId, + EpisodeStatus = episodeStatus.ToString(), + Quality = (quality == null ? String.Empty : quality.Quality.ToString()) }); } catch (Exception ex) @@ -35,5 +37,13 @@ public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episode throw; } } + + public void Handle(EpisodeGrabbedEvent message) + { + foreach (var episode in message.ParseResult.Episodes) + { + UpdateEpisodeStatus(episode.OID, EpisodeStatusType.Downloading, message.ParseResult.Quality); + } + } } } diff --git a/NzbDrone.Core/Providers/SmtpProvider.cs b/NzbDrone.Core/Providers/SmtpProvider.cs index d7c0035a6..4f8716ec8 100644 --- a/NzbDrone.Core/Providers/SmtpProvider.cs +++ b/NzbDrone.Core/Providers/SmtpProvider.cs @@ -1,49 +1,37 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Mail; -using System.Text; using NLog; using NzbDrone.Core.Configuration; -using NzbDrone.Core.Providers.Core; namespace NzbDrone.Core.Providers { public class SmtpProvider { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IConfigService _configService; + private readonly Logger _logger; - public SmtpProvider(IConfigService configService) + public SmtpProvider(IConfigService configService, Logger logger) { _configService = configService; + _logger = logger; } public virtual void SendEmail(string subject, string body, bool htmlBody = false) { - //Create the Email message var email = new MailMessage(); - - //Set the addresses email.From = new MailAddress(_configService.SmtpFromAddress); - - //Allow multiple to addresses (split on each comma) + foreach (var toAddress in _configService.SmtpToAddresses.Split(',')) { email.To.Add(toAddress.Trim()); } - //Set the Subject email.Subject = subject; - - //Set the Body email.Body = body; - - //Html Body email.IsBodyHtml = htmlBody; - - //Handle credentials + var username = _configService.SmtpUsername; var password = _configService.SmtpPassword; @@ -52,15 +40,14 @@ public virtual void SendEmail(string subject, string body, bool htmlBody = false if (!String.IsNullOrWhiteSpace(username)) credentials = new NetworkCredential(username, password); - //Send the email try { Send(email, _configService.SmtpServer, _configService.SmtpPort, _configService.SmtpUseSsl, credentials); } catch(Exception ex) { - Logger.Error("Error sending email. Subject: {0}", email.Subject); - Logger.TraceException(ex.Message, ex); + _logger.Error("Error sending email. Subject: {0}", email.Subject); + _logger.TraceException(ex.Message, ex); } } @@ -69,41 +56,33 @@ public virtual bool SendTestEmail(string server, int port, bool ssl, string user var subject = "NzbDrone SMTP Test Notification"; var body = "This is a test email from NzbDrone, if you received this message you properly configured your SMTP settings! (Now save them!)"; - //Create the Email message var email = new MailMessage(); - //Set the addresses email.From = new MailAddress(fromAddress); - //Allow multiple to addresses (split on each comma) foreach (var toAddress in toAddresses.Split(',')) { email.To.Add(toAddress.Trim()); } - //Set the Subject email.Subject = subject; - //Set the Body email.Body = body; - //Html Body email.IsBodyHtml = false; - //Handle credentials NetworkCredential credentials = null; if (!String.IsNullOrWhiteSpace(username)) credentials = new NetworkCredential(username, password); - //Send the email try { Send(email, server, port, ssl, credentials); } catch(Exception ex) { - Logger.TraceException("Failed to send test email", ex); + _logger.TraceException("Failed to send test email", ex); return false; } return true; @@ -113,22 +92,18 @@ public virtual void Send(MailMessage email, string server, int port, bool ssl, N { try { - //Create the SMTP connection var smtp = new SmtpClient(server, port); - //Enable SSL smtp.EnableSsl = ssl; - //Credentials smtp.Credentials = credentials; - //Send the email smtp.Send(email); } catch (Exception ex) { - Logger.ErrorException("There was an error sending an email.", ex); + _logger.ErrorException("There was an error sending an email.", ex); throw; } } diff --git a/NzbDrone.Core/Providers/StatsProvider.cs b/NzbDrone.Core/Providers/StatsProvider.cs index 75631f1ce..a9bec3e00 100644 --- a/NzbDrone.Core/Providers/StatsProvider.cs +++ b/NzbDrone.Core/Providers/StatsProvider.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; -using NzbDrone.Core.Repository; using PetaPoco; namespace NzbDrone.Core.Providers diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 380726c63..0521663d1 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common.Eventing; +using NzbDrone.Core.Download; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using TvdbLib.Data; @@ -16,7 +18,6 @@ public interface IEpisodeService Episode GetEpisode(int seriesId, DateTime date); IList GetEpisodeBySeries(int seriesId); IList GetEpisodesBySeason(int seriesId, int seasonNumber); - void MarkEpisodeAsFetched(int episodeId); IList GetEpisodesByParseResult(EpisodeParseResult parseResult); IList EpisodesWithoutFiles(bool includeSpecials); IList GetEpisodesByFileId(int episodeFileId); @@ -33,7 +34,7 @@ public interface IEpisodeService List GetEpisodesAiredInMonth(int year, int month); } - public class EpisodeService : IEpisodeService + public class EpisodeService : IEpisodeService, IHandle { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); @@ -80,14 +81,6 @@ public virtual IList GetEpisodesBySeason(int seriesId, int seasonNumber return _episodeRepository.GetEpisodes(seriesId, seasonNumber); } - public virtual void MarkEpisodeAsFetched(int episodeId) - { - logger.Trace("Marking episode {0} as fetched.", episodeId); - var episode = _episodeRepository.Get(episodeId); - episode.GrabDate = DateTime.UtcNow; - _episodeRepository.Update(episode); - } - public virtual IList GetEpisodesByParseResult(EpisodeParseResult parseResult) { var result = new List(); @@ -364,5 +357,15 @@ public List GetEpisodesAiredInMonth(int year, int month) return _episodeRepository.EpisodesBetweenDates(firstDay, lastDay); } + + public void Handle(EpisodeGrabbedEvent message) + { + foreach (var episode in message.ParseResult.Episodes) + { + logger.Trace("Marking episode {0} as fetched.", episode.OID); + episode.GrabDate = DateTime.UtcNow; + _episodeRepository.Update(episode); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/Events/SeriesAddedEvent.cs b/NzbDrone.Core/Tv/Events/SeriesAddedEvent.cs index d7ddc95c6..3b91c75e7 100644 --- a/NzbDrone.Core/Tv/Events/SeriesAddedEvent.cs +++ b/NzbDrone.Core/Tv/Events/SeriesAddedEvent.cs @@ -1,6 +1,8 @@ -namespace NzbDrone.Core.Tv.Events +using NzbDrone.Common.Eventing; + +namespace NzbDrone.Core.Tv.Events { - public class SeriesAddedEvent + public class SeriesAddedEvent:IEvent { public Series Series { get; private set; } diff --git a/NzbDrone.Test.Common/TestBase.cs b/NzbDrone.Test.Common/TestBase.cs index 580f34014..4a9df9a75 100644 --- a/NzbDrone.Test.Common/TestBase.cs +++ b/NzbDrone.Test.Common/TestBase.cs @@ -5,6 +5,7 @@ using NLog; using NUnit.Framework; using NzbDrone.Common; +using NzbDrone.Common.Eventing; using NzbDrone.Test.Common.AutoMoq; namespace NzbDrone.Test.Common @@ -99,5 +100,15 @@ protected string GetTestFilePath(string fileName) { return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName); } + + protected void VerifyEventPublished(TEvent message) where TEvent : IEvent + { + Mocker.GetMock().Verify(c => c.Publish(message), Times.Once()); + } + + protected void VerifyEventNotPublished() where TEvent : IEvent + { + Mocker.GetMock().Verify(c => c.Publish(It.IsAny()), Times.Never()); + } } } diff --git a/NzbDrone.ncrunchsolution b/NzbDrone.ncrunchsolution index 969da6dd4..444b34b1d 100644 --- a/NzbDrone.ncrunchsolution +++ b/NzbDrone.ncrunchsolution @@ -1,6 +1,6 @@ 1 - True + False true true UseDynamicAnalysis