Simplified all of the thumbnail generation and loading.
This commit is contained in:
parent
524ed07a6c
commit
be28a91315
14
cps/fs.py
14
cps/fs.py
|
@ -57,17 +57,9 @@ class FileSystem:
|
||||||
def get_cache_file_path(self, filename, cache_type=None):
|
def get_cache_file_path(self, filename, cache_type=None):
|
||||||
return join(self.get_cache_dir(cache_type), filename) if filename else None
|
return join(self.get_cache_dir(cache_type), filename) if filename else None
|
||||||
|
|
||||||
def list_cache_files(self, cache_type=None):
|
def get_cache_file_exists(self, filename, cache_type=None):
|
||||||
path = self.get_cache_dir(cache_type)
|
path = self.get_cache_file_path(filename, cache_type)
|
||||||
return [file for file in listdir(path) if isfile(join(path, file))]
|
return isfile(path)
|
||||||
|
|
||||||
def list_existing_cache_files(self, filenames, cache_type=None):
|
|
||||||
path = self.get_cache_dir(cache_type)
|
|
||||||
return [file for file in listdir(path) if isfile(join(path, file)) and file in filenames]
|
|
||||||
|
|
||||||
def list_missing_cache_files(self, filenames, cache_type=None):
|
|
||||||
path = self.get_cache_dir(cache_type)
|
|
||||||
return [file for file in listdir(path) if isfile(join(path, file)) and file not in filenames]
|
|
||||||
|
|
||||||
def delete_cache_dir(self, cache_type=None):
|
def delete_cache_dir(self, cache_type=None):
|
||||||
if not cache_type and isdir(self._cache_dir):
|
if not cache_type and isdir(self._cache_dir):
|
||||||
|
|
|
@ -35,7 +35,7 @@ from babel.units import format_unit
|
||||||
from flask import send_from_directory, make_response, redirect, abort, url_for
|
from flask import send_from_directory, make_response, redirect, abort, url_for
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy.sql.expression import true, false, and_, text, func
|
from sqlalchemy.sql.expression import true, false, and_, or_, text, func
|
||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
|
@ -550,26 +550,6 @@ 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))
|
|
||||||
cache = fs.FileSystem()
|
|
||||||
thumbnail_files = cache.list_cache_files(fs.CACHE_TYPE_THUMBNAILS)
|
|
||||||
|
|
||||||
thumbnails = ub.session\
|
|
||||||
.query(ub.Thumbnail)\
|
|
||||||
.filter(ub.Thumbnail.book_id.in_(book_ids))\
|
|
||||||
.filter(ub.Thumbnail.expiration > datetime.utcnow())\
|
|
||||||
.all()
|
|
||||||
|
|
||||||
return list(filter(lambda t: t.filename in thumbnail_files, thumbnails))
|
|
||||||
|
|
||||||
|
|
||||||
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")
|
||||||
|
@ -577,9 +557,9 @@ def get_cover_on_failure(use_generic_cover):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_book_cover(book_id):
|
def get_book_cover(book_id, resolution=None):
|
||||||
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
|
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
|
||||||
return get_book_cover_internal(book, use_generic_cover_on_failure=True)
|
return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution)
|
||||||
|
|
||||||
|
|
||||||
def get_book_cover_with_uuid(book_uuid, use_generic_cover_on_failure=True):
|
def get_book_cover_with_uuid(book_uuid, use_generic_cover_on_failure=True):
|
||||||
|
@ -587,37 +567,6 @@ def get_book_cover_with_uuid(book_uuid, use_generic_cover_on_failure=True):
|
||||||
return get_book_cover_internal(book, use_generic_cover_on_failure)
|
return get_book_cover_internal(book, use_generic_cover_on_failure)
|
||||||
|
|
||||||
|
|
||||||
def get_cached_book_cover(cache_id):
|
|
||||||
parts = cache_id.split('_')
|
|
||||||
book_uuid = parts[0] if len(parts) else None
|
|
||||||
resolution = parts[2] if len(parts) > 2 else None
|
|
||||||
book = calibre_db.get_book_by_uuid(book_uuid) if book_uuid else None
|
|
||||||
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:
|
||||||
|
|
||||||
|
@ -626,7 +575,7 @@ def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None)
|
||||||
thumbnail = get_book_cover_thumbnail(book, resolution)
|
thumbnail = get_book_cover_thumbnail(book, resolution)
|
||||||
if thumbnail:
|
if thumbnail:
|
||||||
cache = fs.FileSystem()
|
cache = fs.FileSystem()
|
||||||
if cache.get_cache_file_path(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS):
|
if cache.get_cache_file_exists(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS):
|
||||||
return send_from_directory(cache.get_cache_dir(fs.CACHE_TYPE_THUMBNAILS), thumbnail.filename)
|
return send_from_directory(cache.get_cache_dir(fs.CACHE_TYPE_THUMBNAILS), thumbnail.filename)
|
||||||
|
|
||||||
# Send the book cover from Google Drive if configured
|
# Send the book cover from Google Drive if configured
|
||||||
|
@ -661,7 +610,7 @@ def get_book_cover_thumbnail(book, resolution):
|
||||||
.query(ub.Thumbnail)\
|
.query(ub.Thumbnail)\
|
||||||
.filter(ub.Thumbnail.book_id == book.id)\
|
.filter(ub.Thumbnail.book_id == book.id)\
|
||||||
.filter(ub.Thumbnail.resolution == resolution)\
|
.filter(ub.Thumbnail.resolution == resolution)\
|
||||||
.filter(ub.Thumbnail.expiration > datetime.utcnow())\
|
.filter(or_(ub.Thumbnail.expiration.is_(None), ub.Thumbnail.expiration > datetime.utcnow()))\
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ from flask_babel import get_locale
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
from . import logger
|
from . import logger
|
||||||
|
from .tasks.thumbnail import THUMBNAIL_RESOLUTION_1X, THUMBNAIL_RESOLUTION_2X, THUMBNAIL_RESOLUTION_3X
|
||||||
|
|
||||||
|
|
||||||
jinjia = Blueprint('jinjia', __name__)
|
jinjia = Blueprint('jinjia', __name__)
|
||||||
|
@ -140,24 +141,17 @@ def uuidfilter(var):
|
||||||
return uuid4()
|
return uuid4()
|
||||||
|
|
||||||
|
|
||||||
@jinjia.app_template_filter('book_cover_cache_id')
|
@jinjia.app_template_filter('last_modified')
|
||||||
def book_cover_cache_id(book, resolution=None):
|
def book_cover_cache_id(book):
|
||||||
timestamp = int(book.last_modified.timestamp() * 1000)
|
timestamp = int(book.last_modified.timestamp() * 1000)
|
||||||
cache_bust = str(book.uuid) + '_' + str(timestamp)
|
return str(timestamp)
|
||||||
return cache_bust if not resolution else cache_bust + '_' + str(resolution)
|
|
||||||
|
|
||||||
|
|
||||||
@jinjia.app_template_filter('get_book_thumbnails')
|
@jinjia.app_template_filter('get_cover_srcset')
|
||||||
def get_book_thumbnails(book_id, thumbnails=None):
|
def get_cover_srcset(book):
|
||||||
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()
|
srcset = list()
|
||||||
for thumbnail in thumbnails:
|
for resolution in [THUMBNAIL_RESOLUTION_1X, THUMBNAIL_RESOLUTION_2X, THUMBNAIL_RESOLUTION_3X]:
|
||||||
timestamp = int(thumbnail.generated_at.timestamp() * 1000)
|
timestamp = int(book.last_modified.timestamp() * 1000)
|
||||||
cache_id = str(thumbnail.uuid) + '_' + str(timestamp)
|
url = url_for('web.get_cover', book_id=book.id, resolution=resolution, cache_bust=str(timestamp))
|
||||||
url = url_for('web.get_cached_cover_thumbnail', cache_id=cache_id)
|
srcset.append(f'{url} {resolution}x')
|
||||||
srcset.append(url + ' ' + str(thumbnail.resolution) + 'x')
|
|
||||||
return ', '.join(srcset)
|
return ', '.join(srcset)
|
||||||
|
|
|
@ -21,7 +21,7 @@ from __future__ import division, print_function, unicode_literals
|
||||||
from .services.background_scheduler import BackgroundScheduler
|
from .services.background_scheduler import BackgroundScheduler
|
||||||
from .services.worker import WorkerThread
|
from .services.worker import WorkerThread
|
||||||
from .tasks.database import TaskReconnectDatabase
|
from .tasks.database import TaskReconnectDatabase
|
||||||
from .tasks.thumbnail import TaskSyncCoverThumbnailCache, TaskGenerateCoverThumbnails
|
from .tasks.thumbnail import TaskGenerateCoverThumbnails
|
||||||
|
|
||||||
|
|
||||||
def register_jobs():
|
def register_jobs():
|
||||||
|
@ -31,9 +31,6 @@ def register_jobs():
|
||||||
# Reconnect metadata.db once every 12 hours
|
# Reconnect metadata.db once every 12 hours
|
||||||
scheduler.add_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='cron', hour='4,16')
|
scheduler.add_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='cron', hour='4,16')
|
||||||
|
|
||||||
# Cleanup book cover cache once every 24 hours
|
|
||||||
scheduler.add_task(user=None, task=lambda: TaskSyncCoverThumbnailCache(), trigger='cron', hour=4)
|
|
||||||
|
|
||||||
# Generate all missing book cover thumbnails once every 24 hours
|
# Generate all missing book cover thumbnails once every 24 hours
|
||||||
scheduler.add_task(user=None, task=lambda: TaskGenerateCoverThumbnails(), trigger='cron', hour=4)
|
scheduler.add_task(user=None, task=lambda: TaskGenerateCoverThumbnails(), trigger='cron', hour=4)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import os
|
||||||
from cps import config, db, fs, gdriveutils, logger, ub
|
from cps import config, db, fs, gdriveutils, logger, ub
|
||||||
from cps.services.worker import CalibreTask
|
from cps.services.worker import CalibreTask
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from sqlalchemy import func
|
from sqlalchemy import or_
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
@ -41,9 +41,8 @@ THUMBNAIL_RESOLUTION_3X = 3
|
||||||
|
|
||||||
|
|
||||||
class TaskGenerateCoverThumbnails(CalibreTask):
|
class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
def __init__(self, limit=100, task_message=u'Generating cover thumbnails'):
|
def __init__(self, task_message=u'Generating cover thumbnails'):
|
||||||
super(TaskGenerateCoverThumbnails, self).__init__(task_message)
|
super(TaskGenerateCoverThumbnails, self).__init__(task_message)
|
||||||
self.limit = limit
|
|
||||||
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)
|
||||||
|
@ -55,74 +54,51 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
|
|
||||||
def run(self, worker_thread):
|
def run(self, worker_thread):
|
||||||
if self.calibre_db.session and use_IM:
|
if self.calibre_db.session and use_IM:
|
||||||
expired_thumbnails = self.get_expired_thumbnails()
|
books_with_covers = self.get_books_with_covers()
|
||||||
thumbnail_book_ids = self.get_thumbnail_book_ids()
|
count = len(books_with_covers)
|
||||||
books_without_thumbnails = self.get_books_without_thumbnails(thumbnail_book_ids)
|
|
||||||
|
|
||||||
count = len(books_without_thumbnails)
|
updated = 0
|
||||||
if count == 0:
|
generated = 0
|
||||||
# Do not display this task on the frontend if there are no covers to update
|
for i, book in enumerate(books_with_covers):
|
||||||
self.self_cleanup = True
|
book_cover_thumbnails = self.get_book_cover_thumbnails(book.id)
|
||||||
|
|
||||||
for i, book in enumerate(books_without_thumbnails):
|
# Generate new thumbnails for missing covers
|
||||||
for resolution in self.resolutions:
|
resolutions = list(map(lambda t: t.resolution, book_cover_thumbnails))
|
||||||
expired_thumbnail = self.get_expired_thumbnail_for_book_and_resolution(
|
missing_resolutions = list(set(self.resolutions).difference(resolutions))
|
||||||
book,
|
for resolution in missing_resolutions:
|
||||||
resolution,
|
generated += 1
|
||||||
expired_thumbnails
|
self.create_book_cover_thumbnail(book, resolution)
|
||||||
)
|
|
||||||
if expired_thumbnail:
|
|
||||||
self.update_book_thumbnail(book, expired_thumbnail)
|
|
||||||
else:
|
|
||||||
self.create_book_thumbnail(book, resolution)
|
|
||||||
|
|
||||||
self.message = u'Generating cover thumbnail {0} of {1}'.format(i + 1, count)
|
# Replace outdated or missing thumbnails
|
||||||
|
for thumbnail in book_cover_thumbnails:
|
||||||
|
if book.last_modified > thumbnail.generated_at:
|
||||||
|
updated += 1
|
||||||
|
self.update_book_cover_thumbnail(book, thumbnail)
|
||||||
|
|
||||||
|
elif not self.cache.get_cache_file_exists(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS):
|
||||||
|
updated += 1
|
||||||
|
self.update_book_cover_thumbnail(book, thumbnail)
|
||||||
|
|
||||||
|
self.message = u'Processing book {0} of {1}'.format(i + 1, count)
|
||||||
self.progress = (1.0 / count) * i
|
self.progress = (1.0 / count) * i
|
||||||
|
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
self.app_db_session.remove()
|
self.app_db_session.remove()
|
||||||
|
|
||||||
def get_expired_thumbnails(self):
|
def get_books_with_covers(self):
|
||||||
return self.app_db_session\
|
|
||||||
.query(ub.Thumbnail)\
|
|
||||||
.filter(ub.Thumbnail.expiration < datetime.utcnow())\
|
|
||||||
.all()
|
|
||||||
|
|
||||||
def get_thumbnail_book_ids(self):
|
|
||||||
return self.app_db_session\
|
|
||||||
.query(ub.Thumbnail.book_id)\
|
|
||||||
.group_by(ub.Thumbnail.book_id)\
|
|
||||||
.having(func.min(ub.Thumbnail.expiration) > datetime.utcnow())\
|
|
||||||
.distinct()
|
|
||||||
|
|
||||||
def get_books_without_thumbnails(self, thumbnail_book_ids):
|
|
||||||
return self.calibre_db.session\
|
return self.calibre_db.session\
|
||||||
.query(db.Books)\
|
.query(db.Books)\
|
||||||
.filter(db.Books.has_cover == 1)\
|
.filter(db.Books.has_cover == 1)\
|
||||||
.filter(db.Books.id.notin_(thumbnail_book_ids))\
|
|
||||||
.limit(self.limit)\
|
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
def get_expired_thumbnail_for_book_and_resolution(self, book, resolution, expired_thumbnails):
|
def get_book_cover_thumbnails(self, book_id):
|
||||||
for thumbnail in expired_thumbnails:
|
return self.app_db_session\
|
||||||
if thumbnail.book_id == book.id and thumbnail.resolution == resolution:
|
.query(ub.Thumbnail)\
|
||||||
return thumbnail
|
.filter(ub.Thumbnail.book_id == book_id)\
|
||||||
|
.filter(or_(ub.Thumbnail.expiration.is_(None), ub.Thumbnail.expiration > datetime.utcnow()))\
|
||||||
|
.all()
|
||||||
|
|
||||||
return None
|
def create_book_cover_thumbnail(self, book, resolution):
|
||||||
|
|
||||||
def update_book_thumbnail(self, book, thumbnail):
|
|
||||||
thumbnail.generated_at = datetime.utcnow()
|
|
||||||
thumbnail.expiration = datetime.utcnow() + timedelta(days=30)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.app_db_session.commit()
|
|
||||||
self.generate_book_thumbnail(book, thumbnail)
|
|
||||||
except Exception as ex:
|
|
||||||
self.log.info(u'Error updating book thumbnail: ' + str(ex))
|
|
||||||
self._handleError(u'Error updating book thumbnail: ' + str(ex))
|
|
||||||
self.app_db_session.rollback()
|
|
||||||
|
|
||||||
def create_book_thumbnail(self, book, resolution):
|
|
||||||
thumbnail = ub.Thumbnail()
|
thumbnail = ub.Thumbnail()
|
||||||
thumbnail.book_id = book.id
|
thumbnail.book_id = book.id
|
||||||
thumbnail.format = 'jpeg'
|
thumbnail.format = 'jpeg'
|
||||||
|
@ -137,6 +113,18 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
self._handleError(u'Error creating book thumbnail: ' + str(ex))
|
self._handleError(u'Error creating book thumbnail: ' + str(ex))
|
||||||
self.app_db_session.rollback()
|
self.app_db_session.rollback()
|
||||||
|
|
||||||
|
def update_book_cover_thumbnail(self, book, thumbnail):
|
||||||
|
thumbnail.generated_at = datetime.utcnow()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.app_db_session.commit()
|
||||||
|
self.cache.delete_cache_file(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS)
|
||||||
|
self.generate_book_thumbnail(book, thumbnail)
|
||||||
|
except Exception as ex:
|
||||||
|
self.log.info(u'Error updating book thumbnail: ' + str(ex))
|
||||||
|
self._handleError(u'Error updating book thumbnail: ' + str(ex))
|
||||||
|
self.app_db_session.rollback()
|
||||||
|
|
||||||
def generate_book_thumbnail(self, book, thumbnail):
|
def generate_book_thumbnail(self, book, thumbnail):
|
||||||
if book and thumbnail:
|
if book and thumbnail:
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
|
@ -190,128 +178,6 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
||||||
return "ThumbnailsGenerate"
|
return "ThumbnailsGenerate"
|
||||||
|
|
||||||
|
|
||||||
class TaskSyncCoverThumbnailCache(CalibreTask):
|
|
||||||
def __init__(self, task_message=u'Syncing cover thumbnail cache'):
|
|
||||||
super(TaskSyncCoverThumbnailCache, self).__init__(task_message)
|
|
||||||
self.log = logger.create()
|
|
||||||
self.app_db_session = ub.get_new_session_instance()
|
|
||||||
self.calibre_db = db.CalibreDB(expire_on_commit=False)
|
|
||||||
self.cache = fs.FileSystem()
|
|
||||||
|
|
||||||
def run(self, worker_thread):
|
|
||||||
cached_thumbnail_files = self.cache.list_cache_files(fs.CACHE_TYPE_THUMBNAILS)
|
|
||||||
|
|
||||||
# Expire thumbnails in the database if the cached file is missing
|
|
||||||
# This case will happen if a user deletes the cache dir or cached files
|
|
||||||
if self.app_db_session:
|
|
||||||
self.expire_missing_thumbnails(cached_thumbnail_files)
|
|
||||||
self.progress = 0.25
|
|
||||||
|
|
||||||
# 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
|
|
||||||
if self.app_db_session and self.calibre_db:
|
|
||||||
book_ids = self.get_book_ids()
|
|
||||||
self.delete_thumbnails_for_missing_books(book_ids)
|
|
||||||
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
|
|
||||||
# This case will happen if a book was deleted and the thumbnail OR the metadata.db file was changed externally
|
|
||||||
if self.app_db_session:
|
|
||||||
db_thumbnail_files = self.get_thumbnail_filenames()
|
|
||||||
self.delete_extraneous_thumbnail_files(cached_thumbnail_files, db_thumbnail_files)
|
|
||||||
|
|
||||||
self._handleSuccess()
|
|
||||||
self.app_db_session.remove()
|
|
||||||
|
|
||||||
def expire_missing_thumbnails(self, filenames):
|
|
||||||
try:
|
|
||||||
self.app_db_session\
|
|
||||||
.query(ub.Thumbnail)\
|
|
||||||
.filter(ub.Thumbnail.filename.notin_(filenames))\
|
|
||||||
.update({"expiration": datetime.utcnow()}, synchronize_session=False)
|
|
||||||
self.app_db_session.commit()
|
|
||||||
except Exception as ex:
|
|
||||||
self.log.info(u'Error expiring thumbnails for missing cache files: ' + str(ex))
|
|
||||||
self._handleError(u'Error expiring thumbnails for missing cache files: ' + str(ex))
|
|
||||||
self.app_db_session.rollback()
|
|
||||||
|
|
||||||
def get_book_ids(self):
|
|
||||||
results = self.calibre_db.session\
|
|
||||||
.query(db.Books.id)\
|
|
||||||
.filter(db.Books.has_cover == 1)\
|
|
||||||
.distinct()
|
|
||||||
|
|
||||||
return [value for value, in results]
|
|
||||||
|
|
||||||
def delete_thumbnails_for_missing_books(self, book_ids):
|
|
||||||
try:
|
|
||||||
self.app_db_session\
|
|
||||||
.query(ub.Thumbnail)\
|
|
||||||
.filter(ub.Thumbnail.book_id.notin_(book_ids))\
|
|
||||||
.delete(synchronize_session=False)
|
|
||||||
self.app_db_session.commit()
|
|
||||||
except Exception as ex:
|
|
||||||
self.log.info(str(ex))
|
|
||||||
self._handleError(u'Error deleting thumbnails for missing books: ' + str(ex))
|
|
||||||
self.app_db_session.rollback()
|
|
||||||
|
|
||||||
def get_thumbnail_filenames(self):
|
|
||||||
results = self.app_db_session\
|
|
||||||
.query(ub.Thumbnail.filename)\
|
|
||||||
.all()
|
|
||||||
|
|
||||||
return [thumbnail for thumbnail, in results]
|
|
||||||
|
|
||||||
def delete_extraneous_thumbnail_files(self, cached_thumbnail_files, db_thumbnail_files):
|
|
||||||
extraneous_files = list(set(cached_thumbnail_files).difference(db_thumbnail_files))
|
|
||||||
for file in extraneous_files:
|
|
||||||
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
|
|
||||||
def name(self):
|
|
||||||
return "ThumbnailsSync"
|
|
||||||
|
|
||||||
|
|
||||||
class TaskClearCoverThumbnailCache(CalibreTask):
|
class TaskClearCoverThumbnailCache(CalibreTask):
|
||||||
def __init__(self, book_id=None, task_message=u'Clearing cover thumbnail cache'):
|
def __init__(self, book_id=None, task_message=u'Clearing cover thumbnail cache'):
|
||||||
super(TaskClearCoverThumbnailCache, self).__init__(task_message)
|
super(TaskClearCoverThumbnailCache, self).__init__(task_message)
|
||||||
|
@ -325,9 +191,9 @@ class TaskClearCoverThumbnailCache(CalibreTask):
|
||||||
if self.book_id:
|
if self.book_id:
|
||||||
thumbnails = self.get_thumbnails_for_book(self.book_id)
|
thumbnails = self.get_thumbnails_for_book(self.book_id)
|
||||||
for thumbnail in thumbnails:
|
for thumbnail in thumbnails:
|
||||||
self.expire_and_delete_thumbnail(thumbnail)
|
self.delete_thumbnail(thumbnail)
|
||||||
else:
|
else:
|
||||||
self.expire_and_delete_all_thumbnails()
|
self.delete_all_thumbnails()
|
||||||
|
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
self.app_db_session.remove()
|
self.app_db_session.remove()
|
||||||
|
@ -338,29 +204,19 @@ class TaskClearCoverThumbnailCache(CalibreTask):
|
||||||
.filter(ub.Thumbnail.book_id == book_id)\
|
.filter(ub.Thumbnail.book_id == book_id)\
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
def expire_and_delete_thumbnail(self, thumbnail):
|
def delete_thumbnail(self, thumbnail):
|
||||||
thumbnail.expiration = datetime.utcnow()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.app_db_session.commit()
|
|
||||||
self.cache.delete_cache_file(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS)
|
self.cache.delete_cache_file(thumbnail.filename, fs.CACHE_TYPE_THUMBNAILS)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log.info(u'Error expiring book thumbnail: ' + str(ex))
|
self.log.info(u'Error deleting book thumbnail: ' + str(ex))
|
||||||
self._handleError(u'Error expiring book thumbnail: ' + str(ex))
|
self._handleError(u'Error deleting book thumbnail: ' + str(ex))
|
||||||
self.app_db_session.rollback()
|
|
||||||
|
|
||||||
def expire_and_delete_all_thumbnails(self):
|
|
||||||
self.app_db_session\
|
|
||||||
.query(ub.Thumbnail)\
|
|
||||||
.update({'expiration': datetime.utcnow()})
|
|
||||||
|
|
||||||
|
def delete_all_thumbnails(self):
|
||||||
try:
|
try:
|
||||||
self.app_db_session.commit()
|
|
||||||
self.cache.delete_cache_dir(fs.CACHE_TYPE_THUMBNAILS)
|
self.cache.delete_cache_dir(fs.CACHE_TYPE_THUMBNAILS)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log.info(u'Error expiring book thumbnails: ' + str(ex))
|
self.log.info(u'Error deleting book thumbnails: ' + str(ex))
|
||||||
self._handleError(u'Error expiring book thumbnails: ' + str(ex))
|
self._handleError(u'Error deleting book thumbnails: ' + str(ex))
|
||||||
self.app_db_session.rollback()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
|
|
@ -37,8 +37,7 @@
|
||||||
<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) }}">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry, title=author.name|safe) }}
|
||||||
<!-- <img title="{{author.name|safe}}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" /> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
{% macro book_cover_image(book, thumbnails, title=None) -%}
|
{% macro book_cover_image(book, title=None) -%}
|
||||||
{%- set book_title = book.title if book.title else book.name -%}
|
{%- set book_title = book.title if book.title else book.name -%}
|
||||||
{%- set book_title = title if title else book_title -%}
|
{%- set book_title = title if title else book_title -%}
|
||||||
{% set srcset = thumbnails|get_book_thumbnail_srcset if thumbnails|length else '' %}
|
{% set srcset = book|get_cover_srcset %}
|
||||||
{%- if srcset|length -%}
|
<img
|
||||||
<img
|
srcset="{{ srcset }}"
|
||||||
srcset="{{ srcset }}"
|
src="{{ url_for('web.get_cover', book_id=book.id, resolution=0, cache_bust=book|last_modified) }}"
|
||||||
src="{{ url_for('web.get_cached_cover', cache_id=book|book_cover_cache_id) }}"
|
title="{{ book_title }}"
|
||||||
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,8 +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.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(book) }}
|
||||||
<!-- <img id="detailcover" title="{{book.title}}" src="{{ url_for('web.get_cover', book_id=book.id, edit=1|uuidfilter) }}" alt="{{ book.title }}"/> -->
|
|
||||||
</div>
|
</div>
|
||||||
{% if g.user.role_delete_books() %}
|
{% if g.user.role_delete_books() %}
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|
|
@ -4,8 +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.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img id="detailcover" title="{{entry.title}}" src="{{ url_for('web.get_cover', book_id=entry.id, edit=1|uuidfilter) }}" alt="{{ entry.title }}" /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-lg-9 book-meta">
|
<div class="col-sm-9 col-lg-9 book-meta">
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
{% 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">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img title="{{entry.title}}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
<a href="{{url_for('web.books_list', data=data, sort_param='stored', book_id=entry[0].series[0].id )}}">
|
<a href="{{url_for('web.books_list', data=data, sort_param='stored', book_id=entry[0].series[0].id )}}">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry[0], entry[0].id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry[0], title=entry[0].series[0].name|shortentitle) }}
|
||||||
<!-- <img title="{{entry.title}}" src="{{ url_for('web.get_cover', book_id=entry[0].id) }}" alt="{{ entry[0].name }}"/> -->
|
|
||||||
<span class="badge">{{entry.count}}</span>
|
<span class="badge">{{entry.count}}</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
<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">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img title="{{ entry.title }}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -88,8 +87,7 @@
|
||||||
<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">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img title="{{ entry.title }}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}"/> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -45,8 +45,7 @@
|
||||||
{% 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">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img title="{{entry.title}}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -32,8 +32,7 @@
|
||||||
<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">
|
||||||
<span class="img">
|
<span class="img">
|
||||||
{{ book_cover_image(entry, entry.id|get_book_thumbnails(thumbnails)) }}
|
{{ book_cover_image(entry) }}
|
||||||
<!-- <img title="{{entry.title}}" src="{{ url_for('web.get_cover', book_id=entry.id) }}" alt="{{ entry.title }}" /> -->
|
|
||||||
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
{% if entry.id in read_book_ids %}<span class="badge read glyphicon glyphicon-ok"></span>{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -532,7 +532,7 @@ class Thumbnail(Base):
|
||||||
resolution = Column(SmallInteger, default=1)
|
resolution = Column(SmallInteger, default=1)
|
||||||
filename = Column(String, default=filename)
|
filename = Column(String, default=filename)
|
||||||
generated_at = Column(DateTime, default=lambda: datetime.datetime.utcnow())
|
generated_at = Column(DateTime, default=lambda: datetime.datetime.utcnow())
|
||||||
expiration = Column(DateTime, default=lambda: datetime.datetime.utcnow() + datetime.timedelta(days=90))
|
expiration = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
# Add missing tables during migration of database
|
# Add missing tables during migration of database
|
||||||
|
|
83
cps/web.py
83
cps/web.py
|
@ -51,7 +51,6 @@ from . import babel, db, ub, config, get_locale, app
|
||||||
from . import calibre_db
|
from . import calibre_db
|
||||||
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
|
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
|
||||||
from .helper import check_valid_domain, render_task_status, check_email, check_username, \
|
from .helper import check_valid_domain, render_task_status, check_email, check_username, \
|
||||||
get_cached_book_cover, get_cached_book_cover_thumbnail, get_thumbnails_for_books, get_thumbnails_for_book_series, \
|
|
||||||
get_cc_columns, get_book_cover, get_download_link, send_mail, generate_random_password, \
|
get_cc_columns, get_book_cover, get_download_link, send_mail, generate_random_password, \
|
||||||
send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password, valid_email
|
send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password, valid_email
|
||||||
from .pagination import Pagination
|
from .pagination import Pagination
|
||||||
|
@ -415,10 +414,8 @@ def render_books_list(data, sort, book_id, page):
|
||||||
db.books_series_link,
|
db.books_series_link,
|
||||||
db.Books.id == db.books_series_link.c.book,
|
db.Books.id == db.books_series_link.c.book,
|
||||||
db.Series)
|
db.Series)
|
||||||
|
|
||||||
thumbnails = get_thumbnails_for_books(entries + random if type(random) is list else entries)
|
|
||||||
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, thumbnails=thumbnails)
|
title=_(u"Books"), page=website)
|
||||||
|
|
||||||
|
|
||||||
def render_rated_books(page, book_id, order):
|
def render_rated_books(page, book_id, order):
|
||||||
|
@ -467,9 +464,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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", thumbnails=thumbnails)
|
title=_(u"Hot Books (Most Downloaded)"), page="hot")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -497,16 +493,13 @@ def render_downloaded_books(page, order, user_id):
|
||||||
.filter(db.Books.id == book.id).first():
|
.filter(db.Books.id == book.id).first():
|
||||||
ub.delete_download(book.id)
|
ub.delete_download(book.id)
|
||||||
user = ub.session.query(ub.User).filter(ub.User.id == user_id).first()
|
user = ub.session.query(ub.User).filter(ub.User.id == user_id).first()
|
||||||
|
|
||||||
thumbnails = get_thumbnails_for_books(entries + random if type(random) is list else entries)
|
|
||||||
return render_title_template('index.html',
|
return render_title_template('index.html',
|
||||||
random=random,
|
random=random,
|
||||||
entries=entries,
|
entries=entries,
|
||||||
pagination=pagination,
|
pagination=pagination,
|
||||||
id=user_id,
|
id=user_id,
|
||||||
title=_(u"Downloaded books by %(user)s",user=user.name),
|
title=_(u"Downloaded books by %(user)s",user=user.name),
|
||||||
page="download",
|
page="download")
|
||||||
thumbnails=thumbnails)
|
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -535,10 +528,9 @@ 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", thumbnails=thumbnails)
|
other_books=other_books, page="author")
|
||||||
|
|
||||||
|
|
||||||
def render_publisher_books(page, book_id, order):
|
def render_publisher_books(page, book_id, order):
|
||||||
|
@ -551,10 +543,8 @@ def render_publisher_books(page, book_id, order):
|
||||||
db.books_series_link,
|
db.books_series_link,
|
||||||
db.Books.id == db.books_series_link.c.book,
|
db.Books.id == db.books_series_link.c.book,
|
||||||
db.Series)
|
db.Series)
|
||||||
thumbnails = get_thumbnails_for_books(entries + random if type(random) is list else entries)
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -566,10 +556,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -581,10 +569,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -596,10 +582,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -614,10 +598,8 @@ def render_category_books(page, book_id, order):
|
||||||
db.books_series_link,
|
db.books_series_link,
|
||||||
db.Books.id == db.books_series_link.c.book,
|
db.Books.id == db.books_series_link.c.book,
|
||||||
db.Series)
|
db.Series)
|
||||||
thumbnails = get_thumbnails_for_books(entries + random if type(random) is list else entries)
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -635,9 +617,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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", thumbnails=thumbnails)
|
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
||||||
|
|
||||||
|
|
||||||
def render_read_books(page, are_read, as_xml=False, order=None):
|
def render_read_books(page, are_read, as_xml=False, order=None):
|
||||||
|
@ -687,10 +668,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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, thumbnails=thumbnails)
|
title=name, page=pagename)
|
||||||
|
|
||||||
|
|
||||||
def render_archived_books(page, order):
|
def render_archived_books(page, order):
|
||||||
|
@ -713,9 +692,8 @@ 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 if type(random) is list else entries)
|
|
||||||
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, thumbnails=thumbnails)
|
title=name, page=pagename)
|
||||||
|
|
||||||
|
|
||||||
def render_prepare_search_form(cc):
|
def render_prepare_search_form(cc):
|
||||||
|
@ -752,7 +730,6 @@ 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):
|
||||||
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
|
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
|
||||||
entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit, *join)
|
entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit, *join)
|
||||||
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,
|
||||||
|
@ -761,8 +738,7 @@ 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 ##################################################################
|
||||||
|
@ -973,10 +949,9 @@ 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",
|
||||||
order=order_no, thumbnails=thumbnails)
|
order=order_no)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
@ -1392,17 +1367,13 @@ 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
|
||||||
|
|
||||||
entries = q[offset:limit_all]
|
|
||||||
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=entries,
|
entries=q[offset:limit_all],
|
||||||
result_count=result_count,
|
result_count=result_count,
|
||||||
title=_(u"Advanced Search"),
|
title=_(u"Advanced Search"),
|
||||||
page="advsearch",
|
page="advsearch")
|
||||||
thumbnails=thumbnails)
|
|
||||||
|
|
||||||
|
|
||||||
@web.route("/advsearch", methods=['GET'])
|
@web.route("/advsearch", methods=['GET'])
|
||||||
|
@ -1417,21 +1388,11 @@ def advanced_search_form():
|
||||||
|
|
||||||
|
|
||||||
@web.route("/cover/<int:book_id>")
|
@web.route("/cover/<int:book_id>")
|
||||||
|
@web.route("/cover/<int:book_id>/<int:resolution>")
|
||||||
|
@web.route("/cover/<int:book_id>/<int:resolution>/<string:cache_bust>")
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def get_cover(book_id):
|
def get_cover(book_id, resolution=None, cache_bust=None):
|
||||||
return get_book_cover(book_id)
|
return get_book_cover(book_id, resolution)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/cached-cover/<string:cache_id>")
|
|
||||||
@login_required_if_no_ano
|
|
||||||
def get_cached_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")
|
||||||
|
@ -1841,7 +1802,6 @@ 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,
|
||||||
|
@ -1853,8 +1813,7 @@ 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"Oops! Selected book title is unavailable. File does not exist or is not accessible")
|
log.debug(u"Oops! Selected book title is unavailable. File does not exist or is not accessible")
|
||||||
flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"),
|
flash(_(u"Oops! Selected book title is unavailable. File does not exist or is not accessible"),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user