-
- Removing will remove the download and the file(s) from the download client.
-
+
+ Remove From Download Client
+
+
+
Blacklist Release
+
Remove
@@ -138,6 +156,7 @@ class RemoveQueueItemModal extends Component {
RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
+ canIgnore: 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 7d8d8d294..074af95cd 100644
--- a/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
+++ b/frontend/src/Activity/Queue/RemoveQueueItemsModal.js
@@ -21,13 +21,29 @@ class RemoveQueueItemsModal extends Component {
super(props, context);
this.state = {
+ remove: true,
blacklist: false,
skipredownload: false
};
}
//
- // Listeners
+ // Control
+
+ resetState = function() {
+ this.setState({
+ remove: true,
+ blacklist: false,
+ skipredownload: false
+ });
+ }
+
+ //
+ // Listeners
+
+ onRemoveChange = ({ value }) => {
+ this.setState({ remove: value });
+ }
onBlacklistChange = ({ value }) => {
this.setState({ blacklist: value });
@@ -37,22 +53,15 @@ class RemoveQueueItemsModal extends Component {
this.setState({ skipredownload: value });
}
- onRemoveQueueItemConfirmed = () => {
- const blacklist = this.state.blacklist;
- const skipredownload = this.state.skipredownload;
+ onRemoveConfirmed = () => {
+ const state = this.state;
- this.setState({
- blacklist: false,
- skipredownload: false
- });
- this.props.onRemovePress(blacklist, skipredownload);
+ this.resetState();
+ this.props.onRemovePress(state);
}
onModalClose = () => {
- this.setState({
- blacklist: false,
- skipredownload: false
- });
+ this.resetState();
this.props.onModalClose();
}
@@ -62,11 +71,11 @@ class RemoveQueueItemsModal extends Component {
render() {
const {
isOpen,
- selectedCount
+ selectedCount,
+ canIgnore
} = this.props;
- const blacklist = this.state.blacklist;
- const skipredownload = this.state.skipredownload;
+ const { remove, blacklist, skipredownload } = this.state;
return (
- Blacklist Release
+ Remove From Download Client
+
+
+
+
+
+
+ Blacklist Release{selectedCount > 1 ? 's' : ''}
+
+
Remove
@@ -134,6 +159,7 @@ class RemoveQueueItemsModal extends Component {
RemoveQueueItemsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
selectedCount: PropTypes.number.isRequired,
+ canIgnore: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
diff --git a/frontend/src/Helpers/Props/icons.js b/frontend/src/Helpers/Props/icons.js
index 6643cbc73..122c1e8e3 100644
--- a/frontend/src/Helpers/Props/icons.js
+++ b/frontend/src/Helpers/Props/icons.js
@@ -157,6 +157,7 @@ export const HEALTH = fasMedkit;
export const HEART = fasHeart;
export const HISTORY = fasHistory;
export const HOUSEKEEPING = fasHome;
+export const IGNORE = fasTimesCircle;
export const INFO = fasInfoCircle;
export const INTERACTIVE = fasUser;
export const KEYBOARD = farKeyboard;
diff --git a/frontend/src/Store/Actions/historyActions.js b/frontend/src/Store/Actions/historyActions.js
index d7552b938..8862464e7 100644
--- a/frontend/src/Store/Actions/historyActions.js
+++ b/frontend/src/Store/Actions/historyActions.js
@@ -179,6 +179,17 @@ export const defaultState = {
type: filterTypes.EQUAL
}
]
+ },
+ {
+ key: 'ignored',
+ label: 'Ignored',
+ filters: [
+ {
+ key: 'eventType',
+ value: '7',
+ type: filterTypes.EQUAL
+ }
+ ]
}
]
diff --git a/frontend/src/Store/Actions/queueActions.js b/frontend/src/Store/Actions/queueActions.js
index 85c301c7d..1ecc1d978 100644
--- a/frontend/src/Store/Actions/queueActions.js
+++ b/frontend/src/Store/Actions/queueActions.js
@@ -345,6 +345,7 @@ export const actionHandlers = handleThunks({
[REMOVE_QUEUE_ITEM]: function(getState, payload, dispatch) {
const {
id,
+ remove,
blacklist,
skipredownload
} = payload;
@@ -352,7 +353,7 @@ export const actionHandlers = handleThunks({
dispatch(updateItem({ section: paged, id, isRemoving: true }));
const promise = createAjaxRequest({
- url: `/queue/${id}?blacklist=${blacklist}&skipredownload=${skipredownload}`,
+ url: `/queue/${id}?removeFromClient=${remove}&blacklist=${blacklist}&skipredownload=${skipredownload}`,
method: 'DELETE'
}).request;
@@ -368,6 +369,7 @@ export const actionHandlers = handleThunks({
[REMOVE_QUEUE_ITEMS]: function(getState, payload, dispatch) {
const {
ids,
+ remove,
blacklist,
skipredownload
} = payload;
@@ -385,7 +387,7 @@ export const actionHandlers = handleThunks({
]));
const promise = createAjaxRequest({
- url: `/queue/bulk?blacklist=${blacklist}&skipredownload=${skipredownload}`,
+ url: `/queue/bulk?removeFromClient=${remove}&blacklist=${blacklist}&skipredownload=${skipredownload}`,
method: 'DELETE',
dataType: 'json',
data: JSON.stringify({ ids })
diff --git a/src/Lidarr.Api.V1/Queue/QueueActionModule.cs b/src/Lidarr.Api.V1/Queue/QueueActionModule.cs
index de1c546df..fe2f29b18 100644
--- a/src/Lidarr.Api.V1/Queue/QueueActionModule.cs
+++ b/src/Lidarr.Api.V1/Queue/QueueActionModule.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using Lidarr.Http;
using Lidarr.Http.Extensions;
using Lidarr.Http.REST;
@@ -15,6 +16,7 @@ namespace Lidarr.Api.V1.Queue
private readonly IQueueService _queueService;
private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IFailedDownloadService _failedDownloadService;
+ private readonly IIgnoredDownloadService _ignoredDownloadService;
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IPendingReleaseService _pendingReleaseService;
private readonly IDownloadService _downloadService;
@@ -22,6 +24,7 @@ namespace Lidarr.Api.V1.Queue
public QueueActionModule(IQueueService queueService,
ITrackedDownloadService trackedDownloadService,
IFailedDownloadService failedDownloadService,
+ IIgnoredDownloadService ignoredDownloadService,
IProvideDownloadClient downloadClientProvider,
IPendingReleaseService pendingReleaseService,
IDownloadService downloadService)
@@ -29,6 +32,7 @@ namespace Lidarr.Api.V1.Queue
_queueService = queueService;
_trackedDownloadService = trackedDownloadService;
_failedDownloadService = failedDownloadService;
+ _ignoredDownloadService = ignoredDownloadService;
_downloadClientProvider = downloadClientProvider;
_pendingReleaseService = pendingReleaseService;
_downloadService = downloadService;
@@ -75,10 +79,11 @@ namespace Lidarr.Api.V1.Queue
private object Remove(int id)
{
+ var removeFromClient = Request.GetBooleanQueryParameter("removeFromClient", true);
var blacklist = Request.GetBooleanQueryParameter("blacklist");
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
- var trackedDownload = Remove(id, blacklist, skipReDownload);
+ var trackedDownload = Remove(id, removeFromClient, blacklist, skipReDownload);
if (trackedDownload != null)
{
@@ -90,6 +95,7 @@ namespace Lidarr.Api.V1.Queue
private object Remove()
{
+ var removeFromClient = Request.GetBooleanQueryParameter("removeFromClient", true);
var blacklist = Request.GetBooleanQueryParameter("blacklist");
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
@@ -98,7 +104,7 @@ namespace Lidarr.Api.V1.Queue
foreach (var id in resource.Ids)
{
- var trackedDownload = Remove(id, blacklist, skipReDownload);
+ var trackedDownload = Remove(id, removeFromClient, blacklist, skipReDownload);
if (trackedDownload != null)
{
@@ -111,7 +117,7 @@ namespace Lidarr.Api.V1.Queue
return new object();
}
- private TrackedDownload Remove(int id, bool blacklist, bool skipReDownload)
+ private TrackedDownload Remove(int id, bool removeFromClient, bool blacklist, bool skipReDownload)
{
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
@@ -129,20 +135,31 @@ namespace Lidarr.Api.V1.Queue
throw new NotFoundException();
}
- var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
-
- if (downloadClient == null)
+ if (removeFromClient)
{
- throw new BadRequestException();
- }
+ var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
+
+ if (downloadClient == null)
+ {
+ throw new BadRequestException();
+ }
- downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadId, true);
+ downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadId, true);
+ }
if (blacklist)
{
_failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId, skipReDownload);
}
+ if (!removeFromClient && !blacklist)
+ {
+ if (!_ignoredDownloadService.IgnoreDownload(trackedDownload))
+ {
+ return null;
+ }
+ }
+
return trackedDownload;
}
diff --git a/src/NzbDrone.Core/Download/DownloadIgnoredEvent.cs b/src/NzbDrone.Core/Download/DownloadIgnoredEvent.cs
new file mode 100644
index 000000000..7373266a7
--- /dev/null
+++ b/src/NzbDrone.Core/Download/DownloadIgnoredEvent.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using NzbDrone.Common.Messaging;
+using NzbDrone.Core.Qualities;
+
+namespace NzbDrone.Core.Download
+{
+ public class DownloadIgnoredEvent : IEvent
+ {
+ public int ArtistId { get; set; }
+ public List AlbumIds { get; set; }
+ public QualityModel Quality { get; set; }
+ public string SourceTitle { get; set; }
+ public string DownloadClient { get; set; }
+ public string DownloadId { get; set; }
+ public string Message { get; set; }
+ }
+}
diff --git a/src/NzbDrone.Core/Download/IgnoredDownloadService.cs b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs
new file mode 100644
index 000000000..ecc051162
--- /dev/null
+++ b/src/NzbDrone.Core/Download/IgnoredDownloadService.cs
@@ -0,0 +1,52 @@
+using System.Linq;
+using NLog;
+using NzbDrone.Common.Extensions;
+using NzbDrone.Core.Download.TrackedDownloads;
+using NzbDrone.Core.Messaging.Events;
+
+namespace NzbDrone.Core.Download
+{
+ public interface IIgnoredDownloadService
+ {
+ bool IgnoreDownload(TrackedDownload trackedDownload);
+ }
+
+ public class IgnoredDownloadService : IIgnoredDownloadService
+ {
+ private readonly IEventAggregator _eventAggregator;
+ private readonly Logger _logger;
+
+ public IgnoredDownloadService(IEventAggregator eventAggregator,
+ Logger logger)
+ {
+ _eventAggregator = eventAggregator;
+ _logger = logger;
+ }
+
+ public bool IgnoreDownload(TrackedDownload trackedDownload)
+ {
+ var artist = trackedDownload.RemoteAlbum.Artist;
+ var albums = trackedDownload.RemoteAlbum.Albums;
+
+ if (artist == null || albums.Empty())
+ {
+ _logger.Warn("Unable to ignore download for unknown artist/album");
+ return false;
+ }
+
+ var downloadIgnoredEvent = new DownloadIgnoredEvent
+ {
+ ArtistId = artist.Id,
+ AlbumIds = albums.Select(e => e.Id).ToList(),
+ Quality = trackedDownload.RemoteAlbum.ParsedAlbumInfo.Quality,
+ SourceTitle = trackedDownload.DownloadItem.Title,
+ DownloadClient = trackedDownload.DownloadItem.DownloadClient,
+ DownloadId = trackedDownload.DownloadItem.DownloadId,
+ Message = "Manually ignored"
+ };
+
+ _eventAggregator.PublishEvent(downloadIgnoredEvent);
+ return true;
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/DownloadMonitoringService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/DownloadMonitoringService.cs
index 63396f973..2fb243335 100644
--- a/src/NzbDrone.Core/Download/TrackedDownloads/DownloadMonitoringService.cs
+++ b/src/NzbDrone.Core/Download/TrackedDownloads/DownloadMonitoringService.cs
@@ -131,8 +131,10 @@ namespace NzbDrone.Core.Download.TrackedDownloads
private bool DownloadIsTrackable(TrackedDownload trackedDownload)
{
- // If the download has already been imported or failed don't track it
- if (trackedDownload.State == TrackedDownloadState.Imported || trackedDownload.State == TrackedDownloadState.DownloadFailed)
+ // If the download has already been imported or failed or the user ignored it don't track it
+ if (trackedDownload.State == TrackedDownloadState.Imported ||
+ trackedDownload.State == TrackedDownloadState.DownloadFailed ||
+ trackedDownload.State == TrackedDownloadState.Ignored)
{
return false;
}
diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs
index d88fb5911..46c46d529 100644
--- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs
+++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs
@@ -41,7 +41,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
ImportPending,
Importing,
ImportFailed,
- Imported
+ Imported,
+ Ignored
}
public enum TrackedDownloadStatus
diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
index 6384c1992..2d7f81281 100644
--- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
+++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownloadService.cs
@@ -252,6 +252,8 @@ namespace NzbDrone.Core.Download.TrackedDownloads
return TrackedDownloadState.Imported;
case HistoryEventType.DownloadFailed:
return TrackedDownloadState.DownloadFailed;
+ case HistoryEventType.DownloadIgnored:
+ return TrackedDownloadState.Ignored;
}
// Since DownloadComplete is a new event type, we can't assume it exists for old downloads
diff --git a/src/NzbDrone.Core/History/History.cs b/src/NzbDrone.Core/History/History.cs
index 2949aae28..8d9592361 100644
--- a/src/NzbDrone.Core/History/History.cs
+++ b/src/NzbDrone.Core/History/History.cs
@@ -41,6 +41,7 @@ namespace NzbDrone.Core.History
TrackFileRenamed = 6,
AlbumImportIncomplete = 7,
DownloadImported = 8,
- TrackFileRetagged = 9
+ TrackFileRetagged = 9,
+ DownloadIgnored = 10
}
}
diff --git a/src/NzbDrone.Core/History/HistoryService.cs b/src/NzbDrone.Core/History/HistoryService.cs
index e4313e635..d4e7d8dc1 100644
--- a/src/NzbDrone.Core/History/HistoryService.cs
+++ b/src/NzbDrone.Core/History/HistoryService.cs
@@ -39,7 +39,8 @@ namespace NzbDrone.Core.History
IHandle,
IHandle,
IHandle,
- IHandle
+ IHandle,
+ IHandle
{
private readonly IHistoryRepository _historyRepository;
private readonly Logger _logger;
@@ -369,6 +370,31 @@ namespace NzbDrone.Core.History
_historyRepository.DeleteForArtist(message.Artist.Id);
}
+ public void Handle(DownloadIgnoredEvent message)
+ {
+ var historyToAdd = new List();
+ foreach (var albumId in message.AlbumIds)
+ {
+ var history = new History
+ {
+ EventType = HistoryEventType.DownloadIgnored,
+ Date = DateTime.UtcNow,
+ Quality = message.Quality,
+ SourceTitle = message.SourceTitle,
+ ArtistId = message.ArtistId,
+ AlbumId = albumId,
+ DownloadId = message.DownloadId
+ };
+
+ history.Data.Add("DownloadClient", message.DownloadClient);
+ history.Data.Add("Message", message.Message);
+
+ historyToAdd.Add(history);
+ }
+
+ _historyRepository.InsertMany(historyToAdd);
+ }
+
public List Since(DateTime date, HistoryEventType? eventType)
{
return _historyRepository.Since(date, eventType);