From f4130d96e570c94cf8f923852cb1a70b526a53d7 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 2 Apr 2023 13:26:49 -0700 Subject: [PATCH] New: Add release info to webhook/custom script import events Closes #5503 --- .../MatchesGrabSpecificationFixture.cs | 28 ++++------ .../Aggregators/AggregateReleaseInfo.cs | 52 +++++++++++++++++++ .../MatchesGrabSpecification.cs | 25 +++------ .../MediaFiles/Events/EpisodeImportedEvent.cs | 2 +- .../CustomScript/CustomScript.cs | 3 ++ .../Notifications/DownloadMessage.cs | 1 + .../Notifications/NotificationService.cs | 3 +- .../Notifications/Webhook/WebhookBase.cs | 3 +- .../Webhook/WebhookGrabbedRelease.cs | 27 ++++++++++ .../Webhook/WebhookImportPayload.cs | 1 + .../Parser/Model/GrabbedReleaseInfo.cs | 13 +++++ .../Parser/Model/LocalEpisode.cs | 1 + 12 files changed, 118 insertions(+), 41 deletions(-) create mode 100644 src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseInfo.cs create mode 100644 src/NzbDrone.Core/Notifications/Webhook/WebhookGrabbedRelease.cs create mode 100644 src/NzbDrone.Core/Parser/Model/GrabbedReleaseInfo.cs diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecificationFixture.cs index 6dae08e61..a874b8fe2 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecificationFixture.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Core.Download; -using NzbDrone.Core.History; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -40,6 +40,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications _localEpisode = Builder.CreateNew() .With(l => l.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic()) .With(l => l.Episodes = new List { _episode1 }) + .With(l => l.Release = null) .Build(); _downloadClientItem = Builder.CreateNew().Build(); @@ -47,19 +48,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications private void GivenHistoryForEpisodes(params Episode[] episodes) { - var history = new List(); - - foreach (var episode in episodes) - { - history.Add(Builder.CreateNew() - .With(h => h.EventType = EpisodeHistoryEventType.Grabbed) - .With(h => h.EpisodeId = episode.Id) - .Build()); - } - - Mocker.GetMock() - .Setup(s => s.FindByDownloadId(It.IsAny())) - .Returns(history); + _localEpisode.Release = new GrabbedReleaseInfo(); + _localEpisode.Release.EpisodeIds = episodes.Select(e => e.Id).ToList(); } [Test] @@ -77,7 +67,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_be_accepted_if_no_grab_history() + public void should_be_accepted_if_no_grabbed_release_info() { GivenHistoryForEpisodes(); @@ -85,7 +75,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_be_accepted_if_file_episode_matches_single_grab_history() + public void should_be_accepted_if_file_episode_matches_single_grabbed_release_info() { GivenHistoryForEpisodes(_episode1); @@ -93,7 +83,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_be_accepted_if_file_episode_is_in_multi_episode_grab_history() + public void should_be_accepted_if_file_episode_is_in_multi_episode_grabbed_release_info() { GivenHistoryForEpisodes(_episode1, _episode2); @@ -101,7 +91,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_be_rejected_if_file_episode_does_not_match_single_grab_history() + public void should_be_rejected_if_file_episode_does_not_match_single_grabbed_release_info() { GivenHistoryForEpisodes(_episode2); @@ -109,7 +99,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications } [Test] - public void should_be_rejected_if_file_episode_is_not_in_multi_episode_grab_history() + public void should_be_rejected_if_file_episode_is_not_in_multi_episode_grabbed_release_info() { GivenHistoryForEpisodes(_episode2, _episode3); diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseInfo.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseInfo.cs new file mode 100644 index 000000000..b35f3ea14 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Aggregation/Aggregators/AggregateReleaseInfo.cs @@ -0,0 +1,52 @@ +using System.Linq; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Download; +using NzbDrone.Core.History; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation.Aggregators +{ + public class AggregateReleaseInfo : IAggregateLocalEpisode + { + private readonly IHistoryService _historyService; + + public AggregateReleaseInfo(IHistoryService historyService) + { + _historyService = historyService; + } + + public LocalEpisode Aggregate(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) + { + if (downloadClientItem == null) + { + return localEpisode; + } + + var grabbedHistories = _historyService.FindByDownloadId(downloadClientItem.DownloadId) + .Where(h => h.EventType == EpisodeHistoryEventType.Grabbed) + .ToList(); + + if (grabbedHistories.Empty()) + { + return localEpisode; + } + + var episodeIds = grabbedHistories.Select(h => h.EpisodeId).Distinct().ToList(); + var grabbedHistory = grabbedHistories.First(); + var releaseInfo = new GrabbedReleaseInfo(); + + grabbedHistory.Data.TryGetValue("indexer", out var indexer); + grabbedHistory.Data.TryGetValue("size", out var sizeString); + long.TryParse(sizeString, out var size); + + releaseInfo.Title = grabbedHistory.SourceTitle; + releaseInfo.Indexer = indexer; + releaseInfo.Size = size; + releaseInfo.EpisodeIds = episodeIds; + + localEpisode.Release = releaseInfo; + + return localEpisode; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs index 532103f17..a37762745 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/MatchesGrabSpecification.cs @@ -4,8 +4,6 @@ using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; -using NzbDrone.Core.History; -using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -14,14 +12,10 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications public class MatchesGrabSpecification : IImportDecisionEngineSpecification { private readonly Logger _logger; - private readonly IParsingService _parsingService; - private readonly IHistoryService _historyService; - public MatchesGrabSpecification(IParsingService parsingService, IHistoryService historyService, Logger logger) + public MatchesGrabSpecification(Logger logger) { _logger = logger; - _parsingService = parsingService; - _historyService = historyService; } public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem) @@ -31,21 +25,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications return Decision.Accept(); } - if (downloadClientItem == null) - { - return Decision.Accept(); - } - - var grabbedHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId) - .Where(h => h.EventType == EpisodeHistoryEventType.Grabbed) - .ToList(); + var releaseInfo = localEpisode.Release; - if (grabbedHistory.Empty()) + if (releaseInfo == null || releaseInfo.EpisodeIds.Empty()) { return Decision.Accept(); } - var unexpected = localEpisode.Episodes.Where(e => grabbedHistory.All(o => o.EpisodeId != e.Id)).ToList(); + var unexpected = localEpisode.Episodes.Where(e => releaseInfo.EpisodeIds.All(o => o != e.Id)).ToList(); if (unexpected.Any()) { @@ -53,10 +40,10 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications if (unexpected.Count == 1) { - return Decision.Reject("Episode {0} was not found in the grabbed release: {1}", FormatEpisode(unexpected), grabbedHistory.First().SourceTitle); + return Decision.Reject("Episode {0} was not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); } - return Decision.Reject("Episodes {0} were not found in the grabbed release: {1}", FormatEpisode(unexpected), grabbedHistory.First().SourceTitle); + return Decision.Reject("Episodes {0} were not found in the grabbed release: {1}", FormatEpisode(unexpected), releaseInfo.Title); } return Decision.Accept(); diff --git a/src/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs b/src/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs index c4de50f9a..35a1fc800 100644 --- a/src/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs +++ b/src/NzbDrone.Core/MediaFiles/Events/EpisodeImportedEvent.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Common.Messaging; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; diff --git a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs index d97e663c2..c963bcfd0 100644 --- a/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs +++ b/src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs @@ -138,6 +138,9 @@ namespace NzbDrone.Core.Notifications.CustomScript environmentVariables.Add("Sonarr_EpisodeFile_MediaInfo_VideoDynamicRangeType", MediaInfoFormatter.FormatVideoDynamicRangeType(episodeFile.MediaInfo)); environmentVariables.Add("Sonarr_EpisodeFile_CustomFormat", string.Join("|", message.EpisodeInfo.CustomFormats)); environmentVariables.Add("Sonarr_EpisodeFile_CustomFormatScore", message.EpisodeInfo.CustomFormatScore.ToString()); + environmentVariables.Add("Sonarr_Release_Indexer", message.Release?.Indexer); + environmentVariables.Add("Sonarr_Release_Size", message.Release?.Size.ToString()); + environmentVariables.Add("Sonarr_Release_Title", message.Release?.Title); if (message.OldFiles.Any()) { diff --git a/src/NzbDrone.Core/Notifications/DownloadMessage.cs b/src/NzbDrone.Core/Notifications/DownloadMessage.cs index 2e94ecf64..62f8143d0 100644 --- a/src/NzbDrone.Core/Notifications/DownloadMessage.cs +++ b/src/NzbDrone.Core/Notifications/DownloadMessage.cs @@ -16,6 +16,7 @@ namespace NzbDrone.Core.Notifications public string SourcePath { get; set; } public DownloadClientItemClientInfo DownloadClientInfo { get; set; } public string DownloadId { get; set; } + public GrabbedReleaseInfo Release { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/Notifications/NotificationService.cs b/src/NzbDrone.Core/Notifications/NotificationService.cs index 3408f97c7..cb6797945 100644 --- a/src/NzbDrone.Core/Notifications/NotificationService.cs +++ b/src/NzbDrone.Core/Notifications/NotificationService.cs @@ -157,7 +157,8 @@ namespace NzbDrone.Core.Notifications OldFiles = message.OldFiles, SourcePath = message.EpisodeInfo.Path, DownloadClientInfo = message.DownloadClientInfo, - DownloadId = message.DownloadId + DownloadId = message.DownloadId, + Release = message.EpisodeInfo.Release }; foreach (var notification in _notificationFactory.OnDownloadEnabled()) diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs index d50c6e04c..e58306d45 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookBase.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Notifications.Webhook DownloadClient = message.DownloadClientName, DownloadClientType = message.DownloadClientType, DownloadId = message.DownloadId, - CustomFormatInfo = new WebhookCustomFormatInfo(remoteEpisode.CustomFormats, remoteEpisode.CustomFormatScore) + CustomFormatInfo = new WebhookCustomFormatInfo(remoteEpisode.CustomFormats, remoteEpisode.CustomFormatScore), }; } @@ -52,6 +52,7 @@ namespace NzbDrone.Core.Notifications.Webhook Series = new WebhookSeries(message.Series), Episodes = episodeFile.Episodes.Value.ConvertAll(x => new WebhookEpisode(x)), EpisodeFile = new WebhookEpisodeFile(episodeFile), + Release = new WebhookGrabbedRelease(message.Release), IsUpgrade = message.OldFiles.Any(), DownloadClient = message.DownloadClientInfo?.Name, DownloadClientType = message.DownloadClientInfo?.Type, diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabbedRelease.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabbedRelease.cs new file mode 100644 index 000000000..bcce1af60 --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookGrabbedRelease.cs @@ -0,0 +1,27 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Notifications.Webhook +{ + public class WebhookGrabbedRelease + { + public WebhookGrabbedRelease() + { + } + + public WebhookGrabbedRelease(GrabbedReleaseInfo release) + { + if (release == null) + { + return; + } + + ReleaseTitle = release.Title; + Indexer = release.Indexer; + Size = release.Size; + } + + public string ReleaseTitle { get; set; } + public string Indexer { get; set; } + public long Size { get; set; } + } +} diff --git a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs index e7720d849..037ce85a8 100644 --- a/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs +++ b/src/NzbDrone.Core/Notifications/Webhook/WebhookImportPayload.cs @@ -13,5 +13,6 @@ namespace NzbDrone.Core.Notifications.Webhook public string DownloadId { get; set; } public List DeletedFiles { get; set; } public WebhookCustomFormatInfo CustomFormatInfo { get; set; } + public WebhookGrabbedRelease Release { get; set; } } } diff --git a/src/NzbDrone.Core/Parser/Model/GrabbedReleaseInfo.cs b/src/NzbDrone.Core/Parser/Model/GrabbedReleaseInfo.cs new file mode 100644 index 000000000..869f8cdab --- /dev/null +++ b/src/NzbDrone.Core/Parser/Model/GrabbedReleaseInfo.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace NzbDrone.Core.Parser.Model +{ + public class GrabbedReleaseInfo + { + public string Title { get; set; } + public string Indexer { get; set; } + public long Size { get; set; } + + public List EpisodeIds { get; set; } + } +} diff --git a/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs b/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs index d9fa0fcd7..77169efaa 100644 --- a/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs +++ b/src/NzbDrone.Core/Parser/Model/LocalEpisode.cs @@ -35,6 +35,7 @@ namespace NzbDrone.Core.Parser.Model public bool OtherVideoFiles { get; set; } public List CustomFormats { get; set; } public int CustomFormatScore { get; set; } + public GrabbedReleaseInfo Release { get; set; } public int SeasonNumber {