mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
IGN: Cache path information in memory as well. Serve default cover when cover is missing
This commit is contained in:
parent
5bcd1957a6
commit
43e6f6d8cb
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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({
|
||||
|
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<div id="count_bar">
|
||||
<span id="left"><img src="/static/first.png" alt="Show first set of books" /> <img src="/static/previous.png" alt="Show previous set of books" /> </span><span id="count"> </span> <span id="right"><img src="/static/next.png" alt="Show first set of books" /> <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"/> <img src="/static/previous.png" alt="Show previous set of books" title="Show previous set of books"/> </span><span id="count"> </span> <span id="right"><img src="/static/next.png" alt="Show first set of books" title="Show first set of books"/> <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..." /> <span id="loading_msg">Loading…</span>
|
||||
<img align="top" src="/static/loading.gif" alt="Loading..." title="Loading..."/> <span id="loading_msg">Loading…</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cover_pane">
|
||||
<img alt="Cover" src="" />
|
||||
<img title="Cover" src="" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user