New: Notifications when Manual Interaction is required for importing

Closes #5193
pull/5632/head
Stickie 2 years ago committed by Mark McDowall
parent 63654b99f2
commit 5ec282750b

@ -65,6 +65,7 @@ class Notification extends Component {
onHealthIssue, onHealthIssue,
onHealthRestored, onHealthRestored,
onApplicationUpdate, onApplicationUpdate,
onManualInteractionRequired,
supportsOnGrab, supportsOnGrab,
supportsOnDownload, supportsOnDownload,
supportsOnUpgrade, supportsOnUpgrade,
@ -75,7 +76,8 @@ class Notification extends Component {
supportsOnEpisodeFileDeleteForUpgrade, supportsOnEpisodeFileDeleteForUpgrade,
supportsOnHealthIssue, supportsOnHealthIssue,
supportsOnHealthRestored, supportsOnHealthRestored,
supportsOnApplicationUpdate supportsOnApplicationUpdate,
supportsOnManualInteractionRequired
} = this.props; } = this.props;
return ( return (
@ -177,7 +179,15 @@ class Notification extends Component {
} }
{ {
!onGrab && !onDownload && !onRename && !onHealthIssue && !onHealthRestored && !onApplicationUpdate && !onSeriesDelete && !onEpisodeFileDelete ? supportsOnManualInteractionRequired && onManualInteractionRequired ?
<Label kind={kinds.SUCCESS}>
On Manual Interaction Required
</Label> :
null
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue && !onHealthRestored && !onApplicationUpdate && !onSeriesDelete && !onEpisodeFileDelete && !onManualInteractionRequired ?
<Label <Label
kind={kinds.DISABLED} kind={kinds.DISABLED}
outline={true} outline={true}
@ -222,6 +232,7 @@ Notification.propTypes = {
onHealthIssue: PropTypes.bool.isRequired, onHealthIssue: PropTypes.bool.isRequired,
onHealthRestored: PropTypes.bool.isRequired, onHealthRestored: PropTypes.bool.isRequired,
onApplicationUpdate: PropTypes.bool.isRequired, onApplicationUpdate: PropTypes.bool.isRequired,
onManualInteractionRequired: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired, supportsOnGrab: PropTypes.bool.isRequired,
supportsOnDownload: PropTypes.bool.isRequired, supportsOnDownload: PropTypes.bool.isRequired,
supportsOnSeriesAdd: PropTypes.bool.isRequired, supportsOnSeriesAdd: PropTypes.bool.isRequired,
@ -233,6 +244,7 @@ Notification.propTypes = {
supportsOnHealthIssue: PropTypes.bool.isRequired, supportsOnHealthIssue: PropTypes.bool.isRequired,
supportsOnHealthRestored: PropTypes.bool.isRequired, supportsOnHealthRestored: PropTypes.bool.isRequired,
supportsOnApplicationUpdate: PropTypes.bool.isRequired, supportsOnApplicationUpdate: PropTypes.bool.isRequired,
supportsOnManualInteractionRequired: PropTypes.bool.isRequired,
onConfirmDeleteNotification: PropTypes.func.isRequired onConfirmDeleteNotification: PropTypes.func.isRequired
}; };

@ -25,6 +25,7 @@ function NotificationEventItems(props) {
onHealthIssue, onHealthIssue,
onHealthRestored, onHealthRestored,
onApplicationUpdate, onApplicationUpdate,
onManualInteractionRequired,
supportsOnGrab, supportsOnGrab,
supportsOnDownload, supportsOnDownload,
supportsOnUpgrade, supportsOnUpgrade,
@ -34,6 +35,7 @@ function NotificationEventItems(props) {
supportsOnEpisodeFileDelete, supportsOnEpisodeFileDelete,
supportsOnEpisodeFileDeleteForUpgrade, supportsOnEpisodeFileDeleteForUpgrade,
supportsOnApplicationUpdate, supportsOnApplicationUpdate,
supportsOnManualInteractionRequired,
supportsOnHealthIssue, supportsOnHealthIssue,
supportsOnHealthRestored, supportsOnHealthRestored,
includeHealthWarnings includeHealthWarnings
@ -188,6 +190,17 @@ function NotificationEventItems(props) {
onChange={onInputChange} onChange={onInputChange}
/> />
</div> </div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onManualInteractionRequired"
helpText="On Manual Interaction Required"
isDisabled={!supportsOnManualInteractionRequired.value}
{...onManualInteractionRequired}
onChange={onInputChange}
/>
</div>
</div> </div>
</div> </div>
</FormGroup> </FormGroup>

@ -111,6 +111,7 @@ export default {
selectedSchema.onEpisodeFileDelete = selectedSchema.supportsOnEpisodeFileDelete; selectedSchema.onEpisodeFileDelete = selectedSchema.supportsOnEpisodeFileDelete;
selectedSchema.onEpisodeFileDeleteForUpgrade = selectedSchema.supportsOnEpisodeFileDeleteForUpgrade; selectedSchema.onEpisodeFileDeleteForUpgrade = selectedSchema.supportsOnEpisodeFileDeleteForUpgrade;
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate; selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
selectedSchema.onManualInteractionRequired = selectedSchema.supportsOnManualInteractionRequired;
return selectedSchema; return selectedSchema;
}); });

@ -88,6 +88,11 @@ namespace NzbDrone.Core.Test.NotificationTests
{ {
TestLogger.Info("OnApplicationUpdate was called"); TestLogger.Info("OnApplicationUpdate was called");
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
TestLogger.Info("OnManualInteractionRequired was called");
}
} }
private class TestNotificationWithNoEvents : NotificationBase<TestSetting> private class TestNotificationWithNoEvents : NotificationBase<TestSetting>
@ -128,6 +133,7 @@ namespace NzbDrone.Core.Test.NotificationTests
notification.SupportsOnHealthIssue.Should().BeTrue(); notification.SupportsOnHealthIssue.Should().BeTrue();
notification.SupportsOnHealthRestored.Should().BeTrue(); notification.SupportsOnHealthRestored.Should().BeTrue();
notification.SupportsOnApplicationUpdate.Should().BeTrue(); notification.SupportsOnApplicationUpdate.Should().BeTrue();
notification.SupportsOnManualInteractionRequired.Should().BeTrue();
} }
[Test] [Test]
@ -145,6 +151,7 @@ namespace NzbDrone.Core.Test.NotificationTests
notification.SupportsOnHealthIssue.Should().BeFalse(); notification.SupportsOnHealthIssue.Should().BeFalse();
notification.SupportsOnHealthRestored.Should().BeFalse(); notification.SupportsOnHealthRestored.Should().BeFalse();
notification.SupportsOnApplicationUpdate.Should().BeFalse(); notification.SupportsOnApplicationUpdate.Should().BeFalse();
notification.SupportsOnManualInteractionRequired.Should().BeFalse();
} }
} }
} }

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(177)]
public class add_on_manual_interaction_required_to_notifications : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Notifications").AddColumn("OnManualInteractionRequired").AsBoolean().WithDefaultValue(false);
}
}
}

@ -94,7 +94,8 @@ namespace NzbDrone.Core.Datastore
.Ignore(i => i.SupportsOnEpisodeFileDeleteForUpgrade) .Ignore(i => i.SupportsOnEpisodeFileDeleteForUpgrade)
.Ignore(i => i.SupportsOnHealthIssue) .Ignore(i => i.SupportsOnHealthIssue)
.Ignore(i => i.SupportsOnHealthRestored) .Ignore(i => i.SupportsOnHealthRestored)
.Ignore(i => i.SupportsOnApplicationUpdate); .Ignore(i => i.SupportsOnApplicationUpdate)
.Ignore(i => i.SupportsOnManualInteractionRequired);
Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel() Mapper.Entity<MetadataDefinition>("Metadata").RegisterModel()
.Ignore(x => x.ImplementationName) .Ignore(x => x.ImplementationName)

@ -105,6 +105,15 @@ namespace NzbDrone.Core.Download
if (seriesMatchType == SeriesMatchType.Id && releaseSource != ReleaseSourceType.InteractiveSearch) if (seriesMatchType == SeriesMatchType.Id && releaseSource != ReleaseSourceType.InteractiveSearch)
{ {
trackedDownload.Warn("Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible. See the FAQ for details."); trackedDownload.Warn("Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible. See the FAQ for details.");
if (!trackedDownload.HasNotifiedManualInteractionRequired)
{
trackedDownload.HasNotifiedManualInteractionRequired = true;
var manualInteractionEvent = new ManualInteractionRequiredEvent(trackedDownload);
_eventAggregator.PublishEvent(manualInteractionEvent);
}
return; return;
} }
} }

@ -1,4 +1,4 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download

@ -0,0 +1,18 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download
{
public class ManualInteractionRequiredEvent : IEvent
{
public RemoteEpisode Episode { get; private set; }
public TrackedDownload TrackedDownload { get; private set; }
public ManualInteractionRequiredEvent(TrackedDownload trackedDownload)
{
TrackedDownload = trackedDownload;
Episode = trackedDownload.RemoteEpisode;
}
}
}

@ -15,6 +15,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
IExecute<CheckForFinishedDownloadCommand>, IExecute<CheckForFinishedDownloadCommand>,
IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeImportedEvent>, IHandle<EpisodeImportedEvent>,
IHandle<ManualInteractionRequiredEvent>,
IHandle<DownloadsProcessedEvent>, IHandle<DownloadsProcessedEvent>,
IHandle<TrackedDownloadsRemovedEvent> IHandle<TrackedDownloadsRemovedEvent>
{ {
@ -170,6 +171,11 @@ namespace NzbDrone.Core.Download.TrackedDownloads
_refreshDebounce.Execute(); _refreshDebounce.Execute();
} }
public void Handle(ManualInteractionRequiredEvent message)
{
_refreshDebounce.Execute();
}
public void Handle(EpisodeImportedEvent message) public void Handle(EpisodeImportedEvent message)
{ {
_refreshDebounce.Execute(); _refreshDebounce.Execute();

@ -1,4 +1,4 @@
using System; using System;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -16,6 +16,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
public string Indexer { get; set; } public string Indexer { get; set; }
public bool IsTrackable { get; set; } public bool IsTrackable { get; set; }
public bool HasNotifiedManualInteractionRequired { get; set; }
public TrackedDownload() public TrackedDownload()
{ {

@ -103,7 +103,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
DownloadClient = downloadClient.Id, DownloadClient = downloadClient.Id,
DownloadItem = downloadItem, DownloadItem = downloadItem,
Protocol = downloadClient.Protocol, Protocol = downloadClient.Protocol,
IsTrackable = true IsTrackable = true,
HasNotifiedManualInteractionRequired = existingItem?.HasNotifiedManualInteractionRequired ?? false
}; };
try try

@ -57,6 +57,11 @@ namespace NzbDrone.Core.Notifications.Apprise
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -56,6 +56,11 @@ namespace NzbDrone.Core.Notifications.Boxcar
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, message.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, message.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -303,6 +303,32 @@ namespace NzbDrone.Core.Notifications.CustomScript
ExecuteScript(environmentVariables); ExecuteScript(environmentVariables);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
var series = message.Series;
var environmentVariables = new StringDictionary();
environmentVariables.Add("Sonarr_EventType", "Download");
environmentVariables.Add("Sonarr_InstanceName", _configFileProvider.InstanceName);
environmentVariables.Add("Sonarr_ApplicationUrl", _configService.ApplicationUrl);
environmentVariables.Add("Sonarr_Series_Id", series.Id.ToString());
environmentVariables.Add("Sonarr_Series_Title", series.Title);
environmentVariables.Add("Sonarr_Series_TitleSlug", series.TitleSlug);
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
environmentVariables.Add("Sonarr_Download_Client", message.DownloadClientName ?? string.Empty);
environmentVariables.Add("Sonarr_Download_Client_Type", message.DownloadClientType ?? string.Empty);
environmentVariables.Add("Sonarr_Download_Id", message.DownloadId ?? string.Empty);
environmentVariables.Add("Sonarr_Download_Size", message.TrackedDownload.DownloadItem.TotalSize.ToString());
environmentVariables.Add("Sonarr_Download_Title", message.TrackedDownload.DownloadItem.Title);
ExecuteScript(environmentVariables);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -375,6 +375,96 @@ namespace NzbDrone.Core.Notifications.Discord
_proxy.SendPayload(payload, Settings); _proxy.SendPayload(payload, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
var series = message.Series;
var episodes = message.Episode.Episodes;
var embed = new Embed
{
Author = new DiscordAuthor
{
Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author,
IconUrl = "https://raw.githubusercontent.com/Sonarr/Sonarr/develop/Logo/256.png"
},
Url = $"http://thetvdb.com/?tab=series&id={series.TvdbId}",
Description = "Manual interaction needed",
Title = GetTitle(series, episodes),
Color = (int)DiscordColors.Standard,
Fields = new List<DiscordField>(),
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
};
if (Settings.ManualInteractionFields.Contains((int)DiscordGrabFieldType.Poster))
{
embed.Thumbnail = new DiscordImage
{
Url = series.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.Url
};
}
if (Settings.ManualInteractionFields.Contains((int)DiscordGrabFieldType.Fanart))
{
embed.Image = new DiscordImage
{
Url = series.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Fanart)?.Url
};
}
foreach (var field in Settings.ManualInteractionFields)
{
var discordField = new DiscordField();
switch ((DiscordManualInteractionFieldType)field)
{
case DiscordManualInteractionFieldType.Overview:
var overview = episodes.First().Overview ?? "";
discordField.Name = "Overview";
discordField.Value = overview.Length <= 300 ? overview : $"{overview.AsSpan(0, 300)}...";
break;
case DiscordManualInteractionFieldType.Rating:
discordField.Name = "Rating";
discordField.Value = episodes.First().Ratings.Value.ToString();
break;
case DiscordManualInteractionFieldType.Genres:
discordField.Name = "Genres";
discordField.Value = series.Genres.Take(5).Join(", ");
break;
case DiscordManualInteractionFieldType.Quality:
discordField.Name = "Quality";
discordField.Inline = true;
discordField.Value = message.Quality.Quality.Name;
break;
case DiscordManualInteractionFieldType.Group:
discordField.Name = "Group";
discordField.Value = message.Episode.ParsedEpisodeInfo.ReleaseGroup;
break;
case DiscordManualInteractionFieldType.Size:
discordField.Name = "Size";
discordField.Value = BytesToString(message.TrackedDownload.DownloadItem.TotalSize);
discordField.Inline = true;
break;
case DiscordManualInteractionFieldType.DownloadTitle:
discordField.Name = "Download";
discordField.Value = string.Format("```{0}```", message.TrackedDownload.DownloadItem.Title);
break;
case DiscordManualInteractionFieldType.Links:
discordField.Name = "Links";
discordField.Value = GetLinksString(series);
break;
}
if (discordField.Name.IsNotNullOrWhiteSpace() && discordField.Value.IsNotNullOrWhiteSpace())
{
embed.Fields.Add(discordField);
}
}
var payload = CreatePayload(null, new List<Embed> { embed });
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -31,4 +31,18 @@ namespace NzbDrone.Core.Notifications.Discord
Poster, Poster,
Fanart Fanart
} }
public enum DiscordManualInteractionFieldType
{
Overview,
Rating,
Genres,
Quality,
Group,
Size,
Links,
DownloadTitle,
Poster,
Fanart
}
} }

@ -21,6 +21,7 @@ namespace NzbDrone.Core.Notifications.Discord
// Set Default Fields // Set Default Fields
GrabFields = new[] { 0, 1, 2, 3, 5, 6, 7, 8, 9 }; GrabFields = new[] { 0, 1, 2, 3, 5, 6, 7, 8, 9 };
ImportFields = new[] { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12 }; ImportFields = new[] { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12 };
ManualInteractionFields = new[] { 0, 1, 2, 3, 5, 6, 7, 8, 9 };
} }
private static readonly DiscordSettingsValidator Validator = new DiscordSettingsValidator(); private static readonly DiscordSettingsValidator Validator = new DiscordSettingsValidator();
@ -43,6 +44,9 @@ namespace NzbDrone.Core.Notifications.Discord
[FieldDefinition(5, Label = "On Import Fields", Advanced = true, SelectOptions = typeof(DiscordImportFieldType), HelpText = "Change the fields that are passed for this 'on import' notification", Type = FieldType.TagSelect)] [FieldDefinition(5, Label = "On Import Fields", Advanced = true, SelectOptions = typeof(DiscordImportFieldType), HelpText = "Change the fields that are passed for this 'on import' notification", Type = FieldType.TagSelect)]
public IEnumerable<int> ImportFields { get; set; } public IEnumerable<int> ImportFields { get; set; }
[FieldDefinition(6, Label = "On Manual Interaction Fields", Advanced = true, SelectOptions = typeof(DiscordManualInteractionFieldType), HelpText = "Change the fields that are passed for this 'on manual interaction' notification", Type = FieldType.TagSelect)]
public IEnumerable<int> ManualInteractionFields { get; set; }
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()
{ {
return new NzbDroneValidationResult(Validator.Validate(this)); return new NzbDroneValidationResult(Validator.Validate(this));

@ -78,6 +78,13 @@ namespace NzbDrone.Core.Notifications.Email
SendEmail(Settings, APPLICATION_UPDATE_TITLE_BRANDED, body); SendEmail(Settings, APPLICATION_UPDATE_TITLE_BRANDED, body);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
var body = $"{message.Message} requires manual interaction.";
SendEmail(Settings, MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, body);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -63,6 +63,11 @@ namespace NzbDrone.Core.Notifications.Gotify
SendNotification(APPLICATION_UPDATE_TITLE, message.Message, null); SendNotification(APPLICATION_UPDATE_TITLE, message.Message, null);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, message.Series);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Notifications
void OnHealthIssue(HealthCheck.HealthCheck healthCheck); void OnHealthIssue(HealthCheck.HealthCheck healthCheck);
void OnHealthRestored(HealthCheck.HealthCheck previousCheck); void OnHealthRestored(HealthCheck.HealthCheck previousCheck);
void OnApplicationUpdate(ApplicationUpdateMessage updateMessage); void OnApplicationUpdate(ApplicationUpdateMessage updateMessage);
void OnManualInteractionRequired(ManualInteractionRequiredMessage message);
void ProcessQueue(); void ProcessQueue();
bool SupportsOnGrab { get; } bool SupportsOnGrab { get; }
bool SupportsOnDownload { get; } bool SupportsOnDownload { get; }
@ -30,5 +31,6 @@ namespace NzbDrone.Core.Notifications
bool SupportsOnHealthIssue { get; } bool SupportsOnHealthIssue { get; }
bool SupportsOnHealthRestored { get; } bool SupportsOnHealthRestored { get; }
bool SupportsOnApplicationUpdate { get; } bool SupportsOnApplicationUpdate { get; }
bool SupportsOnManualInteractionRequired { get; }
} }
} }

@ -57,6 +57,11 @@ namespace NzbDrone.Core.Notifications.Join
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -65,6 +65,11 @@ namespace NzbDrone.Core.Notifications.Mailgun
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -0,0 +1,24 @@
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications
{
public class ManualInteractionRequiredMessage
{
public string Message { get; set; }
public Series Series { get; set; }
public RemoteEpisode Episode { get; set; }
public TrackedDownload TrackedDownload { get; set; }
public QualityModel Quality { get; set; }
public string DownloadClientType { get; set; }
public string DownloadClientName { get; set; }
public string DownloadId { get; set; }
public override string ToString()
{
return Message;
}
}
}

@ -67,6 +67,11 @@ namespace NzbDrone.Core.Notifications.Notifiarr
_proxy.SendNotification(BuildApplicationUpdatePayload(updateMessage), Settings); _proxy.SendNotification(BuildApplicationUpdatePayload(updateMessage), Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(BuildManualInteractionRequiredPayload(message), Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Notifications
protected const string HEALTH_ISSUE_TITLE = "Health Check Failure"; protected const string HEALTH_ISSUE_TITLE = "Health Check Failure";
protected const string HEALTH_RESTORED_TITLE = "Health Check Restored"; protected const string HEALTH_RESTORED_TITLE = "Health Check Restored";
protected const string APPLICATION_UPDATE_TITLE = "Application Updated"; protected const string APPLICATION_UPDATE_TITLE = "Application Updated";
protected const string MANUAL_INTERACTION_REQUIRED_TITLE = "Manual Interaction";
protected const string EPISODE_GRABBED_TITLE_BRANDED = "Sonarr - " + EPISODE_GRABBED_TITLE; protected const string EPISODE_GRABBED_TITLE_BRANDED = "Sonarr - " + EPISODE_GRABBED_TITLE;
protected const string EPISODE_DOWNLOADED_TITLE_BRANDED = "Sonarr - " + EPISODE_DOWNLOADED_TITLE; protected const string EPISODE_DOWNLOADED_TITLE_BRANDED = "Sonarr - " + EPISODE_DOWNLOADED_TITLE;
@ -27,6 +28,7 @@ namespace NzbDrone.Core.Notifications
protected const string HEALTH_ISSUE_TITLE_BRANDED = "Sonarr - " + HEALTH_ISSUE_TITLE; protected const string HEALTH_ISSUE_TITLE_BRANDED = "Sonarr - " + HEALTH_ISSUE_TITLE;
protected const string HEALTH_RESTORED_TITLE_BRANDED = "Sonarr - " + HEALTH_RESTORED_TITLE; protected const string HEALTH_RESTORED_TITLE_BRANDED = "Sonarr - " + HEALTH_RESTORED_TITLE;
protected const string APPLICATION_UPDATE_TITLE_BRANDED = "Sonarr - " + APPLICATION_UPDATE_TITLE; protected const string APPLICATION_UPDATE_TITLE_BRANDED = "Sonarr - " + APPLICATION_UPDATE_TITLE;
protected const string MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED = "Sonarr - " + MANUAL_INTERACTION_REQUIRED_TITLE;
public abstract string Name { get; } public abstract string Name { get; }
@ -77,6 +79,10 @@ namespace NzbDrone.Core.Notifications
{ {
} }
public virtual void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
}
public virtual void ProcessQueue() public virtual void ProcessQueue()
{ {
} }
@ -92,6 +98,7 @@ namespace NzbDrone.Core.Notifications
public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue"); public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue");
public bool SupportsOnHealthRestored => HasConcreteImplementation("OnHealthRestored"); public bool SupportsOnHealthRestored => HasConcreteImplementation("OnHealthRestored");
public bool SupportsOnApplicationUpdate => HasConcreteImplementation("OnApplicationUpdate"); public bool SupportsOnApplicationUpdate => HasConcreteImplementation("OnApplicationUpdate");
public bool SupportsOnManualInteractionRequired => HasConcreteImplementation("OnManualInteractionRequired");
protected TSettings Settings => (TSettings)Definition.Settings; protected TSettings Settings => (TSettings)Definition.Settings;

@ -15,6 +15,7 @@ namespace NzbDrone.Core.Notifications
public bool OnHealthIssue { get; set; } public bool OnHealthIssue { get; set; }
public bool OnHealthRestored { get; set; } public bool OnHealthRestored { get; set; }
public bool OnApplicationUpdate { get; set; } public bool OnApplicationUpdate { get; set; }
public bool OnManualInteractionRequired { get; set; }
public bool SupportsOnGrab { get; set; } public bool SupportsOnGrab { get; set; }
public bool SupportsOnDownload { get; set; } public bool SupportsOnDownload { get; set; }
public bool SupportsOnUpgrade { get; set; } public bool SupportsOnUpgrade { get; set; }
@ -27,7 +28,8 @@ namespace NzbDrone.Core.Notifications
public bool SupportsOnHealthRestored { get; set; } public bool SupportsOnHealthRestored { get; set; }
public bool IncludeHealthWarnings { get; set; } public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnApplicationUpdate { get; set; } public bool SupportsOnApplicationUpdate { get; set; }
public bool SupportsOnManualInteractionRequired { get; set; }
public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnSeriesAdd || OnSeriesDelete || OnEpisodeFileDelete || OnEpisodeFileDeleteForUpgrade || OnHealthIssue || OnHealthRestored || OnApplicationUpdate; public override bool Enable => OnGrab || OnDownload || (OnDownload && OnUpgrade) || OnSeriesAdd || OnSeriesDelete || OnEpisodeFileDelete || OnEpisodeFileDeleteForUpgrade || OnHealthIssue || OnHealthRestored || OnApplicationUpdate || OnManualInteractionRequired;
} }
} }

@ -21,6 +21,7 @@ namespace NzbDrone.Core.Notifications
List<INotification> OnHealthIssueEnabled(); List<INotification> OnHealthIssueEnabled();
List<INotification> OnHealthRestoredEnabled(); List<INotification> OnHealthRestoredEnabled();
List<INotification> OnApplicationUpdateEnabled(); List<INotification> OnApplicationUpdateEnabled();
List<INotification> OnManualInteractionEnabled();
} }
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
@ -85,6 +86,11 @@ namespace NzbDrone.Core.Notifications
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnApplicationUpdate).ToList(); return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnApplicationUpdate).ToList();
} }
public List<INotification> OnManualInteractionEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnManualInteractionRequired).ToList();
}
public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition) public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition)
{ {
base.SetProviderCharacteristics(provider, definition); base.SetProviderCharacteristics(provider, definition);
@ -100,6 +106,7 @@ namespace NzbDrone.Core.Notifications
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue; definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
definition.SupportsOnHealthRestored = provider.SupportsOnHealthRestored; definition.SupportsOnHealthRestored = provider.SupportsOnHealthRestored;
definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate; definition.SupportsOnApplicationUpdate = provider.SupportsOnApplicationUpdate;
definition.SupportsOnManualInteractionRequired = provider.SupportsOnManualInteractionRequired;
} }
} }
} }

@ -4,6 +4,7 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.HealthCheck; using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -25,6 +26,7 @@ namespace NzbDrone.Core.Notifications
IHandle<HealthCheckFailedEvent>, IHandle<HealthCheckFailedEvent>,
IHandle<HealthCheckRestoredEvent>, IHandle<HealthCheckRestoredEvent>,
IHandle<UpdateInstalledEvent>, IHandle<UpdateInstalledEvent>,
IHandle<ManualInteractionRequiredEvent>,
IHandleAsync<DeleteCompletedEvent>, IHandleAsync<DeleteCompletedEvent>,
IHandleAsync<DownloadsProcessedEvent>, IHandleAsync<DownloadsProcessedEvent>,
IHandleAsync<RenameCompletedEvent>, IHandleAsync<RenameCompletedEvent>,
@ -220,6 +222,38 @@ namespace NzbDrone.Core.Notifications
} }
} }
public void Handle(ManualInteractionRequiredEvent message)
{
var manualInteractionMessage = new ManualInteractionRequiredMessage
{
Message = GetMessage(message.Episode.Series, message.Episode.Episodes, message.Episode.ParsedEpisodeInfo.Quality),
Series = message.Episode.Series,
Quality = message.Episode.ParsedEpisodeInfo.Quality,
Episode = message.Episode,
TrackedDownload = message.TrackedDownload,
DownloadClientType = message.TrackedDownload.DownloadItem.DownloadClientInfo.Type,
DownloadClientName = message.TrackedDownload.DownloadItem.DownloadClientInfo.Name,
DownloadId = message.TrackedDownload.DownloadItem.DownloadId
};
foreach (var notification in _notificationFactory.OnManualInteractionEnabled())
{
try
{
if (!ShouldHandleSeries(notification.Definition, message.Episode.Series))
{
continue;
}
notification.OnManualInteractionRequired(manualInteractionMessage);
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to send OnManualInteractionRequired notification to {0}", notification.Definition.Name);
}
}
}
public void Handle(EpisodeFileDeletedEvent message) public void Handle(EpisodeFileDeletedEvent message)
{ {
if (message.EpisodeFile.Episodes.Value.Empty()) if (message.EpisodeFile.Episodes.Value.Empty())

@ -58,6 +58,11 @@ namespace NzbDrone.Core.Notifications.Ntfy
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -56,6 +56,11 @@ namespace NzbDrone.Core.Notifications.Prowl
_prowlProxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _prowlProxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_prowlProxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -59,6 +59,11 @@ namespace NzbDrone.Core.Notifications.PushBullet
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -56,6 +56,11 @@ namespace NzbDrone.Core.Notifications.Pushover
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -59,6 +59,11 @@ namespace NzbDrone.Core.Notifications.SendGrid
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -56,6 +56,11 @@ namespace NzbDrone.Core.Notifications.Simplepush
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -168,6 +168,23 @@ namespace NzbDrone.Core.Notifications.Slack
_proxy.SendPayload(payload, Settings); _proxy.SendPayload(payload, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
var attachments = new List<Attachment>
{
new Attachment
{
Title = Environment.MachineName,
Text = message.Message,
Color = "warning"
}
};
var payload = CreatePayload("Manual Interaction Required", attachments);
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -56,6 +56,11 @@ namespace NzbDrone.Core.Notifications.Telegram
_proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings); _proxy.SendNotification(APPLICATION_UPDATE_TITLE, updateMessage.Message, Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE, message.Message, Settings);
}
public override ValidationResult Test() public override ValidationResult Test()
{ {
var failures = new List<ValidationFailure>(); var failures = new List<ValidationFailure>();

@ -58,6 +58,11 @@ namespace NzbDrone.Core.Notifications.Twitter
_twitterService.SendNotification($"Application Updated: {updateMessage.Message}", Settings); _twitterService.SendNotification($"Application Updated: {updateMessage.Message}", Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_twitterService.SendNotification($"Manual Interaction Required: {message.Message}", Settings);
}
public override object RequestAction(string action, IDictionary<string, string> query) public override object RequestAction(string action, IDictionary<string, string> query)
{ {
if (action == "startOAuth") if (action == "startOAuth")

@ -65,6 +65,11 @@ namespace NzbDrone.Core.Notifications.Webhook
_proxy.SendWebhook(BuildApplicationUpdatePayload(updateMessage), Settings); _proxy.SendWebhook(BuildApplicationUpdatePayload(updateMessage), Settings);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
_proxy.SendWebhook(BuildManualInteractionRequiredPayload(message), Settings);
}
public override string Name => "Webhook"; public override string Name => "Webhook";
public override ValidationResult Test() public override ValidationResult Test()

@ -158,6 +158,23 @@ namespace NzbDrone.Core.Notifications.Webhook
}; };
} }
protected WebhookManualInteractionPayload BuildManualInteractionRequiredPayload(ManualInteractionRequiredMessage message)
{
var remoteEpisode = message.Episode;
var quality = message.Quality;
return new WebhookManualInteractionPayload
{
EventType = WebhookEventType.Grab,
Series = new WebhookSeries(message.Series),
Episodes = remoteEpisode.Episodes.ConvertAll(x => new WebhookEpisode(x)),
DownloadInfo = new WebhookDownloadClientItem(quality, message.TrackedDownload.DownloadItem),
DownloadClient = message.DownloadClientName,
DownloadClientType = message.DownloadClientType,
DownloadId = message.DownloadId
};
}
protected WebhookPayload BuildTestPayload() protected WebhookPayload BuildTestPayload()
{ {
return new WebhookGrabPayload return new WebhookGrabPayload

@ -0,0 +1,26 @@
using NzbDrone.Core.Download;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookDownloadClientItem
{
public WebhookDownloadClientItem()
{
}
public WebhookDownloadClientItem(QualityModel quality, DownloadClientItem downloadClientItem)
{
Quality = quality.Quality.Name;
QualityVersion = quality.Revision.Version;
Title = downloadClientItem.Title;
Size = downloadClientItem.TotalSize;
}
public string Quality { get; set; }
public int QualityVersion { get; set; }
public string Title { get; set; }
public string Indexer { get; set; }
public long Size { get; set; }
}
}

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookManualInteractionPayload : WebhookPayload
{
public WebhookSeries Series { get; set; }
public List<WebhookEpisode> Episodes { get; set; }
public WebhookDownloadClientItem DownloadInfo { get; set; }
public string DownloadClient { get; set; }
public string DownloadClientType { get; set; }
public string DownloadId { get; set; }
}
}

@ -84,6 +84,11 @@ namespace NzbDrone.Core.Notifications.Xbmc
Notify(Settings, APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message); Notify(Settings, APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message);
} }
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage message)
{
Notify(Settings, MANUAL_INTERACTION_REQUIRED_TITLE, message.Message);
}
public override string Name => "Kodi"; public override string Name => "Kodi";
public override ValidationResult Test() public override ValidationResult Test()

@ -16,6 +16,7 @@ namespace Sonarr.Api.V3.Notifications
public bool OnHealthIssue { get; set; } public bool OnHealthIssue { get; set; }
public bool OnHealthRestored { get; set; } public bool OnHealthRestored { get; set; }
public bool OnApplicationUpdate { get; set; } public bool OnApplicationUpdate { get; set; }
public bool OnManualInteractionRequired { get; set; }
public bool SupportsOnGrab { get; set; } public bool SupportsOnGrab { get; set; }
public bool SupportsOnDownload { get; set; } public bool SupportsOnDownload { get; set; }
public bool SupportsOnUpgrade { get; set; } public bool SupportsOnUpgrade { get; set; }
@ -27,6 +28,7 @@ namespace Sonarr.Api.V3.Notifications
public bool SupportsOnHealthIssue { get; set; } public bool SupportsOnHealthIssue { get; set; }
public bool SupportsOnHealthRestored { get; set; } public bool SupportsOnHealthRestored { get; set; }
public bool SupportsOnApplicationUpdate { get; set; } public bool SupportsOnApplicationUpdate { get; set; }
public bool SupportsOnManualInteractionRequired { get; set; }
public bool IncludeHealthWarnings { get; set; } public bool IncludeHealthWarnings { get; set; }
public string TestCommand { get; set; } public string TestCommand { get; set; }
} }
@ -53,6 +55,7 @@ namespace Sonarr.Api.V3.Notifications
resource.OnHealthIssue = definition.OnHealthIssue; resource.OnHealthIssue = definition.OnHealthIssue;
resource.OnHealthRestored = definition.OnHealthRestored; resource.OnHealthRestored = definition.OnHealthRestored;
resource.OnApplicationUpdate = definition.OnApplicationUpdate; resource.OnApplicationUpdate = definition.OnApplicationUpdate;
resource.OnManualInteractionRequired = definition.OnManualInteractionRequired;
resource.SupportsOnGrab = definition.SupportsOnGrab; resource.SupportsOnGrab = definition.SupportsOnGrab;
resource.SupportsOnDownload = definition.SupportsOnDownload; resource.SupportsOnDownload = definition.SupportsOnDownload;
resource.SupportsOnUpgrade = definition.SupportsOnUpgrade; resource.SupportsOnUpgrade = definition.SupportsOnUpgrade;
@ -65,6 +68,7 @@ namespace Sonarr.Api.V3.Notifications
resource.SupportsOnHealthRestored = definition.SupportsOnHealthRestored; resource.SupportsOnHealthRestored = definition.SupportsOnHealthRestored;
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings; resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate; resource.SupportsOnApplicationUpdate = definition.SupportsOnApplicationUpdate;
resource.SupportsOnManualInteractionRequired = definition.SupportsOnManualInteractionRequired;
return resource; return resource;
} }
@ -89,6 +93,7 @@ namespace Sonarr.Api.V3.Notifications
definition.OnHealthIssue = resource.OnHealthIssue; definition.OnHealthIssue = resource.OnHealthIssue;
definition.OnHealthRestored = resource.OnHealthRestored; definition.OnHealthRestored = resource.OnHealthRestored;
definition.OnApplicationUpdate = resource.OnApplicationUpdate; definition.OnApplicationUpdate = resource.OnApplicationUpdate;
definition.OnManualInteractionRequired = resource.OnManualInteractionRequired;
definition.SupportsOnGrab = resource.SupportsOnGrab; definition.SupportsOnGrab = resource.SupportsOnGrab;
definition.SupportsOnDownload = resource.SupportsOnDownload; definition.SupportsOnDownload = resource.SupportsOnDownload;
definition.SupportsOnUpgrade = resource.SupportsOnUpgrade; definition.SupportsOnUpgrade = resource.SupportsOnUpgrade;
@ -101,6 +106,7 @@ namespace Sonarr.Api.V3.Notifications
definition.SupportsOnHealthRestored = resource.SupportsOnHealthRestored; definition.SupportsOnHealthRestored = resource.SupportsOnHealthRestored;
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings; definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate; definition.SupportsOnApplicationUpdate = resource.SupportsOnApplicationUpdate;
definition.SupportsOnManualInteractionRequired = resource.SupportsOnManualInteractionRequired;
return definition; return definition;
} }

Loading…
Cancel
Save