1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-09 04:22:30 +01:00

post grab notification and updates are now using the new EventAggregator

This commit is contained in:
kay.one 2013-02-24 11:18:48 -08:00
parent 13658e3c6d
commit 554924a522
40 changed files with 670 additions and 482 deletions

View File

@ -13,38 +13,56 @@ public class ServiceNameFixture : TestBase
[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 aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler.Object });
aggregator.Publish(12);
intHandler.Verify(c => c.Handle(12), Times.Once());
var intHandler = new Mock<IHandle<EventA>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler.Object });
aggregator.Publish(eventA);
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 aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler1.Object, intHandler2.Object });
aggregator.Publish(12);
intHandler1.Verify(c => c.Handle(12), Times.Once()); var intHandler1 = new Mock<IHandle<EventA>>();
intHandler2.Verify(c => c.Handle(12), Times.Once()); var intHandler2 = new Mock<IHandle<EventA>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { intHandler1.Object, intHandler2.Object });
aggregator.Publish(eventA);
intHandler1.Verify(c => c.Handle(eventA), 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 });
aggregator.Publish(12); var aHandler = new Mock<IHandle<EventA>>();
var bHandler = new Mock<IHandle<EventB>>();
var aggregator = new EventAggregator(TestLogger, new List<IHandle> { aHandler.Object, bHandler.Object });
intHandler.Verify(c => c.Handle(12), Times.Once()); aggregator.Publish(eventA);
stringHandler.Verify(c => c.Handle(It.IsAny<string>()), Times.Never());
aHandler.Verify(c => c.Handle(eventA), Times.Once());
bHandler.Verify(c => c.Handle(It.IsAny<EventB>()), Times.Never());
} }
} }
public class EventA:IEvent
{
}
public class EventB : IEvent
{
}
} }

View File

@ -17,7 +17,7 @@ public EventAggregator(Logger logger, IEnumerable<IHandle> handlers)
_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);

View File

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

View File

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

View File

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

View File

@ -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" />

View File

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

View File

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

View File

@ -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 @@ public void tostring_daily_show_proper()
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]");
}
} }
} }

View File

@ -8,6 +8,7 @@
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 @@ public void should_use_EpisodeFiles_quality()
.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 @@ public void should_log_error_and_return_null_when_source_file_does_not_exists()
.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>()));

View File

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

View File

@ -6,11 +6,9 @@
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 @@
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 @@ private EpisodeParseResult SetupParseResult()
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 @@ private void WithFailedAdd()
} }
[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 @@ public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed()
var parseResult = SetupParseResult(); var parseResult = SetupParseResult();
//Act //Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult); Subject.DownloadReport(parseResult);
//Assert //Assert
@ -99,197 +89,39 @@ public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed()
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>() VerifyEventPublished(It.Is<EpisodeGrabbedEvent>(c => c.ParseResult == parseResult));
.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>()));
}
[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]");
}
} }
} }

View File

@ -7,6 +7,7 @@
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;

View File

@ -6,6 +6,7 @@
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;

View File

@ -4,6 +4,7 @@
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;

View File

@ -8,6 +8,7 @@
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 @@ public void GetEpisode_by_AirDate_without_EpisodeFile()
[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 Mocker.GetMock<IEpisodeRepository>().Verify(c=>c.Update(fakeEpisodes[0]),Times.Once());
episodes.Where(e => e.OID == 2).Single().GrabDate.Should().BeWithin(TimeSpan.FromSeconds(5)).Before( Mocker.GetMock<IEpisodeRepository>().Verify(c=>c.Update(fakeEpisodes[1]),Times.Once());
DateTime.Now);
episodes.Where(e => e.GrabDate == null).Should().HaveCount(4);
} }
[Test] [Test]

View File

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

View File

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

View File

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

View File

@ -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 @@ public virtual void Trim()
_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);
}
}
} }
} }

View File

@ -4,6 +4,7 @@
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;

View File

@ -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 @@ public string CleanTitle
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 @@ public override string ToString()
} }
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;
} }
} }
} }

View File

@ -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">

View File

@ -5,10 +5,9 @@
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 @@ public class DiskScanProvider
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 @@ public class DiskScanProvider
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 @@ public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDown
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)
{ {

View File

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

View File

@ -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
{ {

View File

@ -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 @@ private void InitializeNotifiers(IList<ExternalNotificationBase> 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) 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 @@ public virtual void AfterRename(string message, Series series)
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());
}
}
} }
} }

View File

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

View File

@ -4,6 +4,7 @@
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;

View File

@ -4,6 +4,7 @@
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;

View File

@ -4,6 +4,7 @@
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;

View File

@ -3,6 +3,7 @@
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;

View File

@ -3,6 +3,7 @@
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;

View File

@ -3,6 +3,8 @@
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 @@
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();
@ -24,9 +26,9 @@ public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episode
var context = GlobalHost.ConnectionManager.GetHubContext<EpisodeHub>(); var context = GlobalHost.ConnectionManager.GetHubContext<EpisodeHub>();
context.Clients.updatedStatus(new context.Clients.updatedStatus(new
{ {
EpisodeId = episodeId, EpisodeId = episodeId,
EpisodeStatus = episodeStatus.ToString(), EpisodeStatus = episodeStatus.ToString(),
Quality = (quality == null ? String.Empty : quality.Quality.ToString()) Quality = (quality == null ? String.Empty : quality.Quality.ToString())
}); });
} }
catch (Exception ex) catch (Exception ex)
@ -35,5 +37,13 @@ public virtual void UpdateEpisodeStatus(int episodeId, EpisodeStatusType episode
throw; throw;
} }
} }
public void Handle(EpisodeGrabbedEvent message)
{
foreach (var episode in message.ParseResult.Episodes)
{
UpdateEpisodeStatus(episode.OID, EpisodeStatusType.Downloading, message.ParseResult.Quality);
}
}
} }
} }

View File

@ -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 @@ public virtual void SendEmail(string subject, string body, bool htmlBody = false
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 @@ public virtual bool SendTestEmail(string server, int port, bool ssl, string user
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 @@ public virtual void Send(MailMessage email, string server, int port, bool ssl, N
{ {
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;
} }
} }

View File

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

View File

@ -2,6 +2,8 @@
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 @@ public interface IEpisodeService
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 @@ public interface IEpisodeService
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 @@ public virtual IList<Episode> GetEpisodesBySeason(int seriesId, int seasonNumber
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 @@ public List<Episode> GetEpisodesAiredInMonth(int year, int month)
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);
}
}
} }
} }

View File

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

View File

@ -5,6 +5,7 @@
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 @@ protected string GetTestFilePath(string fileName)
{ {
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());
}
} }
} }

View File

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