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));
// 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
var trackedDownloads = Subject.GetTrackedDownloads();
trackedDownloads.Should().HaveCount(1);
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 ReadOnlyCollection<Book> Added { 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;
Added = new ReadOnlyCollection<Book>(added);
Updated = new ReadOnlyCollection<Book>(updated);
Removed = new ReadOnlyCollection<Book>(removed);
}
}
}

@ -297,9 +297,9 @@ namespace NzbDrone.Core.Books
_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)

@ -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
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;
}
}

@ -14,7 +14,7 @@ using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Download.TrackedDownloads
{
public interface ITrackedDownloadService : IHandle<BookDeletedEvent>
public interface ITrackedDownloadService
{
TrackedDownload Find(string downloadId);
void StopTracking(string downloadId);
@ -24,7 +24,9 @@ namespace NzbDrone.Core.Download.TrackedDownloads
void UpdateTrackable(List<TrackedDownload> trackedDownloads);
}
public class TrackedDownloadService : ITrackedDownloadService
public class TrackedDownloadService : ITrackedDownloadService,
IHandle<BookInfoRefreshedEvent>,
IHandle<AuthorDeletedEvent>
{
private readonly IParsingService _parsingService;
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)
{
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