1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-06-13 17:06:50 +00:00

refactored about

Update dependencies in setup.cfg
Improved detection of library change
This commit is contained in:
Ozzie Isaacs 2022-02-12 12:19:21 +01:00
commit 86b621e768
14 changed files with 1511 additions and 348 deletions

View File

@ -27,8 +27,9 @@ from flask import session
class MyLoginManager(LoginManager): class MyLoginManager(LoginManager):
def _session_protection_failed(self): def _session_protection_failed(self):
sess = session._get_current_object() _session = session._get_current_object()
ident = self._session_identifier_generator() ident = self._session_identifier_generator()
if(sess and not (len(sess) == 1 and sess.get('csrf_token', None))) and ident != sess.get('_id', None): if(_session and not (len(_session) == 1
and _session.get('csrf_token', None))) and ident != _session.get('_id', None):
return super(). _session_protection_failed() return super(). _session_protection_failed()
return False return False

View File

@ -156,7 +156,7 @@ def create_app():
services.goodreads_support.connect(config.config_goodreads_api_key, services.goodreads_support.connect(config.config_goodreads_api_key,
config.config_goodreads_api_secret, config.config_goodreads_api_secret,
config.config_use_goodreads) config.config_use_goodreads)
config.store_calibre_uuid(calibre_db, db.Library_Id)
return app return app
@babel.localeselector @babel.localeselector

View File

@ -25,47 +25,15 @@ import platform
import sqlite3 import sqlite3
from collections import OrderedDict from collections import OrderedDict
import babel
import pytz
import requests
import sqlalchemy
import werkzeug import werkzeug
import flask import flask
import flask_login import flask_login
import flask_principal
import jinja2 import jinja2
from flask_babel import gettext as _ from flask_babel import gettext as _
try:
from flask_wtf import __version__ as flaskwtf_version
except ImportError:
flaskwtf_version = _(u'not installed')
from . import db, calibre_db, converter, uploader, server, isoLanguages, constants, gdriveutils, dep_check from . import db, calibre_db, converter, uploader, constants, dep_check
from .render_template import render_title_template from .render_template import render_title_template
try:
from flask_login import __version__ as flask_loginVersion
except ImportError:
from flask_login.__about__ import __version__ as flask_loginVersion
try:
# pylint: disable=unused-import
import unidecode
# _() necessary to make babel aware of string for translation
unidecode_version = _(u'installed')
except ImportError:
unidecode_version = _(u'not installed')
try:
from flask_dance import __version__ as flask_danceVersion
except ImportError:
flask_danceVersion = None
try:
from greenlet import __version__ as greenlet_Version
except ImportError:
greenlet_Version = None
from . import services
about = flask.Blueprint('about', __name__) about = flask.Blueprint('about', __name__)
@ -81,52 +49,23 @@ else:
calibre_web_version = (constants.STABLE_VERSION['version'] + ' - ' calibre_web_version = (constants.STABLE_VERSION['version'] + ' - '
+ constants.NIGHTLY_VERSION[0].replace('%', '%%') + ' - ' + constants.NIGHTLY_VERSION[0].replace('%', '%%') + ' - '
+ constants.NIGHTLY_VERSION[1].replace('%', '%%')) + constants.NIGHTLY_VERSION[1].replace('%', '%%'))
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
calibre_web_version += " - Exe-Version" calibre_web_version += " - Exe-Version"
elif constants.HOME_CONFIG: elif constants.HOME_CONFIG:
calibre_web_version += " - pyPi" calibre_web_version += " - pyPi"
if not ret: _VERSIONS = OrderedDict(
_VERSIONS = OrderedDict( Platform='{0[0]} {0[2]} {0[3]} {0[4]} {0[5]}'.format(platform.uname()),
Platform='{0[0]} {0[2]} {0[3]} {0[4]} {0[5]}'.format(platform.uname()), Python=sys.version,
Python=sys.version, Calibre_Web=calibre_web_version,
Calibre_Web=calibre_web_version, Werkzeug=werkzeug.__version__,
WebServer=server.VERSION, Jinja2=jinja2.__version__,
Flask=flask.__version__, pySqlite=sqlite3.version,
Flask_Login=flask_loginVersion, SQLite=sqlite3.sqlite_version,
Flask_Principal=flask_principal.__version__, )
Flask_WTF=flaskwtf_version, _VERSIONS.update(ret)
Werkzeug=werkzeug.__version__, _VERSIONS.update(uploader.get_versions(False))
Babel=babel.__version__,
Jinja2=jinja2.__version__,
Requests=requests.__version__,
SqlAlchemy=sqlalchemy.__version__,
pySqlite=sqlite3.version,
SQLite=sqlite3.sqlite_version,
iso639=isoLanguages.__version__,
pytz=pytz.__version__,
Unidecode=unidecode_version,
Flask_SimpleLDAP=u'installed' if bool(services.ldap) else None,
python_LDAP=services.ldapVersion if bool(services.ldapVersion) else None,
Goodreads=u'installed' if bool(services.goodreads_support) else None,
jsonschema=services.SyncToken.__version__ if bool(services.SyncToken) else None,
flask_dance=flask_danceVersion,
greenlet=greenlet_Version
)
_VERSIONS.update(gdriveutils.get_versions())
_VERSIONS.update(uploader.get_versions(True))
else:
_VERSIONS = OrderedDict(
Platform='{0[0]} {0[2]} {0[3]} {0[4]} {0[5]}'.format(platform.uname()),
Python=sys.version,
Calibre_Web=calibre_web_version,
Werkzeug=werkzeug.__version__,
Jinja2=jinja2.__version__,
pySqlite=sqlite3.version,
SQLite=sqlite3.sqlite_version,
)
_VERSIONS.update(ret)
_VERSIONS.update(uploader.get_versions(False))
def collect_stats(): def collect_stats():

View File

@ -1192,8 +1192,10 @@ def _db_simulate_change():
'', '',
param['config_calibre_dir'], param['config_calibre_dir'],
flags=re.IGNORECASE).strip() flags=re.IGNORECASE).strip()
db_change = config.config_calibre_dir != to_save["config_calibre_dir"] and config.config_calibre_dir db_valid, db_change = calibre_db.check_valid_db(to_save["config_calibre_dir"],
db_valid = calibre_db.check_valid_db(to_save["config_calibre_dir"], ub.app_DB_path) ub.app_DB_path,
config.config_calibre_uuid)
db_change = bool(db_change and config.config_calibre_dir)
return db_change, db_valid return db_change, db_valid
@ -1223,12 +1225,15 @@ def _db_configuration_update_helper():
except Exception as ex: except Exception as ex:
return _db_configuration_result('{}'.format(ex), gdrive_error) return _db_configuration_result('{}'.format(ex), gdrive_error)
if db_change or not db_valid or not config.db_configured: if db_change or not db_valid or not config.db_configured \
or config.config_calibre_dir != to_save["config_calibre_dir"]:
if not calibre_db.setup_db(to_save['config_calibre_dir'], ub.app_DB_path): if not calibre_db.setup_db(to_save['config_calibre_dir'], ub.app_DB_path):
return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'),
gdrive_error) gdrive_error)
config.store_calibre_uuid(calibre_db, db.Library_Id)
# if db changed -> delete shelfs, delete download books, delete read books, kobo sync... # if db changed -> delete shelfs, delete download books, delete read books, kobo sync...
if db_change: if db_change:
log.info("Calibre Database changed, delete all Calibre-Web info related to old Database")
ub.session.query(ub.Downloads).delete() ub.session.query(ub.Downloads).delete()
ub.session.query(ub.ArchivedBook).delete() ub.session.query(ub.ArchivedBook).delete()
ub.session.query(ub.ReadBook).delete() ub.session.query(ub.ReadBook).delete()

View File

@ -62,6 +62,7 @@ class _Settings(_Base):
mail_gmail_token = Column(JSON, default={}) mail_gmail_token = Column(JSON, default={})
config_calibre_dir = Column(String) config_calibre_dir = Column(String)
config_calibre_uuid = Column(String)
config_port = Column(Integer, default=constants.DEFAULT_PORT) config_port = Column(Integer, default=constants.DEFAULT_PORT)
config_external_port = Column(Integer, default=constants.DEFAULT_PORT) config_external_port = Column(Integer, default=constants.DEFAULT_PORT)
config_certfile = Column(String) config_certfile = Column(String)
@ -350,6 +351,14 @@ class _ConfigSQL(object):
# self.config_calibre_dir = None # self.config_calibre_dir = None
self.save() self.save()
def store_calibre_uuid(self, calibre_db, Library_table):
try:
calibre_uuid = calibre_db.session.query(Library_table).one_or_none()
if self.config_calibre_uuid != calibre_uuid.uuid:
self.config_calibre_uuid = calibre_uuid.uuid
self.save()
except AttributeError:
pass
def _migrate_table(session, orm_class): def _migrate_table(session, orm_class):
changed = False changed = False
@ -438,19 +447,12 @@ def load_configuration(session):
session.add(_Settings()) session.add(_Settings())
session.commit() session.commit()
conf = _ConfigSQL(session) conf = _ConfigSQL(session)
# Migrate from global restrictions to user based restrictions
#if bool(conf.config_default_show & constants.MATURE_CONTENT) and conf.config_denied_tags == "":
# conf.config_denied_tags = conf.config_mature_content_tags
# conf.save()
# session.query(ub.User).filter(ub.User.mature_content != True). \
# update({"denied_tags": conf.config_mature_content_tags}, synchronize_session=False)
# session.commit()
return conf return conf
def get_flask_session_key(session): def get_flask_session_key(_session):
flask_settings = session.query(_Flask_Settings).one_or_none() flask_settings = _session.query(_Flask_Settings).one_or_none()
if flask_settings == None: if flask_settings == None:
flask_settings = _Flask_Settings(os.urandom(32)) flask_settings = _Flask_Settings(os.urandom(32))
session.add(flask_settings) _session.add(flask_settings)
session.commit() _session.commit()
return flask_settings.flask_session_key return flask_settings.flask_session_key

View File

@ -93,6 +93,12 @@ books_publishers_link = Table('books_publishers_link', Base.metadata,
) )
class Library_Id(Base):
__tablename__ = 'library_id'
id = Column(Integer, primary_key=True)
uuid = Column(String, nullable=False)
class Identifiers(Base): class Identifiers(Base):
__tablename__ = 'identifiers' __tablename__ = 'identifiers'
@ -525,12 +531,12 @@ class CalibreDB():
return cc_classes return cc_classes
@classmethod @classmethod
def check_valid_db(cls, config_calibre_dir, app_db_path): def check_valid_db(cls, config_calibre_dir, app_db_path, config_calibre_uuid):
if not config_calibre_dir: if not config_calibre_dir:
return False return False, False
dbpath = os.path.join(config_calibre_dir, "metadata.db") dbpath = os.path.join(config_calibre_dir, "metadata.db")
if not os.path.exists(dbpath): if not os.path.exists(dbpath):
return False return False, False
try: try:
check_engine = create_engine('sqlite://', check_engine = create_engine('sqlite://',
echo=False, echo=False,
@ -540,10 +546,16 @@ class CalibreDB():
with check_engine.begin() as connection: with check_engine.begin() as connection:
connection.execute(text("attach database '{}' as calibre;".format(dbpath))) connection.execute(text("attach database '{}' as calibre;".format(dbpath)))
connection.execute(text("attach database '{}' as app_settings;".format(app_db_path))) connection.execute(text("attach database '{}' as app_settings;".format(app_db_path)))
local_session = scoped_session(sessionmaker())
local_session.configure(bind=connection)
database_uuid = local_session().query(Library_Id).one_or_none()
# local_session.dispose()
check_engine.connect() check_engine.connect()
db_change = config_calibre_uuid != database_uuid.uuid
except Exception: except Exception:
return False return False, False
return True return True, db_change
@classmethod @classmethod
def update_config(cls, config): def update_config(cls, config):

View File

@ -1,5 +1,7 @@
import os import os
import re import re
import sys
import json
from .constants import BASE_DIR from .constants import BASE_DIR
try: try:
@ -8,7 +10,7 @@ try:
ImportNotFound = BaseException ImportNotFound = BaseException
except ImportError: except ImportError:
importlib = False importlib = False
version = None
if not importlib: if not importlib:
try: try:
@ -20,6 +22,13 @@ if not importlib:
def load_dependencys(optional=False): def load_dependencys(optional=False):
deps = list() deps = list()
if getattr(sys, 'frozen', False):
pip_installed = os.path.join(BASE_DIR, ".pip_installed")
if os.path.exists(pip_installed):
with open(pip_installed) as f:
exe_deps = json.loads("".join(f.readlines()))
else:
return deps
if importlib or pkgresources: if importlib or pkgresources:
if optional: if optional:
req_path = os.path.join(BASE_DIR, "optional-requirements.txt") req_path = os.path.join(BASE_DIR, "optional-requirements.txt")
@ -31,11 +40,14 @@ def load_dependencys(optional=False):
if not line.startswith('#') and not line == '\n' and not line.startswith('git'): if not line.startswith('#') and not line == '\n' and not line.startswith('git'):
res = re.match(r'(.*?)([<=>\s]+)([\d\.]+),?\s?([<=>\s]+)?([\d\.]+)?', line.strip()) res = re.match(r'(.*?)([<=>\s]+)([\d\.]+),?\s?([<=>\s]+)?([\d\.]+)?', line.strip())
try: try:
if importlib: if getattr(sys, 'frozen', False):
dep_version = version(res.group(1)) dep_version = exe_deps[res.group(1).lower().replace('_','-')]
else: else:
dep_version = pkg_resources.get_distribution(res.group(1)).version if importlib:
except ImportNotFound: dep_version = version(res.group(1))
else:
dep_version = pkg_resources.get_distribution(res.group(1)).version
except (ImportNotFound, KeyError):
if optional: if optional:
continue continue
dep_version = "not installed" dep_version = "not installed"

View File

@ -47,7 +47,7 @@ def remove_synced_book(book_id, all=False, session=None):
ub.session_commit() ub.session_commit()
else: else:
session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.book_id == book_id).filter(user).delete() session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.book_id == book_id).filter(user).delete()
ub.session_commit(sess=session) ub.session_commit(_session=session)

View File

@ -31,7 +31,7 @@ from cps import logger, config
from cps.subproc_wrapper import process_open from cps.subproc_wrapper import process_open
from flask_babel import gettext as _ from flask_babel import gettext as _
from cps.kobo_sync_status import remove_synced_book from cps.kobo_sync_status import remove_synced_book
from cps.ub import ini from cps.ub import init_db_thread
from cps.tasks.mail import TaskEmail from cps.tasks.mail import TaskEmail
from cps import gdriveutils from cps import gdriveutils
@ -166,7 +166,7 @@ class TaskConvert(CalibreTask):
local_db.session.merge(new_format) local_db.session.merge(new_format)
local_db.session.commit() local_db.session.commit()
if self.settings['new_book_format'].upper() in ['KEPUB', 'EPUB', 'EPUB3']: if self.settings['new_book_format'].upper() in ['KEPUB', 'EPUB', 'EPUB3']:
ub_session = ini() ub_session = init_db_thread()
remove_synced_book(book_id, True, ub_session) remove_synced_book(book_id, True, ub_session)
ub_session.close() ub_session.close()
except SQLAlchemyError as e: except SQLAlchemyError as e:

123
cps/ub.py
View File

@ -37,6 +37,7 @@ except ImportError as e:
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
oauth_support = True oauth_support = True
except ImportError as e: except ImportError as e:
OAuthConsumerMixin = BaseException
oauth_support = False oauth_support = False
from sqlalchemy import create_engine, exc, exists, event, text from sqlalchemy import create_engine, exc, exists, event, text
from sqlalchemy import Column, ForeignKey from sqlalchemy import Column, ForeignKey
@ -510,7 +511,7 @@ class RemoteAuthToken(Base):
# Add missing tables during migration of database # Add missing tables during migration of database
def add_missing_tables(engine, session): def add_missing_tables(engine, _session):
if not engine.dialect.has_table(engine.connect(), "book_read_link"): if not engine.dialect.has_table(engine.connect(), "book_read_link"):
ReadBook.__table__.create(bind=engine) ReadBook.__table__.create(bind=engine)
if not engine.dialect.has_table(engine.connect(), "bookmark"): if not engine.dialect.has_table(engine.connect(), "bookmark"):
@ -527,26 +528,26 @@ def add_missing_tables(engine, session):
Registration.__table__.create(bind=engine) Registration.__table__.create(bind=engine)
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("insert into registration (domain, allow) values('%.%',1)") conn.execute("insert into registration (domain, allow) values('%.%',1)")
session.commit() _session.commit()
# migrate all settings missing in registration table # migrate all settings missing in registration table
def migrate_registration_table(engine, session): def migrate_registration_table(engine, _session):
try: try:
session.query(exists().where(Registration.allow)).scalar() _session.query(exists().where(Registration.allow)).scalar()
session.commit() _session.commit()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE registration ADD column 'allow' INTEGER") conn.execute("ALTER TABLE registration ADD column 'allow' INTEGER")
conn.execute("update registration set 'allow' = 1") conn.execute("update registration set 'allow' = 1")
session.commit() _session.commit()
try: try:
# Handle table exists, but no content # Handle table exists, but no content
cnt = session.query(Registration).count() cnt = _session.query(Registration).count()
if not cnt: if not cnt:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("insert into registration (domain, allow) values('%.%',1)") conn.execute("insert into registration (domain, allow) values('%.%',1)")
session.commit() _session.commit()
except exc.OperationalError: # Database is not writeable except exc.OperationalError: # Database is not writeable
print('Settings database is not writeable. Exiting...') print('Settings database is not writeable. Exiting...')
sys.exit(2) sys.exit(2)
@ -564,9 +565,9 @@ def migrate_guest_password(engine):
sys.exit(2) sys.exit(2)
def migrate_shelfs(engine, session): def migrate_shelfs(engine, _session):
try: try:
session.query(exists().where(Shelf.uuid)).scalar() _session.query(exists().where(Shelf.uuid)).scalar()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE shelf ADD column 'uuid' STRING") conn.execute("ALTER TABLE shelf ADD column 'uuid' STRING")
@ -574,33 +575,33 @@ def migrate_shelfs(engine, session):
conn.execute("ALTER TABLE shelf ADD column 'last_modified' DATETIME") conn.execute("ALTER TABLE shelf ADD column 'last_modified' DATETIME")
conn.execute("ALTER TABLE book_shelf_link ADD column 'date_added' DATETIME") conn.execute("ALTER TABLE book_shelf_link ADD column 'date_added' DATETIME")
conn.execute("ALTER TABLE shelf ADD column 'kobo_sync' BOOLEAN DEFAULT false") conn.execute("ALTER TABLE shelf ADD column 'kobo_sync' BOOLEAN DEFAULT false")
for shelf in session.query(Shelf).all(): for shelf in _session.query(Shelf).all():
shelf.uuid = str(uuid.uuid4()) shelf.uuid = str(uuid.uuid4())
shelf.created = datetime.datetime.now() shelf.created = datetime.datetime.now()
shelf.last_modified = datetime.datetime.now() shelf.last_modified = datetime.datetime.now()
for book_shelf in session.query(BookShelf).all(): for book_shelf in _session.query(BookShelf).all():
book_shelf.date_added = datetime.datetime.now() book_shelf.date_added = datetime.datetime.now()
session.commit() _session.commit()
try: try:
session.query(exists().where(Shelf.kobo_sync)).scalar() _session.query(exists().where(Shelf.kobo_sync)).scalar()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE shelf ADD column 'kobo_sync' BOOLEAN DEFAULT false") conn.execute("ALTER TABLE shelf ADD column 'kobo_sync' BOOLEAN DEFAULT false")
session.commit() _session.commit()
try: try:
session.query(exists().where(BookShelf.order)).scalar() _session.query(exists().where(BookShelf.order)).scalar()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1") conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1")
session.commit() _session.commit()
def migrate_readBook(engine, session): def migrate_readBook(engine, _session):
try: try:
session.query(exists().where(ReadBook.read_status)).scalar() _session.query(exists().where(ReadBook.read_status)).scalar()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0") conn.execute("ALTER TABLE book_read_link ADD column 'read_status' INTEGER DEFAULT 0")
@ -608,46 +609,46 @@ def migrate_readBook(engine, session):
conn.execute("ALTER TABLE book_read_link ADD column 'last_modified' DATETIME") conn.execute("ALTER TABLE book_read_link ADD column 'last_modified' DATETIME")
conn.execute("ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME") conn.execute("ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME")
conn.execute("ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0") conn.execute("ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0")
session.commit() _session.commit()
test = session.query(ReadBook).filter(ReadBook.last_modified == None).all() test = _session.query(ReadBook).filter(ReadBook.last_modified == None).all()
for book in test: for book in test:
book.last_modified = datetime.datetime.utcnow() book.last_modified = datetime.datetime.utcnow()
session.commit() _session.commit()
def migrate_remoteAuthToken(engine, session): def migrate_remoteAuthToken(engine, _session):
try: try:
session.query(exists().where(RemoteAuthToken.token_type)).scalar() _session.query(exists().where(RemoteAuthToken.token_type)).scalar()
session.commit() _session.commit()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0") conn.execute("ALTER TABLE remote_auth_token ADD column 'token_type' INTEGER DEFAULT 0")
conn.execute("update remote_auth_token set 'token_type' = 0") conn.execute("update remote_auth_token set 'token_type' = 0")
session.commit() _session.commit()
# Migrate database to current version, has to be updated after every database change. Currently migration from # Migrate database to current version, has to be updated after every database change. Currently migration from
# everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding # everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding
# rows with SQL commands # rows with SQL commands
def migrate_Database(session): def migrate_Database(_session):
engine = session.bind engine = _session.bind
add_missing_tables(engine, session) add_missing_tables(engine, _session)
migrate_registration_table(engine, session) migrate_registration_table(engine, _session)
migrate_readBook(engine, session) migrate_readBook(engine, _session)
migrate_remoteAuthToken(engine, session) migrate_remoteAuthToken(engine, _session)
migrate_shelfs(engine, session) migrate_shelfs(engine, _session)
try: try:
create = False create = False
session.query(exists().where(User.sidebar_view)).scalar() _session.query(exists().where(User.sidebar_view)).scalar()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1") conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1")
session.commit() _session.commit()
create = True create = True
try: try:
if create: if create:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("SELECT language_books FROM user") conn.execute("SELECT language_books FROM user")
session.commit() _session.commit()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang " conn.execute("UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang "
@ -657,32 +658,32 @@ def migrate_Database(session):
'side_series': constants.SIDEBAR_SERIES, 'side_category': constants.SIDEBAR_CATEGORY, 'side_series': constants.SIDEBAR_SERIES, 'side_category': constants.SIDEBAR_CATEGORY,
'side_hot': constants.SIDEBAR_HOT, 'side_autor': constants.SIDEBAR_AUTHOR, 'side_hot': constants.SIDEBAR_HOT, 'side_autor': constants.SIDEBAR_AUTHOR,
'detail_random': constants.DETAIL_RANDOM}) 'detail_random': constants.DETAIL_RANDOM})
session.commit() _session.commit()
try: try:
session.query(exists().where(User.denied_tags)).scalar() _session.query(exists().where(User.denied_tags)).scalar()
except exc.OperationalError: # Database is not compatible, some columns are missing except exc.OperationalError: # Database is not compatible, some columns are missing
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE user ADD column `denied_tags` String DEFAULT ''") conn.execute("ALTER TABLE user ADD column `denied_tags` String DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''") conn.execute("ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `denied_column_value` String DEFAULT ''") conn.execute("ALTER TABLE user ADD column `denied_column_value` String DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `allowed_column_value` String DEFAULT ''") conn.execute("ALTER TABLE user ADD column `allowed_column_value` String DEFAULT ''")
session.commit() _session.commit()
try: try:
session.query(exists().where(User.view_settings)).scalar() _session.query(exists().where(User.view_settings)).scalar()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE user ADD column `view_settings` VARCHAR(10) DEFAULT '{}'") conn.execute("ALTER TABLE user ADD column `view_settings` VARCHAR(10) DEFAULT '{}'")
session.commit() _session.commit()
try: try:
session.query(exists().where(User.kobo_only_shelves_sync)).scalar() _session.query(exists().where(User.kobo_only_shelves_sync)).scalar()
except exc.OperationalError: except exc.OperationalError:
with engine.connect() as conn: with engine.connect() as conn:
conn.execute("ALTER TABLE user ADD column `kobo_only_shelves_sync` SMALLINT DEFAULT 0") conn.execute("ALTER TABLE user ADD column `kobo_only_shelves_sync` SMALLINT DEFAULT 0")
session.commit() _session.commit()
try: try:
# check if name is in User table instead of nickname # check if name is in User table instead of nickname
session.query(exists().where(User.name)).scalar() _session.query(exists().where(User.name)).scalar()
except exc.OperationalError: except exc.OperationalError:
# Create new table user_id and copy contents of table user into it # Create new table user_id and copy contents of table user into it
with engine.connect() as conn: with engine.connect() as conn:
@ -712,20 +713,20 @@ def migrate_Database(session):
# delete old user table and rename new user_id table to user: # delete old user table and rename new user_id table to user:
conn.execute(text("DROP TABLE user")) conn.execute(text("DROP TABLE user"))
conn.execute(text("ALTER TABLE user_id RENAME TO user")) conn.execute(text("ALTER TABLE user_id RENAME TO user"))
session.commit() _session.commit()
if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \ if _session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \
is None: is None:
create_anonymous_user(session) create_anonymous_user(_session)
migrate_guest_password(engine) migrate_guest_password(engine)
def clean_database(session): def clean_database(_session):
# Remove expired remote login tokens # Remove expired remote login tokens
now = datetime.datetime.now() now = datetime.datetime.now()
session.query(RemoteAuthToken).filter(now > RemoteAuthToken.expiration).\ _session.query(RemoteAuthToken).filter(now > RemoteAuthToken.expiration).\
filter(RemoteAuthToken.token_type != 1).delete() filter(RemoteAuthToken.token_type != 1).delete()
session.commit() _session.commit()
# Save downloaded books per user in calibre-web's own database # Save downloaded books per user in calibre-web's own database
@ -750,22 +751,22 @@ def delete_download(book_id):
session.rollback() session.rollback()
# Generate user Guest (translated text), as anonymous user, no rights # Generate user Guest (translated text), as anonymous user, no rights
def create_anonymous_user(session): def create_anonymous_user(_session):
user = User() user = User()
user.name = "Guest" user.name = "Guest"
user.email = 'no@email' user.email = 'no@email'
user.role = constants.ROLE_ANONYMOUS user.role = constants.ROLE_ANONYMOUS
user.password = '' user.password = ''
session.add(user) _session.add(user)
try: try:
session.commit() _session.commit()
except Exception: except Exception:
session.rollback() _session.rollback()
# Generate User admin with admin123 password, and access to everything # Generate User admin with admin123 password, and access to everything
def create_admin_user(session): def create_admin_user(_session):
user = User() user = User()
user.name = "admin" user.name = "admin"
user.role = constants.ADMIN_USER_ROLES user.role = constants.ADMIN_USER_ROLES
@ -773,13 +774,13 @@ def create_admin_user(session):
user.password = generate_password_hash(constants.DEFAULT_PASSWORD) user.password = generate_password_hash(constants.DEFAULT_PASSWORD)
session.add(user) _session.add(user)
try: try:
session.commit() _session.commit()
except Exception: except Exception:
session.rollback() _session.rollback()
def ini(): def init_db_thread():
global app_DB_path global app_DB_path
engine = create_engine(u'sqlite:///{0}'.format(app_DB_path), echo=False) engine = create_engine(u'sqlite:///{0}'.format(app_DB_path), echo=False)
@ -844,8 +845,8 @@ def dispose():
except Exception: except Exception:
pass pass
def session_commit(success=None, sess=None): def session_commit(success=None, _session=None):
s = sess if sess else session s = _session if _session else session
try: try:
s.commit() s.commit()
if success: if success:

View File

@ -10,7 +10,6 @@ pyasn1>=0.1.9,<0.5.0
PyDrive2>=1.3.1,<1.11.0 PyDrive2>=1.3.1,<1.11.0
PyYAML>=3.12 PyYAML>=3.12
rsa>=3.4.2,<4.9.0 rsa>=3.4.2,<4.9.0
# six>=1.10.0,<1.17.0
# Gmail # Gmail
google-auth-oauthlib>=0.4.3,<0.5.0 google-auth-oauthlib>=0.4.3,<0.5.0
@ -34,7 +33,7 @@ scholarly>=1.2.0,<1.6
markdown2>=2.0.0,<2.5.0 markdown2>=2.0.0,<2.5.0
html2text>=2020.1.16,<2022.1.1 html2text>=2020.1.16,<2022.1.1
python-dateutil>=2.1,<2.9.0 python-dateutil>=2.1,<2.9.0
beautifulsoup4>=4.0.1,<4.2.0 beautifulsoup4>=4.0.1,<4.11.0
cchardet>=2.0.0,<2.2.0 cchardet>=2.0.0,<2.2.0
# Comics # Comics

View File

@ -5,7 +5,7 @@ Flask-Principal>=0.3.2,<0.5.1
backports_abc>=0.4 backports_abc>=0.4
Flask>=1.0.2,<2.1.0 Flask>=1.0.2,<2.1.0
iso-639>=0.4.5,<0.5.0 iso-639>=0.4.5,<0.5.0
PyPDF3>=1.0.0,<1.0.6 PyPDF3>=1.0.0,<1.0.7
pytz>=2016.10 pytz>=2016.10
requests>=2.11.1,<2.28.0 requests>=2.11.1,<2.28.0
SQLAlchemy>=1.3.0,<1.5.0 SQLAlchemy>=1.3.0,<1.5.0

View File

@ -45,7 +45,7 @@ install_requires =
backports_abc>=0.4 backports_abc>=0.4
Flask>=1.0.2,<2.1.0 Flask>=1.0.2,<2.1.0
iso-639>=0.4.5,<0.5.0 iso-639>=0.4.5,<0.5.0
PyPDF3>=1.0.0,<1.0.6 PyPDF3>=1.0.0,<1.0.7
pytz>=2016.10 pytz>=2016.10
requests>=2.11.1,<2.28.0 requests>=2.11.1,<2.28.0
SQLAlchemy>=1.3.0,<1.5.0 SQLAlchemy>=1.3.0,<1.5.0
@ -70,7 +70,6 @@ gdrive =
PyDrive2>=1.3.1,<1.11.0 PyDrive2>=1.3.1,<1.11.0
PyYAML>=3.12 PyYAML>=3.12
rsa>=3.4.2,<4.9.0 rsa>=3.4.2,<4.9.0
six>=1.10.0,<1.17.0
gmail = gmail =
google-auth-oauthlib>=0.4.3,<0.5.0 google-auth-oauthlib>=0.4.3,<0.5.0
google-api-python-client>=1.7.11,<2.37.0 google-api-python-client>=1.7.11,<2.37.0
@ -89,7 +88,7 @@ metadata =
markdown2>=2.0.0,<2.5.0 markdown2>=2.0.0,<2.5.0
html2text>=2020.1.16,<2022.1.1 html2text>=2020.1.16,<2022.1.1
python-dateutil>=2.1,<2.9.0 python-dateutil>=2.1,<2.9.0
beautifulsoup4>=4.0.1,<4.2.0 beautifulsoup4>=4.0.1,<4.11.0
cchardet>=2.0.0,<2.2.0 cchardet>=2.0.0,<2.2.0
comics = comics =
natsort>=2.2.0,<8.2.0 natsort>=2.2.0,<8.2.0

File diff suppressed because it is too large Load Diff