1
0
mirror of https://github.com/Radarr/Radarr synced 2025-10-06 02:13:00 +02:00

New:(Pushcut) Improved Notification Details (#10897)

This commit is contained in:
Denis Gheorghescu
2025-07-19 17:43:18 +03:00
committed by Robin Dadswell
parent 42512cbcae
commit 1f620eab22
7 changed files with 114 additions and 14 deletions

View File

@@ -1328,6 +1328,10 @@
"NotificationsPushBulletSettingsDeviceIds": "Device IDs",
"NotificationsPushBulletSettingsDeviceIdsHelpText": "List of device IDs (leave blank to send to all devices)",
"NotificationsPushcutSettingsApiKeyHelpText": "API Keys can be managed in the Account view of the Pushcut app",
"NotificationsPushcutSettingsIncludePoster": "Include Poster",
"NotificationsPushcutSettingsIncludePosterHelpText": "Include poster with notification",
"NotificationsPushcutSettingsMetadataLinks": "Metadata Links",
"NotificationsPushcutSettingsMetadataLinksHelpText": "Add a links to series metadata when sending notifications",
"NotificationsPushcutSettingsNotificationName": "Notification Name",
"NotificationsPushcutSettingsNotificationNameHelpText": "Notification name from Notifications tab of the Pushcut app",
"NotificationsPushcutSettingsTimeSensitive": "Time Sensitive",

View File

@@ -0,0 +1,16 @@
namespace NzbDrone.Core.Notifications
{
public class NotificationMetadataLink
{
public MetadataLinkType? Type { get; set; }
public string Label { get; set; }
public string Link { get; set; }
public NotificationMetadataLink(MetadataLinkType? type, string label, string link)
{
Type = type;
Label = label;
Link = link;
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Notifications;
public static class NotificationMetadataLinkGenerator
{
public static List<NotificationMetadataLink> GenerateLinks(Movie movie, IEnumerable<int> metadataLinks)
{
var links = new List<NotificationMetadataLink>();
if (movie == null)
{
return links;
}
foreach (var type in metadataLinks)
{
var linkType = (MetadataLinkType)type;
if (linkType == MetadataLinkType.Imdb && movie.ImdbId.IsNotNullOrWhiteSpace())
{
links.Add(new NotificationMetadataLink(MetadataLinkType.Imdb, "IMDb", $"https://www.imdb.com/title/{movie.ImdbId}"));
}
if (linkType == MetadataLinkType.Tmdb && movie.TmdbId > 0)
{
links.Add(new NotificationMetadataLink(MetadataLinkType.Tmdb, "TMDb", $"https://www.themoviedb.org/movie/{movie.TmdbId}"));
}
}
return links;
}
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Movies;
namespace NzbDrone.Core.Notifications.Pushcut
@@ -30,47 +31,57 @@ namespace NzbDrone.Core.Notifications.Pushcut
public override void OnGrab(GrabMessage grabMessage)
{
_proxy.SendNotification(MOVIE_GRABBED_TITLE, grabMessage?.Message, Settings);
_proxy.SendNotification(MOVIE_GRABBED_TITLE, grabMessage?.Message, GetPosterUrl(grabMessage.Movie), GetLinks(grabMessage.Movie), Settings);
}
public override void OnDownload(DownloadMessage downloadMessage)
{
_proxy.SendNotification(downloadMessage.OldMovieFiles.Any() ? MOVIE_UPGRADED_TITLE : MOVIE_DOWNLOADED_TITLE, downloadMessage.Message, Settings);
_proxy.SendNotification(downloadMessage.OldMovieFiles.Any() ? MOVIE_UPGRADED_TITLE : MOVIE_DOWNLOADED_TITLE, downloadMessage.Message, GetPosterUrl(downloadMessage.Movie), GetLinks(downloadMessage.Movie), Settings);
}
public override void OnMovieAdded(Movie movie)
{
_proxy.SendNotification(MOVIE_ADDED_TITLE, $"{movie.Title} added to library", Settings);
_proxy.SendNotification(MOVIE_ADDED_TITLE, $"{movie.Title} added to library", GetPosterUrl(movie), GetLinks(movie), Settings);
}
public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
{
_proxy.SendNotification(MOVIE_FILE_DELETED_TITLE, deleteMessage.Message, Settings);
_proxy.SendNotification(MOVIE_FILE_DELETED_TITLE, deleteMessage.Message, GetPosterUrl(deleteMessage.Movie), GetLinks(deleteMessage.Movie), Settings);
}
public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
{
_proxy.SendNotification(MOVIE_DELETED_TITLE, deleteMessage.Message, Settings);
_proxy.SendNotification(MOVIE_DELETED_TITLE, deleteMessage.Message, GetPosterUrl(deleteMessage.Movie), GetLinks(deleteMessage.Movie), Settings);
}
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, null, new List<NotificationMetadataLink>(), Settings);
}
public override void OnHealthRestored(HealthCheck.HealthCheck previousCheck)
{
_proxy.SendNotification(HEALTH_RESTORED_TITLE_BRANDED, $"The following issue is now resolved: {previousCheck.Message}", Settings);
_proxy.SendNotification(HEALTH_RESTORED_TITLE_BRANDED, $"The following issue is now resolved: {previousCheck.Message}", null, new List<NotificationMetadataLink>(), Settings);
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, null, new List<NotificationMetadataLink>(), Settings);
}
public override void OnManualInteractionRequired(ManualInteractionRequiredMessage manualInteractionRequiredMessage)
{
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, manualInteractionRequiredMessage.Message, Settings);
_proxy.SendNotification(MANUAL_INTERACTION_REQUIRED_TITLE_BRANDED, manualInteractionRequiredMessage.Message, null, new List<NotificationMetadataLink>(), Settings);
}
private string GetPosterUrl(Movie movie)
{
return movie.MovieMetadata.Value.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Poster)?.RemoteUrl;
}
private List<NotificationMetadataLink> GetLinks(Movie movie)
{
return NotificationMetadataLinkGenerator.GenerateLinks(movie, Settings.MetadataLinks);
}
}
}

View File

@@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace NzbDrone.Core.Notifications.Pushcut
{
public class PushcutPayload
@@ -5,5 +7,13 @@ namespace NzbDrone.Core.Notifications.Pushcut
public string Title { get; set; }
public string Text { get; set; }
public bool? IsTimeSensitive { get; set; }
public string Image { get; set; }
public List<PushcutAction> Actions;
}
public class PushcutAction
{
public string Name { get; set; }
public string Url { get; set; }
}
}

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Notifications.Pushcut
{
public interface IPushcutProxy
{
void SendNotification(string title, string message, PushcutSettings settings);
void SendNotification(string title, string message, string posterUrl, List<NotificationMetadataLink> links, PushcutSettings settings);
ValidationFailure Test(PushcutSettings settings);
}
@@ -29,20 +29,37 @@ namespace NzbDrone.Core.Notifications.Pushcut
_logger = logger;
}
public void SendNotification(string title, string message, PushcutSettings settings)
public void SendNotification(string title, string message, string posterUrl, List<NotificationMetadataLink> links, PushcutSettings settings)
{
if (settings == null)
{
return;
}
var request = new HttpRequestBuilder("https://api.pushcut.io/v1/notifications/{notificationName}")
.SetSegment("notificationName", settings?.NotificationName)
.SetHeader("API-Key", settings?.ApiKey)
.Accept(HttpAccept.Json)
.Build();
var payload = new PushcutPayload
{
Title = title,
Text = message,
IsTimeSensitive = settings?.TimeSensitive
IsTimeSensitive = settings?.TimeSensitive,
Image = settings.IncludePoster ? posterUrl : null,
Actions = new List<PushcutAction>()
};
foreach (var link in links)
{
payload.Actions.Add(new PushcutAction
{
Name = link.Label,
Url = link.Link
});
}
request.Method = HttpMethod.Post;
request.Headers.ContentType = "application/json";
request.SetContent(payload.ToJson());
@@ -64,7 +81,7 @@ namespace NzbDrone.Core.Notifications.Pushcut
{
const string title = "Radarr Test Title";
const string message = "Success! You have properly configured your Pushcut notification settings.";
SendNotification(title, message, settings);
SendNotification(title, message, null, new List<NotificationMetadataLink>(), settings);
}
catch (PushcutException pushcutException) when (pushcutException.InnerException is HttpException httpException)
{

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
@@ -15,7 +16,7 @@ namespace NzbDrone.Core.Notifications.Pushcut
public class PushcutSettings : NotificationSettingsBase<PushcutSettings>
{
private static readonly PushcutSettingsValidator Validator = new ();
private static readonly PushcutSettingsValidator Validator = new PushcutSettingsValidator();
[FieldDefinition(0, Label = "NotificationsPushcutSettingsNotificationName", Type = FieldType.Textbox, HelpText = "NotificationsPushcutSettingsNotificationNameHelpText")]
public string NotificationName { get; set; }
@@ -26,6 +27,12 @@ namespace NzbDrone.Core.Notifications.Pushcut
[FieldDefinition(2, Label = "NotificationsPushcutSettingsTimeSensitive", Type = FieldType.Checkbox, HelpText = "NotificationsPushcutSettingsTimeSensitiveHelpText")]
public bool TimeSensitive { get; set; }
[FieldDefinition(3, Label = "NotificationsPushcutSettingsIncludePoster", Type = FieldType.Checkbox, HelpText = "NotificationsPushcutSettingsIncludePosterHelpText")]
public bool IncludePoster { get; set; }
[FieldDefinition(4, Label = "NotificationsPushcutSettingsMetadataLinks", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsPushcutSettingsMetadataLinksHelpText")]
public IEnumerable<int> MetadataLinks { get; set; } = new List<int>();
public override NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));