diff --git a/frontend/src/Author/Details/BookRow.js b/frontend/src/Author/Details/BookRow.js index 5eee601d2..717355363 100644 --- a/frontend/src/Author/Details/BookRow.js +++ b/frontend/src/Author/Details/BookRow.js @@ -71,7 +71,7 @@ class BookRow extends Component { statistics, releaseDate, title, - position, + seriesTitle, pageCount, ratings, isSaving, @@ -129,13 +129,13 @@ class BookRow extends Component { ); } - if (name === 'position') { + if (name === 'series') { return ( - {position || ''} + {seriesTitle || ''} ); } @@ -215,7 +215,7 @@ BookRow.propTypes = { monitored: PropTypes.bool.isRequired, releaseDate: PropTypes.string, title: PropTypes.string.isRequired, - position: PropTypes.string, + seriesTitle: PropTypes.string.isRequired, pageCount: PropTypes.number, ratings: PropTypes.object.isRequired, titleSlug: PropTypes.string.isRequired, diff --git a/frontend/src/Book/Details/BookDetails.js b/frontend/src/Book/Details/BookDetails.js index 97bb3b679..36dc53ca4 100644 --- a/frontend/src/Book/Details/BookDetails.js +++ b/frontend/src/Book/Details/BookDetails.js @@ -143,6 +143,7 @@ class BookDetails extends Component { id, titleSlug, title, + seriesTitle, pageCount, overview, statistics = {}, @@ -301,6 +302,10 @@ class BookDetails extends Component {
+
+ {seriesTitle} +
+
{ !!pageCount && @@ -510,6 +515,7 @@ BookDetails.propTypes = { id: PropTypes.number.isRequired, titleSlug: PropTypes.string.isRequired, title: PropTypes.string.isRequired, + seriesTitle: PropTypes.string.isRequired, pageCount: PropTypes.number, overview: PropTypes.string, statistics: PropTypes.object.isRequired, diff --git a/src/NzbDrone.Core/Books/Repositories/SeriesBookLinkRepository.cs b/src/NzbDrone.Core/Books/Repositories/SeriesBookLinkRepository.cs index 142278c0b..9f9a9fb55 100644 --- a/src/NzbDrone.Core/Books/Repositories/SeriesBookLinkRepository.cs +++ b/src/NzbDrone.Core/Books/Repositories/SeriesBookLinkRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; @@ -7,6 +8,7 @@ namespace NzbDrone.Core.Books public interface ISeriesBookLinkRepository : IBasicRepository { List GetLinksBySeries(int seriesId); + List GetLinksByBook(List bookIds); } public class SeriesBookLinkRepository : BasicRepository, ISeriesBookLinkRepository @@ -20,5 +22,19 @@ namespace NzbDrone.Core.Books { return Query(x => x.SeriesId == seriesId); } + + public List GetLinksByBook(List bookIds) + { + return _database.QueryJoined( + Builder() + .Join((l, s) => l.SeriesId == s.Id) + .Where(x => bookIds.Contains(x.BookId)), + (link, series) => + { + link.Series = series; + return link; + }) + .ToList(); + } } } diff --git a/src/NzbDrone.Core/Books/Services/SeriesBookLinkService.cs b/src/NzbDrone.Core/Books/Services/SeriesBookLinkService.cs index c0f0585c9..f3c32c3b4 100644 --- a/src/NzbDrone.Core/Books/Services/SeriesBookLinkService.cs +++ b/src/NzbDrone.Core/Books/Services/SeriesBookLinkService.cs @@ -5,6 +5,7 @@ namespace NzbDrone.Core.Books public interface ISeriesBookLinkService { List GetLinksBySeries(int seriesId); + List GetLinksByBook(List bookIds); void InsertMany(List model); void UpdateMany(List model); void DeleteMany(List model); @@ -24,6 +25,11 @@ namespace NzbDrone.Core.Books return _repo.GetLinksBySeries(seriesId); } + public List GetLinksByBook(List bookIds) + { + return _repo.GetLinksByBook(bookIds); + } + public void InsertMany(List model) { _repo.InsertMany(model); diff --git a/src/Readarr.Api.V1/Books/BookModule.cs b/src/Readarr.Api.V1/Books/BookModule.cs index b29f6dce4..3734abc6e 100644 --- a/src/Readarr.Api.V1/Books/BookModule.cs +++ b/src/Readarr.Api.V1/Books/BookModule.cs @@ -37,6 +37,7 @@ namespace Readarr.Api.V1.Books IBookService bookService, IAddBookService addBookService, IEditionService editionService, + ISeriesBookLinkService seriesBookLinkService, IAuthorStatisticsService authorStatisticsService, IMapCoversToLocal coverMapper, IUpgradableSpecification upgradableSpecification, @@ -44,7 +45,7 @@ namespace Readarr.Api.V1.Books QualityProfileExistsValidator qualityProfileExistsValidator, MetadataProfileExistsValidator metadataProfileExistsValidator) - : base(bookService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster) + : base(bookService, seriesBookLinkService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster) { _authorService = authorService; _editionService = editionService; diff --git a/src/Readarr.Api.V1/Books/BookModuleWithSignalR.cs b/src/Readarr.Api.V1/Books/BookModuleWithSignalR.cs index 6cf516c87..bbca83628 100644 --- a/src/Readarr.Api.V1/Books/BookModuleWithSignalR.cs +++ b/src/Readarr.Api.V1/Books/BookModuleWithSignalR.cs @@ -14,18 +14,21 @@ namespace Readarr.Api.V1.Books public abstract class BookModuleWithSignalR : ReadarrRestModuleWithSignalR { protected readonly IBookService _bookService; + protected readonly ISeriesBookLinkService _seriesBookLinkService; protected readonly IAuthorStatisticsService _authorStatisticsService; protected readonly IUpgradableSpecification _qualityUpgradableSpecification; protected readonly IMapCoversToLocal _coverMapper; protected BookModuleWithSignalR(IBookService bookService, - IAuthorStatisticsService authorStatisticsService, - IMapCoversToLocal coverMapper, - IUpgradableSpecification qualityUpgradableSpecification, - IBroadcastSignalRMessage signalRBroadcaster) + ISeriesBookLinkService seriesBookLinkService, + IAuthorStatisticsService authorStatisticsService, + IMapCoversToLocal coverMapper, + IUpgradableSpecification qualityUpgradableSpecification, + IBroadcastSignalRMessage signalRBroadcaster) : base(signalRBroadcaster) { _bookService = bookService; + _seriesBookLinkService = seriesBookLinkService; _authorStatisticsService = authorStatisticsService; _coverMapper = coverMapper; _qualityUpgradableSpecification = qualityUpgradableSpecification; @@ -75,6 +78,22 @@ namespace Readarr.Api.V1.Books protected List MapToResource(List books, bool includeAuthor) { + var seriesLinks = _seriesBookLinkService.GetLinksByBook(books.Select(x => x.Id).ToList()) + .GroupBy(x => x.BookId) + .ToDictionary(x => x.Key, y => y.ToList()); + + foreach (var book in books) + { + if (seriesLinks.TryGetValue(book.Id, out var links)) + { + book.SeriesLinks = links; + } + else + { + book.SeriesLinks = new List(); + } + } + var result = books.ToResource(); if (includeAuthor) diff --git a/src/Readarr.Api.V1/Books/BookResource.cs b/src/Readarr.Api.V1/Books/BookResource.cs index 55f18fbf3..2134eb691 100644 --- a/src/Readarr.Api.V1/Books/BookResource.cs +++ b/src/Readarr.Api.V1/Books/BookResource.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using NzbDrone.Common.Extensions; using NzbDrone.Core.Books; using NzbDrone.Core.MediaCover; using Readarr.Api.V1.Author; @@ -13,6 +14,7 @@ namespace Readarr.Api.V1.Books public class BookResource : RestResource { public string Title { get; set; } + public string SeriesTitle { get; set; } public string Disambiguation { get; set; } public string Overview { get; set; } public int AuthorId { get; set; } @@ -60,6 +62,7 @@ namespace Readarr.Api.V1.Books PageCount = selectedEdition?.PageCount ?? 0, Genres = model.Genres, Title = selectedEdition?.Title ?? model.Title, + SeriesTitle = model.SeriesLinks.Value.Select(x => x.Series.Value.Title + (x.Position.IsNotNullOrWhiteSpace() ? $" #{x.Position}" : string.Empty)).ConcatToString("; "), Disambiguation = selectedEdition?.Disambiguation, Overview = selectedEdition?.Overview, Images = selectedEdition?.Images ?? new List(),