This commit is contained in:
optimous 2025-06-27 15:56:40 -07:00 committed by GitHub
commit 3fccf1197f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 59 additions and 30 deletions

View file

@ -54,7 +54,7 @@ public void should_be_able_to_monitor_author_without_changing_books()
Subject.SetBookMonitoredStatus(_author, null);
Mocker.GetMock<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.IsAny<Author>()), Times.Once());
.Verify(v => v.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()), Times.Once());
Mocker.GetMock<IBookService>()
.Verify(v => v.UpdateMany(It.IsAny<List<Book>>()), 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<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.IsAny<Author>()), Times.Once());
.Verify(v => v.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()), Times.Once());
VerifyMonitored(e => e.ForeignBookId == _books.First().ForeignBookId);
VerifyNotMonitored(e => e.ForeignBookId != _books.First().ForeignBookId);

View file

@ -83,7 +83,7 @@ public void should_revert_author_path_on_error()
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.IsAny<Author>()), Times.Once());
.Verify(v => v.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()), Times.Once());
}
[Test]

View file

@ -113,8 +113,8 @@ private void GivenBooksForRefresh(List<Book> books)
private void AllowAuthorUpdate()
{
Mocker.GetMock<IAuthorService>(MockBehavior.Strict)
.Setup(x => x.UpdateAuthor(It.IsAny<Author>()))
.Returns((Author a) => a);
.Setup(x => x.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()))
.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<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.IsAny<Author>()), Times.Never());
.Verify(v => v.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()), Times.Never());
Mocker.GetMock<IAuthorService>()
.Verify(v => v.DeleteAuthor(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()), 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<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.IsAny<Author>()), Times.Never());
.Verify(v => v.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()), Times.Never());
Mocker.GetMock<IAuthorService>()
.Verify(v => v.DeleteAuthor(It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<bool>()), 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<IAuthorService>(MockBehavior.Strict)
.InSequence(seq)
.Setup(x => x.UpdateAuthor(It.IsAny<Author>()))
.Returns((Author a) => a);
.Setup(x => x.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()))
.Returns((Author a, bool b) => a);
Mocker.GetMock<IBookService>(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<IAuthorService>(MockBehavior.Strict)
.InSequence(seq)
.Setup(x => x.UpdateAuthor(It.IsAny<Author>()))
.Returns((Author a) => a);
.Setup(x => x.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()))
.Returns((Author a, bool b) => a);
Subject.Execute(new RefreshAuthorCommand(_author.Id));
Mocker.GetMock<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.Is<Author>(s => s.AuthorMetadataId == 100 && s.ForeignAuthorId == newAuthorInfo.ForeignAuthorId)),
.Verify(v => v.UpdateAuthor(It.Is<Author>(s => s.AuthorMetadataId == 100 && s.ForeignAuthorId == newAuthorInfo.ForeignAuthorId), It.IsAny<bool>()),
Times.Exactly(2));
}
@ -293,8 +293,8 @@ public void should_merge_if_musicbrainz_id_changed_and_new_id_already_exists()
Mocker.GetMock<IAuthorService>(MockBehavior.Strict)
.InSequence(seq)
.Setup(x => x.UpdateAuthor(It.Is<Author>(a => a.Id == clash.Id)))
.Returns((Author a) => a);
.Setup(x => x.UpdateAuthor(It.Is<Author>(a => a.Id == clash.Id), It.IsAny<bool>()))
.Returns((Author a, bool b) => a);
Mocker.GetMock<IBookService>(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<IAuthorService>(MockBehavior.Strict)
.InSequence(seq)
.Setup(x => x.UpdateAuthor(It.IsAny<Author>()))
.Returns((Author a) => a);
.Setup(x => x.UpdateAuthor(It.IsAny<Author>(), It.IsAny<bool>()))
.Returns((Author a, bool b) => a);
Subject.Execute(new RefreshAuthorCommand(_author.Id));
// the retained author gets updated
Mocker.GetMock<IAuthorService>()
.Verify(v => v.UpdateAuthor(It.Is<Author>(s => s.Id == clash.Id)), Times.Exactly(2));
.Verify(v => v.UpdateAuthor(It.Is<Author>(s => s.Id == clash.Id), It.IsAny<bool>()), Times.Exactly(2));
// the old one gets removed
Mocker.GetMock<IAuthorService>()

View file

@ -26,7 +26,7 @@ public interface IAuthorService
List<Author> GetAllAuthors();
Dictionary<int, List<int>> GetAllAuthorTags();
List<Author> AllForTag(int tagId);
Author UpdateAuthor(Author author);
Author UpdateAuthor(Author author, bool publishUpdatedEvent = true);
List<Author> UpdateAuthors(List<Author> authors, bool useExistingRelativeFolder);
Dictionary<int, string> 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;
}

View file

@ -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)

View file

@ -20,11 +20,12 @@ public interface IMapCoversToLocal
{
void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable<MediaCover> 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<AuthorRefreshCompleteEvent>,
IHandleAsync<BookAddedEvent>,
IHandleAsync<AuthorDeletedEvent>,
IHandleAsync<BookDeletedEvent>,
IMapCoversToLocal
@ -135,8 +136,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<Tuple<MediaCover, bool>>();
foreach (var cover in author.Metadata.Value.Images)
@ -158,6 +160,7 @@ private void EnsureAuthorCovers(Author author)
if (!alreadyExists)
{
DownloadCover(author, cover, serverFileHeaders.LastModified ?? DateTime.Now);
updated = true;
}
}
catch (HttpException e)
@ -189,10 +192,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)
@ -212,6 +218,7 @@ public void EnsureBookCovers(Book book)
if (!alreadyExists)
{
DownloadBookCover(book, cover, serverFileHeaders.LastModified ?? DateTime.Now);
updated = true;
}
}
catch (HttpException e)
@ -227,6 +234,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)
@ -354,15 +363,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)

View file

@ -7,15 +7,18 @@ 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;
Updated = updated;
}
}
}

View file

@ -286,7 +286,10 @@ public void Handle(AuthorRenamedEvent message)
[NonAction]
public void Handle(MediaCoversUpdatedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, GetAuthorResource(message.Author));
if (message.Updated)
{
BroadcastResourceChange(ModelAction.Updated, GetAuthorResource(message.Author));
}
}
}
}