From a59706ceb42acbdb2128143ce027340cf0d9858e Mon Sep 17 00:00:00 2001 From: ta264 Date: Thu, 26 May 2022 21:51:37 +0100 Subject: [PATCH] Fixed: Removed unnecessary author data from book endpoint --- .../src/Book/Details/BookDetailsConnector.js | 17 ++++++-- .../Details/BookDetailsHeaderConnector.js | 12 +++++- .../src/Book/Edit/EditBookModalContent.js | 2 +- .../Edit/EditBookModalContentConnector.js | 30 +++++--------- .../src/Book/Index/BookIndexItemConnector.js | 8 +++- .../Book/Index/Overview/BookIndexOverview.js | 22 ++++++++-- frontend/src/Store/Actions/editionActions.js | 26 +++++++++++- .../Selectors/createBookAuthorSelector.js | 15 +++++++ .../createBookQualityProfileSelector.js | 12 +++--- .../MetadataSource/BookInfo/BookInfoProxy.cs | 2 +- .../ApiTests/WantedFixture.cs | 28 ++++++++++++- .../Client/WantedClient.cs | 40 +++++++++++++++++++ .../IntegrationTestBase.cs | 8 ++-- src/Readarr.Api.V1/Books/BookController.cs | 11 +++++ src/Readarr.Api.V1/Books/BookResource.cs | 3 -- src/Readarr.Api.V1/Search/SearchController.cs | 3 ++ 16 files changed, 190 insertions(+), 49 deletions(-) create mode 100644 frontend/src/Store/Selectors/createBookAuthorSelector.js create mode 100644 src/NzbDrone.Integration.Test/Client/WantedClient.cs diff --git a/frontend/src/Book/Details/BookDetailsConnector.js b/frontend/src/Book/Details/BookDetailsConnector.js index f36c8e9b3..bae8cc1b2 100644 --- a/frontend/src/Book/Details/BookDetailsConnector.js +++ b/frontend/src/Book/Details/BookDetailsConnector.js @@ -8,6 +8,7 @@ import * as commandNames from 'Commands/commandNames'; import { toggleBooksMonitored } from 'Store/Actions/bookActions'; import { clearBookFiles, fetchBookFiles } from 'Store/Actions/bookFileActions'; import { executeCommand } from 'Store/Actions/commandActions'; +import { clearEditions, fetchEditions } from 'Store/Actions/editionActions'; import { cancelFetchReleases, clearReleases } from 'Store/Actions/releaseActions'; import createAllAuthorSelector from 'Store/Selectors/createAllAuthorsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; @@ -43,11 +44,12 @@ function createMapStateToProps() { (state, { titleSlug }) => titleSlug, selectBookFiles, (state) => state.books, + (state) => state.editions, createAllAuthorSelector(), createCommandsSelector(), createUISettingsSelector(), createDimensionsSelector(), - (titleSlug, bookFiles, books, authors, commands, uiSettings, dimensions) => { + (titleSlug, bookFiles, books, editions, authors, commands, uiSettings, dimensions) => { const book = books.items.find((b) => b.titleSlug === titleSlug); const author = authors.find((a) => a.id === book.authorId); const sortedBooks = books.items.filter((b) => b.authorId === book.authorId); @@ -79,8 +81,8 @@ function createMapStateToProps() { isRefreshingCommand.body.bookId === book.id ); - const isFetching = isBookFilesFetching; - const isPopulated = isBookFilesPopulated; + const isFetching = isBookFilesFetching || editions.isFetching; + const isPopulated = isBookFilesPopulated && editions.isPopulated; return { ...book, @@ -104,6 +106,8 @@ const mapDispatchToProps = { executeCommand, fetchBookFiles, clearBookFiles, + fetchEditions, + clearEditions, clearReleases, cancelFetchReleases, toggleBooksMonitored @@ -121,7 +125,8 @@ class BookDetailsConnector extends Component { } componentDidUpdate(prevProps) { - if (!_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) || + if (prevProps.id !== this.props.id || + !_.isEqual(getMonitoredEditions(prevProps), getMonitoredEditions(this.props)) || (prevProps.anyReleaseOk === false && this.props.anyReleaseOk === true)) { this.unpopulate(); this.populate(); @@ -140,12 +145,14 @@ class BookDetailsConnector extends Component { const bookId = this.props.id; this.props.fetchBookFiles({ bookId }); + this.props.fetchEditions({ bookId }); } unpopulate = () => { this.props.cancelFetchReleases(); this.props.clearReleases(); this.props.clearBookFiles(); + this.props.clearEditions(); } // @@ -195,6 +202,8 @@ BookDetailsConnector.propTypes = { titleSlug: PropTypes.string.isRequired, fetchBookFiles: PropTypes.func.isRequired, clearBookFiles: PropTypes.func.isRequired, + fetchEditions: PropTypes.func.isRequired, + clearEditions: PropTypes.func.isRequired, clearReleases: PropTypes.func.isRequired, cancelFetchReleases: PropTypes.func.isRequired, toggleBooksMonitored: PropTypes.func.isRequired, diff --git a/frontend/src/Book/Details/BookDetailsHeaderConnector.js b/frontend/src/Book/Details/BookDetailsHeaderConnector.js index 8a8bebd6f..7c80a6f17 100644 --- a/frontend/src/Book/Details/BookDetailsHeaderConnector.js +++ b/frontend/src/Book/Details/BookDetailsHeaderConnector.js @@ -8,15 +8,25 @@ import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector'; import BookDetailsHeader from './BookDetailsHeader'; +const selectOverview = createSelector( + (state) => state.editions, + (editions) => { + const monitored = editions.items.find((e) => e.monitored === true); + return monitored?.overview; + } +); + function createMapStateToProps() { return createSelector( createBookSelector(), + selectOverview, createUISettingsSelector(), createDimensionsSelector(), - (book, uiSettings, dimensions) => { + (book, overview, uiSettings, dimensions) => { return { ...book, + overview, shortDateFormat: uiSettings.shortDateFormat, isSmallScreen: dimensions.isSmallScreen }; diff --git a/frontend/src/Book/Edit/EditBookModalContent.js b/frontend/src/Book/Edit/EditBookModalContent.js index d2634461f..acadd50de 100644 --- a/frontend/src/Book/Edit/EditBookModalContent.js +++ b/frontend/src/Book/Edit/EditBookModalContent.js @@ -53,7 +53,7 @@ class EditBookModalContent extends Component { editions } = item; - const hasFile = statistics ? statistics.bookFileCount : 0; + const hasFile = statistics ? statistics.bookFileCount > 0 : false; const errorMessage = getErrorMessage(error, 'Unable to load editions'); return ( diff --git a/frontend/src/Book/Edit/EditBookModalContentConnector.js b/frontend/src/Book/Edit/EditBookModalContentConnector.js index f4e9fbf83..869421aeb 100644 --- a/frontend/src/Book/Edit/EditBookModalContentConnector.js +++ b/frontend/src/Book/Edit/EditBookModalContentConnector.js @@ -4,7 +4,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { saveBook, setBookValue } from 'Store/Actions/bookActions'; -import { clearEditions, fetchEditions } from 'Store/Actions/editionActions'; +import { saveEditions } from 'Store/Actions/editionActions'; import createAuthorSelector from 'Store/Selectors/createAuthorSelector'; import createBookSelector from 'Store/Selectors/createBookSelector'; import selectSettings from 'Store/Selectors/selectSettings'; @@ -26,17 +26,14 @@ function createMapStateToProps() { const { isFetching, isPopulated, - error, - items + error } = editionState; - book.editions = items; - const bookSettings = _.pick(book, [ 'monitored', - 'anyEditionOk', - 'editions' + 'anyEditionOk' ]); + bookSettings.editions = editionState.items; const settings = selectSettings(bookSettings, pendingChanges, saveError); @@ -58,10 +55,9 @@ function createMapStateToProps() { } const mapDispatchToProps = { - dispatchFetchEditions: fetchEditions, - dispatchClearEditions: clearEditions, dispatchSetBookValue: setBookValue, - dispatchSaveBook: saveBook + dispatchSaveBook: saveBook, + dispatchSaveEditions: saveEditions }; class EditBookModalContentConnector extends Component { @@ -69,20 +65,12 @@ class EditBookModalContentConnector extends Component { // // Lifecycle - componentDidMount() { - this.props.dispatchFetchEditions({ bookId: this.props.bookId }); - } - componentDidUpdate(prevProps, prevState) { if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) { this.props.onModalClose(); } } - componentWillUnmount() { - this.props.dispatchClearEditions(); - } - // // Listeners @@ -94,6 +82,9 @@ class EditBookModalContentConnector extends Component { this.props.dispatchSaveBook({ id: this.props.bookId }); + this.props.dispatchSaveEditions({ + id: this.props.bookId + }); } // @@ -114,10 +105,9 @@ EditBookModalContentConnector.propTypes = { bookId: PropTypes.number, isSaving: PropTypes.bool.isRequired, saveError: PropTypes.object, - dispatchFetchEditions: PropTypes.func.isRequired, - dispatchClearEditions: PropTypes.func.isRequired, dispatchSetBookValue: PropTypes.func.isRequired, dispatchSaveBook: PropTypes.func.isRequired, + dispatchSaveEditions: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired }; diff --git a/frontend/src/Book/Index/BookIndexItemConnector.js b/frontend/src/Book/Index/BookIndexItemConnector.js index d27abe2a2..626db2a14 100644 --- a/frontend/src/Book/Index/BookIndexItemConnector.js +++ b/frontend/src/Book/Index/BookIndexItemConnector.js @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import * as commandNames from 'Commands/commandNames'; import { executeCommand } from 'Store/Actions/commandActions'; +import createBookAuthorSelector from 'Store/Selectors/createBookAuthorSelector'; import createBookQualityProfileSelector from 'Store/Selectors/createBookQualityProfileSelector'; import createBookSelector from 'Store/Selectors/createBookSelector'; import createExecutingCommandsSelector from 'Store/Selectors/createExecutingCommandsSelector'; @@ -32,11 +33,13 @@ function selectShowSearchAction() { function createMapStateToProps() { return createSelector( createBookSelector(), + createBookAuthorSelector(), createBookQualityProfileSelector(), selectShowSearchAction(), createExecutingCommandsSelector(), ( book, + author, qualityProfile, showSearchAction, executingCommands @@ -54,7 +57,7 @@ function createMapStateToProps() { const isRefreshingBook = executingCommands.some((command) => { return ( (command.name === commandNames.REFRESH_AUTHOR && - command.body.authorId === book.author.id) || + command.body.authorId === book.authorId) || (command.name === commandNames.REFRESH_BOOK && command.body.bookId === book.id) ); @@ -63,7 +66,7 @@ function createMapStateToProps() { const isSearchingBook = executingCommands.some((command) => { return ( (command.name === commandNames.AUTHOR_SEARCH && - command.body.authorId === book.author.id) || + command.body.authorId === book.authorId) || (command.name === commandNames.BOOK_SEARCH && command.body.bookIds.includes(book.id)) ); @@ -71,6 +74,7 @@ function createMapStateToProps() { return { ...book, + author, qualityProfile, showSearchAction, isRefreshingBook, diff --git a/frontend/src/Book/Index/Overview/BookIndexOverview.js b/frontend/src/Book/Index/Overview/BookIndexOverview.js index 37983d3a9..16c3a5f77 100644 --- a/frontend/src/Book/Index/Overview/BookIndexOverview.js +++ b/frontend/src/Book/Index/Overview/BookIndexOverview.js @@ -12,6 +12,7 @@ import SpinnerIconButton from 'Components/Link/SpinnerIconButton'; import { icons } from 'Helpers/Props'; import dimensions from 'Styles/Variables/dimensions'; import fonts from 'Styles/Variables/fonts'; +import createAjaxRequest from 'Utilities/createAjaxRequest'; import stripHtml from 'Utilities/String/stripHtml'; import translate from 'Utilities/String/translate'; import BookIndexOverviewInfo from './BookIndexOverviewInfo'; @@ -42,10 +43,26 @@ class BookIndexOverview extends Component { this.state = { isEditAuthorModalOpen: false, - isDeleteAuthorModalOpen: false + isDeleteAuthorModalOpen: false, + overview: '' }; } + componentDidMount() { + const { id } = this.props; + + // Note that this component is lazy loaded by the virtualised view. + // We want to avoid storing overviews for *all* books which is + // why it's not put into the redux store + const promise = createAjaxRequest({ + url: `/book/${id}/overview` + }).request; + + promise.done((data) => { + this.setState({ overview: data.overview }); + }); + } + // // Listeners @@ -84,7 +101,6 @@ class BookIndexOverview extends Component { const { id, title, - overview, monitored, titleSlug, nextAiring, @@ -118,6 +134,7 @@ class BookIndexOverview extends Component { } = statistics; const { + overview, isEditAuthorModalOpen, isDeleteAuthorModalOpen } = this.state; @@ -267,7 +284,6 @@ class BookIndexOverview extends Component { BookIndexOverview.propTypes = { id: PropTypes.number.isRequired, title: PropTypes.string.isRequired, - overview: PropTypes.string.isRequired, monitored: PropTypes.bool.isRequired, titleSlug: PropTypes.string.isRequired, nextAiring: PropTypes.string, diff --git a/frontend/src/Store/Actions/editionActions.js b/frontend/src/Store/Actions/editionActions.js index f6e0c6f93..6957067ef 100644 --- a/frontend/src/Store/Actions/editionActions.js +++ b/frontend/src/Store/Actions/editionActions.js @@ -1,5 +1,8 @@ import { createAction } from 'redux-actions'; +import { batchActions } from 'redux-batched-actions'; import { createThunk, handleThunks } from 'Store/thunks'; +import getProviderState from 'Utilities/State/getProviderState'; +import { updateItem } from './baseActions'; import createFetchHandler from './Creators/createFetchHandler'; import createHandleActions from './Creators/createHandleActions'; import createClearReducer from './Creators/Reducers/createClearReducer'; @@ -25,18 +28,39 @@ export const defaultState = { export const FETCH_EDITIONS = 'editions/fetchEditions'; export const CLEAR_EDITIONS = 'editions/clearEditions'; +export const SAVE_EDITIONS = 'editions/saveEditions'; // // Action Creators export const fetchEditions = createThunk(FETCH_EDITIONS); export const clearEditions = createAction(CLEAR_EDITIONS); +export const saveEditions = createThunk(SAVE_EDITIONS); // // Action Handlers export const actionHandlers = handleThunks({ - [FETCH_EDITIONS]: createFetchHandler(section, '/edition') + [FETCH_EDITIONS]: createFetchHandler(section, '/edition'), + + [SAVE_EDITIONS]: function(getState, payload, dispatch) { + const { + id, + ...otherPayload + } = payload; + + const saveData = getProviderState({ id, ...otherPayload }, getState, 'books'); + + dispatch(batchActions([ + ...saveData.editions.map((edition) => { + return updateItem({ + id: edition.id, + section: 'editions', + ...edition + }); + }) + ])); + } }); // diff --git a/frontend/src/Store/Selectors/createBookAuthorSelector.js b/frontend/src/Store/Selectors/createBookAuthorSelector.js new file mode 100644 index 000000000..89840de66 --- /dev/null +++ b/frontend/src/Store/Selectors/createBookAuthorSelector.js @@ -0,0 +1,15 @@ +import { createSelector } from 'reselect'; +import createBookSelector from './createBookSelector'; + +function createBookAuthorSelector() { + return createSelector( + createBookSelector(), + (state) => state.authors.itemMap, + (state) => state.authors.items, + (book, authorMap, allAuthors) => { + return allAuthors[authorMap[book.authorId]]; + } + ); +} + +export default createBookAuthorSelector; diff --git a/frontend/src/Store/Selectors/createBookQualityProfileSelector.js b/frontend/src/Store/Selectors/createBookQualityProfileSelector.js index 5ad877c77..8aae742a8 100644 --- a/frontend/src/Store/Selectors/createBookQualityProfileSelector.js +++ b/frontend/src/Store/Selectors/createBookQualityProfileSelector.js @@ -1,18 +1,16 @@ import { createSelector } from 'reselect'; -import createBookSelector from './createBookSelector'; +import createBookAuthorSelector from './createBookAuthorSelector'; function createBookQualityProfileSelector() { return createSelector( (state) => state.settings.qualityProfiles.items, - createBookSelector(), - (qualityProfiles, book) => { - if (!book) { + createBookAuthorSelector(), + (qualityProfiles, author) => { + if (!author) { return {}; } - return qualityProfiles.find((profile) => { - return profile.id === book.author.qualityProfileId; - }); + return qualityProfiles.find((profile) => profile.id === author.qualityProfileId); } ); } diff --git a/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs b/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs index 5f0992c0b..35159e5c7 100644 --- a/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/BookInfo/BookInfoProxy.cs @@ -439,7 +439,7 @@ private Book GetEditionInfo(int id, bool getAllEditions) var edition = book.Editions.Value.SingleOrDefault(e => e.ForeignEditionId == id.ToString()); trimmed.Editions = new List { edition }; - return trimmed; + book = trimmed; } var authorDict = authors.ToDictionary(x => x.ForeignAuthorId); diff --git a/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs b/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs index 5b57a5397..3b98cb06e 100644 --- a/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs +++ b/src/NzbDrone.Integration.Test/ApiTests/WantedFixture.cs @@ -52,12 +52,23 @@ public void missing_should_have_author() { EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true); - var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc"); + var result = WantedMissing.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: true); result.Records.First().Author.Should().NotBeNull(); result.Records.First().Author.AuthorName.Should().Be("Andrew Hunter Murray"); } + [Test] + [Order(1)] + public void missing_should_not_have_author() + { + EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true); + + var result = WantedMissing.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: false); + + result.Records.First().Author.Should().BeNull(); + } + [Test] [Order(2)] public void cutoff_should_have_monitored_items() @@ -103,12 +114,25 @@ public void cutoff_should_have_author() var author = EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true); EnsureBookFile(author, 1, "43765115", Quality.MOBI); - var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc"); + var result = WantedCutoffUnmet.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: true); result.Records.First().Author.Should().NotBeNull(); result.Records.First().Author.AuthorName.Should().Be("Andrew Hunter Murray"); } + [Test] + [Order(2)] + public void cutoff_should_not_have_author() + { + EnsureProfileCutoff(1, Quality.AZW3); + var author = EnsureAuthor("14586394", "43765115", "Andrew Hunter Murray", true); + EnsureBookFile(author, 1, "43765115", Quality.MOBI); + + var result = WantedCutoffUnmet.GetPagedIncludeAuthor(0, 15, "releaseDate", "desc", includeAuthor: false); + + result.Records.First().Author.Should().BeNull(); + } + [Test] [Order(1)] public void missing_should_have_unmonitored_items() diff --git a/src/NzbDrone.Integration.Test/Client/WantedClient.cs b/src/NzbDrone.Integration.Test/Client/WantedClient.cs new file mode 100644 index 000000000..53ccfff98 --- /dev/null +++ b/src/NzbDrone.Integration.Test/Client/WantedClient.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Readarr.Api.V1.Books; +using Readarr.Http; +using RestSharp; + +namespace NzbDrone.Integration.Test.Client +{ + public class WantedClient : ClientBase + { + public WantedClient(IRestClient restClient, string apiKey, string resource) + : base(restClient, apiKey, resource) + { + } + + public PagingResource GetPagedIncludeAuthor(int pageNumber, int pageSize, string sortKey, string sortDir, string filterKey = null, string filterValue = null, bool includeAuthor = true) + { + var request = BuildRequest(); + request.AddParameter("page", pageNumber); + request.AddParameter("pageSize", pageSize); + request.AddParameter("sortKey", sortKey); + request.AddParameter("sortDir", sortDir); + + if (filterKey != null && filterValue != null) + { + request.AddParameter("filterKey", filterKey); + request.AddParameter("filterValue", filterValue); + } + + request.AddParameter("includeAuthor", includeAuthor); + + return Get>(request); + } + + public List GetBooksInAuthor(int authorId) + { + var request = BuildRequest("?authorId=" + authorId.ToString()); + return Get>(request); + } + } +} diff --git a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs index 919b718a0..84b5d567f 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTestBase.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTestBase.cs @@ -53,8 +53,8 @@ public abstract class IntegrationTestBase public ClientBase RootFolders; public AuthorClient Author; public ClientBase Tags; - public ClientBase WantedMissing; - public ClientBase WantedCutoffUnmet; + public WantedClient WantedMissing; + public WantedClient WantedCutoffUnmet; private List _signalRReceived; @@ -118,8 +118,8 @@ protected virtual void InitRestClients() RootFolders = new ClientBase(RestClient, ApiKey); Author = new AuthorClient(RestClient, ApiKey); Tags = new ClientBase(RestClient, ApiKey); - WantedMissing = new ClientBase(RestClient, ApiKey, "wanted/missing"); - WantedCutoffUnmet = new ClientBase(RestClient, ApiKey, "wanted/cutoff"); + WantedMissing = new WantedClient(RestClient, ApiKey, "wanted/missing"); + WantedCutoffUnmet = new WantedClient(RestClient, ApiKey, "wanted/cutoff"); } [OneTimeTearDown] diff --git a/src/Readarr.Api.V1/Books/BookController.cs b/src/Readarr.Api.V1/Books/BookController.cs index ed4dbe9d6..20ccdce25 100644 --- a/src/Readarr.Api.V1/Books/BookController.cs +++ b/src/Readarr.Api.V1/Books/BookController.cs @@ -138,6 +138,17 @@ public List GetBooks([FromQuery]int? authorId, return MapToResource(_bookService.GetBooks(bookIds), false); } + [HttpGet("{id:int}/overview")] + public object Overview(int id) + { + var overview = _editionService.GetEditionsByBook(id).Single(x => x.Monitored).Overview; + return new + { + id, + overview + }; + } + [RestPostById] public ActionResult AddBook(BookResource bookResource) { diff --git a/src/Readarr.Api.V1/Books/BookResource.cs b/src/Readarr.Api.V1/Books/BookResource.cs index 35c576b4c..c8fa6e9f7 100644 --- a/src/Readarr.Api.V1/Books/BookResource.cs +++ b/src/Readarr.Api.V1/Books/BookResource.cs @@ -72,13 +72,10 @@ public static BookResource ToResource(this Book model) AuthorTitle = authorTitle, SeriesTitle = seriesTitle, Disambiguation = selectedEdition?.Disambiguation, - Overview = selectedEdition?.Overview, Images = selectedEdition?.Images ?? new List(), Links = model.Links.Concat(selectedEdition?.Links ?? new List()).ToList(), Ratings = selectedEdition?.Ratings ?? new Ratings(), Added = model.Added, - Author = model.Author?.Value.ToResource(), - Editions = model.Editions?.Value.ToResource() ?? new List() }; } diff --git a/src/Readarr.Api.V1/Search/SearchController.cs b/src/Readarr.Api.V1/Search/SearchController.cs index a6f03ef73..c6dfc99b0 100644 --- a/src/Readarr.Api.V1/Search/SearchController.cs +++ b/src/Readarr.Api.V1/Search/SearchController.cs @@ -51,6 +51,9 @@ private static IEnumerable MapToResource(IEnumerable res { var book = (NzbDrone.Core.Books.Book)result; resource.Book = book.ToResource(); + resource.Book.Overview = book.Editions.Value.Single(x => x.Monitored).Overview; + resource.Book.Author = book.Author.Value.ToResource(); + resource.Book.Editions = book.Editions.Value.ToResource(); resource.ForeignId = book.ForeignBookId; var cover = book.Editions.Value.Single(x => x.Monitored).Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);