New: Add OnDelete Notification to TraktConnection

to remove movies from Trakt Collection when they are removed from Radarr

Add OnDelete Notification to TraktConnection to remove movies from Trakt Collection when they are removed from Radarr

skip the deleteHandler if the delete Event was triggered for reason: Upgrade

change migration from 180 to 182 since 180 and 181 are already in plan
to be used

address comments regarding helpText for OnDelete
and move check for OnUpgrade deletion reason into trakt connection OnDelete handler

reuse TraktCollectMoviesResource for OnDelete
trakt api should just ignore the other properties anyway

add WithDefaultValue to migration

add unit test case for onDelete to

fix unit testcase for onDelete
This commit is contained in:
geogolem 2020-08-16 00:32:53 -04:00 committed by Qstick
parent 261e598c99
commit e033ce1eff
17 changed files with 171 additions and 3 deletions

View File

@ -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 {
</Label>
}
{
supportsOnDelete && onDelete &&
<Label kind={kinds.SUCCESS}>
On Delete
</Label>
}
{
supportsOnDownload && onDownload &&
<Label kind={kinds.SUCCESS}>
@ -113,7 +122,7 @@ class Notification extends Component {
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue &&
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete &&
<Label
kind={kinds.DISABLED}
outline={true}
@ -150,9 +159,11 @@ Notification.propTypes = {
onDownload: PropTypes.bool.isRequired,
onUpgrade: PropTypes.bool.isRequired,
onRename: PropTypes.bool.isRequired,
onDelete: PropTypes.bool.isRequired,
onHealthIssue: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired,
supportsOnDownload: PropTypes.bool.isRequired,
supportsOnDelete: PropTypes.bool.isRequired,
supportsOnUpgrade: PropTypes.bool.isRequired,
supportsOnRename: PropTypes.bool.isRequired,
supportsOnHealthIssue: PropTypes.bool.isRequired,

View File

@ -19,11 +19,13 @@ function NotificationEventItems(props) {
onDownload,
onUpgrade,
onRename,
onDelete,
onHealthIssue,
supportsOnGrab,
supportsOnDownload,
supportsOnUpgrade,
supportsOnRename,
supportsOnDelete,
supportsOnHealthIssue,
includeHealthWarnings
} = item;
@ -84,6 +86,17 @@ function NotificationEventItems(props) {
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onDelete"
helpText="On Delete"
isDisabled={!supportsOnDelete.value}
{...onDelete}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}

View File

@ -17,10 +17,12 @@ namespace NzbDrone.Api.Notifications
resource.OnDownload = definition.OnDownload;
resource.OnUpgrade = definition.OnUpgrade;
resource.OnRename = definition.OnRename;
resource.OnDelete = definition.OnDelete;
resource.SupportsOnGrab = definition.SupportsOnGrab;
resource.SupportsOnDownload = definition.SupportsOnDownload;
resource.SupportsOnUpgrade = definition.SupportsOnUpgrade;
resource.SupportsOnRename = definition.SupportsOnRename;
resource.SupportsOnDelete = definition.SupportsOnDelete;
resource.Tags = definition.Tags;
}
@ -32,10 +34,12 @@ namespace NzbDrone.Api.Notifications
definition.OnDownload = resource.OnDownload;
definition.OnUpgrade = resource.OnUpgrade;
definition.OnRename = resource.OnRename;
definition.OnDelete = resource.OnDelete;
definition.SupportsOnGrab = resource.SupportsOnGrab;
definition.SupportsOnDownload = resource.SupportsOnDownload;
definition.SupportsOnUpgrade = resource.SupportsOnUpgrade;
definition.SupportsOnRename = resource.SupportsOnRename;
definition.SupportsOnDelete = resource.SupportsOnDelete;
definition.Tags = resource.Tags;
}

View File

@ -8,10 +8,12 @@ namespace NzbDrone.Api.Notifications
public bool OnDownload { get; set; }
public bool OnUpgrade { get; set; }
public bool OnRename { 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 SupportsOnDelete { get; set; }
public HashSet<int> Tags { get; set; }
}
}

View File

@ -62,6 +62,11 @@ namespace NzbDrone.Core.Test.NotificationTests
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 @@ namespace NzbDrone.Core.Test.NotificationTests
notification.SupportsOnDownload.Should().BeTrue();
notification.SupportsOnUpgrade.Should().BeTrue();
notification.SupportsOnRename.Should().BeTrue();
notification.SupportsOnDelete.Should().BeTrue();
notification.SupportsOnHealthIssue.Should().BeTrue();
}
@ -112,6 +118,7 @@ namespace NzbDrone.Core.Test.NotificationTests
notification.SupportsOnDownload.Should().BeFalse();
notification.SupportsOnUpgrade.Should().BeFalse();
notification.SupportsOnRename.Should().BeFalse();
notification.SupportsOnDelete.Should().BeFalse();
notification.SupportsOnHealthIssue.Should().BeFalse();
}
}

View File

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

View File

@ -86,6 +86,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(i => i.SupportsOnDownload)
.Ignore(i => i.SupportsOnUpgrade)
.Ignore(i => i.SupportsOnRename)
.Ignore(i => i.SupportsOnDelete)
.Ignore(i => i.SupportsOnHealthIssue);
Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel()

View File

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

View File

@ -11,10 +11,12 @@ namespace NzbDrone.Core.Notifications
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; }
}
}

View File

@ -46,11 +46,16 @@ namespace NzbDrone.Core.Notifications
{
}
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;

View File

@ -9,13 +9,15 @@ namespace NzbDrone.Core.Notifications
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;
}
}

View File

@ -14,6 +14,7 @@ namespace NzbDrone.Core.Notifications
List<INotification> OnUpgradeEnabled();
List<INotification> OnRenameEnabled();
List<INotification> OnHealthIssueEnabled();
List<INotification> OnDeleteEnabled();
}
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
@ -48,6 +49,11 @@ namespace NzbDrone.Core.Notifications
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList();
}
public List<INotification> 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 @@ namespace NzbDrone.Core.Notifications
definition.SupportsOnUpgrade = provider.SupportsOnUpgrade;
definition.SupportsOnRename = provider.SupportsOnRename;
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
definition.SupportsOnDelete = provider.SupportsOnDelete;
}
}
}

View File

@ -16,7 +16,8 @@ namespace NzbDrone.Core.Notifications
: IHandle<MovieRenamedEvent>,
IHandle<MovieGrabbedEvent>,
IHandle<MovieDownloadedEvent>,
IHandle<HealthCheckFailedEvent>
IHandle<HealthCheckFailedEvent>,
IHandle<MovieFileDeletedEvent>
{
private readonly INotificationFactory _notificationFactory;
private readonly Logger _logger;
@ -172,5 +173,29 @@ namespace NzbDrone.Core.Notifications
}
}
}
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);
}
}
}
}
}

View File

@ -29,6 +29,14 @@ namespace NzbDrone.Core.Notifications.Trakt
_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<ValidationFailure>();

View File

@ -12,6 +12,7 @@ namespace NzbDrone.Core.Notifications.Trakt
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 @@ namespace NzbDrone.Core.Notifications.Trakt
}
}
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);

View File

@ -19,6 +19,7 @@ namespace NzbDrone.Core.Notifications.Trakt
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 @@ namespace NzbDrone.Core.Notifications.Trakt
}
}
public void RemoveMovieFromCollection(TraktSettings settings, Movie movie, MovieFile movieFile)
{
var payload = new TraktCollectMoviesResource
{
Movies = new List<TraktCollectMovie>()
};
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

View File

@ -9,11 +9,13 @@ namespace Radarr.Api.V3.Notifications
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 @@ namespace Radarr.Api.V3.Notifications
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 @@ namespace Radarr.Api.V3.Notifications
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;