From c1b41b14fc1fbea097e317e9922679b33c67d851 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 13 Feb 2016 16:12:39 +0530 Subject: [PATCH] Implement picking a random book --- src/calibre/srv/code.py | 18 ++++++++++++++---- src/pyj/book_list/book_details.pyj | 14 +++++++------- src/pyj/book_list/ui.pyj | 8 ++++++-- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index f7d89b5c9d..4e6cffe1c1 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_function) -import re, hashlib +import re, hashlib, random from functools import partial from threading import Lock from json import load as load_json_file @@ -216,17 +216,27 @@ def get_books(ctx, rd): mdata[book_id] = data return ans -@endpoint('/interface-data/book-metadata/{book_id}', postprocess=json, types={'book_id': int}) +@endpoint('/interface-data/book-metadata/{book_id=0}', postprocess=json) def book_metadata(ctx, rd, book_id): ''' - Get metadata for the specified book + Get metadata for the specified book. If no book_id is specified, return metadata for a random book. Optional: ?library_id= ''' library_id, db = get_basic_query_data(ctx, rd.query)[:2] + book_ids = ctx.allowed_book_ids(rd, db) + def notfound(): + raise HTTPNotFound(_('No book with id: %d in library') % book_id) + if not book_ids: + notfound() + if not book_id: + book_id = random.choice(tuple(book_ids)) + elif book_id not in book_ids: + notfound() data = book_as_json(db, book_id) if data is None: - raise HTTPNotFound('No book with id: %d in library' % book_id) + notfound() + data['id'] = book_id return data @endpoint('/interface-data/tag-browser') diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj index eabb793226..8bb8f9f2ac 100644 --- a/src/pyj/book_list/book_details.pyj +++ b/src/pyj/book_list/book_details.pyj @@ -58,17 +58,15 @@ class BookDetailsPanel: def fetch_metadata(self, book_id): if self.is_fetching: self.is_fetching.abort() - def fetched(end_type, xhr, ev): - self.metadata_fetched(book_id, end_type, xhr, ev) - self.is_fetching = ajax('interface-data/book-metadata/' + book_id, fetched, - query={'library_id':self.interface_data.library_id}) + self.is_fetching = ajax('interface-data/book-metadata/' + book_id, self.metadata_fetched.bind(self), + query={'library_id':self.interface_data.library_id}) self.is_fetching.send() self.container.appendChild(E.div( style='margin: 1ex 1em', create_spinner(), '\xa0' + _('Fetching metadata for the book, please wait') + '…', )) - def metadata_fetched(self, book_id, end_type, xhr, event): + def metadata_fetched(self, end_type, xhr, event): if self.is_fetching is None or self.is_fetching is not xhr: return # Fetching was aborted self.is_fetching = None @@ -80,6 +78,7 @@ class BookDetailsPanel: error_dialog(_('Could not fetch metadata for book'), _('Server returned an invalid response'), err.stack or err.toString()) return clear(c) + book_id = data['id'] self.interface_data.metadata[book_id] = data self.render_book(book_id) elif end_type != 'abort': @@ -98,12 +97,13 @@ class BookDetailsPanel: alt = str.format(_('{} by {}'), metadata['title'], metadata['authors'].join(' & ')) img = E.img( src=cover_url, alt=alt, title=alt, data_title=metadata['title'], data_authors=metadata['authors'].join(' & '), - style='margin-left: 1em; max-width: 45vw; max-height: 95vh; display: block; width:auto; height:auto' + style='max-width: 45vw; max-height: 93vh; display: block; width:auto; height:auto' ) img.onerror = self.on_img_err.bind(self) c = self.container c.appendChild(E.div( - E.div(), + style='display:flex; flex-wrap: wrap; align-items:flex-start; padding: 1ex 1em', + E.div(style='margin-right: 1em'), img )) diff --git a/src/pyj/book_list/ui.pyj b/src/pyj/book_list/ui.pyj index 603e79c45b..5e8806e479 100644 --- a/src/pyj/book_list/ui.pyj +++ b/src/pyj/book_list/ui.pyj @@ -67,6 +67,9 @@ def create_book_view_top_bar_state(books_view): ibs.add_button(icon_name='ellipsis-v', tooltip=_('More actions'), action=show_panel_action('more-actions-menu')) return ibs +def random_book(): + get_boss().ui.replace_panel('book-details', extra_query_data={'book-id':'0'}) + class UI: ROOT_PANEL = 'books' @@ -85,6 +88,7 @@ class UI: self.panel_map['more-actions-menu'] = UIState(ClosePanelBar(_('More actions')), panel_data=[ create_item(_('Book List Mode'), replace_panel_action('booklist-mode-menu'), _('Change how the list of books is displayed')), + create_item(_('A Random Book'), random_book, _('Choose a random book from your library')), ]) self.panel_map['booklist-mode-menu'] = UIState(ClosePanelBar(_('Book List Mode')), panel_data=[]) @@ -132,11 +136,11 @@ class UI: else: self.show_panel(self.ROOT_PANEL) - def replace_panel(self, panel_name, force=False): + def replace_panel(self, panel_name, force=False, extra_query_data=None): action_needed = force or panel_name != self.current_panel if action_needed: self.current_panel = panel_name or self.ROOT_PANEL - get_boss().push_state(replace=True) + get_boss().push_state(replace=True, extra_query_data=extra_query_data) if action_needed: self.apply_state()