New: Health Check Failure Notifications (#609)

* New: Health Check Failure Notifications

Fixes #295

* New: OnDownloadFailure and OnImportFailure Notification

* New: On Retag notifications

* Fixed: XBMC notification test

* New: Discord Notifications

Closes #1511

* On Download to On Import on card

* Remove OnDownload, Rename OnAlbumDownload -> OnReleaseImported

* Fixed: Webhook OnReleaseImport notification

* Respect OnUpgrade and fix missing schema items for frontend

* New: Simplify Notification Modal UI

* Fixed: PlexHomeTheater OnReleaseImport notification
pull/693/head
Qstick 5 years ago committed by GitHub
parent 4d8bcd12e3
commit d4d9146599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,6 +14,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputGroup from 'Components/Form/FormInputGroup';
import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
import NotificationEventItems from './NotificationEventItems';
import styles from './EditNotificationModalContent.css';
function EditNotificationModalContent(props) {
@ -38,16 +39,6 @@ function EditNotificationModalContent(props) {
id,
implementationName,
name,
onGrab,
onDownload,
onAlbumDownload,
onUpgrade,
onRename,
supportsOnGrab,
supportsOnDownload,
supportsOnAlbumDownload,
supportsOnUpgrade,
supportsOnRename,
tags,
fields,
message
@ -94,73 +85,10 @@ function EditNotificationModalContent(props) {
/>
</FormGroup>
<FormGroup>
<FormLabel>On Grab</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="onGrab"
helpText="Be notified when albums are available for download and has been sent to a download client"
isDisabled={!supportsOnGrab.value}
{...onGrab}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>On Album Import</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="onAlbumDownload"
helpText="Be notified when complete albums are successfully imported"
isDisabled={!supportsOnAlbumDownload.value}
{...onAlbumDownload}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>On Track Import</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="onDownload"
helpText="Be notified when track files are successfully imported"
isDisabled={!supportsOnDownload.value}
{...onDownload}
onChange={onInputChange}
/>
</FormGroup>
{
onDownload.value &&
<FormGroup>
<FormLabel>On Track Upgrade</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="onUpgrade"
helpText="Be notified when tracks are upgraded to a better quality"
isDisabled={!supportsOnUpgrade.value}
{...onUpgrade}
onChange={onInputChange}
/>
</FormGroup>
}
<FormGroup>
<FormLabel>On Rename</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="onRename"
helpText="Be notified when tracks are renamed"
isDisabled={!supportsOnRename.value}
{...onRename}
onChange={onInputChange}
/>
</FormGroup>
<NotificationEventItems
item={item}
onInputChange={onInputChange}
/>
<FormGroup>
<FormLabel>Tags</FormLabel>

@ -55,15 +55,21 @@ class Notification extends Component {
id,
name,
onGrab,
onDownload,
onAlbumDownload,
onReleaseImport,
onUpgrade,
onRename,
onHealthIssue,
onDownloadFailure,
onImportFailure,
onTrackRetag,
supportsOnGrab,
supportsOnDownload,
supportsOnAlbumDownload,
supportsOnReleaseImport,
supportsOnUpgrade,
supportsOnRename
supportsOnRename,
supportsOnHealthIssue,
supportsOnDownloadFailure,
supportsOnImportFailure,
supportsOnTrackRetag
} = this.props;
return (
@ -84,35 +90,57 @@ class Notification extends Component {
}
{
supportsOnAlbumDownload && onAlbumDownload &&
supportsOnReleaseImport && onReleaseImport &&
<Label kind={kinds.SUCCESS}>
On Album Download
On Release Import
</Label>
}
{
supportsOnDownload && onDownload &&
supportsOnUpgrade && onReleaseImport && onUpgrade &&
<Label kind={kinds.SUCCESS}>
On Download
On Upgrade
</Label>
}
{
supportsOnUpgrade && onDownload && onUpgrade &&
supportsOnRename && onRename &&
<Label kind={kinds.SUCCESS}>
On Upgrade
On Rename
</Label>
}
{
supportsOnRename && onRename &&
supportsOnTrackRetag && onTrackRetag &&
<Label kind={kinds.SUCCESS}>
On Rename
On Track Tag Update
</Label>
}
{
supportsOnHealthIssue && onHealthIssue &&
<Label kind={kinds.SUCCESS}>
On Health Issue
</Label>
}
{
supportsOnDownloadFailure && onDownloadFailure &&
<Label kind={kinds.SUCCESS} >
On Download Failure
</Label>
}
{
supportsOnImportFailure && onImportFailure &&
<Label kind={kinds.SUCCESS} >
On Import Failure
</Label>
}
{
!onGrab && !onAlbumDownload && !onDownload && !onRename &&
!onGrab && !onReleaseImport && !onRename && !onTrackRetag &&
!onHealthIssue && !onDownloadFailure && !onImportFailure &&
<Label
kind={kinds.DISABLED}
outline={true}
@ -146,15 +174,21 @@ Notification.propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
onGrab: PropTypes.bool.isRequired,
onDownload: PropTypes.bool.isRequired,
onAlbumDownload: PropTypes.bool.isRequired,
onReleaseImport: PropTypes.bool.isRequired,
onUpgrade: PropTypes.bool.isRequired,
onRename: PropTypes.bool.isRequired,
onHealthIssue: PropTypes.bool.isRequired,
onDownloadFailure: PropTypes.bool.isRequired,
onImportFailure: PropTypes.bool.isRequired,
onTrackRetag: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired,
supportsOnDownload: PropTypes.bool.isRequired,
supportsOnAlbumDownload: PropTypes.bool.isRequired,
supportsOnReleaseImport: PropTypes.bool.isRequired,
supportsOnUpgrade: PropTypes.bool.isRequired,
supportsOnRename: PropTypes.bool.isRequired,
supportsOnHealthIssue: PropTypes.bool.isRequired,
supportsOnDownloadFailure: PropTypes.bool.isRequired,
supportsOnImportFailure: PropTypes.bool.isRequired,
supportsOnTrackRetag: PropTypes.bool.isRequired,
onConfirmDeleteNotification: PropTypes.func.isRequired
};

@ -0,0 +1,4 @@
.events {
margin-top: 10px;
user-select: none;
}

@ -0,0 +1,160 @@
import PropTypes from 'prop-types';
import React from 'react';
import { inputTypes } from 'Helpers/Props';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputHelpText from 'Components/Form/FormInputHelpText';
import FormInputGroup from 'Components/Form/FormInputGroup';
import styles from './NotificationEventItems.css';
function NotificationEventItems(props) {
const {
item,
onInputChange
} = props;
const {
onGrab,
onReleaseImport,
onUpgrade,
onRename,
onHealthIssue,
onDownloadFailure,
onImportFailure,
onTrackRetag,
supportsOnGrab,
supportsOnReleaseImport,
supportsOnUpgrade,
supportsOnRename,
supportsOnHealthIssue,
includeHealthWarnings,
supportsOnDownloadFailure,
supportsOnImportFailure,
supportsOnTrackRetag
} = item;
return (
<FormGroup>
<FormLabel>Notification Triggers</FormLabel>
<div>
<FormInputHelpText
text="Select which events should trigger this notification"
link="https://github.com/lidarr/Lidarr/wiki/Connections"
/>
<div className={styles.events}>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onGrab"
helpText="On Grab"
isDisabled={!supportsOnGrab.value}
{...onGrab}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onReleaseImport"
helpText="On Release Import"
isDisabled={!supportsOnReleaseImport.value}
{...onReleaseImport}
onChange={onInputChange}
/>
</div>
{
onReleaseImport.value &&
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onUpgrade"
helpText="On Upgrade"
isDisabled={!supportsOnUpgrade.value}
{...onUpgrade}
onChange={onInputChange}
/>
</div>
}
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onDownloadFailure"
helpText="On Download Failure"
isDisabled={!supportsOnDownloadFailure.value}
{...onDownloadFailure}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onImportFailure"
helpText="On Import Failure"
isDisabled={!supportsOnImportFailure.value}
{...onImportFailure}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onRename"
helpText="On Rename"
isDisabled={!supportsOnRename.value}
{...onRename}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onTrackRetag"
helpText="On Track Retag"
isDisabled={!supportsOnTrackRetag.value}
{...onTrackRetag}
onChange={onInputChange}
/>
</div>
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onHealthIssue"
helpText="On Health Issue"
isDisabled={!supportsOnHealthIssue.value}
{...onHealthIssue}
onChange={onInputChange}
/>
</div>
{
onHealthIssue.value &&
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="includeHealthWarnings"
helpText="Include Health Warnings"
isDisabled={!supportsOnHealthIssue.value}
{...includeHealthWarnings}
onChange={onInputChange}
/>
</div>
}
</div>
</div>
</FormGroup>
);
}
NotificationEventItems.propTypes = {
item: PropTypes.object.isRequired,
onInputChange: PropTypes.func.isRequired
};
export default NotificationEventItems;

@ -103,9 +103,13 @@ export default {
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
return selectProviderSchema(state, section, payload, (selectedSchema) => {
selectedSchema.onGrab = selectedSchema.supportsOnGrab;
selectedSchema.onDownload = selectedSchema.supportsOnDownload;
selectedSchema.onReleaseImport = selectedSchema.supportsOnReleaseImport;
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
selectedSchema.onRename = selectedSchema.supportsOnRename;
selectedSchema.onHealthIssue = selectedSchema.supportsOnHealthIssue;
selectedSchema.onDownloadFailure = selectedSchema.supportsOnDownloadFailure;
selectedSchema.onImportFailure = selectedSchema.supportsOnImportFailure;
selectedSchema.onTrackRetag = selectedSchema.supportsOnTrackRetag;
return selectedSchema;
});

@ -13,8 +13,8 @@ namespace Lidarr.Api.V1.Notifications
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
{
if (!definition.OnGrab && !definition.OnDownload) return;
if (!definition.Enable) return;
base.Validate(definition, includeWarnings);
}
}
}
}

@ -6,15 +6,22 @@ namespace Lidarr.Api.V1.Notifications
{
public string Link { get; set; }
public bool OnGrab { get; set; }
public bool OnDownload { get; set; }
public bool OnAlbumDownload { get; set; }
public bool OnReleaseImport { get; set; }
public bool OnUpgrade { get; set; }
public bool OnRename { get; set; }
public bool OnHealthIssue { get; set; }
public bool OnDownloadFailure { get; set; }
public bool OnImportFailure { get; set; }
public bool OnTrackRetag { get; set; }
public bool SupportsOnGrab { get; set; }
public bool SupportsOnDownload { get; set; }
public bool SupportsOnAlbumDownload { get; set; }
public bool SupportsOnReleaseImport { get; set; }
public bool SupportsOnUpgrade { get; set; }
public bool SupportsOnRename { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnDownloadFailure { get; set; }
public bool SupportsOnImportFailure { get; set; }
public bool SupportsOnTrackRetag { get; set; }
public string TestCommand { get; set; }
}
@ -27,15 +34,22 @@ namespace Lidarr.Api.V1.Notifications
var resource = base.ToResource(definition);
resource.OnGrab = definition.OnGrab;
resource.OnDownload = definition.OnDownload;
resource.OnAlbumDownload = definition.OnAlbumDownload;
resource.OnReleaseImport = definition.OnReleaseImport;
resource.OnUpgrade = definition.OnUpgrade;
resource.OnRename = definition.OnRename;
resource.OnHealthIssue = definition.OnHealthIssue;
resource.OnDownloadFailure = definition.OnDownloadFailure;
resource.OnImportFailure = definition.OnImportFailure;
resource.OnTrackRetag = definition.OnTrackRetag;
resource.SupportsOnGrab = definition.SupportsOnGrab;
resource.SupportsOnDownload = definition.SupportsOnDownload;
resource.SupportsOnAlbumDownload = definition.SupportsOnAlbumDownload;
resource.SupportsOnReleaseImport = definition.SupportsOnReleaseImport;
resource.SupportsOnUpgrade = definition.SupportsOnUpgrade;
resource.SupportsOnRename = definition.SupportsOnRename;
resource.SupportsOnHealthIssue = definition.SupportsOnHealthIssue;
resource.IncludeHealthWarnings = definition.IncludeHealthWarnings;
resource.SupportsOnDownloadFailure = definition.SupportsOnDownloadFailure;
resource.SupportsOnImportFailure = definition.SupportsOnImportFailure;
resource.SupportsOnTrackRetag = definition.SupportsOnTrackRetag;
return resource;
}
@ -47,15 +61,22 @@ namespace Lidarr.Api.V1.Notifications
var definition = base.ToModel(resource);
definition.OnGrab = resource.OnGrab;
definition.OnDownload = resource.OnDownload;
definition.OnAlbumDownload = resource.OnAlbumDownload;
definition.OnReleaseImport = resource.OnReleaseImport;
definition.OnUpgrade = resource.OnUpgrade;
definition.OnRename = resource.OnRename;
definition.OnHealthIssue = resource.OnHealthIssue;
definition.OnDownloadFailure = resource.OnDownloadFailure;
definition.OnImportFailure = resource.OnImportFailure;
definition.OnTrackRetag = resource.OnTrackRetag;
definition.SupportsOnGrab = resource.SupportsOnGrab;
definition.SupportsOnDownload = resource.SupportsOnDownload;
definition.SupportsOnAlbumDownload = resource.SupportsOnAlbumDownload;
definition.SupportsOnReleaseImport = resource.SupportsOnReleaseImport;
definition.SupportsOnUpgrade = resource.SupportsOnUpgrade;
definition.SupportsOnRename = resource.SupportsOnRename;
definition.SupportsOnHealthIssue = resource.SupportsOnHealthIssue;
definition.IncludeHealthWarnings = resource.IncludeHealthWarnings;
definition.SupportsOnDownloadFailure = resource.SupportsOnDownloadFailure;
definition.SupportsOnImportFailure = resource.SupportsOnImportFailure;
definition.SupportsOnTrackRetag = resource.SupportsOnTrackRetag;
return definition;
}

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.NotificationTests
}
}
class TestNotificationWithOnDownload : NotificationBase<TestSetting>
class TestNotificationWithOnReleaseImport : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.NotificationTests
throw new NotImplementedException();
}
public override void OnDownload(TrackDownloadMessage trackDownloadMessage)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
TestLogger.Info("OnDownload was called");
}
@ -55,12 +55,7 @@ namespace NzbDrone.Core.Test.NotificationTests
TestLogger.Info("OnGrab was called");
}
public override void OnDownload(TrackDownloadMessage message)
{
TestLogger.Info("OnDownload was called");
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
TestLogger.Info("OnAlbumDownload was called");
}
@ -70,6 +65,25 @@ namespace NzbDrone.Core.Test.NotificationTests
TestLogger.Info("OnRename was called");
}
public override void OnHealthIssue(NzbDrone.Core.HealthCheck.HealthCheck artist)
{
TestLogger.Info("OnHealthIssue was called");
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
TestLogger.Info("OnDownloadFailure was called");
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
TestLogger.Info("OnImportFailure was called");
}
public override void OnTrackRetag(TrackRetagMessage message)
{
TestLogger.Info("OnTrackRetag was called");
}
}
class TestNotificationWithNoEvents : NotificationBase<TestSetting>
@ -87,11 +101,11 @@ namespace NzbDrone.Core.Test.NotificationTests
}
[Test]
public void should_support_OnUpgrade_should_link_to_OnDownload()
public void should_support_OnUpgrade_should_link_to_OnReleaseImport()
{
var notification = new TestNotificationWithOnDownload();
var notification = new TestNotificationWithOnReleaseImport();
notification.SupportsOnDownload.Should().BeTrue();
notification.SupportsOnReleaseImport.Should().BeTrue();
notification.SupportsOnUpgrade.Should().BeTrue();
notification.SupportsOnGrab.Should().BeFalse();
@ -104,10 +118,13 @@ namespace NzbDrone.Core.Test.NotificationTests
var notification = new TestNotificationWithAllEvents();
notification.SupportsOnGrab.Should().BeTrue();
notification.SupportsOnDownload.Should().BeTrue();
notification.SupportsOnAlbumDownload.Should().BeTrue();
notification.SupportsOnReleaseImport.Should().BeTrue();
notification.SupportsOnUpgrade.Should().BeTrue();
notification.SupportsOnRename.Should().BeTrue();
notification.SupportsOnHealthIssue.Should().BeTrue();
notification.SupportsOnDownloadFailure.Should().BeTrue();
notification.SupportsOnImportFailure.Should().BeTrue();
notification.SupportsOnTrackRetag.Should().BeTrue();
}
@ -117,10 +134,13 @@ namespace NzbDrone.Core.Test.NotificationTests
var notification = new TestNotificationWithNoEvents();
notification.SupportsOnGrab.Should().BeFalse();
notification.SupportsOnDownload.Should().BeFalse();
notification.SupportsOnAlbumDownload.Should().BeFalse();
notification.SupportsOnReleaseImport.Should().BeFalse();
notification.SupportsOnUpgrade.Should().BeFalse();
notification.SupportsOnRename.Should().BeFalse();
notification.SupportsOnHealthIssue.Should().BeFalse();
notification.SupportsOnDownloadFailure.Should().BeFalse();
notification.SupportsOnImportFailure.Should().BeFalse();
notification.SupportsOnTrackRetag.Should().BeFalse();
}
}

@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.NotificationTests
[Test]
public void should_remove_old_episodes_on_upgrade()
{
Subject.OnAlbumDownload(_upgrade);
Subject.OnReleaseImport(_upgrade);
Mocker.GetMock<ISynologyIndexerProxy>()
.Verify(v => v.DeleteFile(@"C:\Test\file1.S01E01.mkv".AsOsAgnostic()), Times.Once());
@ -85,7 +85,7 @@ namespace NzbDrone.Core.Test.NotificationTests
[Test]
public void should_add_new_episode_on_upgrade()
{
Subject.OnAlbumDownload(_upgrade);
Subject.OnReleaseImport(_upgrade);
Mocker.GetMock<ISynologyIndexerProxy>()
.Verify(v => v.AddFile(@"C:\Test\file1.S01E01E02.mkv".AsOsAgnostic()), Times.Once());

@ -24,10 +24,12 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
.Build();
_xbmcArtist = Builder<KodiArtist>.CreateListOfSize(3)
.TheFirst(1)
.With(s => s.MusicbrainzArtistId = new List<string> { MB_ID.ToString()})
.Build()
.ToList();
.TheFirst(1)
.With(s => s.MusicbrainzArtistId = new List<string> { MB_ID.ToString()})
.TheNext(2)
.With(s => s.MusicbrainzArtistId = new List<string>())
.Build()
.ToList();
Mocker.GetMock<IXbmcJsonApiProxy>()
.Setup(s => s.GetArtist(_settings))

@ -12,9 +12,9 @@ using NzbDrone.Core.Music;
namespace NzbDrone.Core.Test.NotificationTests.Xbmc
{
[TestFixture]
public class OnDownloadFixture : CoreTest<Notifications.Xbmc.Xbmc>
public class OnReleaseImportFixture : CoreTest<Notifications.Xbmc.Xbmc>
{
private TrackDownloadMessage _trackDownloadMessage;
private AlbumDownloadMessage _albumDownloadMessage;
[SetUp]
public void Setup()
@ -25,11 +25,11 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
var trackFile = Builder<TrackFile>.CreateNew()
.Build();
_trackDownloadMessage = Builder<TrackDownloadMessage>.CreateNew()
.With(d => d.Artist = artist)
.With(d => d.TrackFile = trackFile)
.With(d => d.OldFiles = new List<TrackFile>())
.Build();
_albumDownloadMessage = Builder<AlbumDownloadMessage>.CreateNew()
.With(d => d.Artist = artist)
.With(d => d.TrackFiles = new List<TrackFile> { trackFile })
.With(d => d.OldFiles = new List<TrackFile>())
.Build();
Subject.Definition = new NotificationDefinition();
Subject.Definition.Settings = new XbmcSettings
@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
private void GivenOldFiles()
{
_trackDownloadMessage.OldFiles = Builder<TrackFile>.CreateListOfSize(1)
_albumDownloadMessage.OldFiles = Builder<TrackFile>.CreateListOfSize(1)
.Build()
.ToList();
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
[Test]
public void should_not_clean_if_no_episode_was_replaced()
{
Subject.OnDownload(_trackDownloadMessage);
Subject.OnReleaseImport(_albumDownloadMessage);
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Never());
}
@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc
public void should_clean_if_episode_was_replaced()
{
GivenOldFiles();
Subject.OnDownload(_trackDownloadMessage);
Subject.OnReleaseImport(_albumDownloadMessage);
Mocker.GetMock<IXbmcService>().Verify(v => v.Clean(It.IsAny<XbmcSettings>()), Times.Once());
}

@ -357,7 +357,7 @@
<Compile Include="NotificationTests\Xbmc\Http\UpdateFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\GetArtistPathFixture.cs" />
<Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" />
<Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" />
<Compile Include="NotificationTests\Xbmc\OnReleaseImportFixture.cs" />
<Compile Include="OrganizerTests\BuildFilePathFixture.cs" />
<Compile Include="OrganizerTests\GetAlbumFolderFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\FileNameBuilderFixture.cs" />

@ -0,0 +1,23 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(29)]
public class health_issue_notification : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Notifications").AddColumn("OnHealthIssue").AsBoolean().WithDefaultValue(0);
Alter.Table("Notifications").AddColumn("IncludeHealthWarnings").AsBoolean().WithDefaultValue(0);
Alter.Table("Notifications").AddColumn("OnDownloadFailure").AsBoolean().WithDefaultValue(0);
Alter.Table("Notifications").AddColumn("OnImportFailure").AsBoolean().WithDefaultValue(0);
Alter.Table("Notifications").AddColumn("OnTrackRetag").AsBoolean().WithDefaultValue(0);
Delete.Column("OnDownload").FromTable("Notifications");
Rename.Column("OnAlbumDownload").OnTable("Notifications").To("OnReleaseImport");
}
}
}

@ -30,7 +30,6 @@ using NzbDrone.Core.ArtistStats;
using NzbDrone.Core.Tags;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.CustomFilters;
using NzbDrone.Core.Extras.Metadata;
@ -73,10 +72,13 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications")
.Ignore(i => i.SupportsOnGrab)
.Ignore(i => i.SupportsOnDownload)
.Ignore(i => i.SupportsOnAlbumDownload)
.Ignore(i => i.SupportsOnReleaseImport)
.Ignore(i => i.SupportsOnUpgrade)
.Ignore(i => i.SupportsOnRename);
.Ignore(i => i.SupportsOnRename)
.Ignore(i => i.SupportsOnHealthIssue)
.Ignore(i => i.SupportsOnDownloadFailure)
.Ignore(i => i.SupportsOnImportFailure)
.Ignore(i => i.SupportsOnTrackRetag);
Mapper.Entity<MetadataDefinition>().RegisterDefinition("Metadata")
.Ignore(d => d.Tags);

@ -0,0 +1,14 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.HealthCheck
{
public class HealthCheckFailedEvent : IEvent
{
public HealthCheck HealthCheck { get; private set; }
public HealthCheckFailedEvent(HealthCheck healthCheck)
{
HealthCheck = healthCheck;
}
}
}

@ -80,7 +80,13 @@ namespace NzbDrone.Core.HealthCheck
else
{
if (_healthCheckResults.Find(result.Source.Name) == null)
{
_eventAggregator.PublishEvent(new HealthCheckFailedEvent(result));
}
_healthCheckResults.Set(result.Source.Name, result);
}
}

@ -21,14 +21,24 @@ namespace NzbDrone.Core.Notifications.Boxcar
_proxy.SendNotification(ALBUM_GRABBED_TITLE, grabMessage.Message, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE , message.Message, Settings);
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_proxy.SendNotification(DOWNLOAD_FAILURE_TITLE, message.Message, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_proxy.SendNotification(IMPORT_FAILURE_TITLE, message.Message, Settings);
}
public override ValidationResult Test()

@ -6,6 +6,7 @@ using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Processes;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Music;
using NzbDrone.Core.Validation;
@ -55,17 +56,14 @@ namespace NzbDrone.Core.Notifications.CustomScript
ExecuteScript(environmentVariables);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
var artist = message.Artist;
var album = message.Album;
var release = message.Release;
var trackFile = message.TrackFile;
var sourcePath = message.SourcePath;
var environmentVariables = new StringDictionary();
environmentVariables.Add("Lidarr_EventType", "Download");
environmentVariables.Add("Lidarr_IsUpgrade", message.OldFiles.Any().ToString());
environmentVariables.Add("Lidarr_EventType", "AlbumDownload");
environmentVariables.Add("Lidarr_Artist_Id", artist.Id.ToString());
environmentVariables.Add("Lidarr_Artist_Name", artist.Metadata.Value.Name);
environmentVariables.Add("Lidarr_Artist_Path", artist.Path);
@ -76,21 +74,15 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Lidarr_Album_MBId", album.ForeignAlbumId);
environmentVariables.Add("Lidarr_AlbumRelease_MBId", release.ForeignReleaseId);
environmentVariables.Add("Lidarr_Album_ReleaseDate", album.ReleaseDate.ToString());
environmentVariables.Add("Lidarr_TrackFile_Id", trackFile.Id.ToString());
environmentVariables.Add("Lidarr_TrackFile_TrackCount", trackFile.Tracks.Value.Count.ToString());
environmentVariables.Add("Lidarr_TrackFile_RelativePath", trackFile.RelativePath);
environmentVariables.Add("Lidarr_TrackFile_Path", Path.Combine(artist.Path, trackFile.RelativePath));
environmentVariables.Add("Lidarr_TrackFile_TrackNumbers", string.Join(",", trackFile.Tracks.Value.Select(e => e.TrackNumber)));
environmentVariables.Add("Lidarr_TrackFile_TrackTitles", string.Join("|", trackFile.Tracks.Value.Select(e => e.Title)));
environmentVariables.Add("Lidarr_TrackFile_Quality", trackFile.Quality.Quality.Name);
environmentVariables.Add("Lidarr_TrackFile_QualityVersion", trackFile.Quality.Revision.Version.ToString());
environmentVariables.Add("Lidarr_TrackFile_ReleaseGroup", trackFile.ReleaseGroup ?? string.Empty);
environmentVariables.Add("Lidarr_TrackFile_SceneName", trackFile.SceneName ?? string.Empty);
environmentVariables.Add("Lidarr_TrackFile_SourcePath", sourcePath);
environmentVariables.Add("Lidarr_TrackFile_SourceFolder", Path.GetDirectoryName(sourcePath));
environmentVariables.Add("Lidarr_Download_Client", message.DownloadClient ?? string.Empty);
environmentVariables.Add("Lidarr_Download_Id", message.DownloadId ?? string.Empty);
if (message.TrackFiles.Any())
{
environmentVariables.Add("Lidarr_AddedTrackRelativePaths", string.Join("|", message.TrackFiles.Select(e => e.RelativePath)));
environmentVariables.Add("Lidarr_AddedTrackPaths", string.Join("|", message.TrackFiles.Select(e => Path.Combine(artist.Path, e.RelativePath))));
}
if (message.OldFiles.Any())
{
environmentVariables.Add("Lidarr_DeletedRelativePaths", string.Join("|", message.OldFiles.Select(e => e.RelativePath)));
@ -100,14 +92,29 @@ namespace NzbDrone.Core.Notifications.CustomScript
ExecuteScript(environmentVariables);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnRename(Artist artist)
{
var environmentVariables = new StringDictionary();
environmentVariables.Add("Lidarr_EventType", "Rename");
environmentVariables.Add("Lidarr_Artist_Id", artist.Id.ToString());
environmentVariables.Add("Lidarr_Artist_Name", artist.Metadata.Value.Name);
environmentVariables.Add("Lidarr_Artist_Path", artist.Path);
environmentVariables.Add("Lidarr_Artist_MBId", artist.Metadata.Value.ForeignArtistId);
environmentVariables.Add("Lidarr_Artist_Type", artist.Metadata.Value.Type);
ExecuteScript(environmentVariables);
}
public override void OnTrackRetag(TrackRetagMessage message)
{
var artist = message.Artist;
var album = message.Album;
var release = message.Release;
var trackFile = message.TrackFile;
var environmentVariables = new StringDictionary();
environmentVariables.Add("Lidarr_EventType", "AlbumDownload");
environmentVariables.Add("Lidarr_EventType", "TrackRetag");
environmentVariables.Add("Lidarr_Artist_Id", artist.Id.ToString());
environmentVariables.Add("Lidarr_Artist_Name", artist.Metadata.Value.Name);
environmentVariables.Add("Lidarr_Artist_Path", artist.Path);
@ -118,39 +125,35 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Lidarr_Album_MBId", album.ForeignAlbumId);
environmentVariables.Add("Lidarr_AlbumRelease_MBId", release.ForeignReleaseId);
environmentVariables.Add("Lidarr_Album_ReleaseDate", album.ReleaseDate.ToString());
environmentVariables.Add("Lidarr_Download_Client", message.DownloadClient ?? string.Empty);
environmentVariables.Add("Lidarr_Download_Id", message.DownloadId ?? string.Empty);
if (message.TrackFiles.Any())
{
environmentVariables.Add("Lidarr_AddedTrackRelativePaths", string.Join("|", message.TrackFiles.Select(e => e.RelativePath)));
environmentVariables.Add("Lidarr_AddedTrackPaths", string.Join("|", message.TrackFiles.Select(e => Path.Combine(artist.Path, e.RelativePath))));
}
if (message.OldFiles.Any())
{
environmentVariables.Add("Lidarr_DeletedRelativePaths", string.Join("|", message.OldFiles.Select(e => e.RelativePath)));
environmentVariables.Add("Lidarr_DeletedPaths", string.Join("|", message.OldFiles.Select(e => Path.Combine(artist.Path, e.RelativePath))));
}
environmentVariables.Add("Lidarr_TrackFile_Id", trackFile.Id.ToString());
environmentVariables.Add("Lidarr_TrackFile_TrackCount", trackFile.Tracks.Value.Count.ToString());
environmentVariables.Add("Lidarr_TrackFile_RelativePath", trackFile.RelativePath);
environmentVariables.Add("Lidarr_TrackFile_Path", Path.Combine(artist.Path, trackFile.RelativePath));
environmentVariables.Add("Lidarr_TrackFile_TrackNumbers", string.Join(",", trackFile.Tracks.Value.Select(e => e.TrackNumber)));
environmentVariables.Add("Lidarr_TrackFile_TrackTitles", string.Join("|", trackFile.Tracks.Value.Select(e => e.Title)));
environmentVariables.Add("Lidarr_TrackFile_Quality", trackFile.Quality.Quality.Name);
environmentVariables.Add("Lidarr_TrackFile_QualityVersion", trackFile.Quality.Revision.Version.ToString());
environmentVariables.Add("Lidarr_TrackFile_ReleaseGroup", trackFile.ReleaseGroup ?? string.Empty);
environmentVariables.Add("Lidarr_TrackFile_SceneName", trackFile.SceneName ?? string.Empty);
environmentVariables.Add("Lidarr_Tags_Diff", message.Diff.ToJson());
environmentVariables.Add("Lidarr_Tags_Scrubbed", message.Scrubbed.ToString());
ExecuteScript(environmentVariables);
}
public override void OnRename(Artist artist)
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
var environmentVariables = new StringDictionary();
environmentVariables.Add("Lidarr_EventType", "Rename");
environmentVariables.Add("Lidarr_Artist_Id", artist.Id.ToString());
environmentVariables.Add("Lidarr_Artist_Name", artist.Metadata.Value.Name);
environmentVariables.Add("Lidarr_Artist_Path", artist.Path);
environmentVariables.Add("Lidarr_Artist_MBId", artist.Metadata.Value.ForeignArtistId);
environmentVariables.Add("Lidarr_Artist_Type", artist.Metadata.Value.Type);
environmentVariables.Add("Lidarr_EventType", "HealthIssue");
environmentVariables.Add("Lidarr_Health_Issue_Level", nameof(healthCheck.Type));
environmentVariables.Add("Lidarr_Health_Issue_Message", healthCheck.Message);
environmentVariables.Add("Lidarr_Health_Issue_Type", healthCheck.Source.Name);
environmentVariables.Add("Lidarr_Health_Issue_Wiki", healthCheck.WikiUrl.ToString() ?? string.Empty);
ExecuteScript(environmentVariables);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Notifications.Discord.Payloads;
using NzbDrone.Core.Music;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Discord
{
public class Discord : NotificationBase<DiscordSettings>
{
private readonly IDiscordProxy _proxy;
public Discord(IDiscordProxy proxy)
{
_proxy = proxy;
}
public override string Name => "Discord";
public override string Link => "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
public override void OnGrab(GrabMessage message)
{
var embeds = new List<Embed>
{
new Embed
{
Description = message.Message,
Title = message.Artist.Name,
Text = message.Message,
Color = (int)DiscordColors.Warning
}
};
var payload = CreatePayload($"Grabbed: {message.Message}", embeds);
_proxy.SendPayload(payload, Settings);
}
public override void OnReleaseImport(AlbumDownloadMessage message)
{
var attachments = new List<Embed>
{
new Embed
{
Description = message.Message,
Title = message.Artist.Name,
Text = message.Message,
Color = (int)DiscordColors.Success
}
};
var payload = CreatePayload($"Imported: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnRename(Artist artist)
{
var attachments = new List<Embed>
{
new Embed
{
Title = artist.Name,
}
};
var payload = CreatePayload("Renamed", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
var attachments = new List<Embed>
{
new Embed
{
Title = healthCheck.Source.Name,
Text = healthCheck.Message,
Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? (int)DiscordColors.Warning : (int)DiscordColors.Danger
}
};
var payload = CreatePayload("Health Issue", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnTrackRetag(TrackRetagMessage message)
{
var attachments = new List<Embed>
{
new Embed
{
Title = TRACK_RETAGGED_TITLE,
Text = message.Message
}
};
var payload = CreatePayload($"Track file tags updated: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
var attachments = new List<Embed>
{
new Embed
{
Description = message.Message,
Title = message.SourceTitle,
Text = message.Message,
Color = (int)DiscordColors.Danger
}
};
var payload = CreatePayload($"Download Failed: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
var attachments = new List<Embed>
{
new Embed
{
Description = message.Message,
Title = message.Album.Title,
Text = message.Message,
Color = (int)DiscordColors.Warning
}
};
var payload = CreatePayload($"Import Failed: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(TestMessage());
return new ValidationResult(failures);
}
public ValidationFailure TestMessage()
{
try
{
var message = $"Test message from Lidarr posted at {DateTime.Now}";
var payload = CreatePayload(message);
_proxy.SendPayload(payload, Settings);
}
catch (DiscordException ex)
{
return new NzbDroneValidationFailure("Unable to post", ex.Message);
}
return null;
}
private DiscordPayload CreatePayload(string message, List<Embed> embeds = null)
{
var avatar = Settings.Avatar;
var payload = new DiscordPayload
{
Username = Settings.Username,
Content = message,
Embeds = embeds
};
if (avatar.IsNotNullOrWhiteSpace())
{
payload.AvatarUrl = avatar;
}
if (Settings.Username.IsNotNullOrWhiteSpace())
{
payload.Username = Settings.Username;
}
return payload;
}
}
}

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Notifications.Discord
{
public enum DiscordColors
{
Danger = 15749200,
Success = 2605644,
Warning = 16753920
}
}

@ -0,0 +1,16 @@
using System;
using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.Notifications.Discord
{
class DiscordException : NzbDroneException
{
public DiscordException(string message) : base(message)
{
}
public DiscordException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
{
}
}
}

@ -0,0 +1,46 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications.Discord.Payloads;
using NzbDrone.Core.Rest;
namespace NzbDrone.Core.Notifications.Discord
{
public interface IDiscordProxy
{
void SendPayload(DiscordPayload payload, DiscordSettings settings);
}
public class DiscordProxy : IDiscordProxy
{
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
public DiscordProxy(IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
}
public void SendPayload(DiscordPayload payload, DiscordSettings settings)
{
try
{
var request = new HttpRequestBuilder(settings.WebHookUrl)
.Accept(HttpAccept.Json)
.Build();
request.Method = HttpMethod.POST;
request.Headers.ContentType = "application/json";
request.SetContent(payload.ToJson());
_httpClient.Execute(request);
}
catch (RestException ex)
{
_logger.Error(ex, "Unable to post payload {0}", payload);
throw new DiscordException("Unable to post payload", ex);
}
}
}
}

@ -0,0 +1,35 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Discord
{
public class DiscordSettingsValidator : AbstractValidator<DiscordSettings>
{
public DiscordSettingsValidator()
{
RuleFor(c => c.WebHookUrl).IsValidUrl();
}
}
public class DiscordSettings : IProviderConfig
{
private static readonly DiscordSettingsValidator Validator = new DiscordSettingsValidator();
[FieldDefinition(0, Label = "Webhook URL", HelpText = "Discord channel webhook url")]
public string WebHookUrl { get; set; }
[FieldDefinition(1, Label = "Username", HelpText = "The username to post as, defaults to Discord webhook default")]
public string Username { get; set; }
[FieldDefinition(2, Label = "Avatar", HelpText = "Change the avatar that is used for messages from this integration", Type = FieldType.Textbox)]
public string Avatar { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

@ -0,0 +1,17 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace NzbDrone.Core.Notifications.Discord.Payloads
{
public class DiscordPayload
{
public string Content { get; set; }
public string Username { get; set; }
[JsonProperty("avatar_url")]
public string AvatarUrl { get; set; }
public List<Embed> Embeds { get; set; }
}
}

@ -0,0 +1,10 @@
namespace NzbDrone.Core.Notifications.Discord.Payloads
{
public class Embed
{
public string Description { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int Color { get; set; }
}
}

@ -0,0 +1,22 @@
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Music;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Notifications
{
public class DownloadFailedMessage
{
public string Message { get; set; }
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }
public Language Language { get; set; }
public string DownloadClient { get; set; }
public string DownloadId { get; set; }
public override string ToString()
{
return Message;
}
}
}

@ -25,18 +25,26 @@ namespace NzbDrone.Core.Notifications.Email
_emailService.SendEmail(Settings, ALBUM_GRABBED_TITLE_BRANDED, body);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
var body = $"{message.Message} Downloaded and sorted.";
_emailService.SendEmail(Settings, TRACK_DOWNLOADED_TITLE_BRANDED, body);
_emailService.SendEmail(Settings, ALBUM_DOWNLOADED_TITLE_BRANDED, body);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
var body = $"{message.Message} Downloaded and sorted.";
_emailService.SendEmail(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message);
}
_emailService.SendEmail(Settings, ALBUM_DOWNLOADED_TITLE_BRANDED, body);
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_emailService.SendEmail(Settings, DOWNLOAD_FAILURE_TITLE_BRANDED, message.Message);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_emailService.SendEmail(Settings, IMPORT_FAILURE_TITLE_BRANDED, message.Message);
}
public override ValidationResult Test()

@ -23,14 +23,24 @@ namespace NzbDrone.Core.Notifications.Growl
_growlService.SendNotification(ALBUM_GRABBED_TITLE, grabMessage.Message, "GRAB", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_growlService.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, "TRACKDOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
_growlService.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, "ALBUMDOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
_growlService.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, "ALBUMDOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
_growlService.SendNotification(HEALTH_ISSUE_TITLE, message.Message, "HEALTHISSUE", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_growlService.SendNotification(DOWNLOAD_FAILURE_TITLE, message.Message, "DOWNLOADFAILURE", Settings.Host, Settings.Port, Settings.Password);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_growlService.SendNotification(IMPORT_FAILURE_TITLE, message.Message, "IMPORTFAILURE", Settings.Host, Settings.Port, Settings.Password);
}
public override ValidationResult Test()

@ -8,13 +8,19 @@ namespace NzbDrone.Core.Notifications
string Link { get; }
void OnGrab(GrabMessage grabMessage);
void OnDownload(TrackDownloadMessage message);
void OnAlbumDownload(AlbumDownloadMessage message);
void OnReleaseImport(AlbumDownloadMessage message);
void OnRename(Artist artist);
void OnHealthIssue(HealthCheck.HealthCheck healthCheck);
void OnDownloadFailure(DownloadFailedMessage message);
void OnImportFailure(AlbumDownloadMessage message);
void OnTrackRetag(TrackRetagMessage message);
bool SupportsOnGrab { get; }
bool SupportsOnDownload { get; }
bool SupportsOnAlbumDownload { get; }
bool SupportsOnReleaseImport { get; }
bool SupportsOnUpgrade { get; }
bool SupportsOnRename { get; }
bool SupportsOnHealthIssue { get; }
bool SupportsOnDownloadFailure { get; }
bool SupportsOnImportFailure { get; }
bool SupportsOnTrackRetag { get; }
}
}

@ -22,14 +22,14 @@ namespace NzbDrone.Core.Notifications.Join
_proxy.SendNotification(ALBUM_GRABBED_TITLE_BRANDED, message.Message, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, message.Message, Settings);
}
public override ValidationResult Test()

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Emby
}
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
if (Settings.Notify)
{
@ -39,22 +39,29 @@ namespace NzbDrone.Core.Notifications.Emby
}
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnRename(Artist artist)
{
if (Settings.Notify)
if (Settings.UpdateLibrary)
{
_mediaBrowserService.Notify(Settings, TRACK_DOWNLOADED_TITLE_BRANDED, message.Message);
_mediaBrowserService.Update(Settings, artist);
}
}
public override void OnRename(Artist artist)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
if (Settings.UpdateLibrary)
if (Settings.Notify)
{
_mediaBrowserService.Update(Settings, artist);
_mediaBrowserService.Notify(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message);
}
}
public override void OnTrackRetag(TrackRetagMessage message)
{
if (Settings.Notify)
{
_mediaBrowserService.Notify(Settings, TRACK_RETAGGED_TITLE_BRANDED, message.Message);
}
}
public override ValidationResult Test()
{

@ -9,12 +9,18 @@ namespace NzbDrone.Core.Notifications
public abstract class NotificationBase<TSettings> : INotification where TSettings : IProviderConfig, new()
{
protected const string ALBUM_GRABBED_TITLE = "Album Grabbed";
protected const string TRACK_DOWNLOADED_TITLE = "Track Downloaded";
protected const string ALBUM_DOWNLOADED_TITLE = "Album Downloaded";
protected const string HEALTH_ISSUE_TITLE = "Health Check Failure";
protected const string DOWNLOAD_FAILURE_TITLE = "Download Failed";
protected const string IMPORT_FAILURE_TITLE = "Import Failed";
protected const string TRACK_RETAGGED_TITLE = "Track File Tags Updated";
protected const string ALBUM_GRABBED_TITLE_BRANDED = "Lidarr - " + ALBUM_GRABBED_TITLE;
protected const string TRACK_DOWNLOADED_TITLE_BRANDED = "Lidarr - " + TRACK_DOWNLOADED_TITLE;
protected const string ALBUM_DOWNLOADED_TITLE_BRANDED = "Lidarr - " + ALBUM_DOWNLOADED_TITLE;
protected const string HEALTH_ISSUE_TITLE_BRANDED = "Lidarr - " + HEALTH_ISSUE_TITLE;
protected const string DOWNLOAD_FAILURE_TITLE_BRANDED = "Lidarr - " + DOWNLOAD_FAILURE_TITLE;
protected const string IMPORT_FAILURE_TITLE_BRANDED = "Lidarr - " + IMPORT_FAILURE_TITLE;
protected const string TRACK_RETAGGED_TITLE_BRANDED = "Lidarr - " + TRACK_RETAGGED_TITLE;
public abstract string Name { get; }
@ -34,26 +40,44 @@ namespace NzbDrone.Core.Notifications
}
public virtual void OnDownload(TrackDownloadMessage message)
public virtual void OnReleaseImport(AlbumDownloadMessage message)
{
}
public virtual void OnAlbumDownload(AlbumDownloadMessage message)
public virtual void OnRename(Artist artist)
{
}
public virtual void OnRename(Artist artist)
public virtual void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
}
public virtual void OnDownloadFailure(DownloadFailedMessage message)
{
}
public virtual void OnImportFailure(AlbumDownloadMessage message)
{
}
public virtual void OnTrackRetag(TrackRetagMessage message)
{
}
public bool SupportsOnGrab => HasConcreteImplementation("OnGrab");
public bool SupportsOnRename => HasConcreteImplementation("OnRename");
public bool SupportsOnDownload => HasConcreteImplementation("OnDownload");
public bool SupportsOnAlbumDownload => HasConcreteImplementation("OnAlbumDownload");
public bool SupportsOnUpgrade => SupportsOnDownload;
public bool SupportsOnReleaseImport => HasConcreteImplementation("OnReleaseImport");
public bool SupportsOnUpgrade => SupportsOnReleaseImport;
public bool SupportsOnHealthIssue => HasConcreteImplementation("OnHealthIssue");
public bool SupportsOnDownloadFailure => HasConcreteImplementation("OnDownloadFailure");
public bool SupportsOnImportFailure => HasConcreteImplementation("OnImportFailure");
public bool SupportsOnTrackRetag => HasConcreteImplementation("OnTrackRetag");
protected TSettings Settings => (TSettings)Definition.Settings;

@ -6,16 +6,23 @@ namespace NzbDrone.Core.Notifications
{
public bool OnGrab { get; set; }
public bool OnDownload { get; set; }
public bool OnAlbumDownload { get; set; }
public bool OnReleaseImport { get; set; }
public bool OnUpgrade { get; set; }
public bool OnRename { get; set; }
public bool OnHealthIssue { get; set; }
public bool OnDownloadFailure { get; set; }
public bool OnImportFailure { get; set; }
public bool OnTrackRetag { get; set; }
public bool SupportsOnGrab { get; set; }
public bool SupportsOnDownload { get; set; }
public bool SupportsOnAlbumDownload { get; set; }
public bool SupportsOnReleaseImport { get; set; }
public bool SupportsOnUpgrade { get; set; }
public bool SupportsOnRename { get; set; }
public bool SupportsOnHealthIssue { get; set; }
public bool IncludeHealthWarnings { get; set; }
public bool SupportsOnDownloadFailure { get; set; }
public bool SupportsOnImportFailure { get; set; }
public bool SupportsOnTrackRetag { get; set; }
public override bool Enable => OnGrab || OnDownload || OnAlbumDownload || (OnDownload && OnUpgrade);
public override bool Enable => OnGrab || OnReleaseImport || (OnReleaseImport && OnUpgrade) || OnHealthIssue || OnDownloadFailure || OnImportFailure || OnTrackRetag;
}
}

@ -10,10 +10,13 @@ namespace NzbDrone.Core.Notifications
public interface INotificationFactory : IProviderFactory<INotification, NotificationDefinition>
{
List<INotification> OnGrabEnabled();
List<INotification> OnDownloadEnabled();
List<INotification> OnAlbumDownloadEnabled();
List<INotification> OnReleaseImportEnabled();
List<INotification> OnUpgradeEnabled();
List<INotification> OnRenameEnabled();
List<INotification> OnHealthIssueEnabled();
List<INotification> OnDownloadFailureEnabled();
List<INotification> OnImportFailureEnabled();
List<INotification> OnTrackRetagEnabled();
}
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
@ -28,14 +31,9 @@ namespace NzbDrone.Core.Notifications
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnGrab).ToList();
}
public List<INotification> OnDownloadEnabled()
public List<INotification> OnReleaseImportEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnDownload).ToList();
}
public List<INotification> OnAlbumDownloadEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnAlbumDownload).ToList();
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnReleaseImport).ToList();
}
public List<INotification> OnUpgradeEnabled()
@ -48,15 +46,38 @@ namespace NzbDrone.Core.Notifications
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnRename).ToList();
}
public List<INotification> OnHealthIssueEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnHealthIssue).ToList();
}
public List<INotification> OnDownloadFailureEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnDownloadFailure).ToList();
}
public List<INotification> OnImportFailureEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnImportFailure).ToList();
}
public List<INotification> OnTrackRetagEnabled()
{
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnTrackRetag).ToList();
}
public override void SetProviderCharacteristics(INotification provider, NotificationDefinition definition)
{
base.SetProviderCharacteristics(provider, definition);
definition.SupportsOnGrab = provider.SupportsOnGrab;
definition.SupportsOnDownload = provider.SupportsOnDownload;
definition.SupportsOnAlbumDownload = provider.SupportsOnAlbumDownload;
definition.SupportsOnReleaseImport = provider.SupportsOnReleaseImport;
definition.SupportsOnUpgrade = provider.SupportsOnUpgrade;
definition.SupportsOnRename = provider.SupportsOnRename;
definition.SupportsOnHealthIssue = provider.SupportsOnHealthIssue;
definition.SupportsOnDownloadFailure = provider.SupportsOnDownloadFailure;
definition.SupportsOnImportFailure = provider.SupportsOnImportFailure;
definition.SupportsOnTrackRetag = provider.SupportsOnTrackRetag;
}
}
}

@ -1,4 +1,4 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
@ -7,7 +7,6 @@ namespace NzbDrone.Core.Notifications
{
public interface INotificationRepository : IProviderRepository<NotificationDefinition>
{
}
public class NotificationRepository : ProviderRepository<NotificationDefinition>, INotificationRepository
@ -17,4 +16,4 @@ namespace NzbDrone.Core.Notifications
{
}
}
}
}

@ -10,15 +10,19 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.HealthCheck;
using System.IO;
namespace NzbDrone.Core.Notifications
{
public class NotificationService
: IHandle<AlbumGrabbedEvent>,
IHandle<TrackImportedEvent>,
IHandle<AlbumImportedEvent>,
IHandle<ArtistRenamedEvent>
IHandle<ArtistRenamedEvent>,
IHandle<HealthCheckFailedEvent>,
IHandle<DownloadFailedEvent>,
IHandle<AlbumImportIncompleteEvent>,
IHandle<TrackFileRetaggedEvent>
{
private readonly INotificationFactory _notificationFactory;
private readonly Logger _logger;
@ -47,24 +51,6 @@ namespace NzbDrone.Core.Notifications
qualityString);
}
private string GetTrackMessage(Artist artist, List<Track> tracks, QualityModel quality)
{
var qualityString = quality.Quality.ToString();
if (quality.Revision.Version > 1)
{
qualityString += " Proper";
}
var trackTitles = string.Join(" + ", tracks.Select(e => e.Title));
return string.Format("{0} - {1} - [{2}]",
artist.Name,
trackTitles,
qualityString);
}
private string GetAlbumDownloadMessage(Artist artist, Album album, List<TrackFile> tracks)
{
return string.Format("{0} - {1} ({2} Tracks Imported)",
@ -73,6 +59,25 @@ namespace NzbDrone.Core.Notifications
tracks.Count);
}
private string GetAlbumIncompleteImportMessage(string source)
{
return string.Format("Lidarr failed to Import all tracks for {0}",
source);
}
private string FormatMissing(object value)
{
var text = value?.ToString();
return text.IsNullOrWhiteSpace() ? "<missing>" : text;
}
private string GetTrackRetagMessage(Artist artist, TrackFile trackFile, Dictionary<string, Tuple<string, string>> diff)
{
return string.Format("{0}:\n{1}",
Path.Combine(artist.Path, trackFile.RelativePath),
string.Join("\n", diff.Select(x => $"{x.Key}: {FormatMissing(x.Value.Item1)} → {FormatMissing(x.Value.Item2)}")));
}
private bool ShouldHandleArtist(ProviderDefinition definition, Artist artist)
{
if (definition.Tags.Empty())
@ -92,6 +97,21 @@ namespace NzbDrone.Core.Notifications
return false;
}
private bool ShouldHandleHealthFailure(HealthCheck.HealthCheck healthCheck, bool includeWarnings)
{
if (healthCheck.Type == HealthCheckResult.Error)
{
return true;
}
if (healthCheck.Type == HealthCheckResult.Warning && includeWarnings)
{
return true;
}
return false;
}
public void Handle(AlbumGrabbedEvent message)
{
var grabMessage = new GrabMessage
@ -119,99 +139,141 @@ namespace NzbDrone.Core.Notifications
}
}
public void Handle(TrackImportedEvent message)
public void Handle(AlbumImportedEvent message)
{
if (!message.NewDownload)
{
return;
}
var downloadMessage = new TrackDownloadMessage
var downloadMessage = new AlbumDownloadMessage
{
Message = GetTrackMessage(message.TrackInfo.Artist, message.TrackInfo.Tracks, message.TrackInfo.Quality),
Artist = message.TrackInfo.Artist,
Album = message.TrackInfo.Album,
Release = message.TrackInfo.Release,
TrackFile = message.ImportedTrack,
OldFiles = message.OldFiles,
SourcePath = message.TrackInfo.Path,
Message = GetAlbumDownloadMessage(message.Artist, message.Album, message.ImportedTracks),
Artist = message.Artist,
Album = message.Album,
Release = message.AlbumRelease,
DownloadClient = message.DownloadClient,
DownloadId = message.DownloadId
DownloadId = message.DownloadId,
TrackFiles = message.ImportedTracks,
OldFiles = message.OldFiles,
};
foreach (var notification in _notificationFactory.OnDownloadEnabled())
foreach (var notification in _notificationFactory.OnReleaseImportEnabled())
{
try
{
if (ShouldHandleArtist(notification.Definition, message.TrackInfo.Artist))
if (ShouldHandleArtist(notification.Definition, message.Artist))
{
if (downloadMessage.OldFiles.Empty() || ((NotificationDefinition)notification.Definition).OnUpgrade)
{
notification.OnDownload(downloadMessage);
notification.OnReleaseImport(downloadMessage);
}
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to send OnDownload notification to: " + notification.Definition.Name);
_logger.Warn(ex, "Unable to send OnReleaseImport notification to: " + notification.Definition.Name);
}
}
}
public void Handle(AlbumImportedEvent message)
public void Handle(ArtistRenamedEvent message)
{
if (!message.NewDownload)
{
return;
}
var downloadMessage = new AlbumDownloadMessage
{
Message = GetAlbumDownloadMessage(message.Artist, message.Album, message.ImportedTracks),
Artist = message.Artist,
Album = message.Album,
Release = message.AlbumRelease,
DownloadClient = message.DownloadClient,
DownloadId = message.DownloadId,
TrackFiles = message.ImportedTracks,
OldFiles = message.OldFiles,
};
foreach (var notification in _notificationFactory.OnAlbumDownloadEnabled())
foreach (var notification in _notificationFactory.OnRenameEnabled())
{
try
{
if (ShouldHandleArtist(notification.Definition, message.Artist))
{
notification.OnAlbumDownload(downloadMessage);
notification.OnRename(message.Artist);
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to send OnDownload notification to: " + notification.Definition.Name);
_logger.Warn(ex, "Unable to send OnRename notification to: " + notification.Definition.Name);
}
}
}
public void Handle(ArtistRenamedEvent message)
public void Handle(HealthCheckFailedEvent message)
{
foreach (var notification in _notificationFactory.OnRenameEnabled())
foreach (var notification in _notificationFactory.OnHealthIssueEnabled())
{
try
{
if (ShouldHandleArtist(notification.Definition, message.Artist))
if (ShouldHandleHealthFailure(message.HealthCheck, ((NotificationDefinition)notification.Definition).IncludeHealthWarnings))
{
notification.OnRename(message.Artist);
notification.OnHealthIssue(message.HealthCheck);
}
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to send OnRename notification to: " + notification.Definition.Name);
_logger.Warn(ex, "Unable to send OnHealthIssue notification to: " + notification.Definition.Name);
}
}
}
public void Handle(DownloadFailedEvent message)
{
var downloadFailedMessage = new DownloadFailedMessage
{
DownloadId = message.DownloadId,
DownloadClient = message.DownloadClient,
Quality = message.Quality,
Language = message.Language,
SourceTitle = message.SourceTitle,
Message = message.Message
};
foreach (var notification in _notificationFactory.OnDownloadFailureEnabled())
{
if (ShouldHandleArtist(notification.Definition, message.TrackedDownload.RemoteAlbum.Artist))
{
notification.OnDownloadFailure(downloadFailedMessage);
}
}
}
public void Handle(AlbumImportIncompleteEvent message)
{
// TODO: Build out this message so that we can pass on what failed and what was successful
var downloadMessage = new AlbumDownloadMessage
{
Message = GetAlbumIncompleteImportMessage(message.TrackedDownload.RemoteAlbum.Release.Title),
};
foreach (var notification in _notificationFactory.OnDownloadFailureEnabled())
{
if (ShouldHandleArtist(notification.Definition, message.TrackedDownload.RemoteAlbum.Artist))
{
notification.OnImportFailure(downloadMessage);
}
}
}
public void Handle(TrackFileRetaggedEvent message)
{
var retagMessage = new TrackRetagMessage
{
Message = GetTrackRetagMessage(message.Artist, message.TrackFile, message.Diff),
Artist = message.Artist,
Album = message.TrackFile.Album,
Release = message.TrackFile.Tracks.Value.First().AlbumRelease.Value,
TrackFile = message.TrackFile,
Diff = message.Diff,
Scrubbed = message.Scrubbed
};
foreach (var notification in _notificationFactory.OnTrackRetagEnabled())
{
if (ShouldHandleArtist(notification.Definition, message.Artist))
{
notification.OnTrackRetag(retagMessage);
}
}
}

@ -21,14 +21,14 @@ namespace NzbDrone.Core.Notifications.Plex.HomeTheater
_plexClientService.Notify(Settings, ALBUM_GRABBED_TITLE_BRANDED, grabMessage.Message);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_plexClientService.Notify(Settings, TRACK_DOWNLOADED_TITLE_BRANDED, message.Message);
_plexClientService.Notify(Settings, ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
_plexClientService.Notify(Settings, ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message);
_plexClientService.Notify(Settings, HEALTH_ISSUE_TITLE_BRANDED, message.Message);
}
public override ValidationResult Test()

@ -26,9 +26,9 @@ namespace NzbDrone.Core.Notifications.Plex.HomeTheater
Notify(ALBUM_GRABBED_TITLE_BRANDED, grabMessage.Message);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
Notify(TRACK_DOWNLOADED_TITLE_BRANDED, message.Message);
Notify(ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message);
}
public override ValidationResult Test()

@ -23,19 +23,19 @@ namespace NzbDrone.Core.Notifications.Plex.Server
public override string Link => "https://www.plex.tv/";
public override string Name => "Plex Media Server";
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
UpdateIfEnabled(message.Artist);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnRename(Artist artist)
{
UpdateIfEnabled(message.Artist);
UpdateIfEnabled(artist);
}
public override void OnRename(Artist artist)
public override void OnTrackRetag(TrackRetagMessage message)
{
UpdateIfEnabled(artist);
UpdateIfEnabled(message.Artist);
}
private void UpdateIfEnabled(Artist artist)

@ -22,14 +22,14 @@ namespace NzbDrone.Core.Notifications.Prowl
_prowlService.SendNotification(ALBUM_GRABBED_TITLE, grabMessage.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_prowlService.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
_prowlService.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck message)
{
_prowlService.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
_prowlService.SendNotification(HEALTH_ISSUE_TITLE, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
}
public override ValidationResult Test()

@ -24,14 +24,24 @@ namespace NzbDrone.Core.Notifications.PushBullet
_proxy.SendNotification(ALBUM_GRABBED_TITLE_BRANDED, grabMessage.Message, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_proxy.SendNotification(DOWNLOAD_FAILURE_TITLE_BRANDED, message.Message, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_proxy.SendNotification(IMPORT_FAILURE_TITLE_BRANDED, message.Message, Settings);
}
public override ValidationResult Test()

@ -21,14 +21,24 @@ namespace NzbDrone.Core.Notifications.Pushover
_proxy.SendNotification(ALBUM_GRABBED_TITLE, grabMessage.Message, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, Settings);
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_proxy.SendNotification(DOWNLOAD_FAILURE_TITLE, message.Message, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_proxy.SendNotification(IMPORT_FAILURE_TITLE, message.Message, Settings);
}
public override ValidationResult Test()

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Notifications.Slack.Payloads;
using NzbDrone.Core.Rest;
using NzbDrone.Core.Music;
using NzbDrone.Core.Validation;
using RestSharp;
namespace NzbDrone.Core.Notifications.Slack
@ -17,12 +13,10 @@ namespace NzbDrone.Core.Notifications.Slack
public class Slack : NotificationBase<SlackSettings>
{
private readonly ISlackProxy _proxy;
private readonly Logger _logger;
public Slack(ISlackProxy proxy, Logger logger)
public Slack(ISlackProxy proxy)
{
_proxy = proxy;
_logger = logger;
}
public override string Name => "Slack";
@ -45,24 +39,7 @@ namespace NzbDrone.Core.Notifications.Slack
_proxy.SendPayload(payload, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
{
var attachments = new List<Attachment>
{
new Attachment
{
Fallback = message.Message,
Title = message.Artist.Name,
Text = message.Message,
Color = "good"
}
};
var payload = CreatePayload($"Imported: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
var attachments = new List<Attachment>
{
@ -94,6 +71,73 @@ namespace NzbDrone.Core.Notifications.Slack
_proxy.SendPayload(payload, Settings);
}
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
var attachments = new List<Attachment>
{
new Attachment
{
Title = healthCheck.Source.Name,
Text = healthCheck.Message,
Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? "warning" : "danger"
}
};
var payload = CreatePayload("Health Issue", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnTrackRetag(TrackRetagMessage message)
{
var attachments = new List<Attachment>
{
new Attachment
{
Title = TRACK_RETAGGED_TITLE,
Text = message.Message
}
};
var payload = CreatePayload(TRACK_RETAGGED_TITLE, attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
var attachments = new List<Attachment>
{
new Attachment
{
Fallback = message.Message,
Title = message.SourceTitle,
Text = message.Message,
Color = "danger"
}
};
var payload = CreatePayload($"Download Failed: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
var attachments = new List<Attachment>
{
new Attachment
{
Fallback = message.Message,
Title = message.Album.Title,
Text = message.Message,
Color = "warning"
}
};
var payload = CreatePayload($"Import Failed: {message.Message}", attachments);
_proxy.SendPayload(payload, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Subsonic
Notify(Settings, header, grabMessage.Message);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
const string header = "Lidarr - Downloaded";
@ -36,17 +36,19 @@ namespace NzbDrone.Core.Notifications.Subsonic
Update();
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnRename(Artist artist)
{
const string header = "Lidarr - Downloaded";
Update();
}
Notify(Settings, header, message.Message);
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
Notify(Settings, HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message);
}
public override void OnRename(Artist artist)
public override void OnTrackRetag(TrackRetagMessage message)
{
Update();
Notify(Settings, TRACK_RETAGGED_TITLE_BRANDED, message.Message);
}
public override string Name => "Subsonic";

@ -20,8 +20,7 @@ namespace NzbDrone.Core.Notifications.Synology
public override string Link => "https://www.synology.com";
public override string Name => "Synology Indexer";
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
if (Settings.UpdateLibrary)
{
@ -49,6 +48,13 @@ namespace NzbDrone.Core.Notifications.Synology
}
}
public override void OnTrackRetag(TrackRetagMessage message)
{
if (Settings.UpdateLibrary)
{
_indexerProxy.UpdateFolder(message.Artist.Path);
}
}
public override ValidationResult Test()
{

@ -21,14 +21,24 @@ namespace NzbDrone.Core.Notifications.Telegram
_proxy.SendNotification(ALBUM_GRABBED_TITLE, grabMessage.Message, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, Settings);
_proxy.SendNotification(ALBUM_DOWNLOADED_TITLE, message.Message, Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_proxy.SendNotification(TRACK_DOWNLOADED_TITLE, message.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE, healthCheck.Message, Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_proxy.SendNotification(DOWNLOAD_FAILURE_TITLE, message.Message, Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_proxy.SendNotification(IMPORT_FAILURE_TITLE, message.Message, Settings);
}
public override ValidationResult Test()

@ -1,20 +1,19 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music;
namespace NzbDrone.Core.Notifications
{
public class TrackDownloadMessage
public class TrackRetagMessage
{
public string Message { get; set; }
public Artist Artist { get; set; }
public Album Album { get; set; }
public AlbumRelease Release { get; set; }
public TrackFile TrackFile { get; set; }
public List<TrackFile> OldFiles { get; set; }
public string SourcePath { get; set; }
public string DownloadClient { get; set; }
public string DownloadId { get; set; }
public Dictionary<string, Tuple<string, string>> Diff { get; set; }
public bool Scrubbed { get; set; }
public override string ToString()
{

@ -24,14 +24,24 @@ namespace NzbDrone.Core.Notifications.Twitter
_twitterService.SendNotification($"Grabbed: {message.Message}", Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
_twitterService.SendNotification($"Imported: {message.Message}", Settings);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_twitterService.SendNotification($"Imported: {message.Message}", Settings);
_twitterService.SendNotification($"Health Issue: {healthCheck.Message}", Settings);
}
public override void OnDownloadFailure(DownloadFailedMessage message)
{
_twitterService.SendNotification($"Download Failed: {message.Message}", Settings);
}
public override void OnImportFailure(AlbumDownloadMessage message)
{
_twitterService.SendNotification($"Import Failed: {message.Message}", Settings);
}
public override object RequestAction(string action, IDictionary<string, string> query)

@ -41,23 +41,23 @@ namespace NzbDrone.Core.Notifications.Webhook
_proxy.SendWebhook(payload, Settings);
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
var trackFile = message.TrackFile;
var trackFiles = message.TrackFiles;
var payload = new WebhookImportPayload
{
EventType = "Download",
Artist = new WebhookArtist(message.Artist),
Tracks = trackFile.Tracks.Value.ConvertAll(x => new WebhookTrack(x)
Tracks = trackFiles.SelectMany(x => x.Tracks.Value.Select(y => new WebhookTrack(y)
{
// TODO: Stop passing these parameters inside an episode v3
Quality = trackFile.Quality.Quality.Name,
QualityVersion = trackFile.Quality.Revision.Version,
ReleaseGroup = trackFile.ReleaseGroup
}),
TrackFile = new WebhookTrackFile(trackFile),
Quality = x.Quality.Quality.Name,
QualityVersion = x.Quality.Revision.Version,
ReleaseGroup = x.ReleaseGroup
})).ToList(),
TrackFiles = trackFiles.ConvertAll(x => new WebhookTrackFile(x)),
IsUpgrade = message.OldFiles.Any()
};
@ -75,6 +75,17 @@ namespace NzbDrone.Core.Notifications.Webhook
_proxy.SendWebhook(payload, Settings);
}
public override void OnTrackRetag(TrackRetagMessage message)
{
var payload = new WebhookPayload
{
EventType = "Retag",
Artist = new WebhookArtist(message.Artist)
};
_proxy.SendWebhook(payload, Settings);
}
public override string Name => "Webhook";
public override ValidationResult Test()

@ -5,7 +5,7 @@ namespace NzbDrone.Core.Notifications.Webhook
public class WebhookImportPayload : WebhookPayload
{
public List<WebhookTrack> Tracks { get; set; }
public WebhookTrackFile TrackFile { get; set; }
public List<WebhookTrackFile> TrackFiles { get; set; }
public bool IsUpgrade { get; set; }
}
}

@ -28,24 +28,27 @@ namespace NzbDrone.Core.Notifications.Xbmc
Notify(Settings, header, grabMessage.Message);
}
public override void OnAlbumDownload(AlbumDownloadMessage message)
public override void OnReleaseImport(AlbumDownloadMessage message)
{
const string header = "Lidarr - Downloaded";
Notify(Settings, header, message.Message);
UpdateAndClean(message.Artist, message.OldFiles.Any());
}
public override void OnDownload(TrackDownloadMessage message)
public override void OnRename(Artist artist)
{
const string header = "Lidarr - Downloaded";
UpdateAndClean(artist);
}
Notify(Settings, header, message.Message);
UpdateAndClean(message.Artist, message.OldFiles.Any());
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
Notify(Settings, HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message);
}
public override void OnRename(Artist artist)
public override void OnTrackRetag(TrackRetagMessage message)
{
UpdateAndClean(artist);
UpdateAndClean(message.Artist);
}
public override string Name => "Kodi (XBMC)";
@ -54,7 +57,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_xbmcService.Test(Settings, "Success! XBMC has been successfully configured!"));
failures.AddIfNotNull(_xbmcService.Test(Settings, "Success! Kodi has been successfully configured!"));
return new ValidationResult(failures);
}
@ -70,7 +73,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
}
catch (SocketException ex)
{
var logMessage = string.Format("Unable to connect to XBMC Host: {0}:{1}", Settings.Host, Settings.Port);
var logMessage = string.Format("Unable to connect to Kodi Host: {0}:{1}", Settings.Host, Settings.Port);
_logger.Debug(ex, logMessage);
}
}
@ -91,7 +94,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
}
catch (SocketException ex)
{
var logMessage = string.Format("Unable to connect to XBMC Host: {0}:{1}", Settings.Host, Settings.Port);
var logMessage = string.Format("Unable to connect to Kodi Host: {0}:{1}", Settings.Host, Settings.Port);
_logger.Debug(ex, logMessage);
}
}

@ -183,6 +183,7 @@
<Compile Include="Datastore\Migration\003_add_medium_support.cs" />
<Compile Include="Datastore\Migration\006_separate_automatic_and_interactive_search.cs" />
<Compile Include="Datastore\Migration\007_change_album_path_to_relative.cs" />
<Compile Include="Datastore\Migration\029_health_issue_notification.cs" />
<Compile Include="Datastore\Migration\014_fix_language_metadata_profiles.cs" />
<Compile Include="Datastore\Migration\013_album_download_notification.cs" />
<Compile Include="Datastore\Migration\009_album_releases.cs" />
@ -508,6 +509,7 @@
<Compile Include="HealthCheck\EventDrivenHealthCheck.cs" />
<Compile Include="HealthCheck\HealthCheck.cs" />
<Compile Include="HealthCheck\HealthCheckBase.cs" />
<Compile Include="HealthCheck\HealthCheckFailedEvent.cs" />
<Compile Include="HealthCheck\HealthCheckCompleteEvent.cs" />
<Compile Include="HealthCheck\HealthCheckService.cs" />
<Compile Include="HealthCheck\IProvideHealthCheck.cs" />
@ -711,6 +713,13 @@
<Compile Include="Languages\Language.cs" />
<Compile Include="Languages\LanguageComparer.cs" />
<Compile Include="Languages\LanguagesBelowCutoff.cs" />
<Compile Include="Notifications\Discord\Discord.cs" />
<Compile Include="Notifications\Discord\DiscordColors.cs" />
<Compile Include="Notifications\Discord\DiscordException.cs" />
<Compile Include="Notifications\Discord\DiscordProxy.cs" />
<Compile Include="Notifications\Discord\DiscordSettings.cs" />
<Compile Include="Notifications\Discord\Payloads\Embed.cs" />
<Compile Include="Notifications\Discord\Payloads\DiscordPayload.cs" />
<Compile Include="Lifecycle\ApplicationShutdownRequested.cs" />
<Compile Include="Lifecycle\ApplicationStartedEvent.cs" />
<Compile Include="Lifecycle\Commands\RestartCommand.cs" />
@ -922,6 +931,7 @@
<Compile Include="Music\TrackRepository.cs" />
<Compile Include="Music\TrackService.cs" />
<Compile Include="Notifications\AlbumDownloadMessage.cs" />
<Compile Include="Notifications\DownloadFailedMessage.cs" />
<Compile Include="Notifications\Join\JoinAuthException.cs" />
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
<Compile Include="Notifications\Join\JoinPriority.cs" />
@ -1050,7 +1060,7 @@
<Compile Include="RemotePathMappings\RemotePathMappingRepository.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingService.cs" />
<Compile Include="MediaFiles\TorrentInfo\TorrentFileInfoReader.cs" />
<Compile Include="Notifications\TrackDownloadMessage.cs" />
<Compile Include="Notifications\TrackRetagMessage.cs" />
<Compile Include="Notifications\Email\Email.cs">
<SubType>Code</SubType>
</Compile>

Loading…
Cancel
Save