diff --git a/src/NzbDrone.Core.Test/MusicTests/AlbumMonitoredServiceTests/AlbumMonitoredServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/AlbumMonitoredServiceTests/AlbumMonitoredServiceFixture.cs index 3ca576a5a..3e4ccf047 100644 --- a/src/NzbDrone.Core.Test/MusicTests/AlbumMonitoredServiceTests/AlbumMonitoredServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MusicTests/AlbumMonitoredServiceTests/AlbumMonitoredServiceFixture.cs @@ -54,7 +54,7 @@ public void should_be_able_to_monitor_author_without_changing_books() Subject.SetBookMonitoredStatus(_author, null); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.IsAny()), Times.Once()); + .Verify(v => v.UpdateAuthor(It.IsAny(), It.IsAny()), Times.Once()); Mocker.GetMock() .Verify(v => v.UpdateMany(It.IsAny>()), Times.Never()); @@ -68,7 +68,7 @@ public void should_be_able_to_monitor_books_when_passed_in_author() Subject.SetBookMonitoredStatus(_author, new MonitoringOptions { Monitored = true, BooksToMonitor = booksToMonitor }); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.IsAny()), Times.Once()); + .Verify(v => v.UpdateAuthor(It.IsAny(), It.IsAny()), Times.Once()); VerifyMonitored(e => e.ForeignBookId == _books.First().ForeignBookId); VerifyNotMonitored(e => e.ForeignBookId != _books.First().ForeignBookId); diff --git a/src/NzbDrone.Core.Test/MusicTests/MoveArtistServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/MoveArtistServiceFixture.cs index a61767f7b..32587643c 100644 --- a/src/NzbDrone.Core.Test/MusicTests/MoveArtistServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MusicTests/MoveArtistServiceFixture.cs @@ -83,7 +83,7 @@ public void should_revert_author_path_on_error() ExceptionVerification.ExpectedErrors(1); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.IsAny()), Times.Once()); + .Verify(v => v.UpdateAuthor(It.IsAny(), It.IsAny()), Times.Once()); } [Test] diff --git a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs index 0a45d8d09..e000d74aa 100644 --- a/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MusicTests/RefreshArtistServiceFixture.cs @@ -113,8 +113,8 @@ private void GivenBooksForRefresh(List books) private void AllowAuthorUpdate() { Mocker.GetMock(MockBehavior.Strict) - .Setup(x => x.UpdateAuthor(It.IsAny())) - .Returns((Author a) => a); + .Setup(x => x.UpdateAuthor(It.IsAny(), It.IsAny())) + .Returns((Author a, bool b) => a); } [Test] @@ -187,7 +187,7 @@ public void should_log_error_and_delete_if_musicbrainz_id_not_found_and_author_h Subject.Execute(new RefreshAuthorCommand(_author.Id)); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.IsAny()), Times.Never()); + .Verify(v => v.UpdateAuthor(It.IsAny(), It.IsAny()), Times.Never()); Mocker.GetMock() .Verify(v => v.DeleteAuthor(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); @@ -205,7 +205,7 @@ public void should_log_error_but_not_delete_if_musicbrainz_id_not_found_and_auth Subject.Execute(new RefreshAuthorCommand(_author.Id)); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.IsAny()), Times.Never()); + .Verify(v => v.UpdateAuthor(It.IsAny(), It.IsAny()), Times.Never()); Mocker.GetMock() .Verify(v => v.DeleteAuthor(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); @@ -233,8 +233,8 @@ public void should_update_if_musicbrainz_id_changed_and_no_clash() // Make sure that the author is updated before we refresh the books Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) - .Setup(x => x.UpdateAuthor(It.IsAny())) - .Returns((Author a) => a); + .Setup(x => x.UpdateAuthor(It.IsAny(), It.IsAny())) + .Returns((Author a, bool b) => a); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) @@ -244,13 +244,13 @@ public void should_update_if_musicbrainz_id_changed_and_no_clash() // Update called twice for a move/merge Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) - .Setup(x => x.UpdateAuthor(It.IsAny())) - .Returns((Author a) => a); + .Setup(x => x.UpdateAuthor(It.IsAny(), It.IsAny())) + .Returns((Author a, bool b) => a); Subject.Execute(new RefreshAuthorCommand(_author.Id)); Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.Is(s => s.AuthorMetadataId == 100 && s.ForeignAuthorId == newAuthorInfo.ForeignAuthorId)), + .Verify(v => v.UpdateAuthor(It.Is(s => s.AuthorMetadataId == 100 && s.ForeignAuthorId == newAuthorInfo.ForeignAuthorId), It.IsAny()), Times.Exactly(2)); } @@ -293,8 +293,8 @@ public void should_merge_if_musicbrainz_id_changed_and_new_id_already_exists() Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) - .Setup(x => x.UpdateAuthor(It.Is(a => a.Id == clash.Id))) - .Returns((Author a) => a); + .Setup(x => x.UpdateAuthor(It.Is(a => a.Id == clash.Id), It.IsAny())) + .Returns((Author a, bool b) => a); Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) @@ -304,14 +304,14 @@ public void should_merge_if_musicbrainz_id_changed_and_new_id_already_exists() // Update called twice for a move/merge Mocker.GetMock(MockBehavior.Strict) .InSequence(seq) - .Setup(x => x.UpdateAuthor(It.IsAny())) - .Returns((Author a) => a); + .Setup(x => x.UpdateAuthor(It.IsAny(), It.IsAny())) + .Returns((Author a, bool b) => a); Subject.Execute(new RefreshAuthorCommand(_author.Id)); // the retained author gets updated Mocker.GetMock() - .Verify(v => v.UpdateAuthor(It.Is(s => s.Id == clash.Id)), Times.Exactly(2)); + .Verify(v => v.UpdateAuthor(It.Is(s => s.Id == clash.Id), It.IsAny()), Times.Exactly(2)); // the old one gets removed Mocker.GetMock() diff --git a/src/NzbDrone.Core/Books/Services/AuthorService.cs b/src/NzbDrone.Core/Books/Services/AuthorService.cs index 87b93d9b6..660ef090f 100644 --- a/src/NzbDrone.Core/Books/Services/AuthorService.cs +++ b/src/NzbDrone.Core/Books/Services/AuthorService.cs @@ -26,7 +26,7 @@ public interface IAuthorService List GetAllAuthors(); Dictionary> GetAllAuthorTags(); List AllForTag(int tagId); - Author UpdateAuthor(Author author); + Author UpdateAuthor(Author author, bool publishUpdatedEvent = true); List UpdateAuthors(List authors, bool useExistingRelativeFolder); Dictionary AllAuthorPaths(); bool AuthorPathExists(string folder); @@ -222,7 +222,7 @@ public void RemoveAddOptions(Author author) _authorRepository.SetFields(author, s => s.AddOptions); } - public Author UpdateAuthor(Author author) + public Author UpdateAuthor(Author author, bool publishUpdatedEvent = true) { _cache.Clear(); @@ -232,7 +232,10 @@ public Author UpdateAuthor(Author author) author.AddOptions = storedAuthor.AddOptions; var updatedAuthor = _authorRepository.Update(author); - _eventAggregator.PublishEvent(new AuthorEditedEvent(updatedAuthor, storedAuthor)); + if (publishUpdatedEvent) + { + _eventAggregator.PublishEvent(new AuthorEditedEvent(updatedAuthor, storedAuthor)); + } return updatedAuthor; } diff --git a/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs b/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs index 020903a25..99b549732 100644 --- a/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs +++ b/src/NzbDrone.Core/Books/Services/RefreshAuthorService.cs @@ -217,7 +217,7 @@ protected override Author GetEntityByForeignId(Author local) protected override void SaveEntity(Author local) { - _authorService.UpdateAuthor(local); + _authorService.UpdateAuthor(local, publishUpdatedEvent: false); } protected override void DeleteEntity(Author local, bool deleteFiles) diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index ca12a8b9a..cdb6ac2b1 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -20,11 +20,12 @@ public interface IMapCoversToLocal { void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable covers); string GetCoverPath(int entityId, MediaCoverEntity coverEntity, MediaCoverTypes coverType, string extension, int? height = null); - void EnsureBookCovers(Book book); + bool EnsureBookCovers(Book book); } public class MediaCoverService : IHandleAsync, + IHandleAsync, IHandleAsync, IHandleAsync, IMapCoversToLocal @@ -132,8 +133,9 @@ private string GetBookCoverPath(int bookId) return Path.Combine(_coverRootFolder, "Books", bookId.ToString()); } - private void EnsureAuthorCovers(Author author) + private bool EnsureAuthorCovers(Author author) { + var updated = false; var toResize = new List>(); foreach (var cover in author.Metadata.Value.Images) @@ -155,6 +157,7 @@ private void EnsureAuthorCovers(Author author) if (!alreadyExists) { DownloadCover(author, cover, serverFileHeaders.LastModified ?? DateTime.Now); + updated = true; } } catch (HttpException e) @@ -186,10 +189,13 @@ private void EnsureAuthorCovers(Author author) { _semaphore.Release(); } + + return updated; } - public void EnsureBookCovers(Book book) + public bool EnsureBookCovers(Book book) { + var updated = false; foreach (var cover in book.Editions.Value.Single(x => x.Monitored).Images.Where(e => e.CoverType == MediaCoverTypes.Cover)) { if (cover.CoverType == MediaCoverTypes.Unknown) @@ -209,6 +215,7 @@ public void EnsureBookCovers(Book book) if (!alreadyExists) { DownloadBookCover(book, cover, serverFileHeaders.LastModified ?? DateTime.Now); + updated = true; } } catch (HttpException e) @@ -224,6 +231,8 @@ public void EnsureBookCovers(Book book) _logger.Error(e, "Couldn't download media cover for {0}", book); } } + + return updated; } private void DownloadCover(Author author, MediaCover cover, DateTime lastModified) @@ -351,15 +360,26 @@ private HttpHeader GetServerHeaders(string url) public void HandleAsync(AuthorRefreshCompleteEvent message) { - EnsureAuthorCovers(message.Author); + var updated = EnsureAuthorCovers(message.Author); var books = _bookService.GetBooksByAuthor(message.Author.Id); + foreach (var book in books) { - EnsureBookCovers(book); + updated |= EnsureBookCovers(book); } - _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Author)); + _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Author, updated)); + } + + public void HandleAsync(BookAddedEvent message) + { + if (message.DoRefresh) + { + var updated = EnsureBookCovers(message.Book); + + _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Book, updated)); + } } public void HandleAsync(AuthorDeletedEvent message) diff --git a/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs b/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs index 4736e84c5..780fbfb7d 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoversUpdatedEvent.cs @@ -7,13 +7,15 @@ public class MediaCoversUpdatedEvent : IEvent { public Author Author { get; set; } public Book Book { get; set; } + public bool Updated { get; set; } - public MediaCoversUpdatedEvent(Author author) + public MediaCoversUpdatedEvent(Author author, bool updated) { Author = author; + Updated = updated; } - public MediaCoversUpdatedEvent(Book book) + public MediaCoversUpdatedEvent(Book book, bool updated) { Book = book; }