Notifications wired up server sided

This commit is contained in:
Mark McDowall 2013-05-19 16:17:32 -07:00
parent 3f44339381
commit e9bf78a97d
57 changed files with 977 additions and 951 deletions

View File

@ -4,6 +4,8 @@ using System.Linq;
using NzbDrone.Api.Extensions;
using NzbDrone.Common.Composition;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Api.Commands
{

View File

@ -5,7 +5,7 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;

View File

@ -4,8 +4,9 @@ using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.ExternalNotification;
using NzbDrone.Core.Model;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Notifications.Growl;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Test.Framework;

View File

@ -1,6 +1,4 @@

using System;
using System;
using System.IO;
using System.Linq;
using System.Net;
@ -10,14 +8,8 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.ExternalNotification;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Notifications.Plex;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests
{
@ -25,32 +17,27 @@ namespace NzbDrone.Core.Test.ProviderTests
public class PlexProviderTest : CoreTest
{
private void WithSingleClient()
{
Mocker.GetMock<IConfigService>().SetupGet(s => s.PlexClientHosts)
.Returns("localhost:3000");
}
private void WithMultipleClients()
{
Mocker.GetMock<IConfigService>().SetupGet(s => s.PlexClientHosts)
.Returns("localhost:3000, 192.168.0.10:3000");
}
private PlexClientSettings _clientSettings;
public void WithClientCredentials()
{
Mocker.GetMock<IConfigService>().SetupGet(s => s.PlexUsername)
.Returns("plex");
_clientSettings.Username = "plex";
_clientSettings.Password = "plex";
}
Mocker.GetMock<IConfigService>().SetupGet(s => s.PlexPassword)
.Returns("plex");
[SetUp]
public void Setup()
{
_clientSettings = new PlexClientSettings
{
Host = "localhost",
Port = 3000
};
}
[Test]
public void GetSectionKeys_should_return_single_section_key_when_only_one_show_section()
{
{
var response = "<MediaContainer size=\"1\" mediaTagPrefix=\"/system/bundle/media/flags/\" mediaTagVersion=\"1329809559\" title1=\"Plex Library\" identifier=\"com.plexapp.plugins.library\"><Directory refreshing=\"0\" key=\"5\" type=\"show\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory></MediaContainer>";
Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(response));
@ -123,10 +110,8 @@ namespace NzbDrone.Core.Test.ProviderTests
}
[Test]
public void Notify_should_send_notification_for_single_client_when_only_one_is_configured()
public void Notify_should_send_notification()
{
WithSingleClient();
const string header = "Test Header";
const string message = "Test Message";
@ -138,37 +123,15 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(header, message);
Mocker.Resolve<PlexProvider>().Notify(_clientSettings, header, message);
fakeHttp.Verify(v => v.DownloadString(expectedUrl), Times.Once());
}
[Test]
public void Notify_should_send_notifcation_to_all_configured_clients()
{
WithMultipleClients();
const string header = "Test Header";
const string message = "Test Message";
var fakeHttp = Mocker.GetMock<IHttpProvider>();
fakeHttp.Setup(s => s.DownloadString(It.IsAny<string>()))
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(header, message);
fakeHttp.Verify(v => v.DownloadString(It.IsAny<string>()), Times.Exactly(2));
}
[Test]
public void Notify_should_send_notification_with_credentials_when_configured()
{
WithSingleClient();
WithClientCredentials();
const string header = "Test Header";
@ -181,31 +144,10 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(header, message);
Mocker.Resolve<PlexProvider>().Notify(_clientSettings, header, message);
fakeHttp.Verify(v => v.DownloadString(expectedUrl, "plex", "plex"), Times.Once());
}
[Test]
public void Notify_should_send_notification_with_credentials_when_configured_for_all_clients()
{
WithMultipleClients();
WithClientCredentials();
const string header = "Test Header";
const string message = "Test Message";
var fakeHttp = Mocker.GetMock<IHttpProvider>();
fakeHttp.Setup(s => s.DownloadString(It.IsAny<string>(), "plex", "plex"))
.Returns("ok");
Mocker.Resolve<PlexProvider>().Notify(header, message);
fakeHttp.Verify(v => v.DownloadString(It.IsAny<string>(), "plex", "plex"), Times.Exactly(2));
}
}
}

View File

@ -5,6 +5,7 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Notifications.Prowl;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Test.Framework;

View File

@ -8,11 +8,10 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
@ -22,6 +21,7 @@ namespace NzbDrone.Core.Test.ProviderTests
public class XbmcProviderTest : CoreTest
{
private XbmcSettings _settings;
private string EdenActivePlayers;
private void WithNoActivePlayers()
@ -49,6 +49,19 @@ namespace NzbDrone.Core.Test.ProviderTests
EdenActivePlayers = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":[{\"playerid\":1,\"type\":\"audio\"},{\"playerid\":2,\"type\":\"picture\"},{\"playerid\":3,\"type\":\"video\"}]}";
}
[SetUp]
public void Setup()
{
_settings = new XbmcSettings
{
Host = "localhost",
Port = 8080,
AlwaysUpdate = false,
CleanLibrary = false,
UpdateLibrary = true
};
}
[Test]
public void JsonError_true()
{
@ -290,21 +303,13 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test]
public void Notify_true()
{
var header = "NzbDrone Test";
var message = "Test Message!";
var fakeConfig = Mocker.GetMock<IConfigService>();
fakeConfig.SetupGet(s => s.XbmcHosts).Returns("localhost:8080");
//var fakeUdpProvider = Mocker.GetMock<EventClient>();
var fakeEventClient = Mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", "localhost")).Returns(true);
Mocker.Resolve<XbmcProvider>().Notify(header, message);
Mocker.Resolve<XbmcProvider>().Notify(_settings, header, message);
Mocker.VerifyAllMocks();
@ -437,19 +442,10 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test]
public void Clean()
{
var fakeConfig = Mocker.GetMock<IConfigService>();
fakeConfig.SetupGet(s => s.XbmcHosts).Returns("localhost:8080");
var fakeEventClient = Mocker.GetMock<EventClientProvider>();
fakeEventClient.Setup(s => s.SendAction("localhost", ActionType.ExecBuiltin, "ExecBuiltIn(CleanLibrary(video))")).Returns(true);
Mocker.Resolve<XbmcProvider>().Clean();
Mocker.Resolve<XbmcProvider>().Clean(_settings);
Mocker.VerifyAllMocks();
}

View File

@ -126,94 +126,12 @@ namespace NzbDrone.Core.Configuration
set { SetValue("DefaultQualityProfile", value); }
}
public Boolean XbmcUpdateLibrary
{
get { return GetValueBoolean("XbmcUpdateLibrary"); }
set { SetValue("XbmcUpdateLibrary", value); }
}
public Boolean XbmcCleanLibrary
{
get { return GetValueBoolean("XbmcCleanLibrary"); }
set { SetValue("XbmcCleanLibrary", value); }
}
public Boolean XbmcUpdateWhenPlaying
{
get { return GetValueBoolean("XbmcUpdateWhenPlaying"); }
set { SetValue("XbmcUpdateWhenPlaying", value); }
}
public string XbmcHosts
{
get { return GetValue("XbmcHosts", "localhost:8080"); }
set { SetValue("XbmcHosts", value); }
}
public string XbmcUsername
{
get { return GetValue("XbmcUsername", "xbmc"); }
set { SetValue("XbmcUsername", value); }
}
public string XbmcPassword
{
get { return GetValue("XbmcPassword", String.Empty); }
set { SetValue("XbmcPassword", value); }
}
public string UpdateUrl
{
get { return GetValue("UpdateUrl", "http://update.nzbdrone.com/vnext/"); }
set { SetValue("UpdateUrl", value); }
}
public string SmtpServer
{
get { return GetValue("SmtpServer", String.Empty); }
set { SetValue("SmtpServer", value); }
}
public int SmtpPort
{
get { return GetValueInt("SmtpPort", 25); }
set { SetValue("SmtpPort", value); }
}
public Boolean SmtpUseSsl
{
get { return GetValueBoolean("SmtpUseSsl"); }
set { SetValue("SmtpUseSsl", value); }
}
public string SmtpUsername
{
get { return GetValue("SmtpUsername", String.Empty); }
set { SetValue("SmtpUsername", value); }
}
public string SmtpPassword
{
get { return GetValue("SmtpPassword", String.Empty); }
set { SetValue("SmtpPassword", value); }
}
public string SmtpFromAddress
{
get { return GetValue("SmtpFromAddress", String.Empty); }
set { SetValue("SmtpFromAddress", value); }
}
public string SmtpToAddresses
{
get { return GetValue("SmtpToAddresses", String.Empty); }
set { SetValue("SmtpToAddresses", value); }
}
public string TwitterAccessToken
{
get { return GetValue("TwitterAccessToken", String.Empty); }
@ -226,30 +144,6 @@ namespace NzbDrone.Core.Configuration
set { SetValue("TwitterAccessTokenSecret", value); }
}
public string GrowlHost
{
get { return GetValue("GrowlHost", "localhost:23053"); }
set { SetValue("GrowlHost", value); }
}
public string GrowlPassword
{
get { return GetValue("GrowlPassword", String.Empty); }
set { SetValue("GrowlPassword", value); }
}
public string ProwlApiKeys
{
get { return GetValue("ProwlApiKeys", String.Empty); }
set { SetValue("ProwlApiKeys", value); }
}
public int ProwlPriority
{
get { return GetValueInt("ProwlPriority", 0); }
set { SetValue("ProwlPriority", value); }
}
public bool EnableBacklogSearching
{
get { return GetValueBoolean("EnableBacklogSearching"); }
@ -291,37 +185,6 @@ namespace NzbDrone.Core.Configuration
get { return "http://services.nzbdrone.com"; }
}
public Boolean PlexUpdateLibrary
{
get { return GetValueBoolean("PlexUpdateLibrary"); }
set { SetValue("PlexUpdateLibrary", value); }
}
public string PlexServerHost
{
get { return GetValue("PlexServerHost", "localhost:32400"); }
set { SetValue("PlexServerHost", value); }
}
public string PlexClientHosts
{
get { return GetValue("PlexClientHosts", "localhost:3000"); }
set { SetValue("PlexClientHosts", value); }
}
public string PlexUsername
{
get { return GetValue("PlexUsername"); }
set { SetValue("PlexUsername", value); }
}
public string PlexPassword
{
get { return GetValue("PlexPassword"); }
set { SetValue("PlexPassword", value); }
}
public Boolean MetadataUseBanners
{
get { return GetValueBoolean("MetadataUseBanners"); }

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Download;
using NzbDrone.Core.Model;
using NzbDrone.Core.Download.Clients.Nzbget;
using NzbDrone.Core.Download.Clients.Sabnzbd;
@ -11,6 +10,7 @@ namespace NzbDrone.Core.Configuration
{
IEnumerable<Config> All();
Dictionary<String, Object> AllWithDefaults();
string UpdateUrl { get; set; }
String SabHost { get; set; }
int SabPort { get; set; }
String SabApiKey { get; set; }
@ -23,26 +23,8 @@ namespace NzbDrone.Core.Configuration
bool UseSeasonFolder { get; set; }
string SortingSeasonFolderFormat { get; set; }
int DefaultQualityProfile { get; set; }
Boolean XbmcUpdateLibrary { get; set; }
Boolean XbmcCleanLibrary { get; set; }
Boolean XbmcUpdateWhenPlaying { get; set; }
string XbmcHosts { get; set; }
string XbmcUsername { get; set; }
string XbmcPassword { get; set; }
string UpdateUrl { get; set; }
string SmtpServer { get; set; }
int SmtpPort { get; set; }
Boolean SmtpUseSsl { get; set; }
string SmtpUsername { get; set; }
string SmtpPassword { get; set; }
string SmtpFromAddress { get; set; }
string SmtpToAddresses { get; set; }
string TwitterAccessToken { get; set; }
string TwitterAccessTokenSecret { get; set; }
string GrowlHost { get; set; }
string GrowlPassword { get; set; }
string ProwlApiKeys { get; set; }
int ProwlPriority { get; set; }
bool EnableBacklogSearching { get; set; }
bool AutoIgnorePreviouslyDownloadedEpisodes { get; set; }
int Retention { get; set; }
@ -50,11 +32,6 @@ namespace NzbDrone.Core.Configuration
DownloadClientType DownloadClient { get; set; }
string BlackholeDirectory { get; set; }
string ServiceRootUrl { get; }
Boolean PlexUpdateLibrary { get; set; }
string PlexServerHost { get; set; }
string PlexClientHosts { get; set; }
string PlexUsername { get; set; }
string PlexPassword { get; set; }
Boolean MetadataUseBanners { get; set; }
string PneumaticDirectory { get; set; }
string RecycleBin { get; set; }

View File

@ -83,10 +83,12 @@ namespace NzbDrone.Core.Datastore.Migration
.WithColumn("NzbInfoUrl").AsString().Nullable()
.WithColumn("ReleaseGroup").AsString().Nullable();
Create.TableForModel("ExternalNotificationDefinitions")
.WithColumn("Enable").AsBoolean()
.WithColumn("Type").AsString().Unique()
.WithColumn("Name").AsString().Unique();
Create.TableForModel("NotificationDefinitions")
.WithColumn("Name").AsString()
.WithColumn("OnGrab").AsBoolean()
.WithColumn("OnDownload").AsBoolean()
.WithColumn("Settings").AsString()
.WithColumn("Implementation").AsString();
Create.TableForModel("ScheduledTasks")
.WithColumn("TypeName").AsString().Unique()

View File

@ -8,11 +8,11 @@ using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.ExternalNotification;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.RootFolders;
@ -35,7 +35,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<IndexerDefinition>().RegisterModel("IndexerDefinitions");
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks");
Mapper.Entity<ExternalNotificationDefinition>().RegisterModel("ExternalNotificationDefinitions");
Mapper.Entity<NotificationDefinition>().RegisterModel("NotificationDefinitions");
Mapper.Entity<SceneMapping>().RegisterModel("SceneMappings");

View File

@ -4,11 +4,7 @@ using System.Linq;
using NLog;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Model;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Download
{
@ -20,16 +16,14 @@ namespace NzbDrone.Core.Download
public class DownloadService : IDownloadService
{
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IConfigService _configService;
private readonly IMessageAggregator _messageAggregator;
private readonly Logger _logger;
public DownloadService(IProvideDownloadClient downloadClientProvider, IConfigService configService,
public DownloadService(IProvideDownloadClient downloadClientProvider,
IMessageAggregator messageAggregator, Logger logger)
{
_downloadClientProvider = downloadClientProvider;
_configService = configService;
_messageAggregator = messageAggregator;
_logger = logger;
}

View File

@ -1,148 +0,0 @@
using System;
using NLog;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.ExternalNotification
{
public abstract class ExternalNotificationBase
: IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeDownloadedEvent>,
IHandle<SeriesRenamedEvent>
{
private readonly IExternalNotificationRepository _externalNotificationRepository;
private readonly Logger _logger;
protected ExternalNotificationBase(IExternalNotificationRepository externalNotificationRepository, Logger logger)
{
_externalNotificationRepository = externalNotificationRepository;
_logger = logger;
}
public abstract string Name { get; }
public bool NotifyOnGrab
{
get
{
return GetEnableStatus(c => c.OnGrab);
}
set
{
SetEnableStatus(c => c.OnGrab = value);
}
}
public bool NotifyOnDownload
{
get
{
return GetEnableStatus(c => c.OnDownload);
}
set
{
SetEnableStatus(c => c.OnDownload = value);
}
}
public bool NotifyOnRename
{
get
{
return GetEnableStatus(c => c.OnRename);
}
set
{
SetEnableStatus(c => c.OnRename = value);
}
}
private void SetEnableStatus(Action<ExternalNotificationDefinition> updateAction)
{
var def = _externalNotificationRepository.Get(Name) ??
new ExternalNotificationDefinition { Name = Name };
updateAction(def);
_externalNotificationRepository.Upsert(def);
}
private bool GetEnableStatus(Func<ExternalNotificationDefinition, bool> readFunction)
{
var def = _externalNotificationRepository.Get(Name) ??
new ExternalNotificationDefinition { Name = Name };
return readFunction(def);
}
public void Handle(EpisodeGrabbedEvent message)
{
if (NotifyOnGrab)
{
try
{
_logger.Trace("Sending grab notification to {0}", Name);
//todo: pass all the info to grab event and let the handlers deal with it.
OnGrab(message.Episode.ToString());
}
catch (Exception e)
{
_logger.WarnException("Couldn't send grab notification to " + Name, e);
}
}
}
public void Handle(EpisodeDownloadedEvent message)
{
if (NotifyOnDownload)
{
try
{
_logger.Trace("Sending download notification to {0}", Name);
OnDownload(message.ParsedEpisodeInfo.ToString(), message.Series);
}
catch (Exception e)
{
_logger.WarnException("Couldn't send download notification to " + Name, e);
}
}
}
public void Handle(SeriesRenamedEvent message)
{
if (NotifyOnRename)
{
try
{
_logger.Trace("Sending rename notification to {0}", Name);
AfterRename(message.Series);
}
catch (Exception e)
{
_logger.WarnException("Couldn't send rename notification to " + Name, e);
}
}
}
protected virtual void OnGrab(string message)
{
}
protected virtual void OnDownload(string message, Series series)
{
}
protected virtual void AfterRename(Series series)
{
}
}
}

View File

@ -1,13 +0,0 @@
using System.Linq;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.ExternalNotification
{
public class ExternalNotificationDefinition : ModelBase
{
public string Name { get; set; }
public bool OnGrab { get; set; }
public bool OnDownload { get; set; }
public bool OnRename { get; set; }
}
}

View File

@ -1,25 +0,0 @@
using System.Data;
using System.Linq;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.ExternalNotification
{
public interface IExternalNotificationRepository : IBasicRepository<ExternalNotificationDefinition>
{
ExternalNotificationDefinition Get(string name);
}
public class ExternalNotificationRepository : BasicRepository<ExternalNotificationDefinition>, IExternalNotificationRepository
{
public ExternalNotificationRepository(IDatabase database, IMessageAggregator messageAggregator)
: base(database, messageAggregator)
{
}
public ExternalNotificationDefinition Get(string name)
{
return Query.SingleOrDefault(c => c.Name.ToLower() == name.ToLower());
}
}
}

View File

@ -1,48 +0,0 @@
using System.Linq;
using System;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.ExternalNotification
{
public class Growl : ExternalNotificationBase
{
private readonly IConfigService _configService;
private readonly GrowlProvider _growlProvider;
public Growl(IExternalNotificationRepository repository, IConfigService configService, GrowlProvider growlProvider, Logger logger)
: base(repository, logger)
{
_configService = configService;
_growlProvider = growlProvider;
}
public override string Name
{
get { return "Growl"; }
}
protected override void OnGrab(string message)
{
const string title = "Episode Grabbed";
var growlHost = _configService.GrowlHost.Split(':');
var host = growlHost[0];
var port = Convert.ToInt32(growlHost[1]);
_growlProvider.SendNotification(title, message, "GRAB", host, port, _configService.GrowlPassword);
}
protected override void OnDownload(string message, Series series)
{
const string title = "Episode Downloaded";
var growlHost = _configService.GrowlHost.Split(':');
var host = growlHost[0];
var port = Convert.ToInt32(growlHost[1]);
_growlProvider.SendNotification(title, message, "DOWNLOAD", host, port, _configService.GrowlPassword);
}
}
}

View File

@ -1,51 +0,0 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.ExternalNotification
{
public class Plex : ExternalNotificationBase
{
private readonly IConfigService _configService;
private readonly PlexProvider _plexProvider;
public Plex(IConfigService configService, IExternalNotificationRepository repository, PlexProvider plexProvider, Logger logger)
: base(repository, logger)
{
_configService = configService;
_plexProvider = plexProvider;
}
public override string Name
{
get { return "Plex"; }
}
protected override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
_plexProvider.Notify(header, message);
}
protected override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
_plexProvider.Notify(header, message);
UpdateIfEnabled();
}
protected override void AfterRename( Series series)
{
UpdateIfEnabled();
}
private void UpdateIfEnabled()
{
if (_configService.PlexUpdateLibrary)
{
_plexProvider.UpdateLibrary();
}
}
}
}

View File

@ -1,48 +0,0 @@
using System.Linq;
using System;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Tv;
using Prowlin;
namespace NzbDrone.Core.ExternalNotification
{
public class Prowl : ExternalNotificationBase
{
private readonly IConfigService _configService;
private readonly ProwlProvider _prowlProvider;
public Prowl(IConfigService configService, IExternalNotificationRepository repository, ProwlProvider prowlProvider, Logger logger)
: base(repository, logger)
{
_configService = configService;
_prowlProvider = prowlProvider;
}
public override string Name
{
get { return "Prowl"; }
}
protected override void OnGrab(string message)
{
const string title = "Episode Grabbed";
var apiKeys = _configService.ProwlApiKeys;
var priority = _configService.ProwlPriority;
_prowlProvider.SendNotification(title, message, apiKeys, (NotificationPriority)priority);
}
protected override void OnDownload(string message, Series series)
{
const string title = "Episode Downloaded";
var apiKeys = _configService.ProwlApiKeys;
var priority = _configService.ProwlPriority;
_prowlProvider.SendNotification(title, message, apiKeys, (NotificationPriority)priority);
}
}
}

View File

@ -1,59 +0,0 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.ExternalNotification
{
public class Xbmc : ExternalNotificationBase
{
private readonly IConfigService _configService;
private readonly XbmcProvider _xbmcProvider;
public Xbmc(IConfigService configService, IExternalNotificationRepository repository, XbmcProvider xbmcProvider, Logger logger)
: base(repository, logger)
{
_configService = configService;
_xbmcProvider = xbmcProvider;
}
public override string Name
{
get { return "XBMC"; }
}
protected override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
_xbmcProvider.Notify(header, message);
}
protected override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
_xbmcProvider.Notify(header, message);
UpdateAndClean(series);
}
protected override void AfterRename(Series series)
{
UpdateAndClean(series);
}
private void UpdateAndClean(Series series)
{
if (_configService.XbmcUpdateLibrary)
{
_xbmcProvider.Update(series);
}
if (_configService.XbmcCleanLibrary)
{
_xbmcProvider.Clean();
}
}
}
}

View File

@ -134,7 +134,5 @@ namespace NzbDrone.Core.IndexerSearch
return _makeDownloadDecision.GetSearchDecision(reports, definitionBase).ToList();
}
}
}

View File

@ -18,7 +18,7 @@ namespace NzbDrone.Core.Indexers
Name = Name,
Enable = true,
Implementation = GetType().Name,
Settings = string.Empty
Settings = String.Empty
};
}
}
@ -32,7 +32,6 @@ namespace NzbDrone.Core.Indexers
}
public abstract IEnumerable<string> RecentFeed { get; }
public abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber);
public abstract IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, DateTime date);
public abstract IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int seasonNumber);

View File

@ -40,7 +40,5 @@ namespace NzbDrone.Core.Indexers.NzbsRUs
{
return new List<string>();
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Growl
{
public class Growl : NotificationWithSetting<GrowlSettings>
{
private readonly GrowlProvider _growlProvider;
public Growl(GrowlProvider growlProvider)
{
_growlProvider = growlProvider;
}
public override string Name
{
get { return "Growl"; }
}
public override void OnGrab(string message)
{
const string title = "Episode Grabbed";
_growlProvider.SendNotification(title, message, "GRAB", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnDownload(string message, Series series)
{
const string title = "Episode Downloaded";
_growlProvider.SendNotification(title, message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
}
public override void AfterRename(Series series)
{
}
}
}

View File

@ -3,8 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using Growl.Connector;
using NLog;
using GrowlNotification = Growl.Connector.Notification;
namespace NzbDrone.Core.ExternalNotification
namespace NzbDrone.Core.Notifications.Growl
{
public class GrowlProvider
{
@ -39,7 +40,7 @@ namespace NzbDrone.Core.ExternalNotification
{
var notificationType = _notificationTypes.Single(n => n.Name == notificationTypeName);
var notification = new Notification("NzbDrone", notificationType.Name, DateTime.Now.Ticks.ToString(), title, message);
var notification = new GrowlNotification("NzbDrone", notificationType.Name, DateTime.Now.Ticks.ToString(), title, message);
_growlConnector = new GrowlConnector(password, hostname, port);

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Growl
{
public class GrowlSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "Host", HelpText = "Growl Host (IP or Hostname)")]
public String Host { get; set; }
[FieldDefinition(1, Label = "Port", HelpText = "Growl Port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Password", HelpText = "Password for Growl")]
public String Password { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(Host) && !string.IsNullOrWhiteSpace(Password) && Port > 0;
}
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Notifications
{
public interface INotifcationSettings
{
bool IsValid { get; }
}
public class NullSetting : INotifcationSettings
{
public static NullSetting Instance = new NullSetting();
private NullSetting()
{
}
public bool IsValid
{
get
{
return true;
}
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications
{
public interface INotification
{
string Name { get; }
NotificationDefinition InstanceDefinition { get; set; }
void OnGrab(string message);
void OnDownload(string message, Series series);
void AfterRename(Series series);
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Notifications
{
public class Notification
{
public int Id { get; set; }
public string Name { get; set; }
public bool OnGrab { get; set; }
public bool OnDownload { get; set; }
public INotifcationSettings Settings { get; set; }
public INotification Instance { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using NLog;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications
{
public abstract class NotificationBase : INotification
{
public abstract string Name { get; }
public NotificationDefinition InstanceDefinition { get; set; }
public abstract void OnGrab(string message);
public abstract void OnDownload(string message, Series series);
public abstract void AfterRename(Series series);
}
}

View File

@ -0,0 +1,14 @@
using System;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Notifications
{
public class NotificationDefinition : ModelBase
{
public String Name { get; set; }
public Boolean OnGrab { get; set; }
public Boolean OnDownload { get; set; }
public String Settings { get; set; }
public String Implementation { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Linq;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Notifications
{
public interface INotificationRepository : IBasicRepository<NotificationDefinition>
{
NotificationDefinition Get(string name);
NotificationDefinition Find(string name);
}
public class NotificationRepository : BasicRepository<NotificationDefinition>, INotificationRepository
{
public NotificationRepository(IDatabase database, IMessageAggregator messageAggregator)
: base(database, messageAggregator)
{
}
public NotificationDefinition Get(string name)
{
return Query.Single(i => i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
}
public NotificationDefinition Find(string name)
{
return Query.SingleOrDefault(i => i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Composition;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
namespace NzbDrone.Core.Notifications
{
public interface INotificationService
{
List<Notification> All();
}
public class NotificationService
: INotificationService,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeDownloadedEvent>,
IHandle<SeriesRenamedEvent>
{
private readonly INotificationRepository _notificationRepository;
private readonly IContainer _container;
private readonly List<INotification> _notifications;
private readonly Logger _logger;
public NotificationService(INotificationRepository notificationRepository,
IEnumerable<INotification> notifications,
IContainer container,
Logger logger)
{
_notificationRepository = notificationRepository;
_container = container;
_notifications = notifications.ToList();
_logger = logger;
}
public List<Notification> All()
{
return _notificationRepository.All().Select(ToNotification).ToList();
}
private Notification ToNotification(NotificationDefinition definition)
{
var notification = new Notification();
notification.Id = definition.Id;
notification.OnGrab = definition.OnGrab;
notification.OnDownload = definition.OnDownload;
notification.Instance = GetInstance(definition);
notification.Name = definition.Name;
if (notification.Instance.GetType().GetMethod("ImportSettingsFromJson") != null)
{
notification.Settings = ((dynamic)notification.Instance).ImportSettingsFromJson(definition.Settings);
}
else
{
notification.Settings = NullSetting.Instance;
}
return notification;
}
private INotification GetInstance(NotificationDefinition indexerDefinition)
{
var type = _notifications.Single(c => c.GetType().Name.Equals(indexerDefinition.Implementation, StringComparison.InvariantCultureIgnoreCase)).GetType();
var instance = (INotification)_container.Resolve(type);
instance.InstanceDefinition = indexerDefinition;
return instance;
}
public void Handle(EpisodeGrabbedEvent message)
{
All().Where(n => n.OnGrab)
.ToList()
.ForEach(notification =>
notification.Instance
.OnGrab("Grabbed!")
);
}
public void Handle(EpisodeDownloadedEvent message)
{
All().Where(n => n.OnDownload)
.ToList()
.ForEach(notification =>
notification.Instance
.OnDownload("Downloaded!", message.Series)
);
}
public void Handle(SeriesRenamedEvent message)
{
All().Where(n => n.OnDownload)
.ToList()
.ForEach(notification =>
notification.Instance
.OnDownload("Renamed!", message.Series)
);
}
}
}

View File

@ -0,0 +1,31 @@
using Newtonsoft.Json;
namespace NzbDrone.Core.Notifications
{
public interface INotificationSettingsProvider
{
TSetting Get<TSetting>(INotification indexer) where TSetting : INotifcationSettings, new();
}
public class NotificationSettingsProvider : INotificationSettingsProvider
{
private readonly INotificationRepository _notificationRepository;
public NotificationSettingsProvider(INotificationRepository notificationRepository)
{
_notificationRepository = notificationRepository;
}
public TSetting Get<TSetting>(INotification indexer) where TSetting : INotifcationSettings, new()
{
var indexerDef = _notificationRepository.Find(indexer.Name);
if (indexerDef == null || string.IsNullOrWhiteSpace(indexerDef.Settings))
{
return new TSetting();
}
return JsonConvert.DeserializeObject<TSetting>(indexerDef.Settings);
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Notifications
{
public abstract class NotificationWithSetting<TSetting> : NotificationBase where TSetting : class, INotifcationSettings, new()
{
public TSetting Settings { get; private set; }
public TSetting ImportSettingsFromJson(string json)
{
Settings = Json.Deserialize<TSetting>(json) ?? new TSetting();
return Settings;
}
}
}

View File

@ -0,0 +1,37 @@
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexClient : NotificationWithSetting<PlexClientSettings>
{
private readonly PlexProvider _plexProvider;
public PlexClient(PlexProvider plexProvider)
{
_plexProvider = plexProvider;
}
public override string Name
{
get { return "Plex Client"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
_plexProvider.Notify(Settings, header, message);
}
public override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
_plexProvider.Notify(Settings, header, message);
}
public override void AfterRename(Series series)
{
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexClientSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "Host", HelpText = "Plex Client Host (IP or Hostname)")]
public String Host { get; set; }
[FieldDefinition(1, Label = "Port", HelpText = "Plex Client Port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Username", HelpText = "Username for Plex")]
public String Username { get; set; }
[FieldDefinition(3, Label = "Password", HelpText = "Password for Plex")]
public String Password { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(Host);
}
}
}
}

View File

@ -6,46 +6,33 @@ using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.ExternalNotification
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexProvider
{
private readonly IHttpProvider _httpProvider;
private readonly IConfigService _configService;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public PlexProvider(IHttpProvider httpProvider, IConfigService configService)
public PlexProvider(IHttpProvider httpProvider)
{
_httpProvider = httpProvider;
_configService = configService;
}
public PlexProvider()
public virtual void Notify(PlexClientSettings settings, string header, string message)
{
}
public virtual void Notify(string header, string message)
{
//Foreach plex client send a notification
foreach(var host in _configService.PlexClientHosts.Split(','))
try
{
try
{
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", header, message);
SendCommand(host.Trim(), command, _configService.PlexUsername, _configService.PlexPassword);
}
catch(Exception ex)
{
logger.WarnException("Failed to send notification to Plex Client: " + host.Trim(), ex);
}
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", header, message);
SendCommand(settings.Host, command, settings.Username, settings.Password);
}
catch(Exception ex)
{
logger.WarnException("Failed to send notification to Plex Client: " + settings.Host, ex);
}
}
public virtual void UpdateLibrary()
public virtual void UpdateLibrary(string host)
{
var host = _configService.PlexServerHost;
try
{
logger.Trace("Sending Update Request to Plex Server");
@ -91,14 +78,11 @@ namespace NzbDrone.Core.ExternalNotification
return _httpProvider.DownloadString(url);
}
public virtual void TestNotification(string hosts, string username, string password)
public virtual void TestNotification(string host, string username, string password)
{
foreach (var host in hosts.Split(','))
{
logger.Trace("Sending Test Notifcation to XBMC Host: {0}", host);
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
SendCommand(host.Trim(), command, _configService.PlexUsername, _configService.PlexPassword);
}
logger.Trace("Sending Test Notifcation to XBMC Host: {0}", host);
var command = String.Format("ExecBuiltIn(Notification({0}, {1}))", "Test Notification", "Success! Notifications are setup correctly");
SendCommand(host.Trim(), command, username, password);
}
}
}

View File

@ -0,0 +1,43 @@
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexServer : NotificationWithSetting<PlexServerSettings>
{
private readonly PlexProvider _plexProvider;
public PlexServer(PlexProvider plexProvider)
{
_plexProvider = plexProvider;
}
public override string Name
{
get { return "Plex Server"; }
}
public override void OnGrab(string message)
{
}
public override void OnDownload(string message, Series series)
{
UpdateIfEnabled();
}
public override void AfterRename(Series series)
{
UpdateIfEnabled();
}
private void UpdateIfEnabled()
{
if (Settings.UpdateLibrary)
{
_plexProvider.UpdateLibrary(Settings.Host);
}
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Plex
{
public class PlexServerSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "Host", HelpText = "Plex Server Host (IP or Hostname)")]
public String Host { get; set; }
[FieldDefinition(1, Label = "Update Library", HelpText = "Update Library on Download/Rename")]
public Boolean UpdateLibrary { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(Host);
}
}
}
}

View File

@ -0,0 +1,38 @@
using NzbDrone.Core.Tv;
using Prowlin;
namespace NzbDrone.Core.Notifications.Prowl
{
public class Prowl : NotificationWithSetting<ProwlSettings>
{
private readonly ProwlProvider _prowlProvider;
public Prowl(ProwlProvider prowlProvider)
{
_prowlProvider = prowlProvider;
}
public override string Name
{
get { return "Prowl"; }
}
public override void OnGrab(string message)
{
const string title = "Episode Grabbed";
_prowlProvider.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void OnDownload(string message, Series series)
{
const string title = "Episode Downloaded";
_prowlProvider.SendNotification(title, message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void AfterRename(Series series)
{
}
}
}

View File

@ -1,9 +1,8 @@
using System;
using System.Linq;
using NLog;
using Prowlin;
namespace NzbDrone.Core.Providers
namespace NzbDrone.Core.Notifications.Prowl
{
public class ProwlProvider
{
@ -34,11 +33,11 @@ namespace NzbDrone.Core.Providers
return false;
}
public virtual bool SendNotification(string title, string message, string apiKeys, NotificationPriority priority = NotificationPriority.Normal, string url = null)
public virtual bool SendNotification(string title, string message, string apiKey, NotificationPriority priority = NotificationPriority.Normal, string url = null)
{
try
{
var notification = new Notification
var notification = new Prowlin.Notification
{
Application = "NzbDrone",
Description = message,
@ -47,8 +46,7 @@ namespace NzbDrone.Core.Providers
Url = url
};
foreach (var apiKey in apiKeys.Split(','))
notification.AddApiKey(apiKey.Trim());
notification.AddApiKey(apiKey.Trim());
var client = new ProwlClient();
@ -63,7 +61,7 @@ namespace NzbDrone.Core.Providers
catch (Exception ex)
{
Logger.TraceException(ex.Message, ex);
Logger.Warn("Invalid API Key(s): {0}", apiKeys);
Logger.Warn("Invalid API Key: {0}", apiKey);
}
return false;

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Prowl
{
public class ProwlSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "API Key", HelpText = "API Key for Prowl")]
public String ApiKey { get; set; }
[FieldDefinition(1, Label = "Priority", HelpText = "Priority to send messages at")]
public Int32 Priority { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(ApiKey) && Priority > 0;
}
}
}
}

View File

@ -1,17 +1,15 @@
using System.Linq;
using System;
using System;
using NLog;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.ExternalNotification
namespace NzbDrone.Core.Notifications.Smtp
{
public class Smtp : ExternalNotificationBase
public class Smtp : NotificationWithSetting<SmtpSettings>
{
private readonly SmtpProvider _smtpProvider;
public Smtp(IExternalNotificationRepository repository, SmtpProvider smtpProvider, Logger logger)
: base(repository, logger)
public Smtp(SmtpProvider smtpProvider)
{
_smtpProvider = smtpProvider;
}
@ -21,19 +19,24 @@ namespace NzbDrone.Core.ExternalNotification
get { return "SMTP"; }
}
protected override void OnGrab(string message)
public override void OnGrab(string message)
{
const string subject = "NzbDrone [TV] - Grabbed";
var body = String.Format("{0} sent to SABnzbd queue.", message);
_smtpProvider.SendEmail(subject, body);
_smtpProvider.SendEmail(Settings, subject, body);
}
protected override void OnDownload(string message, Series series)
public override void OnDownload(string message, Series series)
{
const string subject = "NzbDrone [TV] - Downloaded";
var body = String.Format("{0} Downloaded and sorted.", message);
_smtpProvider.SendEmail(subject, body);
_smtpProvider.SendEmail(Settings, subject, body);
}
public override void AfterRename(Series series)
{
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Net;
using System.Net.Mail;
using NLog;
namespace NzbDrone.Core.Notifications.Smtp
{
public class SmtpProvider
{
private readonly Logger _logger;
public SmtpProvider(Logger logger)
{
_logger = logger;
}
// 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!)";
public virtual void SendEmail(SmtpSettings settings, string subject, string body, bool htmlBody = false)
{
var email = new MailMessage();
email.From = new MailAddress(settings.From);
email.To.Add(settings.To);
email.Subject = subject;
email.Body = body;
email.IsBodyHtml = htmlBody;
NetworkCredential credentials = null;
if (!String.IsNullOrWhiteSpace(settings.Username))
credentials = new NetworkCredential(settings.Username, settings.Password);
try
{
Send(email, settings.Server, settings.Port, settings.UseSsl, credentials);
}
catch(Exception ex)
{
_logger.Error("Error sending email. Subject: {0}", email.Subject);
_logger.TraceException(ex.Message, ex);
}
}
public virtual void Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
{
try
{
var smtp = new SmtpClient(server, port);
smtp.EnableSsl = ssl;
smtp.Credentials = credentials;
smtp.Send(email);
}
catch (Exception ex)
{
_logger.ErrorException("There was an error sending an email.", ex);
throw;
}
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Smtp
{
public class SmtpSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "Server", HelpText = "Hostname or IP of SMTP server")]
public String Server { get; set; }
[FieldDefinition(1, Label = "Port", HelpText = "SMTP Server Port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Use SSL", HelpText = "Does your SMTP server use SSL?")]
public Boolean UseSsl { get; set; }
[FieldDefinition(3, Label = "Username", HelpText = "SMTP Server Username")]
public String Username { get; set; }
[FieldDefinition(4, Label = "Password", HelpText = "SMTP Server Password")]
public String Password { get; set; }
[FieldDefinition(5, Label = "From Address", HelpText = "Sender's address")]
public String From { get; set; }
[FieldDefinition(6, Label = "To Address", HelpText = "Recipient address")]
public String To { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(Server) && Port > 0 && !string.IsNullOrWhiteSpace(From) && !string.IsNullOrWhiteSpace(To);
}
}
}
}

View File

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text;
using NzbDrone.Common;
using NzbDrone.Core.Model.Xbmc;
namespace NzbDrone.Core.Providers.Xbmc
namespace NzbDrone.Core.Notifications.Xbmc
{
public class EventClientProvider
{

View File

@ -1,7 +1,7 @@
using System.Drawing;
using System.IO;
namespace NzbDrone.Core.Providers.Xbmc
namespace NzbDrone.Core.Notifications.Xbmc
{
public class ResourceManager
{

View File

@ -0,0 +1,53 @@
using NLog;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Xbmc
{
public class Xbmc : NotificationWithSetting<XbmcSettings>
{
private readonly XbmcProvider _xbmcProvider;
public Xbmc(XbmcProvider xbmcProvider, Logger logger)
{
_xbmcProvider = xbmcProvider;
}
public override string Name
{
get { return "XBMC"; }
}
public override void OnGrab(string message)
{
const string header = "NzbDrone [TV] - Grabbed";
_xbmcProvider.Notify(Settings, header, message);
}
public override void OnDownload(string message, Series series)
{
const string header = "NzbDrone [TV] - Downloaded";
_xbmcProvider.Notify(Settings, header, message);
UpdateAndClean(series);
}
public override void AfterRename(Series series)
{
UpdateAndClean(series);
}
private void UpdateAndClean(Series series)
{
if (Settings.UpdateLibrary)
{
_xbmcProvider.Update(Settings, series);
}
if (Settings.CleanLibrary)
{
_xbmcProvider.Clean(Settings);
}
}
}
}

View File

@ -7,118 +7,105 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Model.Xbmc;
using NzbDrone.Core.Providers.Xbmc;
namespace NzbDrone.Core.Providers
namespace NzbDrone.Core.Notifications.Xbmc
{
public class XbmcProvider
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IConfigService _configService;
private readonly IHttpProvider _httpProvider;
private readonly EventClientProvider _eventClientProvider;
public XbmcProvider(IConfigService configService, IHttpProvider httpProvider, EventClientProvider eventClientProvider)
public XbmcProvider(IHttpProvider httpProvider, EventClientProvider eventClientProvider)
{
_configService = configService;
_httpProvider = httpProvider;
_eventClientProvider = eventClientProvider;
}
public XbmcProvider()
{
}
public virtual void Notify(string header, string message)
public virtual void Notify(XbmcSettings settings, string header, string message)
{
//Always use EventServer, until Json has real support for it
foreach (var host in _configService.XbmcHosts.Split(','))
{
Logger.Trace("Sending Notifcation to XBMC Host: {0}", host);
_eventClientProvider.SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", GetHostWithoutPort(host));
}
var host = settings.Host;
Logger.Trace("Sending Notifcation to XBMC Host: {0}", host);
_eventClientProvider.SendNotification(header, message, IconType.Jpeg, "NzbDrone.jpg", host);
}
public virtual void Update(Series series)
public virtual void Update(XbmcSettings settings, Series series)
{
//Use Json for Eden/Nightly or depricated HTTP for 10.x (Dharma) to get the proper path
//Perform update with EventServer (Json currently doesn't support updating a specific path only - July 2011)
var username = _configService.XbmcUsername;
var password = _configService.XbmcPassword;
var username = settings.Username;
var password = settings.Password;
var host = settings.Host;
foreach (var host in _configService.XbmcHosts.Split(','))
Logger.Trace("Determining version of XBMC Host: {0}", host);
var version = GetJsonVersion(host, username, password);
//If Dharma
if (version == new XbmcVersion(2))
{
Logger.Trace("Determining version of XBMC Host: {0}", host);
var version = GetJsonVersion(host, username, password);
//If Dharma
if (version == new XbmcVersion(2))
//Check for active player only when we should skip updates when playing
if (!settings.AlwaysUpdate)
{
//Check for active player only when we should skip updates when playing
if (!_configService.XbmcUpdateWhenPlaying)
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersDharma(host, username, password);
//If video is currently playing, then skip update
if (activePlayers["video"])
{
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersDharma(host, username, password);
//If video is currently playing, then skip update
if (activePlayers["video"])
{
Logger.Debug("Video is currently playing, skipping library update");
continue;
}
Logger.Debug("Video is currently playing, skipping library update");
return;
}
}
UpdateWithHttp(series, host, username, password);
}
UpdateWithHttp(series, host, username, password);
//If Eden or newer (attempting to make it future compatible)
else if (version == new XbmcVersion(3) || version == new XbmcVersion(4))
{
//Check for active player only when we should skip updates when playing
if (!settings.AlwaysUpdate)
{
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersEden(host, username, password);
//If video is currently playing, then skip update
if (activePlayers.Any(a => a.Type.Equals("video")))
{
Logger.Debug("Video is currently playing, skipping library update");
return;
}
}
//If Eden or newer (attempting to make it future compatible)
else if (version == new XbmcVersion(3) || version == new XbmcVersion(4))
UpdateWithJsonExecBuiltIn(series, host, username, password);
}
else if (version >= new XbmcVersion(5))
{
//Check for active player only when we should skip updates when playing
if (!settings.AlwaysUpdate)
{
//Check for active player only when we should skip updates when playing
if (!_configService.XbmcUpdateWhenPlaying)
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersEden(host, username, password);
//If video is currently playing, then skip update
if (activePlayers.Any(a => a.Type.Equals("video")))
{
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersEden(host, username, password);
//If video is currently playing, then skip update
if (activePlayers.Any(a => a.Type.Equals("video")))
{
Logger.Debug("Video is currently playing, skipping library update");
continue;
}
Logger.Debug("Video is currently playing, skipping library update");
return;
}
UpdateWithJsonExecBuiltIn(series, host, username, password);
}
else if (version >= new XbmcVersion(5))
{
//Check for active player only when we should skip updates when playing
if (!_configService.XbmcUpdateWhenPlaying)
{
Logger.Trace("Determining if there are any active players on XBMC host: {0}", host);
var activePlayers = GetActivePlayersEden(host, username, password);
//If video is currently playing, then skip update
if (activePlayers.Any(a => a.Type.Equals("video")))
{
Logger.Debug("Video is currently playing, skipping library update");
continue;
}
}
UpdateWithJsonVideoLibraryScan(series, host, username, password);
}
UpdateWithJsonVideoLibraryScan(series, host, username, password);
}
//Log Version zero if check failed
else
Logger.Trace("Unknown version: [{0}], skipping.", version);
}
else
Logger.Trace("Unknown version: [{0}], skipping.", version);
}
public virtual bool UpdateWithJsonExecBuiltIn(Series series, string host, string username, string password)
@ -245,16 +232,14 @@ namespace NzbDrone.Core.Providers
return true;
}
public virtual void Clean()
public virtual void Clean(XbmcSettings settings)
{
//Use EventServer, once Dharma is extinct use Json?
foreach (var host in _configService.XbmcHosts.Split(','))
{
Logger.Trace("Sending DB Clean Request to XBMC Host: {0}", host);
var command = "ExecBuiltIn(CleanLibrary(video))";
_eventClientProvider.SendAction(GetHostWithoutPort(host), ActionType.ExecBuiltin, command);
}
var host = settings.Host;
Logger.Trace("Sending DB Clean Request to XBMC Host: {0}", host);
var command = "ExecBuiltIn(CleanLibrary(video))";
_eventClientProvider.SendAction(host, ActionType.ExecBuiltin, command);
}
public virtual string SendCommand(string host, string command, string username, string password)
@ -451,7 +436,7 @@ namespace NzbDrone.Core.Providers
foreach (var host in hosts.Split(','))
{
Logger.Trace("Sending Test Notifcation to XBMC Host: {0}", host);
_eventClientProvider.SendNotification("Test Notification", "Success! Notifications are setup correctly", IconType.Jpeg, "NzbDrone.jpg", GetHostWithoutPort(host));
_eventClientProvider.SendNotification("Test Notification", "Success! Notifications are setup correctly", IconType.Jpeg, "NzbDrone.jpg", host);
}
}
@ -465,10 +450,5 @@ namespace NzbDrone.Core.Providers
throw new Exception("Failed to get JSON version in test");
}
}
private string GetHostWithoutPort(string address)
{
return address.Split(':')[0];
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Notifications.Xbmc
{
public class XbmcSettings : INotifcationSettings
{
[FieldDefinition(0, Label = "Host", HelpText = "XBMC Hostnname or IP")]
public String Host { get; set; }
[FieldDefinition(1, Label = "Port", HelpText = "Webserver port")]
public Int32 Port { get; set; }
[FieldDefinition(2, Label = "Username", HelpText = "Webserver Username")]
public String Username { get; set; }
[FieldDefinition(3, Label = "Password", HelpText = "Webserver Password ")]
public String Password { get; set; }
[FieldDefinition(4, Label = "Update Library", HelpText = "Update Library on Download & Rename?")]
public Boolean UpdateLibrary { get; set; }
[FieldDefinition(5, Label = "Update Library", HelpText = "Clean Library after update?")]
public Boolean CleanLibrary { get; set; }
[FieldDefinition(6, Label = "Always Update", HelpText = "Update Library even when a video is playing?")]
public Boolean AlwaysUpdate { get; set; }
public bool IsValid
{
get
{
return !string.IsNullOrWhiteSpace(Host) && Port > 0;
}
}
}
}

View File

@ -254,7 +254,12 @@
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
<Compile Include="Download\SeriesRenamedEvent.cs" />
<Compile Include="ExternalNotification\ExternalNotificationRepository.cs" />
<Compile Include="Notifications\Growl\GrowlSettings.cs" />
<Compile Include="Notifications\NotificationSettingsProvider.cs" />
<Compile Include="Notifications\INotification.cs" />
<Compile Include="Notifications\Notification.cs" />
<Compile Include="Notifications\NotificationService.cs" />
<Compile Include="Notifications\NotificationRepository.cs" />
<Compile Include="Fluent.cs" />
<Compile Include="Helpers\SortHelper.cs" />
<Compile Include="History\HistoryRepository.cs" />
@ -282,7 +287,7 @@
<Compile Include="Indexers\Nzbx\NzbxParser.cs" />
<Compile Include="Indexers\Omgwtfnzbs\Omgwtfnzbs.cs" />
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsParser.cs" />
<Compile Include="Indexers\IIndexerSettings.cs" />
<Compile Include="Indexers\IIndexerSetting.cs" />
<Compile Include="Indexers\NzbsRUs\NzbsrusSettings.cs" />
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsSettings.cs" />
<Compile Include="Indexers\Wombles\Wombles.cs" />
@ -307,6 +312,14 @@
<Compile Include="MetadataSource\Trakt\Images.cs" />
<Compile Include="MetadataSource\Trakt\Season.cs" />
<Compile Include="MetadataSource\Trakt\Show.cs" />
<Compile Include="Notifications\INotifcationSettings.cs" />
<Compile Include="Notifications\NotificationWithSetting.cs" />
<Compile Include="Notifications\Plex\PlexServer.cs" />
<Compile Include="Notifications\Plex\PlexClientSettings.cs" />
<Compile Include="Notifications\Plex\PlexServerSettings.cs" />
<Compile Include="Notifications\Prowl\ProwlSettings.cs" />
<Compile Include="Notifications\Smtp\SmtpSettings.cs" />
<Compile Include="Notifications\Xbmc\XbmcSettings.cs" />
<Compile Include="Organizer\EpisodeSortingType.cs" />
<Compile Include="Organizer\FileNameBuilder.cs" />
<Compile Include="Instrumentation\LogService.cs" />
@ -417,25 +430,25 @@
<Compile Include="Tv\EpisodeRepository.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\ExternalNotificationBase.cs">
<Compile Include="Notifications\NotificationBase.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\Growl.cs">
<Compile Include="Notifications\Growl\Growl.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\Plex.cs">
<Compile Include="Notifications\Plex\PlexClient.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\Prowl.cs">
<Compile Include="Notifications\Prowl\Prowl.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\Smtp.cs">
<Compile Include="Notifications\Smtp\Smtp.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\Xbmc.cs">
<Compile Include="Notifications\Xbmc\Xbmc.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\GrowlProvider.cs">
<Compile Include="Notifications\Growl\GrowlProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="History\HistoryService.cs">
@ -459,11 +472,11 @@
<Compile Include="Providers\NotificationProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\PlexProvider.cs" />
<Compile Include="Notifications\Plex\PlexProvider.cs" />
<Compile Include="MediaFiles\DownloadedEpisodesImportService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\ProwlProvider.cs">
<Compile Include="Notifications\Prowl\ProwlProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Qualities\QualityProfileService.cs">
@ -479,19 +492,19 @@
<Compile Include="Tv\SeriesService.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\SmtpProvider.cs">
<Compile Include="Notifications\Smtp\SmtpProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\XbmcProvider.cs">
<Compile Include="Notifications\Xbmc\XbmcProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\Xbmc\EventClientProvider.cs">
<Compile Include="Notifications\Xbmc\EventClientProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Providers\Xbmc\ResourceManager.cs">
<Compile Include="Notifications\Xbmc\ResourceManager.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExternalNotification\ExternalNotificationDefinition.cs" />
<Compile Include="Notifications\NotificationDefinition.cs" />
<Compile Include="Download\Clients\Sabnzbd\SabPriorityType.cs" />
<Compile Include="MediaFiles\EpisodeFile.cs" />
<Compile Include="Model\Notification\ProgressNotificationStatus.cs" />

View File

@ -1,111 +0,0 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Mail;
using NLog;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Providers
{
public class SmtpProvider
{
private readonly IConfigService _configService;
private readonly Logger _logger;
public SmtpProvider(IConfigService configService, Logger logger)
{
_configService = configService;
_logger = logger;
}
public virtual void SendEmail(string subject, string body, bool htmlBody = false)
{
var email = new MailMessage();
email.From = new MailAddress(_configService.SmtpFromAddress);
foreach (var toAddress in _configService.SmtpToAddresses.Split(','))
{
email.To.Add(toAddress.Trim());
}
email.Subject = subject;
email.Body = body;
email.IsBodyHtml = htmlBody;
var username = _configService.SmtpUsername;
var password = _configService.SmtpPassword;
NetworkCredential credentials = null;
if (!String.IsNullOrWhiteSpace(username))
credentials = new NetworkCredential(username, password);
try
{
Send(email, _configService.SmtpServer, _configService.SmtpPort, _configService.SmtpUseSsl, credentials);
}
catch(Exception ex)
{
_logger.Error("Error sending email. Subject: {0}", email.Subject);
_logger.TraceException(ex.Message, ex);
}
}
public virtual bool SendTestEmail(string server, int port, bool ssl, string username, string password, string fromAddress, string toAddresses)
{
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 email = new MailMessage();
email.From = new MailAddress(fromAddress);
foreach (var toAddress in toAddresses.Split(','))
{
email.To.Add(toAddress.Trim());
}
email.Subject = subject;
email.Body = body;
email.IsBodyHtml = false;
NetworkCredential credentials = null;
if (!String.IsNullOrWhiteSpace(username))
credentials = new NetworkCredential(username, password);
try
{
Send(email, server, port, ssl, credentials);
}
catch(Exception ex)
{
_logger.TraceException("Failed to send test email", ex);
return false;
}
return true;
}
public virtual void Send(MailMessage email, string server, int port, bool ssl, NetworkCredential credentials)
{
try
{
var smtp = new SmtpClient(server, port);
smtp.EnableSsl = ssl;
smtp.Credentials = credentials;
smtp.Send(email);
}
catch (Exception ex)
{
_logger.ErrorException("There was an error sending an email.", ex);
throw;
}
}
}
}

View File

@ -6,7 +6,7 @@ using NzbDrone.Api.SignalR;
using NzbDrone.Common;
using NzbDrone.Common.Composition;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.ExternalNotification;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.RootFolders;
@ -25,7 +25,7 @@ namespace NzbDrone
private MainAppContainerBuilder()
: base("NzbDrone", "NzbDrone.Common", "NzbDrone.Core", "NzbDrone.Api")
{
AutoRegisterImplementations<ExternalNotificationBase>();
AutoRegisterImplementations<NotificationBase>();
AutoRegisterImplementations<NzbDronePersistentConnection>();
Container.Register(typeof(IBasicRepository<RootFolder>), typeof(BasicRepository<RootFolder>));

View File

@ -0,0 +1,19 @@
<div class="control-group">
<label class="control-label">{{label}}</label>
<div class="controls">
<label class="checkbox toggle well">
<input type="checkbox" name="fields.{{order}}.value"/>
<p>
<span>On</span>
<span>Off</span>
</p>
<div class="btn btn-primary slide-button"></div>
</label>
<span class="help-inline-checkbox">
<i class="icon-question-sign" title="{{helpText}}"></i>
</span>
</div>
</div>

View File

@ -0,0 +1,10 @@
<div class="control-group">
<label class="control-label">{{label}}</label>
<div class="controls">
<input type="text" name="fields.{{order}}.value"/>
<span class="help-inline">
<i class="icon-question-sign" title="{{helpText}}"></i>
</span>
</div>
</div>

View File

@ -7,6 +7,14 @@ Handlebars.registerHelper('partial', function (templateName) {
return new Handlebars.SafeString(templateFunction(this));
});
Handlebars.registerHelper('formField', function () {
if (!this.type) {
return Handlebars.helpers.partial.apply(this, ['Form/TextboxTemplate']);
}
return Handlebars.helpers.partial.apply(this, ['Form/TextboxTemplate']);
});
Handlebars.registerHelper("debug", function(optionalValue) {
console.log("Current Context");
console.log("====================");

View File

@ -22,15 +22,6 @@
</div>
{{#each fields}}
<div class="control-group">
<label class="control-label">{{label}}</label>
<div class="controls">
<input type="text" name="fields.{{@index}}.value"/>
<span class="help-inline">
<i class="icon-question-sign" title="{{helpText}}"></i>
</span>
</div>
</div>
{{formField}}
{{/each}}
</fieldset>