Added thumbnail urls to book cover srcsets with cache busting ids
This commit is contained in:
parent
626051e489
commit
242a2767a1
|
@ -609,7 +609,8 @@ class CalibreDB():
|
||||||
randm = self.session.query(Books) \
|
randm = self.session.query(Books) \
|
||||||
.filter(self.common_filters(allow_show_archived)) \
|
.filter(self.common_filters(allow_show_archived)) \
|
||||||
.order_by(func.random()) \
|
.order_by(func.random()) \
|
||||||
.limit(self.config.config_random_books)
|
.limit(self.config.config_random_books) \
|
||||||
|
.all()
|
||||||
else:
|
else:
|
||||||
randm = false()
|
randm = false()
|
||||||
off = int(int(pagesize) * (page - 1))
|
off = int(int(pagesize) * (page - 1))
|
||||||
|
|
|
@ -533,6 +533,21 @@ def delete_book(book, calibrepath, book_format):
|
||||||
return delete_book_file(book, calibrepath, book_format)
|
return delete_book_file(book, calibrepath, book_format)
|
||||||
|
|
||||||
|
|
||||||
|
def get_thumbnails_for_books(books):
|
||||||
|
books_with_covers = list(filter(lambda b: b.has_cover, books))
|
||||||
|
book_ids = list(map(lambda b: b.id, books_with_covers))
|
||||||
|
return ub.session\
|
||||||
|
.query(ub.Thumbnail)\
|
||||||
|
.filter(ub.Thumbnail.book_id.in_(book_ids))\
|
||||||
|
.filter(ub.Thumbnail.expiration > datetime.utcnow())\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
|
||||||
|
def get_thumbnails_for_book_series(series):
|
||||||
|
books = list(map(lambda s: s[0], series))
|
||||||
|
return get_thumbnails_for_books(books)
|
||||||
|
|
||||||
|
|
||||||
def get_cover_on_failure(use_generic_cover):
|
def get_cover_on_failure(use_generic_cover):
|
||||||
if use_generic_cover:
|
if use_generic_cover:
|
||||||
return send_from_directory(_STATIC_DIR, "generic_cover.jpg")
|
return send_from_directory(_STATIC_DIR, "generic_cover.jpg")
|
||||||
|
@ -558,6 +573,29 @@ def get_cached_book_cover(cache_id):
|
||||||
return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution)
|
return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution)
|
||||||
|
|
||||||
|
|
||||||
|
def get_cached_book_cover_thumbnail(cache_id):
|
||||||
|
parts = cache_id.split('_')
|
||||||
|
thumbnail_uuid = parts[0] if len(parts) else None
|
||||||
|
thumbnail = None
|
||||||
|
if thumbnail_uuid:
|
||||||
|
thumbnail = ub.session\
|
||||||
|
.query(ub.Thumbnail)\
|
||||||
|
.filter(ub.Thumbnail.uuid == thumbnail_uuid)\
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if thumbnail and thumbnail.expiration > datetime.utcnow():
|
||||||
|
cache = fs.FileSystem()
|
||||||
|
if cache.get_cache_file_path(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS):
|
||||||
|
return send_from_directory(cache.get_cache_dir(fs.CACHE_TYPE_THUMBNAILS), thumbnail.filename)
|
||||||
|
|
||||||
|
elif thumbnail:
|
||||||
|
book = calibre_db.get_book(thumbnail.book_id)
|
||||||
|
return get_book_cover_internal(book, use_generic_cover_on_failure=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return get_cover_on_failure(True)
|
||||||
|
|
||||||
|
|
||||||
def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None):
|
def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None):
|
||||||
if book and book.has_cover:
|
if book and book.has_cover:
|
||||||
|
|
||||||
|
|
|
@ -139,3 +139,19 @@ def book_cover_cache_id(book, resolution=None):
|
||||||
timestamp = int(book.last_modified.timestamp() * 1000)
|
timestamp = int(book.last_modified.timestamp() * 1000)
|
||||||
cache_bust = str(book.uuid) + '_' + str(timestamp)
|
cache_bust = str(book.uuid) + '_' + str(timestamp)
|
||||||
return cache_bust if not resolution else cache_bust + '_' + str(resolution)
|
return cache_bust if not resolution else cache_bust + '_' + str(resolution)
|
||||||
|
|
||||||
|
|
||||||
|
@jinjia.app_template_filter('get_book_thumbnails')
|
||||||
|
def get_book_thumbnails(book_id, thumbnails=None):
|
||||||
|
return list(filter(lambda t: t.book_id == book_id, thumbnails)) if book_id > -1 and thumbnails else list()
|
||||||
|
|
||||||
|
|
||||||
|
@jinjia.app_template_filter('get_book_thumbnail_srcset')
|
||||||
|
def get_book_thumbnail_srcset(thumbnails):
|
||||||
|
srcset = list()
|
||||||
|
for thumbnail in thumbnails:
|
||||||
|
timestamp = int(thumbnail.generated_at.timestamp() * 1000)
|
||||||
|
cache_id = str(thumbnail.uuid) + '_' + str(timestamp)
|
||||||
|
url = url_for('web.get_cached_cover_thumbnail', cache_id=cache_id)
|
||||||
|
srcset.append(url + ' ' + str(thumbnail.resolution) + 'x')
|
||||||
|
return ', '.join(srcset)
|
||||||
|
|
|
@ -20,17 +20,17 @@ from __future__ import division, print_function, unicode_literals
|
||||||
|
|
||||||
from .services.background_scheduler import BackgroundScheduler
|
from .services.background_scheduler import BackgroundScheduler
|
||||||
from .tasks.database import TaskReconnectDatabase
|
from .tasks.database import TaskReconnectDatabase
|
||||||
from .tasks.thumbnail import TaskCleanupCoverThumbnailCache, TaskGenerateCoverThumbnails
|
from .tasks.thumbnail import TaskSyncCoverThumbnailCache, TaskGenerateCoverThumbnails
|
||||||
|
|
||||||
|
|
||||||
def register_jobs():
|
def register_jobs():
|
||||||
scheduler = BackgroundScheduler()
|
scheduler = BackgroundScheduler()
|
||||||
|
|
||||||
# Generate 100 book cover thumbnails every 5 minutes
|
# Generate 100 book cover thumbnails every 5 minutes
|
||||||
scheduler.add_task(user=None, task=lambda: TaskGenerateCoverThumbnails(limit=100), trigger='interval', minutes=5)
|
scheduler.add_task(user=None, task=lambda: TaskGenerateCoverThumbnails(limit=100), trigger='cron', minute='*/5')
|
||||||
|
|
||||||
# Cleanup book cover cache every day at 4am
|
# Cleanup book cover cache every 6 hours
|
||||||
scheduler.add_task(user=None, task=lambda: TaskCleanupCoverThumbnailCache(), trigger='cron', hour=4)
|
scheduler.add_task(user=None, task=lambda: TaskSyncCoverThumbnailCache(), trigger='cron', minute='15', hour='*/6')
|
||||||
|
|
||||||
# Reconnect metadata.db every 4 hours
|
# Reconnect metadata.db every 4 hours
|
||||||
scheduler.add_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='interval', hours=4)
|
scheduler.add_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='cron', minute='5', hour='*/4')
|
||||||
|
|
|
@ -59,6 +59,10 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
books_without_thumbnails = self.get_books_without_thumbnails(thumbnail_book_ids)
|
books_without_thumbnails = self.get_books_without_thumbnails(thumbnail_book_ids)
|
||||||
|
|
||||||
count = len(books_without_thumbnails)
|
count = len(books_without_thumbnails)
|
||||||
|
if count == 0:
|
||||||
|
# Do not display this task on the frontend if there are no covers to update
|
||||||
|
self.self_cleanup = True
|
||||||
|
|
||||||
for i, book in enumerate(books_without_thumbnails):
|
for i, book in enumerate(books_without_thumbnails):
|
||||||
for resolution in self.resolutions:
|
for resolution in self.resolutions:
|
||||||
expired_thumbnail = self.get_expired_thumbnail_for_book_and_resolution(
|
expired_thumbnail = self.get_expired_thumbnail_for_book_and_resolution(
|
||||||
|
@ -71,6 +75,7 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
else:
|
else:
|
||||||
self.create_book_thumbnail(book, resolution)
|
self.create_book_thumbnail(book, resolution)
|
||||||
|
|
||||||
|
self.message(u'Generating cover thumbnail {0} of {1}'.format(i, count))
|
||||||
self.progress = (1.0 / count) * i
|
self.progress = (1.0 / count) * i
|
||||||
|
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
|
@ -181,12 +186,12 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return "GenerateCoverThumbnails"
|
return "ThumbnailsGenerate"
|
||||||
|
|
||||||
|
|
||||||
class TaskCleanupCoverThumbnailCache(CalibreTask):
|
class TaskSyncCoverThumbnailCache(CalibreTask):
|
||||||
def __init__(self, task_message=u'Cleaning up cover thumbnail cache'):
|
def __init__(self, task_message=u'Syncing cover thumbnail cache'):
|
||||||
super(TaskCleanupCoverThumbnailCache, self).__init__(task_message)
|
super(TaskSyncCoverThumbnailCache, self).__init__(task_message)
|
||||||
self.log = logger.create()
|
self.log = logger.create()
|
||||||
self.app_db_session = ub.get_new_session_instance()
|
self.app_db_session = ub.get_new_session_instance()
|
||||||
self.calibre_db = db.CalibreDB(expire_on_commit=False)
|
self.calibre_db = db.CalibreDB(expire_on_commit=False)
|
||||||
|
@ -199,14 +204,23 @@ class TaskCleanupCoverThumbnailCache(CalibreTask):
|
||||||
# This case will happen if a user deletes the cache dir or cached files
|
# This case will happen if a user deletes the cache dir or cached files
|
||||||
if self.app_db_session:
|
if self.app_db_session:
|
||||||
self.expire_missing_thumbnails(cached_thumbnail_files)
|
self.expire_missing_thumbnails(cached_thumbnail_files)
|
||||||
self.progress = 0.33
|
self.progress = 0.25
|
||||||
|
|
||||||
# Delete thumbnails in the database if the book has been removed
|
# Delete thumbnails in the database if the book has been removed
|
||||||
# This case will happen if a book is removed in Calibre and the metadata.db file is updated in the filesystem
|
# This case will happen if a book is removed in Calibre and the metadata.db file is updated in the filesystem
|
||||||
if self.app_db_session and self.calibre_db:
|
if self.app_db_session and self.calibre_db:
|
||||||
book_ids = self.get_book_ids()
|
book_ids = self.get_book_ids()
|
||||||
self.delete_thumbnails_for_missing_books(book_ids)
|
self.delete_thumbnails_for_missing_books(book_ids)
|
||||||
self.progress = 0.66
|
self.progress = 0.50
|
||||||
|
|
||||||
|
# Expire thumbnails in the database if their corresponding book has been updated since they were generated
|
||||||
|
# This case will happen if the book was updated externally
|
||||||
|
if self.app_db_session and self.cache:
|
||||||
|
books = self.get_books_updated_in_the_last_day()
|
||||||
|
book_ids = list(map(lambda b: b.id, books))
|
||||||
|
thumbnails = self.get_thumbnails_for_updated_books(book_ids)
|
||||||
|
self.expire_thumbnails_for_updated_book(books, thumbnails)
|
||||||
|
self.progress = 0.75
|
||||||
|
|
||||||
# Delete extraneous cached thumbnail files
|
# Delete extraneous cached thumbnail files
|
||||||
# This case will happen if a book was deleted and the thumbnail OR the metadata.db file was changed externally
|
# This case will happen if a book was deleted and the thumbnail OR the metadata.db file was changed externally
|
||||||
|
@ -261,9 +275,40 @@ class TaskCleanupCoverThumbnailCache(CalibreTask):
|
||||||
for file in extraneous_files:
|
for file in extraneous_files:
|
||||||
self.cache.delete_cache_file(file, fs.CACHE_TYPE_THUMBNAILS)
|
self.cache.delete_cache_file(file, fs.CACHE_TYPE_THUMBNAILS)
|
||||||
|
|
||||||
|
def get_books_updated_in_the_last_day(self):
|
||||||
|
return self.calibre_db.session\
|
||||||
|
.query(db.Books)\
|
||||||
|
.filter(db.Books.has_cover == 1)\
|
||||||
|
.filter(db.Books.last_modified > datetime.utcnow() - timedelta(days=1, hours=1))\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
def get_thumbnails_for_updated_books(self, book_ids):
|
||||||
|
return self.app_db_session\
|
||||||
|
.query(ub.Thumbnail)\
|
||||||
|
.filter(ub.Thumbnail.book_id.in_(book_ids))\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
def expire_thumbnails_for_updated_book(self, books, thumbnails):
|
||||||
|
thumbnail_ids = list()
|
||||||
|
for book in books:
|
||||||
|
for thumbnail in thumbnails:
|
||||||
|
if thumbnail.book_id == book.id and thumbnail.generated_at < book.last_modified:
|
||||||
|
thumbnail_ids.append(thumbnail.id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.app_db_session\
|
||||||
|
.query(ub.Thumbnail)\
|
||||||
|
.filter(ub.Thumbnail.id.in_(thumbnail_ids)) \
|
||||||
|
.update({"expiration": datetime.utcnow()}, synchronize_session=False)
|
||||||
|
self.app_db_session.commit()
|
||||||
|
except Exception as ex:
|
||||||
|
self.log.info(u'Error expiring thumbnails for updated books: ' + str(ex))
|
||||||
|
self._handleError(u'Error expiring thumbnails for updated books: ' + str(ex))
|
||||||
|
self.app_db_session.rollback()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return "CleanupCoverThumbnailCache"
|
return "ThumbnailsSync"
|
||||||
|
|
||||||
|
|
||||||
class TaskClearCoverThumbnailCache(CalibreTask):
|
class TaskClearCoverThumbnailCache(CalibreTask):
|
||||||
|
@ -318,4 +363,4 @@ class TaskClearCoverThumbnailCache(CalibreTask):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return "ClearCoverThumbnailCache"
|
return "ThumbnailsClear"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book">
|
<div id="books" class="col-sm-3 col-lg-2 col-xs-6 book">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
{% macro book_cover_image(book, book_title) -%}
|
{% macro book_cover_image(book, thumbnails) -%}
|
||||||
|
{%- set book_title = book.title if book.title else book.name -%}
|
||||||
|
{% set srcset = thumbnails|get_book_thumbnail_srcset if thumbnails|length else '' %}
|
||||||
|
{%- if srcset|length -%}
|
||||||
<img
|
<img
|
||||||
srcset="{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id(1)) }} 1x,
|
srcset="{{ srcset }}"
|
||||||
{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id(2)) }} 2x"
|
|
||||||
src="{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id) }}"
|
src="{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id) }}"
|
||||||
alt="{{ book_title }}"
|
alt="{{ book_title }}"
|
||||||
/>
|
/>
|
||||||
|
{%- else -%}
|
||||||
|
<img src="{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id) }}" alt="{{ book_title }}" />
|
||||||
|
{%- endif -%}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% if book %}
|
{% if book %}
|
||||||
<div class="col-sm-3 col-lg-3 col-xs-12">
|
<div class="col-sm-3 col-lg-3 col-xs-12">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{{ book_cover_image(book, book.title) }}
|
{{ book_cover_image(book, book.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</div>
|
</div>
|
||||||
{% if g.user.role_delete_books() %}
|
{% if g.user.role_delete_books() %}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-lg-3 col-xs-5">
|
<div class="col-sm-3 col-lg-3 col-xs-5">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-lg-9 book-meta">
|
<div class="col-sm-9 col-lg-9 book-meta">
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{% if entry.has_cover is defined %}
|
{% if entry.has_cover is defined %}
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<div class="col-sm-3 col-lg-2 col-xs-6 book sortable" {% if entry[0].sort %}data-name="{{entry[0].series[0].name}}"{% endif %} data-id="{% if entry[0].series[0].name %}{{entry[0].series[0].name}}{% endif %}">
|
<div class="col-sm-3 col-lg-2 col-xs-6 book sortable" {% if entry[0].sort %}data-name="{{entry[0].series[0].name}}"{% endif %} data-id="{% if entry[0].series[0].name %}{{entry[0].series[0].name}}{% endif %}">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{url_for('web.books_list', data=data, sort_param='new', book_id=entry[0].series[0].id )}}">
|
<a href="{{url_for('web.books_list', data=data, sort_param='new', book_id=entry[0].series[0].id )}}">
|
||||||
{{ book_cover_image(entry[0], entry[0].name) }}
|
{{ book_cover_image(entry[0], entry[0].id|get_book_thumbnails(thumbnails)) }}
|
||||||
<span class="badge">{{entry.count}}</span>
|
<span class="badge">{{entry.count}}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand">
|
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books">
|
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{% if entry.has_cover is defined %}
|
{% if entry.has_cover is defined %}
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="col-sm-3 col-lg-2 col-xs-6 book">
|
<div class="col-sm-3 col-lg-2 col-xs-6 book">
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
<a href="{{ url_for('web.show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||||
{{ book_cover_image(entry, entry.title) }}
|
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
|
|
89
cps/web.py
89
cps/web.py
|
@ -49,9 +49,10 @@ from . import constants, logger, isoLanguages, services
|
||||||
from . import babel, db, ub, config, get_locale, app
|
from . import babel, db, ub, config, get_locale, app
|
||||||
from . import calibre_db, shelf
|
from . import calibre_db, shelf
|
||||||
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
|
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
|
||||||
from .helper import check_valid_domain, render_task_status, \
|
from .helper import check_valid_domain, render_task_status, get_cc_columns, get_book_cover, get_cached_book_cover, \
|
||||||
get_cc_columns, get_book_cover, get_cached_book_cover, get_download_link, send_mail, generate_random_password, \
|
get_cached_book_cover_thumbnail, get_thumbnails_for_books, get_thumbnails_for_book_series, get_download_link, \
|
||||||
send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password
|
send_mail, generate_random_password, send_registration_mail, check_send_to_kindle, check_read_formats, \
|
||||||
|
tags_filters, reset_password
|
||||||
from .pagination import Pagination
|
from .pagination import Pagination
|
||||||
from .redirect import redirect_back
|
from .redirect import redirect_back
|
||||||
from .usermanagement import login_required_if_no_ano
|
from .usermanagement import login_required_if_no_ano
|
||||||
|
@ -386,16 +387,18 @@ def render_books_list(data, sort, book_id, page):
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.ratings.any(db.Ratings.rating > 9),
|
db.Books.ratings.any(db.Ratings.rating > 9),
|
||||||
order)
|
order)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
id=book_id, title=_(u"Top Rated Books"), page="rated")
|
id=book_id, title=_(u"Top Rated Books"), page="rated", thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
elif data == "discover":
|
elif data == "discover":
|
||||||
if current_user.check_visibility(constants.SIDEBAR_RANDOM):
|
if current_user.check_visibility(constants.SIDEBAR_RANDOM):
|
||||||
entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [func.randomblob(2)])
|
entries, __, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, [func.randomblob(2)])
|
||||||
pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page)
|
pagination = Pagination(1, config.config_books_per_page, config.config_books_per_page)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries)
|
||||||
return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id,
|
return render_title_template('discover.html', entries=entries, pagination=pagination, id=book_id,
|
||||||
title=_(u"Discover (Random Books)"), page="discover")
|
title=_(u"Discover (Random Books)"), page="discover", thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
elif data == "unread":
|
elif data == "unread":
|
||||||
|
@ -433,8 +436,9 @@ def render_books_list(data, sort, book_id, page):
|
||||||
else:
|
else:
|
||||||
website = data or "newest"
|
website = data or "newest"
|
||||||
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order)
|
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
title=_(u"Books"), page=website)
|
title=_(u"Books"), page=website, thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
def render_hot_books(page):
|
def render_hot_books(page):
|
||||||
|
@ -458,8 +462,9 @@ def render_hot_books(page):
|
||||||
ub.delete_download(book.Downloads.book_id)
|
ub.delete_download(book.Downloads.book_id)
|
||||||
numBooks = entries.__len__()
|
numBooks = entries.__len__()
|
||||||
pagination = Pagination(page, config.config_books_per_page, numBooks)
|
pagination = Pagination(page, config.config_books_per_page, numBooks)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
title=_(u"Hot Books (Most Downloaded)"), page="hot")
|
title=_(u"Hot Books (Most Downloaded)"), page="hot", thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -490,12 +495,14 @@ def render_downloaded_books(page, order):
|
||||||
.filter(db.Books.id == book.id).first():
|
.filter(db.Books.id == book.id).first():
|
||||||
ub.delete_download(book.id)
|
ub.delete_download(book.id)
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html',
|
return render_title_template('index.html',
|
||||||
random=random,
|
random=random,
|
||||||
entries=entries,
|
entries=entries,
|
||||||
pagination=pagination,
|
pagination=pagination,
|
||||||
title=_(u"Downloaded books by %(user)s",user=current_user.nickname),
|
title=_(u"Downloaded books by %(user)s",user=current_user.nickname),
|
||||||
page="download")
|
page="download",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -521,9 +528,10 @@ def render_author_books(page, author_id, order):
|
||||||
author_info = services.goodreads_support.get_author_info(author_name)
|
author_info = services.goodreads_support.get_author_info(author_name)
|
||||||
other_books = services.goodreads_support.get_other_books(author_info, entries)
|
other_books = services.goodreads_support.get_other_books(author_info, entries)
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_books(entries)
|
||||||
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
|
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
|
||||||
title=_(u"Author: %(name)s", name=author_name), author=author_info,
|
title=_(u"Author: %(name)s", name=author_name), author=author_info,
|
||||||
other_books=other_books, page="author")
|
other_books=other_books, page="author", thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
def render_publisher_books(page, book_id, order):
|
def render_publisher_books(page, book_id, order):
|
||||||
|
@ -535,8 +543,10 @@ def render_publisher_books(page, book_id, order):
|
||||||
[db.Series.name, order[0], db.Books.series_index],
|
[db.Series.name, order[0], db.Books.series_index],
|
||||||
db.books_series_link,
|
db.books_series_link,
|
||||||
db.Series)
|
db.Series)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
|
||||||
title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher")
|
title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -548,8 +558,10 @@ def render_series_books(page, book_id, order):
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.series.any(db.Series.id == book_id),
|
db.Books.series.any(db.Series.id == book_id),
|
||||||
[order[0]])
|
[order[0]])
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"Series: %(serie)s", serie=name.name), page="series")
|
title=_(u"Series: %(serie)s", serie=name.name), page="series",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -561,8 +573,10 @@ def render_ratings_books(page, book_id, order):
|
||||||
db.Books.ratings.any(db.Ratings.id == book_id),
|
db.Books.ratings.any(db.Ratings.id == book_id),
|
||||||
[order[0]])
|
[order[0]])
|
||||||
if name and name.rating <= 10:
|
if name and name.rating <= 10:
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings")
|
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -574,8 +588,10 @@ def render_formats_books(page, book_id, order):
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.data.any(db.Data.format == book_id.upper()),
|
db.Books.data.any(db.Data.format == book_id.upper()),
|
||||||
[order[0]])
|
[order[0]])
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"File format: %(format)s", format=name.format), page="formats")
|
title=_(u"File format: %(format)s", format=name.format), page="formats",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -588,8 +604,10 @@ def render_category_books(page, book_id, order):
|
||||||
db.Books.tags.any(db.Tags.id == book_id),
|
db.Books.tags.any(db.Tags.id == book_id),
|
||||||
[order[0], db.Series.name, db.Books.series_index],
|
[order[0], db.Series.name, db.Books.series_index],
|
||||||
db.books_series_link, db.Series)
|
db.books_series_link, db.Series)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
|
||||||
title=_(u"Category: %(name)s", name=name.name), page="category")
|
title=_(u"Category: %(name)s", name=name.name), page="category",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -607,8 +625,9 @@ def render_language_books(page, name, order):
|
||||||
db.Books,
|
db.Books,
|
||||||
db.Books.languages.any(db.Languages.lang_code == name),
|
db.Books.languages.any(db.Languages.lang_code == name),
|
||||||
[order[0]])
|
[order[0]])
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
||||||
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
title=_(u"Language: %(name)s", name=lang_name), page="language", thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
def render_read_books(page, are_read, as_xml=False, order=None):
|
def render_read_books(page, are_read, as_xml=False, order=None):
|
||||||
|
@ -652,8 +671,10 @@ def render_read_books(page, are_read, as_xml=False, order=None):
|
||||||
else:
|
else:
|
||||||
name = _(u'Unread Books') + ' (' + str(pagination.total_count) + ')'
|
name = _(u'Unread Books') + ' (' + str(pagination.total_count) + ')'
|
||||||
pagename = "unread"
|
pagename = "unread"
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
title=name, page=pagename)
|
title=name, page=pagename, thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
def render_archived_books(page, order):
|
def render_archived_books(page, order):
|
||||||
|
@ -676,8 +697,9 @@ def render_archived_books(page, order):
|
||||||
|
|
||||||
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
|
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
|
||||||
pagename = "archived"
|
pagename = "archived"
|
||||||
|
thumbnails = get_thumbnails_for_books(entries + random)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
title=name, page=pagename)
|
title=name, page=pagename, thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
def render_prepare_search_form(cc):
|
def render_prepare_search_form(cc):
|
||||||
|
@ -710,6 +732,7 @@ def render_prepare_search_form(cc):
|
||||||
|
|
||||||
def render_search_results(term, offset=None, order=None, limit=None):
|
def render_search_results(term, offset=None, order=None, limit=None):
|
||||||
entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit)
|
entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit)
|
||||||
|
thumbnails = get_thumbnails_for_books(entries)
|
||||||
return render_title_template('search.html',
|
return render_title_template('search.html',
|
||||||
searchterm=term,
|
searchterm=term,
|
||||||
pagination=pagination,
|
pagination=pagination,
|
||||||
|
@ -718,7 +741,8 @@ def render_search_results(term, offset=None, order=None, limit=None):
|
||||||
entries=entries,
|
entries=entries,
|
||||||
result_count=result_count,
|
result_count=result_count,
|
||||||
title=_(u"Search"),
|
title=_(u"Search"),
|
||||||
page="search")
|
page="search",
|
||||||
|
thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
# ################################### View Books list ##################################################################
|
# ################################### View Books list ##################################################################
|
||||||
|
@ -748,6 +772,7 @@ def books_table():
|
||||||
return render_title_template('book_table.html', title=_(u"Books List"), page="book_table",
|
return render_title_template('book_table.html', title=_(u"Books List"), page="book_table",
|
||||||
visiblility=visibility)
|
visiblility=visibility)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/ajax/listbooks")
|
@web.route("/ajax/listbooks")
|
||||||
@login_required
|
@login_required
|
||||||
def list_books():
|
def list_books():
|
||||||
|
@ -780,6 +805,7 @@ def list_books():
|
||||||
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@web.route("/ajax/table_settings", methods=['POST'])
|
@web.route("/ajax/table_settings", methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def update_table_settings():
|
def update_table_settings():
|
||||||
|
@ -834,6 +860,7 @@ def publisher_list():
|
||||||
charlist = calibre_db.session.query(func.upper(func.substr(db.Publishers.name, 1, 1)).label('char')) \
|
charlist = calibre_db.session.query(func.upper(func.substr(db.Publishers.name, 1, 1)).label('char')) \
|
||||||
.join(db.books_publishers_link).join(db.Books).filter(calibre_db.common_filters()) \
|
.join(db.books_publishers_link).join(db.Books).filter(calibre_db.common_filters()) \
|
||||||
.group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all()
|
.group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all()
|
||||||
|
|
||||||
return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
|
return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
|
||||||
title=_(u"Publishers"), page="publisherlist", data="publisher")
|
title=_(u"Publishers"), page="publisherlist", data="publisher")
|
||||||
else:
|
else:
|
||||||
|
@ -865,8 +892,10 @@ def series_list():
|
||||||
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
|
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
|
||||||
.group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all()
|
.group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all()
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_book_series(entries)
|
||||||
return render_title_template('grid.html', entries=entries, folder='web.books_list', charlist=charlist,
|
return render_title_template('grid.html', entries=entries, folder='web.books_list', charlist=charlist,
|
||||||
title=_(u"Series"), page="serieslist", data="series", bodyClass="grid-view")
|
title=_(u"Series"), page="serieslist", data="series", bodyClass="grid-view",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -1150,13 +1179,16 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
|
||||||
else:
|
else:
|
||||||
offset = 0
|
offset = 0
|
||||||
limit_all = result_count
|
limit_all = result_count
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_books(entries)
|
||||||
return render_title_template('search.html',
|
return render_title_template('search.html',
|
||||||
adv_searchterm=searchterm,
|
adv_searchterm=searchterm,
|
||||||
pagination=pagination,
|
pagination=pagination,
|
||||||
entries=q[offset:limit_all],
|
entries=q[offset:limit_all],
|
||||||
result_count=result_count,
|
result_count=result_count,
|
||||||
title=_(u"Advanced Search"), page="advsearch")
|
title=_(u"Advanced Search"),
|
||||||
|
page="advsearch",
|
||||||
|
thumbnails=thumbnails)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/advsearch", methods=['GET'])
|
@web.route("/advsearch", methods=['GET'])
|
||||||
|
@ -1171,10 +1203,9 @@ def advanced_search_form():
|
||||||
|
|
||||||
|
|
||||||
@web.route("/cover/<int:book_id>")
|
@web.route("/cover/<int:book_id>")
|
||||||
@web.route("/cover/<int:book_id>/<int:resolution>")
|
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def get_cover(book_id, resolution=1):
|
def get_cover(book_id):
|
||||||
return get_book_cover(book_id, resolution)
|
return get_book_cover(book_id)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/cached-cover/<string:cache_id>")
|
@web.route("/cached-cover/<string:cache_id>")
|
||||||
|
@ -1183,6 +1214,12 @@ def get_cached_cover(cache_id):
|
||||||
return get_cached_book_cover(cache_id)
|
return get_cached_book_cover(cache_id)
|
||||||
|
|
||||||
|
|
||||||
|
@web.route("/cached-cover-thumbnail/<string:cache_id>")
|
||||||
|
@login_required_if_no_ano
|
||||||
|
def get_cached_cover_thumbnail(cache_id):
|
||||||
|
return get_cached_book_cover_thumbnail(cache_id)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/robots.txt")
|
@web.route("/robots.txt")
|
||||||
def get_robots():
|
def get_robots():
|
||||||
return send_from_directory(constants.STATIC_DIR, "robots.txt")
|
return send_from_directory(constants.STATIC_DIR, "robots.txt")
|
||||||
|
@ -1591,6 +1628,7 @@ def show_book(book_id):
|
||||||
if media_format.format.lower() in constants.EXTENSIONS_AUDIO:
|
if media_format.format.lower() in constants.EXTENSIONS_AUDIO:
|
||||||
audioentries.append(media_format.format.lower())
|
audioentries.append(media_format.format.lower())
|
||||||
|
|
||||||
|
thumbnails = get_thumbnails_for_books([entries])
|
||||||
return render_title_template('detail.html',
|
return render_title_template('detail.html',
|
||||||
entry=entries,
|
entry=entries,
|
||||||
audioentries=audioentries,
|
audioentries=audioentries,
|
||||||
|
@ -1602,7 +1640,8 @@ def show_book(book_id):
|
||||||
is_archived=is_archived,
|
is_archived=is_archived,
|
||||||
kindle_list=kindle_list,
|
kindle_list=kindle_list,
|
||||||
reader_list=reader_list,
|
reader_list=reader_list,
|
||||||
page="book")
|
page="book",
|
||||||
|
thumbnails=thumbnails)
|
||||||
else:
|
else:
|
||||||
log.debug(u"Error opening eBook. File does not exist or file is not accessible")
|
log.debug(u"Error opening eBook. File does not exist or file is not accessible")
|
||||||
flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error")
|
flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user