IGN: Cache path information in memory as well. Serve default cover when cover is missing

This commit is contained in:
Kovid Goyal 2008-11-03 16:12:16 -08:00
parent 5bcd1957a6
commit 43e6f6d8cb
6 changed files with 51 additions and 27 deletions

View File

@ -53,7 +53,7 @@ def sanitize_file_name(name, substitute='_'):
FIELD_MAP = {'id':0, 'title':1, 'authors':2, 'publisher':3, 'rating':4, 'timestamp':5, FIELD_MAP = {'id':0, 'title':1, 'authors':2, 'publisher':3, 'rating':4, 'timestamp':5,
'size':6, 'tags':7, 'comments':8, 'series':9, 'series_index':10, 'size':6, 'tags':7, 'comments':8, 'series':9, 'series_index':10,
'sort':11, 'author_sort':12, 'formats':13, 'isbn':14} 'sort':11, 'author_sort':12, 'formats':13, 'isbn':14, 'path':15}
INDEX_MAP = dict(zip(FIELD_MAP.values(), FIELD_MAP.keys())) INDEX_MAP = dict(zip(FIELD_MAP.values(), FIELD_MAP.keys()))
@ -414,15 +414,39 @@ class LibraryDatabase2(LibraryDatabase):
self.conn.executescript(script%dict(ltable='tags', table='tags', ltable_col='tag')) self.conn.executescript(script%dict(ltable='tags', table='tags', ltable_col='tag'))
self.conn.executescript(script%dict(ltable='series', table='series', ltable_col='series')) self.conn.executescript(script%dict(ltable='series', table='series', ltable_col='series'))
def upgrade_version_3(self):
' Add path to result cache '
self.conn.executescript('''
DROP VIEW meta;
CREATE VIEW meta AS
SELECT id, title,
(SELECT concat(name) FROM authors WHERE authors.id IN (SELECT author from books_authors_link WHERE book=books.id)) authors,
(SELECT name FROM publishers WHERE publishers.id IN (SELECT publisher from books_publishers_link WHERE book=books.id)) publisher,
(SELECT rating FROM ratings WHERE ratings.id IN (SELECT rating from books_ratings_link WHERE book=books.id)) rating,
timestamp,
(SELECT MAX(uncompressed_size) FROM data WHERE book=books.id) size,
(SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=books.id)) tags,
(SELECT text FROM comments WHERE book=books.id) comments,
(SELECT name FROM series WHERE series.id IN (SELECT series FROM books_series_link WHERE book=books.id)) series,
series_index,
sort,
author_sort,
(SELECT concat(format) FROM data WHERE data.book=books.id) formats,
isbn,
path
FROM books;
''')
def last_modified(self): def last_modified(self):
''' Return last modified time as a UTC datetime object''' ''' Return last modified time as a UTC datetime object'''
return datetime.utcfromtimestamp(os.stat(self.dbpath).st_mtime) return datetime.utcfromtimestamp(os.stat(self.dbpath).st_mtime)
def path(self, index, index_is_id=False): def path(self, index, index_is_id=False):
'Return the relative path to the directory containing this books files as a unicode string.' 'Return the relative path to the directory containing this books files as a unicode string.'
id = index if index_is_id else self.id(index) row = self.data._data[index] if index_is_id else self.data[index]
path = self.conn.get('SELECT path FROM books WHERE id=?', (id,), all=False).replace('/', os.sep) return row[FIELD_MAP['path']].replace('/', os.sep)
return path
def abspath(self, index, index_is_id=False): def abspath(self, index, index_is_id=False):
'Return the absolute path to the directory containing this books files as a unicode string.' 'Return the absolute path to the directory containing this books files as a unicode string.'
@ -496,6 +520,7 @@ class LibraryDatabase2(LibraryDatabase):
self.add_format(id, format, stream, index_is_id=True, path=tpath) self.add_format(id, format, stream, index_is_id=True, path=tpath)
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id)) self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
self.conn.commit() self.conn.commit()
self.data.set(id, FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories # Delete not needed directories
if current_path and os.path.exists(spath): if current_path and os.path.exists(spath):
if normpath(spath) != normpath(tpath): if normpath(spath) != normpath(tpath):
@ -650,8 +675,8 @@ class LibraryDatabase2(LibraryDatabase):
''' '''
Removes book from the result cache and the underlying database. Removes book from the result cache and the underlying database.
''' '''
path = os.path.join(self.library_path, self.path(id, index_is_id=True))
self.data.remove(id) self.data.remove(id)
path = os.path.join(self.library_path, self.path(id, True))
if os.path.exists(path): if os.path.exists(path):
shutil.rmtree(path) shutil.rmtree(path)
parent = os.path.dirname(path) parent = os.path.dirname(path)

View File

@ -123,12 +123,11 @@ class LibraryServer(object):
def get_cover(self, id, thumbnail=False): def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=True) cover = self.db.cover(id, index_is_id=True, as_file=True)
if cover is None: if cover is None:
raise cherrypy.HTTPError(404, 'no cover available for id: %d'%id) cover = cStringIO.StringIO(server_resources['default_cover.jpg'])
cherrypy.response.headers['Content-Type'] = 'image/jpeg' cherrypy.response.headers['Content-Type'] = 'image/jpeg'
path = getattr(cover, 'name', None) path = getattr(cover, 'name', False)
if path and os.path.exists(path): updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) if path and os.access(path, os.R_OK) else build_time
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
if not thumbnail: if not thumbnail:
return cover.read() return cover.read()
try: try:

View File

@ -11,9 +11,12 @@ import sqlite3 as sqlite, traceback, time
from sqlite3 import IntegrityError from sqlite3 import IntegrityError
from threading import Thread from threading import Thread
from Queue import Queue from Queue import Queue
from threading import RLock
from calibre.library import title_sort from calibre.library import title_sort
global_lock = RLock()
class Concatenate(object): class Concatenate(object):
'''String concatenation aggregator for sqlite''' '''String concatenation aggregator for sqlite'''
def __init__(self, sep=','): def __init__(self, sep=','):
@ -93,15 +96,16 @@ class DatabaseException(Exception):
def proxy(fn): def proxy(fn):
''' Decorator to call methods on the database connection in the proxy thread ''' ''' Decorator to call methods on the database connection in the proxy thread '''
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
if self.proxy.unhandled_error[0] is not None: with global_lock:
raise DatabaseException(*self.proxy.unhandled_error) if self.proxy.unhandled_error[0] is not None:
self.proxy.requests.put((fn.__name__, args, kwargs)) raise DatabaseException(*self.proxy.unhandled_error)
ok, res = self.proxy.results.get() self.proxy.requests.put((fn.__name__, args, kwargs))
if not ok: ok, res = self.proxy.results.get()
if isinstance(res[0], IntegrityError): if not ok:
raise IntegrityError(unicode(res[0])) if isinstance(res[0], IntegrityError):
raise DatabaseException(*res) raise IntegrityError(unicode(res[0]))
return res raise DatabaseException(*res)
return res
return run return run

View File

@ -140,8 +140,3 @@ Loading message
z-index: 2; z-index: 2;
margin: 0pt; padding: 0pt; border-width: 0pt; margin: 0pt; padding: 0pt; border-width: 0pt;
} }
#cover_pane img {
border: solid thin black;
z-index: 10;
}

View File

@ -118,6 +118,7 @@ function fetch_library_books(start, num, timeout, sort, order, search) {
current_library_request = null; current_library_request = null;
} }
$('#cover_pane').css('visibility', 'hidden');
$('#loading').css('visibility', 'visible'); $('#loading').css('visibility', 'visible');
current_library_request = $.ajax({ current_library_request = $.ajax({

View File

@ -22,7 +22,7 @@
</div> </div>
<div id="count_bar"> <div id="count_bar">
<span id="left"><img src="/static/first.png" alt="Show first set of books" />&nbsp;<img src="/static/previous.png" alt="Show previous set of books" />&nbsp;</span><span id="count">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;<span id="right"><img src="/static/next.png" alt="Show first set of books" />&nbsp;<img src="/static/last.png" alt="Show previous set of books" /></span> <span id="left"><img src="/static/first.png" alt="Show first set of books" title="Show first set of books"/>&nbsp;<img src="/static/previous.png" alt="Show previous set of books" title="Show previous set of books"/>&nbsp;</span><span id="count">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;<span id="right"><img src="/static/next.png" alt="Show first set of books" title="Show first set of books"/>&nbsp;<img src="/static/last.png" alt="Show previous set of books" title="Show previous set of books" /></span>
</div> </div>
<div id="main"> <div id="main">
@ -38,12 +38,12 @@
<div id="loading"> <div id="loading">
<div> <div>
<img align="top" src="/static/loading.gif" alt="Loading..." />&nbsp;<span id="loading_msg">Loading&hellip;</span> <img align="top" src="/static/loading.gif" alt="Loading..." title="Loading..."/>&nbsp;<span id="loading_msg">Loading&hellip;</span>
</div> </div>
</div> </div>
<div id="cover_pane"> <div id="cover_pane">
<img alt="Cover" src="" /> <img title="Cover" src="" />
</div> </div>
</body> </body>
</html> </html>