mirror of
https://github.com/Readarr/Readarr
synced 2026-02-16 11:43:58 +01:00
Fixed: Errors loading queue after episodes in series are removed
Closes #3565
This commit is contained in:
parent
16fcba02ba
commit
7b0802cfd6
5 changed files with 170 additions and 11 deletions
|
|
@ -129,12 +129,124 @@ public void should_unmap_tracked_download_if_book_deleted()
|
|||
.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 @@ public class BookInfoRefreshedEvent : IEvent
|
|||
{
|
||||
public Author Author { get; set; }
|
||||
public ReadOnlyCollection<Book> Added { get; private set; }
|
||||
public ReadOnlyCollection<Book> Updated { get; private set; }
|
||||
|
||||
public BookInfoRefreshedEvent(Author author, IList<Book> added, IList<Book> updated)
|
||||
public ReadOnlyCollection<Book> Updated { get; private set; }
|
||||
public ReadOnlyCollection<Book> Removed { get; private set; }
|
||||
|
||||
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 @@ protected override void PublishRefreshCompleteEvent(Author 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)
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ protected virtual void PublishRefreshCompleteEvent(TEntity entity)
|
|||
{
|
||||
}
|
||||
|
||||
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 @@ protected bool SortChildren(TEntity entity, List<TChild> remoteChildren, Author
|
|||
// 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 @@
|
|||
|
||||
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 @@ public interface ITrackedDownloadService : IHandle<BookDeletedEvent>
|
|||
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 @@ private void LogItemChange(TrackedDownload trackedDownload, DownloadClientItem e
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType
|
|||
}
|
||||
}
|
||||
|
||||
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…
Reference in a new issue