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,
'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()))
@ -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='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):
''' Return last modified time as a UTC datetime object'''
return datetime.utcfromtimestamp(os.stat(self.dbpath).st_mtime)
def path(self, index, index_is_id=False):
'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)
path = self.conn.get('SELECT path FROM books WHERE id=?', (id,), all=False).replace('/', os.sep)
return path
row = self.data._data[index] if index_is_id else self.data[index]
return row[FIELD_MAP['path']].replace('/', os.sep)
def abspath(self, index, index_is_id=False):
'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.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
self.conn.commit()
self.data.set(id, FIELD_MAP['path'], path, row_is_id=True)
# Delete not needed directories
if current_path and os.path.exists(spath):
if normpath(spath) != normpath(tpath):
@ -650,8 +675,8 @@ class LibraryDatabase2(LibraryDatabase):
'''
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)
path = os.path.join(self.library_path, self.path(id, True))
if os.path.exists(path):
shutil.rmtree(path)
parent = os.path.dirname(path)

View File

@ -123,12 +123,11 @@ class LibraryServer(object):
def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=True)
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'
path = getattr(cover, 'name', None)
if path and os.path.exists(path):
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime)
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
path = getattr(cover, 'name', False)
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) if path and os.access(path, os.R_OK) else build_time
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
if not thumbnail:
return cover.read()
try:

View File

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

View File

@ -140,8 +140,3 @@ Loading message
z-index: 2;
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;
}
$('#cover_pane').css('visibility', 'hidden');
$('#loading').css('visibility', 'visible');
current_library_request = $.ajax({

View File

@ -22,7 +22,7 @@
</div>
<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 id="main">
@ -38,12 +38,12 @@
<div id="loading">
<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 id="cover_pane">
<img alt="Cover" src="" />
<img title="Cover" src="" />
</div>
</body>
</html>