diff --git a/src/calibre/srv/content.py b/src/calibre/srv/content.py index 235a175538..1fbd93808d 100644 --- a/src/calibre/srv/content.py +++ b/src/calibre/srv/content.py @@ -205,7 +205,7 @@ def get(ctx, rd, what, book_id, library_id): if db is None: raise HTTPNotFound('Library %r not found' % library_id) with db.safe_read_lock: - if not db.has_id(book_id): + if book_id not in ctx.allowed_book_ids(rd, db): raise HTTPNotFound('Book with id %r does not exist' % book_id) library_id = db.server_library_id # in case library_id was None if what == 'thumb': diff --git a/src/calibre/srv/handler.py b/src/calibre/srv/handler.py index 26e3c68980..62059f7d93 100644 --- a/src/calibre/srv/handler.py +++ b/src/calibre/srv/handler.py @@ -7,6 +7,7 @@ __license__ = 'GPL v3' __copyright__ = '2015, Kovid Goyal ' import os +from collections import OrderedDict from importlib import import_module from threading import Lock @@ -14,6 +15,7 @@ from calibre import force_unicode from calibre.db.cache import Cache from calibre.db.legacy import create_backend, LibraryDatabase from calibre.srv.routes import Router +from calibre.utils.date import utcnow def init_library(library_path): db = Cache(create_backend(library_path)) @@ -36,6 +38,8 @@ class LibraryBroker(object): if path is libraries[0]: self.default_library = library_id self.lmap[library_id] = path + self.category_caches = {lid:OrderedDict() for lid in self.lmap} + self.search_caches = {lid:OrderedDict() for lid in self.lmap} def get(self, library_id=None): with self.lock: @@ -57,6 +61,8 @@ class Context(object): log = None url_for = None + CATEGORY_CACHE_SIZE = 25 + SEARCH_CACHE_SIZE = 100 def __init__(self, libraries, opts, testing=False): self.opts = opts @@ -72,14 +78,43 @@ class Context(object): def get_library(self, library_id=None): return self.library_broker.get(library_id) - def restrict_to_ids(self, db, data): + def allowed_book_ids(self, data, db): # TODO: Implement this based on data.username caching result on the # data object - ans = data.restrict_to_ids.get(db.server_library_id) + ans = data.allowed_book_ids.get(db.server_library_id) if ans is None: - ans = data.restrict_to_ids[db.server_library_id] = db.all_book_ids() + ans = data.allowed_book_ids[db.server_library_id] = db.all_book_ids() return ans + def get_categories(self, data, db, restrict_to_ids=None): + if restrict_to_ids is None: + restrict_to_ids = self.allowed_book_ids(data, db) + cache = self.library_broker.category_caches[db.server_library_id] + old = cache.pop(restrict_to_ids, None) + if old is None or old[0] <= db.last_modified(): + categories = db.get_categories(book_ids=restrict_to_ids) + cache[restrict_to_ids] = old = (utcnow(), categories) + if len(cache) > self.CATEGORY_CACHE_SIZE: + cache.popitem(last=False) + else: + cache[restrict_to_ids] = old + return old[1] + + def search(self, data, db, query, restrict_to_ids=None): + if restrict_to_ids is None: + restrict_to_ids = self.allowed_book_ids(data, db) + cache = self.library_broker.search_caches[db.server_library_id] + key = (query, restrict_to_ids) + old = cache.pop(key, None) + if old is None or old[0] < db.clear_search_cache_count: + matches = db.search(query, book_ids=restrict_to_ids) + cache[key] = old = (db.clear_search_cache_count, matches) + if len(self._search_cache) > self.SEARCH_CACHE_SIZE: + cache.popitem(last=False) + else: + cache[key] = old + return old[1] + class Handler(object): def __init__(self, libraries, opts, testing=False):