Merge branch 'master' into Develop
This commit is contained in:
commit
be5c67fddd
|
@ -31,7 +31,10 @@ from flask_babel import gettext as _
|
||||||
|
|
||||||
from . import db, converter, uploader, server, isoLanguages
|
from . import db, converter, uploader, server, isoLanguages
|
||||||
from .web import render_title_template
|
from .web import render_title_template
|
||||||
|
try:
|
||||||
|
from flask_login import __version__ as flask_loginVersion
|
||||||
|
except ImportError:
|
||||||
|
from flask_login.__about__ import __version__ as flask_loginVersion
|
||||||
|
|
||||||
about = flask.Blueprint('about', __name__)
|
about = flask.Blueprint('about', __name__)
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ _VERSIONS = OrderedDict(
|
||||||
Python=sys.version,
|
Python=sys.version,
|
||||||
WebServer=server.VERSION,
|
WebServer=server.VERSION,
|
||||||
Flask=flask.__version__,
|
Flask=flask.__version__,
|
||||||
Flask_Login=flask_login.__version__,
|
Flask_Login=flask_loginVersion,
|
||||||
Flask_Principal=flask_principal.__version__,
|
Flask_Principal=flask_principal.__version__,
|
||||||
Werkzeug=werkzeug.__version__,
|
Werkzeug=werkzeug.__version__,
|
||||||
Babel=babel.__version__,
|
Babel=babel.__version__,
|
||||||
|
|
40
cps/admin.py
40
cps/admin.py
|
@ -45,7 +45,7 @@ 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
|
||||||
|
|
||||||
feature_support = {
|
feature_support = {
|
||||||
'ldap': bool(services.ldap),
|
'ldap': False, # bool(services.ldap),
|
||||||
'goodreads': bool(services.goodreads)
|
'goodreads': bool(services.goodreads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,10 +56,11 @@ feature_support = {
|
||||||
# feature_support['rar'] = False
|
# feature_support['rar'] = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .oauth_bb import oauth_check
|
from .oauth_bb import oauth_check, oauthblueprints
|
||||||
feature_support['oauth'] = True
|
feature_support['oauth'] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
feature_support['oauth'] = False
|
feature_support['oauth'] = False
|
||||||
|
oauthblueprints = []
|
||||||
oauth_check = {}
|
oauth_check = {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -343,18 +344,27 @@ def _configuration_update_helper():
|
||||||
_config_int("config_updatechannel")
|
_config_int("config_updatechannel")
|
||||||
|
|
||||||
# GitHub OAuth configuration
|
# GitHub OAuth configuration
|
||||||
if config.config_login_type == constants.LOGIN_OAUTH_GITHUB:
|
if config.config_login_type == constants.LOGIN_OAUTH:
|
||||||
_config_string("config_github_oauth_client_id")
|
active_oauths = 0
|
||||||
_config_string("config_github_oauth_client_secret")
|
|
||||||
if not config.config_github_oauth_client_id or not config.config_github_oauth_client_secret:
|
|
||||||
return _configuration_result('Please enter Github oauth credentials', gdriveError)
|
|
||||||
|
|
||||||
# Google OAuth configuration
|
for element in oauthblueprints:
|
||||||
if config.config_login_type == constants.LOGIN_OAUTH_GOOGLE:
|
if to_save["config_"+str(element['id'])+"_oauth_client_id"] \
|
||||||
_config_string("config_google_oauth_client_id")
|
and to_save["config_"+str(element['id'])+"_oauth_client_secret"]:
|
||||||
_config_string("config_google_oauth_client_secret")
|
active_oauths += 1
|
||||||
if not config.config_google_oauth_client_id or not config.config_google_oauth_client_secret:
|
element["active"] = 1
|
||||||
return _configuration_result('Please enter Google oauth credentials', gdriveError)
|
ub.session.query(ub.OAuthProvider).filter(ub.OAuthProvider.id == element['id']).update(
|
||||||
|
{"oauth_client_id":to_save["config_"+str(element['id'])+"_oauth_client_id"],
|
||||||
|
"oauth_client_secret":to_save["config_"+str(element['id'])+"_oauth_client_secret"],
|
||||||
|
"active":1})
|
||||||
|
if to_save["config_" + str(element['id']) + "_oauth_client_id"] != element['oauth_client_id'] \
|
||||||
|
or to_save["config_" + str(element['id']) + "_oauth_client_secret"] != element['oauth_client_secret']:
|
||||||
|
reboot_required = True
|
||||||
|
element['oauth_client_id'] = to_save["config_"+str(element['id'])+"_oauth_client_id"]
|
||||||
|
element['oauth_client_secret'] = to_save["config_"+str(element['id'])+"_oauth_client_secret"]
|
||||||
|
else:
|
||||||
|
ub.session.query(ub.OAuthProvider).filter(ub.OAuthProvider.id == element['id']).update(
|
||||||
|
{"active":0})
|
||||||
|
element["active"] = 0
|
||||||
|
|
||||||
_config_int("config_log_level")
|
_config_int("config_log_level")
|
||||||
_config_string("config_logfile")
|
_config_string("config_logfile")
|
||||||
|
@ -410,7 +420,7 @@ def _configuration_result(error_flash=None, gdriveError=None):
|
||||||
flash(_(error_flash), category="error")
|
flash(_(error_flash), category="error")
|
||||||
show_login_button = False
|
show_login_button = False
|
||||||
|
|
||||||
return render_title_template("config_edit.html", config=config,
|
return render_title_template("config_edit.html", config=config, provider=oauthblueprints,
|
||||||
show_back_button=show_back_button, show_login_button=show_login_button,
|
show_back_button=show_back_button, show_login_button=show_login_button,
|
||||||
show_authenticate_google_drive=gdrive_authenticate,
|
show_authenticate_google_drive=gdrive_authenticate,
|
||||||
gdriveError=gdriveError, gdrivefolders=gdrivefolders, feature_support=feature_support,
|
gdriveError=gdriveError, gdrivefolders=gdrivefolders, feature_support=feature_support,
|
||||||
|
@ -653,7 +663,7 @@ def send_logfile(logtype):
|
||||||
@admi.route("/get_update_status", methods=['GET'])
|
@admi.route("/get_update_status", methods=['GET'])
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def get_update_status():
|
def get_update_status():
|
||||||
return updater_thread.get_available_updates(request.method)
|
return updater_thread.get_available_updates(request.method, locale=get_locale())
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/get_updater_status", methods=['GET', 'POST'])
|
@admi.route("/get_updater_status", methods=['GET', 'POST'])
|
||||||
|
|
|
@ -31,7 +31,7 @@ try:
|
||||||
from comicapi.comicarchive import ComicArchive, MetaDataStyle
|
from comicapi.comicarchive import ComicArchive, MetaDataStyle
|
||||||
use_comic_meta = True
|
use_comic_meta = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.warning('cannot import comicapi, extracting comic metadata will not work: %s', e)
|
log.debug('cannot import comicapi, extracting comic metadata will not work: %s', e)
|
||||||
import zipfile
|
import zipfile
|
||||||
import tarfile
|
import tarfile
|
||||||
use_comic_meta = False
|
use_comic_meta = False
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
from __future__ import division, print_function, unicode_literals
|
from __future__ import division, print_function, unicode_literals
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean
|
from sqlalchemy import exc, Column, String, Integer, SmallInteger, Boolean
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
@ -43,41 +44,48 @@ class _Settings(_Base):
|
||||||
mail_login = Column(String, default='mail@example.com')
|
mail_login = Column(String, default='mail@example.com')
|
||||||
mail_password = Column(String, default='mypassword')
|
mail_password = Column(String, default='mypassword')
|
||||||
mail_from = Column(String, default='automailer <mail@example.com>')
|
mail_from = Column(String, default='automailer <mail@example.com>')
|
||||||
|
|
||||||
config_calibre_dir = Column(String)
|
config_calibre_dir = Column(String)
|
||||||
config_port = Column(Integer, default=constants.DEFAULT_PORT)
|
config_port = Column(Integer, default=constants.DEFAULT_PORT)
|
||||||
config_certfile = Column(String)
|
config_certfile = Column(String)
|
||||||
config_keyfile = Column(String)
|
config_keyfile = Column(String)
|
||||||
|
|
||||||
config_calibre_web_title = Column(String, default=u'Calibre-Web')
|
config_calibre_web_title = Column(String, default=u'Calibre-Web')
|
||||||
config_books_per_page = Column(Integer, default=60)
|
config_books_per_page = Column(Integer, default=60)
|
||||||
config_random_books = Column(Integer, default=4)
|
config_random_books = Column(Integer, default=4)
|
||||||
config_authors_max = Column(Integer, default=0)
|
config_authors_max = Column(Integer, default=0)
|
||||||
config_read_column = Column(Integer, default=0)
|
config_read_column = Column(Integer, default=0)
|
||||||
config_title_regex = Column(String, default=r'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
config_title_regex = Column(String, default=u'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
||||||
|
config_mature_content_tags = Column(String, default='')
|
||||||
|
config_theme = Column(Integer, default=0)
|
||||||
|
|
||||||
config_log_level = Column(SmallInteger, default=logger.DEFAULT_LOG_LEVEL)
|
config_log_level = Column(SmallInteger, default=logger.DEFAULT_LOG_LEVEL)
|
||||||
|
config_logfile = Column(String)
|
||||||
config_access_log = Column(SmallInteger, default=0)
|
config_access_log = Column(SmallInteger, default=0)
|
||||||
|
config_access_logfile = Column(String)
|
||||||
|
|
||||||
config_uploading = Column(SmallInteger, default=0)
|
config_uploading = Column(SmallInteger, default=0)
|
||||||
config_anonbrowse = Column(SmallInteger, default=0)
|
config_anonbrowse = Column(SmallInteger, default=0)
|
||||||
config_public_reg = Column(SmallInteger, default=0)
|
config_public_reg = Column(SmallInteger, default=0)
|
||||||
|
config_remote_login = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
|
||||||
config_default_role = Column(SmallInteger, default=0)
|
config_default_role = Column(SmallInteger, default=0)
|
||||||
config_default_show = Column(SmallInteger, default=6143)
|
config_default_show = Column(SmallInteger, default=6143)
|
||||||
config_columns_to_ignore = Column(String)
|
config_columns_to_ignore = Column(String)
|
||||||
|
|
||||||
config_use_google_drive = Column(Boolean, default=False)
|
config_use_google_drive = Column(Boolean, default=False)
|
||||||
config_google_drive_folder = Column(String)
|
config_google_drive_folder = Column(String)
|
||||||
config_google_drive_watch_changes_response = Column(String)
|
config_google_drive_watch_changes_response = Column(String)
|
||||||
config_remote_login = Column(Boolean, default=False)
|
|
||||||
config_use_goodreads = Column(Boolean, default=False)
|
config_use_goodreads = Column(Boolean, default=False)
|
||||||
config_goodreads_api_key = Column(String)
|
config_goodreads_api_key = Column(String)
|
||||||
config_goodreads_api_secret = Column(String)
|
config_goodreads_api_secret = Column(String)
|
||||||
|
|
||||||
config_login_type = Column(Integer, default=0)
|
config_login_type = Column(Integer, default=0)
|
||||||
# config_use_ldap = Column(Boolean)
|
|
||||||
config_ldap_provider_url = Column(String)
|
# config_oauth_provider = Column(Integer)
|
||||||
config_ldap_dn = Column(String)
|
|
||||||
# config_use_github_oauth = Column(Boolean)
|
|
||||||
config_github_oauth_client_id = Column(String)
|
|
||||||
config_github_oauth_client_secret = Column(String)
|
|
||||||
# config_use_google_oauth = Column(Boolean)
|
|
||||||
config_google_oauth_client_id = Column(String)
|
|
||||||
config_google_oauth_client_secret = Column(String)
|
|
||||||
config_ldap_provider_url = Column(String, default='localhost')
|
config_ldap_provider_url = Column(String, default='localhost')
|
||||||
config_ldap_port = Column(SmallInteger, default=389)
|
config_ldap_port = Column(SmallInteger, default=389)
|
||||||
config_ldap_schema = Column(String, default='ldap')
|
config_ldap_schema = Column(String, default='ldap')
|
||||||
|
@ -90,14 +98,12 @@ class _Settings(_Base):
|
||||||
config_ldap_dn = Column(String)
|
config_ldap_dn = Column(String)
|
||||||
config_ldap_user_object = Column(String)
|
config_ldap_user_object = Column(String)
|
||||||
config_ldap_openldap = Column(Boolean, default=False)
|
config_ldap_openldap = Column(Boolean, default=False)
|
||||||
config_mature_content_tags = Column(String, default='')
|
|
||||||
config_logfile = Column(String)
|
|
||||||
config_access_logfile = Column(String)
|
|
||||||
config_ebookconverter = Column(Integer, default=0)
|
config_ebookconverter = Column(Integer, default=0)
|
||||||
config_converterpath = Column(String)
|
config_converterpath = Column(String)
|
||||||
config_calibre = Column(String)
|
config_calibre = Column(String)
|
||||||
config_rarfile_location = Column(String)
|
config_rarfile_location = Column(String)
|
||||||
config_theme = Column(Integer, default=0)
|
|
||||||
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
|
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -106,6 +112,7 @@ class _Settings(_Base):
|
||||||
|
|
||||||
# Class holds all application specific settings in calibre-web
|
# Class holds all application specific settings in calibre-web
|
||||||
class _ConfigSQL(object):
|
class _ConfigSQL(object):
|
||||||
|
# pylint: disable=no-member
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
self._session = session
|
self._session = session
|
||||||
self._settings = None
|
self._settings = None
|
||||||
|
@ -264,8 +271,15 @@ def _migrate_table(session, orm_class):
|
||||||
session.query(column).first()
|
session.query(column).first()
|
||||||
except exc.OperationalError as err:
|
except exc.OperationalError as err:
|
||||||
log.debug("%s: %s", column_name, err)
|
log.debug("%s: %s", column_name, err)
|
||||||
|
if column.default is not None:
|
||||||
|
if sys.version_info < (3, 0):
|
||||||
|
if isinstance(column.default.arg,unicode):
|
||||||
|
column.default.arg = column.default.arg.encode('utf-8')
|
||||||
column_default = "" if column.default is None else ("DEFAULT %r" % column.default.arg)
|
column_default = "" if column.default is None else ("DEFAULT %r" % column.default.arg)
|
||||||
alter_table = "ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__, column_name, column.type, column_default)
|
alter_table = "ALTER TABLE %s ADD COLUMN `%s` %s %s" % (orm_class.__tablename__,
|
||||||
|
column_name,
|
||||||
|
column.type,
|
||||||
|
column_default)
|
||||||
log.debug(alter_table)
|
log.debug(alter_table)
|
||||||
session.execute(alter_table)
|
session.execute(alter_table)
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -273,6 +287,17 @@ def _migrate_table(session, orm_class):
|
||||||
if changed:
|
if changed:
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
def autodetect_calibre_binary():
|
||||||
|
if sys.platform == "win32":
|
||||||
|
calibre_path = ["C:\\program files\calibre\calibre-convert.exe",
|
||||||
|
"C:\\program files(x86)\calibre\calibre-convert.exe"]
|
||||||
|
else:
|
||||||
|
calibre_path = ["/opt/calibre/ebook-convert"]
|
||||||
|
for element in calibre_path:
|
||||||
|
if os.path.isfile(element) and os.access(element, os.X_OK):
|
||||||
|
return element
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _migrate_database(session):
|
def _migrate_database(session):
|
||||||
# make sure the table is created, if it does not exist
|
# make sure the table is created, if it does not exist
|
||||||
|
|
|
@ -21,6 +21,7 @@ import sys
|
||||||
import os
|
import os
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
HOME_CONFIG = False
|
||||||
|
|
||||||
# Base dir is parent of current file, necessary if called from different folder
|
# Base dir is parent of current file, necessary if called from different folder
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
|
@ -32,6 +33,13 @@ else:
|
||||||
STATIC_DIR = os.path.join(BASE_DIR, 'cps', 'static')
|
STATIC_DIR = os.path.join(BASE_DIR, 'cps', 'static')
|
||||||
TEMPLATES_DIR = os.path.join(BASE_DIR, 'cps', 'templates')
|
TEMPLATES_DIR = os.path.join(BASE_DIR, 'cps', 'templates')
|
||||||
TRANSLATIONS_DIR = os.path.join(BASE_DIR, 'cps', 'translations')
|
TRANSLATIONS_DIR = os.path.join(BASE_DIR, 'cps', 'translations')
|
||||||
|
|
||||||
|
if HOME_CONFIG:
|
||||||
|
home_dir = os.path.join(os.path.expanduser("~"),".calibre-web")
|
||||||
|
if not os.path.exists(home_dir):
|
||||||
|
os.makedirs(home_dir)
|
||||||
|
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', home_dir)
|
||||||
|
else:
|
||||||
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', BASE_DIR)
|
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', BASE_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,8 +91,8 @@ AUTO_UPDATE_NIGHTLY = 1 << 2
|
||||||
|
|
||||||
LOGIN_STANDARD = 0
|
LOGIN_STANDARD = 0
|
||||||
LOGIN_LDAP = 1
|
LOGIN_LDAP = 1
|
||||||
LOGIN_OAUTH_GITHUB = 2
|
LOGIN_OAUTH = 2
|
||||||
LOGIN_OAUTH_GOOGLE = 3
|
# LOGIN_OAUTH_GOOGLE = 3
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PASSWORD = "admin123"
|
DEFAULT_PASSWORD = "admin123"
|
||||||
|
@ -116,7 +124,7 @@ def selected_roles(dictionary):
|
||||||
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
||||||
'series_id, languages')
|
'series_id, languages')
|
||||||
|
|
||||||
STABLE_VERSION = {'version': '0.6.4 Beta'}
|
STABLE_VERSION = {'version': '0.6.5 Beta'}
|
||||||
|
|
||||||
NIGHTLY_VERSION = {}
|
NIGHTLY_VERSION = {}
|
||||||
NIGHTLY_VERSION[0] = '$Format:%H$'
|
NIGHTLY_VERSION[0] = '$Format:%H$'
|
||||||
|
|
|
@ -39,12 +39,12 @@ except ImportError:
|
||||||
gdrive_support = False
|
gdrive_support = False
|
||||||
|
|
||||||
from . import logger, cli, config
|
from . import logger, cli, config
|
||||||
from .constants import BASE_DIR as _BASE_DIR
|
from .constants import CONFIG_DIR as _CONFIG_DIR
|
||||||
|
|
||||||
|
|
||||||
SETTINGS_YAML = os.path.join(_BASE_DIR, 'settings.yaml')
|
SETTINGS_YAML = os.path.join(_CONFIG_DIR, 'settings.yaml')
|
||||||
CREDENTIALS = os.path.join(_BASE_DIR, 'gdrive_credentials')
|
CREDENTIALS = os.path.join(_CONFIG_DIR, 'gdrive_credentials')
|
||||||
CLIENT_SECRETS = os.path.join(_BASE_DIR, 'client_secrets.json')
|
CLIENT_SECRETS = os.path.join(_CONFIG_DIR, 'client_secrets.json')
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
|
|
@ -71,14 +71,6 @@ from .worker import TASK_EMAIL, TASK_CONVERT, TASK_UPLOAD, TASK_CONVERT_ANY
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
def update_download(book_id, user_id):
|
|
||||||
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
|
|
||||||
book_id).first()
|
|
||||||
if not check:
|
|
||||||
new_download = ub.Downloads(user_id=user_id, book_id=book_id)
|
|
||||||
ub.session.add(new_download)
|
|
||||||
ub.session.commit()
|
|
||||||
|
|
||||||
# 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 = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||||
|
@ -563,6 +555,7 @@ def check_unrar(unrarLocation):
|
||||||
try:
|
try:
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
unrarLocation = unrarLocation.encode(sys.getfilesystemencoding())
|
unrarLocation = unrarLocation.encode(sys.getfilesystemencoding())
|
||||||
|
unrarLocation = [unrarLocation]
|
||||||
for lines in process_wait(unrarLocation):
|
for lines in process_wait(unrarLocation):
|
||||||
value = re.search('UNRAR (.*) freeware', lines)
|
value = re.search('UNRAR (.*) freeware', lines)
|
||||||
if value:
|
if value:
|
||||||
|
|
|
@ -23,7 +23,7 @@ import logging
|
||||||
from logging import Formatter, StreamHandler
|
from logging import Formatter, StreamHandler
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
from .constants import BASE_DIR as _BASE_DIR
|
from .constants import CONFIG_DIR as _CONFIG_DIR
|
||||||
|
|
||||||
|
|
||||||
ACCESS_FORMATTER_GEVENT = Formatter("%(message)s")
|
ACCESS_FORMATTER_GEVENT = Formatter("%(message)s")
|
||||||
|
@ -31,8 +31,8 @@ ACCESS_FORMATTER_TORNADO = Formatter("[%(asctime)s] %(message)s")
|
||||||
|
|
||||||
FORMATTER = Formatter("[%(asctime)s] %(levelname)5s {%(name)s:%(lineno)d} %(message)s")
|
FORMATTER = Formatter("[%(asctime)s] %(levelname)5s {%(name)s:%(lineno)d} %(message)s")
|
||||||
DEFAULT_LOG_LEVEL = logging.INFO
|
DEFAULT_LOG_LEVEL = logging.INFO
|
||||||
DEFAULT_LOG_FILE = os.path.join(_BASE_DIR, "calibre-web.log")
|
DEFAULT_LOG_FILE = os.path.join(_CONFIG_DIR, "calibre-web.log")
|
||||||
DEFAULT_ACCESS_LOG = os.path.join(_BASE_DIR, "access.log")
|
DEFAULT_ACCESS_LOG = os.path.join(_CONFIG_DIR, "access.log")
|
||||||
LOG_TO_STDERR = '/dev/stderr'
|
LOG_TO_STDERR = '/dev/stderr'
|
||||||
|
|
||||||
logging.addLevelName(logging.WARNING, "WARN")
|
logging.addLevelName(logging.WARNING, "WARN")
|
||||||
|
@ -76,7 +76,7 @@ def is_valid_logfile(file_path):
|
||||||
def _absolute_log_file(log_file, default_log_file):
|
def _absolute_log_file(log_file, default_log_file):
|
||||||
if log_file:
|
if log_file:
|
||||||
if not os.path.dirname(log_file):
|
if not os.path.dirname(log_file):
|
||||||
log_file = os.path.join(_BASE_DIR, log_file)
|
log_file = os.path.join(_CONFIG_DIR, log_file)
|
||||||
return os.path.abspath(log_file)
|
return os.path.abspath(log_file)
|
||||||
|
|
||||||
return default_log_file
|
return default_log_file
|
||||||
|
@ -95,6 +95,11 @@ def setup(log_file, log_level=None):
|
||||||
Configure the logging output.
|
Configure the logging output.
|
||||||
May be called multiple times.
|
May be called multiple times.
|
||||||
'''
|
'''
|
||||||
|
# if debugging, start logging to stderr immediately
|
||||||
|
if os.environ.get('FLASK_DEBUG', None):
|
||||||
|
log_file = LOG_TO_STDERR
|
||||||
|
log_level = logging.DEBUG
|
||||||
|
|
||||||
log_file = _absolute_log_file(log_file, DEFAULT_LOG_FILE)
|
log_file = _absolute_log_file(log_file, DEFAULT_LOG_FILE)
|
||||||
|
|
||||||
log_level = log_level or DEFAULT_LOG_LEVEL
|
log_level = log_level or DEFAULT_LOG_LEVEL
|
||||||
|
@ -162,8 +167,3 @@ class StderrLogger(object):
|
||||||
self.buffer += message
|
self.buffer += message
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.debug("Logging Error")
|
self.log.debug("Logging Error")
|
||||||
|
|
||||||
|
|
||||||
# if debugging, start logging to stderr immediately
|
|
||||||
if os.environ.get('FLASK_DEBUG', None):
|
|
||||||
setup(LOG_TO_STDERR, logging.DEBUG)
|
|
||||||
|
|
33
cps/oauth.py
33
cps/oauth.py
|
@ -1,6 +1,22 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 2018-2019 jim3ma
|
||||||
|
#
|
||||||
|
# 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/>
|
||||||
|
|
||||||
from __future__ import division, print_function, unicode_literals
|
from __future__ import division, print_function, unicode_literals
|
||||||
from flask import session
|
from flask import session
|
||||||
|
|
||||||
|
@ -16,13 +32,14 @@ try:
|
||||||
|
|
||||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||||
"""
|
"""
|
||||||
def __init__(self, model, session,
|
def __init__(self, model, session, provider_id,
|
||||||
user=None, user_id=None, user_required=None, anon_user=None,
|
user=None, user_id=None, user_required=None, anon_user=None,
|
||||||
cache=None):
|
cache=None):
|
||||||
|
self.provider_id = provider_id
|
||||||
super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache)
|
super(OAuthBackend, self).__init__(model, session, user, user_id, user_required, anon_user, cache)
|
||||||
|
|
||||||
def get(self, blueprint, user=None, user_id=None):
|
def get(self, blueprint, user=None, user_id=None):
|
||||||
if blueprint.name + '_oauth_token' in session and session[blueprint.name + '_oauth_token'] != '':
|
if self.provider_id + '_oauth_token' in session and session[self.provider_id + '_oauth_token'] != '':
|
||||||
return session[blueprint.name + '_oauth_token']
|
return session[blueprint.name + '_oauth_token']
|
||||||
# check cache
|
# check cache
|
||||||
cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id)
|
cache_key = self.make_cache_key(blueprint=blueprint, user=user, user_id=user_id)
|
||||||
|
@ -33,15 +50,15 @@ try:
|
||||||
# if not cached, make database queries
|
# if not cached, make database queries
|
||||||
query = (
|
query = (
|
||||||
self.session.query(self.model)
|
self.session.query(self.model)
|
||||||
.filter_by(provider=blueprint.name)
|
.filter_by(provider=self.provider_id)
|
||||||
)
|
)
|
||||||
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
||||||
u = first(_get_real_user(ref, self.anon_user)
|
u = first(_get_real_user(ref, self.anon_user)
|
||||||
for ref in (user, self.user, blueprint.config.get("user")))
|
for ref in (user, self.user, blueprint.config.get("user")))
|
||||||
|
|
||||||
use_provider_user_id = False
|
use_provider_user_id = False
|
||||||
if blueprint.name + '_oauth_user_id' in session and session[blueprint.name + '_oauth_user_id'] != '':
|
if self.provider_id + '_oauth_user_id' in session and session[self.provider_id + '_oauth_user_id'] != '':
|
||||||
query = query.filter_by(provider_user_id=session[blueprint.name + '_oauth_user_id'])
|
query = query.filter_by(provider_user_id=session[self.provider_id + '_oauth_user_id'])
|
||||||
use_provider_user_id = True
|
use_provider_user_id = True
|
||||||
|
|
||||||
if self.user_required and not u and not uid and not use_provider_user_id:
|
if self.user_required and not u and not uid and not use_provider_user_id:
|
||||||
|
@ -78,7 +95,7 @@ try:
|
||||||
# if there was an existing model, delete it
|
# if there was an existing model, delete it
|
||||||
existing_query = (
|
existing_query = (
|
||||||
self.session.query(self.model)
|
self.session.query(self.model)
|
||||||
.filter_by(provider=blueprint.name)
|
.filter_by(provider=self.provider_id)
|
||||||
)
|
)
|
||||||
# check for user ID
|
# check for user ID
|
||||||
has_user_id = hasattr(self.model, "user_id")
|
has_user_id = hasattr(self.model, "user_id")
|
||||||
|
@ -92,7 +109,7 @@ try:
|
||||||
existing_query.delete()
|
existing_query.delete()
|
||||||
# create a new model for this token
|
# create a new model for this token
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"provider": blueprint.name,
|
"provider": self.provider_id,
|
||||||
"token": token,
|
"token": token,
|
||||||
}
|
}
|
||||||
if has_user_id and uid:
|
if has_user_id and uid:
|
||||||
|
@ -110,7 +127,7 @@ try:
|
||||||
def delete(self, blueprint, user=None, user_id=None):
|
def delete(self, blueprint, user=None, user_id=None):
|
||||||
query = (
|
query = (
|
||||||
self.session.query(self.model)
|
self.session.query(self.model)
|
||||||
.filter_by(provider=blueprint.name)
|
.filter_by(provider=self.provider_id)
|
||||||
)
|
)
|
||||||
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
uid = first([user_id, self.user_id, blueprint.config.get("user_id")])
|
||||||
u = first(_get_real_user(ref, self.anon_user)
|
u = first(_get_real_user(ref, self.anon_user)
|
||||||
|
|
143
cps/oauth_bb.py
143
cps/oauth_bb.py
|
@ -45,10 +45,10 @@ oauth = Blueprint('oauth', __name__)
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
def github_oauth_required(f):
|
def oauth_required(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
if config.config_login_type == constants.LOGIN_OAUTH_GITHUB:
|
if config.config_login_type == constants.LOGIN_OAUTH:
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
if request.is_xhr:
|
if request.is_xhr:
|
||||||
data = {'status': 'error', 'message': 'Not Found'}
|
data = {'status': 'error', 'message': 'Not Found'}
|
||||||
|
@ -60,30 +60,14 @@ def github_oauth_required(f):
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def google_oauth_required(f):
|
def register_oauth_blueprint(id, show_name):
|
||||||
@wraps(f)
|
oauth_check[id] = show_name
|
||||||
def inner(*args, **kwargs):
|
|
||||||
if config.config_use_google_oauth == constants.LOGIN_OAUTH_GOOGLE:
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
if request.is_xhr:
|
|
||||||
data = {'status': 'error', 'message': 'Not Found'}
|
|
||||||
response = make_response(json.dumps(data, ensure_ascii=False))
|
|
||||||
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
|
||||||
return response, 404
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
def register_oauth_blueprint(blueprint, show_name):
|
|
||||||
if blueprint.name != "":
|
|
||||||
oauth_check[blueprint.name] = show_name
|
|
||||||
|
|
||||||
|
|
||||||
def register_user_with_oauth(user=None):
|
def register_user_with_oauth(user=None):
|
||||||
all_oauth = {}
|
all_oauth = {}
|
||||||
for oauth in oauth_check.keys():
|
for oauth in oauth_check.keys():
|
||||||
if oauth + '_oauth_user_id' in session and session[oauth + '_oauth_user_id'] != '':
|
if str(oauth) + '_oauth_user_id' in session and session[str(oauth) + '_oauth_user_id'] != '':
|
||||||
all_oauth[oauth] = oauth_check[oauth]
|
all_oauth[oauth] = oauth_check[oauth]
|
||||||
if len(all_oauth.keys()) == 0:
|
if len(all_oauth.keys()) == 0:
|
||||||
return
|
return
|
||||||
|
@ -94,7 +78,7 @@ def register_user_with_oauth(user=None):
|
||||||
# Find this OAuth token in the database, or create it
|
# Find this OAuth token in the database, or create it
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
provider=oauth,
|
provider=oauth,
|
||||||
provider_user_id=session[oauth + "_oauth_user_id"],
|
provider_user_id=session[str(oauth) + "_oauth_user_id"],
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
oauth = query.one()
|
oauth = query.one()
|
||||||
|
@ -111,39 +95,61 @@ def register_user_with_oauth(user=None):
|
||||||
|
|
||||||
def logout_oauth_user():
|
def logout_oauth_user():
|
||||||
for oauth in oauth_check.keys():
|
for oauth in oauth_check.keys():
|
||||||
if oauth + '_oauth_user_id' in session:
|
if str(oauth) + '_oauth_user_id' in session:
|
||||||
session.pop(oauth + '_oauth_user_id')
|
session.pop(str(oauth) + '_oauth_user_id')
|
||||||
|
|
||||||
if ub.oauth_support:
|
if ub.oauth_support:
|
||||||
github_blueprint = make_github_blueprint(
|
oauthblueprints =[]
|
||||||
client_id=config.config_github_oauth_client_id,
|
if not ub.session.query(ub.OAuthProvider).count():
|
||||||
client_secret=config.config_github_oauth_client_secret,
|
oauth = ub.OAuthProvider()
|
||||||
redirect_to="oauth.github_login",)
|
oauth.provider_name = "github"
|
||||||
|
oauth.active = False
|
||||||
|
ub.session.add(oauth)
|
||||||
|
ub.session.commit()
|
||||||
|
oauth = ub.OAuthProvider()
|
||||||
|
oauth.provider_name = "google"
|
||||||
|
oauth.active = False
|
||||||
|
ub.session.add(oauth)
|
||||||
|
ub.session.commit()
|
||||||
|
|
||||||
google_blueprint = make_google_blueprint(
|
oauth_ids = ub.session.query(ub.OAuthProvider).all()
|
||||||
client_id=config.config_google_oauth_client_id,
|
ele1=dict(provider_name='github',
|
||||||
client_secret=config.config_google_oauth_client_secret,
|
id=oauth_ids[0].id,
|
||||||
redirect_to="oauth.google_login",
|
active=oauth_ids[0].active,
|
||||||
scope=[
|
oauth_client_id=oauth_ids[0].oauth_client_id,
|
||||||
"https://www.googleapis.com/auth/plus.me",
|
scope=None,
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
oauth_client_secret=oauth_ids[0].oauth_client_secret,
|
||||||
]
|
obtain_link='https://github.com/settings/developers')
|
||||||
|
ele2=dict(provider_name='google',
|
||||||
|
id=oauth_ids[1].id,
|
||||||
|
active=oauth_ids[1].active,
|
||||||
|
scope=["https://www.googleapis.com/auth/plus.me", "https://www.googleapis.com/auth/userinfo.email"],
|
||||||
|
oauth_client_id=oauth_ids[1].oauth_client_id,
|
||||||
|
oauth_client_secret=oauth_ids[1].oauth_client_secret,
|
||||||
|
obtain_link='https://github.com/settings/developers')
|
||||||
|
oauthblueprints.append(ele1)
|
||||||
|
oauthblueprints.append(ele2)
|
||||||
|
|
||||||
|
for element in oauthblueprints:
|
||||||
|
if element['provider_name'] == 'github':
|
||||||
|
blueprint_func = make_github_blueprint
|
||||||
|
else:
|
||||||
|
blueprint_func = make_google_blueprint
|
||||||
|
blueprint = blueprint_func(
|
||||||
|
client_id=element['oauth_client_id'],
|
||||||
|
client_secret=element['oauth_client_secret'],
|
||||||
|
redirect_to="oauth."+element['provider_name']+"_login",
|
||||||
|
scope = element['scope']
|
||||||
)
|
)
|
||||||
|
element['blueprint']=blueprint
|
||||||
app.register_blueprint(google_blueprint, url_prefix="/login")
|
app.register_blueprint(blueprint, url_prefix="/login")
|
||||||
app.register_blueprint(github_blueprint, url_prefix='/login')
|
element['blueprint'].backend = OAuthBackend(ub.OAuth, ub.session, str(element['id']),
|
||||||
|
user=current_user, user_required=True)
|
||||||
github_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
if element['active']:
|
||||||
google_blueprint.backend = OAuthBackend(ub.OAuth, ub.session, user=current_user, user_required=True)
|
register_oauth_blueprint(element['id'], element['provider_name'])
|
||||||
|
|
||||||
|
|
||||||
if config.config_login_type == constants.LOGIN_OAUTH_GITHUB:
|
@oauth_authorized.connect_via(oauthblueprints[0]['blueprint'])
|
||||||
register_oauth_blueprint(github_blueprint, 'GitHub')
|
|
||||||
if config.config_login_type == constants.LOGIN_OAUTH_GOOGLE:
|
|
||||||
register_oauth_blueprint(google_blueprint, 'Google')
|
|
||||||
|
|
||||||
|
|
||||||
@oauth_authorized.connect_via(github_blueprint)
|
|
||||||
def github_logged_in(blueprint, token):
|
def github_logged_in(blueprint, token):
|
||||||
if not token:
|
if not token:
|
||||||
flash(_(u"Failed to log in with GitHub."), category="error")
|
flash(_(u"Failed to log in with GitHub."), category="error")
|
||||||
|
@ -156,10 +162,10 @@ if ub.oauth_support:
|
||||||
|
|
||||||
github_info = resp.json()
|
github_info = resp.json()
|
||||||
github_user_id = str(github_info["id"])
|
github_user_id = str(github_info["id"])
|
||||||
return oauth_update_token(blueprint, token, github_user_id)
|
return oauth_update_token(str(oauthblueprints[0]['id']), token, github_user_id)
|
||||||
|
|
||||||
|
|
||||||
@oauth_authorized.connect_via(google_blueprint)
|
@oauth_authorized.connect_via(oauthblueprints[1]['blueprint'])
|
||||||
def google_logged_in(blueprint, token):
|
def google_logged_in(blueprint, token):
|
||||||
if not token:
|
if not token:
|
||||||
flash(_(u"Failed to log in with Google."), category="error")
|
flash(_(u"Failed to log in with Google."), category="error")
|
||||||
|
@ -172,17 +178,16 @@ if ub.oauth_support:
|
||||||
|
|
||||||
google_info = resp.json()
|
google_info = resp.json()
|
||||||
google_user_id = str(google_info["id"])
|
google_user_id = str(google_info["id"])
|
||||||
|
return oauth_update_token(str(oauthblueprints[1]['id']), token, google_user_id)
|
||||||
return oauth_update_token(blueprint, token, google_user_id)
|
|
||||||
|
|
||||||
|
|
||||||
def oauth_update_token(blueprint, token, provider_user_id):
|
def oauth_update_token(provider_id, token, provider_user_id):
|
||||||
session[blueprint.name + "_oauth_user_id"] = provider_user_id
|
session[provider_id + "_oauth_user_id"] = provider_user_id
|
||||||
session[blueprint.name + "_oauth_token"] = token
|
session[provider_id + "_oauth_token"] = token
|
||||||
|
|
||||||
# Find this OAuth token in the database, or create it
|
# Find this OAuth token in the database, or create it
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
provider=blueprint.name,
|
provider=provider_id,
|
||||||
provider_user_id=provider_user_id,
|
provider_user_id=provider_user_id,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
@ -191,7 +196,7 @@ if ub.oauth_support:
|
||||||
oauth.token = token
|
oauth.token = token
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
oauth = ub.OAuth(
|
oauth = ub.OAuth(
|
||||||
provider=blueprint.name,
|
provider=provider_id,
|
||||||
provider_user_id=provider_user_id,
|
provider_user_id=provider_user_id,
|
||||||
token=token,
|
token=token,
|
||||||
)
|
)
|
||||||
|
@ -206,9 +211,9 @@ if ub.oauth_support:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def bind_oauth_or_register(provider, provider_user_id, redirect_url):
|
def bind_oauth_or_register(provider_id, provider_user_id, redirect_url):
|
||||||
query = ub.session.query(ub.OAuth).filter_by(
|
query = ub.session.query(ub.OAuth).filter_by(
|
||||||
provider=provider,
|
provider=provider_id,
|
||||||
provider_user_id=provider_user_id,
|
provider_user_id=provider_user_id,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
@ -245,7 +250,7 @@ if ub.oauth_support:
|
||||||
try:
|
try:
|
||||||
oauths = query.all()
|
oauths = query.all()
|
||||||
for oauth in oauths:
|
for oauth in oauths:
|
||||||
status.append(oauth.provider)
|
status.append(int(oauth.provider))
|
||||||
return status
|
return status
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
return None
|
return None
|
||||||
|
@ -278,7 +283,7 @@ if ub.oauth_support:
|
||||||
|
|
||||||
|
|
||||||
# notify on OAuth provider error
|
# notify on OAuth provider error
|
||||||
@oauth_error.connect_via(github_blueprint)
|
@oauth_error.connect_via(oauthblueprints[0]['blueprint'])
|
||||||
def github_error(blueprint, error, error_description=None, error_uri=None):
|
def github_error(blueprint, error, error_description=None, error_uri=None):
|
||||||
msg = (
|
msg = (
|
||||||
u"OAuth error from {name}! "
|
u"OAuth error from {name}! "
|
||||||
|
@ -293,14 +298,14 @@ if ub.oauth_support:
|
||||||
|
|
||||||
|
|
||||||
@oauth.route('/github')
|
@oauth.route('/github')
|
||||||
@github_oauth_required
|
@oauth_required
|
||||||
def github_login():
|
def github_login():
|
||||||
if not github.authorized:
|
if not github.authorized:
|
||||||
return redirect(url_for('github.login'))
|
return redirect(url_for('github.login'))
|
||||||
account_info = github.get('/user')
|
account_info = github.get('/user')
|
||||||
if account_info.ok:
|
if account_info.ok:
|
||||||
account_info_json = account_info.json()
|
account_info_json = account_info.json()
|
||||||
return bind_oauth_or_register(github_blueprint.name, account_info_json['id'], 'github.login')
|
return bind_oauth_or_register(oauthblueprints[0]['id'], account_info_json['id'], 'github.login')
|
||||||
flash(_(u"GitHub Oauth error, please retry later."), category="error")
|
flash(_(u"GitHub Oauth error, please retry later."), category="error")
|
||||||
return redirect(url_for('web.login'))
|
return redirect(url_for('web.login'))
|
||||||
|
|
||||||
|
@ -308,23 +313,23 @@ if ub.oauth_support:
|
||||||
@oauth.route('/unlink/github', methods=["GET"])
|
@oauth.route('/unlink/github', methods=["GET"])
|
||||||
@login_required
|
@login_required
|
||||||
def github_login_unlink():
|
def github_login_unlink():
|
||||||
return unlink_oauth(github_blueprint.name)
|
return unlink_oauth(oauthblueprints[0]['id'])
|
||||||
|
|
||||||
|
|
||||||
@oauth.route('/login/google')
|
@oauth.route('/login/google')
|
||||||
@google_oauth_required
|
@oauth_required
|
||||||
def google_login():
|
def google_login():
|
||||||
if not google.authorized:
|
if not google.authorized:
|
||||||
return redirect(url_for("google.login"))
|
return redirect(url_for("google.login"))
|
||||||
resp = google.get("/oauth2/v2/userinfo")
|
resp = google.get("/oauth2/v2/userinfo")
|
||||||
if resp.ok:
|
if resp.ok:
|
||||||
account_info_json = resp.json()
|
account_info_json = resp.json()
|
||||||
return bind_oauth_or_register(google_blueprint.name, account_info_json['id'], 'google.login')
|
return bind_oauth_or_register(oauthblueprints[1]['id'], account_info_json['id'], 'google.login')
|
||||||
flash(_(u"Google Oauth error, please retry later."), category="error")
|
flash(_(u"Google Oauth error, please retry later."), category="error")
|
||||||
return redirect(url_for('web.login'))
|
return redirect(url_for('web.login'))
|
||||||
|
|
||||||
|
|
||||||
@oauth_error.connect_via(google_blueprint)
|
@oauth_error.connect_via(oauthblueprints[1]['blueprint'])
|
||||||
def google_error(blueprint, error, error_description=None, error_uri=None):
|
def google_error(blueprint, error, error_description=None, error_uri=None):
|
||||||
msg = (
|
msg = (
|
||||||
u"OAuth error from {name}! "
|
u"OAuth error from {name}! "
|
||||||
|
@ -341,4 +346,4 @@ if ub.oauth_support:
|
||||||
@oauth.route('/unlink/google', methods=["GET"])
|
@oauth.route('/unlink/google', methods=["GET"])
|
||||||
@login_required
|
@login_required
|
||||||
def google_login_unlink():
|
def google_login_unlink():
|
||||||
return unlink_oauth(google_blueprint.name)
|
return unlink_oauth(oauthblueprints[1]['blueprint'].name)
|
||||||
|
|
|
@ -47,8 +47,6 @@ def init_app(app, config):
|
||||||
app.config['LDAP_USE_TLS'] = bool(config.config_ldap_use_tls)
|
app.config['LDAP_USE_TLS'] = bool(config.config_ldap_use_tls)
|
||||||
app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap)
|
app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap)
|
||||||
|
|
||||||
# app.config['LDAP_BASE_DN'] = 'ou=users,dc=yunohost,dc=org'
|
|
||||||
# app.config['LDAP_USER_OBJECT_FILTER'] = '(uid=%s)'
|
|
||||||
_ldap.init_app(app)
|
_ldap.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ input.pill:not(:checked) + label .glyphicon {
|
||||||
|
|
||||||
.filterheader { margin-bottom: 20px; }
|
.filterheader { margin-bottom: 20px; }
|
||||||
|
|
||||||
|
.errorlink {margin-top: 20px;}
|
||||||
.modal-body .comments {
|
.modal-body .comments {
|
||||||
max-height:300px;
|
max-height:300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
|
@ -37,10 +37,11 @@ $(document).on("change", "select[data-control]", function() {
|
||||||
var showOrHide = parseInt($this.val());
|
var showOrHide = parseInt($this.val());
|
||||||
// var showOrHideLast = $("#" + name + " option:last").val()
|
// var showOrHideLast = $("#" + name + " option:last").val()
|
||||||
for (var i = 0; i < $(this)[0].length; i++) {
|
for (var i = 0; i < $(this)[0].length; i++) {
|
||||||
if (parseInt($(this)[0][i].value) === showOrHide) {
|
var element = parseInt($(this)[0][i].value);
|
||||||
$("[data-related=\"" + name + "-" + i + "\"]").show();
|
if (element === showOrHide) {
|
||||||
|
$("[data-related=" + name + "-" + element + "]").show();
|
||||||
} else {
|
} else {
|
||||||
$("[data-related=\"" + name + "-" + i + "\"]").hide();
|
$("[data-related=" + name + "-" + element + "]").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<label for="config_calibre_dir">{{_('Location of Calibre database')}}</label>
|
<label for="config_calibre_dir">{{_('Location of Calibre database')}}</label>
|
||||||
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if config.config_calibre_dir != None %}{{ config.config_calibre_dir }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if config.config_calibre_dir != None %}{{ config.config_calibre_dir }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
{% if feature_support['gdrive'] %}
|
||||||
<div class="form-group required">
|
<div class="form-group required">
|
||||||
<input type="checkbox" id="config_use_google_drive" name="config_use_google_drive" data-control="gdrive_settings" {% if config.config_use_google_drive %}checked{% endif %} >
|
<input type="checkbox" id="config_use_google_drive" name="config_use_google_drive" data-control="gdrive_settings" {% if config.config_use_google_drive %}checked{% endif %} >
|
||||||
<label for="config_use_google_drive">{{_('Use Google Drive?')}}</label>
|
<label for="config_use_google_drive">{{_('Use Google Drive?')}}</label>
|
||||||
|
@ -66,6 +67,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -193,8 +195,7 @@
|
||||||
<option value="1" {% if config.config_login_type == 1 %}selected{% endif %}>{{_('Use LDAP Authentication')}}</option>
|
<option value="1" {% if config.config_login_type == 1 %}selected{% endif %}>{{_('Use LDAP Authentication')}}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if feature_support['oauth'] %}
|
{% if feature_support['oauth'] %}
|
||||||
<option value="2" {% if config.config_login_type == 2 %}selected{% endif %}>{{_('Use GitHub OAuth')}}</option>
|
<option value="2" {% if config.config_login_type == 2 %}selected{% endif %}>{{_('Use OAuth')}}</option>
|
||||||
<option value="3" {% if config.config_login_type == 3 %}selected{% endif %}>{{_('Use Google OAuth')}}</option>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,30 +255,19 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if feature_support['oauth'] %}
|
{% if feature_support['oauth'] %}
|
||||||
<div data-related="login-settings-2">
|
<div data-related="login-settings-2">
|
||||||
|
{% for prov in provider %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<a href="https://github.com/settings/developers" target="_blank">{{_('Obtain GitHub OAuth Credential')}}</a>
|
<a href="{{prov['obtain_link']}}" target="_blank">{{_('Obtain %(provider)s OAuth Credential', provider=prov['provider_name'])}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="config_github_oauth_client_id">{{_('GitHub OAuth Client Id')}}</label>
|
<label for="config_{{ prov['id'] }}_oauth_client_id">{{_('%(provider)s OAuth Client Id', provider=prov['provider_name'])}}</label>
|
||||||
<input type="text" class="form-control" id="config_github_oauth_client_id" name="config_github_oauth_client_id" value="{% if config.config_github_oauth_client_id != None %}{{ config.config_github_oauth_client_id }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_{{ prov['id'] }}_oauth_client_id" name="config_{{ prov['id'] }}_oauth_client_id" value="{% if prov['oauth_client_id']%}{{ prov['oauth_client_id'] }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="config_github_oauth_client_secret">{{_('GitHub OAuth Client Secret')}}</label>
|
<label for="config_{{ prov['id'] }}_oauth_client_secret">{{_('%(provider)s OAuth Client Secret', provider=prov['provider_name'])}}</label>
|
||||||
<input type="text" class="form-control" id="config_github_oauth_client_secret" name="config_github_oauth_client_secret" value="{% if config.config_github_oauth_client_secret != None %}{{ config.config_github_oauth_client_secret }}{% endif %}" autocomplete="off">
|
<input type="text" class="form-control" id="config_{{ prov['id'] }}_oauth_client_secret" name="config_{{ prov['id'] }}_oauth_client_secret" value="{% if prov['oauth_client_secret']%}{{ prov['oauth_client_secret'] }}{% endif %}" autocomplete="off">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div data-related="login-settings-3">
|
|
||||||
<div class="form-group">
|
|
||||||
<a href="https://console.developers.google.com/apis/credentials" target="_blank">{{_('Obtain Google OAuth Credential')}}</a>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="config_google_oauth_client_id">{{_('Google OAuth Client Id')}}</label>
|
|
||||||
<input type="text" class="form-control" id="config_google_oauth_client_id" name="config_google_oauth_client_id" value="{% if config.config_google_oauth_client_id != None %}{{ config.config_google_oauth_client_id }}{% endif %}" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="config_google_oauth_client_secret">{{_('Google OAuth Client Secret')}}</label>
|
|
||||||
<input type="text" class="form-control" id="config_google_oauth_client_secret" name="config_google_oauth_client_secret" value="{% if config.config_google_oauth_client_secret != None %}{{ config.config_google_oauth_client_secret }}{% endif %}" autocomplete="off">
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,10 +17,34 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="text-center">
|
<div class="container-fluid">
|
||||||
<h1>{{ error_code }}</h1>
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h1 class="text-center">{{ error_code }}</h1>
|
||||||
<h3>{{ error_name }}</h3>
|
<h3>{{ error_name }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-offset-4 text-left">
|
||||||
|
{% for element in error_stack %}
|
||||||
|
<div>{{ element }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if issue %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col errorlink">Please report this issue with all related information:
|
||||||
|
<a href="https://github.com/janeczku/calibre-web/issues/new">{{_('Create issue')}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col errorlink">
|
||||||
<a href="{{url_for('web.index')}}" title="{{ _('Back to home') }}">{{_('Back to home')}}</a>
|
<a href="{{url_for('web.index')}}" title="{{ _('Back to home') }}">{{_('Back to home')}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
|
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
|
||||||
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{{url_for('web.language', name=lang.lang_code)}}">{{lang.name}}</a></div>
|
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{{url_for('web.books_list', book_id=lang.lang_code, data=data, sort='new')}}">{{lang.name}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -79,15 +79,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="overlay"></div>
|
<div class="overlay"></div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.calibre = {
|
|
||||||
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
|
||||||
cssPath: "{{ url_for('static', filename='css/') }}",
|
|
||||||
bookmarkUrl: "{{ url_for('bookmark', book_id=bookid, book_format='EPUB') }}",
|
|
||||||
bookUrl: "{{ url_for('get_download_link_ext', book_id=bookid, book_format="epub", anyname='file.epub') }}",
|
|
||||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
|
||||||
useBookmarks: {{ g.user.is_authenticated | tojson }} };
|
|
||||||
</script>
|
|
||||||
<script src="{{ url_for('static', filename='js/libs/jszip.min.js') }}">
|
<script src="{{ url_for('static', filename='js/libs/jszip.min.js') }}">
|
||||||
</script> <script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
</script> <script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
@ -47,13 +47,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if registered_oauth.keys()| length > 0 %}
|
{% if registered_oauth.keys()| length > 0 %}
|
||||||
{% for oauth, name in registered_oauth.iteritems() %}
|
{% for id, name in registered_oauth.items() %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ name }} {{_('OAuth Settings')}}</label>
|
<label>{{ name }} {{_('OAuth Settings')}}</label>
|
||||||
{% if oauth not in oauth_status %}
|
{% if id not in oauth_status %}
|
||||||
<div><a href="{{ url_for(oauth +'.login') }}" id="config_{{ oauth }}_oauth" class="btn btn-primary">{{_('Link')}}</a></div>
|
<div><a href="{{ url_for('oauth.'+ name +'_login') }}" id="config_{{ id }}_oauth" class="btn btn-primary">{{_('Link')}}</a></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div><a href="{{ url_for('oauth.'+ oauth +'_login_unlink') }}" id="config_{{ oauth }}_oauth" class="btn btn-primary">{{_('Unlink')}}</a></div>
|
<div><a href="{{ url_for('oauth.'+ name +'_login_unlink') }}" id="config_{{ id }}_oauth" class="btn btn-primary">{{_('Unlink')}}</a></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
11
cps/ub.py
11
cps/ub.py
|
@ -180,6 +180,7 @@ class User(UserBase, Base):
|
||||||
default_language = Column(String(3), default="all")
|
default_language = Column(String(3), default="all")
|
||||||
mature_content = Column(Boolean, default=True)
|
mature_content = Column(Boolean, default=True)
|
||||||
|
|
||||||
|
|
||||||
if oauth_support:
|
if oauth_support:
|
||||||
class OAuth(OAuthConsumerMixin, Base):
|
class OAuth(OAuthConsumerMixin, Base):
|
||||||
provider_user_id = Column(String(256))
|
provider_user_id = Column(String(256))
|
||||||
|
@ -187,6 +188,16 @@ if oauth_support:
|
||||||
user = relationship(User)
|
user = relationship(User)
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthProvider(Base):
|
||||||
|
__tablename__ = 'oauthProvider'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
provider_name = Column(String)
|
||||||
|
oauth_client_id = Column(String)
|
||||||
|
oauth_client_secret = Column(String)
|
||||||
|
active = Column(Boolean)
|
||||||
|
|
||||||
|
|
||||||
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
|
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
|
||||||
# anonymous user
|
# anonymous user
|
||||||
class Anonymous(AnonymousUserMixin, UserBase):
|
class Anonymous(AnonymousUserMixin, UserBase):
|
||||||
|
|
|
@ -33,7 +33,7 @@ import requests
|
||||||
from babel.dates import format_datetime
|
from babel.dates import format_datetime
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
|
|
||||||
from . import constants, logger, config, get_locale, web_server
|
from . import constants, logger, config, web_server
|
||||||
|
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
@ -62,10 +62,10 @@ class Updater(threading.Thread):
|
||||||
return self._stable_version_info()
|
return self._stable_version_info()
|
||||||
return self._nightly_version_info()
|
return self._nightly_version_info()
|
||||||
|
|
||||||
def get_available_updates(self, request_method):
|
def get_available_updates(self, request_method, locale):
|
||||||
if config.config_updatechannel == constants.UPDATE_STABLE:
|
if config.config_updatechannel == constants.UPDATE_STABLE:
|
||||||
return self._stable_available_updates(request_method)
|
return self._stable_available_updates(request_method)
|
||||||
return self._nightly_available_updates(request_method)
|
return self._nightly_available_updates(request_method,locale)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
|
@ -239,7 +239,7 @@ class Updater(threading.Thread):
|
||||||
def _stable_version_info(cls):
|
def _stable_version_info(cls):
|
||||||
return constants.STABLE_VERSION # Current version
|
return constants.STABLE_VERSION # Current version
|
||||||
|
|
||||||
def _nightly_available_updates(self, request_method):
|
def _nightly_available_updates(self, request_method, locale):
|
||||||
tz = datetime.timedelta(seconds=time.timezone if (time.localtime().tm_isdst == 0) else time.altzone)
|
tz = datetime.timedelta(seconds=time.timezone if (time.localtime().tm_isdst == 0) else time.altzone)
|
||||||
if request_method == "GET":
|
if request_method == "GET":
|
||||||
repository_url = _REPOSITORY_API_URL
|
repository_url = _REPOSITORY_API_URL
|
||||||
|
@ -288,7 +288,7 @@ class Updater(threading.Thread):
|
||||||
update_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
|
update_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
|
||||||
parents.append(
|
parents.append(
|
||||||
[
|
[
|
||||||
format_datetime(new_commit_date, format='short', locale=get_locale()),
|
format_datetime(new_commit_date, format='short', locale=locale),
|
||||||
update_data['message'],
|
update_data['message'],
|
||||||
update_data['sha']
|
update_data['sha']
|
||||||
]
|
]
|
||||||
|
@ -319,7 +319,7 @@ class Updater(threading.Thread):
|
||||||
parent_commit_date = datetime.datetime.strptime(
|
parent_commit_date = datetime.datetime.strptime(
|
||||||
parent_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
|
parent_data['committer']['date'], '%Y-%m-%dT%H:%M:%SZ') - tz
|
||||||
parent_commit_date = format_datetime(
|
parent_commit_date = format_datetime(
|
||||||
parent_commit_date, format='short', locale=get_locale())
|
parent_commit_date, format='short', locale=locale)
|
||||||
|
|
||||||
parents.append([parent_commit_date,
|
parents.append([parent_commit_date,
|
||||||
parent_data['message'].replace('\r\n', '<p>').replace('\n', '<p>')])
|
parent_data['message'].replace('\r\n', '<p>').replace('\n', '<p>')])
|
||||||
|
@ -331,7 +331,7 @@ class Updater(threading.Thread):
|
||||||
else:
|
else:
|
||||||
# parent is our current version
|
# parent is our current version
|
||||||
break
|
break
|
||||||
|
status['history'] = parents[::-1]
|
||||||
else:
|
else:
|
||||||
status['success'] = False
|
status['success'] = False
|
||||||
status['message'] = _(u'Could not fetch update information')
|
status['message'] = _(u'Could not fetch update information')
|
||||||
|
|
|
@ -42,7 +42,7 @@ try:
|
||||||
from wand.exceptions import PolicyError
|
from wand.exceptions import PolicyError
|
||||||
use_generic_pdf_cover = False
|
use_generic_pdf_cover = False
|
||||||
except (ImportError, RuntimeError) as e:
|
except (ImportError, RuntimeError) as e:
|
||||||
log.warning('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e)
|
log.debug('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e)
|
||||||
use_generic_pdf_cover = True
|
use_generic_pdf_cover = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -50,21 +50,21 @@ try:
|
||||||
from PyPDF2 import __version__ as PyPdfVersion
|
from PyPDF2 import __version__ as PyPdfVersion
|
||||||
use_pdf_meta = True
|
use_pdf_meta = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.warning('cannot import PyPDF2, extracting pdf metadata will not work: %s', e)
|
log.debug('cannot import PyPDF2, extracting pdf metadata will not work: %s', e)
|
||||||
use_pdf_meta = False
|
use_pdf_meta = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from . import epub
|
from . import epub
|
||||||
use_epub_meta = True
|
use_epub_meta = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.warning('cannot import epub, extracting epub metadata will not work: %s', e)
|
log.debug('cannot import epub, extracting epub metadata will not work: %s', e)
|
||||||
use_epub_meta = False
|
use_epub_meta = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from . import fb2
|
from . import fb2
|
||||||
use_fb2_meta = True
|
use_fb2_meta = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.warning('cannot import fb2, extracting fb2 metadata will not work: %s', e)
|
log.debug('cannot import fb2, extracting fb2 metadata will not work: %s', e)
|
||||||
use_fb2_meta = False
|
use_fb2_meta = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -72,7 +72,7 @@ try:
|
||||||
from PIL import __version__ as PILversion
|
from PIL import __version__ as PILversion
|
||||||
use_PIL = True
|
use_PIL = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.warning('cannot import Pillow, using png and webp images as cover will not work: %s', e)
|
log.debug('cannot import Pillow, using png and webp images as cover will not work: %s', e)
|
||||||
use_generic_pdf_cover = True
|
use_generic_pdf_cover = True
|
||||||
use_PIL = False
|
use_PIL = False
|
||||||
|
|
||||||
|
|
74
cps/web.py
74
cps/web.py
|
@ -27,6 +27,8 @@ import base64
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
import traceback
|
||||||
|
import sys
|
||||||
|
|
||||||
from babel import Locale as LC
|
from babel import Locale as LC
|
||||||
from babel.dates import format_date
|
from babel.dates import format_date
|
||||||
|
@ -52,7 +54,7 @@ from .pagination import Pagination
|
||||||
from .redirect import redirect_back
|
from .redirect import redirect_back
|
||||||
|
|
||||||
feature_support = {
|
feature_support = {
|
||||||
'ldap': bool(services.ldap),
|
'ldap': False, # bool(services.ldap),
|
||||||
'goodreads': bool(services.goodreads)
|
'goodreads': bool(services.goodreads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,16 +85,30 @@ except ImportError:
|
||||||
# custom error page
|
# custom error page
|
||||||
def error_http(error):
|
def error_http(error):
|
||||||
return render_template('http_error.html',
|
return render_template('http_error.html',
|
||||||
error_code=error.code,
|
error_code="Error {0}".format(error.code),
|
||||||
error_name=error.name,
|
error_name=error.name,
|
||||||
|
issue=False,
|
||||||
instance=config.config_calibre_web_title
|
instance=config.config_calibre_web_title
|
||||||
), error.code
|
), error.code
|
||||||
|
|
||||||
|
|
||||||
|
def internal_error(error):
|
||||||
|
__, __, tb = sys.exc_info()
|
||||||
|
return render_template('http_error.html',
|
||||||
|
error_code="Internal Server Error",
|
||||||
|
error_name=str(error),
|
||||||
|
issue=True,
|
||||||
|
error_stack=traceback.format_tb(tb),
|
||||||
|
instance=config.config_calibre_web_title
|
||||||
|
), 500
|
||||||
|
|
||||||
|
|
||||||
# http error handling
|
# http error handling
|
||||||
for ex in default_exceptions:
|
for ex in default_exceptions:
|
||||||
# new routine for all client errors, server errors stay
|
|
||||||
if ex < 500:
|
if ex < 500:
|
||||||
app.register_error_handler(ex, error_http)
|
app.register_error_handler(ex, error_http)
|
||||||
|
elif ex == 500:
|
||||||
|
app.register_error_handler(ex, internal_error)
|
||||||
|
|
||||||
|
|
||||||
web = Blueprint('web', __name__)
|
web = Blueprint('web', __name__)
|
||||||
|
@ -498,6 +514,8 @@ def books_list(data, sort, book_id, page):
|
||||||
return render_formats_books(page, book_id, order)
|
return render_formats_books(page, book_id, order)
|
||||||
elif data == "category":
|
elif data == "category":
|
||||||
return render_category_books(page, book_id, order)
|
return render_category_books(page, book_id, order)
|
||||||
|
elif data == "language":
|
||||||
|
return render_language_books(page, book_id, order)
|
||||||
else:
|
else:
|
||||||
entries, random, pagination = fill_indexpage(page, db.Books, True, order)
|
entries, random, pagination = fill_indexpage(page, db.Books, True, order)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||||
|
@ -552,8 +570,8 @@ def render_author_books(page, author_id, order):
|
||||||
other_books = services.goodreads.get_other_books(author_info, entries)
|
other_books = services.goodreads.get_other_books(author_info, entries)
|
||||||
|
|
||||||
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
|
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
|
||||||
title=_(u"Author: %(name)s", name=author_name), author=author_info, other_books=other_books,
|
title=_(u"Author: %(name)s", name=author_name), author=author_info,
|
||||||
page="author")
|
other_books=other_books, page="author")
|
||||||
|
|
||||||
|
|
||||||
def render_publisher_books(page, book_id, order):
|
def render_publisher_books(page, book_id, order):
|
||||||
|
@ -574,18 +592,18 @@ def render_series_books(page, book_id, order):
|
||||||
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]])
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries,
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"Series: %(serie)s", serie=name.name), page="series")
|
title=_(u"Series: %(serie)s", serie=name.name), page="series")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
def render_ratings_books(page, book_id, order):
|
def render_ratings_books(page, book_id, order):
|
||||||
if book_id <=5:
|
|
||||||
name = db.session.query(db.Ratings).filter(db.Ratings.id == book_id).first()
|
name = 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]])
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries,
|
if name and name.rating <= 10:
|
||||||
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"Rating: %(rating)s stars", rating=int(name.rating/2)), page="ratings")
|
title=_(u"Rating: %(rating)s stars", rating=int(name.rating/2)), page="ratings")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -596,7 +614,7 @@ def render_formats_books(page, book_id, order):
|
||||||
if name:
|
if name:
|
||||||
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.data.any(db.Data.format == book_id.upper()),
|
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.data.any(db.Data.format == book_id.upper()),
|
||||||
[db.Books.timestamp.desc(), order[0]])
|
[db.Books.timestamp.desc(), order[0]])
|
||||||
return render_title_template('index.html', random=random, pagination=pagination, entries=entries,
|
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
|
||||||
title=_(u"File format: %(format)s", format=name.format), page="formats")
|
title=_(u"File format: %(format)s", format=name.format), page="formats")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -608,12 +626,27 @@ def render_category_books(page, book_id, order):
|
||||||
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),
|
||||||
[db.Series.name, db.Books.series_index, order[0]],
|
[db.Series.name, db.Books.series_index, order[0]],
|
||||||
db.books_series_link, db.Series)
|
db.books_series_link, db.Series)
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
|
||||||
title=_(u"Category: %(name)s", name=name.name), page="category")
|
title=_(u"Category: %(name)s", name=name.name), page="category")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
def render_language_books(page, name, order):
|
||||||
|
try:
|
||||||
|
cur_l = LC.parse(name)
|
||||||
|
lang_name = cur_l.get_language_name(get_locale())
|
||||||
|
except UnknownLocaleError:
|
||||||
|
try:
|
||||||
|
lang_name = _(isoLanguages.get(part3=name).name)
|
||||||
|
except KeyError:
|
||||||
|
abort(404)
|
||||||
|
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.languages.any(db.Languages.lang_code == name),
|
||||||
|
[db.Books.timestamp.desc(), order[0]])
|
||||||
|
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
|
||||||
|
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
||||||
|
|
||||||
|
|
||||||
@web.route("/author")
|
@web.route("/author")
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def author_list():
|
def author_list():
|
||||||
|
@ -714,29 +747,12 @@ def language_overview():
|
||||||
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,
|
||||||
charlist=charlist, title=_(u"Available languages"), page="langlist", data="language")
|
charlist=charlist, title=_(u"Available languages"), page="langlist",
|
||||||
|
data="language")
|
||||||
else:
|
else:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@web.route("/language/<name>", defaults={'page': 1})
|
|
||||||
@web.route('/language/<name>/page/<int:page>')
|
|
||||||
@login_required_if_no_ano
|
|
||||||
def language(name, page):
|
|
||||||
try:
|
|
||||||
cur_l = LC.parse(name)
|
|
||||||
lang_name = cur_l.get_language_name(get_locale())
|
|
||||||
except UnknownLocaleError:
|
|
||||||
try:
|
|
||||||
lang_name = _(isoLanguages.get(part3=name).name)
|
|
||||||
except KeyError:
|
|
||||||
abort(404)
|
|
||||||
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.languages.any(db.Languages.lang_code == name),
|
|
||||||
[db.Books.timestamp.desc()])
|
|
||||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
|
||||||
title=_(u"Language: %(name)s", name=lang_name), page="language")
|
|
||||||
|
|
||||||
|
|
||||||
@web.route("/category")
|
@web.route("/category")
|
||||||
@login_required_if_no_ano
|
@login_required_if_no_ano
|
||||||
def category_list():
|
def category_list():
|
||||||
|
|
102
cps/worker.py
102
cps/worker.py
|
@ -187,25 +187,28 @@ class WorkerThread(threading.Thread):
|
||||||
self.UIqueue = list()
|
self.UIqueue = list()
|
||||||
self.asyncSMTP = None
|
self.asyncSMTP = None
|
||||||
self.id = 0
|
self.id = 0
|
||||||
|
self.doLock = threading.Lock()
|
||||||
|
|
||||||
# Main thread loop starting the different tasks
|
# Main thread loop starting the different tasks
|
||||||
def run(self):
|
def run(self):
|
||||||
main_thread = _get_main_thread()
|
main_thread = _get_main_thread()
|
||||||
while main_thread.is_alive():
|
while main_thread.is_alive():
|
||||||
doLock = threading.Lock()
|
self.doLock.acquire()
|
||||||
doLock.acquire()
|
|
||||||
if self.current != self.last:
|
if self.current != self.last:
|
||||||
doLock.release()
|
index = self.current
|
||||||
if self.queue[self.current]['taskType'] == TASK_EMAIL:
|
self.doLock.release()
|
||||||
|
if self.queue[index]['taskType'] == TASK_EMAIL:
|
||||||
self._send_raw_email()
|
self._send_raw_email()
|
||||||
if self.queue[self.current]['taskType'] == TASK_CONVERT:
|
if self.queue[index]['taskType'] == TASK_CONVERT:
|
||||||
self._convert_any_format()
|
self._convert_any_format()
|
||||||
if self.queue[self.current]['taskType'] == TASK_CONVERT_ANY:
|
if self.queue[index]['taskType'] == TASK_CONVERT_ANY:
|
||||||
self._convert_any_format()
|
self._convert_any_format()
|
||||||
# TASK_UPLOAD is handled implicitly
|
# TASK_UPLOAD is handled implicitly
|
||||||
|
self.doLock.acquire()
|
||||||
self.current += 1
|
self.current += 1
|
||||||
|
self.doLock.release()
|
||||||
else:
|
else:
|
||||||
doLock.release()
|
self.doLock.release()
|
||||||
if main_thread.is_alive():
|
if main_thread.is_alive():
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
@ -226,6 +229,7 @@ class WorkerThread(threading.Thread):
|
||||||
self.last = len(self.queue)
|
self.last = len(self.queue)
|
||||||
|
|
||||||
def get_taskstatus(self):
|
def get_taskstatus(self):
|
||||||
|
self.doLock.acquire()
|
||||||
if self.current < len(self.queue):
|
if self.current < len(self.queue):
|
||||||
if self.UIqueue[self.current]['stat'] == STAT_STARTED:
|
if self.UIqueue[self.current]['stat'] == STAT_STARTED:
|
||||||
if self.queue[self.current]['taskType'] == TASK_EMAIL:
|
if self.queue[self.current]['taskType'] == TASK_EMAIL:
|
||||||
|
@ -234,30 +238,37 @@ class WorkerThread(threading.Thread):
|
||||||
self.UIqueue[self.current]['rt'] = self.UIqueue[self.current]['formRuntime'].days*24*60 \
|
self.UIqueue[self.current]['rt'] = self.UIqueue[self.current]['formRuntime'].days*24*60 \
|
||||||
+ self.UIqueue[self.current]['formRuntime'].seconds \
|
+ self.UIqueue[self.current]['formRuntime'].seconds \
|
||||||
+ self.UIqueue[self.current]['formRuntime'].microseconds
|
+ self.UIqueue[self.current]['formRuntime'].microseconds
|
||||||
|
self.doLock.release()
|
||||||
return self.UIqueue
|
return self.UIqueue
|
||||||
|
|
||||||
def _convert_any_format(self):
|
def _convert_any_format(self):
|
||||||
# convert book, and upload in case of google drive
|
# convert book, and upload in case of google drive
|
||||||
self.UIqueue[self.current]['stat'] = STAT_STARTED
|
self.doLock.acquire()
|
||||||
self.queue[self.current]['starttime'] = datetime.now()
|
index = self.current
|
||||||
self.UIqueue[self.current]['formStarttime'] = self.queue[self.current]['starttime']
|
self.doLock.release()
|
||||||
curr_task = self.queue[self.current]['taskType']
|
self.UIqueue[index]['stat'] = STAT_STARTED
|
||||||
|
self.queue[index]['starttime'] = datetime.now()
|
||||||
|
self.UIqueue[index]['formStarttime'] = self.queue[self.current]['starttime']
|
||||||
|
curr_task = self.queue[index]['taskType']
|
||||||
filename = self._convert_ebook_format()
|
filename = self._convert_ebook_format()
|
||||||
if filename:
|
if filename:
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
gdriveutils.updateGdriveCalibreFromLocal()
|
gdriveutils.updateGdriveCalibreFromLocal()
|
||||||
if curr_task == TASK_CONVERT:
|
if curr_task == TASK_CONVERT:
|
||||||
self.add_email(self.queue[self.current]['settings']['subject'], self.queue[self.current]['path'],
|
self.add_email(self.queue[index]['settings']['subject'], self.queue[index]['path'],
|
||||||
filename, self.queue[self.current]['settings'], self.queue[self.current]['kindle'],
|
filename, self.queue[index]['settings'], self.queue[index]['kindle'],
|
||||||
self.UIqueue[self.current]['user'], self.queue[self.current]['title'],
|
self.UIqueue[index]['user'], self.queue[index]['title'],
|
||||||
self.queue[self.current]['settings']['body'])
|
self.queue[index]['settings']['body'])
|
||||||
|
|
||||||
def _convert_ebook_format(self):
|
def _convert_ebook_format(self):
|
||||||
error_message = None
|
error_message = None
|
||||||
file_path = self.queue[self.current]['file_path']
|
self.doLock.acquire()
|
||||||
bookid = self.queue[self.current]['bookid']
|
index = self.current
|
||||||
format_old_ext = u'.' + self.queue[self.current]['settings']['old_book_format'].lower()
|
self.doLock.release()
|
||||||
format_new_ext = u'.' + self.queue[self.current]['settings']['new_book_format'].lower()
|
file_path = self.queue[index]['file_path']
|
||||||
|
bookid = self.queue[index]['bookid']
|
||||||
|
format_old_ext = u'.' + self.queue[index]['settings']['old_book_format'].lower()
|
||||||
|
format_new_ext = u'.' + self.queue[index]['settings']['new_book_format'].lower()
|
||||||
|
|
||||||
# check to see if destination format already exists -
|
# check to see if destination format already exists -
|
||||||
# if it does - mark the conversion task as complete and return a success
|
# if it does - mark the conversion task as complete and return a success
|
||||||
|
@ -265,8 +276,8 @@ class WorkerThread(threading.Thread):
|
||||||
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 = db.session.query(db.Books).filter(db.Books.id == bookid).first()
|
||||||
self.queue[self.current]['path'] = file_path
|
self.queue[index]['path'] = file_path
|
||||||
self.queue[self.current]['title'] = cur_book.title
|
self.queue[index]['title'] = cur_book.title
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
return file_path + format_new_ext
|
return file_path + format_new_ext
|
||||||
else:
|
else:
|
||||||
|
@ -304,13 +315,13 @@ class WorkerThread(threading.Thread):
|
||||||
else:'''
|
else:'''
|
||||||
command = [config.config_converterpath, (file_path + format_old_ext),
|
command = [config.config_converterpath, (file_path + format_old_ext),
|
||||||
(file_path + format_new_ext)]
|
(file_path + format_new_ext)]
|
||||||
index = 3
|
quotes_index = 3
|
||||||
if config.config_calibre:
|
if config.config_calibre:
|
||||||
parameters = config.config_calibre.split(" ")
|
parameters = config.config_calibre.split(" ")
|
||||||
for param in parameters:
|
for param in parameters:
|
||||||
command.append(param)
|
command.append(param)
|
||||||
quotes.append(index)
|
quotes.append(quotes_index)
|
||||||
index += 1
|
quotes_index += 1
|
||||||
p = process_open(command, quotes)
|
p = process_open(command, quotes)
|
||||||
# p = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
|
# p = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@ -338,7 +349,7 @@ class WorkerThread(threading.Thread):
|
||||||
# parse progress string from calibre-converter
|
# parse progress string from calibre-converter
|
||||||
progress = re.search(r"(\d+)%\s.*", nextline)
|
progress = re.search(r"(\d+)%\s.*", nextline)
|
||||||
if progress:
|
if progress:
|
||||||
self.UIqueue[self.current]['progress'] = progress.group(1) + ' %'
|
self.UIqueue[index]['progress'] = progress.group(1) + ' %'
|
||||||
|
|
||||||
# process returncode
|
# process returncode
|
||||||
check = p.returncode
|
check = p.returncode
|
||||||
|
@ -359,12 +370,12 @@ class WorkerThread(threading.Thread):
|
||||||
cur_book = db.session.query(db.Books).filter(db.Books.id == bookid).first()
|
cur_book = 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):
|
||||||
new_format = db.Data(name=cur_book.data[0].name,
|
new_format = db.Data(name=cur_book.data[0].name,
|
||||||
book_format=self.queue[self.current]['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)
|
cur_book.data.append(new_format)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
self.queue[self.current]['path'] = cur_book.path
|
self.queue[index]['path'] = cur_book.path
|
||||||
self.queue[self.current]['title'] = cur_book.title
|
self.queue[index]['title'] = cur_book.title
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
os.remove(file_path + format_old_ext)
|
os.remove(file_path + format_old_ext)
|
||||||
self._handleSuccess()
|
self._handleSuccess()
|
||||||
|
@ -430,16 +441,19 @@ class WorkerThread(threading.Thread):
|
||||||
|
|
||||||
|
|
||||||
def _send_raw_email(self):
|
def _send_raw_email(self):
|
||||||
self.queue[self.current]['starttime'] = datetime.now()
|
self.doLock.acquire()
|
||||||
self.UIqueue[self.current]['formStarttime'] = self.queue[self.current]['starttime']
|
index = self.current
|
||||||
self.UIqueue[self.current]['stat'] = STAT_STARTED
|
self.doLock.release()
|
||||||
obj=self.queue[self.current]
|
self.queue[index]['starttime'] = datetime.now()
|
||||||
|
self.UIqueue[index]['formStarttime'] = self.queue[index]['starttime']
|
||||||
|
self.UIqueue[index]['stat'] = STAT_STARTED
|
||||||
|
obj=self.queue[index]
|
||||||
# create MIME message
|
# create MIME message
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
msg['Subject'] = self.queue[self.current]['subject']
|
msg['Subject'] = self.queue[index]['subject']
|
||||||
msg['Message-Id'] = make_msgid('calibre-web')
|
msg['Message-Id'] = make_msgid('calibre-web')
|
||||||
msg['Date'] = formatdate(localtime=True)
|
msg['Date'] = formatdate(localtime=True)
|
||||||
text = self.queue[self.current]['text']
|
text = self.queue[index]['text']
|
||||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
||||||
if obj['attachment']:
|
if obj['attachment']:
|
||||||
result = get_attachment(obj['filepath'], obj['attachment'])
|
result = get_attachment(obj['filepath'], obj['attachment'])
|
||||||
|
@ -506,15 +520,21 @@ class WorkerThread(threading.Thread):
|
||||||
|
|
||||||
def _handleError(self, error_message):
|
def _handleError(self, error_message):
|
||||||
log.error(error_message)
|
log.error(error_message)
|
||||||
self.UIqueue[self.current]['stat'] = STAT_FAIL
|
self.doLock.acquire()
|
||||||
self.UIqueue[self.current]['progress'] = "100 %"
|
index = self.current
|
||||||
self.UIqueue[self.current]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
|
self.doLock.release()
|
||||||
self.UIqueue[self.current]['message'] = error_message
|
self.UIqueue[index]['stat'] = STAT_FAIL
|
||||||
|
self.UIqueue[index]['progress'] = "100 %"
|
||||||
|
self.UIqueue[index]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
|
||||||
|
self.UIqueue[index]['message'] = error_message
|
||||||
|
|
||||||
def _handleSuccess(self):
|
def _handleSuccess(self):
|
||||||
self.UIqueue[self.current]['stat'] = STAT_FINISH_SUCCESS
|
self.doLock.acquire()
|
||||||
self.UIqueue[self.current]['progress'] = "100 %"
|
index = self.current
|
||||||
self.UIqueue[self.current]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
|
self.doLock.release()
|
||||||
|
self.UIqueue[index]['stat'] = STAT_FINISH_SUCCESS
|
||||||
|
self.UIqueue[index]['progress'] = "100 %"
|
||||||
|
self.UIqueue[index]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
|
||||||
|
|
||||||
|
|
||||||
_worker = WorkerThread()
|
_worker = WorkerThread()
|
||||||
|
|
|
@ -18,7 +18,11 @@ python-Levenshtein>=0.12.0
|
||||||
|
|
||||||
# ldap login
|
# ldap login
|
||||||
python_ldap>=3.0.0
|
python_ldap>=3.0.0
|
||||||
flask-simpleldap
|
flask-simpleldap>1.3.0
|
||||||
|
|
||||||
|
#oauth
|
||||||
|
flask-dance>=0.13.0
|
||||||
|
sqlalchemy_utils>=0.33.5
|
||||||
|
|
||||||
# extracting metadata
|
# extracting metadata
|
||||||
lxml>=3.8.0
|
lxml>=3.8.0
|
||||||
|
@ -27,7 +31,4 @@ rarfile>=2.7
|
||||||
|
|
||||||
# other
|
# other
|
||||||
natsort>=2.2.0
|
natsort>=2.2.0
|
||||||
|
git+https://github.com/OzzieIsaacs/comicapi.git@5346716578b2843f54d522f44d01bc8d25001d24#egg=comicapi
|
||||||
# Oauth Login
|
|
||||||
flask-dance>=0.13.0
|
|
||||||
sqlalchemy_utils>=0.33.5
|
|
||||||
|
|
|
@ -13,4 +13,3 @@ SQLAlchemy>=1.1.0
|
||||||
tornado>=4.1
|
tornado>=4.1
|
||||||
Wand>=0.4.4
|
Wand>=0.4.4
|
||||||
unidecode>=0.04.19
|
unidecode>=0.04.19
|
||||||
git+https://github.com/wildthyme/comicapi.git@cb279168f9c5cec742b5a05ac8326b9c168a8a91#egg=comicapi
|
|
||||||
|
|
11
setup.cfg
11
setup.cfg
|
@ -31,14 +31,14 @@ keywords =
|
||||||
calibre
|
calibre
|
||||||
calibre-web
|
calibre-web
|
||||||
library
|
library
|
||||||
|
python_requires = >=2.6
|
||||||
|
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
cps = calibreweb:main
|
cps = calibreweb:main
|
||||||
[options]
|
[options]
|
||||||
python_requires = >=2.6
|
|
||||||
include_package_data = True
|
include_package_data = True
|
||||||
zip_safe = False
|
dependency_links = comicapi @ git+https://github.com/OzzieIsaacs/comicapi.git@5346716578b2843f54d522f44d01bc8d25001d24#egg=comicapi
|
||||||
install_requires =
|
install_requires =
|
||||||
Babel >= 1.3
|
Babel >= 1.3
|
||||||
Flask-Babel >= 0.11.1
|
Flask-Babel >= 0.11.1
|
||||||
|
@ -80,7 +80,8 @@ metadata =
|
||||||
Wand >= 0.4.4
|
Wand >= 0.4.4
|
||||||
comics=
|
comics=
|
||||||
natsort>=2.2.0
|
natsort>=2.2.0
|
||||||
# https://github.com/wildthyme/comicapi/archive/cb279168f9c5cec742b5a05ac8326b9c168a8a91.zip#egg=comicapi
|
# find solution for this should belong to comics
|
||||||
# comicapi @ git+https://github.com/wildthyme/comicapi.git@cb279168f9c5cec742b5a05ac8326b9c168a8a91#egg=comicapi
|
# comicapi @ git+https://github.com/OzzieIsaacs/comicapi/archive/5346716578b2843f54d522f44d01bc8d25001d24.zip#egg=comicapi
|
||||||
# find solution for this
|
|
||||||
|
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -20,6 +20,7 @@
|
||||||
# """Calibre-web distribution package setuptools installer."""
|
# """Calibre-web distribution package setuptools installer."""
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from setuptools import find_packages
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import codecs
|
import codecs
|
||||||
|
@ -39,6 +40,7 @@ def find_version(*file_paths):
|
||||||
raise RuntimeError("Unable to find version string.")
|
raise RuntimeError("Unable to find version string.")
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
packages=find_packages("src"),
|
||||||
package_dir = {'': 'src'},
|
package_dir = {'': 'src'},
|
||||||
version=find_version("src", "calibreweb", "cps", "constants.py")
|
version=find_version("src", "calibreweb", "cps", "constants.py")
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user