mirror of
https://github.com/Readarr/Readarr
synced 2026-02-12 09:42:04 +01:00
Fixed: "Specific Book" setting for readarr list import (#1200)
This commit is contained in:
parent
109fd22d1f
commit
466876da62
6 changed files with 143 additions and 62 deletions
|
|
@ -88,6 +88,19 @@ private void WithBookId()
|
|||
_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()
|
||||
{
|
||||
Mocker.GetMock<IAuthorService>()
|
||||
|
|
@ -290,5 +303,26 @@ public void should_not_add_book_if_excluded_author()
|
|||
Mocker.GetMock<IAddBookService>()
|
||||
.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 @@ public void SetBookMonitoredStatus(Author author, MonitoringOptions monitoringOp
|
|||
if (monitoredBooks.Any())
|
||||
{
|
||||
ToggleBooksMonitoredState(
|
||||
books.Where(s => monitoredBooks.Any(t => t == s.ForeignBookId)), true);
|
||||
books.Where(s => monitoredBooks.Contains(s.ForeignBookId)), true);
|
||||
ToggleBooksMonitoredState(
|
||||
books.Where(s => monitoredBooks.Any(t => t != s.ForeignBookId)), false);
|
||||
books.Where(s => !monitoredBooks.Contains(s.ForeignBookId)), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -99,12 +99,12 @@ private List<Book> ProcessReports(List<ImportListItemInfo> reports)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
ProcessBookReport(importList, report, listExclusions, booksToAdd);
|
||||
ProcessBookReport(importList, report, listExclusions, booksToAdd, authorsToAdd);
|
||||
}
|
||||
else if (report.Author.IsNotNullOrWhiteSpace() || report.AuthorGoodreadsId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
|
@ -147,7 +147,7 @@ private void MapBookReport(ImportListItemInfo report)
|
|||
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)
|
||||
{
|
||||
_logger.Trace($"Nothing found for {report.EditionGoodreadsId}");
|
||||
|
|
@ -159,11 +159,11 @@ private void MapBookReport(ImportListItemInfo report)
|
|||
|
||||
report.BookGoodreadsId = mappedBook.ForeignBookId;
|
||||
report.Book = mappedBook.Title;
|
||||
report.Author = mappedBook.AuthorMetadata?.Value?.Name;
|
||||
report.AuthorGoodreadsId = mappedBook.AuthorMetadata?.Value?.ForeignAuthorId;
|
||||
report.Author ??= mappedBook.AuthorMetadata?.Value?.Name;
|
||||
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)
|
||||
{
|
||||
|
|
@ -181,13 +181,13 @@ private void ProcessBookReport(ImportListDefinition importList, ImportListItemIn
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -223,6 +223,26 @@ private void ProcessBookReport(ImportListDefinition importList, ImportListItemIn
|
|||
{
|
||||
var monitored = importList.ShouldMonitor != ImportListMonitorType.None;
|
||||
|
||||
var toAddAuthor = new Author
|
||||
{
|
||||
Monitored = monitored,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AddOptions = new AddAuthorOptions
|
||||
{
|
||||
SearchForMissingBooks = importList.ShouldSearch,
|
||||
Monitored = monitored,
|
||||
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,
|
||||
|
|
@ -235,29 +255,16 @@ private void ProcessBookReport(ImportListDefinition importList, ImportListItemIn
|
|||
Monitored = true
|
||||
}
|
||||
},
|
||||
Author = new Author
|
||||
{
|
||||
Monitored = monitored,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AddOptions = new AddAuthorOptions
|
||||
{
|
||||
SearchForMissingBooks = importList.ShouldSearch,
|
||||
Monitored = monitored,
|
||||
Monitor = monitored ? MonitorTypes.All : MonitorTypes.None
|
||||
}
|
||||
},
|
||||
Author = toAddAuthor,
|
||||
AddOptions = new AddBookOptions
|
||||
{
|
||||
SearchForNewBook = monitored
|
||||
SearchForNewBook = importList.ShouldSearch
|
||||
}
|
||||
};
|
||||
|
||||
if (importList.ShouldMonitor == ImportListMonitorType.SpecificBook)
|
||||
{
|
||||
toAdd.Author.Value.AddOptions.BooksToMonitor.Add(toAdd.ForeignBookId);
|
||||
toAddAuthor.AddOptions.BooksToMonitor.Add(toAdd.ForeignBookId);
|
||||
}
|
||||
|
||||
booksToAdd.Add(toAdd);
|
||||
|
|
@ -272,11 +279,11 @@ private void MapAuthorReport(ImportListItemInfo report)
|
|||
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)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check to see if author in DB
|
||||
|
|
@ -285,10 +292,13 @@ private void ProcessAuthorReport(ImportListDefinition importList, ImportListItem
|
|||
// Check to see if author excluded
|
||||
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)
|
||||
{
|
||||
_logger.Debug("{0} [{1}] Rejected due to list exlcusion", report.AuthorGoodreadsId, report.Author);
|
||||
return;
|
||||
_logger.Debug("{0} [{1}] Rejected due to list exclusion", report.AuthorGoodreadsId, report.Author);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (existingAuthor != null)
|
||||
|
|
@ -301,34 +311,41 @@ private void ProcessAuthorReport(ImportListDefinition importList, ImportListItem
|
|||
_authorService.UpdateAuthor(existingAuthor);
|
||||
}
|
||||
|
||||
return;
|
||||
return existingAuthor;
|
||||
}
|
||||
|
||||
// Append Author if not already in DB or already on add list
|
||||
if (authorsToAdd.All(s => s.Metadata.Value.ForeignAuthorId != report.AuthorGoodreadsId))
|
||||
if (existingImportAuthor != null)
|
||||
{
|
||||
var monitored = importList.ShouldMonitor != ImportListMonitorType.None;
|
||||
_logger.Debug("{0} [{1}] Rejected, Author Exists in Import.", report.AuthorGoodreadsId, report.Author);
|
||||
|
||||
authorsToAdd.Add(new Author
|
||||
{
|
||||
Metadata = new AuthorMetadata
|
||||
{
|
||||
ForeignAuthorId = report.AuthorGoodreadsId,
|
||||
Name = report.Author
|
||||
},
|
||||
Monitored = monitored,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AddOptions = new AddAuthorOptions
|
||||
{
|
||||
SearchForMissingBooks = importList.ShouldSearch,
|
||||
Monitored = monitored,
|
||||
Monitor = monitored ? MonitorTypes.All : MonitorTypes.None
|
||||
}
|
||||
});
|
||||
return existingImportAuthor;
|
||||
}
|
||||
|
||||
var monitored = importList.ShouldMonitor != ImportListMonitorType.None;
|
||||
|
||||
var toAdd = new Author
|
||||
{
|
||||
Metadata = new AuthorMetadata
|
||||
{
|
||||
ForeignAuthorId = report.AuthorGoodreadsId,
|
||||
Name = report.Author
|
||||
},
|
||||
Monitored = monitored,
|
||||
RootFolderPath = importList.RootFolderPath,
|
||||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AddOptions = new AddAuthorOptions
|
||||
{
|
||||
SearchForMissingBooks = importList.ShouldSearch,
|
||||
Monitored = monitored,
|
||||
Monitor = monitored ? MonitorTypes.All : MonitorTypes.None
|
||||
}
|
||||
};
|
||||
|
||||
authorsToAdd.Add(toAdd);
|
||||
|
||||
return toAdd;
|
||||
}
|
||||
|
||||
public void Execute(ImportListSyncCommand message)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,26 @@ public class ReadarrAuthor
|
|||
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 string Name { get; set; }
|
||||
|
|
|
|||
|
|
@ -30,21 +30,25 @@ public ReadarrImport(IReadarrV1Proxy readarrV1Proxy,
|
|||
|
||||
public override IList<ImportListItemInfo> Fetch()
|
||||
{
|
||||
var authors = new List<ImportListItemInfo>();
|
||||
var authorsAndBooks = new List<ImportListItemInfo>();
|
||||
|
||||
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)) &&
|
||||
(!Settings.TagIds.Any() || Settings.TagIds.Any(x => remoteAuthor.Tags.Any(y => y == x))))
|
||||
if ((!Settings.ProfileIds.Any() || Settings.ProfileIds.Contains(remoteBook.Author.QualityProfileId)) &&
|
||||
(!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,
|
||||
Author = remoteAuthor.AuthorName
|
||||
BookGoodreadsId = remoteBook.ForeignBookId,
|
||||
Book = remoteBook.Title,
|
||||
EditionGoodreadsId = remoteBook.Editions.Single(x => x.Monitored).ForeignEditionId,
|
||||
Author = remoteBook.Author.AuthorName,
|
||||
AuthorGoodreadsId = remoteBook.Author.ForeignAuthorId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +60,7 @@ public override IList<ImportListItemInfo> Fetch()
|
|||
_importListStatusService.RecordFailure(Definition.Id);
|
||||
}
|
||||
|
||||
return CleanupListItems(authors);
|
||||
return CleanupListItems(authorsAndBooks);
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.Readarr
|
|||
public interface IReadarrV1Proxy
|
||||
{
|
||||
List<ReadarrAuthor> GetAuthors(ReadarrSettings settings);
|
||||
List<ReadarrBook> GetBooks(ReadarrSettings settings);
|
||||
List<ReadarrProfile> GetProfiles(ReadarrSettings settings);
|
||||
List<ReadarrTag> GetTags(ReadarrSettings settings);
|
||||
ValidationFailure Test(ReadarrSettings settings);
|
||||
|
|
@ -33,6 +34,11 @@ public List<ReadarrAuthor> GetAuthors(ReadarrSettings 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)
|
||||
{
|
||||
return Execute<ReadarrProfile>("/api/v1/qualityprofile", settings);
|
||||
|
|
|
|||
Loading…
Reference in a new issue