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_cached_cover', cache_id=book|book_cover_cache_id) }}" |         src="{{ url_for('web.get_cover', book_id=book.id, resolution=0, cache_bust=book|last_modified) }}" | ||||||
|  |         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