Fix for #1407 converting books should now be possible again

This commit is contained in:
Ozzieisaacs 2020-05-21 18:16:11 +02:00
parent a48418364c
commit 1a458fe39f
12 changed files with 383 additions and 310 deletions

View File

@ -37,6 +37,9 @@ from . import config_sql, logger, cache_buster, cli, ub, db
from .reverseproxy import ReverseProxied from .reverseproxy import ReverseProxied
from .server import WebServer from .server import WebServer
# import queue
# queue = queue.Queue()
mimetypes.init() mimetypes.init()
mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('application/xhtml+xml', '.xhtml')
mimetypes.add_type('application/epub+zip', '.epub') mimetypes.add_type('application/epub+zip', '.epub')
@ -82,6 +85,8 @@ log = logger.create()
from . import services from . import services
calibre_db = db.CalibreDB()
def create_app(): def create_app():
app.wsgi_app = ReverseProxied(app.wsgi_app) app.wsgi_app = ReverseProxied(app.wsgi_app)
# For python2 convert path to unicode # 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)) app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))
web_server.init_app(app, config) 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.init_app(app)
_BABEL_TRANSLATIONS.update(str(item) for item in babel.list_translations()) _BABEL_TRANSLATIONS.update(str(item) for item in babel.list_translations())

View File

@ -30,7 +30,7 @@ import babel, pytz, requests, sqlalchemy
import werkzeug, flask, flask_login, flask_principal, jinja2 import werkzeug, flask, flask_login, flask_principal, jinja2
from flask_babel import gettext as _ 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 from .web import render_title_template
try: try:
from flask_login import __version__ as flask_loginVersion from flask_login import __version__ as flask_loginVersion
@ -85,10 +85,10 @@ _VERSIONS.update(uploader.get_versions())
@about.route("/stats") @about.route("/stats")
@flask_login.login_required @flask_login.login_required
def stats(): def stats():
counter = db.session.query(db.Books).count() counter = calibre_db.session.query(db.Books).count()
authors = db.session.query(db.Authors).count() authors = calibre_db.session.query(db.Authors).count()
categorys = db.session.query(db.Tags).count() categorys = calibre_db.session.query(db.Tags).count()
series = db.session.query(db.Series).count() series = calibre_db.session.query(db.Series).count()
_VERSIONS['ebook converter'] = _(converter.get_calibre_version()) _VERSIONS['ebook converter'] = _(converter.get_calibre_version())
_VERSIONS['unrar'] = _(converter.get_unrar_version()) _VERSIONS['unrar'] = _(converter.get_unrar_version())
_VERSIONS['kepubify'] = _(converter.get_kepubify_version()) _VERSIONS['kepubify'] = _(converter.get_kepubify_version())

View File

@ -38,7 +38,7 @@ from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from . import constants, logger, helper, services 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 .helper import speaking_language, check_valid_domain, send_test_mail, reset_password, generate_password_hash
from .gdriveutils import is_gdrive_ready, gdrive_support from .gdriveutils import is_gdrive_ready, gdrive_support
from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano
@ -86,7 +86,7 @@ def shutdown():
showtext = {} showtext = {}
if task in (0, 1): # valid commandos received if task in (0, 1): # valid commandos received
# close all database connections # close all database connections
db.dispose() calibre_db.dispose()
ub.dispose() ub.dispose()
if task == 0: if task == 0:
@ -99,7 +99,7 @@ def shutdown():
if task == 2: if task == 2:
log.warning("reconnecting to calibre database") 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') showtext['text'] = _(u'Reconnect successful')
return json.dumps(showtext) return json.dumps(showtext)
@ -148,9 +148,9 @@ def configuration():
@login_required @login_required
@admin_required @admin_required
def view_configuration(): 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() .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() .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, return render_title_template("config_view_edit.html", conf=config, readColumns=readColumn,
restrictColumns=restrictColumns, restrictColumns=restrictColumns,
@ -944,7 +944,7 @@ def edit_user(user_id):
translations = babel.list_translations() + [LC('en')] translations = babel.list_translations() + [LC('en')]
kobo_support = feature_support['kobo'] and config.config_kobo_sync kobo_support = feature_support['kobo'] and config.config_kobo_sync
for book in content.downloads: 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: if downloadbook:
downloads.append(downloadbook) downloads.append(downloadbook)
else: else:

109
cps/db.py Executable file → Normal file
View File

@ -23,18 +23,22 @@ import os
import re import re
import ast import ast
from datetime import datetime 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 Table, Column, ForeignKey, CheckConstraint
from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float
from sqlalchemy.orm import relationship, sessionmaker, scoped_session from sqlalchemy.orm import relationship, sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import OperationalError
from . import logger
session = None # session = None
cc_exceptions = ['datetime', 'comments', 'composite', 'series'] cc_exceptions = ['datetime', 'comments', 'composite', 'series']
cc_classes = {} cc_classes = {}
engine = None # engine = None
Base = declarative_base() Base = declarative_base()
@ -226,6 +230,7 @@ class Publishers(Base):
class Data(Base): class Data(Base):
__tablename__ = 'data' __tablename__ = 'data'
__table_args__ = {'schema':'calibre'}
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
book = Column(Integer, ForeignKey('books.id'), nullable=False) book = Column(Integer, ForeignKey('books.id'), nullable=False)
@ -314,24 +319,45 @@ class Custom_Columns(Base):
return display_dict return display_dict
def update_title_sort(config, conn=None): class CalibreDB(threading.Thread):
# 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 def __init__(self):
conn.create_function("title_sort", 1, _title_sort) 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 setup_db(config, app_db_path): def stop(self):
dispose() self.queue.put('dummy')
global engine
def setup_db(self, config, app_db_path):
self.dispose()
# global engine
if not config.config_calibre_dir: if not config.config_calibre_dir:
config.invalidate() config.invalidate()
@ -344,21 +370,21 @@ def setup_db(config, app_db_path):
try: try:
#engine = create_engine('sqlite:///{0}'.format(dbpath), #engine = create_engine('sqlite:///{0}'.format(dbpath),
engine = create_engine('sqlite://', self.engine = create_engine('sqlite://',
echo=False, echo=False,
isolation_level="SERIALIZABLE", isolation_level="SERIALIZABLE",
connect_args={'check_same_thread': False}) connect_args={'check_same_thread': False})
engine.execute("attach database '{}' as calibre;".format(dbpath)) self.engine.execute("attach database '{}' as calibre;".format(dbpath))
engine.execute("attach database '{}' as app_settings;".format(app_db_path)) self.engine.execute("attach database '{}' as app_settings;".format(app_db_path))
conn = engine.connect() conn = self.engine.connect()
# conn.text_factory = lambda b: b.decode(errors = 'ignore') possible fix for #1302 # conn.text_factory = lambda b: b.decode(errors = 'ignore') possible fix for #1302
except Exception as e: except Exception as e:
config.invalidate(e) config.invalidate(e)
return False return False
config.db_configured = True config.db_configured = True
update_title_sort(config, conn.connection) self.update_title_sort(config, conn.connection)
if not cc_classes: if not cc_classes:
cc = conn.execute("SELECT id, datatype FROM custom_columns") cc = conn.execute("SELECT id, datatype FROM custom_columns")
@ -408,19 +434,32 @@ def setup_db(config, app_db_path):
backref='books')) backref='books'))
global session # global session
Session = scoped_session(sessionmaker(autocommit=False, Session = scoped_session(sessionmaker(autocommit=False,
autoflush=False, autoflush=False,
bind=engine)) bind=self.engine))
session = Session() self.session = Session()
return True 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()
def dispose(): conn = conn or self.session.connection().connection.connection
global session conn.create_function("title_sort", 1, _title_sort)
old_session = session def dispose(self):
session = None # global session
old_session = self.session
self.session = None
if old_session: if old_session:
try: old_session.close() try: old_session.close()
except: pass except: pass
@ -443,7 +482,7 @@ def dispose():
Base.metadata.remove(table) Base.metadata.remove(table)
def reconnect_db(config, app_db_path): def reconnect_db(self, config, app_db_path):
session.close() self.session.close()
engine.dispose() self.engine.dispose()
setup_db(config, app_db_path) self.setup_db(config, app_db_path)

View File

@ -33,7 +33,8 @@ from flask_login import current_user, login_required
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper 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 .helper import order_authors, common_filters
from .web import login_required_if_no_ano, render_title_template, edit_required, upload_required 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 @login_required
def delete_book(book_id, book_format): def delete_book(book_id, book_format):
if current_user.role_delete_books(): 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: if book:
try: try:
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper()) 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: # check if only this book links to:
# author, language, series, tags, custom columns # author, language, series, tags, custom columns
modify_database_object([u''], book.authors, db.Authors, db.session, 'author') modify_database_object([u''], book.authors, db.Authors, calibre_db.session, 'author')
modify_database_object([u''], book.tags, db.Tags, db.session, 'tags') modify_database_object([u''], book.tags, db.Tags, calibre_db.session, 'tags')
modify_database_object([u''], book.series, db.Series, db.session, 'series') modify_database_object([u''], book.series, db.Series, calibre_db.session, 'series')
modify_database_object([u''], book.languages, db.Languages, db.session, 'languages') modify_database_object([u''], book.languages, db.Languages, calibre_db.session, 'languages')
modify_database_object([u''], book.publishers, db.Publishers, db.session, 'publishers') 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() filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
for c in cc: for c in cc:
cc_string = "custom_column_" + str(c.id) 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] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
log.debug('remove ' + str(c.id)) log.debug('remove ' + str(c.id))
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
db.session.commit() calibre_db.session.commit()
elif c.datatype == 'rating': elif c.datatype == 'rating':
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0: if len(del_cc.books) == 0:
log.debug('remove ' + str(c.id)) log.debug('remove ' + str(c.id))
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
db.session.commit() calibre_db.session.commit()
else: else:
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
log.debug('remove ' + str(c.id)) log.debug('remove ' + str(c.id))
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
db.session.commit() calibre_db.session.commit()
else: else:
modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id], modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id],
db.session, 'custom') calibre_db.session, 'custom')
db.session.query(db.Books).filter(db.Books.id == book_id).delete() calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete()
else: 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() filter(db.Data.format == book_format).delete()
db.session.commit() calibre_db.session.commit()
except Exception as e: except Exception as e:
log.debug(e) log.debug(e)
db.session.rollback() calibre_db.session.rollback()
else: else:
# book not found # book not found
log.error('Book with id "%s" could not be deleted: not found', book_id) 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): def render_edit_book(book_id):
db.update_title_sort(config) calibre_db.update_title_sort(config)
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()
book = db.session.query(db.Books)\ book = calibre_db.session.query(db.Books)\
.filter(db.Books.id == book_id).filter(common_filters()).first() .filter(db.Books.id == book_id).filter(common_filters()).first()
if not book: if not book:
@ -304,7 +305,7 @@ def edit_book_ratings(to_save, book):
ratingx2 = int(float(to_save["rating"]) * 2) ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating: if ratingx2 != old_rating:
changed = True 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: if is_rating:
book.ratings.append(is_rating) book.ratings.append(is_rating)
else: else:
@ -326,7 +327,7 @@ def edit_book_languages(to_save, book):
for l in unknown_languages: for l in unknown_languages:
log.error('%s is not a valid language', l) log.error('%s is not a valid language', l)
flash(_(u"%(langname)s is not a valid language", langname=l), category="error") 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): def edit_book_publisher(to_save, book):
@ -334,15 +335,15 @@ def edit_book_publisher(to_save, book):
if to_save["publisher"]: if to_save["publisher"]:
publisher = to_save["publisher"].rstrip().strip() publisher = to_save["publisher"].rstrip().strip()
if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name): 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): 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 return changed
def edit_cc_data(book_id, book, to_save): def edit_cc_data(book_id, book, to_save):
changed = False 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: for c in cc:
cc_string = "custom_column_" + str(c.id) cc_string = "custom_column_" + str(c.id)
if not c.is_multiple: if not c.is_multiple:
@ -365,12 +366,12 @@ def edit_cc_data(book_id, book, to_save):
else: else:
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
changed = True changed = True
else: else:
cc_class = db.cc_classes[c.id] cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_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 changed = True
else: else:
@ -382,18 +383,18 @@ def edit_cc_data(book_id, book, to_save):
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0: if len(del_cc.books) == 0:
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
changed = True changed = True
cc_class = db.cc_classes[c.id] 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() cc_class.value == to_save[cc_string].strip()).first()
# if no cc val is found add it # if no cc val is found add it
if new_cc is None: if new_cc is None:
new_cc = cc_class(value=to_save[cc_string].strip()) new_cc = cc_class(value=to_save[cc_string].strip())
db.session.add(new_cc) calibre_db.session.add(new_cc)
changed = True changed = True
db.session.flush() calibre_db.session.flush()
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() cc_class.value == to_save[cc_string].strip()).first()
# add cc value to book # add cc value to book
getattr(book, cc_string).append(new_cc) 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] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
if not del_cc.books or len(del_cc.books) == 0: if not del_cc.books or len(del_cc.books) == 0:
db.session.delete(del_cc) calibre_db.session.delete(del_cc)
changed = True changed = True
else: else:
input_tags = to_save[cc_string].split(',') input_tags = to_save[cc_string].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags)) 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') 'custom')
return changed 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)) return redirect(url_for('web.show_book', book_id=book.id))
file_size = os.path.getsize(saved_filename) 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() filter(db.Data.format == file_ext.upper()).first()
# Format entry already exists, no need to update the database # Format entry already exists, no need to update the database
@ -455,11 +456,11 @@ def upload_single_file(request, book, book_id):
else: else:
try: try:
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name) db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
db.session.add(db_format) calibre_db.session.add(db_format)
db.session.commit() calibre_db.session.commit()
db.update_title_sort(config) calibre_db.update_title_sort(config)
except OperationalError as e: except OperationalError as e:
db.session.rollback() calibre_db.session.rollback()
log.error('Database error: %s', e) log.error('Database error: %s', e)
flash(_(u"Database error: %(error)s.", error=e), category="error") flash(_(u"Database error: %(error)s.", error=e), category="error")
return redirect(url_for('web.show_book', book_id=book.id)) 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) return render_edit_book(book_id)
# create the function for sorting... # create the function for sorting...
db.update_title_sort(config) calibre_db.update_title_sort(config)
book = db.session.query(db.Books)\ book = calibre_db.session.query(db.Books)\
.filter(db.Books.id == book_id).filter(common_filters()).first() .filter(db.Books.id == book_id).filter(common_filters()).first()
# Book not found # Book not found
@ -531,13 +532,13 @@ def edit_book(book_id):
if input_authors == ['']: if input_authors == ['']:
input_authors = [_(u'Unknown')] # prevent empty Author 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 # 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 # everything then is assembled for sorted author field in database
sort_authors_list = list() sort_authors_list = list()
for inp in input_authors: 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: if not stored_author:
stored_author = helper.get_sorted_author(inp) stored_author = helper.get_sorted_author(inp)
else: else:
@ -581,17 +582,17 @@ def edit_book(book_id):
# Handle identifiers # Handle identifiers
input_identifiers = identifier_list(to_save, book) 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 # Handle book tags
input_tags = to_save["tags"].split(',') input_tags = to_save["tags"].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags)) 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 # Handle book series
input_series = [to_save["series"].strip()] input_series = [to_save["series"].strip()]
input_series = [x for x in input_series if x != ''] 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"]: if to_save["pubdate"]:
try: try:
@ -615,7 +616,7 @@ def edit_book(book_id):
if modif_date: if modif_date:
book.last_modified = datetime.utcnow() book.last_modified = datetime.utcnow()
db.session.commit() calibre_db.session.commit()
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
if "detail_view" in to_save: if "detail_view" in to_save:
@ -624,12 +625,12 @@ def edit_book(book_id):
flash(_("Metadata successfully updated"), category="success") flash(_("Metadata successfully updated"), category="success")
return render_edit_book(book_id) return render_edit_book(book_id)
else: else:
db.session.rollback() calibre_db.session.rollback()
flash(error, category="error") flash(error, category="error")
return render_edit_book(book_id) return render_edit_book(book_id)
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
db.session.rollback() calibre_db.session.rollback()
flash(_("Error editing book, please check logfile for details"), category="error") flash(_("Error editing book, please check logfile for details"), category="error")
return redirect(url_for('web.show_book', book_id=book.id)) 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"): for requested_file in request.files.getlist("btn-upload"):
try: try:
# create the function for sorting... # create the function for sorting...
db.update_title_sort(config) calibre_db.update_title_sort(config)
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
# check if file extension is correct # check if file extension is correct
if '.' in requested_file.filename: if '.' in requested_file.filename:
@ -708,13 +709,13 @@ def upload():
+ Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning") + Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning")
# handle authors # 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: if is_author:
db_author = is_author db_author = is_author
authr= is_author.name authr= is_author.name
else: else:
db_author = db.Authors(authr, helper.get_sorted_author(authr), "") 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) title_dir = helper.get_valid_filename(title)
author_dir = helper.get_valid_filename(authr) author_dir = helper.get_valid_filename(authr)
@ -746,29 +747,29 @@ def upload():
# handle series # handle series
db_series = None 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: if is_series:
db_series = is_series db_series = is_series
elif series != '': elif series != '':
db_series = db.Series(series, "") db_series = db.Series(series, "")
db.session.add(db_series) calibre_db.session.add(db_series)
# add language actually one value in list # add language actually one value in list
input_language = meta.languages input_language = meta.languages
db_language = None db_language = None
if input_language != "": if input_language != "":
input_language = isoLanguages.get(name=input_language).part3 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: if hasLanguage:
db_language = hasLanguage db_language = hasLanguage
else: else:
db_language = db.Languages(input_language) 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 # 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 # the book it's language is set to the filter language
if db_language != current_user.filter_language() and current_user.filter_language() != "all": 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() filter(db.Languages.lang_code == current_user.filter_language()).first()
# combine path and normalize path from windows systems # combine path and normalize path from windows systems
@ -788,25 +789,25 @@ def upload():
input_tags = tags.split(',') input_tags = tags.split(',')
input_tags = list(map(lambda it: it.strip(), input_tags)) input_tags = list(map(lambda it: it.strip(), input_tags))
if input_tags[0] !="": 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 # flush content, get db_book.id available
db_book.data.append(db_data) db_book.data.append(db_data)
db.session.add(db_book) calibre_db.session.add(db_book)
db.session.flush() calibre_db.session.flush()
# add comment # add comment
book_id = db_book.id book_id = db_book.id
upload_comment = Markup(meta.description).unescape() upload_comment = Markup(meta.description).unescape()
if upload_comment != "": 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 # save data to database, reread data
db.session.commit() calibre_db.session.commit()
db.update_title_sort(config) 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 # 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) # 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 # upload book to gdrive if nesseccary and add "(bookid)" to folder name
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
@ -823,7 +824,7 @@ def upload():
flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath, flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath,
error=e), error=e),
category="error") category="error")
db.session.commit() calibre_db.session.commit()
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
if error: if error:
@ -846,7 +847,7 @@ def upload():
resp = {"location": url_for('web.show_book', book_id=db_book.id)} resp = {"location": url_for('web.show_book', book_id=db_book.id)}
return Response(json.dumps(resp), mimetype='application/json') return Response(json.dumps(resp), mimetype='application/json')
except OperationalError as e: except OperationalError as e:
db.session.rollback() calibre_db.session.rollback()
log.error("Database error: %s", e) log.error("Database error: %s", e)
flash(_(u"Database error: %(error)s.", error=e), category="error") flash(_(u"Database error: %(error)s.", error=e), category="error")
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json') return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')

View File

@ -23,7 +23,6 @@ import os
import io import io
import json import json
import mimetypes import mimetypes
import random
import re import re
import shutil import shutil
import time import time
@ -42,6 +41,7 @@ from flask_login import current_user
from sqlalchemy.sql.expression import true, false, and_, or_, 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 . import calibre_db
try: try:
from urllib.parse import quote from urllib.parse import quote
@ -74,8 +74,8 @@ log = logger.create()
# Convert existing book entry to new format # 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): 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() book = calibre_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() data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == old_book_format).first()
if not data: if not data:
error_message = _(u"%(format)s format not found for book id: %(book)d", format=old_book_format, book=book_id) 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) 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 # 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): def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id):
"""Send email with attachments""" """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: if convert == 1:
# returns None if success, otherwise errormessage # 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): 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) path = os.path.join(calibrepath, localbook.path)
authordir = localbook.path.split('/')[0] 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): def update_dir_structure_gdrive(book_id, first_author):
error = False 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 path = book.path
authordir = book.path.split('/')[0] authordir = book.path.split('/')[0]
@ -494,13 +494,13 @@ def get_cover_on_failure(use_generic_cover):
def get_book_cover(book_id): 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) return get_book_cover_internal(book, use_generic_cover_on_failure=True)
def get_book_cover_with_uuid(book_uuid, def get_book_cover_with_uuid(book_uuid,
use_generic_cover_on_failure=True): 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) 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 # Creates for all stored languages a translated speaking name in the array for the UI
def speaking_language(languages=None): def speaking_language(languages=None):
if not languages: 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())\ .filter(common_filters())\
.group_by(text('books_languages_link.lang_code')).all() .group_by(text('books_languages_link.lang_code')).all()
for lang in languages: for lang in languages:
@ -808,7 +808,7 @@ def order_authors(entry):
error = False error = False
for auth in sort_authors: for auth in sort_authors:
# ToDo: How to handle not found authorname # 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: if not result:
error = True error = True
break 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): def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_show_archived, *join):
if current_user.show_detail_random(): 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) .order_by(func.random()).limit(config.config_random_books)
else: else:
randm = false() randm = false()
off = int(int(config.config_books_per_page) * (page - 1)) 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(db_filter).\
filter(common_filters(allow_show_archived)) filter(common_filters(allow_show_archived))
pagination = Pagination(page, config.config_books_per_page, 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()): def get_typeahead(database, query, replace=('', ''), tag_filter=true()):
query = query or '' query = query or ''
db.session.connection().connection.connection.create_function("lower", 1, lcase) calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase)
entries = db.session.query(database).filter(tag_filter).\ entries = calibre_db.session.query(database).filter(tag_filter).\
filter(func.lower(database.name).ilike("%" + query + "%")).all() filter(func.lower(database.name).ilike("%" + query + "%")).all()
json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries]) json_dumps = json.dumps([dict(name=r.name.replace(*replace)) for r in entries])
return json_dumps 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 # read search results from calibre-database and return it (function is used for feed and simple search
def get_search_results(term): 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() q = list()
authorterms = re.split("[, ]+", term) authorterms = re.split("[, ]+", term)
for authorterm in authorterms: for authorterm in authorterms:
@ -860,7 +860,7 @@ def get_search_results(term):
db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + 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 + "%")), 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.series.any(func.lower(db.Series.name).ilike("%" + term + "%")),
db.Books.authors.any(and_(*q)), db.Books.authors.any(and_(*q)),
@ -870,7 +870,7 @@ def get_search_results(term):
def get_cc_columns(filter_config_custom_read=False): 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 = [] cc = []
r = None r = None
if config.config_columns_to_ignore: 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): def get_download_link(book_id, book_format, client):
book_format = book_format.split(".")[0] 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: 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() .filter(db.Data.format == book_format.upper()).first()
else: else:
abort(404) abort(404)
@ -911,13 +911,13 @@ def get_download_link(book_id, book_format, client):
def check_exists_book(authr, title): 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() q = list()
authorterms = re.split(r'\s*&\s*', authr) authorterms = re.split(r'\s*&\s*', authr)
for authorterm in authorterms: for authorterm in authorterms:
q.append(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + authorterm + "%"))) 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)), and_(db.Books.authors.any(and_(*q)),
func.lower(db.Books.title).ilike("%" + title + "%") func.lower(db.Books.title).ilike("%" + title + "%")
)).first() )).first()

View File

@ -48,7 +48,7 @@ from sqlalchemy.sql.expression import and_, or_
from sqlalchemy.exc import StatementError from sqlalchemy.exc import StatementError
import requests 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 .services import SyncToken as SyncToken
from .web import download_required from .web import download_required
from .kobo_auth import requires_kobo_auth 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 # 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. # the comparison because of the +00:00 suffix.
changed_entries = ( changed_entries = (
db.session.query(db.Books) calibre_db.session.query(db.Books)
.join(db.Data) .join(db.Data)
.filter(or_(func.datetime(db.Books.last_modified) > sync_token.books_last_modified, .filter(or_(func.datetime(db.Books.last_modified) > sync_token.books_last_modified,
db.Books.id.in_(recently_restored_or_archived_books))) db.Books.id.in_(recently_restored_or_archived_books)))
@ -207,7 +207,7 @@ def HandleSyncRequest():
ub.KoboReadingState.user_id == current_user.id, ub.KoboReadingState.user_id == current_user.id,
ub.KoboReadingState.book_id.notin_(reading_states_in_new_entitlements)))) ub.KoboReadingState.book_id.notin_(reading_states_in_new_entitlements))))
for kobo_reading_state in changed_reading_states.all(): 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: if book:
sync_results.append({ sync_results.append({
"ChangedReadingState": { "ChangedReadingState": {
@ -256,7 +256,7 @@ def HandleMetadataRequest(book_uuid):
if not current_app.wsgi_app.is_proxied: if not current_app.wsgi_app.is_proxied:
log.debug('Kobo: Received unproxied request, changed request port to server port') log.debug('Kobo: Received unproxied request, changed request port to server port')
log.info("Kobo library metadata request received for book %s" % book_uuid) 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: if not book or not book.data:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()
@ -474,7 +474,7 @@ def add_items_to_shelf(items, shelf):
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue 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: if not book:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
@ -545,7 +545,7 @@ def HandleTagRemoveItem(tag_id):
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue 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: if not book:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
@ -613,7 +613,7 @@ def create_kobo_tag(shelf):
"Type": "UserTag" "Type": "UserTag"
} }
for book_shelf in shelf.books: 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: if not book:
log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id) log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id)
continue continue
@ -629,7 +629,7 @@ def create_kobo_tag(shelf):
@kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"]) @kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"])
@login_required @login_required
def HandleStateRequest(book_uuid): 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: if not book or not book.data:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()
@ -804,7 +804,7 @@ def TopLevelEndpoint():
@login_required @login_required
def HandleBookDeletionRequest(book_uuid): def HandleBookDeletionRequest(book_uuid):
log.info("Kobo book deletion request received for book %s" % 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: if not book:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()

View File

@ -30,7 +30,7 @@ from flask_login import current_user
from sqlalchemy.sql.expression import func, text, or_, and_ from sqlalchemy.sql.expression import func, text, or_, and_
from werkzeug.security import check_password_hash 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 .helper import fill_indexpage, get_download_link, get_book_cover, speaking_language
from .pagination import Pagination from .pagination import Pagination
from .web import common_filters, get_search_results, render_read_books, download_required from .web import common_filters, get_search_results, render_read_books, download_required
@ -108,7 +108,7 @@ def feed_new():
@opds.route("/opds/discover") @opds.route("/opds/discover")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_discover(): 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) .limit(config.config_books_per_page)
pagination = Pagination(1, config.config_books_per_page, int(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) 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) hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list() entries = list()
for book in hot_books: 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: if downloadBook:
entries.append( 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() .filter(db.Books.id == book.Downloads.book_id).first()
) )
else: else:
@ -153,11 +153,11 @@ def feed_hot():
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_authorindex(): def feed_authorindex():
off = request.args.get("offset") or 0 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)\ .group_by(text('books_authors_link.author')).order_by(db.Authors.sort).limit(config.config_books_per_page)\
.offset(off) .offset(off)
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), 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.Authors).all())) len(calibre_db.session.query(db.Authors).all()))
return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_author', pagination=pagination) 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 @requires_basic_auth_if_no_ano
def feed_publisherindex(): def feed_publisherindex():
off = request.args.get("offset") or 0 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)\ .group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.sort)\
.limit(config.config_books_per_page).offset(off) .limit(config.config_books_per_page).offset(off)
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), 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.Publishers).all())) len(calibre_db.session.query(db.Publishers).all()))
return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_publisher', pagination=pagination) 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 @requires_basic_auth_if_no_ano
def feed_categoryindex(): def feed_categoryindex():
off = request.args.get("offset") or 0 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) .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, 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) 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 @requires_basic_auth_if_no_ano
def feed_seriesindex(): def feed_seriesindex():
off = request.args.get("offset") or 0 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() .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, 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) 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 @requires_basic_auth_if_no_ano
def feed_ratingindex(): def feed_ratingindex():
off = request.args.get("offset") or 0 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')) \ (db.Ratings.rating / 2).label('name')) \
.join(db.books_ratings_link).join(db.Books).filter(common_filters()) \ .join(db.books_ratings_link).join(db.Books).filter(common_filters()) \
.group_by(text('books_ratings_link.rating')).order_by(db.Ratings.rating).all() .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 @requires_basic_auth_if_no_ano
def feed_formatindex(): def feed_formatindex():
off = request.args.get("offset") or 0 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() .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, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(entries)) len(entries))
@ -305,7 +305,7 @@ def feed_languagesindex():
cur_l = LC.parse(current_user.filter_language()) cur_l = LC.parse(current_user.filter_language())
except UnknownLocaleError: except UnknownLocaleError:
cur_l = None 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() db.Languages.lang_code == current_user.filter_language()).all()
if cur_l: if cur_l:
languages[0].name = cur_l.get_language_name(get_locale()) 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( books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == book_id).order_by(
ub.BookShelf.order.asc()).all() ub.BookShelf.order.asc()).all()
for book in books_in_shelf: 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) result.append(cur_book)
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(result)) len(result))
@ -378,7 +378,7 @@ def opds_download_link(book_id, book_format):
@opds.route("/ajax/book/<string:uuid>", defaults={'library': ""}) @opds.route("/ajax/book/<string:uuid>", defaults={'library': ""})
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def get_metadata_calibre_companion(uuid, library): 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: if entry is not None:
js = render_template('json.txt', entry=entry) js = render_template('json.txt', entry=entry)
response = make_response(js) response = make_response(js)

View File

@ -196,6 +196,9 @@ class WebServer(object):
def stop(self, restart=False): def stop(self, restart=False):
from . import updater_thread from . import updater_thread
updater_thread.stop() updater_thread.stop()
from . import calibre_db
calibre_db.stop()
log.info("webserver stop (restart=%s)", restart) log.info("webserver stop (restart=%s)", restart)
self.restart = restart self.restart = restart

View File

@ -28,7 +28,7 @@ from flask_babel import gettext as _
from flask_login import login_required, current_user from flask_login import login_required, current_user
from sqlalchemy.sql.expression import func 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 .web import render_title_template
from .helper import common_filters 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)\ books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\
.order_by(ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf: 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: if cur_book:
result.append(cur_book) result.append(cur_book)
else: 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: if not cur_book:
log.info('Not existing book %s in %s deleted', book.book_id, shelf) 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() 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) \ books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \
.order_by(ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf2: 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: if cur_book:
result.append({'title': cur_book.title, result.append({'title': cur_book.title,
'id': cur_book.id, 'id': cur_book.id,
@ -364,7 +364,7 @@ def order_shelf(shelf_id):
'series': cur_book.series, 'series': cur_book.series,
'series_index': cur_book.series_index}) 'series_index': cur_book.series_index})
else: 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'), result.append({'title': _('Hidden Book'),
'id': cur_book.id, 'id': cur_book.id,
'author': [], 'author': [],

View File

@ -50,6 +50,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
from . import constants, logger, isoLanguages, services, worker from . import constants, logger, isoLanguages, services, worker
from . import searched_ids, lm, babel, db, ub, config, get_locale, app from . import searched_ids, lm, babel, db, ub, config, get_locale, app
from . import calibre_db
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
from .helper import common_filters, get_search_results, fill_indexpage, fill_indexpage_with_archived_books, \ 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, \ 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() ub.session.commit()
else: else:
try: try:
db.update_title_sort(config) calibre_db.update_title_sort(config)
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()
read_status = getattr(book, 'custom_column_' + str(config.config_read_column)) read_status = getattr(book, 'custom_column_' + str(config.config_read_column))
if len(read_status): if len(read_status):
read_status[0].value = not read_status[0].value read_status[0].value = not read_status[0].value
db.session.commit() calibre_db.session.commit()
else: else:
cc_class = db.cc_classes[config.config_read_column] cc_class = db.cc_classes[config.config_read_column]
new_cc = cc_class(value=1, book=book_id) new_cc = cc_class(value=1, book=book_id)
db.session.add(new_cc) calibre_db.session.add(new_cc)
db.session.commit() calibre_db.session.commit()
except KeyError: except KeyError:
log.error(u"Custom Column No.%d is not exisiting in calibre database", config.config_read_column) log.error(u"Custom Column No.%d is not exisiting in calibre database", config.config_read_column)
except OperationalError as e: except OperationalError as e:
db.session.rollback() calibre_db.session.rollback()
log.error(u"Read status could not set: %e", e) log.error(u"Read status could not set: %e", e)
return "" return ""
@ -496,7 +497,7 @@ def update_view():
@web.route("/ajax/getcomic/<int:book_id>/<book_format>/<int:page>") @web.route("/ajax/getcomic/<int:book_id>/<book_format>/<int:page>")
@login_required @login_required
def get_comic_book(book_id, book_format, page): 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: if not book:
return "", 204 return "", 204
else: else:
@ -589,8 +590,8 @@ def get_languages_json():
@login_required_if_no_ano @login_required_if_no_ano
def get_matching_tags(): def get_matching_tags():
tag_dict = {'tags': []} tag_dict = {'tags': []}
q = db.session.query(db.Books) q = calibre_db.session.query(db.Books)
db.session.connection().connection.connection.create_function("lower", 1, lcase) calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase)
author_input = request.args.get('author_name') or '' author_input = request.args.get('author_name') or ''
title_input = request.args.get('book_title') or '' title_input = request.args.get('book_title') or ''
include_tag_inputs = request.args.getlist('include_tag') 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): def render_hot_books(page):
if current_user.check_visibility(constants.SIDEBAR_HOT): if current_user.check_visibility(constants.SIDEBAR_HOT):
if current_user.show_detail_random(): 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) .order_by(func.random()).limit(config.config_random_books)
else: else:
random = false() random = false()
@ -702,7 +703,7 @@ def render_hot_books(page):
hot_books = all_books.offset(off).limit(config.config_books_per_page) hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list() entries = list()
for book in hot_books: 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() db.Books.id == book.Downloads.book_id).first()
if downloadBook: if downloadBook:
entries.append(downloadBook) entries.append(downloadBook)
@ -727,7 +728,7 @@ def render_author_books(page, author_id, order):
category="error") category="error")
return redirect(url_for("web.index")) 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_name = author.name.replace('|', ',')
author_info = None author_info = None
@ -742,7 +743,7 @@ def render_author_books(page, author_id, order):
def render_publisher_books(page, book_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: if publisher:
entries, random, pagination = fill_indexpage(page, db.Books, entries, random, pagination = fill_indexpage(page, db.Books,
db.Books.publishers.any(db.Publishers.id == book_id), 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): 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: if name:
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.id == book_id), entries, random, pagination = fill_indexpage(page, db.Books, db.Books.series.any(db.Series.id == book_id),
[db.Books.series_index, order[0]]) [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): 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), entries, random, pagination = fill_indexpage(page, db.Books, db.Books.ratings.any(db.Ratings.id == book_id),
[db.Books.timestamp.desc(), order[0]]) [db.Books.timestamp.desc(), order[0]])
if name and name.rating <= 10: 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): 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: if name:
entries, random, pagination = fill_indexpage(page, db.Books, entries, random, pagination = fill_indexpage(page, db.Books,
db.Books.data.any(db.Data.format == book_id.upper()), 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): 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: if name:
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id), 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], [order[0], db.Series.name, db.Books.series_index],
@ -825,10 +826,10 @@ def books_table():
@login_required_if_no_ano @login_required_if_no_ano
def author_list(): def author_list():
if current_user.check_visibility(constants.SIDEBAR_AUTHOR): 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()) \ .join(db.books_authors_link).join(db.Books).filter(common_filters()) \
.group_by(text('books_authors_link.author')).order_by(db.Authors.sort).all() .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()) \ .join(db.books_authors_link).join(db.Books).filter(common_filters()) \
.group_by(func.upper(func.substr(db.Authors.sort, 1, 1))).all() .group_by(func.upper(func.substr(db.Authors.sort, 1, 1))).all()
for entry in entries: for entry in entries:
@ -843,10 +844,10 @@ def author_list():
@login_required_if_no_ano @login_required_if_no_ano
def publisher_list(): def publisher_list():
if current_user.check_visibility(constants.SIDEBAR_PUBLISHER): 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()) \ .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \
.group_by(text('books_publishers_link.publisher')).order_by(db.Publishers.name).all() .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()) \ .join(db.books_publishers_link).join(db.Books).filter(common_filters()) \
.group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all() .group_by(func.upper(func.substr(db.Publishers.name, 1, 1))).all()
return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
@ -860,19 +861,19 @@ def publisher_list():
def series_list(): def series_list():
if current_user.check_visibility(constants.SIDEBAR_SERIES): if current_user.check_visibility(constants.SIDEBAR_SERIES):
if current_user.series_view == 'list': 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()) \ .join(db.books_series_link).join(db.Books).filter(common_filters()) \
.group_by(text('books_series_link.series')).order_by(db.Series.sort).all() .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()) \ .join(db.books_series_link).join(db.Books).filter(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()
return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist, return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
title=_(u"Series"), page="serieslist", data="series") title=_(u"Series"), page="serieslist", data="series")
else: 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()) \ .join(db.books_series_link).join(db.Series).filter(common_filters()) \
.group_by(text('books_series_link.series')).order_by(db.Series.sort).all() .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()) \ .join(db.books_series_link).join(db.Books).filter(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()
@ -886,9 +887,9 @@ def series_list():
@login_required_if_no_ano @login_required_if_no_ano
def ratings_list(): def ratings_list():
if current_user.check_visibility(constants.SIDEBAR_RATING): 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')) \ (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() .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(), return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=list(),
title=_(u"Ratings list"), page="ratingslist", data="ratings") title=_(u"Ratings list"), page="ratingslist", data="ratings")
@ -900,7 +901,7 @@ def ratings_list():
@login_required_if_no_ano @login_required_if_no_ano
def formats_list(): def formats_list():
if current_user.check_visibility(constants.SIDEBAR_FORMAT): 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()) \ .join(db.Books).filter(common_filters()) \
.group_by(db.Data.format).order_by(db.Data.format).all() .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(), 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()) cur_l = LC.parse(current_user.filter_language())
except UnknownLocaleError: except UnknownLocaleError:
cur_l = None 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() db.Languages.lang_code == current_user.filter_language()).all()
if cur_l: if cur_l:
languages[0].name = cur_l.get_language_name(get_locale()) languages[0].name = cur_l.get_language_name(get_locale())
else: else:
languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name) 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( func.count('books_languages_link.book').label('bookcount')).group_by(
text('books_languages_link.lang_code')).all() text('books_languages_link.lang_code')).all()
return render_title_template('languages.html', languages=languages, lang_counter=lang_counter, return render_title_template('languages.html', languages=languages, lang_counter=lang_counter,
@ -942,10 +943,10 @@ def language_overview():
@login_required_if_no_ano @login_required_if_no_ano
def category_list(): def category_list():
if current_user.check_visibility(constants.SIDEBAR_CATEGORY): 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()) \ .join(db.books_tags_link).join(db.Books).order_by(db.Tags.name).filter(common_filters()) \
.group_by(text('books_tags_link.tag')).all() .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()) \ .join(db.books_tags_link).join(db.Books).filter(common_filters()) \
.group_by(func.upper(func.substr(db.Tags.name, 1, 1))).all() .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, return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
@ -1003,8 +1004,8 @@ def search():
def advanced_search(): def advanced_search():
# Build custom columns names # Build custom columns names
cc = get_cc_columns() cc = get_cc_columns()
db.session.connection().connection.connection.create_function("lower", 1, lcase) calibre_db.session.connection().connection.connection.create_function("lower", 1, lcase)
q = db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort) q = calibre_db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort)
include_tag_inputs = request.args.getlist('include_tag') include_tag_inputs = request.args.getlist('include_tag')
exclude_tag_inputs = request.args.getlist('exclude_tag') exclude_tag_inputs = request.args.getlist('exclude_tag')
@ -1057,11 +1058,11 @@ def advanced_search():
format='medium', locale=get_locale())]) format='medium', locale=get_locale())])
except ValueError: except ValueError:
pub_start = u"" 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) 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) 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: if language_names:
language_names = speaking_language(language_names) language_names = speaking_language(language_names)
searchterm.extend(language.name for language in 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, return render_title_template('search.html', adv_searchterm=searchterm,
entries=q, title=_(u"search"), page="search") entries=q, title=_(u"search"), page="search")
# prepare data for search-form # 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() .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() .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() .group_by(db.Data.format).order_by(db.Data.format).all()
if current_user.filter_language() == u"all": if current_user.filter_language() == u"all":
@ -1229,8 +1230,8 @@ def get_cover(book_id):
@viewer_required @viewer_required
def serve_book(book_id, book_format, anyname): def serve_book(book_id, book_format, anyname):
book_format = book_format.split(".")[0] book_format = book_format.split(".")[0]
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()
data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()) \ data = calibre_db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()) \
.first() .first()
log.info('Serving book: %s', data.name) log.info('Serving book: %s', data.name)
if config.config_use_google_drive: if config.config_use_google_drive:
@ -1522,9 +1523,9 @@ def profile():
oauth_status = None oauth_status = None
for book in current_user.downloads: 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: 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: else:
ub.delete_download(book.book_id) ub.delete_download(book.book_id)
if request.method == "POST": if request.method == "POST":
@ -1603,7 +1604,7 @@ def profile():
@login_required_if_no_ano @login_required_if_no_ano
@viewer_required @viewer_required
def read_book(book_id, book_format): 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: if not book:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
log.debug(u"Error opening eBook. File does not exist or file is not accessible:") log.debug(u"Error opening eBook. File does not exist or file is not accessible:")
@ -1627,7 +1628,7 @@ def read_book(book_id, book_format):
else: else:
for fileExt in constants.EXTENSIONS_AUDIO: for fileExt in constants.EXTENSIONS_AUDIO:
if book_format.lower() == fileExt: 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) log.debug(u"Start mp3 listening for %d", book_id)
return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(), return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(),
title=_(u"Read a Book"), entry=entries, bookmark=bookmark) title=_(u"Read a Book"), entry=entries, bookmark=bookmark)
@ -1653,7 +1654,7 @@ def read_book(book_id, book_format):
@web.route("/book/<int:book_id>") @web.route("/book/<int:book_id>")
@login_required_if_no_ano @login_required_if_no_ano
def show_book(book_id): 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() common_filters(allow_show_archived=True))).first()
if entries: if entries:
for index in range(0, len(entries.languages)): for index in range(0, len(entries.languages)):

View File

@ -24,10 +24,10 @@ import smtplib
import socket import socket
import time import time
import threading import threading
import queue
from glob import glob from glob import glob
from shutil import copyfile from shutil import copyfile
from datetime import datetime from datetime import datetime
from sqlalchemy.exc import OperationalError
try: try:
from StringIO import StringIO from StringIO import StringIO
@ -46,7 +46,8 @@ from email.utils import make_msgid
from email.generator import Generator from email.generator import Generator
from flask_babel import gettext as _ 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 .subproc_wrapper import process_open
from . import gdriveutils from . import gdriveutils
@ -190,6 +191,8 @@ class WorkerThread(threading.Thread):
self.UIqueue = list() self.UIqueue = list()
self.asyncSMTP = None self.asyncSMTP = None
self.id = 0 self.id = 0
self.db_queue = queue.Queue()
calibre_db.add_queue(self.db_queue)
self.doLock = threading.Lock() self.doLock = threading.Lock()
# Main thread loop starting the different tasks # Main thread loop starting the different tasks
@ -275,6 +278,18 @@ class WorkerThread(threading.Thread):
self.doLock.acquire() self.doLock.acquire()
index = self.current index = self.current
self.doLock.release() 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'] file_path = self.queue[index]['file_path']
bookid = self.queue[index]['bookid'] bookid = self.queue[index]['bookid']
format_old_ext = u'.' + self.queue[index]['settings']['old_book_format'].lower() 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 # this will allow send to kindle workflow to continue to work
if os.path.isfile(file_path + format_new_ext): if os.path.isfile(file_path + format_new_ext):
log.info("Book id %d already converted to %s", bookid, 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]['path'] = file_path
self.queue[index]['title'] = cur_book.title self.queue[index]['title'] = cur_book.title
self._handleSuccess() 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) check, error_message = self._convert_calibre(file_path, format_old_ext, format_new_ext, index)
if check == 0: 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): if os.path.isfile(file_path + format_new_ext):
# self.db_queue.join()
new_format = db.Data(name=cur_book.data[0].name, new_format = db.Data(name=cur_book.data[0].name,
book_format=self.queue[index]['settings']['new_book_format'].upper(), book_format=self.queue[index]['settings']['new_book_format'].upper(),
book=bookid, uncompressed_size=os.path.getsize(file_path + format_new_ext)) 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: try:
db.session.commit() # db.session.merge(cur_book)
calibre_db.session.commit()
except OperationalError as e: except OperationalError as e:
db.session.rollback() calibre_db.session.rollback()
log.error("Database error: %s", e) log.error("Database error: %s", e)
self._handleError(_(u"Database error: %(error)s.", error=e)) self._handleError(_(u"Database error: %(error)s.", error=e))
return return'''
self.queue[index]['path'] = cur_book.path self.queue[index]['path'] = cur_book.path
self.queue[index]['title'] = cur_book.title self.queue[index]['title'] = cur_book.title
@ -375,6 +397,7 @@ class WorkerThread(threading.Thread):
# process returncode # process returncode
check = p.returncode check = p.returncode
calibre_traceback = p.stderr.readlines() calibre_traceback = p.stderr.readlines()
error_message = ""
for ele in calibre_traceback: for ele in calibre_traceback:
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
ele = ele.decode('utf-8') ele = ele.decode('utf-8')