diff --git a/frontend/src/Activity/Queue/Queue.js b/frontend/src/Activity/Queue/Queue.js index fd500c967..0efc29f21 100644 --- a/frontend/src/Activity/Queue/Queue.js +++ b/frontend/src/Activity/Queue/Queue.js @@ -25,7 +25,7 @@ import toggleSelected from 'Utilities/Table/toggleSelected'; import QueueFilterModal from './QueueFilterModal'; import QueueOptionsConnector from './QueueOptionsConnector'; import QueueRowConnector from './QueueRowConnector'; -import RemoveQueueItemsModal from './RemoveQueueItemsModal'; +import RemoveQueueItemModal from './RemoveQueueItemModal'; class Queue extends Component { @@ -309,9 +309,16 @@ class Queue extends Component { } - { + const item = items.find((i) => i.id === id); + + return !!(item && item.downloadClientHasPostImportCategory); + }) + )} canIgnore={isConfirmRemoveModalOpen && ( selectedIds.every((id) => { const item = items.find((i) => i.id === id); @@ -319,7 +326,7 @@ class Queue extends Component { return !!(item && item.artistId && item.albumId); }) )} - allPending={isConfirmRemoveModalOpen && ( + pending={isConfirmRemoveModalOpen && ( selectedIds.every((id) => { const item = items.find((i) => i.id === id); diff --git a/frontend/src/Activity/Queue/QueueRow.js b/frontend/src/Activity/Queue/QueueRow.js index 4e3a0b380..d0f1fbacf 100644 --- a/frontend/src/Activity/Queue/QueueRow.js +++ b/frontend/src/Activity/Queue/QueueRow.js @@ -98,6 +98,7 @@ class QueueRow extends Component { indexer, outputPath, downloadClient, + downloadClientHasPostImportCategory, downloadForced, estimatedCompletionTime, added, @@ -403,6 +404,7 @@ class QueueRow extends Component { { - this.setState({ removeFromClient: value }); - }; - - onBlocklistChange = ({ value }) => { - this.setState({ blocklist: value }); - }; - - onSkipRedownloadChange = ({ value }) => { - this.setState({ skipRedownload: value }); - }; - - onRemoveConfirmed = () => { - const state = this.state; - - this.resetState(); - this.props.onRemovePress(state); - }; - - onModalClose = () => { - this.resetState(); - this.props.onModalClose(); - }; - - // - // Render - - render() { - const { - isOpen, - sourceTitle, - canIgnore, - isPending - } = this.props; - - const { removeFromClient, blocklist, skipRedownload } = this.state; - - return ( - - - - Remove - {sourceTitle} - - - -
- Are you sure you want to remove '{sourceTitle}' from the queue? -
- - { - isPending ? - null : - - {translate('RemoveFromDownloadClient')} - - - - } - - - - {translate('BlocklistRelease')} - - - - - - { - blocklist && - - - {translate('SkipRedownload')} - - - - } - -
- - - - - - -
-
- ); - } -} - -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 -}; - -export default RemoveQueueItemModal; diff --git a/frontend/src/Activity/Queue/RemoveQueueItemModal.tsx b/frontend/src/Activity/Queue/RemoveQueueItemModal.tsx new file mode 100644 index 000000000..4348f818c --- /dev/null +++ b/frontend/src/Activity/Queue/RemoveQueueItemModal.tsx @@ -0,0 +1,230 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import Button from 'Components/Link/Button'; +import Modal from 'Components/Modal/Modal'; +import ModalBody from 'Components/Modal/ModalBody'; +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'; +import styles from './RemoveQueueItemModal.css'; + +interface RemovePressProps { + remove: boolean; + changeCategory: boolean; + blocklist: boolean; + skipRedownload: boolean; +} + +interface RemoveQueueItemModalProps { + isOpen: boolean; + sourceTitle: string; + canChangeCategory: boolean; + canIgnore: boolean; + isPending: boolean; + selectedCount?: number; + onRemovePress(props: RemovePressProps): void; + onModalClose: () => void; +} + +type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore'; +type BlocklistMethod = + | 'doNotBlocklist' + | 'blocklistAndSearch' + | 'blocklistOnly'; + +function RemoveQueueItemModal(props: RemoveQueueItemModalProps) { + const { + isOpen, + sourceTitle, + canIgnore, + canChangeCategory, + isPending, + selectedCount, + onRemovePress, + onModalClose, + } = props; + + const multipleSelected = selectedCount && selectedCount > 1; + + const [removalMethod, setRemovalMethod] = + useState('removeFromClient'); + const [blocklistMethod, setBlocklistMethod] = + useState('doNotBlocklist'); + + const { title, message } = useMemo(() => { + if (!selectedCount) { + return { + title: translate('RemoveQueueItem', { sourceTitle }), + message: translate('RemoveQueueItemConfirmation', { sourceTitle }), + }; + } + + if (selectedCount === 1) { + return { + title: translate('RemoveSelectedItem'), + message: translate('RemoveSelectedItemQueueMessageText'), + }; + } + + return { + title: translate('RemoveSelectedItems'), + message: translate('RemoveSelectedItemsQueueMessageText', { + selectedCount, + }), + }; + }, [sourceTitle, selectedCount]); + + const removalMethodOptions = useMemo(() => { + return [ + { + key: 'removeFromClient', + value: translate('RemoveFromDownloadClient'), + hint: multipleSelected + ? translate('RemoveMultipleFromDownloadClientHint') + : translate('RemoveFromDownloadClientHint'), + }, + { + key: 'changeCategory', + value: translate('ChangeCategory'), + isDisabled: !canChangeCategory, + hint: multipleSelected + ? translate('ChangeCategoryMultipleHint') + : translate('ChangeCategoryHint'), + }, + { + key: 'ignore', + value: multipleSelected + ? translate('IgnoreDownloads') + : translate('IgnoreDownload'), + isDisabled: !canIgnore, + hint: multipleSelected + ? translate('IgnoreDownloadsHint') + : translate('IgnoreDownloadHint'), + }, + ]; + }, [canChangeCategory, canIgnore, multipleSelected]); + + const blocklistMethodOptions = useMemo(() => { + return [ + { + key: 'doNotBlocklist', + value: translate('DoNotBlocklist'), + hint: translate('DoNotBlocklistHint'), + }, + { + key: 'blocklistAndSearch', + value: translate('BlocklistAndSearch'), + hint: multipleSelected + ? translate('BlocklistAndSearchMultipleHint') + : translate('BlocklistAndSearchHint'), + }, + { + key: 'blocklistOnly', + value: translate('BlocklistOnly'), + hint: multipleSelected + ? translate('BlocklistMultipleOnlyHint') + : translate('BlocklistOnlyHint'), + }, + ]; + }, [multipleSelected]); + + const handleRemovalMethodChange = useCallback( + ({ value }: { value: RemovalMethod }) => { + setRemovalMethod(value); + }, + [setRemovalMethod] + ); + + const handleBlocklistMethodChange = useCallback( + ({ value }: { value: BlocklistMethod }) => { + setBlocklistMethod(value); + }, + [setBlocklistMethod] + ); + + const handleConfirmRemove = useCallback(() => { + onRemovePress({ + remove: removalMethod === 'removeFromClient', + changeCategory: removalMethod === 'changeCategory', + blocklist: blocklistMethod !== 'doNotBlocklist', + skipRedownload: blocklistMethod === 'blocklistOnly', + }); + + setRemovalMethod('removeFromClient'); + setBlocklistMethod('doNotBlocklist'); + }, [ + removalMethod, + blocklistMethod, + setRemovalMethod, + setBlocklistMethod, + onRemovePress, + ]); + + const handleModalClose = useCallback(() => { + setRemovalMethod('removeFromClient'); + setBlocklistMethod('doNotBlocklist'); + + onModalClose(); + }, [setRemovalMethod, setBlocklistMethod, onModalClose]); + + return ( + + + {title} + + +
{message}
+ + {isPending ? null : ( + + {translate('RemoveQueueItemRemovalMethod')} + + + + )} + + + + {multipleSelected + ? translate('BlocklistReleases') + : translate('BlocklistRelease')} + + + + +
+ + + + + + +
+
+ ); +} + +export default RemoveQueueItemModal; diff --git a/frontend/src/Components/Form/FormInputGroup.js b/frontend/src/Components/Form/FormInputGroup.js index 2befeaf1a..c0f0bf5dd 100644 --- a/frontend/src/Components/Form/FormInputGroup.js +++ b/frontend/src/Components/Form/FormInputGroup.js @@ -276,6 +276,7 @@ FormInputGroup.propTypes = { name: PropTypes.string.isRequired, value: PropTypes.any, values: PropTypes.arrayOf(PropTypes.any), + isDisabled: PropTypes.bool, type: PropTypes.string.isRequired, kind: PropTypes.oneOf(kinds.all), min: PropTypes.number, diff --git a/frontend/src/Store/Actions/queueActions.js b/frontend/src/Store/Actions/queueActions.js index bdf84818d..4bf200a5c 100644 --- a/frontend/src/Store/Actions/queueActions.js +++ b/frontend/src/Store/Actions/queueActions.js @@ -412,13 +412,14 @@ export const actionHandlers = handleThunks({ id, removeFromClient, blocklist, - skipRedownload + skipRedownload, + changeCategory } = payload; dispatch(updateItem({ section: paged, id, isRemoving: true })); const promise = createAjaxRequest({ - url: `/queue/${id}?removeFromClient=${removeFromClient}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`, + url: `/queue/${id}?removeFromClient=${removeFromClient}&blocklist=${blocklist}&skipRedownload=${skipRedownload}&changeCategory=${changeCategory}`, method: 'DELETE' }).request; @@ -436,7 +437,8 @@ export const actionHandlers = handleThunks({ ids, removeFromClient, blocklist, - skipRedownload + skipRedownload, + changeCategory } = payload; dispatch(batchActions([ @@ -452,7 +454,7 @@ export const actionHandlers = handleThunks({ ])); const promise = createAjaxRequest({ - url: `/queue/bulk?removeFromClient=${removeFromClient}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`, + url: `/queue/bulk?removeFromClient=${removeFromClient}&blocklist=${blocklist}&skipRedownload=${skipRedownload}&changeCategory=${changeCategory}`, method: 'DELETE', dataType: 'json', contentType: 'application/json', diff --git a/src/Lidarr.Api.V1/Queue/QueueController.cs b/src/Lidarr.Api.V1/Queue/QueueController.cs index 995a50060..3a584b23e 100644 --- a/src/Lidarr.Api.V1/Queue/QueueController.cs +++ b/src/Lidarr.Api.V1/Queue/QueueController.cs @@ -65,7 +65,7 @@ namespace Lidarr.Api.V1.Queue } [RestDeleteById] - public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false) + public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false, bool changeCategory = false) { var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id); @@ -83,12 +83,12 @@ namespace Lidarr.Api.V1.Queue throw new NotFoundException(); } - Remove(trackedDownload, removeFromClient, blocklist, skipRedownload); + Remove(trackedDownload, removeFromClient, blocklist, skipRedownload, changeCategory); _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) + public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false, [FromQuery] bool changeCategory = false) { var trackedDownloadIds = new List(); var pendingToRemove = new List(); @@ -119,7 +119,7 @@ namespace Lidarr.Api.V1.Queue foreach (var trackedDownload in trackedToRemove.DistinctBy(t => t.DownloadItem.DownloadId)) { - Remove(trackedDownload, removeFromClient, blocklist, skipRedownload); + Remove(trackedDownload, removeFromClient, blocklist, skipRedownload, changeCategory); trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId); } @@ -269,7 +269,7 @@ namespace Lidarr.Api.V1.Queue _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id); } - private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload) + private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload, bool changeCategory) { if (removeFromClient) { @@ -282,13 +282,24 @@ namespace Lidarr.Api.V1.Queue downloadClient.RemoveItem(trackedDownload.DownloadItem, true); } + else if (changeCategory) + { + var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient); + + if (downloadClient == null) + { + throw new BadRequestException(); + } + + downloadClient.MarkItemAsImported(trackedDownload.DownloadItem); + } if (blocklist) { _failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId, skipRedownload); } - if (!removeFromClient && !blocklist) + if (!removeFromClient && !blocklist && !changeCategory) { if (!_ignoredDownloadService.IgnoreDownload(trackedDownload)) { diff --git a/src/Lidarr.Api.V1/Queue/QueueResource.cs b/src/Lidarr.Api.V1/Queue/QueueResource.cs index 8c0777b86..25fab081d 100644 --- a/src/Lidarr.Api.V1/Queue/QueueResource.cs +++ b/src/Lidarr.Api.V1/Queue/QueueResource.cs @@ -35,6 +35,7 @@ namespace Lidarr.Api.V1.Queue public string DownloadId { get; set; } public DownloadProtocol Protocol { get; set; } public string DownloadClient { get; set; } + public bool DownloadClientHasPostImportCategory { get; set; } public string Indexer { get; set; } public string OutputPath { get; set; } public bool DownloadForced { get; set; } @@ -76,6 +77,7 @@ namespace Lidarr.Api.V1.Queue DownloadId = model.DownloadId, Protocol = model.Protocol, DownloadClient = model.DownloadClient, + DownloadClientHasPostImportCategory = model.DownloadClientHasPostImportCategory, Indexer = model.Indexer, OutputPath = model.OutputPath, DownloadForced = model.DownloadForced diff --git a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs index debb0fb8a..b008e070a 100644 --- a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs +++ b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs @@ -132,7 +132,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2 CanMoveFiles = false, CanBeRemoved = torrent.Status == "complete", Category = null, - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = torrent.InfoHash?.ToUpper(), IsEncrypted = false, Message = torrent.ErrorMessage, diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs index 6b21924cd..02639664a 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs @@ -89,7 +89,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole { yield return new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = Definition.Name + "_" + item.DownloadId, Category = "Lidarr", Title = item.Title, diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs index 4998fbb96..aedbc03c1 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs @@ -59,7 +59,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole { yield return new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = Definition.Name + "_" + item.DownloadId, Category = "Lidarr", Title = item.Title, diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs index a0e3e7050..9cbf3c4ca 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs @@ -135,7 +135,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge item.Title = torrent.Name; item.Category = Settings.MusicCategory; - item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.MusicImportedCategory.IsNotNullOrWhiteSpace()); var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath)); item.OutputPath = outputPath + torrent.Name; diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs index 0138f431f..664900f60 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs @@ -89,7 +89,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation var item = new DownloadClientItem() { Category = Settings.MusicCategory, - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = CreateDownloadId(torrent.Id, serialNumber), Title = torrent.Title, TotalSize = torrent.Size, diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index 0e01e8fc2..1add52b29 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -97,7 +97,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation var item = new DownloadClientItem() { Category = Settings.MusicCategory, - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = CreateDownloadId(nzb.Id, serialNumber), Title = nzb.Title, TotalSize = nzb.Size, diff --git a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs index b775472e3..3c775d563 100644 --- a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs +++ b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs @@ -108,7 +108,7 @@ namespace NzbDrone.Core.Download.Clients.Flood var item = new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = torrent.Key, Title = properties.Name, OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(properties.Directory)), diff --git a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs index a5c9d2f7d..3109aed0f 100644 --- a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs +++ b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs @@ -73,7 +73,7 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload Category = Settings.Category, Title = torrent.Name, TotalSize = torrent.Size, - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), RemainingSize = (long)(torrent.Size * (double)(1 - ((double)torrent.ReceivedPrct / 10000))), RemainingTime = torrent.Eta <= 0 ? null : TimeSpan.FromSeconds(torrent.Eta), SeedRatio = torrent.StopRatio <= 0 ? 0 : torrent.StopRatio / 100, diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs index 84e97e6ca..261bce3da 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs @@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken var item = new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = torrent.InfoHash.ToUpper(), OutputPath = outputPath + torrent.Name, RemainingSize = torrent.TotalSize - torrent.DownloadedBytes, diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs index 00488f667..dba7b3ffb 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs @@ -56,7 +56,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex { var queueItem = new DownloadClientItem(); - queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false); queueItem.DownloadId = vortexQueueItem.AddUUID ?? vortexQueueItem.Id.ToString(); queueItem.Category = vortexQueueItem.GroupName; queueItem.Title = vortexQueueItem.UiTitle; diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index b15e1c381..df3e86411 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -72,7 +72,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget queueItem.Title = item.NzbName; queueItem.TotalSize = totalSize; queueItem.Category = item.Category; - queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false); queueItem.CanMoveFiles = true; queueItem.CanBeRemoved = true; @@ -119,7 +119,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget var historyItem = new DownloadClientItem(); var itemDir = item.FinalDir.IsNullOrWhiteSpace() ? item.DestDir : item.FinalDir; - historyItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + historyItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false); historyItem.DownloadId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString(); historyItem.Title = item.Name; historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo); diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs index df47e136d..becc142d2 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs @@ -73,7 +73,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic var historyItem = new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = GetDownloadClientId(file), Title = title, diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index b308f06b2..23f2564df 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -229,7 +229,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent Category = torrent.Category.IsNotNullOrWhiteSpace() ? torrent.Category : torrent.Label, Title = torrent.Name, TotalSize = torrent.Size, - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.MusicImportedCategory.IsNotNullOrWhiteSpace()), RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress)), RemainingTime = GetRemainingTime(torrent), SeedRatio = torrent.Ratio diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index f6c725fa6..bb149ddef 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -63,7 +63,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd } var queueItem = new DownloadClientItem(); - queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false); queueItem.DownloadId = sabQueueItem.Id; queueItem.Category = sabQueueItem.Category; queueItem.Title = sabQueueItem.Title; @@ -118,7 +118,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd var historyItem = new DownloadClientItem { - DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this), + DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false), DownloadId = sabHistoryItem.Id, Category = sabHistoryItem.Category, Title = sabHistoryItem.Title, diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index 208b0cd00..89d2eaa77 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -72,7 +72,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission item.Category = Settings.MusicCategory; item.Title = torrent.Name; - item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false); item.OutputPath = GetOutputPath(outputPath, torrent); item.TotalSize = torrent.TotalSize; diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index 90e38f736..de5e3044a 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -143,7 +143,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent } var item = new DownloadClientItem(); - item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.MusicImportedCategory.IsNotNullOrWhiteSpace()); item.Title = torrent.Name; item.DownloadId = torrent.Hash; item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path)); diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs index 134d342de..60cf79c5f 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs @@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent item.Title = torrent.Name; item.TotalSize = torrent.Size; item.Category = torrent.Label; - item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this); + item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.MusicImportedCategory.IsNotNullOrWhiteSpace()); item.RemainingSize = torrent.Remaining; item.SeedRatio = torrent.Ratio; diff --git a/src/NzbDrone.Core/Download/DownloadClientItem.cs b/src/NzbDrone.Core/Download/DownloadClientItem.cs index aa44a3acf..c32afa1e3 100644 --- a/src/NzbDrone.Core/Download/DownloadClientItem.cs +++ b/src/NzbDrone.Core/Download/DownloadClientItem.cs @@ -38,8 +38,10 @@ namespace NzbDrone.Core.Download public string Type { get; set; } public int Id { get; set; } public string Name { get; set; } + public bool HasPostImportCategory { get; set; } - public static DownloadClientItemClientInfo FromDownloadClient(DownloadClientBase downloadClient) + public static DownloadClientItemClientInfo FromDownloadClient( + DownloadClientBase downloadClient, bool hasPostImportCategory) where TSettings : IProviderConfig, new() { return new DownloadClientItemClientInfo @@ -47,7 +49,8 @@ namespace NzbDrone.Core.Download Protocol = downloadClient.Protocol, Type = downloadClient.Name, Id = downloadClient.Definition.Id, - Name = downloadClient.Definition.Name + Name = downloadClient.Definition.Name, + HasPostImportCategory = hasPostImportCategory }; } } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 03d4cec17..74babb223 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -160,6 +160,12 @@ "BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces", "BindAddressHelpTextWarning": "Requires restart to take effect", "Blocklist": "Blocklist", + "BlocklistAndSearch": "Blocklist and Search", + "BlocklistAndSearchHint": "Start a search for a replacement after blocklisting", + "BlocklistAndSearchMultipleHint": "Start searches for replacements after blocklisting", + "BlocklistMultipleOnlyHint": "Blocklist without searching for replacements", + "BlocklistOnly": "Blocklist Only", + "BlocklistOnlyHint": "Blocklist without searching for a replacement", "BlocklistRelease": "Blocklist Release", "BlocklistReleaseHelpText": "Prevents {appName} from automatically grabbing these files again", "BlocklistReleases": "Blocklist Releases", @@ -176,6 +182,9 @@ "CatalogNumber": "Catalog Number", "CertificateValidation": "Certificate Validation", "CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.", + "ChangeCategory": "Change Category", + "ChangeCategoryHint": "Changes download to the 'Post-Import Category' from Download Client", + "ChangeCategoryMultipleHint": "Changes downloads to the 'Post-Import Category' from Download Client", "ChangeFileDate": "Change File Date", "ChangeHasNotBeenSavedYet": "Change has not been saved yet", "ChmodFolder": "chmod Folder", @@ -332,6 +341,8 @@ "DiscNumber": "Disc Number", "Discography": "Discography", "DiskSpace": "Disk Space", + "DoNotBlocklist": "Do not Blocklist", + "DoNotBlocklistHint": "Remove without blocklisting", "DoNotPrefer": "Do Not Prefer", "DoNotUpgradeAutomatically": "Do not Upgrade Automatically", "Docker": "Docker", @@ -491,6 +502,10 @@ "ICalLink": "iCal Link", "IconForCutoffUnmet": "Icon for Cutoff Unmet", "IfYouDontAddAnImportListExclusionAndTheArtistHasAMetadataProfileOtherThanNoneThenThisAlbumMayBeReaddedDuringTheNextArtistRefresh": "If you don't add an import list exclusion and the artist has a metadata profile other than 'None' then this album may be re-added during the next artist refresh.", + "IgnoreDownload": "Ignore Download", + "IgnoreDownloadHint": "Stops {appName} from processing this download further", + "IgnoreDownloads": "Ignore Downloads", + "IgnoreDownloadsHint": "Stops {appName} from processing these downloads further", "Ignored": "Ignored", "IgnoredAddresses": "Ignored Addresses", "IgnoredHelpText": "The release will be rejected if it contains one or more of terms (case insensitive)", @@ -861,9 +876,15 @@ "RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history", "RemoveFilter": "Remove filter", "RemoveFromBlocklist": "Remove from blocklist", - "RemoveFromDownloadClient": "Remove From Download Client", - "RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.", + "RemoveFromDownloadClient": "Remove from Download Client", + "RemoveFromDownloadClientHint": "Removes download and file(s) from download client", "RemoveFromQueue": "Remove from queue", + "RemoveMultipleFromDownloadClientHint": "Removes downloads and files from download client", + "RemoveQueueItem": "Remove - {sourceTitle}", + "RemoveQueueItemConfirmation": "Are you sure you want to remove '{sourceTitle}' from the queue?", + "RemoveQueueItemRemovalMethod": "Removal Method", + "RemoveQueueItemRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the download and the file(s) from the download client.", + "RemoveQueueItemsRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the downloads and the files from the download client.", "RemoveSelected": "Remove Selected", "RemoveSelectedItem": "Remove Selected Item", "RemoveSelectedItemBlocklistMessageText": "Are you sure you want to remove the selected items from the blocklist?", diff --git a/src/NzbDrone.Core/Queue/Queue.cs b/src/NzbDrone.Core/Queue/Queue.cs index a3c8e69be..d05d03947 100644 --- a/src/NzbDrone.Core/Queue/Queue.cs +++ b/src/NzbDrone.Core/Queue/Queue.cs @@ -28,6 +28,7 @@ namespace NzbDrone.Core.Queue public RemoteAlbum RemoteAlbum { get; set; } public DownloadProtocol Protocol { get; set; } public string DownloadClient { get; set; } + public bool DownloadClientHasPostImportCategory { get; set; } public string Indexer { get; set; } public string OutputPath { get; set; } public string ErrorMessage { get; set; } diff --git a/src/NzbDrone.Core/Queue/QueueService.cs b/src/NzbDrone.Core/Queue/QueueService.cs index 6d0931fab..68c4389ba 100644 --- a/src/NzbDrone.Core/Queue/QueueService.cs +++ b/src/NzbDrone.Core/Queue/QueueService.cs @@ -90,7 +90,8 @@ namespace NzbDrone.Core.Queue Indexer = trackedDownload.Indexer, OutputPath = trackedDownload.DownloadItem.OutputPath.ToString(), DownloadForced = downloadForced, - Added = trackedDownload.Added + Added = trackedDownload.Added, + DownloadClientHasPostImportCategory = trackedDownload.DownloadItem.DownloadClientInfo.HasPostImportCategory }; queue.Id = HashConverter.GetHashInt31($"trackedDownload-{trackedDownload.DownloadClient}-{trackedDownload.DownloadItem.DownloadId}-album{album?.Id ?? 0}");