From 1adebcc7008415fc885895696fbec48b2bfe6f62 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 26 Nov 2015 13:11:21 +0530 Subject: [PATCH] Allow specifying a search as a parameter to the top-level URL --- src/calibre/srv/code.py | 22 ++++++++++++++++------ src/calibre/srv/metadata.py | 6 +++++- src/pyj/book_list/top_bar.pyj | 3 +++ src/pyj/book_list/ui.pyj | 5 ++++- src/pyj/srv.pyj | 7 ++++++- src/pyj/utils.pyj | 21 +++++++++++++++++++++ 6 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index b61c12e325..2d9fe9cf03 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -90,6 +90,7 @@ def interface_data(ctx, rd): Return the data needed to create the server main UI Optional: ?num=50&sort=timestamp.desc&library_id= + &search=''&extra_books='' ''' ans = {'username':rd.username} ans['library_map'], ans['default_library'] = ctx.library_map @@ -111,7 +112,7 @@ def interface_data(ctx, rd): except Exception: raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num')) with db.safe_read_lock: - ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders)) + ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders)) sf = db.field_metadata.ui_sortable_field_keys() sf.pop('ondevice', None) ans['sortable_fields'] = sorted((( @@ -120,9 +121,16 @@ def interface_data(ctx, rd): ans['field_metadata'] = db.field_metadata.all_metadata() ans['icon_map'] = icon_map() mdata = ans['metadata'] = {} - for book_id in ans['search_result']['book_ids']: - data = book_as_json(db, book_id) - mdata[book_id] = data + try: + extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(',')) + except Exception: + extra_books = () + for coll in (ans['search_result']['book_ids'], extra_books): + for book_id in coll: + if book_id not in mdata: + data = book_as_json(db, book_id) + if data is not None: + mdata[book_id] = data return ans @@ -153,7 +161,8 @@ def more_books(ctx, rd): mdata = ans['metadata'] = {} for book_id in ans['search_result']['book_ids']: data = book_as_json(db, book_id) - mdata[book_id] = data + if data is not None: + mdata[book_id] = data return ans @@ -197,7 +206,8 @@ def get_books(ctx, rd): raise HTTPBadRequest('Invalid search expression: %s' % as_unicode(err)) for book_id in ans['search_result']['book_ids']: data = book_as_json(db, book_id) - mdata[book_id] = data + if data is not None: + mdata[book_id] = data return ans @endpoint('/interface-data/tag-browser', postprocess=json) diff --git a/src/calibre/srv/metadata.py b/src/calibre/srv/metadata.py index 674ccb6812..47d19f3fcf 100644 --- a/src/calibre/srv/metadata.py +++ b/src/calibre/srv/metadata.py @@ -33,11 +33,13 @@ def encode_datetime(dateval): return None return isoformat(dateval) +empty_val = ((), '', {}) + def add_field(field, db, book_id, ans, field_metadata): datatype = field_metadata.get('datatype') if datatype is not None: val = db._field_for(field, book_id) - if val is not None and val != (): + if val is not None and val not in empty_val: if datatype == 'datetime': val = encode_datetime(val) if val is None: @@ -48,6 +50,8 @@ def book_as_json(db, book_id): db = db.new_api with db.safe_read_lock: ans = {'formats':db._formats(book_id)} + if not ans['formats'] and not db.has_id(book_id): + return None fm = db.field_metadata for field in fm.all_field_keys(): if field not in IGNORED_FIELDS: diff --git a/src/pyj/book_list/top_bar.pyj b/src/pyj/book_list/top_bar.pyj index 10ed3bf7ed..312876aded 100644 --- a/src/pyj/book_list/top_bar.pyj +++ b/src/pyj/book_list/top_bar.pyj @@ -57,6 +57,9 @@ class TopBar: if not tooltip: tooltip = _('Donate to support calibre development') + if callable(title): + title = title() + for bar in self.bar, self.dummy_bar: left = bar.firstChild clear(left) diff --git a/src/pyj/book_list/ui.pyj b/src/pyj/book_list/ui.pyj index 37889c5388..c29d06bf7b 100644 --- a/src/pyj/book_list/ui.pyj +++ b/src/pyj/book_list/ui.pyj @@ -88,7 +88,10 @@ class UI: self.books_view = BooksView(interface_data, book_list_container) self.items_view = ItemsView(interface_data, book_list_container) self.search_panel = SearchPanel(interface_data, book_list_container) - ibs = BarState(run_animation=True) + ibs = BarState(run_animation=True, title=def(): + q = self.books_view.interface_data['search_result']['query'] + return (_('Books matching:') + ' ' + q) if q else 'calibre' + ) ibs.add_button(icon_name='sort-amount-desc', tooltip=_('Sort books'), action=show_panel_action('booklist-sort-menu')) ibs.add_button(icon_name='search', tooltip=_('Search for books'), action=show_panel_action('booklist-search')) ibs.add_button(icon_name='ellipsis-v', tooltip=_('More actions'), action=show_panel_action('more-actions-menu')) diff --git a/src/pyj/srv.pyj b/src/pyj/srv.pyj index ac838eba3e..e7fa32610c 100644 --- a/src/pyj/srv.pyj +++ b/src/pyj/srv.pyj @@ -3,10 +3,12 @@ from ajax import ajax from elementmaker import E +from gettext import gettext as _ from session import UserSessionData +from utils import parse_url_params + from book_list.boss import Boss from book_list.globals import set_boss, set_session_data -from gettext import gettext as _ def on_library_loaded(end_type, xhr, ev): p = document.getElementById('page_load_progress') @@ -35,6 +37,9 @@ def load_book_list(): query = {k:temp.get(k) for k in str.split( 'library_id sort partition_method collapse_at dont_collapse sort_tags_by' )} + url_query = parse_url_params() + for key in url_query: + query[key] = url_query[key] ajax('interface-data/init', on_library_loaded, on_library_load_progress, query=query).send() def on_load(): diff --git a/src/pyj/utils.pyj b/src/pyj/utils.pyj index 2261c83f04..49a17dee0f 100644 --- a/src/pyj/utils.pyj +++ b/src/pyj/utils.pyj @@ -33,3 +33,24 @@ def to_utf8(string): ua[i] = ch.charCodeAt(0) ) return ua + +def parse_url_params(url=None, allow_multiple=False): + url = url or window.location.href + qs = url.indexOf('?') + ans = {} + if qs < 0: + return ans + q = url.slice(qs + 1, ((url.indexOf('#') + 1) or (url.length + 1))) + if not q: + return ans + pairs = q.replace(/\+/g, " ").split("&") + for pair in pairs: + key, val = str.partition(pair, '=')[::2] + key, val = decodeURIComponent(key), decodeURIComponent(val) + if allow_multiple: + if not Object.prototype.hasOwnProperty.call(ans, key): + ans[key] = [] + ans[key].append(val) + else: + ans[key] = val + return ans