diff --git a/src/calibre/srv/TODO.rst b/src/calibre/srv/TODO.rst index 3a5432d293..a3e83cdc2f 100644 --- a/src/calibre/srv/TODO.rst +++ b/src/calibre/srv/TODO.rst @@ -20,9 +20,6 @@ New features for the server generally - Create a UI for making changes to the library such as editing metadta, adding/deleting books, converting, sending by email, etc. -- Add links to easily search goodread/amazon/google books from the book - details page - - Add a way to search the set of locally available books stored in offline storage. diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj index a570144c47..3c2c383340 100644 --- a/src/pyj/book_list/book_details.pyj +++ b/src/pyj/book_list/book_details.pyj @@ -6,7 +6,8 @@ import traceback from elementmaker import E from gettext import gettext as _ -from ajax import ajax +from ajax import ajax, encode_query_component +from book_list.item_list import create_item, create_item_list from book_list.library_data import ( book_metadata, cover_url, current_library_id, current_virtual_library, download_url, library_data, load_status, set_book_metadata @@ -29,6 +30,7 @@ from widgets import create_button, create_spinner bd_counter = 0 CLASS_NAME = 'book-details-panel' +SEARCH_INTERNET_CLASS = 'book-details-search-internet' FORMAT_PRIORITIES = [ 'EPUB', 'AZW3', 'DOCX', 'LIT', 'MOBI', 'ODT', 'RTF', 'MD', 'MARKDOWN', 'TXT', 'PDF' ] @@ -347,6 +349,11 @@ add_extra_css(def(): style += build_rule(sel + 'table.metadata a[href]', color='blue') style += build_rule(sel + 'table.metadata a[href]:hover', color=get_color('window-hover-foreground')) style += build_rule(sel + 'table.metadata a[href]:active', color=get_color('window-hover-foreground'), transform='scale(1.5)') + + sel = '.' + SEARCH_INTERNET_CLASS + style += build_rule(sel, margin='1ex 1em') + style += build_rule(sel + ' ul > li', list_style_type='none') + style += build_rule(sel + ' ul > li > a', padding='2ex 1em', display='block', width='100%') return style ) @@ -388,6 +395,7 @@ def download_book(book_id): def render_book(container_id, book_id): + render_book.book_id = book_id c = document.getElementById(container_id) if not c: return @@ -424,6 +432,15 @@ def render_book(container_id, book_id): render_metadata(metadata, table, book_id) +def add_top_bar_buttons(container_id): + container = document.getElementById(container_id) + if container: + book_id = parse_url_params().book_id + if book_id is '0': + add_button(container.parentNode, 'random', def(): fetch_metadata(container_id, 0);) + add_button(container, 'ellipsis-v', action=show_subsequent_panel.bind(None, 'more_actions'), tooltip=_('More actions')) + + def metadata_fetched(container_id, book_id, end_type, xhr, event): nonlocal current_fetch if current_fetch is None or current_fetch is not xhr: @@ -443,6 +460,7 @@ def metadata_fetched(container_id, book_id, end_type, xhr, event): book_id = int(data['id']) set_book_metadata(book_id, data) render_book(container_id, book_id) + add_top_bar_buttons(container_id) elif end_type is not 'abort': clear(c) c.appendChild(E.div( @@ -480,9 +498,8 @@ def create_book_details(container): container_id = container.parentNode.id if current_book_id is not 0 and book_metadata(current_book_id): render_book(container_id, current_book_id) + add_top_bar_buttons(container_id) else: - if current_book_id is 0: - add_button(container, 'random', def(): fetch_metadata(container_id, 0);) fetch_metadata(container_id, current_book_id) @@ -526,4 +543,64 @@ def init(container_id): conditional_timeout(container_id, 5, check_for_books_loaded) +def show_subsequent_panel(name, replace=False): + q = parse_url_params() + q.book_id = (read_book.book_id or q.book_id) + '' + show_panel('book_details^' + name, query=q, replace=replace) + + +def create_more_actions_panel(container_id): + container = document.getElementById(container_id) + create_top_bar(container, title=_('More actions…'), action=back, icon='close') + items = [ + create_item(_('Search the internet'), subtitle=_('Search for this author or book on various websites'), + action=def(): + show_subsequent_panel('search_internet', replace=True) + ), + ] + container.appendChild(E.div()) + create_item_list(container.lastChild, items) + + +def return_to_book_details(): + q = parse_url_params() + show_panel('book_details', query=q, replace=True) + + +def url_for(template, data): + def eqc(x): + return encode_query_component(x).replace(/%20/g, '+') + return template.format(title=eqc(data.title), author=eqc(data.author)) + + +def search_internet(container_id): + if not render_book.book_id or not book_metadata(render_book.book_id): + return return_to_book_details() + container = document.getElementById(container_id) + create_top_bar(container, title=_('Search the internet'), action=back, icon='close') + mi = book_metadata(render_book.book_id) + data = {'title':mi.title, 'author':mi.authors[0]} + + def link_for(name, template): + return E.a(name, class_='simple-link', href=url_for(template, data), target="_blank") + + container.appendChild(E.div(class_=SEARCH_INTERNET_CLASS, + safe_set_inner_html(E.h2(), _('Search for the author {} at:').format(data.author)), + E.ul( + E.li(link_for(_('Goodreads'), 'https://www.goodreads.com/book/author/{author}')), + E.li(link_for(_('Wikipedia'), 'https://en.wikipedia.org/w/index.php?search={author}')), + E.li(link_for(_('Google books'), 'https://www.google.com/search?tbm=bks&q=inauthor:%22{author}%22')), + ), + E.hr(), + safe_set_inner_html(E.h2(), _('Search for the book {} at:').format(data.title)), + E.ul( + E.li(link_for(_('Goodreads'), 'https://www.goodreads.com/search?q={author}+{title}&search%5Bsource%5D=goodreads&search_type=books&tab=books')), + E.li(link_for(_('Google books'), 'https://www.google.com/search?tbm=bks&q=inauthor:%22{author}%22+intitle:%22{title}%22')), + E.li(link_for(_('Amazon'), 'https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks&field-keywords={author}+{title}')), + ), + + )) + set_panel_handler('book_details', init) +set_panel_handler('book_details^more_actions', create_more_actions_panel) +set_panel_handler('book_details^search_internet', search_internet)