diff --git a/src/NzbDrone.Api/Notifications/NotificationResource.cs b/src/NzbDrone.Api/Notifications/NotificationResource.cs index e54f4cfa6..965e0af15 100644 --- a/src/NzbDrone.Api/Notifications/NotificationResource.cs +++ b/src/NzbDrone.Api/Notifications/NotificationResource.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace NzbDrone.Api.Notifications { @@ -9,6 +8,11 @@ namespace NzbDrone.Api.Notifications public bool OnGrab { get; set; } public bool OnDownload { get; set; } public bool OnUpgrade { get; set; } + public bool OnRename { get; set; } + public bool SupportsOnGrab { get; set; } + public bool SupportsOnDownload { get; set; } + public bool SupportsOnUpgrade { get; set; } + public bool SupportsOnRename { get; set; } public string TestCommand { get; set; } public HashSet Tags { get; set; } } diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj index 76f210985..f3110d9d3 100644 --- a/src/NzbDrone.Common/NzbDrone.Common.csproj +++ b/src/NzbDrone.Common/NzbDrone.Common.csproj @@ -177,6 +177,7 @@ + diff --git a/src/NzbDrone.Common/Processes/ProcessOutput.cs b/src/NzbDrone.Common/Processes/ProcessOutput.cs index e1b3cea27..54947237f 100644 --- a/src/NzbDrone.Common/Processes/ProcessOutput.cs +++ b/src/NzbDrone.Common/Processes/ProcessOutput.cs @@ -1,17 +1,32 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; namespace NzbDrone.Common.Processes { public class ProcessOutput { - public List Standard { get; private set; } - public List Error { get; private set; } + public int ExitCode { get; set; } + public List Lines { get; set; } public ProcessOutput() { - Standard = new List(); - Error = new List(); + Lines = new List(); + } + + public List Standard + { + get + { + return Lines.Where(c => c.Level == ProcessOutputLevel.Standard).ToList(); + } + } + + public List Error + { + get + { + return Lines.Where(c => c.Level == ProcessOutputLevel.Error).ToList(); + } } } } diff --git a/src/NzbDrone.Common/Processes/ProcessOutputLine.cs b/src/NzbDrone.Common/Processes/ProcessOutputLine.cs new file mode 100644 index 000000000..2b1e0a4ec --- /dev/null +++ b/src/NzbDrone.Common/Processes/ProcessOutputLine.cs @@ -0,0 +1,29 @@ +using System; + +namespace NzbDrone.Common.Processes +{ + public class ProcessOutputLine + { + public ProcessOutputLevel Level { get; set; } + public string Content { get; set; } + public DateTime Time { get; set; } + + public ProcessOutputLine(ProcessOutputLevel level, string content) + { + Level = level; + Content = content; + Time = DateTime.UtcNow; + } + + public override string ToString() + { + return String.Format("{0} - {1} - {2}", Time, Level, Content); + } + } + + public enum ProcessOutputLevel + { + Standard = 0, + Error = 1 + } +} diff --git a/src/NzbDrone.Common/Processes/ProcessProvider.cs b/src/NzbDrone.Common/Processes/ProcessProvider.cs index 7289ebbdc..aae8dac96 100644 --- a/src/NzbDrone.Common/Processes/ProcessProvider.cs +++ b/src/NzbDrone.Common/Processes/ProcessProvider.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -24,9 +26,9 @@ namespace NzbDrone.Common.Processes Boolean Exists(int processId); Boolean Exists(string processName); ProcessPriorityClass GetCurrentProcessPriority(); - Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null); - Process SpawnNewProcess(string path, string args = null); - ProcessOutput StartAndCapture(string path, string args = null); + Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null); + Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null); + ProcessOutput StartAndCapture(string path, string args = null, StringDictionary environmentVariables = null); } public class ProcessProvider : IProcessProvider @@ -104,7 +106,7 @@ namespace NzbDrone.Common.Processes process.Start(); } - public Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null) + public Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null) { if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) { @@ -123,6 +125,13 @@ namespace NzbDrone.Common.Processes RedirectStandardInput = true }; + if (environmentVariables != null) + { + foreach (DictionaryEntry environmentVariable in environmentVariables) + { + startInfo.EnvironmentVariables.Add(environmentVariable.Key.ToString(), environmentVariable.Value.ToString()); + } + } logger.Debug("Starting {0} {1}", path, args); @@ -163,7 +172,7 @@ namespace NzbDrone.Common.Processes return process; } - public Process SpawnNewProcess(string path, string args = null) + public Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null) { if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) { @@ -184,10 +193,14 @@ namespace NzbDrone.Common.Processes return process; } - public ProcessOutput StartAndCapture(string path, string args = null) + public ProcessOutput StartAndCapture(string path, string args = null, StringDictionary environmentVariables = null) { var output = new ProcessOutput(); - Start(path, args, s => output.Standard.Add(s), error => output.Error.Add(error)).WaitForExit(); + var process = Start(path, args, environmentVariables, s => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Standard, s)), + error => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Error, error))); + + process.WaitForExit(); + output.ExitCode = process.ExitCode; return output; } diff --git a/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs index 2055fe41e..d8b1954d4 100644 --- a/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs +++ b/src/NzbDrone.Core.Test/NotificationTests/SynologyIndexerFixture.cs @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.NotificationTests { (Subject.Definition.Settings as SynologyIndexerSettings).UpdateLibrary = false; - Subject.AfterRename(_series); + Subject.OnRename(_series); Mocker.GetMock() .Verify(v => v.UpdateFolder(_series.Path), Times.Never()); @@ -93,7 +93,7 @@ namespace NzbDrone.Core.Test.NotificationTests [Test] public void should_update_entire_series_folder_on_rename() { - Subject.AfterRename(_series); + Subject.OnRename(_series); Mocker.GetMock() .Verify(v => v.UpdateFolder(@"C:\Test\".AsOsAgnostic()), Times.Once()); diff --git a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs index 404837749..5be4abbf0 100644 --- a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs +++ b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs @@ -145,7 +145,7 @@ namespace NzbDrone.Core.Test.UpdateTests Subject.Execute(new ApplicationUpdateCommand()); Mocker.GetMock() - .Verify(c => c.Start(It.IsAny(), It.Is(s => s.StartsWith("12")), null, null), Times.Once()); + .Verify(c => c.Start(It.IsAny(), It.Is(s => s.StartsWith("12")), null, null, null), Times.Once()); } [Test] @@ -179,7 +179,7 @@ namespace NzbDrone.Core.Test.UpdateTests Subject.Execute(new ApplicationUpdateCommand()); - Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null), Times.Once()); + Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null, null), Times.Once()); } [Test] @@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.UpdateTests Assert.Throws(() => Subject.Execute(new ApplicationUpdateCommand())); ExceptionVerification.ExpectedErrors(1); - Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null), Times.Never()); + Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null, null), Times.Never()); } [Test] @@ -207,7 +207,7 @@ namespace NzbDrone.Core.Test.UpdateTests Assert.Throws(() => Subject.Execute(new ApplicationUpdateCommand())); ExceptionVerification.ExpectedErrors(1); - Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null), Times.Never()); + Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null, null), Times.Never()); } [Test] @@ -225,7 +225,7 @@ namespace NzbDrone.Core.Test.UpdateTests Assert.Throws(() => Subject.Execute(new ApplicationUpdateCommand())); ExceptionVerification.ExpectedErrors(1); - Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null), Times.Never()); + Mocker.GetMock().Verify(v => v.Start(scriptPath, It.IsAny(), null, null, null), Times.Never()); } [Test] diff --git a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs index 21fec2e11..60a1a8223 100644 --- a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs +++ b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace NzbDrone.Core.Annotations { @@ -10,10 +11,10 @@ namespace NzbDrone.Core.Annotations Order = order; } - public Int32 Order { get; private set; } - public String Label { get; set; } - public String HelpText { get; set; } - public String HelpLink { get; set; } + public int Order { get; private set; } + public string Label { get; set; } + public string HelpText { get; set; } + public string HelpLink { get; set; } public FieldType Type { get; set; } public Boolean Advanced { get; set; } public Type SelectOptions { get; set; } diff --git a/src/NzbDrone.Core/Datastore/Migration/087_add_on_rename_to_notifcations.cs b/src/NzbDrone.Core/Datastore/Migration/087_add_on_rename_to_notifcations.cs new file mode 100644 index 000000000..544a7cdc8 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/087_add_on_rename_to_notifcations.cs @@ -0,0 +1,21 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(87)] + public class add_on_rename_to_notifcations : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Notifications").AddColumn("OnRename").AsBoolean().Nullable(); + + Execute.Sql("UPDATE Notifications SET OnRename = OnDownload WHERE Implementation IN ('PlexServer', 'Xbmc', 'MediaBrowser')"); + Execute.Sql("UPDATE Notifications SET OnRename = 0 WHERE Implementation NOT IN ('PlexServer', 'Xbmc', 'MediaBrowser')"); + + Alter.Table("Notifications").AlterColumn("OnRename").AsBoolean().NotNullable(); + + Execute.Sql("UPDATE Notifications SET OnGrab = 0 WHERE Implementation = 'PlexServer'"); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/089_add_on_rename_to_notifcations.cs b/src/NzbDrone.Core/Datastore/Migration/089_add_on_rename_to_notifcations.cs new file mode 100644 index 000000000..e06db676b --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/089_add_on_rename_to_notifcations.cs @@ -0,0 +1,21 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(89)] + public class add_on_rename_to_notifcations : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Notifications").AddColumn("OnRename").AsBoolean().Nullable(); + + Execute.Sql("UPDATE Notifications SET OnRename = OnDownload WHERE Implementation IN ('PlexServer', 'Xbmc', 'MediaBrowser')"); + Execute.Sql("UPDATE Notifications SET OnRename = 0 WHERE Implementation NOT IN ('PlexServer', 'Xbmc', 'MediaBrowser')"); + + Alter.Table("Notifications").AlterColumn("OnRename").AsBoolean().NotNullable(); + + Execute.Sql("UPDATE Notifications SET OnGrab = 0 WHERE Implementation = 'PlexServer'"); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 16bd5fcda..5f8eb8c2d 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -53,7 +53,12 @@ namespace NzbDrone.Core.Datastore .Ignore(i => i.SupportsRss) .Ignore(i => i.SupportsSearch); - Mapper.Entity().RegisterDefinition("Notifications"); + Mapper.Entity().RegisterDefinition("Notifications") + .Ignore(i => i.SupportsOnGrab) + .Ignore(i => i.SupportsOnDownload) + .Ignore(i => i.SupportsOnUpgrade) + .Ignore(i => i.SupportsOnRename); + Mapper.Entity().RegisterDefinition("Metadata"); Mapper.Entity().RegisterDefinition("DownloadClients") diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs new file mode 100644 index 000000000..f1389e146 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using FluentValidation.Results; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Notifications.CustomScript +{ + public class CustomScript : NotificationBase + { + private readonly ICustomScriptService _customScriptService; + + public CustomScript(ICustomScriptService customScriptService) + { + _customScriptService = customScriptService; + } + + public override string Link + { + get { return "https://github.com/Sonarr/Sonarr/wiki/Custom-Post-Processing-Scripts"; } + } + + public override void OnGrab(string message) + { + } + + public override void OnDownload(DownloadMessage message) + { + _customScriptService.OnDownload(message.Series, message.EpisodeFile, Settings); + } + + public override void OnRename(Series series) + { + _customScriptService.OnRename(series, Settings); + } + + public override string Name + { + get + { + return "Custom Script"; + } + } + + public override bool SupportsOnGrab + { + get + { + return false; + } + } + + public override ValidationResult Test() + { + var failures = new List(); + + return new ValidationResult(failures); + } + } +} diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptService.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptService.cs new file mode 100644 index 000000000..1748f655c --- /dev/null +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptService.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using FluentValidation.Results; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Common.Processes; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Notifications.CustomScript +{ + public interface ICustomScriptService + { + void OnDownload(Series series, EpisodeFile episodeFile, CustomScriptSettings settings); + void OnRename(Series series, CustomScriptSettings settings); + ValidationFailure Test(CustomScriptSettings settings); + } + + public class CustomScriptService : ICustomScriptService + { + private readonly IProcessProvider _processProvider; + private readonly IDiskProvider _diskProvider; + private readonly Logger _logger; + + public CustomScriptService(IProcessProvider processProvider, IDiskProvider diskProvider, Logger logger) + { + _processProvider = processProvider; + _diskProvider = diskProvider; + _logger = logger; + } + + public void OnDownload(Series series, EpisodeFile episodeFile, CustomScriptSettings settings) + { + var environmentVariables = new StringDictionary(); + + environmentVariables.Add("Sonarr.EventType", "Download"); + environmentVariables.Add("Sonarr.Series.Id", series.Id.ToString()); + environmentVariables.Add("Sonarr.Series.Title", series.Title); + environmentVariables.Add("Sonarr.Series.Path", series.Path); + environmentVariables.Add("Sonarr.Series.TvdbId", series.TvdbId.ToString()); + environmentVariables.Add("Sonarr.EpisodeFile.Id", episodeFile.Id.ToString()); + environmentVariables.Add("Sonarr.EpisodeFile.RelativePath", episodeFile.RelativePath); + environmentVariables.Add("Sonarr.EpisodeFile.Path", Path.Combine(series.Path, episodeFile.RelativePath)); + environmentVariables.Add("Sonarr.EpisodeFile.SeasonNumber", episodeFile.SeasonNumber.ToString()); + environmentVariables.Add("Sonarr.EpisodeFile.EpisodeNumbers", String.Join(",", episodeFile.Episodes.Value.Select(e => e.EpisodeNumber))); + environmentVariables.Add("Sonarr.EpisodeFile.EpisodeAirDates", String.Join(",", episodeFile.Episodes.Value.Select(e => e.AirDate))); + environmentVariables.Add("Sonarr.EpisodeFile.EpisodeAirDatesUtc", String.Join(",", episodeFile.Episodes.Value.Select(e => e.AirDateUtc))); + environmentVariables.Add("Sonarr.EpisodeFile.Quality", episodeFile.Quality.Quality.Name); + environmentVariables.Add("Sonarr.EpisodeFile.QualityVersion", episodeFile.Quality.Revision.Version.ToString()); + environmentVariables.Add("Sonarr.EpisodeFile.ReleaseGroup", episodeFile.ReleaseGroup ?? String.Empty); + environmentVariables.Add("Sonarr.EpisodeFile.SceneName", episodeFile.SceneName ?? String.Empty); + + ExecuteScript(environmentVariables, settings); + } + + public void OnRename(Series series, CustomScriptSettings settings) + { + var environmentVariables = new StringDictionary(); + + environmentVariables.Add("Sonarr.EventType", "Rename"); + environmentVariables.Add("Sonarr.Series.Id", series.Id.ToString()); + environmentVariables.Add("Sonarr.Series.Title", series.Title); + environmentVariables.Add("Sonarr.Series.Path", series.Path); + environmentVariables.Add("Sonarr.Series.TvdbId", series.TvdbId.ToString()); + + ExecuteScript(environmentVariables, settings); + } + + public ValidationFailure Test(CustomScriptSettings settings) + { + if (!_diskProvider.FileExists(settings.Path)) + { + return new NzbDroneValidationFailure("Path", "File does not exist"); + } + + return null; + } + + private void ExecuteScript(StringDictionary environmentVariables, CustomScriptSettings settings) + { + _logger.Debug("Executing external script: {0}", settings.Path); + + var process = _processProvider.StartAndCapture(settings.Path, settings.Arguments, environmentVariables); + + _logger.Debug("Executed external script: {0} - Status: {1}", settings.Path, process.ExitCode); + _logger.Debug("Script Output: \r\n{0}", String.Join("\r\n", process.Lines)); + } + } +} diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptSettings.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptSettings.cs new file mode 100644 index 000000000..fca85951c --- /dev/null +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScriptSettings.cs @@ -0,0 +1,33 @@ +using System; +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; +using NzbDrone.Core.Validation.Paths; + +namespace NzbDrone.Core.Notifications.CustomScript +{ + public class CustomScriptSettingsValidator : AbstractValidator + { + public CustomScriptSettingsValidator() + { + RuleFor(c => c.Path).IsValidPath(); + } + } + + public class CustomScriptSettings : IProviderConfig + { + private static readonly CustomScriptSettingsValidator Validator = new CustomScriptSettingsValidator(); + + [FieldDefinition(0, Label = "Path", Type = FieldType.Path)] + public String Path { get; set; } + + [FieldDefinition(0, Label = "Arguments", HelpText = "Arguments to pass to the script")] + public String Arguments { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/Notifications/Email/Email.cs b/src/NzbDrone.Core/Notifications/Email/Email.cs index cad241f6c..6bdf017c4 100644 --- a/src/NzbDrone.Core/Notifications/Email/Email.cs +++ b/src/NzbDrone.Core/Notifications/Email/Email.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Email _emailService.SendEmail(Settings, subject, body); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -48,6 +48,14 @@ namespace NzbDrone.Core.Notifications.Email } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Growl/Growl.cs b/src/NzbDrone.Core/Notifications/Growl/Growl.cs index 9d71bcd20..1b2ff1e29 100644 --- a/src/NzbDrone.Core/Notifications/Growl/Growl.cs +++ b/src/NzbDrone.Core/Notifications/Growl/Growl.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Notifications.Growl _growlService.SendNotification(title, message.Message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -45,6 +45,14 @@ namespace NzbDrone.Core.Notifications.Growl } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/INotification.cs b/src/NzbDrone.Core/Notifications/INotification.cs index 97ee4dc0a..700237d1c 100644 --- a/src/NzbDrone.Core/Notifications/INotification.cs +++ b/src/NzbDrone.Core/Notifications/INotification.cs @@ -9,6 +9,10 @@ namespace NzbDrone.Core.Notifications void OnGrab(string message); void OnDownload(DownloadMessage message); - void AfterRename(Series series); + void OnRename(Series series); + bool SupportsOnGrab { get; } + bool SupportsOnDownload { get; } + bool SupportsOnUpgrade { get; } + bool SupportsOnRename { get; } } } diff --git a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs index 613216eef..590943fd1 100644 --- a/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs +++ b/src/NzbDrone.Core/Notifications/MediaBrowser/MediaBrowser.cs @@ -45,7 +45,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser } } - public override void AfterRename(Series series) + public override void OnRename(Series series) { if (Settings.UpdateLibrary) { diff --git a/src/NzbDrone.Core/Notifications/NotificationBase.cs b/src/NzbDrone.Core/Notifications/NotificationBase.cs index 1306c286c..fda893ab3 100644 --- a/src/NzbDrone.Core/Notifications/NotificationBase.cs +++ b/src/NzbDrone.Core/Notifications/NotificationBase.cs @@ -40,8 +40,13 @@ namespace NzbDrone.Core.Notifications public abstract string Link { get; } public abstract void OnGrab(string message); - public abstract void OnDownload(DownloadMessage message); - public abstract void AfterRename(Series series); + public abstract void OnDownload(DownloadMessage message); + public abstract void OnRename(Series series); + + public virtual bool SupportsOnGrab { get { return true; } } + public virtual bool SupportsOnDownload { get { return true; } } + public virtual bool SupportsOnUpgrade { get { return true; } } + public virtual bool SupportsOnRename { get { return true; } } protected TSettings Settings { diff --git a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs index 527985136..1c0bbb9b3 100644 --- a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs +++ b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs @@ -11,12 +11,17 @@ namespace NzbDrone.Core.Notifications Tags = new HashSet(); } - public Boolean OnGrab { get; set; } - public Boolean OnDownload { get; set; } - public Boolean OnUpgrade { get; set; } - public HashSet Tags { get; set; } + public bool OnGrab { get; set; } + public bool OnDownload { get; set; } + public bool OnUpgrade { get; set; } + public bool OnRename { get; set; } + public bool SupportsOnGrab { get; set; } + public bool SupportsOnDownload { get; set; } + public bool SupportsOnUpgrade { get; set; } + public bool SupportsOnRename { get; set; } + public HashSet Tags { get; set; } - public override Boolean Enable + public override bool Enable { get { diff --git a/src/NzbDrone.Core/Notifications/NotificationFactory.cs b/src/NzbDrone.Core/Notifications/NotificationFactory.cs index 4fd14d6f5..ffc94a9cc 100644 --- a/src/NzbDrone.Core/Notifications/NotificationFactory.cs +++ b/src/NzbDrone.Core/Notifications/NotificationFactory.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Core.Notifications List OnGrabEnabled(); List OnDownloadEnabled(); List OnUpgradeEnabled(); + List OnRenameEnabled(); } public class NotificationFactory : ProviderFactory, INotificationFactory @@ -35,5 +36,22 @@ namespace NzbDrone.Core.Notifications { return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnUpgrade).ToList(); } + + public List OnRenameEnabled() + { + return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnRename).ToList(); + } + + public override NotificationDefinition GetProviderCharacteristics(INotification provider, NotificationDefinition definition) + { + definition = base.GetProviderCharacteristics(provider, definition); + + definition.SupportsOnGrab = provider.SupportsOnGrab; + definition.SupportsOnDownload = provider.SupportsOnDownload; + definition.SupportsOnUpgrade = provider.SupportsOnUpgrade; + definition.SupportsOnRename = provider.SupportsOnRename; + + return definition; + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index 7276895ea..94cbdb4e0 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -137,19 +137,19 @@ namespace NzbDrone.Core.Notifications public void Handle(SeriesRenamedEvent message) { - foreach (var notification in _notificationFactory.OnDownloadEnabled()) + foreach (var notification in _notificationFactory.OnRenameEnabled()) { try { if (ShouldHandleSeries(notification.Definition, message.Series)) { - notification.AfterRename(message.Series); + notification.OnRename(message.Series); } } catch (Exception ex) { - _logger.WarnException("Unable to send AfterRename notification to: " + notification.Definition.Name, ex); + _logger.WarnException("Unable to send OnRename notification to: " + notification.Definition.Name, ex); } } } diff --git a/src/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs b/src/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs index 38cb7463d..c4a346bee 100644 --- a/src/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs +++ b/src/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid _proxy.SendNotification(title, message.Message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -46,6 +46,14 @@ namespace NzbDrone.Core.Notifications.NotifyMyAndroid } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexClient.cs b/src/NzbDrone.Core/Notifications/Plex/PlexClient.cs index bdd72bb90..927532663 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexClient.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexClient.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Notifications.Plex _plexClientService.Notify(Settings, header, message.Message); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -43,6 +43,14 @@ namespace NzbDrone.Core.Notifications.Plex } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexHomeTheater.cs b/src/NzbDrone.Core/Notifications/Plex/PlexHomeTheater.cs index 3e20df536..fbf056ab1 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexHomeTheater.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexHomeTheater.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Notifications.Plex Notify(Settings, header, message.Message); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -52,6 +52,14 @@ namespace NzbDrone.Core.Notifications.Plex } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexServer.cs b/src/NzbDrone.Core/Notifications/Plex/PlexServer.cs index 839df84fd..4d73dfd3b 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexServer.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexServer.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Plex UpdateIfEnabled(message.Series); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { UpdateIfEnabled(series); } @@ -49,6 +49,14 @@ namespace NzbDrone.Core.Notifications.Plex } } + public override bool SupportsOnGrab + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs b/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs index d4f26fabf..c52f4d370 100644 --- a/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs +++ b/src/NzbDrone.Core/Notifications/Prowl/Prowl.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Prowl _prowlService.SendNotification(title, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -46,6 +46,14 @@ namespace NzbDrone.Core.Notifications.Prowl } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs b/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs index c0e155244..d127583f4 100644 --- a/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs +++ b/src/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Notifications.PushBullet _proxy.SendNotification(title, message.Message, Settings); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -45,6 +45,14 @@ namespace NzbDrone.Core.Notifications.PushBullet } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Pushalot/Pushalot.cs b/src/NzbDrone.Core/Notifications/Pushalot/Pushalot.cs index d21e1d011..8649e5ee5 100644 --- a/src/NzbDrone.Core/Notifications/Pushalot/Pushalot.cs +++ b/src/NzbDrone.Core/Notifications/Pushalot/Pushalot.cs @@ -34,7 +34,7 @@ namespace NzbDrone.Core.Notifications.Pushalot _proxy.SendNotification(title, message.Message, Settings); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -46,6 +46,14 @@ namespace NzbDrone.Core.Notifications.Pushalot } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs b/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs index 3fcf7d489..9f5ae7c2b 100644 --- a/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs +++ b/src/NzbDrone.Core/Notifications/Pushover/Pushover.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Notifications.Pushover _proxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { } @@ -45,6 +45,14 @@ namespace NzbDrone.Core.Notifications.Pushover } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs b/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs index 12d5424b0..045112bbe 100644 --- a/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs +++ b/src/NzbDrone.Core/Notifications/Synology/SynologyIndexer.cs @@ -45,7 +45,7 @@ namespace NzbDrone.Core.Notifications.Synology } } - public override void AfterRename(Series series) + public override void OnRename(Series series) { if (Settings.UpdateLibrary) { @@ -61,6 +61,14 @@ namespace NzbDrone.Core.Notifications.Synology } } + public override bool SupportsOnRename + { + get + { + return false; + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs index e17de97c3..cd9aa4a11 100644 --- a/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs +++ b/src/NzbDrone.Core/Notifications/Xbmc/Xbmc.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Core.Notifications.Xbmc UpdateAndClean(message.Series, message.OldFiles.Any()); } - public override void AfterRename(Series series) + public override void OnRename(Series series) { UpdateAndClean(series); } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index faa22abc5..67a841331 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -254,6 +254,7 @@ + @@ -710,6 +711,9 @@ + + + diff --git a/src/NzbDrone.Host/AccessControl/SslAdapter.cs b/src/NzbDrone.Host/AccessControl/SslAdapter.cs index 56319a318..7b4853833 100644 --- a/src/NzbDrone.Host/AccessControl/SslAdapter.cs +++ b/src/NzbDrone.Host/AccessControl/SslAdapter.cs @@ -56,11 +56,11 @@ namespace NzbDrone.Host.AccessControl if (output == null || !output.Standard.Any()) return false; - var hashLine = output.Standard.SingleOrDefault(line => CertificateHashRegex.IsMatch(line)); + var hashLine = output.Standard.SingleOrDefault(line => CertificateHashRegex.IsMatch(line.Content)); if (hashLine != null) { - var match = CertificateHashRegex.Match(hashLine); + var match = CertificateHashRegex.Match(hashLine.Content); if (match.Success) { @@ -73,7 +73,7 @@ namespace NzbDrone.Host.AccessControl } } - return output.Standard.Any(line => line.Contains(ipPort)); + return output.Standard.Any(line => line.Content.Contains(ipPort)); } private void Unregister() diff --git a/src/NzbDrone.Host/AccessControl/UrlAclAdapter.cs b/src/NzbDrone.Host/AccessControl/UrlAclAdapter.cs index d8f42256e..85f4a1d9b 100644 --- a/src/NzbDrone.Host/AccessControl/UrlAclAdapter.cs +++ b/src/NzbDrone.Host/AccessControl/UrlAclAdapter.cs @@ -139,7 +139,7 @@ namespace NzbDrone.Host.AccessControl return output.Standard.Select(line => { - var match = UrlAclRegex.Match(line); + var match = UrlAclRegex.Match(line.Content); if (match.Success) { diff --git a/src/NzbDrone.Test.Common/NzbDroneRunner.cs b/src/NzbDrone.Test.Common/NzbDroneRunner.cs index 953c7f39e..27bad483d 100644 --- a/src/NzbDrone.Test.Common/NzbDroneRunner.cs +++ b/src/NzbDrone.Test.Common/NzbDroneRunner.cs @@ -85,7 +85,7 @@ namespace NzbDrone.Test.Common private void Start(string outputNzbdroneConsoleExe) { var args = "-nobrowser -data=\"" + AppData + "\""; - _nzbDroneProcess = _processProvider.Start(outputNzbdroneConsoleExe, args, OnOutputDataReceived, OnOutputDataReceived); + _nzbDroneProcess = _processProvider.Start(outputNzbdroneConsoleExe, args, null, OnOutputDataReceived, OnOutputDataReceived); } diff --git a/src/NzbDrone.Update.Test/StartNzbDroneService.cs b/src/NzbDrone.Update.Test/StartNzbDroneService.cs index ab9f2f665..4cb97c91d 100644 --- a/src/NzbDrone.Update.Test/StartNzbDroneService.cs +++ b/src/NzbDrone.Update.Test/StartNzbDroneService.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Update.Test Subject.Start(AppType.Service, targetFolder); - Mocker.GetMock().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", "/" + StartupContext.NO_BROWSER), Times.Once()); + Mocker.GetMock().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", "/" + StartupContext.NO_BROWSER, null), Times.Once()); ExceptionVerification.ExpectedWarns(1); } diff --git a/src/UI/Settings/Notifications/Add/NotificationAddItemView.js b/src/UI/Settings/Notifications/Add/NotificationAddItemView.js index c5dc8ff00..04b7c8944 100644 --- a/src/UI/Settings/Notifications/Add/NotificationAddItemView.js +++ b/src/UI/Settings/Notifications/Add/NotificationAddItemView.js @@ -27,9 +27,10 @@ module.exports = Marionette.ItemView.extend({ this.model.set({ id : undefined, - onGrab : true, - onDownload : true, - onUpgrade : true + onGrab : this.model.get('supportsOnGrab'), + onDownload : this.model.get('supportsOnDownload'), + onUpgrade : this.model.get('supportsOnUpgrade'), + onRename : this.model.get('supportsOnRename') }); var editView = new EditView({ @@ -47,9 +48,10 @@ module.exports = Marionette.ItemView.extend({ this.model.set({ id : undefined, - onGrab : true, - onDownload : true, - onUpgrade : true + onGrab : this.model.get('supportsOnGrab'), + onDownload : this.model.get('supportsOnDownload'), + onUpgrade : this.model.get('supportsOnUpgrade'), + onRename : this.model.get('supportsOnRename') }); var editView = new EditView({ diff --git a/src/UI/Settings/Notifications/Edit/NotificationEditView.js b/src/UI/Settings/Notifications/Edit/NotificationEditView.js index 735f725a7..c1f6b50d0 100644 --- a/src/UI/Settings/Notifications/Edit/NotificationEditView.js +++ b/src/UI/Settings/Notifications/Edit/NotificationEditView.js @@ -6,6 +6,7 @@ var AsValidatedView = require('../../../Mixins/AsValidatedView'); var AsEditModalView = require('../../../Mixins/AsEditModalView'); require('../../../Form/FormBuilder'); require('../../../Mixins/TagInput'); +require('../../../Mixins/FileBrowser'); require('bootstrap.tagsinput'); var view = Marionette.ItemView.extend({ @@ -15,7 +16,9 @@ var view = Marionette.ItemView.extend({ onDownloadToggle : '.x-on-download', onUpgradeSection : '.x-on-upgrade', tags : '.x-tags', - formTag : '.x-form-tag' + modalBody : '.modal-body', + formTag : '.x-form-tag', + path : '.x-path' }, events : { @@ -43,6 +46,14 @@ var view = Marionette.ItemView.extend({ }); }, + onShow : function() { + if (this.ui.path.length > 0) { + this.ui.modalBody.addClass('modal-overflow'); + } + + this.ui.path.fileBrowser(); + }, + _onAfterSave : function() { this.targetCollection.add(this.model, { merge : true }); vent.trigger(vent.Commands.CloseModalCommand); diff --git a/src/UI/Settings/Notifications/Edit/NotificationEditViewTemplate.hbs b/src/UI/Settings/Notifications/Edit/NotificationEditViewTemplate.hbs index b64b9df05..8833326e9 100644 --- a/src/UI/Settings/Notifications/Edit/NotificationEditViewTemplate.hbs +++ b/src/UI/Settings/Notifications/Edit/NotificationEditViewTemplate.hbs @@ -23,7 +23,7 @@