New: Refresh button on book page that bypasses cache

pull/434/head
ta264 4 years ago
parent b93bc1e5e5
commit 2f8ac793ff

@ -117,6 +117,7 @@ class BookDetails extends Component {
images, images,
links, links,
isSaving, isSaving,
isRefreshing,
isFetching, isFetching,
isPopulated, isPopulated,
bookFilesError, bookFilesError,
@ -127,6 +128,7 @@ class BookDetails extends Component {
nextBook, nextBook,
isSearching, isSearching,
onMonitorTogglePress, onMonitorTogglePress,
onRefreshPress,
onSearchPress onSearchPress
} = this.props; } = this.props;
@ -142,6 +144,15 @@ class BookDetails extends Component {
<PageContent title={title}> <PageContent title={title}>
<PageToolbar> <PageToolbar>
<PageToolbarSection> <PageToolbarSection>
<PageToolbarButton
label="Refresh"
iconName={icons.REFRESH}
spinningName={icons.REFRESH}
title="Refresh information"
isSpinning={isRefreshing}
onPress={onRefreshPress}
/>
<PageToolbarButton <PageToolbarButton
label="Search Book" label="Search Book"
iconName={icons.SEARCH} iconName={icons.SEARCH}
@ -473,6 +484,7 @@ BookDetails.propTypes = {
monitored: PropTypes.bool.isRequired, monitored: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired, shortDateFormat: PropTypes.string.isRequired,
isSaving: PropTypes.bool.isRequired, isSaving: PropTypes.bool.isRequired,
isRefreshing: PropTypes.bool,
isSearching: PropTypes.bool, isSearching: PropTypes.bool,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
isPopulated: PropTypes.bool, isPopulated: PropTypes.bool,

@ -70,6 +70,12 @@ function createMapStateToProps() {
isSearchingCommand.body.bookIds.indexOf(book.id) > -1 isSearchingCommand.body.bookIds.indexOf(book.id) > -1
); );
const isRefreshingCommand = findCommand(commands, { name: commandNames.REFRESH_BOOK });
const isRefreshing = (
isCommandExecuting(isRefreshingCommand) &&
isRefreshingCommand.body.bookId === book.id
);
const isFetching = isBookFilesFetching; const isFetching = isBookFilesFetching;
const isPopulated = isBookFilesPopulated; const isPopulated = isBookFilesPopulated;
@ -77,6 +83,7 @@ function createMapStateToProps() {
...book, ...book,
shortDateFormat: uiSettings.shortDateFormat, shortDateFormat: uiSettings.shortDateFormat,
author, author,
isRefreshing,
isSearching, isSearching,
isFetching, isFetching,
isPopulated, isPopulated,
@ -147,6 +154,13 @@ class BookDetailsConnector extends Component {
}); });
} }
onRefreshPress = () => {
this.props.executeCommand({
name: commandNames.REFRESH_BOOK,
bookId: this.props.id
});
}
onSearchPress = () => { onSearchPress = () => {
this.props.executeCommand({ this.props.executeCommand({
name: commandNames.BOOK_SEARCH, name: commandNames.BOOK_SEARCH,
@ -162,6 +176,7 @@ class BookDetailsConnector extends Component {
<BookDetails <BookDetails
{...this.props} {...this.props}
onMonitorTogglePress={this.onMonitorTogglePress} onMonitorTogglePress={this.onMonitorTogglePress}
onRefreshPress={this.onRefreshPress}
onSearchPress={this.onSearchPress} onSearchPress={this.onSearchPress}
/> />
); );

@ -12,6 +12,7 @@ export const INTERACTIVE_IMPORT = 'ManualImport';
export const MISSING_BOOK_SEARCH = 'MissingBookSearch'; export const MISSING_BOOK_SEARCH = 'MissingBookSearch';
export const MOVE_AUTHOR = 'MoveAuthor'; export const MOVE_AUTHOR = 'MoveAuthor';
export const REFRESH_AUTHOR = 'RefreshAuthor'; export const REFRESH_AUTHOR = 'RefreshAuthor';
export const REFRESH_BOOK = 'RefreshBook';
export const RENAME_FILES = 'RenameFiles'; export const RENAME_FILES = 'RenameFiles';
export const RENAME_AUTHOR = 'RenameAuthor'; export const RENAME_AUTHOR = 'RenameAuthor';
export const RESCAN_FOLDERS = 'RescanFolders'; export const RESCAN_FOLDERS = 'RescanFolders';

@ -32,8 +32,8 @@ namespace NzbDrone.Core.Test.Framework
var httpClient = Mocker.Resolve<IHttpClient>(); var httpClient = Mocker.Resolve<IHttpClient>();
Mocker.GetMock<ICachedHttpResponseService>() Mocker.GetMock<ICachedHttpResponseService>()
.Setup(x => x.Get(It.IsAny<HttpRequest>(), It.IsAny<TimeSpan>())) .Setup(x => x.Get(It.IsAny<HttpRequest>(), It.IsAny<bool>(), It.IsAny<TimeSpan>()))
.Returns((HttpRequest request, TimeSpan ttl) => httpClient.Get(request)); .Returns((HttpRequest request, bool useCavhe, TimeSpan ttl) => httpClient.Get(request));
} }
} }

@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.MusicTests
.Build(); .Build();
Mocker.GetMock<IProvideBookInfo>() Mocker.GetMock<IProvideBookInfo>()
.Setup(s => s.GetBookInfo(readarrId)) .Setup(s => s.GetBookInfo(readarrId, true))
.Returns(Tuple.Create(_fakeArtist.Metadata.Value.ForeignAuthorId, .Returns(Tuple.Create(_fakeArtist.Metadata.Value.ForeignAuthorId,
_fakeAlbum, _fakeAlbum,
new List<AuthorMetadata> { _fakeArtist.Metadata.Value })); new List<AuthorMetadata> { _fakeArtist.Metadata.Value }));
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Test.MusicTests
var newAlbum = AlbumToAdd("edition", "book", "author"); var newAlbum = AlbumToAdd("edition", "book", "author");
Mocker.GetMock<IProvideBookInfo>() Mocker.GetMock<IProvideBookInfo>()
.Setup(s => s.GetBookInfo("edition")) .Setup(s => s.GetBookInfo("edition", true))
.Throws(new BookNotFoundException("edition")); .Throws(new BookNotFoundException("edition"));
Assert.Throws<ValidationException>(() => Subject.AddBook(newAlbum)); Assert.Throws<ValidationException>(() => Subject.AddBook(newAlbum));

@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.MusicTests
private void GivenValidArtist(string readarrId) private void GivenValidArtist(string readarrId)
{ {
Mocker.GetMock<IProvideAuthorInfo>() Mocker.GetMock<IProvideAuthorInfo>()
.Setup(s => s.GetAuthorInfo(readarrId)) .Setup(s => s.GetAuthorInfo(readarrId, true))
.Returns(_fakeArtist); .Returns(_fakeArtist);
} }
@ -113,7 +113,7 @@ namespace NzbDrone.Core.Test.MusicTests
}; };
Mocker.GetMock<IProvideAuthorInfo>() Mocker.GetMock<IProvideAuthorInfo>()
.Setup(s => s.GetAuthorInfo(newArtist.ForeignAuthorId)) .Setup(s => s.GetAuthorInfo(newArtist.ForeignAuthorId, true))
.Throws(new AuthorNotFoundException(newArtist.ForeignAuthorId)); .Throws(new AuthorNotFoundException(newArtist.ForeignAuthorId));
Mocker.GetMock<IAddAuthorValidator>() Mocker.GetMock<IAddAuthorValidator>()

@ -5,10 +5,13 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Books.Commands;
using NzbDrone.Core.Books.Events; using NzbDrone.Core.Books.Events;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MetadataSource; using NzbDrone.Core.MetadataSource;
@ -20,12 +23,15 @@ namespace NzbDrone.Core.Books
bool RefreshBookInfo(List<Book> books, List<Book> remoteBooks, Author remoteData, bool forceBookRefresh, bool forceUpdateFileTags, DateTime? lastUpdate); bool RefreshBookInfo(List<Book> books, List<Book> remoteBooks, Author remoteData, bool forceBookRefresh, bool forceUpdateFileTags, DateTime? lastUpdate);
} }
public class RefreshBookService : RefreshEntityServiceBase<Book, Edition>, IRefreshBookService public class RefreshBookService : RefreshEntityServiceBase<Book, Edition>,
IRefreshBookService,
IExecute<RefreshBookCommand>
{ {
private readonly IBookService _bookService; private readonly IBookService _bookService;
private readonly IAuthorService _authorService; private readonly IAuthorService _authorService;
private readonly IAddAuthorService _addAuthorService; private readonly IAddAuthorService _addAuthorService;
private readonly IEditionService _editionService; private readonly IEditionService _editionService;
private readonly IProvideAuthorInfo _authorInfo;
private readonly IProvideBookInfo _bookInfo; private readonly IProvideBookInfo _bookInfo;
private readonly IRefreshEditionService _refreshEditionService; private readonly IRefreshEditionService _refreshEditionService;
private readonly IMediaFileService _mediaFileService; private readonly IMediaFileService _mediaFileService;
@ -40,6 +46,7 @@ namespace NzbDrone.Core.Books
IAddAuthorService addAuthorService, IAddAuthorService addAuthorService,
IEditionService editionService, IEditionService editionService,
IAuthorMetadataService authorMetadataService, IAuthorMetadataService authorMetadataService,
IProvideAuthorInfo authorInfo,
IProvideBookInfo bookInfo, IProvideBookInfo bookInfo,
IRefreshEditionService refreshEditionService, IRefreshEditionService refreshEditionService,
IMediaFileService mediaFileService, IMediaFileService mediaFileService,
@ -54,6 +61,7 @@ namespace NzbDrone.Core.Books
_authorService = authorService; _authorService = authorService;
_addAuthorService = addAuthorService; _addAuthorService = addAuthorService;
_editionService = editionService; _editionService = editionService;
_authorInfo = authorInfo;
_bookInfo = bookInfo; _bookInfo = bookInfo;
_refreshEditionService = refreshEditionService; _refreshEditionService = refreshEditionService;
_mediaFileService = mediaFileService; _mediaFileService = mediaFileService;
@ -64,6 +72,32 @@ namespace NzbDrone.Core.Books
_logger = logger; _logger = logger;
} }
private Author GetSkyhookData(Book book)
{
var foreignId = book.Editions.Value.First().ForeignEditionId;
try
{
var tuple = _bookInfo.GetBookInfo(foreignId, false);
var author = _authorInfo.GetAuthorInfo(tuple.Item1, false);
var newbook = tuple.Item2;
newbook.Author = author;
newbook.AuthorMetadata = author.Metadata.Value;
newbook.AuthorMetadataId = book.AuthorMetadataId;
newbook.AuthorMetadata.Value.Id = book.AuthorMetadataId;
author.Books = new List<Book> { newbook };
return author;
}
catch (BookNotFoundException)
{
_logger.Error($"Could not find book with id {foreignId}");
}
return null;
}
protected override RemoteData GetRemoteData(Book local, List<Book> remote, Author data) protected override RemoteData GetRemoteData(Book local, List<Book> remote, Author data)
{ {
var result = new RemoteData(); var result = new RemoteData();
@ -326,5 +360,22 @@ namespace NzbDrone.Core.Books
{ {
return RefreshEntityInfo(book, remoteBooks, remoteData, true, forceUpdateFileTags, null); return RefreshEntityInfo(book, remoteBooks, remoteData, true, forceUpdateFileTags, null);
} }
public bool RefreshBookInfo(Book book)
{
var data = GetSkyhookData(book);
return RefreshBookInfo(book, data.Books, data, false);
}
public void Execute(RefreshBookCommand message)
{
if (message.BookId.HasValue)
{
var book = _bookService.GetBook(message.BookId.Value);
RefreshBookInfo(book);
}
}
} }
} }

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Http
{ {
public interface ICachedHttpResponseService public interface ICachedHttpResponseService
{ {
HttpResponse Get(HttpRequest request, TimeSpan ttl); HttpResponse Get(HttpRequest request, bool useCache, TimeSpan ttl);
} }
public class CachedHttpResponseService : ICachedHttpResponseService public class CachedHttpResponseService : ICachedHttpResponseService
@ -21,11 +21,11 @@ namespace NzbDrone.Core.Http
_httpClient = httpClient; _httpClient = httpClient;
} }
public HttpResponse Get(HttpRequest request, TimeSpan ttl) public HttpResponse Get(HttpRequest request, bool useCache, TimeSpan ttl)
{ {
var cached = _repo.FindByUrl(request.Url.ToString()); var cached = _repo.FindByUrl(request.Url.ToString());
if (cached != null && cached.Expiry > DateTime.UtcNow) if (useCache && cached != null && cached.Expiry > DateTime.UtcNow)
{ {
return new HttpResponse(request, new HttpHeader(), cached.Value, (HttpStatusCode)cached.StatusCode); return new HttpResponse(request, new HttpHeader(), cached.Value, (HttpStatusCode)cached.StatusCode);
} }

@ -73,7 +73,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
return null; return null;
} }
public Author GetAuthorInfo(string foreignAuthorId) public Author GetAuthorInfo(string foreignAuthorId, bool useCache = true)
{ {
_logger.Debug("Getting Author details GoodreadsId of {0}", foreignAuthorId); _logger.Debug("Getting Author details GoodreadsId of {0}", foreignAuthorId);
@ -85,7 +85,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
httpRequest.AllowAutoRedirect = true; httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true; httpRequest.SuppressHttpError = true;
var httpResponse = _cachedHttpClient.Get(httpRequest, TimeSpan.FromDays(30)); var httpResponse = _cachedHttpClient.Get(httpRequest, useCache, TimeSpan.FromDays(30));
if (httpResponse.HasHttpError) if (httpResponse.HasHttpError)
{ {
@ -217,7 +217,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
httpRequest.AllowAutoRedirect = true; httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true; httpRequest.SuppressHttpError = true;
var httpResponse = _cachedHttpClient.Get(httpRequest, TimeSpan.FromDays(7)); var httpResponse = _cachedHttpClient.Get(httpRequest, true, TimeSpan.FromDays(7));
if (httpResponse.HasHttpError) if (httpResponse.HasHttpError)
{ {
@ -249,7 +249,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
httpRequest.AllowAutoRedirect = true; httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true; httpRequest.SuppressHttpError = true;
var httpResponse = _cachedHttpClient.Get(httpRequest, TimeSpan.FromDays(90)); var httpResponse = _cachedHttpClient.Get(httpRequest, true, TimeSpan.FromDays(90));
if (httpResponse.HasHttpError) if (httpResponse.HasHttpError)
{ {
@ -311,7 +311,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
return null; return null;
} }
public Tuple<string, Book, List<AuthorMetadata>> GetBookInfo(string foreignEditionId) public Tuple<string, Book, List<AuthorMetadata>> GetBookInfo(string foreignEditionId, bool useCache = true)
{ {
_logger.Debug("Getting Book with GoodreadsId of {0}", foreignEditionId); _logger.Debug("Getting Book with GoodreadsId of {0}", foreignEditionId);
@ -323,7 +323,7 @@ namespace NzbDrone.Core.MetadataSource.Goodreads
httpRequest.AllowAutoRedirect = true; httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true; httpRequest.SuppressHttpError = true;
var httpResponse = _cachedHttpClient.Get(httpRequest, TimeSpan.FromDays(90)); var httpResponse = _cachedHttpClient.Get(httpRequest, useCache, TimeSpan.FromDays(90));
if (httpResponse.HasHttpError) if (httpResponse.HasHttpError)
{ {

@ -6,7 +6,7 @@ namespace NzbDrone.Core.MetadataSource
{ {
public interface IProvideAuthorInfo public interface IProvideAuthorInfo
{ {
Author GetAuthorInfo(string readarrId); Author GetAuthorInfo(string readarrId, bool useCache = true);
Author GetAuthorAndBooks(string readarrId, double minPopularity = 0); Author GetAuthorAndBooks(string readarrId, double minPopularity = 0);
HashSet<string> GetChangedArtists(DateTime startTime); HashSet<string> GetChangedArtists(DateTime startTime);
} }

@ -6,7 +6,7 @@ namespace NzbDrone.Core.MetadataSource
{ {
public interface IProvideBookInfo public interface IProvideBookInfo
{ {
Tuple<string, Book, List<AuthorMetadata>> GetBookInfo(string id); Tuple<string, Book, List<AuthorMetadata>> GetBookInfo(string id, bool useCache = true);
HashSet<string> GetChangedBooks(DateTime startTime); HashSet<string> GetChangedBooks(DateTime startTime);
} }
} }

Loading…
Cancel
Save