diff --git a/frontend/src/Settings/Notifications/Notifications/Notification.js b/frontend/src/Settings/Notifications/Notifications/Notification.js index d329006ab..751840232 100644 --- a/frontend/src/Settings/Notifications/Notifications/Notification.js +++ b/frontend/src/Settings/Notifications/Notifications/Notification.js @@ -59,11 +59,13 @@ class Notification extends Component { onDownload, onUpgrade, onRename, + onDelete, onHealthIssue, supportsOnGrab, supportsOnDownload, supportsOnUpgrade, supportsOnRename, + supportsOnDelete, supportsOnHealthIssue } = this.props; @@ -84,6 +86,13 @@ class Notification extends Component { } + { + supportsOnDelete && onDelete && + + On Delete + + } + { supportsOnDownload && onDownload && @@ -113,7 +122,7 @@ class Notification extends Component { } { - !onGrab && !onDownload && !onRename && !onHealthIssue && + !onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete && + + + + Tags { get; set; } } } diff --git a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs index f61b2350b..7ed370e84 100644 --- a/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs +++ b/src/NzbDrone.Core.Test/NotificationTests/NotificationBaseFixture.cs @@ -62,6 +62,11 @@ public override void OnMovieRename(Movie movie) TestLogger.Info("OnRename was called"); } + public override void OnDelete(DeleteMessage message) + { + TestLogger.Info("OnDelete was called"); + } + public override void OnHealthIssue(NzbDrone.Core.HealthCheck.HealthCheck artist) { TestLogger.Info("OnHealthIssue was called"); @@ -100,6 +105,7 @@ public void should_support_all_if_implemented() notification.SupportsOnDownload.Should().BeTrue(); notification.SupportsOnUpgrade.Should().BeTrue(); notification.SupportsOnRename.Should().BeTrue(); + notification.SupportsOnDelete.Should().BeTrue(); notification.SupportsOnHealthIssue.Should().BeTrue(); } @@ -112,6 +118,7 @@ public void should_support_none_if_none_are_implemented() notification.SupportsOnDownload.Should().BeFalse(); notification.SupportsOnUpgrade.Should().BeFalse(); notification.SupportsOnRename.Should().BeFalse(); + notification.SupportsOnDelete.Should().BeFalse(); notification.SupportsOnHealthIssue.Should().BeFalse(); } } diff --git a/src/NzbDrone.Core/Datastore/Migration/182_on_delete_notification.cs b/src/NzbDrone.Core/Datastore/Migration/182_on_delete_notification.cs new file mode 100644 index 000000000..192a49253 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/182_on_delete_notification.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(182)] + public class on_delete_notification : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Notifications").AddColumn("OnDelete").AsBoolean().WithDefaultValue(false); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 0163362d4..6f6b44b75 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -86,6 +86,7 @@ public static void Map() .Ignore(i => i.SupportsOnDownload) .Ignore(i => i.SupportsOnUpgrade) .Ignore(i => i.SupportsOnRename) + .Ignore(i => i.SupportsOnDelete) .Ignore(i => i.SupportsOnHealthIssue); Mapper.Entity("Metadata").RegisterModel() diff --git a/src/NzbDrone.Core/Notifications/DeleteMessage.cs b/src/NzbDrone.Core/Notifications/DeleteMessage.cs new file mode 100644 index 000000000..ec700f7b4 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/DeleteMessage.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Movies; + +namespace NzbDrone.Core.Notifications +{ + public class DeleteMessage + { + public string Message { get; set; } + public Movie Movie { get; set; } + public MovieFile MovieFile { get; set; } + + public DeleteMediaFileReason Reason { get; set; } + + public override string ToString() + { + return Message; + } + } +} diff --git a/src/NzbDrone.Core/Notifications/INotification.cs b/src/NzbDrone.Core/Notifications/INotification.cs index ce589f618..0a1f9a1f2 100644 --- a/src/NzbDrone.Core/Notifications/INotification.cs +++ b/src/NzbDrone.Core/Notifications/INotification.cs @@ -11,10 +11,12 @@ public interface INotification : IProvider void OnDownload(DownloadMessage message); void OnMovieRename(Movie movie); void OnHealthIssue(HealthCheck.HealthCheck healthCheck); + void OnDelete(DeleteMessage deleteMessage); bool SupportsOnGrab { get; } bool SupportsOnDownload { get; } bool SupportsOnUpgrade { get; } bool SupportsOnRename { get; } bool SupportsOnHealthIssue { get; } + bool SupportsOnDelete { get; } } } diff --git a/src/NzbDrone.Core/Notifications/NotificationBase.cs b/src/NzbDrone.Core/Notifications/NotificationBase.cs index 08841fdf0..a7a27d566 100644 --- a/src/NzbDrone.Core/Notifications/NotificationBase.cs +++ b/src/NzbDrone.Core/Notifications/NotificationBase.cs @@ -46,11 +46,16 @@ public virtual void OnHealthIssue(HealthCheck.HealthCheck healthCheck) { } + public virtual void OnDelete(DeleteMessage deleteMessage) + { + } + public bool SupportsOnGrab => HasConcreteImplementation("OnGrab"); public bool SupportsOnRename => HasConcreteImplementation("OnMovieRename"); public bool SupportsOnDownload => HasConcreteImplementation("OnDownload"); public bool SupportsOnUpgrade => SupportsOnDownload; public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue"); + public bool SupportsOnDelete => HasConcreteImplementation("OnDelete"); protected TSettings Settings => (TSettings)Definition.Settings; diff --git a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs index fc3fa2b75..77e978e1e 100644 --- a/src/NzbDrone.Core/Notifications/NotificationDefinition.cs +++ b/src/NzbDrone.Core/Notifications/NotificationDefinition.cs @@ -9,13 +9,15 @@ public class NotificationDefinition : ProviderDefinition public bool OnUpgrade { get; set; } public bool OnRename { get; set; } public bool OnHealthIssue { get; set; } + public bool OnDelete { get; set; } public bool SupportsOnGrab { get; set; } public bool SupportsOnDownload { get; set; } public bool SupportsOnUpgrade { get; set; } public bool SupportsOnRename { get; set; } public bool SupportsOnHealthIssue { get; set; } + public bool SupportsOnDelete { get; set; } public bool IncludeHealthWarnings { get; set; } - public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnHealthIssue; + public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnHealthIssue || OnDelete; } } diff --git a/src/NzbDrone.Core/Notifications/NotificationFactory.cs b/src/NzbDrone.Core/Notifications/NotificationFactory.cs index 709abc455..1fe06c1f3 100644 --- a/src/NzbDrone.Core/Notifications/NotificationFactory.cs +++ b/src/NzbDrone.Core/Notifications/NotificationFactory.cs @@ -14,6 +14,7 @@ public interface INotificationFactory : IProviderFactory OnUpgradeEnabled(); List OnRenameEnabled(); List OnHealthIssueEnabled(); + List OnDeleteEnabled(); } public class NotificationFactory : ProviderFactory, INotificationFactory @@ -48,6 +49,11 @@ public List OnHealthIssueEnabled() return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList(); } + public List OnDeleteEnabled() + { + return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnDelete).ToList(); + } + public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition) { base.SetProviderCharacteristics(provider, definition); @@ -57,6 +63,7 @@ public override void SetProviderCharacteristics(INotification provider, Notifica definition.SupportsOnUpgrade = provider.SupportsOnUpgrade; definition.SupportsOnRename = provider.SupportsOnRename; definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue; + definition.SupportsOnDelete = provider.SupportsOnDelete; } } } diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index 302dff045..2eeb6dbdd 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -16,7 +16,8 @@ public class NotificationService : IHandle, IHandle, IHandle, - IHandle + IHandle, + IHandle { private readonly INotificationFactory _notificationFactory; private readonly Logger _logger; @@ -172,5 +173,29 @@ public void Handle(HealthCheckFailedEvent message) } } } + + public void Handle(MovieFileDeletedEvent message) + { + var deleteMessage = new DeleteMessage(); + deleteMessage.Message = GetMessage(message.MovieFile.Movie, message.MovieFile.Quality); + deleteMessage.MovieFile = message.MovieFile; + deleteMessage.Movie = message.MovieFile.Movie; + deleteMessage.Reason = message.Reason; + + foreach (var notification in _notificationFactory.OnDeleteEnabled()) + { + try + { + if (ShouldHandleMovie(notification.Definition, message.MovieFile.Movie)) + { + notification.OnDelete(deleteMessage); + } + } + catch (Exception ex) + { + _logger.Warn(ex, "Unable to send OnDelete notification to: " + notification.Definition.Name); + } + } + } } } diff --git a/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs b/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs index e846bb676..36ca30a31 100644 --- a/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs +++ b/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs @@ -29,6 +29,14 @@ public override void OnDownload(DownloadMessage message) _traktService.AddMovieToCollection(Settings, message.Movie, message.MovieFile); } + public override void OnDelete(DeleteMessage message) + { + if (message.Reason != MediaFiles.DeleteMediaFileReason.Upgrade) + { + _traktService.RemoveMovieFromCollection(Settings, message.Movie, message.MovieFile); + } + } + public override ValidationResult Test() { var failures = new List(); diff --git a/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs b/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs index a428335ce..fb6a64fa3 100644 --- a/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs +++ b/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs @@ -12,6 +12,7 @@ public interface ITraktProxy HttpRequest GetOAuthRequest(string callbackUrl); TraktAuthRefreshResource RefreshAuthToken(string refreshToken); void AddToCollection(TraktCollectMoviesResource payload, string accessToken); + void RemoveFromCollection(TraktCollectMoviesResource payload, string accessToken); HttpRequest BuildTraktRequest(string resource, HttpMethod method, string accessToken); } @@ -50,6 +51,24 @@ public void AddToCollection(TraktCollectMoviesResource payload, string accessTok } } + public void RemoveFromCollection(TraktCollectMoviesResource payload, string accessToken) + { + var request = BuildTraktRequest("sync/collection/remove", HttpMethod.POST, accessToken); + + request.Headers.ContentType = "application/json"; + request.SetContent(payload.ToJson()); + + try + { + _httpClient.Execute(request); + } + catch (HttpException ex) + { + _logger.Error(ex, "Unable to post payload {0}", payload); + throw new TraktException("Unable to post payload", ex); + } + } + public string GetUserName(string accessToken) { var request = BuildTraktRequest("users/settings", HttpMethod.GET, accessToken); diff --git a/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs b/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs index 9090191d6..4c8c6ba9f 100644 --- a/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs +++ b/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs @@ -19,6 +19,7 @@ public interface ITraktService HttpRequest GetOAuthRequest(string callbackUrl); TraktAuthRefreshResource RefreshAuthToken(string refreshToken); void AddMovieToCollection(TraktSettings settings, Movie movie, MovieFile movieFile); + void RemoveMovieFromCollection(TraktSettings settings, Movie movie, MovieFile movieFile); string GetUserName(string accessToken); ValidationFailure Test(TraktSettings settings); } @@ -75,6 +76,27 @@ public ValidationFailure Test(TraktSettings settings) } } + public void RemoveMovieFromCollection(TraktSettings settings, Movie movie, MovieFile movieFile) + { + var payload = new TraktCollectMoviesResource + { + Movies = new List() + }; + + payload.Movies.Add(new TraktCollectMovie + { + Title = movie.Title, + Year = movie.Year, + Ids = new TraktMovieIdsResource + { + Tmdb = movie.TmdbId, + Imdb = movie.ImdbId ?? "", + } + }); + + _proxy.RemoveFromCollection(payload, settings.AccessToken); + } + public void AddMovieToCollection(TraktSettings settings, Movie movie, MovieFile movieFile) { var payload = new TraktCollectMoviesResource diff --git a/src/Radarr.Api.V3/Notifications/NotificationResource.cs b/src/Radarr.Api.V3/Notifications/NotificationResource.cs index f04302fb0..ee0b90bfe 100644 --- a/src/Radarr.Api.V3/Notifications/NotificationResource.cs +++ b/src/Radarr.Api.V3/Notifications/NotificationResource.cs @@ -9,11 +9,13 @@ public class NotificationResource : ProviderResource public bool OnDownload { get; set; } public bool OnUpgrade { get; set; } public bool OnRename { get; set; } + public bool OnDelete { get; set; } public bool OnHealthIssue { get; set; } public bool SupportsOnGrab { get; set; } public bool SupportsOnDownload { get; set; } public bool SupportsOnUpgrade { get; set; } public bool SupportsOnRename { get; set; } + public bool SupportsOnDelete { get; set; } public bool SupportsOnHealthIssue { get; set; } public bool IncludeHealthWarnings { get; set; } public string TestCommand { get; set; } @@ -34,11 +36,13 @@ public override NotificationResource ToResource(NotificationDefinition definitio resource.OnDownload = definition.OnDownload; resource.OnUpgrade = definition.OnUpgrade; resource.OnRename = definition.OnRename; + resource.OnDelete = definition.OnDelete; resource.OnHealthIssue = definition.OnHealthIssue; resource.SupportsOnGrab = definition.SupportsOnGrab; resource.SupportsOnDownload = definition.SupportsOnDownload; resource.SupportsOnUpgrade = definition.SupportsOnUpgrade; resource.SupportsOnRename = definition.SupportsOnRename; + resource.SupportsOnDelete = definition.SupportsOnDelete; resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue; resource.IncludeHealthWarnings = definition.IncludeHealthWarnings; @@ -58,11 +62,13 @@ public override NotificationDefinition ToModel(NotificationResource resource) definition.OnDownload = resource.OnDownload; definition.OnUpgrade = resource.OnUpgrade; definition.OnRename = resource.OnRename; + definition.OnDelete = resource.OnDelete; definition.OnHealthIssue = resource.OnHealthIssue; definition.SupportsOnGrab = resource.SupportsOnGrab; definition.SupportsOnDownload = resource.SupportsOnDownload; definition.SupportsOnUpgrade = resource.SupportsOnUpgrade; definition.SupportsOnRename = resource.SupportsOnRename; + definition.SupportsOnDelete = resource.SupportsOnDelete; definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue; definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;