Refactored startup for compatibility with pyinstaller 5.0
This commit is contained in:
parent
db03fb3edd
commit
9410b47144
77
cps.py
77
cps.py
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||
# Copyright (C) 2012-2019 OzzieIsaacs
|
||||
# Copyright (C) 2022 OzzieIsaacs
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,72 +17,19 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Are we running from commandline?
|
||||
if __package__ == '':
|
||||
# Add local path to sys.path so we can import cps
|
||||
path = os.path.dirname(os.path.dirname(__file__))
|
||||
sys.path.insert(0, path)
|
||||
|
||||
# Insert local directories into path
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'vendor'))
|
||||
|
||||
|
||||
from cps import create_app
|
||||
from cps import web_server
|
||||
from cps.opds import opds
|
||||
from cps.web import web
|
||||
from cps.jinjia import jinjia
|
||||
from cps.about import about
|
||||
from cps.shelf import shelf
|
||||
from cps.admin import admi
|
||||
from cps.gdrive import gdrive
|
||||
from cps.editbooks import EditBook
|
||||
from cps.remotelogin import remotelogin
|
||||
from cps.search_metadata import meta
|
||||
from cps.error_handler import init_errorhandler
|
||||
from cps.schedule import register_scheduled_tasks, register_startup_tasks
|
||||
|
||||
try:
|
||||
from cps.kobo import kobo, get_kobo_activated
|
||||
from cps.kobo_auth import kobo_auth
|
||||
kobo_available = get_kobo_activated()
|
||||
except (ImportError, AttributeError): # Catch also error for not installed flask-WTF (missing csrf decorator)
|
||||
kobo_available = False
|
||||
|
||||
try:
|
||||
from cps.oauth_bb import oauth
|
||||
oauth_available = True
|
||||
except ImportError:
|
||||
oauth_available = False
|
||||
|
||||
|
||||
def main():
|
||||
app = create_app()
|
||||
|
||||
init_errorhandler()
|
||||
|
||||
app.register_blueprint(web)
|
||||
app.register_blueprint(opds)
|
||||
app.register_blueprint(jinjia)
|
||||
app.register_blueprint(about)
|
||||
app.register_blueprint(shelf)
|
||||
app.register_blueprint(admi)
|
||||
app.register_blueprint(remotelogin)
|
||||
app.register_blueprint(meta)
|
||||
app.register_blueprint(gdrive)
|
||||
app.register_blueprint(EditBook)
|
||||
if kobo_available:
|
||||
app.register_blueprint(kobo)
|
||||
app.register_blueprint(kobo_auth)
|
||||
if oauth_available:
|
||||
app.register_blueprint(oauth)
|
||||
|
||||
# Register scheduled tasks
|
||||
register_scheduled_tasks() # ToDo only reconnect if reconnect is enabled
|
||||
register_startup_tasks()
|
||||
|
||||
success = web_server.start()
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
from cps.main import main as _main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
_main()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
__package__ = "cps"
|
||||
|
||||
import sys
|
||||
import os
|
||||
import mimetypes
|
||||
|
@ -28,15 +26,23 @@ import mimetypes
|
|||
from babel import Locale as LC
|
||||
from babel import negotiate_locale
|
||||
from babel.core import UnknownLocaleError
|
||||
from flask import Flask, request, g
|
||||
from flask import request, g
|
||||
from flask import Flask
|
||||
from .MyLoginManager import MyLoginManager
|
||||
from flask_babel import Babel
|
||||
from flask_principal import Principal
|
||||
|
||||
from . import config_sql, logger, cache_buster, cli, ub, db
|
||||
from . import config_sql
|
||||
from . import logger
|
||||
from . import cache_buster
|
||||
from .cli import CliParameter
|
||||
from .constants import CONFIG_DIR
|
||||
from . import ub, db
|
||||
from .reverseproxy import ReverseProxied
|
||||
from .server import WebServer
|
||||
from .dep_check import dependency_check
|
||||
from . import services
|
||||
from .updater import Updater
|
||||
|
||||
try:
|
||||
import lxml
|
||||
|
@ -50,6 +56,7 @@ try:
|
|||
except ImportError:
|
||||
wtf_present = False
|
||||
|
||||
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
mimetypes.add_type('application/epub+zip', '.epub')
|
||||
|
@ -81,37 +88,55 @@ app.config.update(
|
|||
|
||||
|
||||
lm = MyLoginManager()
|
||||
lm.login_view = 'web.login'
|
||||
lm.anonymous_user = ub.Anonymous
|
||||
lm.session_protection = 'strong'
|
||||
|
||||
if wtf_present:
|
||||
csrf = CSRFProtect()
|
||||
csrf.init_app(app)
|
||||
else:
|
||||
csrf = None
|
||||
|
||||
ub.init_db(cli.settings_path)
|
||||
# pylint: disable=no-member
|
||||
config = config_sql.load_configuration(ub.session)
|
||||
|
||||
web_server = WebServer()
|
||||
|
||||
babel = Babel()
|
||||
_BABEL_TRANSLATIONS = set()
|
||||
|
||||
log = logger.create()
|
||||
|
||||
config = config_sql._ConfigSQL()
|
||||
|
||||
from . import services
|
||||
|
||||
db.CalibreDB.update_config(config)
|
||||
db.CalibreDB.setup_db(config.config_calibre_dir, cli.settings_path)
|
||||
cli_param = CliParameter()
|
||||
|
||||
if wtf_present:
|
||||
csrf = CSRFProtect()
|
||||
else:
|
||||
csrf = None
|
||||
|
||||
calibre_db = db.CalibreDB()
|
||||
|
||||
web_server = WebServer()
|
||||
|
||||
updater_thread = Updater()
|
||||
|
||||
|
||||
def create_app():
|
||||
lm.login_view = 'web.login'
|
||||
lm.anonymous_user = ub.Anonymous
|
||||
lm.session_protection = 'strong'
|
||||
|
||||
if csrf:
|
||||
csrf.init_app(app)
|
||||
|
||||
cli_param.init()
|
||||
|
||||
ub.init_db(os.path.join(CONFIG_DIR, "app.db"), cli_param.user_credentials)
|
||||
|
||||
# ub.init_db(os.path.join(CONFIG_DIR, "app.db"))
|
||||
# pylint: disable=no-member
|
||||
config_sql.load_configuration(config, ub.session, cli_param)
|
||||
|
||||
db.CalibreDB.update_config(config)
|
||||
db.CalibreDB.setup_db(config.config_calibre_dir, cli_param.settings_path)
|
||||
calibre_db.init_db()
|
||||
|
||||
updater_thread.init_updater(config, web_server)
|
||||
# Perform dry run of updater and exit afterwards
|
||||
if cli_param.dry_run:
|
||||
updater_thread.dry_run()
|
||||
sys.exit(0)
|
||||
updater_thread.start()
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
log.info(
|
||||
'*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, please update your installation to Python3 ***')
|
||||
|
@ -156,7 +181,7 @@ def create_app():
|
|||
services.goodreads_support.connect(config.config_goodreads_api_key,
|
||||
config.config_goodreads_api_secret,
|
||||
config.config_use_goodreads)
|
||||
config.store_calibre_uuid(calibre_db, db.LibraryId)
|
||||
config.store_calibre_uuid(calibre_db, db.Library_Id)
|
||||
return app
|
||||
|
||||
|
||||
|
@ -179,11 +204,8 @@ def get_locale():
|
|||
return negotiate_locale(preferred or ['en'], _BABEL_TRANSLATIONS)
|
||||
|
||||
|
||||
from .updater import Updater
|
||||
updater_thread = Updater()
|
||||
'''@babel.timezoneselector
|
||||
def get_timezone():
|
||||
user = getattr(g, 'user', None)
|
||||
return user.timezone if user else None'''
|
||||
|
||||
# Perform dry run of updater and exit afterwards
|
||||
if cli.dry_run:
|
||||
updater_thread.dry_run()
|
||||
sys.exit(0)
|
||||
updater_thread.start()
|
||||
|
|
|
@ -1254,7 +1254,7 @@ def _db_configuration_update_helper():
|
|||
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'),
|
||||
gdrive_error)
|
||||
config.store_calibre_uuid(calibre_db, db.LibraryId)
|
||||
config.store_calibre_uuid(calibre_db, db.Library_Id)
|
||||
# if db changed -> delete shelfs, delete download books, delete read books, kobo sync...
|
||||
if db_change:
|
||||
log.info("Calibre Database changed, all Calibre-Web info related to old Database gets deleted")
|
||||
|
|
169
cps/cli.py
169
cps/cli.py
|
@ -31,96 +31,99 @@ def version_info():
|
|||
return "Calibre-Web version: %s - unkown git-clone" % _STABLE_VERSION['version']
|
||||
return "Calibre-Web version: %s -%s" % (_STABLE_VERSION['version'], _NIGHTLY_VERSION[1])
|
||||
|
||||
class CliParameter(object):
|
||||
|
||||
parser = argparse.ArgumentParser(description='Calibre Web is a web app'
|
||||
' providing a interface for browsing, reading and downloading eBooks\n', prog='cps.py')
|
||||
parser.add_argument('-p', metavar='path', help='path and name to settings db, e.g. /opt/cw.db')
|
||||
parser.add_argument('-g', metavar='path', help='path and name to gdrive db, e.g. /opt/gd.db')
|
||||
parser.add_argument('-c', metavar='path',
|
||||
help='path and name to SSL certfile, e.g. /opt/test.cert, works only in combination with keyfile')
|
||||
parser.add_argument('-k', metavar='path',
|
||||
help='path and name to SSL keyfile, e.g. /opt/test.key, works only in combination with certfile')
|
||||
parser.add_argument('-v', '--version', action='version', help='Shows version number and exits Calibre-Web',
|
||||
version=version_info())
|
||||
parser.add_argument('-i', metavar='ip-address', help='Server IP-Address to listen')
|
||||
parser.add_argument('-s', metavar='user:pass', help='Sets specific username to new password and exits Calibre-Web')
|
||||
parser.add_argument('-f', action='store_true', help='Flag is depreciated and will be removed in next version')
|
||||
parser.add_argument('-l', action='store_true', help='Allow loading covers from localhost')
|
||||
parser.add_argument('-d', action='store_true', help='Dry run of updater to check file permissions in advance '
|
||||
'and exits Calibre-Web')
|
||||
parser.add_argument('-r', action='store_true', help='Enable public database reconnect route under /reconnect')
|
||||
args = parser.parse_args()
|
||||
def init(self):
|
||||
self.arg_parser()
|
||||
|
||||
settings_path = args.p or os.path.join(_CONFIG_DIR, DEFAULT_SETTINGS_FILE)
|
||||
gd_path = args.g or os.path.join(_CONFIG_DIR, DEFAULT_GDRIVE_FILE)
|
||||
def arg_parser(self):
|
||||
parser = argparse.ArgumentParser(description='Calibre Web is a web app'
|
||||
' providing a interface for browsing, reading and downloading eBooks\n',
|
||||
prog='cps.py')
|
||||
parser.add_argument('-p', metavar='path', help='path and name to settings db, e.g. /opt/cw.db')
|
||||
parser.add_argument('-g', metavar='path', help='path and name to gdrive db, e.g. /opt/gd.db')
|
||||
parser.add_argument('-c', metavar='path',
|
||||
help='path and name to SSL certfile, e.g. /opt/test.cert, works only in combination with keyfile')
|
||||
parser.add_argument('-k', metavar='path',
|
||||
help='path and name to SSL keyfile, e.g. /opt/test.key, works only in combination with certfile')
|
||||
parser.add_argument('-v', '--version', action='version', help='Shows version number and exits Calibre-Web',
|
||||
version=version_info())
|
||||
parser.add_argument('-i', metavar='ip-address', help='Server IP-Address to listen')
|
||||
parser.add_argument('-s', metavar='user:pass',
|
||||
help='Sets specific username to new password and exits Calibre-Web')
|
||||
parser.add_argument('-f', action='store_true', help='Flag is depreciated and will be removed in next version')
|
||||
parser.add_argument('-l', action='store_true', help='Allow loading covers from localhost')
|
||||
parser.add_argument('-d', action='store_true', help='Dry run of updater to check file permissions in advance '
|
||||
'and exits Calibre-Web')
|
||||
parser.add_argument('-r', action='store_true', help='Enable public database reconnect route under /reconnect')
|
||||
args = parser.parse_args()
|
||||
|
||||
if os.path.isdir(settings_path):
|
||||
settings_path = os.path.join(settings_path, DEFAULT_SETTINGS_FILE)
|
||||
self.settings_path = args.p or os.path.join(_CONFIG_DIR, DEFAULT_SETTINGS_FILE)
|
||||
self.gd_path = args.g or os.path.join(_CONFIG_DIR, DEFAULT_GDRIVE_FILE)
|
||||
|
||||
if os.path.isdir(gd_path):
|
||||
gd_path = os.path.join(gd_path, DEFAULT_GDRIVE_FILE)
|
||||
if os.path.isdir(self.settings_path):
|
||||
self.settings_path = os.path.join(self.settings_path, DEFAULT_SETTINGS_FILE)
|
||||
|
||||
if os.path.isdir(self.gd_path):
|
||||
self.gd_path = os.path.join(self.gd_path, DEFAULT_GDRIVE_FILE)
|
||||
|
||||
|
||||
# handle and check parameter for ssl encryption
|
||||
certfilepath = None
|
||||
keyfilepath = None
|
||||
if args.c:
|
||||
if os.path.isfile(args.c):
|
||||
certfilepath = args.c
|
||||
else:
|
||||
print("Certfile path is invalid. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
if args.c == "":
|
||||
certfilepath = ""
|
||||
|
||||
if args.k:
|
||||
if os.path.isfile(args.k):
|
||||
keyfilepath = args.k
|
||||
else:
|
||||
print("Keyfile path is invalid. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
if (args.k and not args.c) or (not args.k and args.c):
|
||||
print("Certfile and Keyfile have to be used together. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
if args.k == "":
|
||||
keyfilepath = ""
|
||||
|
||||
|
||||
# dry run updater
|
||||
dry_run = args.d or None
|
||||
# enable reconnect endpoint for docker database reconnect
|
||||
reconnect_enable = args.r or os.environ.get("CALIBRE_RECONNECT", None)
|
||||
# load covers from localhost
|
||||
allow_localhost = args.l or os.environ.get("CALIBRE_LOCALHOST", None)
|
||||
# handle and check ip address argument
|
||||
ip_address = args.i or None
|
||||
|
||||
|
||||
if ip_address:
|
||||
try:
|
||||
# try to parse the given ip address with socket
|
||||
if hasattr(socket, 'inet_pton'):
|
||||
if ':' in ip_address:
|
||||
socket.inet_pton(socket.AF_INET6, ip_address)
|
||||
# handle and check parameter for ssl encryption
|
||||
self.certfilepath = None
|
||||
self.keyfilepath = None
|
||||
if args.c:
|
||||
if os.path.isfile(args.c):
|
||||
self.certfilepath = args.c
|
||||
else:
|
||||
socket.inet_pton(socket.AF_INET, ip_address)
|
||||
else:
|
||||
# on windows python < 3.4, inet_pton is not available
|
||||
# inet_atom only handles IPv4 addresses
|
||||
socket.inet_aton(ip_address)
|
||||
except socket.error as err:
|
||||
print(ip_address, ':', err)
|
||||
sys.exit(1)
|
||||
print("Certfile path is invalid. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
# handle and check user password argument
|
||||
user_credentials = args.s or None
|
||||
if user_credentials and ":" not in user_credentials:
|
||||
print("No valid 'username:password' format")
|
||||
sys.exit(3)
|
||||
if args.c == "":
|
||||
self.certfilepath = ""
|
||||
|
||||
if args.f:
|
||||
print("Warning: -f flag is depreciated and will be removed in next version")
|
||||
if args.k:
|
||||
if os.path.isfile(args.k):
|
||||
self.keyfilepath = args.k
|
||||
else:
|
||||
print("Keyfile path is invalid. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
if (args.k and not args.c) or (not args.k and args.c):
|
||||
print("Certfile and Keyfile have to be used together. Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
if args.k == "":
|
||||
self.keyfilepath = ""
|
||||
|
||||
# dry run updater
|
||||
self.dry_run =args.d or None
|
||||
# enable reconnect endpoint for docker database reconnect
|
||||
self.reconnect_enable = args.r or os.environ.get("CALIBRE_RECONNECT", None)
|
||||
# load covers from localhost
|
||||
self.allow_localhost = args.l or os.environ.get("CALIBRE_LOCALHOST", None)
|
||||
# handle and check ip address argument
|
||||
self.ip_address = args.i or None
|
||||
if self.ip_address:
|
||||
try:
|
||||
# try to parse the given ip address with socket
|
||||
if hasattr(socket, 'inet_pton'):
|
||||
if ':' in self.ip_address:
|
||||
socket.inet_pton(socket.AF_INET6, self.ip_address)
|
||||
else:
|
||||
socket.inet_pton(socket.AF_INET, self.ip_address)
|
||||
else:
|
||||
# on windows python < 3.4, inet_pton is not available
|
||||
# inet_atom only handles IPv4 addresses
|
||||
socket.inet_aton(self.ip_address)
|
||||
except socket.error as err:
|
||||
print(self.ip_address, ':', err)
|
||||
sys.exit(1)
|
||||
|
||||
# handle and check user password argument
|
||||
self.user_credentials = args.s or None
|
||||
if self.user_credentials and ":" not in self.user_credentials:
|
||||
print("No valid 'username:password' format")
|
||||
sys.exit(3)
|
||||
|
||||
if args.f:
|
||||
print("Warning: -f flag is depreciated and will be removed in next version")
|
||||
|
|
|
@ -29,7 +29,7 @@ try:
|
|||
except ImportError:
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from . import constants, cli, logger
|
||||
from . import constants, logger
|
||||
|
||||
|
||||
log = logger.create()
|
||||
|
@ -154,12 +154,16 @@ class _Settings(_Base):
|
|||
# Class holds all application specific settings in calibre-web
|
||||
class _ConfigSQL(object):
|
||||
# pylint: disable=no-member
|
||||
def __init__(self, session):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def init_config(self, session, cli):
|
||||
self._session = session
|
||||
self._settings = None
|
||||
self.db_configured = None
|
||||
self.config_calibre_dir = None
|
||||
self.load()
|
||||
self.cli = cli
|
||||
|
||||
change = False
|
||||
if self.config_converterpath == None: # pylint: disable=access-member-before-definition
|
||||
|
@ -184,22 +188,21 @@ class _ConfigSQL(object):
|
|||
return self._settings
|
||||
|
||||
def get_config_certfile(self):
|
||||
if cli.certfilepath:
|
||||
return cli.certfilepath
|
||||
if cli.certfilepath == "":
|
||||
if self.cli.certfilepath:
|
||||
return self.cli.certfilepath
|
||||
if self.cli.certfilepath == "":
|
||||
return None
|
||||
return self.config_certfile
|
||||
|
||||
def get_config_keyfile(self):
|
||||
if cli.keyfilepath:
|
||||
return cli.keyfilepath
|
||||
if cli.certfilepath == "":
|
||||
if self.cli.keyfilepath:
|
||||
return self.cli.keyfilepath
|
||||
if self.cli.certfilepath == "":
|
||||
return None
|
||||
return self.config_keyfile
|
||||
|
||||
@staticmethod
|
||||
def get_config_ipaddress():
|
||||
return cli.ip_address or ""
|
||||
def get_config_ipaddress(self):
|
||||
return self.cli.ip_address or ""
|
||||
|
||||
def _has_role(self, role_flag):
|
||||
return constants.has_flag(self.config_default_role, role_flag)
|
||||
|
@ -449,14 +452,15 @@ def _migrate_database(session):
|
|||
_migrate_table(session, _Flask_Settings)
|
||||
|
||||
|
||||
def load_configuration(session):
|
||||
def load_configuration(conf, session, cli):
|
||||
_migrate_database(session)
|
||||
|
||||
if not session.query(_Settings).count():
|
||||
session.add(_Settings())
|
||||
session.commit()
|
||||
conf = _ConfigSQL(session)
|
||||
return conf
|
||||
# conf = _ConfigSQL()
|
||||
conf.init_config(session, cli)
|
||||
# return conf
|
||||
|
||||
def get_flask_session_key(_session):
|
||||
flask_settings = _session.query(_Flask_Settings).one_or_none()
|
||||
|
|
|
@ -89,7 +89,7 @@ books_publishers_link = Table('books_publishers_link', Base.metadata,
|
|||
)
|
||||
|
||||
|
||||
class LibraryId(Base):
|
||||
class Library_Id(Base):
|
||||
__tablename__ = 'library_id'
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String, nullable=False)
|
||||
|
@ -440,10 +440,12 @@ class CalibreDB:
|
|||
# instances alive once they reach the end of their respective scopes
|
||||
instances = WeakSet()
|
||||
|
||||
def __init__(self, expire_on_commit=True):
|
||||
def __init__(self):
|
||||
""" Initialize a new CalibreDB session
|
||||
"""
|
||||
self.session = None
|
||||
|
||||
def init_db(self, expire_on_commit=True):
|
||||
if self._init:
|
||||
self.init_session(expire_on_commit)
|
||||
|
||||
|
@ -543,7 +545,7 @@ class CalibreDB:
|
|||
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(LibraryId).one_or_none()
|
||||
database_uuid = local_session().query(Library_Id).one_or_none()
|
||||
# local_session.dispose()
|
||||
|
||||
check_engine.connect()
|
||||
|
|
|
@ -49,7 +49,7 @@ from .usermanagement import login_required_if_no_ano
|
|||
from .kobo_sync_status import change_archived_books
|
||||
|
||||
|
||||
EditBook = Blueprint('edit-book', __name__)
|
||||
editbook = Blueprint('edit-book', __name__)
|
||||
log = logger.create()
|
||||
|
||||
|
||||
|
@ -228,14 +228,14 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session):
|
|||
return changed, error
|
||||
|
||||
|
||||
@EditBook.route("/ajax/delete/<int:book_id>", methods=["POST"])
|
||||
@editbook.route("/ajax/delete/<int:book_id>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_book_from_details(book_id):
|
||||
return Response(delete_book_from_table(book_id, "", True), mimetype='application/json')
|
||||
|
||||
|
||||
@EditBook.route("/delete/<int:book_id>", defaults={'book_format': ""}, methods=["POST"])
|
||||
@EditBook.route("/delete/<int:book_id>/<string:book_format>", methods=["POST"])
|
||||
@editbook.route("/delete/<int:book_id>", defaults={'book_format': ""}, methods=["POST"])
|
||||
@editbook.route("/delete/<int:book_id>/<string:book_format>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_book_ajax(book_id, book_format):
|
||||
return delete_book_from_table(book_id, book_format, False)
|
||||
|
@ -743,14 +743,14 @@ def handle_author_on_edit(book, author_name, update_stored=True):
|
|||
return input_authors, change, renamed
|
||||
|
||||
|
||||
@EditBook.route("/admin/book/<int:book_id>", methods=['GET'])
|
||||
@editbook.route("/admin/book/<int:book_id>", methods=['GET'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def show_edit_book(book_id):
|
||||
return render_edit_book(book_id)
|
||||
|
||||
|
||||
@EditBook.route("/admin/book/<int:book_id>", methods=['POST'])
|
||||
@editbook.route("/admin/book/<int:book_id>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_book(book_id):
|
||||
|
@ -1084,7 +1084,7 @@ def move_coverfile(meta, db_book):
|
|||
category="error")
|
||||
|
||||
|
||||
@EditBook.route("/upload", methods=["POST"])
|
||||
@editbook.route("/upload", methods=["POST"])
|
||||
@login_required_if_no_ano
|
||||
@upload_required
|
||||
def upload():
|
||||
|
@ -1153,7 +1153,7 @@ def upload():
|
|||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
|
||||
@EditBook.route("/admin/book/convert/<int:book_id>", methods=['POST'])
|
||||
@editbook.route("/admin/book/convert/<int:book_id>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def convert_bookformat(book_id):
|
||||
|
@ -1178,7 +1178,7 @@ def convert_bookformat(book_id):
|
|||
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
|
||||
|
||||
|
||||
@EditBook.route("/ajax/getcustomenum/<int:c_id>")
|
||||
@editbook.route("/ajax/getcustomenum/<int:c_id>")
|
||||
@login_required
|
||||
def table_get_custom_enum(c_id):
|
||||
ret = list()
|
||||
|
@ -1191,7 +1191,7 @@ def table_get_custom_enum(c_id):
|
|||
return json.dumps(ret)
|
||||
|
||||
|
||||
@EditBook.route("/ajax/editbooks/<param>", methods=['POST'])
|
||||
@editbook.route("/ajax/editbooks/<param>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_list_book(param):
|
||||
|
@ -1303,7 +1303,7 @@ def edit_list_book(param):
|
|||
return ret
|
||||
|
||||
|
||||
@EditBook.route("/ajax/sort_value/<field>/<int:bookid>")
|
||||
@editbook.route("/ajax/sort_value/<field>/<int:bookid>")
|
||||
@login_required
|
||||
def get_sorted_entry(field, bookid):
|
||||
if field in ['title', 'authors', 'sort', 'author_sort']:
|
||||
|
@ -1320,7 +1320,7 @@ def get_sorted_entry(field, bookid):
|
|||
return ""
|
||||
|
||||
|
||||
@EditBook.route("/ajax/simulatemerge", methods=['POST'])
|
||||
@editbook.route("/ajax/simulatemerge", methods=['POST'])
|
||||
@login_required
|
||||
@edit_required
|
||||
def simulate_merge_list_book():
|
||||
|
@ -1336,7 +1336,7 @@ def simulate_merge_list_book():
|
|||
return ""
|
||||
|
||||
|
||||
@EditBook.route("/ajax/mergebooks", methods=['POST'])
|
||||
@editbook.route("/ajax/mergebooks", methods=['POST'])
|
||||
@login_required
|
||||
@edit_required
|
||||
def merge_list_book():
|
||||
|
@ -1374,7 +1374,7 @@ def merge_list_book():
|
|||
return ""
|
||||
|
||||
|
||||
@EditBook.route("/ajax/xchange", methods=['POST'])
|
||||
@editbook.route("/ajax/xchange", methods=['POST'])
|
||||
@login_required
|
||||
@edit_required
|
||||
def table_xchange_author_title():
|
||||
|
|
|
@ -63,7 +63,7 @@ except ImportError as err:
|
|||
importError = err
|
||||
gdrive_support = False
|
||||
|
||||
from . import logger, cli, config
|
||||
from . import logger, cli_param, config
|
||||
from .constants import CONFIG_DIR as _CONFIG_DIR
|
||||
|
||||
|
||||
|
@ -142,7 +142,7 @@ def is_gdrive_ready():
|
|||
return os.path.exists(SETTINGS_YAML) and os.path.exists(CREDENTIALS)
|
||||
|
||||
|
||||
engine = create_engine('sqlite:///{0}'.format(cli.gd_path), echo=False)
|
||||
engine = create_engine('sqlite:///{0}'.format(cli_param.gd_path), echo=False)
|
||||
Base = declarative_base()
|
||||
|
||||
# Open session for database connection
|
||||
|
@ -190,11 +190,11 @@ def migrate():
|
|||
session.execute('ALTER TABLE gdrive_ids2 RENAME to gdrive_ids')
|
||||
break
|
||||
|
||||
if not os.path.exists(cli.gd_path):
|
||||
if not os.path.exists(cli_param.gd_path):
|
||||
try:
|
||||
Base.metadata.create_all(engine)
|
||||
except Exception as ex:
|
||||
log.error("Error connect to database: {} - {}".format(cli.gd_path, ex))
|
||||
log.error("Error connect to database: {} - {}".format(cli_param.gd_path, ex))
|
||||
raise
|
||||
migrate()
|
||||
|
||||
|
@ -544,6 +544,7 @@ def deleteDatabaseOnChange():
|
|||
except (OperationalError, InvalidRequestError) as ex:
|
||||
session.rollback()
|
||||
log.error_or_exception('Database error: {}'.format(ex))
|
||||
session.rollback()
|
||||
|
||||
|
||||
def updateGdriveCalibreFromLocal():
|
||||
|
|
71
cps/main.py
Normal file
71
cps/main.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||
# Copyright (C) 2012-2022 OzzieIsaacs
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from . import create_app
|
||||
from .jinjia import jinjia
|
||||
from .shelf import shelf
|
||||
from .remotelogin import remotelogin
|
||||
from .search_metadata import meta
|
||||
from .error_handler import init_errorhandler
|
||||
|
||||
try:
|
||||
from kobo import kobo, get_kobo_activated
|
||||
from kobo_auth import kobo_auth
|
||||
kobo_available = get_kobo_activated()
|
||||
except (ImportError, AttributeError): # Catch also error for not installed flask-WTF (missing csrf decorator)
|
||||
kobo_available = False
|
||||
|
||||
try:
|
||||
from oauth_bb import oauth
|
||||
oauth_available = True
|
||||
except ImportError:
|
||||
oauth_available = False
|
||||
|
||||
|
||||
def main():
|
||||
app = create_app()
|
||||
|
||||
from .web import web
|
||||
from .opds import opds
|
||||
from .admin import admi
|
||||
from .gdrive import gdrive
|
||||
from .editbooks import editbook
|
||||
from .about import about
|
||||
|
||||
from . import web_server
|
||||
init_errorhandler()
|
||||
|
||||
app.register_blueprint(web)
|
||||
app.register_blueprint(opds)
|
||||
app.register_blueprint(jinjia)
|
||||
app.register_blueprint(about)
|
||||
app.register_blueprint(shelf)
|
||||
app.register_blueprint(admi) #
|
||||
app.register_blueprint(remotelogin)
|
||||
app.register_blueprint(meta)
|
||||
app.register_blueprint(gdrive)
|
||||
app.register_blueprint(editbook)
|
||||
if kobo_available:
|
||||
app.register_blueprint(kobo)
|
||||
app.register_blueprint(kobo_auth)
|
||||
if oauth_available:
|
||||
app.register_blueprint(oauth)
|
||||
success = web_server.start()
|
||||
sys.exit(0 if success else 1)
|
|
@ -24,11 +24,10 @@ import mimetypes
|
|||
|
||||
from io import StringIO
|
||||
from email.message import EmailMessage
|
||||
from email.utils import parseaddr
|
||||
|
||||
from email.utils import formatdate, parseaddr
|
||||
from email.generator import Generator
|
||||
from flask_babel import lazy_gettext as N_
|
||||
from email.utils import formatdate
|
||||
from email.generator import Generator
|
||||
|
||||
from cps.services.worker import CalibreTask
|
||||
from cps.services import gmail
|
||||
|
|
|
@ -210,7 +210,7 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{{_('Update')}}</h2>
|
||||
<h2>{{_('Version Information')}}</h2>
|
||||
<table class="table table-striped" id="update_table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -53,7 +53,7 @@ except ImportError:
|
|||
from sqlalchemy.orm import backref, relationship, sessionmaker, Session, scoped_session
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from . import constants, logger, cli
|
||||
from . import constants, logger
|
||||
|
||||
log = logger.create()
|
||||
|
||||
|
@ -816,7 +816,7 @@ def init_db_thread():
|
|||
return Session()
|
||||
|
||||
|
||||
def init_db(app_db_path):
|
||||
def init_db(app_db_path, user_credentials=None):
|
||||
# Open session for database connection
|
||||
global session
|
||||
global app_DB_path
|
||||
|
@ -837,8 +837,8 @@ def init_db(app_db_path):
|
|||
create_admin_user(session)
|
||||
create_anonymous_user(session)
|
||||
|
||||
if cli.user_credentials:
|
||||
username, password = cli.user_credentials.split(':', 1)
|
||||
if user_credentials:
|
||||
username, password = user_credentials.split(':', 1)
|
||||
user = session.query(User).filter(func.lower(User.name) == username.lower()).first()
|
||||
if user:
|
||||
if not password:
|
||||
|
|
|
@ -31,7 +31,7 @@ import requests
|
|||
from babel.dates import format_datetime
|
||||
from flask_babel import gettext as _
|
||||
|
||||
from . import constants, logger, config, web_server
|
||||
from . import constants, logger # config, web_server
|
||||
|
||||
|
||||
log = logger.create()
|
||||
|
@ -58,13 +58,17 @@ class Updater(threading.Thread):
|
|||
self.status = -1
|
||||
self.updateIndex = None
|
||||
|
||||
def init_updater(self, config, web_server):
|
||||
self.config = config
|
||||
self.web_server = web_server
|
||||
|
||||
def get_current_version_info(self):
|
||||
if config.config_updatechannel == constants.UPDATE_STABLE:
|
||||
if self.config.config_updatechannel == constants.UPDATE_STABLE:
|
||||
return self._stable_version_info()
|
||||
return self._nightly_version_info()
|
||||
|
||||
def get_available_updates(self, request_method, locale):
|
||||
if config.config_updatechannel == constants.UPDATE_STABLE:
|
||||
if self.config.config_updatechannel == constants.UPDATE_STABLE:
|
||||
return self._stable_available_updates(request_method)
|
||||
return self._nightly_available_updates(request_method, locale)
|
||||
|
||||
|
@ -95,7 +99,7 @@ class Updater(threading.Thread):
|
|||
self.status = 6
|
||||
log.debug(u'Preparing restart of server')
|
||||
time.sleep(2)
|
||||
web_server.stop(True)
|
||||
self.web_server.stop(True)
|
||||
self.status = 7
|
||||
time.sleep(2)
|
||||
return True
|
||||
|
|
|
@ -54,7 +54,7 @@ install_requires =
|
|||
tornado>=4.1,<6.2
|
||||
Wand>=0.4.4,<0.7.0
|
||||
unidecode>=0.04.19,<1.4.0
|
||||
lxml>=3.8.0,<4.8.0
|
||||
lxml>=3.8.0,<4.9.0
|
||||
flask-wtf>=0.14.2,<1.1.0
|
||||
chardet>=3.0.0,<4.1.0
|
||||
advocate>=1.0.0,<1.1.0
|
||||
|
@ -62,7 +62,7 @@ install_requires =
|
|||
|
||||
[options.extras_require]
|
||||
gdrive =
|
||||
google-api-python-client>=1.7.11,<2.44.0
|
||||
google-api-python-client>=1.7.11,<2.46.0
|
||||
gevent>20.6.0,<22.0.0
|
||||
greenlet>=0.4.17,<1.2.0
|
||||
httplib2>=0.9.2,<0.21.0
|
||||
|
@ -75,7 +75,7 @@ gdrive =
|
|||
rsa>=3.4.2,<4.9.0
|
||||
gmail =
|
||||
google-auth-oauthlib>=0.4.3,<0.6.0
|
||||
google-api-python-client>=1.7.11,<2.44.0
|
||||
google-api-python-client>=1.7.11,<2.46.0
|
||||
goodreads =
|
||||
goodreads>=0.3.2,<0.4.0
|
||||
python-Levenshtein>=0.12.0,<0.13.0
|
||||
|
|
Loading…
Reference in New Issue
Block a user