From d7025a98deb321397db9a4933cf688b68eafeba9 Mon Sep 17 00:00:00 2001 From: PearsonFlyer Date: Thu, 13 Jul 2023 14:57:57 -0400 Subject: [PATCH] New: Ability to skip redownload when marking an item as failed from Activity Queue --- frontend/src/Activity/Queue/QueueRow.js | 4 +-- .../Activity/Queue/RemoveQueueItemModal.js | 27 ++++++++++++++++--- .../Activity/Queue/RemoveQueueItemsModal.js | 26 +++++++++++++++--- frontend/src/Store/Actions/queueActions.js | 10 ++++--- .../Download/DownloadFailedEvent.cs | 1 + .../Download/FailedDownloadService.cs | 17 ++++++------ .../RedownloadFailedDownloadService.cs | 8 +++++- src/NzbDrone.Core/Localization/Core/en.json | 2 ++ src/Sonarr.Api.V3/Queue/QueueController.cs | 12 ++++----- 9 files changed, 80 insertions(+), 27 deletions(-) diff --git a/frontend/src/Activity/Queue/QueueRow.js b/frontend/src/Activity/Queue/QueueRow.js index 708f960bb..0f66fbfa4 100644 --- a/frontend/src/Activity/Queue/QueueRow.js +++ b/frontend/src/Activity/Queue/QueueRow.js @@ -45,14 +45,14 @@ class QueueRow extends Component { this.setState({ isRemoveQueueItemModalOpen: true }); }; - onRemoveQueueItemModalConfirmed = (blocklist) => { + onRemoveQueueItemModalConfirmed = (blocklist, skipRedownload) => { const { onRemoveQueueItemPress, onQueueRowModalOpenOrClose } = this.props; onQueueRowModalOpenOrClose(false); - onRemoveQueueItemPress(blocklist); + onRemoveQueueItemPress(blocklist, skipRedownload); this.setState({ isRemoveQueueItemModalOpen: false }); }; diff --git a/frontend/src/Activity/Queue/RemoveQueueItemModal.js b/frontend/src/Activity/Queue/RemoveQueueItemModal.js index f1a152734..8260d4362 100644 --- a/frontend/src/Activity/Queue/RemoveQueueItemModal.js +++ b/frontend/src/Activity/Queue/RemoveQueueItemModal.js @@ -10,6 +10,7 @@ import ModalContent from 'Components/Modal/ModalContent'; import ModalFooter from 'Components/Modal/ModalFooter'; import ModalHeader from 'Components/Modal/ModalHeader'; import { inputTypes, kinds, sizes } from 'Helpers/Props'; +import translate from 'Utilities/String/translate'; class RemoveQueueItemModal extends Component { @@ -21,7 +22,8 @@ class RemoveQueueItemModal extends Component { this.state = { remove: true, - blocklist: false + blocklist: false, + skipRedownload: false }; } @@ -31,7 +33,8 @@ class RemoveQueueItemModal extends Component { resetState = function() { this.setState({ remove: true, - blocklist: false + blocklist: false, + skipRedownload: false }); }; @@ -46,6 +49,10 @@ class RemoveQueueItemModal extends Component { this.setState({ blocklist: value }); }; + onSkipRedownloadChange = ({ value }) => { + this.setState({ skipRedownload: value }); + }; + onRemoveConfirmed = () => { const state = this.state; @@ -69,7 +76,7 @@ class RemoveQueueItemModal extends Component { isPending } = this.props; - const { remove, blocklist } = this.state; + const { remove, blocklist, skipRedownload } = this.state; return ( + { + blocklist ? + + {translate('SkipRedownload')} + + : + null + } diff --git a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js index 738559ed5..9ee6fef87 100644 --- a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js +++ b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js @@ -23,7 +23,8 @@ class RemoveQueueItemsModal extends Component { this.state = { remove: true, - blocklist: false + blocklist: false, + skipRedownload: false }; } @@ -33,7 +34,8 @@ class RemoveQueueItemsModal extends Component { resetState = function() { this.setState({ remove: true, - blocklist: false + blocklist: false, + skipRedownload: false }); }; @@ -48,6 +50,10 @@ class RemoveQueueItemsModal extends Component { this.setState({ blocklist: value }); }; + onSkipRedownloadChange = ({ value }) => { + this.setState({ skipRedownload: value }); + }; + onRemoveConfirmed = () => { const state = this.state; @@ -71,7 +77,7 @@ class RemoveQueueItemsModal extends Component { allPending } = this.props; - const { remove, blocklist } = this.state; + const { remove, blocklist, skipRedownload } = this.state; return ( + { + blocklist ? + + {translate('SkipRedownload')} + + : + null + } diff --git a/frontend/src/Store/Actions/queueActions.js b/frontend/src/Store/Actions/queueActions.js index ef0466466..e4fbb3d90 100644 --- a/frontend/src/Store/Actions/queueActions.js +++ b/frontend/src/Store/Actions/queueActions.js @@ -381,13 +381,14 @@ export const actionHandlers = handleThunks({ const { id, remove, - blocklist + blocklist, + skipRedownload } = payload; dispatch(updateItem({ section: paged, id, isRemoving: true })); const promise = createAjaxRequest({ - url: `/queue/${id}?removeFromClient=${remove}&blocklist=${blocklist}`, + url: `/queue/${id}?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`, method: 'DELETE' }).request; @@ -404,7 +405,8 @@ export const actionHandlers = handleThunks({ const { ids, remove, - blocklist + blocklist, + skipRedownload } = payload; dispatch(batchActions([ @@ -420,7 +422,7 @@ export const actionHandlers = handleThunks({ ])); const promise = createAjaxRequest({ - url: `/queue/bulk?removeFromClient=${remove}&blocklist=${blocklist}`, + url: `/queue/bulk?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`, method: 'DELETE', dataType: 'json', contentType: 'application/json', diff --git a/src/NzbDrone.Core/Download/DownloadFailedEvent.cs b/src/NzbDrone.Core/Download/DownloadFailedEvent.cs index 143eba14e..757e5da97 100644 --- a/src/NzbDrone.Core/Download/DownloadFailedEvent.cs +++ b/src/NzbDrone.Core/Download/DownloadFailedEvent.cs @@ -23,5 +23,6 @@ namespace NzbDrone.Core.Download public Dictionary Data { get; set; } public TrackedDownload TrackedDownload { get; set; } public List Languages { get; set; } + public bool SkipRedownload { get; set; } } } diff --git a/src/NzbDrone.Core/Download/FailedDownloadService.cs b/src/NzbDrone.Core/Download/FailedDownloadService.cs index f26741ca8..8e07b23fd 100644 --- a/src/NzbDrone.Core/Download/FailedDownloadService.cs +++ b/src/NzbDrone.Core/Download/FailedDownloadService.cs @@ -9,8 +9,8 @@ namespace NzbDrone.Core.Download { public interface IFailedDownloadService { - void MarkAsFailed(int historyId); - void MarkAsFailed(string downloadId); + void MarkAsFailed(int historyId, bool skipRedownload = false); + void MarkAsFailed(string downloadId, bool skipRedownload = false); void Check(TrackedDownload trackedDownload); void ProcessFailed(TrackedDownload trackedDownload); } @@ -30,14 +30,14 @@ namespace NzbDrone.Core.Download _eventAggregator = eventAggregator; } - public void MarkAsFailed(int historyId) + public void MarkAsFailed(int historyId, bool skipRedownload = false) { var history = _historyService.Get(historyId); var downloadId = history.DownloadId; if (downloadId.IsNullOrWhiteSpace()) { - PublishDownloadFailedEvent(new List { history }, "Manually marked as failed"); + PublishDownloadFailedEvent(new List { history }, "Manually marked as failed", skipRedownload: skipRedownload); return; } @@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download PublishDownloadFailedEvent(grabbedHistory, "Manually marked as failed"); } - public void MarkAsFailed(string downloadId) + public void MarkAsFailed(string downloadId, bool skipRedownload = false) { var history = _historyService.Find(downloadId, EpisodeHistoryEventType.Grabbed); @@ -65,7 +65,7 @@ namespace NzbDrone.Core.Download { var trackedDownload = _trackedDownloadService.Find(downloadId); - PublishDownloadFailedEvent(history, "Manually marked as failed", trackedDownload); + PublishDownloadFailedEvent(history, "Manually marked as failed", trackedDownload, skipRedownload: skipRedownload); } } @@ -125,7 +125,7 @@ namespace NzbDrone.Core.Download PublishDownloadFailedEvent(grabbedItems, failure, trackedDownload); } - private void PublishDownloadFailedEvent(List historyItems, string message, TrackedDownload trackedDownload = null) + private void PublishDownloadFailedEvent(List historyItems, string message, TrackedDownload trackedDownload = null, bool skipRedownload = false) { var historyItem = historyItems.First(); @@ -140,7 +140,8 @@ namespace NzbDrone.Core.Download Message = message, Data = historyItem.Data, TrackedDownload = trackedDownload, - Languages = historyItem.Languages + Languages = historyItem.Languages, + SkipRedownload = skipRedownload }; _eventAggregator.PublishEvent(downloadFailedEvent); diff --git a/src/NzbDrone.Core/Download/RedownloadFailedDownloadService.cs b/src/NzbDrone.Core/Download/RedownloadFailedDownloadService.cs index ed23f021b..20c19e15d 100644 --- a/src/NzbDrone.Core/Download/RedownloadFailedDownloadService.cs +++ b/src/NzbDrone.Core/Download/RedownloadFailedDownloadService.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using NLog; using NzbDrone.Core.Configuration; using NzbDrone.Core.IndexerSearch; @@ -30,6 +30,12 @@ namespace NzbDrone.Core.Download [EventHandleOrder(EventHandleOrder.Last)] public void Handle(DownloadFailedEvent message) { + if (message.SkipRedownload) + { + _logger.Debug("Skip redownloading requested by user"); + return; + } + if (!_configService.AutoRedownloadFailed) { _logger.Debug("Auto redownloading failed episodes is disabled"); diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 31d7cfe44..b5320ab95 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -276,6 +276,8 @@ "ShownClickToHide": "Shown, click to hide", "Size": "Size", "SizeOnDisk": "Size on disk", + "SkipRedownloadHelpText": "Prevents Sonarr from trying to download an alternative release for this item", + "SkipRedownload": "Skip Redownload", "Source": "Source", "Special": "Special", "Started": "Started", diff --git a/src/Sonarr.Api.V3/Queue/QueueController.cs b/src/Sonarr.Api.V3/Queue/QueueController.cs index 605bc9db9..6259956c3 100644 --- a/src/Sonarr.Api.V3/Queue/QueueController.cs +++ b/src/Sonarr.Api.V3/Queue/QueueController.cs @@ -70,7 +70,7 @@ namespace Sonarr.Api.V3.Queue } [RestDeleteById] - public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false) + public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false) { var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id); @@ -88,12 +88,12 @@ namespace Sonarr.Api.V3.Queue throw new NotFoundException(); } - Remove(trackedDownload, removeFromClient, blocklist); + Remove(trackedDownload, removeFromClient, blocklist, skipRedownload); _trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId); } [HttpDelete("bulk")] - public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false) + public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false) { var trackedDownloadIds = new List(); var pendingToRemove = new List(); @@ -124,7 +124,7 @@ namespace Sonarr.Api.V3.Queue foreach (var trackedDownload in trackedToRemove.DistinctBy(t => t.DownloadItem.DownloadId)) { - Remove(trackedDownload, removeFromClient, blocklist); + Remove(trackedDownload, removeFromClient, blocklist, skipRedownload); trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId); } @@ -255,7 +255,7 @@ namespace Sonarr.Api.V3.Queue _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id); } - private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist) + private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload) { if (removeFromClient) { @@ -271,7 +271,7 @@ namespace Sonarr.Api.V3.Queue if (blocklist) { - _failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId); + _failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId, skipRedownload); } if (!removeFromClient && !blocklist)