post grab notification and updates are now using the new EventAggregator

pull/2/head
kay.one 12 years ago
parent 13658e3c6d
commit 554924a522

@ -13,38 +13,56 @@ namespace NzbDrone.Common.Test.EventingTests
[Test] [Test]
public void should_publish_event_to_handlers() public void should_publish_event_to_handlers()
{ {
var intHandler = new Mock<IHandle<int>>(); var eventA = new EventA();
var intHandler = new Mock<IHandle<EventA>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler.Object }); var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler.Object });
aggregator.Publish(12); aggregator.Publish(eventA);
intHandler.Verify(c => c.Handle(12), Times.Once()); intHandler.Verify(c => c.Handle(eventA), Times.Once());
} }
[Test] [Test]
public void should_publish_to_more_than_one_handler() public void should_publish_to_more_than_one_handler()
{ {
var intHandler1 = new Mock<IHandle<int>>(); var eventA = new EventA();
var intHandler2 = new Mock<IHandle<int>>();
var intHandler1 = new Mock<IHandle<EventA>>();
var intHandler2 = new Mock<IHandle<EventA>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler1.Object, intHandler2.Object }); var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler1.Object, intHandler2.Object });
aggregator.Publish(12); aggregator.Publish(eventA);
intHandler1.Verify(c => c.Handle(12), Times.Once()); intHandler1.Verify(c => c.Handle(eventA), Times.Once());
intHandler2.Verify(c => c.Handle(12), Times.Once()); intHandler2.Verify(c => c.Handle(eventA), Times.Once());
} }
[Test] [Test]
public void should_not_publish_to_incompatible_handlers() public void should_not_publish_to_incompatible_handlers()
{ {
var intHandler = new Mock<IHandle<int>>(); var eventA = new EventA();
var stringHandler = new Mock<IHandle<string>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler.Object, stringHandler.Object }); var aHandler = new Mock<IHandle<EventA>>();
var bHandler = new Mock<IHandle<EventB>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { aHandler.Object, bHandler.Object });
aggregator.Publish(12); aggregator.Publish(eventA);
intHandler.Verify(c => c.Handle(12), Times.Once()); aHandler.Verify(c => c.Handle(eventA), Times.Once());
stringHandler.Verify(c => c.Handle(It.IsAny<string>()), Times.Never()); bHandler.Verify(c => c.Handle(It.IsAny<EventB>()), Times.Never());
} }
} }
public class EventA:IEvent
{
}
public class EventB : IEvent
{
}
} }

@ -17,7 +17,7 @@ namespace NzbDrone.Common.Eventing
_handlers = handlers; _handlers = handlers;
} }
public void Publish<TEvent>(TEvent message) public void Publish<TEvent>(TEvent message) where TEvent : IEvent
{ {
_logger.Trace("Publishing {0}", message.GetType().Name); _logger.Trace("Publishing {0}", message.GetType().Name);

@ -0,0 +1,8 @@
using System.Linq;
namespace NzbDrone.Common.Eventing
{
public interface IEvent
{
}
}

@ -8,6 +8,6 @@ namespace NzbDrone.Common.Eventing
/// </summary> /// </summary>
public interface IEventAggregator public interface IEventAggregator
{ {
void Publish<TEvent>(TEvent message); void Publish<TEvent>(TEvent message) where TEvent : IEvent;
} }
} }

@ -5,14 +5,14 @@ namespace NzbDrone.Common.Eventing
/// <summary> /// <summary>
/// Denotes a class which can handle a particular type of message. /// Denotes a class which can handle a particular type of message.
/// </summary> /// </summary>
/// <typeparam name = "TMessage">The type of message to handle.</typeparam> /// <typeparam name = "TEvent">The type of message to handle.</typeparam>
public interface IHandle<TMessage> : IHandle public interface IHandle<TEvent> : IHandle where TEvent : IEvent
{ {
/// <summary> /// <summary>
/// Handles the message. /// Handles the message.
/// </summary> /// </summary>
/// <param name = "message">The message.</param> /// <param name = "message">The message.</param>
void Handle(TMessage message); void Handle(TEvent message);
} }
/// <summary> /// <summary>

@ -111,6 +111,7 @@
<Compile Include="EnsureThat\StringExtensions.cs" /> <Compile Include="EnsureThat\StringExtensions.cs" />
<Compile Include="EnsureThat\TypeParam.cs" /> <Compile Include="EnsureThat\TypeParam.cs" />
<Compile Include="Eventing\EventAggregator.cs" /> <Compile Include="Eventing\EventAggregator.cs" />
<Compile Include="Eventing\IEvent.cs" />
<Compile Include="Eventing\IEventAggregator.cs" /> <Compile Include="Eventing\IEventAggregator.cs" />
<Compile Include="Eventing\IHandle.cs" /> <Compile Include="Eventing\IHandle.cs" />
<Compile Include="HostController.cs" /> <Compile Include="HostController.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<ConfigService>
{
[SetUp]
public void Setup()
{
Mocker.GetMock<IConfigRepository>().Setup(c => c.All())
.Returns(new List<Config> { 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<IConfigRepository>().Verify(c => c.All(), Times.Once());
}
}
}

@ -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<ConfigService, Config>
{
[SetUp]
public void SetUp()
{
Mocker.Resolve<IConfigRepository, ConfigRepository>();
}
[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.");
}
}
}

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -131,5 +133,123 @@ namespace NzbDrone.Core.Test
parseResult.ToString().Should().Be("My Series - 2010-12-30 HDTV-720p [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<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var fakeEpisodes = new List<Episode>();
foreach (var episode in episodes)
fakeEpisodes.Add(Builder<Episode>
.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<Series>.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<Series>.CreateNew()
.With(c => c.SeriesType = SeriesType.Daily)
.With(c => c.Title = "My Series Name")
.Build();
var episode = Builder<Episode>.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> { episode }
};
return parsResult.GetDownloadTitle();
}
[Test]
public void should_not_repeat_the_same_episode_title()
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var fakeEpisodes = Builder<Episode>.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<int> { 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]");
}
} }
} }

@ -8,6 +8,7 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
@ -112,10 +113,6 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) .Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi); .Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new QualityModel { Quality = QualityTypes.WEBDL720p, Proper = false })))
.Returns(message);
Mocker.GetMock<DiskProvider>() Mocker.GetMock<DiskProvider>()
.Setup(s => s.FileExists(currentFilename)) .Setup(s => s.FileExists(currentFilename))
.Returns(true); .Returns(true);
@ -175,10 +172,6 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) .Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi); .Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new QualityModel { Quality = QualityTypes.WEBDL720p, Proper = false })))
.Returns(message);
Mocker.GetMock<ExternalNotificationProvider>() Mocker.GetMock<ExternalNotificationProvider>()
.Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>())); .Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()));

@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests

@ -6,11 +6,9 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.History; using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients; using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -20,17 +18,9 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests
{ {
[TestFixture] [TestFixture]
public class DownloadProviderFixture : CoreTest public class DownloadProviderFixture : CoreTest<DownloadProvider>
{ {
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) private void SetDownloadClient(DownloadClientType clientType)
{ {
@ -53,7 +43,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests
return Builder<EpisodeParseResult>.CreateNew() return Builder<EpisodeParseResult>.CreateNew()
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, false)) .With(c => c.Quality = new QualityModel(QualityTypes.DVD, false))
.With(c => c.Series = Builder<Series>.CreateNew().Build()) .With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c => c.EpisodeNumbers = new List<int>{2}) .With(c => c.EpisodeNumbers = new List<int> { 2 })
.With(c => c.Episodes = episodes) .With(c => c.Episodes = episodes)
.Build(); .Build();
} }
@ -81,7 +71,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests
} }
[Test] [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(); WithSuccessfullAdd();
SetDownloadClient(DownloadClientType.Sabnzbd); SetDownloadClient(DownloadClientType.Sabnzbd);
@ -89,7 +79,7 @@ namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests
var parseResult = SetupParseResult(); var parseResult = SetupParseResult();
//Act //Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult); Subject.DownloadReport(parseResult);
//Assert //Assert
@ -99,197 +89,39 @@ namespace NzbDrone.Core.Test.ProviderTests.DownloadProviderTests
Mocker.GetMock<BlackholeProvider>() Mocker.GetMock<BlackholeProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>(), true), Times.Never()); .Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>(), true), Times.Never());
Mocker.GetMock<HistoryService>()
.Verify(s => s.Add(It.Is<History.History>(h => h.Episode == parseResult.Episodes[0])), Times.Once());
Mocker.GetMock<HistoryService>()
.Verify(s => s.Add(It.Is<History.History>(h => h.Episode == parseResult.Episodes[1])), Times.Once());
Mocker.GetMock<IEpisodeService>()
.Verify(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<IEpisodeService>() VerifyEventPublished(It.Is<EpisodeGrabbedEvent>(c => c.ParseResult == parseResult));
.Verify(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<string>()));
}
[Test]
public void should_download_nzb_to_blackhole_add_to_history_mark_as_grabbed()
{
WithSuccessfullAdd();
SetDownloadClient(DownloadClientType.Blackhole);
var parseResult = SetupParseResult();
//Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
//Assert
Mocker.GetMock<SabProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>(), true), Times.Never());
Mocker.GetMock<BlackholeProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>(), true), Times.Once());
Mocker.GetMock<HistoryService>()
.Verify(s => s.Add(It.Is<History.History>(h => h.Episode == parseResult.Episodes[0])), Times.Once());
Mocker.GetMock<HistoryService>()
.Verify(s => s.Add(It.Is<History.History>(h => h.Episode == parseResult.Episodes[1])), Times.Once());
Mocker.GetMock<IEpisodeService>()
.Verify(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<IEpisodeService>()
.Verify(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<string>()));
} }
[TestCase(DownloadClientType.Sabnzbd)] [TestCase(DownloadClientType.Sabnzbd)]
[TestCase(DownloadClientType.Blackhole)] [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(); WithFailedAdd();
SetDownloadClient(clientType); SetDownloadClient(clientType);
var parseResult = SetupParseResult(); var parseResult = SetupParseResult();
//Act Subject.DownloadReport(parseResult);
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
Mocker.GetMock<HistoryService>()
.Verify(s => s.Add(It.IsAny<History.History>()), Times.Never());
Mocker.GetMock<IEpisodeService>() VerifyEventNotPublished<EpisodeGrabbedEvent>();
.Verify(c => c.MarkEpisodeAsFetched(It.IsAny<int>()), Times.Never());
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<String>()), Times.Never());
} }
[Test] [Test]
public void should_return_sab_as_active_client() public void should_return_sab_as_active_client()
{ {
SetDownloadClient(DownloadClientType.Sabnzbd); SetDownloadClient(DownloadClientType.Sabnzbd);
Mocker.Resolve<DownloadProvider>().GetActiveDownloadClient().Should().BeAssignableTo<SabProvider>(); Subject.GetActiveDownloadClient().Should().BeAssignableTo<SabProvider>();
} }
[Test] [Test]
public void should_return_blackhole_as_active_client() public void should_return_blackhole_as_active_client()
{ {
SetDownloadClient(DownloadClientType.Blackhole); SetDownloadClient(DownloadClientType.Blackhole);
Mocker.Resolve<DownloadProvider>().GetActiveDownloadClient().Should().BeAssignableTo<BlackholeProvider>(); Subject.GetActiveDownloadClient().Should().BeAssignableTo<BlackholeProvider>();
}
[Test, TestCaseSource("SabNamingCases")]
public void create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality, bool proper, string expected)
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var fakeEpisodes = new List<Episode>();
foreach(var episode in episodes)
fakeEpisodes.Add(Builder<Episode>
.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<DownloadProvider>().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<Series>.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<DownloadProvider>().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<Series>.CreateNew()
.With(c => c.SeriesType = SeriesType.Daily)
.With(c => c.Title = "My Series Name")
.Build();
var episode = Builder<Episode>.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>{ episode }
};
return Mocker.Resolve<DownloadProvider>().GetDownloadTitle(parsResult);
}
[Test]
public void should_not_repeat_the_same_episode_title()
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var fakeEpisodes = Builder<Episode>.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<int>{ 10, 11 },
Quality = new QualityModel(QualityTypes.HDTV720p, false),
SeasonNumber = 35,
Series = series,
Episodes = fakeEpisodes
};
Mocker.Resolve<DownloadProvider>().GetDownloadTitle(parsResult).Should().Be("My Series Name - 5x01-5x02 - My Episode Title [HDTV-720p]");
}
} }
} }

@ -7,6 +7,7 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;

@ -6,6 +6,7 @@ using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;

@ -8,6 +8,7 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
@ -941,23 +942,17 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeProviderTests
[Test] [Test]
public void MarkEpisodeAsFetched() public void MarkEpisodeAsFetched()
{ {
WithRealDb();
var fakeEpisodes = Builder<Episode>.CreateListOfSize(5) var fakeEpisodes = Builder<Episode>.CreateListOfSize(2)
.All().With(e => e.GrabDate = null) .All().With(e => e.GrabDate = null)
.Build(); .Build();
Db.InsertMany(fakeEpisodes); var parseResult = new EpisodeParseResult() { Episodes = fakeEpisodes };
//Act Mocker.Resolve<EpisodeService>().Handle(new EpisodeGrabbedEvent(parseResult));
Mocker.Resolve<EpisodeService>().MarkEpisodeAsFetched(2);
var episodes = Db.Fetch<Episode>();
//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<IEpisodeRepository>().Verify(c=>c.Update(fakeEpisodes[0]),Times.Once());
Mocker.GetMock<IEpisodeRepository>().Verify(c=>c.Update(fakeEpisodes[1]),Times.Once());
} }
[Test] [Test]

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;

@ -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));
}
}
}

@ -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;
}
}
}

@ -1,12 +1,22 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Eventing;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
{ {
public interface IHistoryService
{
List<History> All();
void Purge();
void Trim();
QualityModel GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber);
}
public class HistoryService public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>
{ {
private readonly IHistoryRepository _historyRepository; private readonly IHistoryRepository _historyRepository;
private readonly Logger _logger; private readonly Logger _logger;
@ -33,15 +43,28 @@ namespace NzbDrone.Core.History
_historyRepository.Trim(); _historyRepository.Trim();
} }
public void Add(History item)
{
}
public virtual QualityModel GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber) public virtual QualityModel GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber)
{ {
return _historyRepository.GetBestQualityInHistory(seriesId, seasonNumber, 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);
}
}
} }
} }

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
@ -54,7 +55,6 @@ namespace NzbDrone.Core.Model
public override string ToString() public override string ToString()
{ {
string episodeString = "[Unknown Episode]"; string episodeString = "[Unknown Episode]";
if (AirDate != null && EpisodeNumbers == null) if (AirDate != null && EpisodeNumbers == null)
@ -67,11 +67,69 @@ namespace NzbDrone.Core.Model
} }
else if (EpisodeNumbers != null && EpisodeNumbers.Any()) 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); 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<String>();
var episodeNames = new List<String>();
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;
} }
} }
} }

@ -260,6 +260,7 @@
<Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" /> <Compile Include="Datastore\PetaPoco\EpisodeSeasonRelator.cs" />
<Compile Include="Datastore\PetaPoco\PetaPoco.cs" /> <Compile Include="Datastore\PetaPoco\PetaPoco.cs" />
<Compile Include="Datastore\SqlCeProxy.cs" /> <Compile Include="Datastore\SqlCeProxy.cs" />
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
<Compile Include="Fluent.cs" /> <Compile Include="Fluent.cs" />
<Compile Include="Helpers\Converters\EpochDateTimeConverter.cs" /> <Compile Include="Helpers\Converters\EpochDateTimeConverter.cs" />
<Compile Include="Helpers\SabnzbdQueueTimeConverter.cs" /> <Compile Include="Helpers\SabnzbdQueueTimeConverter.cs" />
@ -457,7 +458,7 @@
<Compile Include="Providers\DownloadClients\SabProvider.cs"> <Compile Include="Providers\DownloadClients\SabProvider.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Providers\DownloadProvider.cs"> <Compile Include="Download\DownloadProvider.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Tv\EpisodeRepository.cs"> <Compile Include="Tv\EpisodeRepository.cs">

@ -5,10 +5,9 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
@ -19,7 +18,6 @@ namespace NzbDrone.Core.Providers
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly IEpisodeService _episodeService; private readonly IEpisodeService _episodeService;
private readonly MediaFileProvider _mediaFileProvider; private readonly MediaFileProvider _mediaFileProvider;
private readonly ISeriesService _seriesService;
private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly DownloadProvider _downloadProvider; private readonly DownloadProvider _downloadProvider;
private readonly SignalRProvider _signalRProvider; private readonly SignalRProvider _signalRProvider;
@ -28,15 +26,13 @@ namespace NzbDrone.Core.Providers
private readonly MediaInfoProvider _mediaInfoProvider; private readonly MediaInfoProvider _mediaInfoProvider;
private readonly ISeriesRepository _seriesRepository; private readonly ISeriesRepository _seriesRepository;
public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, MediaFileProvider mediaFileProvider,
ISeriesService seriesService, MediaFileProvider mediaFileProvider,
ExternalNotificationProvider externalNotificationProvider, DownloadProvider downloadProvider, ExternalNotificationProvider externalNotificationProvider, DownloadProvider downloadProvider,
SignalRProvider signalRProvider, IConfigService configService, SignalRProvider signalRProvider, IConfigService configService,
RecycleBinProvider recycleBinProvider, MediaInfoProvider mediaInfoProvider, ISeriesRepository seriesRepository) RecycleBinProvider recycleBinProvider, MediaInfoProvider mediaInfoProvider, ISeriesRepository seriesRepository)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_episodeService = episodeService; _episodeService = episodeService;
_seriesService = seriesService;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_downloadProvider = downloadProvider; _downloadProvider = downloadProvider;
@ -235,7 +231,7 @@ namespace NzbDrone.Core.Providers
parseResult.Quality = new QualityModel { Quality = episodeFile.Quality, Proper = episodeFile.Proper }; parseResult.Quality = new QualityModel { Quality = episodeFile.Quality, Proper = episodeFile.Proper };
parseResult.Episodes = episodes; parseResult.Episodes = episodes;
var message = _downloadProvider.GetDownloadTitle(parseResult); var message = parseResult.GetDownloadTitle();
if (newDownload) if (newDownload)
{ {

@ -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<String>();
var episodeNames = new List<String>();
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));
}
}
}

@ -1,8 +1,6 @@
using System; using System;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.ExternalNotification namespace NzbDrone.Core.Providers.ExternalNotification
{ {

@ -2,15 +2,16 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Eventing;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Providers.ExternalNotification;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using PetaPoco; using PetaPoco;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
public class ExternalNotificationProvider public class ExternalNotificationProvider : IHandle<EpisodeGrabbedEvent>
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IDatabase _database; private readonly IDatabase _database;
@ -86,14 +87,6 @@ namespace NzbDrone.Core.Providers
} }
} }
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) public virtual void OnDownload(string message, Series series)
{ {
foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable))
@ -117,5 +110,13 @@ namespace NzbDrone.Core.Providers
notifier.AfterRename(message, 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());
}
}
} }
} }

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NLog; using NLog;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Search; using NzbDrone.Core.Repository.Search;

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NLog; using NLog;
using NzbDrone.Common.Eventing;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Hubs; using NzbDrone.Core.Providers.Hubs;
@ -11,7 +13,7 @@ using SignalR.Hubs;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
public class SignalRProvider public class SignalRProvider : IHandle<EpisodeGrabbedEvent>
{ {
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
@ -35,5 +37,13 @@ namespace NzbDrone.Core.Providers
throw; throw;
} }
} }
public void Handle(EpisodeGrabbedEvent message)
{
foreach (var episode in message.ParseResult.Episodes)
{
UpdateEpisodeStatus(episode.OID, EpisodeStatusType.Downloading, message.ParseResult.Quality);
}
}
} }
} }

@ -1,49 +1,37 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Mail; using System.Net.Mail;
using System.Text;
using NLog; using NLog;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
public class SmtpProvider public class SmtpProvider
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Logger _logger;
public SmtpProvider(IConfigService configService) public SmtpProvider(IConfigService configService, Logger logger)
{ {
_configService = configService; _configService = configService;
_logger = logger;
} }
public virtual void SendEmail(string subject, string body, bool htmlBody = false) public virtual void SendEmail(string subject, string body, bool htmlBody = false)
{ {
//Create the Email message
var email = new MailMessage(); var email = new MailMessage();
//Set the addresses
email.From = new MailAddress(_configService.SmtpFromAddress); email.From = new MailAddress(_configService.SmtpFromAddress);
//Allow multiple to addresses (split on each comma)
foreach (var toAddress in _configService.SmtpToAddresses.Split(',')) foreach (var toAddress in _configService.SmtpToAddresses.Split(','))
{ {
email.To.Add(toAddress.Trim()); email.To.Add(toAddress.Trim());
} }
//Set the Subject
email.Subject = subject; email.Subject = subject;
//Set the Body
email.Body = body; email.Body = body;
//Html Body
email.IsBodyHtml = htmlBody; email.IsBodyHtml = htmlBody;
//Handle credentials
var username = _configService.SmtpUsername; var username = _configService.SmtpUsername;
var password = _configService.SmtpPassword; var password = _configService.SmtpPassword;
@ -52,15 +40,14 @@ namespace NzbDrone.Core.Providers
if (!String.IsNullOrWhiteSpace(username)) if (!String.IsNullOrWhiteSpace(username))
credentials = new NetworkCredential(username, password); credentials = new NetworkCredential(username, password);
//Send the email
try try
{ {
Send(email, _configService.SmtpServer, _configService.SmtpPort, _configService.SmtpUseSsl, credentials); Send(email, _configService.SmtpServer, _configService.SmtpPort, _configService.SmtpUseSsl, credentials);
} }
catch(Exception ex) catch(Exception ex)
{ {
Logger.Error("Error sending email. Subject: {0}", email.Subject); _logger.Error("Error sending email. Subject: {0}", email.Subject);
Logger.TraceException(ex.Message, ex); _logger.TraceException(ex.Message, ex);
} }
} }
@ -69,41 +56,33 @@ namespace NzbDrone.Core.Providers
var subject = "NzbDrone SMTP Test Notification"; 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!)"; 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(); var email = new MailMessage();
//Set the addresses
email.From = new MailAddress(fromAddress); email.From = new MailAddress(fromAddress);
//Allow multiple to addresses (split on each comma)
foreach (var toAddress in toAddresses.Split(',')) foreach (var toAddress in toAddresses.Split(','))
{ {
email.To.Add(toAddress.Trim()); email.To.Add(toAddress.Trim());
} }
//Set the Subject
email.Subject = subject; email.Subject = subject;
//Set the Body
email.Body = body; email.Body = body;
//Html Body
email.IsBodyHtml = false; email.IsBodyHtml = false;
//Handle credentials
NetworkCredential credentials = null; NetworkCredential credentials = null;
if (!String.IsNullOrWhiteSpace(username)) if (!String.IsNullOrWhiteSpace(username))
credentials = new NetworkCredential(username, password); credentials = new NetworkCredential(username, password);
//Send the email
try try
{ {
Send(email, server, port, ssl, credentials); Send(email, server, port, ssl, credentials);
} }
catch(Exception ex) catch(Exception ex)
{ {
Logger.TraceException("Failed to send test email", ex); _logger.TraceException("Failed to send test email", ex);
return false; return false;
} }
return true; return true;
@ -113,22 +92,18 @@ namespace NzbDrone.Core.Providers
{ {
try try
{ {
//Create the SMTP connection
var smtp = new SmtpClient(server, port); var smtp = new SmtpClient(server, port);
//Enable SSL
smtp.EnableSsl = ssl; smtp.EnableSsl = ssl;
//Credentials
smtp.Credentials = credentials; smtp.Credentials = credentials;
//Send the email
smtp.Send(email); smtp.Send(email);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("There was an error sending an email.", ex); _logger.ErrorException("There was an error sending an email.", ex);
throw; throw;
} }
} }

@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
using PetaPoco; using PetaPoco;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Eventing;
using NzbDrone.Core.Download;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using TvdbLib.Data; using TvdbLib.Data;
@ -16,7 +18,6 @@ namespace NzbDrone.Core.Tv
Episode GetEpisode(int seriesId, DateTime date); Episode GetEpisode(int seriesId, DateTime date);
IList<Episode> GetEpisodeBySeries(int seriesId); IList<Episode> GetEpisodeBySeries(int seriesId);
IList<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber); IList<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber);
void MarkEpisodeAsFetched(int episodeId);
IList<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult); IList<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult);
IList<Episode> EpisodesWithoutFiles(bool includeSpecials); IList<Episode> EpisodesWithoutFiles(bool includeSpecials);
IList<Episode> GetEpisodesByFileId(int episodeFileId); IList<Episode> GetEpisodesByFileId(int episodeFileId);
@ -33,7 +34,7 @@ namespace NzbDrone.Core.Tv
List<Episode> GetEpisodesAiredInMonth(int year, int month); List<Episode> GetEpisodesAiredInMonth(int year, int month);
} }
public class EpisodeService : IEpisodeService public class EpisodeService : IEpisodeService, IHandle<EpisodeGrabbedEvent>
{ {
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
@ -80,14 +81,6 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.GetEpisodes(seriesId, 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<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult) public virtual IList<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult)
{ {
var result = new List<Episode>(); var result = new List<Episode>();
@ -364,5 +357,15 @@ namespace NzbDrone.Core.Tv
return _episodeRepository.EpisodesBetweenDates(firstDay, lastDay); 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);
}
}
} }
} }

@ -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; } public Series Series { get; private set; }

@ -5,6 +5,7 @@ using Moq;
using NLog; using NLog;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Eventing;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Test.Common namespace NzbDrone.Test.Common
@ -99,5 +100,15 @@ namespace NzbDrone.Test.Common
{ {
return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName); return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName);
} }
protected void VerifyEventPublished<TEvent>(TEvent message) where TEvent : IEvent
{
Mocker.GetMock<IEventAggregator>().Verify(c => c.Publish(message), Times.Once());
}
protected void VerifyEventNotPublished<TEvent>() where TEvent : IEvent
{
Mocker.GetMock<IEventAggregator>().Verify(c => c.Publish(It.IsAny<TEvent>()), Times.Never());
}
} }
} }

@ -1,6 +1,6 @@
<SolutionConfiguration> <SolutionConfiguration>
<FileVersion>1</FileVersion> <FileVersion>1</FileVersion>
<AutoEnableOnStartup>True</AutoEnableOnStartup> <AutoEnableOnStartup>False</AutoEnableOnStartup>
<AllowParallelTestExecution>true</AllowParallelTestExecution> <AllowParallelTestExecution>true</AllowParallelTestExecution>
<AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves> <AllowTestsToRunInParallelWithThemselves>true</AllowTestsToRunInParallelWithThemselves>
<FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit> <FrameworkUtilisationTypeForNUnit>UseDynamicAnalysis</FrameworkUtilisationTypeForNUnit>

Loading…
Cancel
Save