diff --git a/frontend/src/Activity/Queue/Queue.js b/frontend/src/Activity/Queue/Queue.js
index b73ce0ad7..27d5f9e26 100644
--- a/frontend/src/Activity/Queue/Queue.js
+++ b/frontend/src/Activity/Queue/Queue.js
@@ -299,6 +299,17 @@ class Queue extends Component {
return !!(item && item.artistId && item.albumId);
})
)}
+ allPending={isConfirmRemoveModalOpen && (
+ selectedIds.every((id) => {
+ const item = items.find((i) => i.id === id);
+
+ if (!item) {
+ return false;
+ }
+
+ return item.status === 'delay' || item.status === 'downloadClientUnavailable';
+ })
+ )}
onRemovePress={this.onRemoveSelectedConfirmed}
onModalClose={this.onConfirmRemoveModalClose}
/>
diff --git a/frontend/src/Activity/Queue/QueueRow.js b/frontend/src/Activity/Queue/QueueRow.js
index 08634d9f7..4c2829f55 100644
--- a/frontend/src/Activity/Queue/QueueRow.js
+++ b/frontend/src/Activity/Queue/QueueRow.js
@@ -394,6 +394,7 @@ class QueueRow extends Component {
isOpen={isRemoveQueueItemModalOpen}
sourceTitle={title}
canIgnore={!!artist}
+ isPending={isPending}
onRemovePress={this.onRemoveQueueItemModalConfirmed}
onModalClose={this.onRemoveQueueItemModalClose}
/>
diff --git a/frontend/src/Activity/Queue/RemoveQueueItemModal.js b/frontend/src/Activity/Queue/RemoveQueueItemModal.js
index d9e4dd7f6..4509791fc 100644
--- a/frontend/src/Activity/Queue/RemoveQueueItemModal.js
+++ b/frontend/src/Activity/Queue/RemoveQueueItemModal.js
@@ -72,7 +72,8 @@ class RemoveQueueItemModal extends Component {
const {
isOpen,
sourceTitle,
- canIgnore
+ canIgnore,
+ isPending
} = this.props;
const { removeFromClient, blocklist, skipRedownload } = this.state;
@@ -95,20 +96,22 @@ class RemoveQueueItemModal extends Component {
Are you sure you want to remove '{sourceTitle}' from the queue?
-
-
- {translate('RemoveFromDownloadClient')}
-
+ {
+ isPending ?
+ null :
+
+ {translate('RemoveFromDownloadClient')}
-
-
+
+
+ }
@@ -164,6 +167,7 @@ RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
canIgnore: PropTypes.bool.isRequired,
+ isPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
diff --git a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
index 3b9164e68..f607161b0 100644
--- a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
+++ b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
@@ -73,7 +73,8 @@ class RemoveQueueItemsModal extends Component {
const {
isOpen,
selectedCount,
- canIgnore
+ canIgnore,
+ allPending
} = this.props;
const { removeFromClient, blocklist, skipRedownload } = this.state;
@@ -96,20 +97,22 @@ class RemoveQueueItemsModal extends Component {
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', [selectedCount]) : translate('RemoveSelectedItemQueueMessageText')}
-
-
- {translate('RemoveFromDownloadClient')}
-
+ {
+ allPending ?
+ null :
+
+ {translate('RemoveFromDownloadClient')}
-
-
+
+
+ }
@@ -165,6 +168,7 @@ RemoveQueueItemsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
selectedCount: PropTypes.number.isRequired,
canIgnore: PropTypes.bool.isRequired,
+ allPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
diff --git a/src/Lidarr.Api.V1/Queue/QueueController.cs b/src/Lidarr.Api.V1/Queue/QueueController.cs
index 4bc5af52a..ddc6cafe8 100644
--- a/src/Lidarr.Api.V1/Queue/QueueController.cs
+++ b/src/Lidarr.Api.V1/Queue/QueueController.cs
@@ -7,6 +7,7 @@ using Lidarr.Http.REST;
using Lidarr.Http.REST.Attributes;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Download;
@@ -17,6 +18,7 @@ using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Queue;
using NzbDrone.SignalR;
+using Sentry.Protocol;
namespace Lidarr.Api.V1.Queue
{
@@ -32,6 +34,7 @@ namespace Lidarr.Api.V1.Queue
private readonly IFailedDownloadService _failedDownloadService;
private readonly IIgnoredDownloadService _ignoredDownloadService;
private readonly IProvideDownloadClient _downloadClientProvider;
+ private readonly IBlocklistService _blocklistService;
public QueueController(IBroadcastSignalRMessage broadcastSignalRMessage,
IQueueService queueService,
@@ -40,7 +43,8 @@ namespace Lidarr.Api.V1.Queue
ITrackedDownloadService trackedDownloadService,
IFailedDownloadService failedDownloadService,
IIgnoredDownloadService ignoredDownloadService,
- IProvideDownloadClient downloadClientProvider)
+ IProvideDownloadClient downloadClientProvider,
+ IBlocklistService blocklistService)
: base(broadcastSignalRMessage)
{
_queueService = queueService;
@@ -49,6 +53,7 @@ namespace Lidarr.Api.V1.Queue
_failedDownloadService = failedDownloadService;
_ignoredDownloadService = ignoredDownloadService;
_downloadClientProvider = downloadClientProvider;
+ _blocklistService = blocklistService;
_qualityComparer = new QualityModelComparer(qualityProfileService.GetDefaultProfile(string.Empty));
}
@@ -62,29 +67,62 @@ namespace Lidarr.Api.V1.Queue
[RestDeleteById]
public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false)
{
- var trackedDownload = Remove(id, removeFromClient, blocklist, skipRedownload);
+ var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
- if (trackedDownload != null)
+ if (pendingRelease != null)
{
- _trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId);
+ Remove(pendingRelease);
+
+ return;
}
+
+ var trackedDownload = GetTrackedDownload(id);
+
+ if (trackedDownload == null)
+ {
+ throw new NotFoundException();
+ }
+
+ 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, [FromQuery] bool skipRedownload = false)
{
var trackedDownloadIds = new List();
+ var pendingToRemove = new List();
+ var trackedToRemove = new List();
foreach (var id in resource.Ids)
{
- var trackedDownload = Remove(id, removeFromClient, blocklist, skipRedownload);
+ var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
+
+ if (pendingRelease != null)
+ {
+ pendingToRemove.Add(pendingRelease);
+ continue;
+ }
+
+ var trackedDownload = GetTrackedDownload(id);
if (trackedDownload != null)
{
- trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId);
+ trackedToRemove.Add(trackedDownload);
}
}
+ foreach (var pendingRelease in pendingToRemove.DistinctBy(p => p.Id))
+ {
+ Remove(pendingRelease);
+ }
+
+ foreach (var trackedDownload in trackedToRemove.DistinctBy(t => t.DownloadItem.DownloadId))
+ {
+ Remove(trackedDownload, removeFromClient, blocklist, skipRedownload);
+ trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId);
+ }
+
_trackedDownloadService.StopTracking(trackedDownloadIds);
return new { };
@@ -195,24 +233,14 @@ namespace Lidarr.Api.V1.Queue
}
}
- private TrackedDownload Remove(int id, bool removeFromClient, bool blocklist, bool skipRedownload)
+ private void Remove(NzbDrone.Core.Queue.Queue pendingRelease)
{
- var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
-
- if (pendingRelease != null)
- {
- _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
-
- return null;
- }
-
- var trackedDownload = GetTrackedDownload(id);
-
- if (trackedDownload == null)
- {
- throw new NotFoundException();
- }
+ _blocklistService.Block(pendingRelease.RemoteAlbum, "Pending release manually blocklisted");
+ _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
+ }
+ private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload)
+ {
if (removeFromClient)
{
var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
diff --git a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
index 707147b73..783d94782 100644
--- a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
+++ b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
@@ -16,6 +16,7 @@ namespace NzbDrone.Core.Blocklisting
{
bool Blocklisted(int artistId, ReleaseInfo release);
PagingSpec Paged(PagingSpec pagingSpec);
+ void Block(RemoteAlbum remoteAlbum, string message);
void Delete(int id);
void Delete(List ids);
}
@@ -66,6 +67,30 @@ namespace NzbDrone.Core.Blocklisting
return _blocklistRepository.GetPaged(pagingSpec);
}
+ public void Block(RemoteAlbum remoteAlbum, string message)
+ {
+ var blocklist = new Blocklist
+ {
+ ArtistId = remoteAlbum.Artist.Id,
+ AlbumIds = remoteAlbum.Albums.Select(e => e.Id).ToList(),
+ SourceTitle = remoteAlbum.Release.Title,
+ Quality = remoteAlbum.ParsedAlbumInfo.Quality,
+ Date = DateTime.UtcNow,
+ PublishedDate = remoteAlbum.Release.PublishDate,
+ Size = remoteAlbum.Release.Size,
+ Indexer = remoteAlbum.Release.Indexer,
+ Protocol = remoteAlbum.Release.DownloadProtocol,
+ Message = message,
+ };
+
+ if (remoteAlbum.Release is TorrentInfo torrentRelease)
+ {
+ blocklist.TorrentInfoHash = torrentRelease.InfoHash;
+ }
+
+ _blocklistRepository.Insert(blocklist);
+ }
+
public void Delete(int id)
{
_blocklistRepository.Delete(id);
diff --git a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs
index ace9a46d5..b3a69d16c 100644
--- a/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs
+++ b/src/NzbDrone.Core/Download/Pending/PendingReleaseService.cs
@@ -137,13 +137,6 @@ namespace NzbDrone.Core.Download.Pending
}
}
- private ILookup CreateAlbumLookup(IEnumerable alreadyPending)
- {
- return alreadyPending.SelectMany(v => v.RemoteAlbum.Albums
- .Select(d => new { Album = d, PendingRelease = v }))
- .ToLookup(v => v.Album.Id, v => v.PendingRelease);
- }
-
public List GetPending()
{
var releases = _repository.All().Select(p =>
@@ -163,13 +156,6 @@ namespace NzbDrone.Core.Download.Pending
return releases;
}
- private List FilterBlockedIndexers(List releases)
- {
- var blockedIndexers = new HashSet(_indexerStatusService.GetBlockedProviders().Select(v => v.ProviderId));
-
- return releases.Where(release => !blockedIndexers.Contains(release.IndexerId)).ToList();
- }
-
public List GetPendingRemoteAlbums(int artistId)
{
return IncludeRemoteAlbums(_repository.AllByArtistId(artistId)).Select(v => v.RemoteAlbum).ToList();
@@ -263,6 +249,20 @@ namespace NzbDrone.Core.Download.Pending
.MaxBy(p => p.Release.AgeHours);
}
+ private ILookup CreateAlbumLookup(IEnumerable alreadyPending)
+ {
+ return alreadyPending.SelectMany(v => v.RemoteAlbum.Albums
+ .Select(d => new { Album = d, PendingRelease = v }))
+ .ToLookup(v => v.Album.Id, v => v.PendingRelease);
+ }
+
+ private List FilterBlockedIndexers(List releases)
+ {
+ var blockedIndexers = new HashSet(_indexerStatusService.GetBlockedProviders().Select(v => v.ProviderId));
+
+ return releases.Where(release => !blockedIndexers.Contains(release.IndexerId)).ToList();
+ }
+
private List GetPendingReleases()
{
return IncludeRemoteAlbums(_repository.All().ToList());
@@ -354,13 +354,6 @@ namespace NzbDrone.Core.Download.Pending
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());
}
- private static Func MatchingReleasePredicate(ReleaseInfo release)
- {
- return p => p.Title == release.Title &&
- p.Release.PublishDate == release.PublishDate &&
- p.Release.Indexer == release.Indexer;
- }
-
private int GetDelay(RemoteAlbum remoteAlbum)
{
var delayProfile = _delayProfileService.AllForTags(remoteAlbum.Artist.Tags).OrderBy(d => d.Order).First();
@@ -455,5 +448,12 @@ namespace NzbDrone.Core.Download.Pending
{
RemoveRejected(message.ProcessedDecisions.Rejected);
}
+
+ private static Func MatchingReleasePredicate(ReleaseInfo release)
+ {
+ return p => p.Title == release.Title &&
+ p.Release.PublishDate == release.PublishDate &&
+ p.Release.Indexer == release.Indexer;
+ }
}
}