diff --git a/cps/__init__.py b/cps/__init__.py index 5effb3d6..04ff4431 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -37,6 +37,9 @@ from . import config_sql, logger, cache_buster, cli, ub, db from .reverseproxy import ReverseProxied from .server import WebServer +# import queue +# queue = queue.Queue() + mimetypes.init() mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('application/epub+zip', '.epub') @@ -82,6 +85,8 @@ log = logger.create() from . import services +calibre_db = db.CalibreDB() + def create_app(): app.wsgi_app = ReverseProxied(app.wsgi_app) # For python2 convert path to unicode @@ -98,7 +103,8 @@ def create_app(): app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session)) web_server.init_app(app, config) - db.setup_db(config, cli.settingspath) + calibre_db.setup_db(config, cli.settingspath) + calibre_db.start() babel.init_app(app) _BABEL_TRANSLATIONS.update(str(item) for item in babel.list_translations()) diff --git a/cps/about.py b/cps/about.py index 5ca2c68a..676d91db 100644 --- a/cps/about.py +++ b/cps/about.py @@ -30,7 +30,7 @@ import babel, pytz, requests, sqlalchemy import werkzeug, flask, flask_login, flask_principal, jinja2 from flask_babel import gettext as _ -from . import db, converter, uploader, server, isoLanguages, constants +from . import db, calibre_db, converter, uploader, server, isoLanguages, constants from .web import render_title_template try: from flask_login import __version__ as flask_loginVersion @@ -85,10 +85,10 @@ _VERSIONS.update(uploader.get_versions()) @about.route("/stats") @flask_login.login_required def stats(): - counter = db.session.query(db.Books).count() - authors = db.session.query(db.Authors).count() - categorys = db.session.query(db.Tags).count() - series = db.session.query(db.Series).count() + counter = calibre_db.session.query(db.Books).count() + authors = calibre_db.session.query(db.Authors).count() + categorys = calibre_db.session.query(db.Tags).count() + series = calibre_db.session.query(db.Series).count() _VERSIONS['ebook converter'] = _(converter.get_calibre_version()) _VERSIONS['unrar'] = _(converter.get_unrar_version()) _VERSIONS['kepubify'] = _(converter.get_kepubify_version()) diff --git a/cps/admin.py b/cps/admin.py index 3450f2ed..b5483db1 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -38,7 +38,7 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.sql.expression import func from . import constants, logger, helper, services -from . import db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils +from . import db, calibre_db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils from .helper import speaking_language, check_valid_domain, send_test_mail, reset_password, generate_password_hash from .gdriveutils import is_gdrive_ready, gdrive_support from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano @@ -86,7 +86,7 @@ def shutdown(): showtext = {} if task in (0, 1): # valid commandos received # close all database connections - db.dispose() + calibre_db.dispose() ub.dispose() if task == 0: @@ -99,7 +99,7 @@ def shutdown(): if task == 2: log.warning("reconnecting to calibre database") - db.setup_db(config, ub.app_DB_path) + calibre_db.setup_db(config, ub.app_DB_path) showtext['text'] = _(u'Reconnect successful') return json.dumps(showtext) @@ -148,9 +148,9 @@ def configuration(): @login_required @admin_required def view_configuration(): - readColumn = db.session.query(db.Custom_Columns)\ + readColumn = calibre_db.session.query(db.Custom_Columns)\ .filter(and_(db.Custom_Columns.datatype == 'bool',db.Custom_Columns.mark_for_delete == 0)).all() - restrictColumns= db.session.query(db.Custom_Columns)\ + restrictColumns= calibre_db.session.query(db.Custom_Columns)\ .filter(and_(db.Custom_Columns.datatype == 'text',db.Custom_Columns.mark_for_delete == 0)).all() return render_title_template("config_view_edit.html", conf=config, readColumns=readColumn, restrictColumns=restrictColumns, @@ -944,7 +944,7 @@ def edit_user(user_id): translations = babel.list_translations() + [LC('en')] kobo_support = feature_support['kobo'] and config.config_kobo_sync for book in content.downloads: - downloadbook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + downloadbook = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() if downloadbook: downloads.append(downloadbook) else: diff --git a/cps/db.py b/cps/db.py old mode 100755 new mode 100644 index e58b2534..c908e1f2 --- a/cps/db.py +++ b/cps/db.py @@ -23,18 +23,22 @@ import os import re import ast from datetime import datetime +import threading +import time +import queue -from sqlalchemy import create_engine, event +from sqlalchemy import create_engine from sqlalchemy import Table, Column, ForeignKey, CheckConstraint from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float from sqlalchemy.orm import relationship, sessionmaker, scoped_session from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.exc import OperationalError - -session = None +from . import logger +# session = None cc_exceptions = ['datetime', 'comments', 'composite', 'series'] cc_classes = {} -engine = None +# engine = None Base = declarative_base() @@ -226,6 +230,7 @@ class Publishers(Base): class Data(Base): __tablename__ = 'data' + __table_args__ = {'schema':'calibre'} id = Column(Integer, primary_key=True) book = Column(Integer, ForeignKey('books.id'), nullable=False) @@ -314,136 +319,170 @@ class Custom_Columns(Base): return display_dict -def update_title_sort(config, conn=None): - # user defined sort function for calibre databases (Series, etc.) - def _title_sort(title): - # calibre sort stuff - title_pat = re.compile(config.config_title_regex, re.IGNORECASE) - match = title_pat.search(title) - if match: - prep = match.group(1) - title = title.replace(prep, '') + ', ' + prep - return title.strip() - - conn = conn or session.connection().connection.connection - conn.create_function("title_sort", 1, _title_sort) - - -def setup_db(config, app_db_path): - dispose() - global engine - - if not config.config_calibre_dir: - config.invalidate() - return False - - dbpath = os.path.join(config.config_calibre_dir, "metadata.db") - if not os.path.exists(dbpath): - config.invalidate() - return False - - try: - #engine = create_engine('sqlite:///{0}'.format(dbpath), - engine = create_engine('sqlite://', - echo=False, - isolation_level="SERIALIZABLE", - connect_args={'check_same_thread': False}) - engine.execute("attach database '{}' as calibre;".format(dbpath)) - engine.execute("attach database '{}' as app_settings;".format(app_db_path)) - - conn = engine.connect() - # conn.text_factory = lambda b: b.decode(errors = 'ignore') possible fix for #1302 - except Exception as e: - config.invalidate(e) - return False - - config.db_configured = True - update_title_sort(config, conn.connection) - - if not cc_classes: - cc = conn.execute("SELECT id, datatype FROM custom_columns") - - cc_ids = [] - books_custom_column_links = {} - for row in cc: - if row.datatype not in cc_exceptions: - books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata, - Column('book', Integer, ForeignKey('books.id'), - primary_key=True), - Column('value', Integer, - ForeignKey('custom_column_' + str(row.id) + '.id'), - primary_key=True) - ) - cc_ids.append([row.id, row.datatype]) - if row.datatype == 'bool': - ccdict = {'__tablename__': 'custom_column_' + str(row.id), - 'id': Column(Integer, primary_key=True), - 'book': Column(Integer, ForeignKey('books.id')), - 'value': Column(Boolean)} - elif row.datatype == 'int': - ccdict = {'__tablename__': 'custom_column_' + str(row.id), - 'id': Column(Integer, primary_key=True), - 'book': Column(Integer, ForeignKey('books.id')), - 'value': Column(Integer)} - elif row.datatype == 'float': - ccdict = {'__tablename__': 'custom_column_' + str(row.id), - 'id': Column(Integer, primary_key=True), - 'book': Column(Integer, ForeignKey('books.id')), - 'value': Column(Float)} +class CalibreDB(threading.Thread): + + def __init__(self): + threading.Thread.__init__(self) + self.engine = None + self.session = None + self.queue = None + self.log = None + + def add_queue(self,queue): + self.queue = queue + self.log = logger.create() + + def run(self): + while True: + i = self.queue.get() + if i == 'dummy': + self.queue.task_done() + break + if i['task'] == 'add_format': + cur_book = self.session.query(Books).filter(Books.id == i['id']).first() + cur_book.data.append(i['format']) + try: + # db.session.merge(cur_book) + self.session.commit() + except OperationalError as e: + self.session.rollback() + self.log.error("Database error: %s", e) + # self._handleError(_(u"Database error: %(error)s.", error=e)) + # return + self.queue.task_done() + + + def stop(self): + self.queue.put('dummy') + + def setup_db(self, config, app_db_path): + self.dispose() + # global engine + + if not config.config_calibre_dir: + config.invalidate() + return False + + dbpath = os.path.join(config.config_calibre_dir, "metadata.db") + if not os.path.exists(dbpath): + config.invalidate() + return False + + try: + #engine = create_engine('sqlite:///{0}'.format(dbpath), + self.engine = create_engine('sqlite://', + echo=False, + isolation_level="SERIALIZABLE", + connect_args={'check_same_thread': False}) + self.engine.execute("attach database '{}' as calibre;".format(dbpath)) + self.engine.execute("attach database '{}' as app_settings;".format(app_db_path)) + + conn = self.engine.connect() + # conn.text_factory = lambda b: b.decode(errors = 'ignore') possible fix for #1302 + except Exception as e: + config.invalidate(e) + return False + + config.db_configured = True + self.update_title_sort(config, conn.connection) + + if not cc_classes: + cc = conn.execute("SELECT id, datatype FROM custom_columns") + + cc_ids = [] + books_custom_column_links = {} + for row in cc: + if row.datatype not in cc_exceptions: + books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata, + Column('book', Integer, ForeignKey('books.id'), + primary_key=True), + Column('value', Integer, + ForeignKey('custom_column_' + str(row.id) + '.id'), + primary_key=True) + ) + cc_ids.append([row.id, row.datatype]) + if row.datatype == 'bool': + ccdict = {'__tablename__': 'custom_column_' + str(row.id), + 'id': Column(Integer, primary_key=True), + 'book': Column(Integer, ForeignKey('books.id')), + 'value': Column(Boolean)} + elif row.datatype == 'int': + ccdict = {'__tablename__': 'custom_column_' + str(row.id), + 'id': Column(Integer, primary_key=True), + 'book': Column(Integer, ForeignKey('books.id')), + 'value': Column(Integer)} + elif row.datatype == 'float': + ccdict = {'__tablename__': 'custom_column_' + str(row.id), + 'id': Column(Integer, primary_key=True), + 'book': Column(Integer, ForeignKey('books.id')), + 'value': Column(Float)} + else: + ccdict = {'__tablename__': 'custom_column_' + str(row.id), + 'id': Column(Integer, primary_key=True), + 'value': Column(String)} + cc_classes[row.id] = type(str('Custom_Column_' + str(row.id)), (Base,), ccdict) + + for cc_id in cc_ids: + if (cc_id[1] == 'bool') or (cc_id[1] == 'int') or (cc_id[1] == 'float'): + setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], + primaryjoin=( + Books.id == cc_classes[cc_id[0]].book), + backref='books')) else: - ccdict = {'__tablename__': 'custom_column_' + str(row.id), - 'id': Column(Integer, primary_key=True), - 'value': Column(String)} - cc_classes[row.id] = type(str('Custom_Column_' + str(row.id)), (Base,), ccdict) - - for cc_id in cc_ids: - if (cc_id[1] == 'bool') or (cc_id[1] == 'int') or (cc_id[1] == 'float'): - setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], - primaryjoin=( - Books.id == cc_classes[cc_id[0]].book), - backref='books')) - else: - setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], - secondary=books_custom_column_links[cc_id[0]], - backref='books')) - - - global session - Session = scoped_session(sessionmaker(autocommit=False, - autoflush=False, - bind=engine)) - session = Session() - return True - - -def dispose(): - global session - - old_session = session - session = None - if old_session: - try: old_session.close() - except: pass - if old_session.bind: - try: old_session.bind.dispose() - except Exception: pass - - for attr in list(Books.__dict__.keys()): - if attr.startswith("custom_column_"): - setattr(Books, attr, None) - - for db_class in cc_classes.values(): - Base.metadata.remove(db_class.__table__) - cc_classes.clear() - - for table in reversed(Base.metadata.sorted_tables): - name = table.key - if name.startswith("custom_column_") or name.startswith("books_custom_column_"): - if table is not None: - Base.metadata.remove(table) - - -def reconnect_db(config, app_db_path): - session.close() - engine.dispose() - setup_db(config, app_db_path) + setattr(Books, 'custom_column_' + str(cc_id[0]), relationship(cc_classes[cc_id[0]], + secondary=books_custom_column_links[cc_id[0]], + backref='books')) + + + # global session + Session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=self.engine)) + self.session = Session() + return True + + def update_title_sort(self, config, conn=None): + # user defined sort function for calibre databases (Series, etc.) + def _title_sort(title): + # calibre sort stuff + title_pat = re.compile(config.config_title_regex, re.IGNORECASE) + match = title_pat.search(title) + if match: + prep = match.group(1) + title = title.replace(prep, '') + ', ' + prep + return title.strip() + + conn = conn or self.session.connection().connection.connection + conn.create_function("title_sort", 1, _title_sort) + + def dispose(self): + # global session + + old_session = self.session + self.session = None + if old_session: + try: old_session.close() + except: pass + if old_session.bind: + try: old_session.bind.dispose() + except Exception: pass + + for attr in list(Books.__dict__.keys()): + if attr.startswith("custom_column_"): + setattr(Books, attr, None) + + for db_class in cc_classes.values(): + Base.metadata.remove(db_class.__table__) + cc_classes.clear() + + for table in reversed(Base.metadata.sorted_tables): + name = table.key + if name.startswith("custom_column_") or name.startswith("books_custom_column_"): + if table is not None: + Base.metadata.remove(table) + + + def reconnect_db(self, config, app_db_path): + self.session.close() + self.engine.dispose() + self.setup_db(config, app_db_path) diff --git a/cps/editbooks.py b/cps/editbooks.py index 79e3eafe..e54a5a28 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -33,7 +33,8 @@ from flask_login import current_user, login_required from sqlalchemy.exc import OperationalError from . import constants, logger, isoLanguages, gdriveutils, uploader, helper -from . import config, get_locale, db, ub, worker +from . import config, get_locale, ub, worker, db +from . import calibre_db from .helper import order_authors, common_filters from .web import login_required_if_no_ano, render_title_template, edit_required, upload_required @@ -175,7 +176,7 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session): @login_required def delete_book(book_id, book_format): if current_user.role_delete_books(): - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() if book: try: result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper()) @@ -193,13 +194,13 @@ def delete_book(book_id, book_format): # check if only this book links to: # author, language, series, tags, custom columns - modify_database_object([u''], book.authors, db.Authors, db.session, 'author') - modify_database_object([u''], book.tags, db.Tags, db.session, 'tags') - modify_database_object([u''], book.series, db.Series, db.session, 'series') - modify_database_object([u''], book.languages, db.Languages, db.session, 'languages') - modify_database_object([u''], book.publishers, db.Publishers, db.session, 'publishers') + modify_database_object([u''], book.authors, db.Authors, calibre_db.session, 'author') + modify_database_object([u''], book.tags, db.Tags, calibre_db.session, 'tags') + modify_database_object([u''], book.series, db.Series, calibre_db.session, 'series') + modify_database_object([u''], book.languages, db.Languages, calibre_db.session, 'languages') + modify_database_object([u''], book.publishers, db.Publishers, calibre_db.session, 'publishers') - cc = db.session.query(db.Custom_Columns).\ + cc = calibre_db.session.query(db.Custom_Columns).\ filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() for c in cc: cc_string = "custom_column_" + str(c.id) @@ -209,32 +210,32 @@ def delete_book(book_id, book_format): del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) log.debug('remove ' + str(c.id)) - db.session.delete(del_cc) - db.session.commit() + calibre_db.session.delete(del_cc) + calibre_db.session.commit() elif c.datatype == 'rating': del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) if len(del_cc.books) == 0: log.debug('remove ' + str(c.id)) - db.session.delete(del_cc) - db.session.commit() + calibre_db.session.delete(del_cc) + calibre_db.session.commit() else: del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) log.debug('remove ' + str(c.id)) - db.session.delete(del_cc) - db.session.commit() + calibre_db.session.delete(del_cc) + calibre_db.session.commit() else: modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id], - db.session, 'custom') - db.session.query(db.Books).filter(db.Books.id == book_id).delete() + calibre_db.session, 'custom') + calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete() else: - db.session.query(db.Data).filter(db.Data.book == book.id).\ + calibre_db.session.query(db.Data).filter(db.Data.book == book.id).\ filter(db.Data.format == book_format).delete() - db.session.commit() + calibre_db.session.commit() except Exception as e: log.debug(e) - db.session.rollback() + calibre_db.session.rollback() else: # book not found log.error('Book with id "%s" could not be deleted: not found', book_id) @@ -247,9 +248,9 @@ def delete_book(book_id, book_format): def render_edit_book(book_id): - db.update_title_sort(config) - cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() - book = db.session.query(db.Books)\ + calibre_db.update_title_sort(config) + cc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() + book = calibre_db.session.query(db.Books)\ .filter(db.Books.id == book_id).filter(common_filters()).first() if not book: @@ -304,7 +305,7 @@ def edit_book_ratings(to_save, book): ratingx2 = int(float(to_save["rating"]) * 2) if ratingx2 != old_rating: changed = True - is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first() + is_rating = calibre_db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first() if is_rating: book.ratings.append(is_rating) else: @@ -326,7 +327,7 @@ def edit_book_languages(to_save, book): for l in unknown_languages: log.error('%s is not a valid language', l) flash(_(u"%(langname)s is not a valid language", langname=l), category="error") - return modify_database_object(list(input_l), book.languages, db.Languages, db.session, 'languages') + return modify_database_object(list(input_l), book.languages, db.Languages, calibre_db.session, 'languages') def edit_book_publisher(to_save, book): @@ -334,15 +335,15 @@ def edit_book_publisher(to_save, book): if to_save["publisher"]: publisher = to_save["publisher"].rstrip().strip() if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name): - changed |= modify_database_object([publisher], book.publishers, db.Publishers, db.session, 'publisher') + changed |= modify_database_object([publisher], book.publishers, db.Publishers, calibre_db.session, 'publisher') elif len(book.publishers): - changed |= modify_database_object([], book.publishers, db.Publishers, db.session, 'publisher') + changed |= modify_database_object([], book.publishers, db.Publishers, calibre_db.session, 'publisher') return changed def edit_cc_data(book_id, book, to_save): changed = False - cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() + cc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() for c in cc: cc_string = "custom_column_" + str(c.id) if not c.is_multiple: @@ -365,12 +366,12 @@ def edit_cc_data(book_id, book, to_save): else: del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) - db.session.delete(del_cc) + calibre_db.session.delete(del_cc) changed = True else: cc_class = db.cc_classes[c.id] new_cc = cc_class(value=to_save[cc_string], book=book_id) - db.session.add(new_cc) + calibre_db.session.add(new_cc) changed = True else: @@ -382,18 +383,18 @@ def edit_cc_data(book_id, book, to_save): del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) if len(del_cc.books) == 0: - db.session.delete(del_cc) + calibre_db.session.delete(del_cc) changed = True cc_class = db.cc_classes[c.id] - new_cc = db.session.query(cc_class).filter( + new_cc = calibre_db.session.query(cc_class).filter( cc_class.value == to_save[cc_string].strip()).first() # if no cc val is found add it if new_cc is None: new_cc = cc_class(value=to_save[cc_string].strip()) - db.session.add(new_cc) + calibre_db.session.add(new_cc) changed = True - db.session.flush() - new_cc = db.session.query(cc_class).filter( + calibre_db.session.flush() + new_cc = calibre_db.session.query(cc_class).filter( cc_class.value == to_save[cc_string].strip()).first() # add cc value to book getattr(book, cc_string).append(new_cc) @@ -403,12 +404,12 @@ def edit_cc_data(book_id, book, to_save): del_cc = getattr(book, cc_string)[0] getattr(book, cc_string).remove(del_cc) if not del_cc.books or len(del_cc.books) == 0: - db.session.delete(del_cc) + calibre_db.session.delete(del_cc) changed = True else: input_tags = to_save[cc_string].split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) - changed |= modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session, + changed |= modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], calibre_db.session, 'custom') return changed @@ -446,7 +447,7 @@ def upload_single_file(request, book, book_id): return redirect(url_for('web.show_book', book_id=book.id)) file_size = os.path.getsize(saved_filename) - is_format = db.session.query(db.Data).filter(db.Data.book == book_id).\ + is_format = calibre_db.session.query(db.Data).filter(db.Data.book == book_id).\ filter(db.Data.format == file_ext.upper()).first() # Format entry already exists, no need to update the database @@ -455,11 +456,11 @@ def upload_single_file(request, book, book_id): else: try: db_format = db.Data(book_id, file_ext.upper(), file_size, file_name) - db.session.add(db_format) - db.session.commit() - db.update_title_sort(config) + calibre_db.session.add(db_format) + calibre_db.session.commit() + calibre_db.update_title_sort(config) except OperationalError as e: - db.session.rollback() + calibre_db.session.rollback() log.error('Database error: %s', e) flash(_(u"Database error: %(error)s.", error=e), category="error") return redirect(url_for('web.show_book', book_id=book.id)) @@ -498,8 +499,8 @@ def edit_book(book_id): return render_edit_book(book_id) # create the function for sorting... - db.update_title_sort(config) - book = db.session.query(db.Books)\ + calibre_db.update_title_sort(config) + book = calibre_db.session.query(db.Books)\ .filter(db.Books.id == book_id).filter(common_filters()).first() # Book not found @@ -531,13 +532,13 @@ def edit_book(book_id): if input_authors == ['']: input_authors = [_(u'Unknown')] # prevent empty Author - modif_date |= modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') + modif_date |= modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author') # Search for each author if author is in database, if not, authorname and sorted authorname is generated new # everything then is assembled for sorted author field in database sort_authors_list = list() for inp in input_authors: - stored_author = db.session.query(db.Authors).filter(db.Authors.name == inp).first() + stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first() if not stored_author: stored_author = helper.get_sorted_author(inp) else: @@ -581,17 +582,17 @@ def edit_book(book_id): # Handle identifiers input_identifiers = identifier_list(to_save, book) - modif_date |= modify_identifiers(input_identifiers, book.identifiers, db.session) + modif_date |= modify_identifiers(input_identifiers, book.identifiers, calibre_db.session) # Handle book tags input_tags = to_save["tags"].split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) - modif_date |= modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags') + modif_date |= modify_database_object(input_tags, book.tags, db.Tags, calibre_db.session, 'tags') # Handle book series input_series = [to_save["series"].strip()] input_series = [x for x in input_series if x != ''] - modif_date |= modify_database_object(input_series, book.series, db.Series, db.session, 'series') + modif_date |= modify_database_object(input_series, book.series, db.Series, calibre_db.session, 'series') if to_save["pubdate"]: try: @@ -615,7 +616,7 @@ def edit_book(book_id): if modif_date: book.last_modified = datetime.utcnow() - db.session.commit() + calibre_db.session.commit() if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() if "detail_view" in to_save: @@ -624,12 +625,12 @@ def edit_book(book_id): flash(_("Metadata successfully updated"), category="success") return render_edit_book(book_id) else: - db.session.rollback() + calibre_db.session.rollback() flash(error, category="error") return render_edit_book(book_id) except Exception as e: log.exception(e) - db.session.rollback() + calibre_db.session.rollback() flash(_("Error editing book, please check logfile for details"), category="error") return redirect(url_for('web.show_book', book_id=book.id)) @@ -671,8 +672,8 @@ def upload(): for requested_file in request.files.getlist("btn-upload"): try: # create the function for sorting... - db.update_title_sort(config) - db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) + calibre_db.update_title_sort(config) + calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) # check if file extension is correct if '.' in requested_file.filename: @@ -708,13 +709,13 @@ def upload(): + Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning") # handle authors - is_author = db.session.query(db.Authors).filter(db.Authors.name == authr).first() + is_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == authr).first() if is_author: db_author = is_author authr= is_author.name else: db_author = db.Authors(authr, helper.get_sorted_author(authr), "") - db.session.add(db_author) + calibre_db.session.add(db_author) title_dir = helper.get_valid_filename(title) author_dir = helper.get_valid_filename(authr) @@ -746,29 +747,29 @@ def upload(): # handle series db_series = None - is_series = db.session.query(db.Series).filter(db.Series.name == series).first() + is_series = calibre_db.session.query(db.Series).filter(db.Series.name == series).first() if is_series: db_series = is_series elif series != '': db_series = db.Series(series, "") - db.session.add(db_series) + calibre_db.session.add(db_series) # add language actually one value in list input_language = meta.languages db_language = None if input_language != "": input_language = isoLanguages.get(name=input_language).part3 - hasLanguage = db.session.query(db.Languages).filter(db.Languages.lang_code == input_language).first() + hasLanguage = calibre_db.session.query(db.Languages).filter(db.Languages.lang_code == input_language).first() if hasLanguage: db_language = hasLanguage else: db_language = db.Languages(input_language) - db.session.add(db_language) + calibre_db.session.add(db_language) # If the language of the file is excluded from the users view, it's not imported, to allow the user to view # the book it's language is set to the filter language if db_language != current_user.filter_language() and current_user.filter_language() != "all": - db_language = db.session.query(db.Languages).\ + db_language = calibre_db.session.query(db.Languages).\ filter(db.Languages.lang_code == current_user.filter_language()).first() # combine path and normalize path from windows systems @@ -788,25 +789,25 @@ def upload(): input_tags = tags.split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) if input_tags[0] !="": - modify_database_object(input_tags, db_book.tags, db.Tags, db.session, 'tags') + modify_database_object(input_tags, db_book.tags, db.Tags, calibre_db.session, 'tags') # flush content, get db_book.id available db_book.data.append(db_data) - db.session.add(db_book) - db.session.flush() + calibre_db.session.add(db_book) + calibre_db.session.flush() # add comment book_id = db_book.id upload_comment = Markup(meta.description).unescape() if upload_comment != "": - db.session.add(db.Comments(upload_comment, book_id)) + calibre_db.session.add(db.Comments(upload_comment, book_id)) # save data to database, reread data - db.session.commit() - db.update_title_sort(config) + calibre_db.session.commit() + calibre_db.update_title_sort(config) # Reread book. It's important not to filter the result, as it could have language which hide it from # current users view (tags are not stored/extracted from metadata and could also be limited) - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() # upload book to gdrive if nesseccary and add "(bookid)" to folder name if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() @@ -823,7 +824,7 @@ def upload(): flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath, error=e), category="error") - db.session.commit() + calibre_db.session.commit() if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() if error: @@ -846,7 +847,7 @@ def upload(): resp = {"location": url_for('web.show_book', book_id=db_book.id)} return Response(json.dumps(resp), mimetype='application/json') except OperationalError as e: - db.session.rollback() + calibre_db.session.rollback() log.error("Database error: %s", e) flash(_(u"Database error: %(error)s.", error=e), category="error") return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json') diff --git a/cps/helper.py b/cps/helper.py index 15d78490..68d1b26e 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -23,7 +23,6 @@ import os import io import json import mimetypes -import random import re import shutil import time @@ -42,6 +41,7 @@ from flask_login import current_user from sqlalchemy.sql.expression import true, false, and_, or_, text, func from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash +from . import calibre_db try: from urllib.parse import quote @@ -74,8 +74,8 @@ log = logger.create() # Convert existing book entry to new format def convert_book_format(book_id, calibrepath, old_book_format, new_book_format, user_id, kindle_mail=None): - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() - data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == old_book_format).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == old_book_format).first() if not data: error_message = _(u"%(format)s format not found for book id: %(book)d", format=old_book_format, book=book_id) log.error("convert_book_format: %s", error_message) @@ -212,7 +212,7 @@ def check_read_formats(entry): # 3: If Pdf file is existing, it's directly send to kindle email def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id): """Send email with attachments""" - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() if convert == 1: # returns None if success, otherwise errormessage @@ -324,7 +324,7 @@ def delete_book_file(book, calibrepath, book_format=None): def update_dir_structure_file(book_id, calibrepath, first_author): - localbook = db.session.query(db.Books).filter(db.Books.id == book_id).first() + localbook = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() path = os.path.join(calibrepath, localbook.path) authordir = localbook.path.split('/')[0] @@ -383,7 +383,7 @@ def update_dir_structure_file(book_id, calibrepath, first_author): def update_dir_structure_gdrive(book_id, first_author): error = False - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() path = book.path authordir = book.path.split('/')[0] @@ -494,13 +494,13 @@ def get_cover_on_failure(use_generic_cover): def get_book_cover(book_id): - book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters(allow_show_archived=True)).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters(allow_show_archived=True)).first() return get_book_cover_internal(book, use_generic_cover_on_failure=True) def get_book_cover_with_uuid(book_uuid, use_generic_cover_on_failure=True): - book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() return get_book_cover_internal(book, use_generic_cover_on_failure) @@ -775,7 +775,7 @@ def tags_filters(): # Creates for all stored languages a translated speaking name in the array for the UI def speaking_language(languages=None): if not languages: - languages = db.session.query(db.Languages).join(db.books_languages_link).join(db.Books)\ + languages = calibre_db.session.query(db.Languages).join(db.books_languages_link).join(db.Books)\ .filter(common_filters())\ .group_by(text('books_languages_link.lang_code')).all() for lang in languages: @@ -808,7 +808,7 @@ def order_authors(entry): error = False for auth in sort_authors: # ToDo: How to handle not found authorname - result = db.session.query(db.Authors).filter(db.Authors.sort == auth.lstrip().strip()).first() + result = calibre_db.session.query(db.Authors).filter(db.Authors.sort == auth.lstrip().strip()).first() if not result: error = True break @@ -825,12 +825,12 @@ def fill_indexpage(page, database, db_filter, order, *join): def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_show_archived, *join): if current_user.show_detail_random(): - randm = db.session.query(db.Books).filter(common_filters(allow_show_archived))\ + randm = calibre_db.session.query(db.Books).filter(common_filters(allow_show_archived))\ .order_by(func.random()).limit(config.config_random_books) else: randm = false() off = int(int(config.config_books_per_page) * (page - 1)) - query = db.session.query(database).join(*join, isouter=True).\ + query = calibre_db.session.query(database).join(*join, isouter=True).\ filter(db_filter).\ filter(common_filters(allow_show_archived)) pagination = Pagination(page, config.config_books_per_page, @@ -843,8 +843,8 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s def get_typeahead(database, query, replace=('', ''), tag_filter=true()): query = query or '' - db.session.connection().connection.connection.create_function("lower", 1, lcase) - entries = db.session.query(database).filter(tag_filter).\ + calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) + entries = calibre_db.session.query(database).filter(tag_filter).\ filter(func.lower(database.name).ilike("%" + query + "%")).all() json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) return json_dumps @@ -852,7 +852,7 @@ def get_typeahead(database, query, replace=('', ''), tag_filter=true()): # read search results from calibre-database and return it (function is used for feed and simple search def get_search_results(term): - db.session.connection().connection.connection.create_function("lower", 1, lcase) + calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) q = list() authorterms = re.split("[, ]+", term) for authorterm in authorterms: @@ -860,7 +860,7 @@ def get_search_results(term): db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + term + "%")) - return db.session.query(db.Books).filter(common_filters()).filter( + return calibre_db.session.query(db.Books).filter(common_filters()).filter( or_(db.Books.tags.any(func.lower(db.Tags.name).ilike("%" + term + "%")), db.Books.series.any(func.lower(db.Series.name).ilike("%" + term + "%")), db.Books.authors.any(and_(*q)), @@ -870,7 +870,7 @@ def get_search_results(term): def get_cc_columns(filter_config_custom_read=False): - tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() + tmpcc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() cc = [] r = None if config.config_columns_to_ignore: @@ -887,9 +887,9 @@ def get_cc_columns(filter_config_custom_read=False): def get_download_link(book_id, book_format, client): book_format = book_format.split(".")[0] - book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() if book: - data1 = db.session.query(db.Data).filter(db.Data.book == book.id)\ + data1 = calibre_db.session.query(db.Data).filter(db.Data.book == book.id)\ .filter(db.Data.format == book_format.upper()).first() else: abort(404) @@ -911,13 +911,13 @@ def get_download_link(book_id, book_format, client): def check_exists_book(authr, title): - db.session.connection().connection.connection.create_function("lower", 1, lcase) + calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) q = list() authorterms = re.split(r'\s*&\s*', authr) for authorterm in authorterms: q.append(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) - return db.session.query(db.Books).filter( + return calibre_db.session.query(db.Books).filter( and_(db.Books.authors.any(and_(*q)), func.lower(db.Books.title).ilike("%" + title + "%") )).first() diff --git a/cps/kobo.py b/cps/kobo.py index 58f43f06..eb543875 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -48,7 +48,7 @@ from sqlalchemy.sql.expression import and_, or_ from sqlalchemy.exc import StatementError import requests -from . import config, logger, kobo_auth, db, helper, shelf as shelf_lib, ub +from . import config, logger, kobo_auth, db, calibre_db, helper, shelf as shelf_lib, ub from .services import SyncToken as SyncToken from .web import download_required from .kobo_auth import requires_kobo_auth @@ -170,7 +170,7 @@ def HandleSyncRequest(): # It looks like it's treating the db.Books.last_modified field as a string and may fail # the comparison because of the +00:00 suffix. changed_entries = ( - db.session.query(db.Books) + calibre_db.session.query(db.Books) .join(db.Data) .filter(or_(func.datetime(db.Books.last_modified) > sync_token.books_last_modified, db.Books.id.in_(recently_restored_or_archived_books))) @@ -207,7 +207,7 @@ def HandleSyncRequest(): ub.KoboReadingState.user_id == current_user.id, ub.KoboReadingState.book_id.notin_(reading_states_in_new_entitlements)))) for kobo_reading_state in changed_reading_states.all(): - book = db.session.query(db.Books).filter(db.Books.id == kobo_reading_state.book_id).one_or_none() + book = calibre_db.session.query(db.Books).filter(db.Books.id == kobo_reading_state.book_id).one_or_none() if book: sync_results.append({ "ChangedReadingState": { @@ -256,7 +256,7 @@ def HandleMetadataRequest(book_uuid): if not current_app.wsgi_app.is_proxied: log.debug('Kobo: Received unproxied request, changed request port to server port') log.info("Kobo library metadata request received for book %s" % book_uuid) - book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() if not book or not book.data: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() @@ -474,7 +474,7 @@ def add_items_to_shelf(items, shelf): items_unknown_to_calibre.append(item) continue - book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() if not book: items_unknown_to_calibre.append(item) continue @@ -545,7 +545,7 @@ def HandleTagRemoveItem(tag_id): items_unknown_to_calibre.append(item) continue - book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() if not book: items_unknown_to_calibre.append(item) continue @@ -613,7 +613,7 @@ def create_kobo_tag(shelf): "Type": "UserTag" } for book_shelf in shelf.books: - book = db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none() if not book: log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id) continue @@ -629,7 +629,7 @@ def create_kobo_tag(shelf): @kobo.route("/v1/library//state", methods=["GET", "PUT"]) @login_required def HandleStateRequest(book_uuid): - book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() if not book or not book.data: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() @@ -804,7 +804,7 @@ def TopLevelEndpoint(): @login_required def HandleBookDeletionRequest(book_uuid): log.info("Kobo book deletion request received for book %s" % book_uuid) - book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() + book = calibre_db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() if not book: log.info(u"Book %s not found in database", book_uuid) return redirect_or_proxy_request() diff --git a/cps/opds.py b/cps/opds.py index 165d23ad..6b850371 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -30,7 +30,7 @@ from flask_login import current_user from sqlalchemy.sql.expression import func, text, or_, and_ from werkzeug.security import check_password_hash -from . import constants, logger, config, db, ub, services, get_locale, isoLanguages +from . import constants, logger, config, db, calibre_db, ub, services, get_locale, isoLanguages from .helper import fill_indexpage, get_download_link, get_book_cover, speaking_language from .pagination import Pagination from .web import common_filters, get_search_results, render_read_books, download_required @@ -108,7 +108,7 @@ def feed_new(): @opds.route("/opds/discover") @requires_basic_auth_if_no_ano def feed_discover(): - entries = db.session.query(db.Books).filter(common_filters()).order_by(func.random())\ + entries = calibre_db.session.query(db.Books).filter(common_filters()).order_by(func.random())\ .limit(config.config_books_per_page) pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page)) return render_xml_template('feed.xml', entries=entries, pagination=pagination) @@ -133,10 +133,10 @@ def feed_hot(): hot_books = all_books.offset(off).limit(config.config_books_per_page) entries = list() for book in hot_books: - downloadBook = db.session.query(db.Books).filter(db.Books.id == book.Downloads.book_id).first() + downloadBook = calibre_db.session.query(db.Books).filter(db.Books.id == book.Downloads.book_id).first() if downloadBook: entries.append( - db.session.query(db.Books).filter(common_filters()) + calibre_db.session.query(db.Books).filter(common_filters()) .filter(db.Books.id == book.Downloads.book_id).first() ) else: @@ -153,11 +153,11 @@ def feed_hot(): @requires_basic_auth_if_no_ano def feed_authorindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\ + entries = calibre_db.session.query(db.Authors).join(db.books_authors_link).join(db.Books).filter(common_filters())\ .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).limit(config.config_books_per_page)\ .offset(off) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, - len(db.session.query(db.Authors).all())) + len(calibre_db.session.query(db.Authors).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_author', pagination=pagination) @@ -176,11 +176,11 @@ def feed_author(book_id): @requires_basic_auth_if_no_ano def feed_publisherindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\ + entries = calibre_db.session.query(db.Publishers).join(db.books_publishers_link).join(db.Books).filter(common_filters())\ .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.sort)\ .limit(config.config_books_per_page).offset(off) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, - len(db.session.query(db.Publishers).all())) + len(calibre_db.session.query(db.Publishers).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_publisher', pagination=pagination) @@ -199,10 +199,10 @@ def feed_publisher(book_id): @requires_basic_auth_if_no_ano def feed_categoryindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters())\ + entries = calibre_db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters())\ .group_by(text('books_tags_link.tag')).order_by(db.Tags.name).offset(off).limit(config.config_books_per_page) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, - len(db.session.query(db.Tags).all())) + len(calibre_db.session.query(db.Tags).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_category', pagination=pagination) @@ -221,10 +221,10 @@ def feed_category(book_id): @requires_basic_auth_if_no_ano def feed_seriesindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters())\ + entries = calibre_db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters())\ .group_by(text('books_series_link.series')).order_by(db.Series.sort).offset(off).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, - len(db.session.query(db.Series).all())) + len(calibre_db.session.query(db.Series).all())) return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_series', pagination=pagination) @@ -243,7 +243,7 @@ def feed_series(book_id): @requires_basic_auth_if_no_ano def feed_ratingindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), + entries = calibre_db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), (db.Ratings.rating / 2).label('name')) \ .join(db.books_ratings_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_ratings_link.rating')).order_by(db.Ratings.rating).all() @@ -271,7 +271,7 @@ def feed_ratings(book_id): @requires_basic_auth_if_no_ano def feed_formatindex(): off = request.args.get("offset") or 0 - entries = db.session.query(db.Data).join(db.Books).filter(common_filters()) \ + entries = calibre_db.session.query(db.Data).join(db.Books).filter(common_filters()) \ .group_by(db.Data.format).order_by(db.Data.format).all() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(entries)) @@ -305,7 +305,7 @@ def feed_languagesindex(): cur_l = LC.parse(current_user.filter_language()) except UnknownLocaleError: cur_l = None - languages = db.session.query(db.Languages).filter( + languages = calibre_db.session.query(db.Languages).filter( db.Languages.lang_code == current_user.filter_language()).all() if cur_l: languages[0].name = cur_l.get_language_name(get_locale()) @@ -356,7 +356,7 @@ def feed_shelf(book_id): books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == book_id).order_by( ub.BookShelf.order.asc()).all() for book in books_in_shelf: - cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() result.append(cur_book) pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, len(result)) @@ -378,7 +378,7 @@ def opds_download_link(book_id, book_format): @opds.route("/ajax/book/", defaults={'library': ""}) @requires_basic_auth_if_no_ano def get_metadata_calibre_companion(uuid, library): - entry = db.session.query(db.Books).filter(db.Books.uuid.like("%" + uuid + "%")).first() + entry = calibre_db.session.query(db.Books).filter(db.Books.uuid.like("%" + uuid + "%")).first() if entry is not None: js = render_template('json.txt', entry=entry) response = make_response(js) diff --git a/cps/server.py b/cps/server.py index d2253ab2..9180c821 100644 --- a/cps/server.py +++ b/cps/server.py @@ -196,6 +196,9 @@ class WebServer(object): def stop(self, restart=False): from . import updater_thread updater_thread.stop() + from . import calibre_db + calibre_db.stop() + log.info("webserver stop (restart=%s)", restart) self.restart = restart diff --git a/cps/shelf.py b/cps/shelf.py index 4d6d5103..1620789c 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -28,7 +28,7 @@ from flask_babel import gettext as _ from flask_login import login_required, current_user from sqlalchemy.sql.expression import func -from . import logger, ub, searched_ids, db +from . import logger, ub, searched_ids, db, calibre_db from .web import render_title_template from .helper import common_filters @@ -320,11 +320,11 @@ def show_shelf(shelf_type, shelf_id): books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\ .order_by(ub.BookShelf.order.asc()).all() for book in books_in_shelf: - cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() if cur_book: result.append(cur_book) else: - cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() if not cur_book: log.info('Not existing book %s in %s deleted', book.book_id, shelf) ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book.book_id).delete() @@ -356,7 +356,7 @@ def order_shelf(shelf_id): books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ .order_by(ub.BookShelf.order.asc()).all() for book in books_in_shelf2: - cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() if cur_book: result.append({'title': cur_book.title, 'id': cur_book.id, @@ -364,7 +364,7 @@ def order_shelf(shelf_id): 'series': cur_book.series, 'series_index': cur_book.series_index}) else: - cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() result.append({'title': _('Hidden Book'), 'id': cur_book.id, 'author': [], diff --git a/cps/web.py b/cps/web.py index 887b3620..48845a2b 100644 --- a/cps/web.py +++ b/cps/web.py @@ -50,6 +50,7 @@ from werkzeug.security import generate_password_hash, check_password_hash from . import constants, logger, isoLanguages, services, worker from . import searched_ids, lm, babel, db, ub, config, get_locale, app +from . import calibre_db from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download from .helper import common_filters, get_search_results, fill_indexpage, fill_indexpage_with_archived_books, \ speaking_language, check_valid_domain, order_authors, get_typeahead, render_task_status, json_serial, \ @@ -438,21 +439,21 @@ def toggle_read(book_id): ub.session.commit() else: try: - db.update_title_sort(config) - book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + calibre_db.update_title_sort(config) + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() read_status = getattr(book, 'custom_column_' + str(config.config_read_column)) if len(read_status): read_status[0].value = not read_status[0].value - db.session.commit() + calibre_db.session.commit() else: cc_class = db.cc_classes[config.config_read_column] new_cc = cc_class(value=1, book=book_id) - db.session.add(new_cc) - db.session.commit() + calibre_db.session.add(new_cc) + calibre_db.session.commit() except KeyError: log.error(u"Custom Column No.%d is not exisiting in calibre database", config.config_read_column) except OperationalError as e: - db.session.rollback() + calibre_db.session.rollback() log.error(u"Read status could not set: %e", e) return "" @@ -496,7 +497,7 @@ def update_view(): @web.route("/ajax/getcomic///") @login_required def get_comic_book(book_id, book_format, page): - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() if not book: return "", 204 else: @@ -589,8 +590,8 @@ def get_languages_json(): @login_required_if_no_ano def get_matching_tags(): tag_dict = {'tags': []} - q = db.session.query(db.Books) - db.session.connection().connection.connection.create_function("lower", 1, lcase) + q = calibre_db.session.query(db.Books) + calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) author_input = request.args.get('author_name') or '' title_input = request.args.get('book_title') or '' include_tag_inputs = request.args.getlist('include_tag') or '' @@ -692,7 +693,7 @@ def books_list(data, sort, book_id, page): def render_hot_books(page): if current_user.check_visibility(constants.SIDEBAR_HOT): if current_user.show_detail_random(): - random = db.session.query(db.Books).filter(common_filters()) \ + random = calibre_db.session.query(db.Books).filter(common_filters()) \ .order_by(func.random()).limit(config.config_random_books) else: random = false() @@ -702,7 +703,7 @@ def render_hot_books(page): hot_books = all_books.offset(off).limit(config.config_books_per_page) entries = list() for book in hot_books: - downloadBook = db.session.query(db.Books).filter(common_filters()).filter( + downloadBook = calibre_db.session.query(db.Books).filter(common_filters()).filter( db.Books.id == book.Downloads.book_id).first() if downloadBook: entries.append(downloadBook) @@ -727,7 +728,7 @@ def render_author_books(page, author_id, order): category="error") return redirect(url_for("web.index")) - author = db.session.query(db.Authors).get(author_id) + author = calibre_db.session.query(db.Authors).get(author_id) author_name = author.name.replace('|', ',') author_info = None @@ -742,7 +743,7 @@ def render_author_books(page, author_id, order): def render_publisher_books(page, book_id, order): - publisher = db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first() + publisher = calibre_db.session.query(db.Publishers).filter(db.Publishers.id == book_id).first() if publisher: entries, random, pagination = fill_indexpage(page, db.Books, db.Books.publishers.any(db.Publishers.id == book_id), @@ -755,7 +756,7 @@ def render_publisher_books(page, book_id, order): def render_series_books(page, book_id, order): - name = db.session.query(db.Series).filter(db.Series.id == book_id).first() + name = calibre_db.session.query(db.Series).filter(db.Series.id == book_id).first() if name: entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index, order[0]]) @@ -766,7 +767,7 @@ def render_series_books(page, book_id, order): def render_ratings_books(page, book_id, order): - name = db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first() + name = calibre_db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first() entries, random, pagination = fill_indexpage(page, db.Books, db.Books.ratings.any(db.Ratings.id == book_id), [db.Books.timestamp.desc(), order[0]]) if name and name.rating <= 10: @@ -777,7 +778,7 @@ def render_ratings_books(page, book_id, order): def render_formats_books(page, book_id, order): - name = db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first() + name = calibre_db.session.query(db.Data).filter(db.Data.format == book_id.upper()).first() if name: entries, random, pagination = fill_indexpage(page, db.Books, db.Books.data.any(db.Data.format == book_id.upper()), @@ -789,7 +790,7 @@ def render_formats_books(page, book_id, order): def render_category_books(page, book_id, order): - name = db.session.query(db.Tags).filter(db.Tags.id == book_id).first() + name = calibre_db.session.query(db.Tags).filter(db.Tags.id == book_id).first() if name: entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id), [order[0], db.Series.name, db.Books.series_index], @@ -825,10 +826,10 @@ def books_table(): @login_required_if_no_ano def author_list(): if current_user.check_visibility(constants.SIDEBAR_AUTHOR): - entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count')) \ + entries = calibre_db.session.query(db.Authors, func.count('books_authors_link.book').label('count')) \ .join(db.books_authors_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).all() - charlist = db.session.query(func.upper(func.substr(db.Authors.sort, 1, 1)).label('char')) \ + charlist = calibre_db.session.query(func.upper(func.substr(db.Authors.sort, 1, 1)).label('char')) \ .join(db.books_authors_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Authors.sort, 1, 1))).all() for entry in entries: @@ -843,10 +844,10 @@ def author_list(): @login_required_if_no_ano def publisher_list(): if current_user.check_visibility(constants.SIDEBAR_PUBLISHER): - entries = db.session.query(db.Publishers, func.count('books_publishers_link.book').label('count')) \ + entries = calibre_db.session.query(db.Publishers, func.count('books_publishers_link.book').label('count')) \ .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.name).all() - charlist = db.session.query(func.upper(func.substr(db.Publishers.name, 1, 1)).label('char')) \ + charlist = calibre_db.session.query(func.upper(func.substr(db.Publishers.name, 1, 1)).label('char')) \ .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, @@ -860,19 +861,19 @@ def publisher_list(): def series_list(): if current_user.check_visibility(constants.SIDEBAR_SERIES): if current_user.series_view == 'list': - entries = db.session.query(db.Series, func.count('books_series_link.book').label('count')) \ + entries = calibre_db.session.query(db.Series, func.count('books_series_link.book').label('count')) \ .join(db.books_series_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_series_link.series')).order_by(db.Series.sort).all() - charlist = db.session.query(func.upper(func.substr(db.Series.sort, 1, 1)).label('char')) \ + charlist = calibre_db.session.query(func.upper(func.substr(db.Series.sort, 1, 1)).label('char')) \ .join(db.books_series_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, title=_(u"Series"), page="serieslist", data="series") else: - entries = db.session.query(db.Books, func.count('books_series_link').label('count')) \ + entries = calibre_db.session.query(db.Books, func.count('books_series_link').label('count')) \ .join(db.books_series_link).join(db.Series).filter(common_filters()) \ .group_by(text('books_series_link.series')).order_by(db.Series.sort).all() - charlist = db.session.query(func.upper(func.substr(db.Series.sort, 1, 1)).label('char')) \ + charlist = calibre_db.session.query(func.upper(func.substr(db.Series.sort, 1, 1)).label('char')) \ .join(db.books_series_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all() @@ -886,9 +887,9 @@ def series_list(): @login_required_if_no_ano def ratings_list(): if current_user.check_visibility(constants.SIDEBAR_RATING): - entries = db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), + entries = calibre_db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'), (db.Ratings.rating / 2).label('name')) \ - .join(db.books_ratings_link).join(db.Books).filter(common_filters()) \ + .join(calibre_db.books_ratings_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_ratings_link.rating')).order_by(db.Ratings.rating).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(), title=_(u"Ratings list"), page="ratingslist", data="ratings") @@ -900,7 +901,7 @@ def ratings_list(): @login_required_if_no_ano def formats_list(): if current_user.check_visibility(constants.SIDEBAR_FORMAT): - entries = db.session.query(db.Data, func.count('data.book').label('count'), db.Data.format.label('format')) \ + entries = calibre_db.session.query(db.Data, func.count('data.book').label('count'), db.Data.format.label('format')) \ .join(db.Books).filter(common_filters()) \ .group_by(db.Data.format).order_by(db.Data.format).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(), @@ -922,13 +923,13 @@ def language_overview(): cur_l = LC.parse(current_user.filter_language()) except UnknownLocaleError: cur_l = None - languages = db.session.query(db.Languages).filter( + languages = calibre_db.session.query(db.Languages).filter( db.Languages.lang_code == current_user.filter_language()).all() if cur_l: languages[0].name = cur_l.get_language_name(get_locale()) else: languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name) - lang_counter = db.session.query(db.books_languages_link, + lang_counter = calibre_db.session.query(db.books_languages_link, func.count('books_languages_link.book').label('bookcount')).group_by( text('books_languages_link.lang_code')).all() return render_title_template('languages.html', languages=languages, lang_counter=lang_counter, @@ -942,10 +943,10 @@ def language_overview(): @login_required_if_no_ano def category_list(): if current_user.check_visibility(constants.SIDEBAR_CATEGORY): - entries = db.session.query(db.Tags, func.count('books_tags_link.book').label('count')) \ + entries = calibre_db.session.query(db.Tags, func.count('books_tags_link.book').label('count')) \ .join(db.books_tags_link).join(db.Books).order_by(db.Tags.name).filter(common_filters()) \ .group_by(text('books_tags_link.tag')).all() - charlist = db.session.query(func.upper(func.substr(db.Tags.name, 1, 1)).label('char')) \ + charlist = calibre_db.session.query(func.upper(func.substr(db.Tags.name, 1, 1)).label('char')) \ .join(db.books_tags_link).join(db.Books).filter(common_filters()) \ .group_by(func.upper(func.substr(db.Tags.name, 1, 1))).all() return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, @@ -1003,8 +1004,8 @@ def search(): def advanced_search(): # Build custom columns names cc = get_cc_columns() - db.session.connection().connection.connection.create_function("lower", 1, lcase) - q = db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort) + calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase) + q = calibre_db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort) include_tag_inputs = request.args.getlist('include_tag') exclude_tag_inputs = request.args.getlist('exclude_tag') @@ -1057,11 +1058,11 @@ def advanced_search(): format='medium', locale=get_locale())]) except ValueError: pub_start = u"" - tag_names = db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all() + tag_names = calibre_db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all() searchterm.extend(tag.name for tag in tag_names) - serie_names = db.session.query(db.Series).filter(db.Series.id.in_(include_series_inputs)).all() + serie_names = calibre_db.session.query(db.Series).filter(db.Series.id.in_(include_series_inputs)).all() searchterm.extend(serie.name for serie in serie_names) - language_names = db.session.query(db.Languages).filter(db.Languages.id.in_(include_languages_inputs)).all() + language_names = calibre_db.session.query(db.Languages).filter(db.Languages.id.in_(include_languages_inputs)).all() if language_names: language_names = speaking_language(language_names) searchterm.extend(language.name for language in language_names) @@ -1136,11 +1137,11 @@ def advanced_search(): return render_title_template('search.html', adv_searchterm=searchterm, entries=q, title=_(u"search"), page="search") # prepare data for search-form - tags = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters()) \ + tags = calibre_db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_tags_link.tag')).order_by(db.Tags.name).all() - series = db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters()) \ + series = calibre_db.session.query(db.Series).join(db.books_series_link).join(db.Books).filter(common_filters()) \ .group_by(text('books_series_link.series')).order_by(db.Series.name).filter(common_filters()).all() - extensions = db.session.query(db.Data).join(db.Books).filter(common_filters()) \ + extensions = calibre_db.session.query(db.Data).join(db.Books).filter(common_filters()) \ .group_by(db.Data.format).order_by(db.Data.format).all() if current_user.filter_language() == u"all": @@ -1229,8 +1230,8 @@ def get_cover(book_id): @viewer_required def serve_book(book_id, book_format, anyname): book_format = book_format.split(".")[0] - book = db.session.query(db.Books).filter(db.Books.id == book_id).first() - data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()) \ + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).first() + data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()) \ .first() log.info('Serving book: %s', data.name) if config.config_use_google_drive: @@ -1522,9 +1523,9 @@ def profile(): oauth_status = None for book in current_user.downloads: - downloadBook = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() + downloadBook = calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first() if downloadBook: - downloads.append(db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) + downloads.append(calibre_db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) else: ub.delete_download(book.book_id) if request.method == "POST": @@ -1603,7 +1604,7 @@ def profile(): @login_required_if_no_ano @viewer_required def read_book(book_id, book_format): - book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + book = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() if not book: flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") log.debug(u"Error opening eBook. File does not exist or file is not accessible:") @@ -1627,7 +1628,7 @@ def read_book(book_id, book_format): else: for fileExt in constants.EXTENSIONS_AUDIO: if book_format.lower() == fileExt: - entries = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() + entries = calibre_db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first() log.debug(u"Start mp3 listening for %d", book_id) return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(), title=_(u"Read a Book"), entry=entries, bookmark=bookmark) @@ -1653,7 +1654,7 @@ def read_book(book_id, book_format): @web.route("/book/") @login_required_if_no_ano def show_book(book_id): - entries = db.session.query(db.Books).filter(and_(db.Books.id == book_id, + entries = calibre_db.session.query(db.Books).filter(and_(db.Books.id == book_id, common_filters(allow_show_archived=True))).first() if entries: for index in range(0, len(entries.languages)): diff --git a/cps/worker.py b/cps/worker.py index fc08e6b1..d08853a0 100644 --- a/cps/worker.py +++ b/cps/worker.py @@ -24,10 +24,10 @@ import smtplib import socket import time import threading +import queue from glob import glob from shutil import copyfile from datetime import datetime -from sqlalchemy.exc import OperationalError try: from StringIO import StringIO @@ -46,7 +46,8 @@ from email.utils import make_msgid from email.generator import Generator from flask_babel import gettext as _ -from . import db, logger, config +from . import calibre_db, db +from . import logger, config from .subproc_wrapper import process_open from . import gdriveutils @@ -190,6 +191,8 @@ class WorkerThread(threading.Thread): self.UIqueue = list() self.asyncSMTP = None self.id = 0 + self.db_queue = queue.Queue() + calibre_db.add_queue(self.db_queue) self.doLock = threading.Lock() # Main thread loop starting the different tasks @@ -275,6 +278,18 @@ class WorkerThread(threading.Thread): self.doLock.acquire() index = self.current self.doLock.release() + '''dbpath = os.path.join(config.config_calibre_dir, "metadata.db") + engine = create_engine('sqlite://', + echo=False, + isolation_level="SERIALIZABLE", + connect_args={'check_same_thread': True}) + engine.execute("attach database '{}' as calibre;".format(dbpath)) + conn = engine.connect() + Session = scoped_session(sessionmaker(autocommit=False, + autoflush=False, + bind=engine)) + w_session = Session() + engine.execute("attach database '{}' as calibre;".format(dbpath))''' file_path = self.queue[index]['file_path'] bookid = self.queue[index]['bookid'] format_old_ext = u'.' + self.queue[index]['settings']['old_book_format'].lower() @@ -285,7 +300,7 @@ class WorkerThread(threading.Thread): # this will allow send to kindle workflow to continue to work if os.path.isfile(file_path + format_new_ext): log.info("Book id %d already converted to %s", bookid, format_new_ext) - cur_book = db.session.query(db.Books).filter(db.Books.id == bookid).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == bookid).first() self.queue[index]['path'] = file_path self.queue[index]['title'] = cur_book.title self._handleSuccess() @@ -309,19 +324,26 @@ class WorkerThread(threading.Thread): check, error_message = self._convert_calibre(file_path, format_old_ext, format_new_ext, index) if check == 0: - cur_book = db.session.query(db.Books).filter(db.Books.id == bookid).first() + cur_book = calibre_db.session.query(db.Books).filter(db.Books.id == bookid).first() if os.path.isfile(file_path + format_new_ext): + # self.db_queue.join() new_format = db.Data(name=cur_book.data[0].name, book_format=self.queue[index]['settings']['new_book_format'].upper(), book=bookid, uncompressed_size=os.path.getsize(file_path + format_new_ext)) - cur_book.data.append(new_format) + task = {'task':'add_format','id': bookid, 'format': new_format} + self.db_queue.put(task) + # To Do how to handle error? + print('finished') + + '''cur_book.data.append(new_format) try: - db.session.commit() + # db.session.merge(cur_book) + calibre_db.session.commit() except OperationalError as e: - db.session.rollback() + calibre_db.session.rollback() log.error("Database error: %s", e) self._handleError(_(u"Database error: %(error)s.", error=e)) - return + return''' self.queue[index]['path'] = cur_book.path self.queue[index]['title'] = cur_book.title @@ -375,6 +397,7 @@ class WorkerThread(threading.Thread): # process returncode check = p.returncode calibre_traceback = p.stderr.readlines() + error_message = "" for ele in calibre_traceback: if sys.version_info < (3, 0): ele = ele.decode('utf-8')