Fixed: "Specific Book" setting for readarr list import (#1200)

pull/1202/head
Thomas White 4 years ago committed by GitHub
parent 109fd22d1f
commit 466876da62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -88,6 +88,19 @@ namespace NzbDrone.Core.Test.ImportListTests
_importListReports.First().EditionGoodreadsId = "1234"; _importListReports.First().EditionGoodreadsId = "1234";
} }
private void WithSecondBook()
{
var importListItem2 = new ImportListItemInfo
{
Author = "Linkin Park",
AuthorGoodreadsId = "f59c5520-5f46-4d2c-b2c4-822eabf53419",
Book = "Meteora 2",
EditionGoodreadsId = "5678",
BookGoodreadsId = "8765"
};
_importListReports.Add(importListItem2);
}
private void WithExistingAuthor() private void WithExistingAuthor()
{ {
Mocker.GetMock<IAuthorService>() Mocker.GetMock<IAuthorService>()
@ -290,5 +303,26 @@ namespace NzbDrone.Core.Test.ImportListTests
Mocker.GetMock<IAddBookService>() Mocker.GetMock<IAddBookService>()
.Verify(v => v.AddBooks(It.Is<List<Book>>(t => t.Count == 0), false)); .Verify(v => v.AddBooks(It.Is<List<Book>>(t => t.Count == 0), false));
} }
[TestCase(ImportListMonitorType.None, 0, false)]
[TestCase(ImportListMonitorType.SpecificBook, 2, true)]
[TestCase(ImportListMonitorType.EntireAuthor, 0, true)]
public void should_add_two_books(ImportListMonitorType monitor, int expectedBooksMonitored, bool expectedAuthorMonitored)
{
WithBook();
WithBookId();
WithSecondBook();
WithAuthorId();
WithMonitorType(monitor);
Subject.Execute(new ImportListSyncCommand());
Mocker.GetMock<IAddBookService>()
.Verify(v => v.AddBooks(It.Is<List<Book>>(t => t.Count == 2), false));
Mocker.GetMock<IAddAuthorService>()
.Verify(v => v.AddAuthors(It.Is<List<Author>>(t => t.Count == 1 &&
t.First().AddOptions.BooksToMonitor.Count == expectedBooksMonitored &&
t.First().Monitored == expectedAuthorMonitored), false));
}
} }
} }

@ -41,9 +41,9 @@ namespace NzbDrone.Core.Books
if (monitoredBooks.Any()) if (monitoredBooks.Any())
{ {
ToggleBooksMonitoredState( ToggleBooksMonitoredState(
books.Where(s => monitoredBooks.Any(t => t == s.ForeignBookId)), true); books.Where(s => monitoredBooks.Contains(s.ForeignBookId)), true);
ToggleBooksMonitoredState( ToggleBooksMonitoredState(
books.Where(s => monitoredBooks.Any(t => t != s.ForeignBookId)), false); books.Where(s => !monitoredBooks.Contains(s.ForeignBookId)), false);
} }
else else
{ {

@ -99,12 +99,12 @@ namespace NzbDrone.Core.ImportLists
if (report.Book.IsNotNullOrWhiteSpace() || report.EditionGoodreadsId.IsNotNullOrWhiteSpace()) if (report.Book.IsNotNullOrWhiteSpace() || report.EditionGoodreadsId.IsNotNullOrWhiteSpace())
{ {
if (report.EditionGoodreadsId.IsNullOrWhiteSpace() || report.AuthorGoodreadsId.IsNullOrWhiteSpace()) if (report.EditionGoodreadsId.IsNullOrWhiteSpace() || report.AuthorGoodreadsId.IsNullOrWhiteSpace() || report.BookGoodreadsId.IsNullOrWhiteSpace())
{ {
MapBookReport(report); MapBookReport(report);
} }
ProcessBookReport(importList, report, listExclusions, booksToAdd); ProcessBookReport(importList, report, listExclusions, booksToAdd, authorsToAdd);
} }
else if (report.Author.IsNotNullOrWhiteSpace() || report.AuthorGoodreadsId.IsNotNullOrWhiteSpace()) else if (report.Author.IsNotNullOrWhiteSpace() || report.AuthorGoodreadsId.IsNotNullOrWhiteSpace())
{ {
@ -147,7 +147,7 @@ namespace NzbDrone.Core.ImportLists
mappedBook = _bookSearchService.SearchForNewBook(report.Book, report.Author).FirstOrDefault(); mappedBook = _bookSearchService.SearchForNewBook(report.Book, report.Author).FirstOrDefault();
} }
// Break if we are looking for an book and cant find it. This will avoid us from adding the author and possibly getting it wrong. // Break if we are looking for a book and cant find it. This will avoid us from adding the author and possibly getting it wrong.
if (mappedBook == null) if (mappedBook == null)
{ {
_logger.Trace($"Nothing found for {report.EditionGoodreadsId}"); _logger.Trace($"Nothing found for {report.EditionGoodreadsId}");
@ -159,11 +159,11 @@ namespace NzbDrone.Core.ImportLists
report.BookGoodreadsId = mappedBook.ForeignBookId; report.BookGoodreadsId = mappedBook.ForeignBookId;
report.Book = mappedBook.Title; report.Book = mappedBook.Title;
report.Author = mappedBook.AuthorMetadata?.Value?.Name; report.Author ??= mappedBook.AuthorMetadata?.Value?.Name;
report.AuthorGoodreadsId = mappedBook.AuthorMetadata?.Value?.ForeignAuthorId; report.AuthorGoodreadsId ??= mappedBook.AuthorMetadata?.Value?.ForeignAuthorId;
} }
private void ProcessBookReport(ImportListDefinition importList, ImportListItemInfo report, List<ImportListExclusion> listExclusions, List<Book> booksToAdd) private void ProcessBookReport(ImportListDefinition importList, ImportListItemInfo report, List<ImportListExclusion> listExclusions, List<Book> booksToAdd, List<Author> authorsToAdd)
{ {
if (report.EditionGoodreadsId == null) if (report.EditionGoodreadsId == null)
{ {
@ -181,13 +181,13 @@ namespace NzbDrone.Core.ImportLists
if (excludedBook != null) if (excludedBook != null)
{ {
_logger.Debug("{0} [{1}] Rejected due to list exlcusion", report.EditionGoodreadsId, report.Book); _logger.Debug("{0} [{1}] Rejected due to list exclusion", report.EditionGoodreadsId, report.Book);
return; return;
} }
if (excludedAuthor != null) if (excludedAuthor != null)
{ {
_logger.Debug("{0} [{1}] Rejected due to list exlcusion for parent author", report.EditionGoodreadsId, report.Book); _logger.Debug("{0} [{1}] Rejected due to list exclusion for parent author", report.EditionGoodreadsId, report.Book);
return; return;
} }
@ -223,19 +223,7 @@ namespace NzbDrone.Core.ImportLists
{ {
var monitored = importList.ShouldMonitor != ImportListMonitorType.None; var monitored = importList.ShouldMonitor != ImportListMonitorType.None;
var toAdd = new Book var toAddAuthor = new Author
{
ForeignBookId = report.BookGoodreadsId,
Monitored = monitored,
Editions = new List<Edition>
{
new Edition
{
ForeignEditionId = report.EditionGoodreadsId,
Monitored = true
}
},
Author = new Author
{ {
Monitored = monitored, Monitored = monitored,
RootFolderPath = importList.RootFolderPath, RootFolderPath = importList.RootFolderPath,
@ -248,16 +236,35 @@ namespace NzbDrone.Core.ImportLists
Monitored = monitored, Monitored = monitored,
Monitor = monitored ? MonitorTypes.All : MonitorTypes.None Monitor = monitored ? MonitorTypes.All : MonitorTypes.None
} }
};
if (report.AuthorGoodreadsId != null && report.Author != null)
{
toAddAuthor = ProcessAuthorReport(importList, report, listExclusions, authorsToAdd);
}
var toAdd = new Book
{
ForeignBookId = report.BookGoodreadsId,
Monitored = monitored,
Editions = new List<Edition>
{
new Edition
{
ForeignEditionId = report.EditionGoodreadsId,
Monitored = true
}
}, },
Author = toAddAuthor,
AddOptions = new AddBookOptions AddOptions = new AddBookOptions
{ {
SearchForNewBook = monitored SearchForNewBook = importList.ShouldSearch
} }
}; };
if (importList.ShouldMonitor == ImportListMonitorType.SpecificBook) if (importList.ShouldMonitor == ImportListMonitorType.SpecificBook)
{ {
toAdd.Author.Value.AddOptions.BooksToMonitor.Add(toAdd.ForeignBookId); toAddAuthor.AddOptions.BooksToMonitor.Add(toAdd.ForeignBookId);
} }
booksToAdd.Add(toAdd); booksToAdd.Add(toAdd);
@ -272,11 +279,11 @@ namespace NzbDrone.Core.ImportLists
report.Author = mappedAuthor?.Metadata.Value?.Name; report.Author = mappedAuthor?.Metadata.Value?.Name;
} }
private void ProcessAuthorReport(ImportListDefinition importList, ImportListItemInfo report, List<ImportListExclusion> listExclusions, List<Author> authorsToAdd) private Author ProcessAuthorReport(ImportListDefinition importList, ImportListItemInfo report, List<ImportListExclusion> listExclusions, List<Author> authorsToAdd)
{ {
if (report.AuthorGoodreadsId == null) if (report.AuthorGoodreadsId == null)
{ {
return; return null;
} }
// Check to see if author in DB // Check to see if author in DB
@ -285,10 +292,13 @@ namespace NzbDrone.Core.ImportLists
// Check to see if author excluded // Check to see if author excluded
var excludedAuthor = listExclusions.Where(s => s.ForeignId == report.AuthorGoodreadsId).SingleOrDefault(); var excludedAuthor = listExclusions.Where(s => s.ForeignId == report.AuthorGoodreadsId).SingleOrDefault();
// Check to see if author in import
var existingImportAuthor = authorsToAdd.Find(i => i.ForeignAuthorId == report.AuthorGoodreadsId);
if (excludedAuthor != null) if (excludedAuthor != null)
{ {
_logger.Debug("{0} [{1}] Rejected due to list exlcusion", report.AuthorGoodreadsId, report.Author); _logger.Debug("{0} [{1}] Rejected due to list exclusion", report.AuthorGoodreadsId, report.Author);
return; return null;
} }
if (existingAuthor != null) if (existingAuthor != null)
@ -301,15 +311,19 @@ namespace NzbDrone.Core.ImportLists
_authorService.UpdateAuthor(existingAuthor); _authorService.UpdateAuthor(existingAuthor);
} }
return; return existingAuthor;
} }
// Append Author if not already in DB or already on add list if (existingImportAuthor != null)
if (authorsToAdd.All(s => s.Metadata.Value.ForeignAuthorId != report.AuthorGoodreadsId))
{ {
_logger.Debug("{0} [{1}] Rejected, Author Exists in Import.", report.AuthorGoodreadsId, report.Author);
return existingImportAuthor;
}
var monitored = importList.ShouldMonitor != ImportListMonitorType.None; var monitored = importList.ShouldMonitor != ImportListMonitorType.None;
authorsToAdd.Add(new Author var toAdd = new Author
{ {
Metadata = new AuthorMetadata Metadata = new AuthorMetadata
{ {
@ -327,8 +341,11 @@ namespace NzbDrone.Core.ImportLists
Monitored = monitored, Monitored = monitored,
Monitor = monitored ? MonitorTypes.All : MonitorTypes.None Monitor = monitored ? MonitorTypes.All : MonitorTypes.None
} }
}); };
}
authorsToAdd.Add(toAdd);
return toAdd;
} }
public void Execute(ImportListSyncCommand message) public void Execute(ImportListSyncCommand message)

@ -13,6 +13,26 @@ namespace NzbDrone.Core.ImportLists.Readarr
public HashSet<int> Tags { get; set; } public HashSet<int> Tags { get; set; }
} }
public class ReadarrEdition
{
public string Title { get; set; }
public string ForeignEditionId { get; set; }
public string Overview { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }
public bool Monitored { get; set; }
}
public class ReadarrBook
{
public string Title { get; set; }
public string ForeignBookId { get; set; }
public string Overview { get; set; }
public List<MediaCover.MediaCover> Images { get; set; }
public bool Monitored { get; set; }
public ReadarrAuthor Author { get; set; }
public List<ReadarrEdition> Editions { get; set; }
}
public class ReadarrProfile public class ReadarrProfile
{ {
public string Name { get; set; } public string Name { get; set; }

@ -30,21 +30,25 @@ namespace NzbDrone.Core.ImportLists.Readarr
public override IList<ImportListItemInfo> Fetch() public override IList<ImportListItemInfo> Fetch()
{ {
var authors = new List<ImportListItemInfo>(); var authorsAndBooks = new List<ImportListItemInfo>();
try try
{ {
var remoteAuthors = _readarrV1Proxy.GetAuthors(Settings); var remoteBooks = _readarrV1Proxy.GetBooks(Settings);
foreach (var remoteAuthor in remoteAuthors) foreach (var remoteBook in remoteBooks)
{ {
if ((!Settings.ProfileIds.Any() || Settings.ProfileIds.Contains(remoteAuthor.QualityProfileId)) && if ((!Settings.ProfileIds.Any() || Settings.ProfileIds.Contains(remoteBook.Author.QualityProfileId)) &&
(!Settings.TagIds.Any() || Settings.TagIds.Any(x => remoteAuthor.Tags.Any(y => y == x)))) (!Settings.TagIds.Any() || Settings.TagIds.Any(x => remoteBook.Author.Tags.Any(y => y == x))) &&
remoteBook.Monitored && remoteBook.Author.Monitored)
{ {
authors.Add(new ImportListItemInfo authorsAndBooks.Add(new ImportListItemInfo
{ {
AuthorGoodreadsId = remoteAuthor.ForeignAuthorId, BookGoodreadsId = remoteBook.ForeignBookId,
Author = remoteAuthor.AuthorName Book = remoteBook.Title,
EditionGoodreadsId = remoteBook.Editions.Single(x => x.Monitored).ForeignEditionId,
Author = remoteBook.Author.AuthorName,
AuthorGoodreadsId = remoteBook.Author.ForeignAuthorId
}); });
} }
} }
@ -56,7 +60,7 @@ namespace NzbDrone.Core.ImportLists.Readarr
_importListStatusService.RecordFailure(Definition.Id); _importListStatusService.RecordFailure(Definition.Id);
} }
return CleanupListItems(authors); return CleanupListItems(authorsAndBooks);
} }
public override object RequestAction(string action, IDictionary<string, string> query) public override object RequestAction(string action, IDictionary<string, string> query)

@ -12,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.Readarr
public interface IReadarrV1Proxy public interface IReadarrV1Proxy
{ {
List<ReadarrAuthor> GetAuthors(ReadarrSettings settings); List<ReadarrAuthor> GetAuthors(ReadarrSettings settings);
List<ReadarrBook> GetBooks(ReadarrSettings settings);
List<ReadarrProfile> GetProfiles(ReadarrSettings settings); List<ReadarrProfile> GetProfiles(ReadarrSettings settings);
List<ReadarrTag> GetTags(ReadarrSettings settings); List<ReadarrTag> GetTags(ReadarrSettings settings);
ValidationFailure Test(ReadarrSettings settings); ValidationFailure Test(ReadarrSettings settings);
@ -33,6 +34,11 @@ namespace NzbDrone.Core.ImportLists.Readarr
return Execute<ReadarrAuthor>("/api/v1/author", settings); return Execute<ReadarrAuthor>("/api/v1/author", settings);
} }
public List<ReadarrBook> GetBooks(ReadarrSettings settings)
{
return Execute<ReadarrBook>("/api/v1/book", settings);
}
public List<ReadarrProfile> GetProfiles(ReadarrSettings settings) public List<ReadarrProfile> GetProfiles(ReadarrSettings settings)
{ {
return Execute<ReadarrProfile>("/api/v1/qualityprofile", settings); return Execute<ReadarrProfile>("/api/v1/qualityprofile", settings);

Loading…
Cancel
Save