Fixed: Errors loading queue after episodes in series are removed

Closes #3565
pull/1392/head
Mark McDowall 4 years ago committed by ta264
parent 16fcba02ba
commit 7b0802cfd6

@ -129,12 +129,124 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
.Returns(default(RemoteBook)); .Returns(default(RemoteBook));
// handle deletion event // handle deletion event
Subject.Handle(new BookDeletedEvent(remoteBook.Books.First(), false, false)); Subject.Handle(new BookInfoRefreshedEvent(remoteBook.Author, new List<Book>(), new List<Book>(), remoteBook.Books));
// verify download has null remote book // verify download has null remote book
var trackedDownloads = Subject.GetTrackedDownloads(); var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1); trackedDownloads.Should().HaveCount(1);
trackedDownloads.First().RemoteBook.Should().BeNull(); trackedDownloads.First().RemoteBook.Should().BeNull();
} }
[Test]
public void should_not_throw_when_processing_deleted_episodes()
{
GivenDownloadHistory();
var remoteEpisode = new RemoteBook
{
Author = new Author() { Id = 5 },
Books = new List<Book> { new Book { Id = 4 } },
ParsedBookInfo = new ParsedBookInfo()
{
BookTitle = "TV Series"
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedBookInfo>(), It.IsAny<int>(), It.IsAny<List<int>>()))
.Returns(default(RemoteBook));
Mocker.GetMock<IHistoryService>()
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
.Returns(new List<EntityHistory>());
var client = new DownloadClientDefinition()
{
Id = 1,
Protocol = DownloadProtocol.Torrent
};
var item = new DownloadClientItem()
{
Title = "TV Series - S01E01",
DownloadId = "12345",
DownloadClientInfo = new DownloadClientItemClientInfo
{
Id = 1,
Type = "Blackhole",
Name = "Blackhole Client",
Protocol = DownloadProtocol.Torrent
}
};
Subject.TrackDownload(client, item);
Subject.GetTrackedDownloads().Should().HaveCount(1);
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedBookInfo>(), It.IsAny<int>(), It.IsAny<List<int>>()))
.Returns(default(RemoteBook));
Subject.Handle(new BookInfoRefreshedEvent(remoteEpisode.Author, new List<Book>(), new List<Book>(), remoteEpisode.Books));
var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1);
trackedDownloads.First().RemoteBook.Should().BeNull();
}
[Test]
public void should_not_throw_when_processing_deleted_series()
{
GivenDownloadHistory();
var remoteEpisode = new RemoteBook
{
Author = new Author() { Id = 5 },
Books = new List<Book> { new Book { Id = 4 } },
ParsedBookInfo = new ParsedBookInfo()
{
BookTitle = "TV Series",
}
};
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedBookInfo>(), It.IsAny<int>(), It.IsAny<List<int>>()))
.Returns(default(RemoteBook));
Mocker.GetMock<IHistoryService>()
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
.Returns(new List<EntityHistory>());
var client = new DownloadClientDefinition()
{
Id = 1,
Protocol = DownloadProtocol.Torrent
};
var item = new DownloadClientItem()
{
Title = "TV Series - S01E01",
DownloadId = "12345",
DownloadClientInfo = new DownloadClientItemClientInfo
{
Id = 1,
Type = "Blackhole",
Name = "Blackhole Client",
Protocol = DownloadProtocol.Torrent
}
};
Subject.TrackDownload(client, item);
Subject.GetTrackedDownloads().Should().HaveCount(1);
Mocker.GetMock<IParsingService>()
.Setup(s => s.Map(It.IsAny<ParsedBookInfo>(), It.IsAny<int>(), It.IsAny<List<int>>()))
.Returns(default(RemoteBook));
Subject.Handle(new AuthorDeletedEvent(remoteEpisode.Author, true, true));
var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1);
trackedDownloads.First().RemoteBook.Should().BeNull();
}
} }
} }

@ -8,13 +8,16 @@ namespace NzbDrone.Core.Books.Events
{ {
public Author Author { get; set; } public Author Author { get; set; }
public ReadOnlyCollection<Book> Added { get; private set; } public ReadOnlyCollection<Book> Added { get; private set; }
public ReadOnlyCollection<Book> Updated { get; private set; } public ReadOnlyCollection<Book> Updated { get; private set; }
public ReadOnlyCollection<Book> Removed { get; private set; }
public BookInfoRefreshedEvent(Author author, IList<Book> added, IList<Book> updated) public BookInfoRefreshedEvent(Author author, IList<Book> added, IList<Book> updated, IList<Book> removed)
{ {
Author = author; Author = author;
Added = new ReadOnlyCollection<Book>(added); Added = new ReadOnlyCollection<Book>(added);
Updated = new ReadOnlyCollection<Book>(updated); Updated = new ReadOnlyCollection<Book>(updated);
Removed = new ReadOnlyCollection<Book>(removed);
} }
} }
} }

@ -297,9 +297,9 @@ namespace NzbDrone.Core.Books
_eventAggregator.PublishEvent(new AuthorRefreshCompleteEvent(entity)); _eventAggregator.PublishEvent(new AuthorRefreshCompleteEvent(entity));
} }
protected override void PublishChildrenUpdatedEvent(Author entity, List<Book> newChildren, List<Book> updateChildren) protected override void PublishChildrenUpdatedEvent(Author entity, List<Book> newChildren, List<Book> updateChildren, List<Book> deleteChildren)
{ {
_eventAggregator.PublishEvent(new BookInfoRefreshedEvent(entity, newChildren, updateChildren)); _eventAggregator.PublishEvent(new BookInfoRefreshedEvent(entity, newChildren, updateChildren, deleteChildren));
} }
private void Rescan(List<int> authorIds, bool isNew, CommandTrigger trigger, bool infoUpdated) private void Rescan(List<int> authorIds, bool isNew, CommandTrigger trigger, bool infoUpdated)

@ -109,7 +109,7 @@ namespace NzbDrone.Core.Books
{ {
} }
protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List<TChild> newChildren, List<TChild> updateChildren) protected virtual void PublishChildrenUpdatedEvent(TEntity entity, List<TChild> newChildren, List<TChild> updateChildren, List<TChild> deleteChildren)
{ {
} }
@ -290,7 +290,7 @@ namespace NzbDrone.Core.Books
// now trigger updates // now trigger updates
var updated = RefreshChildren(sortedChildren, remoteChildren, remoteData, forceChildRefresh, forceUpdateFileTags, lastUpdate); var updated = RefreshChildren(sortedChildren, remoteChildren, remoteData, forceChildRefresh, forceUpdateFileTags, lastUpdate);
PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated); PublishChildrenUpdatedEvent(entity, sortedChildren.Added, sortedChildren.Updated, sortedChildren.Deleted);
return updated; return updated;
} }
} }

@ -14,7 +14,7 @@ using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Download.TrackedDownloads namespace NzbDrone.Core.Download.TrackedDownloads
{ {
public interface ITrackedDownloadService : IHandle<BookDeletedEvent> public interface ITrackedDownloadService
{ {
TrackedDownload Find(string downloadId); TrackedDownload Find(string downloadId);
void StopTracking(string downloadId); void StopTracking(string downloadId);
@ -24,7 +24,9 @@ namespace NzbDrone.Core.Download.TrackedDownloads
void UpdateTrackable(List<TrackedDownload> trackedDownloads); void UpdateTrackable(List<TrackedDownload> trackedDownloads);
} }
public class TrackedDownloadService : ITrackedDownloadService public class TrackedDownloadService : ITrackedDownloadService,
IHandle<BookInfoRefreshedEvent>,
IHandle<AuthorDeletedEvent>
{ {
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
@ -238,6 +240,13 @@ namespace NzbDrone.Core.Download.TrackedDownloads
} }
} }
private void UpdateCachedItem(TrackedDownload trackedDownload)
{
var parsedEpisodeInfo = Parser.Parser.ParseBookTitle(trackedDownload.DownloadItem.Title);
trackedDownload.RemoteBook = parsedEpisodeInfo == null ? null : _parsingService.Map(parsedEpisodeInfo, 0, new[] { 0 });
}
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType) private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
{ {
switch (eventType) switch (eventType)
@ -255,9 +264,44 @@ namespace NzbDrone.Core.Download.TrackedDownloads
} }
} }
public void Handle(BookDeletedEvent message) public void Handle(BookInfoRefreshedEvent message)
{ {
UpdateBookCache(message.Book.Id); var needsToUpdate = false;
foreach (var episode in message.Removed)
{
var cachedItems = _cache.Values.Where(t =>
t.RemoteBook?.Books != null &&
t.RemoteBook.Books.Any(e => e.Id == episode.Id))
.ToList();
if (cachedItems.Any())
{
needsToUpdate = true;
}
cachedItems.ForEach(UpdateCachedItem);
}
if (needsToUpdate)
{
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
}
}
public void Handle(AuthorDeletedEvent message)
{
var cachedItems = _cache.Values.Where(t =>
t.RemoteBook?.Author != null &&
t.RemoteBook.Author.Id == message.Author.Id)
.ToList();
if (cachedItems.Any())
{
cachedItems.ForEach(UpdateCachedItem);
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
}
} }
} }
} }

Loading…
Cancel
Save