Fixed: Blocklisting pending releases

Closes #4598
This commit is contained in:
Mark McDowall 2021-08-01 14:36:16 -07:00
parent ead1371846
commit 3f60e28c42
7 changed files with 102 additions and 46 deletions

View File

@ -279,6 +279,17 @@ class Queue extends Component {
return !!(item && item.seriesId && item.episodeId); return !!(item && item.seriesId && item.episodeId);
}) })
)} )}
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} onRemovePress={this.onRemoveSelectedConfirmed}
onModalClose={this.onConfirmRemoveModalClose} onModalClose={this.onConfirmRemoveModalClose}
/> />

View File

@ -377,6 +377,7 @@ class QueueRow extends Component {
isOpen={isRemoveQueueItemModalOpen} isOpen={isRemoveQueueItemModalOpen}
sourceTitle={title} sourceTitle={title}
canIgnore={!!series} canIgnore={!!series}
isPending={isPending}
onRemovePress={this.onRemoveQueueItemModalConfirmed} onRemovePress={this.onRemoveQueueItemModalConfirmed}
onModalClose={this.onRemoveQueueItemModalClose} onModalClose={this.onRemoveQueueItemModalClose}
/> />

View File

@ -65,7 +65,8 @@ class RemoveQueueItemModal extends Component {
const { const {
isOpen, isOpen,
sourceTitle, sourceTitle,
canIgnore canIgnore,
isPending
} = this.props; } = this.props;
const { remove, blocklist } = this.state; const { remove, blocklist } = this.state;
@ -88,18 +89,22 @@ class RemoveQueueItemModal extends Component {
Are you sure you want to remove '{sourceTitle}' from the queue? Are you sure you want to remove '{sourceTitle}' from the queue?
</div> </div>
<FormGroup> {
<FormLabel>Remove From Download Client</FormLabel> isPending ?
null :
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="remove" name="remove"
value={remove} value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client." helpTextWarning="Removing will remove the download and the file(s) from the download client."
isDisabled={!canIgnore} isDisabled={!canIgnore}
onChange={this.onRemoveChange} onChange={this.onRemoveChange}
/> />
</FormGroup> </FormGroup>
}
<FormGroup> <FormGroup>
<FormLabel>Add Release To Blocklist</FormLabel> <FormLabel>Add Release To Blocklist</FormLabel>
@ -137,6 +142,7 @@ RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
canIgnore: PropTypes.bool.isRequired, canIgnore: PropTypes.bool.isRequired,
isPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired, onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };

View File

@ -66,7 +66,8 @@ class RemoveQueueItemsModal extends Component {
const { const {
isOpen, isOpen,
selectedCount, selectedCount,
canIgnore canIgnore,
allPending
} = this.props; } = this.props;
const { remove, blocklist } = this.state; const { remove, blocklist } = this.state;
@ -89,18 +90,22 @@ class RemoveQueueItemsModal extends Component {
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue? Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
</div> </div>
<FormGroup> {
<FormLabel>Remove From Download Client</FormLabel> allPending ?
null :
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="remove" name="remove"
value={remove} value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client." helpTextWarning="Removing will remove the download and the file(s) from the download client."
isDisabled={!canIgnore} isDisabled={!canIgnore}
onChange={this.onRemoveChange} onChange={this.onRemoveChange}
/> />
</FormGroup> </FormGroup>
}
<FormGroup> <FormGroup>
<FormLabel> <FormLabel>
@ -140,6 +145,7 @@ RemoveQueueItemsModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
selectedCount: PropTypes.number.isRequired, selectedCount: PropTypes.number.isRequired,
canIgnore: PropTypes.bool.isRequired, canIgnore: PropTypes.bool.isRequired,
allPending: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired, onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };

View File

@ -16,6 +16,7 @@ namespace NzbDrone.Core.Blocklisting
{ {
bool Blocklisted(int seriesId, ReleaseInfo release); bool Blocklisted(int seriesId, ReleaseInfo release);
PagingSpec<Blocklist> Paged(PagingSpec<Blocklist> pagingSpec); PagingSpec<Blocklist> Paged(PagingSpec<Blocklist> pagingSpec);
void Block(RemoteEpisode remoteEpisode, string message);
void Delete(int id); void Delete(int id);
void Delete(List<int> ids); void Delete(List<int> ids);
} }
@ -61,6 +62,32 @@ namespace NzbDrone.Core.Blocklisting
return _blocklistRepository.GetPaged(pagingSpec); return _blocklistRepository.GetPaged(pagingSpec);
} }
public void Block(RemoteEpisode remoteEpisode, string message)
{
var blocklist = new Blocklist
{
SeriesId = remoteEpisode.Series.Id,
EpisodeIds = remoteEpisode.Episodes.Select(e => e.Id).ToList(),
SourceTitle = remoteEpisode.Release.Title,
Quality = remoteEpisode.ParsedEpisodeInfo.Quality,
Date = DateTime.UtcNow,
PublishedDate = remoteEpisode.Release.PublishDate,
Size = remoteEpisode.Release.Size,
Indexer = remoteEpisode.Release.Indexer,
Protocol = remoteEpisode.Release.DownloadProtocol,
Message = message,
Language = remoteEpisode.ParsedEpisodeInfo.Language
};
if (remoteEpisode.Release is TorrentInfo torrentRelease)
{
blocklist.TorrentInfoHash = torrentRelease.InfoHash;
}
_blocklistRepository.Insert(blocklist);
}
public void Delete(int id) public void Delete(int id)
{ {
_blocklistRepository.Delete(id); _blocklistRepository.Delete(id);

View File

@ -130,13 +130,6 @@ namespace NzbDrone.Core.Download.Pending
} }
} }
private ILookup<int, PendingRelease> CreateEpisodeLookup(IEnumerable<PendingRelease> alreadyPending)
{
return alreadyPending.SelectMany(v => v.RemoteEpisode.Episodes
.Select(d => new { Episode = d, PendingRelease = v }))
.ToLookup(v => v.Episode.Id, v => v.PendingRelease);
}
public List<ReleaseInfo> GetPending() public List<ReleaseInfo> GetPending()
{ {
var releases = _repository.All().Select(p => p.Release).ToList(); var releases = _repository.All().Select(p => p.Release).ToList();
@ -149,13 +142,6 @@ namespace NzbDrone.Core.Download.Pending
return releases; return releases;
} }
private List<ReleaseInfo> FilterBlockedIndexers(List<ReleaseInfo> releases)
{
var blockedIndexers = new HashSet<int>(_indexerStatusService.GetBlockedProviders().Select(v => v.ProviderId));
return releases.Where(release => !blockedIndexers.Contains(release.IndexerId)).ToList();
}
public List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId) public List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId)
{ {
return IncludeRemoteEpisodes(_repository.AllBySeriesId(seriesId)).Select(v => v.RemoteEpisode).ToList(); return IncludeRemoteEpisodes(_repository.AllBySeriesId(seriesId)).Select(v => v.RemoteEpisode).ToList();
@ -252,6 +238,20 @@ namespace NzbDrone.Core.Download.Pending
.FirstOrDefault(); .FirstOrDefault();
} }
private ILookup<int, PendingRelease> CreateEpisodeLookup(IEnumerable<PendingRelease> alreadyPending)
{
return alreadyPending.SelectMany(v => v.RemoteEpisode.Episodes
.Select(d => new { Episode = d, PendingRelease = v }))
.ToLookup(v => v.Episode.Id, v => v.PendingRelease);
}
private List<ReleaseInfo> FilterBlockedIndexers(List<ReleaseInfo> releases)
{
var blockedIndexers = new HashSet<int>(_indexerStatusService.GetBlockedProviders().Select(v => v.ProviderId));
return releases.Where(release => !blockedIndexers.Contains(release.IndexerId)).ToList();
}
private List<PendingRelease> GetPendingReleases() private List<PendingRelease> GetPendingReleases()
{ {
return IncludeRemoteEpisodes(_repository.All().ToList()); return IncludeRemoteEpisodes(_repository.All().ToList());
@ -345,13 +345,6 @@ namespace NzbDrone.Core.Download.Pending
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent()); _eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());
} }
private static Func<PendingRelease, bool> MatchingReleasePredicate(ReleaseInfo release)
{
return p => p.Title == release.Title &&
p.Release.PublishDate == release.PublishDate &&
p.Release.Indexer == release.Indexer;
}
private int GetDelay(RemoteEpisode remoteEpisode) private int GetDelay(RemoteEpisode remoteEpisode)
{ {
var delayProfile = _delayProfileService.AllForTags(remoteEpisode.Series.Tags).OrderBy(d => d.Order).First(); var delayProfile = _delayProfileService.AllForTags(remoteEpisode.Series.Tags).OrderBy(d => d.Order).First();
@ -446,5 +439,12 @@ namespace NzbDrone.Core.Download.Pending
{ {
RemoveRejected(message.ProcessedDecisions.Rejected); RemoveRejected(message.ProcessedDecisions.Rejected);
} }
private static Func<PendingRelease, bool> MatchingReleasePredicate(ReleaseInfo release)
{
return p => p.Title == release.Title &&
p.Release.PublishDate == release.PublishDate &&
p.Release.Indexer == release.Indexer;
}
} }
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy; using Nancy;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
@ -21,6 +22,7 @@ namespace Sonarr.Api.V3.Queue
private readonly IProvideDownloadClient _downloadClientProvider; private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IPendingReleaseService _pendingReleaseService; private readonly IPendingReleaseService _pendingReleaseService;
private readonly IDownloadService _downloadService; private readonly IDownloadService _downloadService;
private readonly IBlocklistService _blocklistService;
public QueueActionModule(IQueueService queueService, public QueueActionModule(IQueueService queueService,
ITrackedDownloadService trackedDownloadService, ITrackedDownloadService trackedDownloadService,
@ -28,7 +30,8 @@ namespace Sonarr.Api.V3.Queue
IIgnoredDownloadService ignoredDownloadService, IIgnoredDownloadService ignoredDownloadService,
IProvideDownloadClient downloadClientProvider, IProvideDownloadClient downloadClientProvider,
IPendingReleaseService pendingReleaseService, IPendingReleaseService pendingReleaseService,
IDownloadService downloadService) IDownloadService downloadService,
IBlocklistService blocklistService)
{ {
_queueService = queueService; _queueService = queueService;
_trackedDownloadService = trackedDownloadService; _trackedDownloadService = trackedDownloadService;
@ -37,6 +40,7 @@ namespace Sonarr.Api.V3.Queue
_downloadClientProvider = downloadClientProvider; _downloadClientProvider = downloadClientProvider;
_pendingReleaseService = pendingReleaseService; _pendingReleaseService = pendingReleaseService;
_downloadService = downloadService; _downloadService = downloadService;
_blocklistService = blocklistService;
Post(@"/grab/(?<id>[\d]{1,10})", x => Grab((int)x.Id)); Post(@"/grab/(?<id>[\d]{1,10})", x => Grab((int)x.Id));
Post("/grab/bulk", x => Grab()); Post("/grab/bulk", x => Grab());
@ -126,6 +130,7 @@ namespace Sonarr.Api.V3.Queue
if (pendingRelease != null) if (pendingRelease != null)
{ {
_blocklistService.Block(pendingRelease.RemoteEpisode, "Pending release manually blocklisted");
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id); _pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
return null; return null;