Merge branch 'master' into Develop

# Conflicts:
#	cps/__init__.py
#	cps/comic.py
#	cps/editbooks.py
#	cps/helper.py
#	cps/kobo.py
#	cps/translations/nl/LC_MESSAGES/messages.mo
#	cps/translations/nl/LC_MESSAGES/messages.po
#	cps/ub.py
#	cps/uploader.py
#	cps/web.py
pull/1346/head
Ozzieisaacs 5 years ago
commit b160a8de0b

@ -36,6 +36,10 @@ from flask_principal import Principal
from . import config_sql, logger, cache_buster, cli, ub, db from . import config_sql, logger, cache_buster, cli, ub, db
from .reverseproxy import ReverseProxied from .reverseproxy import ReverseProxied
from .server import WebServer from .server import WebServer
try:
from werkzeug.middleware.proxy_fix import ProxyFix
except ImportError:
from werkzeug.contrib.fixers import ProxyFix
mimetypes.init() mimetypes.init()
mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('application/xhtml+xml', '.xhtml')
@ -76,7 +80,10 @@ log = logger.create()
from . import services from . import services
def create_app(): def create_app():
app.wsgi_app = ReverseProxied(app.wsgi_app) try:
app.wsgi_app = ReverseProxied(ProxyFix(app.wsgi_app, x_for=1, x_host=1))
except TypeError:
app.wsgi_app = ReverseProxied(ProxyFix(app.wsgi_app))
# For python2 convert path to unicode # For python2 convert path to unicode
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
app.static_folder = app.static_folder.decode('utf-8') app.static_folder = app.static_folder.decode('utf-8')
@ -88,7 +95,10 @@ def create_app():
log.info('Starting Calibre Web...') log.info('Starting Calibre Web...')
Principal(app) Principal(app)
lm.init_app(app) lm.init_app(app)
if os.environ.get('FLASK_DEBUG'):
app.secret_key = os.getenv('SECRET_KEY', 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT') app.secret_key = os.getenv('SECRET_KEY', 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT')
else:
app.secret_key = os.getenv('SECRET_KEY', os.urandom(32))
web_server.init_app(app, config) web_server.init_app(app, config)
db.setup_db(config) db.setup_db(config)

@ -50,11 +50,11 @@ feature_support = {
'kobo': bool(services.kobo) 'kobo': bool(services.kobo)
} }
# try: try:
# import rarfile import rarfile
# feature_support['rar'] = True feature_support['rar'] = True
# except ImportError: except ImportError:
# feature_support['rar'] = False feature_support['rar'] = False
try: try:
from .oauth_bb import oauth_check, oauthblueprints from .oauth_bb import oauth_check, oauthblueprints
@ -253,23 +253,23 @@ def list_domain(allow):
response.headers["Content-Type"] = "application/json; charset=utf-8" response.headers["Content-Type"] = "application/json; charset=utf-8"
return response return response
@admi.route("/ajax/editrestriction/<int:type>", methods=['POST']) @admi.route("/ajax/editrestriction/<int:res_type>", methods=['POST'])
@login_required @login_required
@admin_required @admin_required
def edit_restriction(type): def edit_restriction(res_type):
element = request.form.to_dict() element = request.form.to_dict()
if element['id'].startswith('a'): if element['id'].startswith('a'):
if type == 0: # Tags as template if res_type == 0: # Tags as template
elementlist = config.list_allowed_tags() elementlist = config.list_allowed_tags()
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
config.config_allowed_tags = ','.join(elementlist) config.config_allowed_tags = ','.join(elementlist)
config.save() config.save()
if type == 1: # CustomC if res_type == 1: # CustomC
elementlist = config.list_allowed_column_values() elementlist = config.list_allowed_column_values()
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
config.config_allowed_column_value = ','.join(elementlist) config.config_allowed_column_value = ','.join(elementlist)
config.save() config.save()
if type == 2: # Tags per user if res_type == 2: # Tags per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -279,7 +279,7 @@ def edit_restriction(type):
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
usr.allowed_tags = ','.join(elementlist) usr.allowed_tags = ','.join(elementlist)
ub.session.commit() ub.session.commit()
if type == 3: # CColumn per user if res_type == 3: # CColumn per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -290,18 +290,17 @@ def edit_restriction(type):
usr.allowed_column_value = ','.join(elementlist) usr.allowed_column_value = ','.join(elementlist)
ub.session.commit() ub.session.commit()
if element['id'].startswith('d'): if element['id'].startswith('d'):
if type == 0: # Tags as template if res_type == 0: # Tags as template
elementlist = config.list_denied_tags() elementlist = config.list_denied_tags()
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
config.config_denied_tags = ','.join(elementlist) config.config_denied_tags = ','.join(elementlist)
config.save() config.save()
if type == 1: # CustomC if res_type == 1: # CustomC
elementlist = config.list_denied_column_values() elementlist = config.list_denied_column_values()
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
config.config_denied_column_value = ','.join(elementlist) config.config_denied_column_value = ','.join(elementlist)
config.save() config.save()
pass if res_type == 2: # Tags per user
if type == 2: # Tags per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -311,7 +310,7 @@ def edit_restriction(type):
elementlist[int(element['id'][1:])]=element['Element'] elementlist[int(element['id'][1:])]=element['Element']
usr.denied_tags = ','.join(elementlist) usr.denied_tags = ','.join(elementlist)
ub.session.commit() ub.session.commit()
if type == 3: # CColumn per user if res_type == 3: # CColumn per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -339,26 +338,26 @@ def restriction_deletion(element, list_func):
return ','.join(elementlist) return ','.join(elementlist)
@admi.route("/ajax/addrestriction/<int:type>", methods=['POST']) @admi.route("/ajax/addrestriction/<int:res_type>", methods=['POST'])
@login_required @login_required
@admin_required @admin_required
def add_restriction(type): def add_restriction(res_type):
element = request.form.to_dict() element = request.form.to_dict()
if type == 0: # Tags as template if res_type == 0: # Tags as template
if 'submit_allow' in element: if 'submit_allow' in element:
config.config_allowed_tags = restriction_addition(element, config.list_allowed_tags) config.config_allowed_tags = restriction_addition(element, config.list_allowed_tags)
config.save() config.save()
elif 'submit_deny' in element: elif 'submit_deny' in element:
config.config_denied_tags = restriction_addition(element, config.list_denied_tags) config.config_denied_tags = restriction_addition(element, config.list_denied_tags)
config.save() config.save()
if type == 1: # CCustom as template if res_type == 1: # CCustom as template
if 'submit_allow' in element: if 'submit_allow' in element:
config.config_allowed_column_value = restriction_addition(element, config.list_denied_column_values) config.config_allowed_column_value = restriction_addition(element, config.list_denied_column_values)
config.save() config.save()
elif 'submit_deny' in element: elif 'submit_deny' in element:
config.config_denied_column_value = restriction_addition(element, config.list_allowed_column_values) config.config_denied_column_value = restriction_addition(element, config.list_allowed_column_values)
config.save() config.save()
if type == 2: # Tags per user if res_type == 2: # Tags per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -370,7 +369,7 @@ def add_restriction(type):
elif 'submit_deny' in element: elif 'submit_deny' in element:
usr.denied_tags = restriction_addition(element, usr.list_denied_tags) usr.denied_tags = restriction_addition(element, usr.list_denied_tags)
ub.session.commit() ub.session.commit()
if type == 3: # CustomC per user if res_type == 3: # CustomC per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -384,26 +383,26 @@ def add_restriction(type):
ub.session.commit() ub.session.commit()
return "" return ""
@admi.route("/ajax/deleterestriction/<int:type>", methods=['POST']) @admi.route("/ajax/deleterestriction/<int:res_type>", methods=['POST'])
@login_required @login_required
@admin_required @admin_required
def delete_restriction(type): def delete_restriction(res_type):
element = request.form.to_dict() element = request.form.to_dict()
if type == 0: # Tags as template if res_type == 0: # Tags as template
if element['id'].startswith('a'): if element['id'].startswith('a'):
config.config_allowed_tags = restriction_deletion(element, config.list_allowed_tags) config.config_allowed_tags = restriction_deletion(element, config.list_allowed_tags)
config.save() config.save()
elif element['id'].startswith('d'): elif element['id'].startswith('d'):
config.config_denied_tags = restriction_deletion(element, config.list_denied_tags) config.config_denied_tags = restriction_deletion(element, config.list_denied_tags)
config.save() config.save()
elif type == 1: # CustomC as template elif res_type == 1: # CustomC as template
if element['id'].startswith('a'): if element['id'].startswith('a'):
config.config_allowed_column_value = restriction_deletion(element, config.list_allowed_column_values) config.config_allowed_column_value = restriction_deletion(element, config.list_allowed_column_values)
config.save() config.save()
elif element['id'].startswith('d'): elif element['id'].startswith('d'):
config.config_denied_column_value = restriction_deletion(element, config.list_denied_column_values) config.config_denied_column_value = restriction_deletion(element, config.list_denied_column_values)
config.save() config.save()
elif type == 2: # Tags per user elif res_type == 2: # Tags per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -415,7 +414,7 @@ def delete_restriction(type):
elif element['id'].startswith('d'): elif element['id'].startswith('d'):
usr.denied_tags = restriction_deletion(element, usr.list_denied_tags) usr.denied_tags = restriction_deletion(element, usr.list_denied_tags)
ub.session.commit() ub.session.commit()
elif type == 3: # Columns per user elif res_type == 3: # Columns per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: # select current user if admins are editing their own rights if usr_id.isdigit() == True: # select current user if admins are editing their own rights
usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first() usr = ub.session.query(ub.User).filter(ub.User.id == int(usr_id)).first()
@ -431,23 +430,23 @@ def delete_restriction(type):
#@admi.route("/ajax/listrestriction/<int:type>/<int:user_id>", defaults={'user_id': '0'}) #@admi.route("/ajax/listrestriction/<int:type>/<int:user_id>", defaults={'user_id': '0'})
@admi.route("/ajax/listrestriction/<int:type>") @admi.route("/ajax/listrestriction/<int:res_type>")
@login_required @login_required
@admin_required @admin_required
def list_restriction(type): def list_restriction(res_type):
if type == 0: # Tags as template if res_type == 0: # Tags as template
restrict = [{'Element': x, 'type':_('Deny'), 'id': 'd'+str(i) } restrict = [{'Element': x, 'type':_('Deny'), 'id': 'd'+str(i) }
for i,x in enumerate(config.list_denied_tags()) if x != '' ] for i,x in enumerate(config.list_denied_tags()) if x != '' ]
allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) } allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) }
for i,x in enumerate(config.list_allowed_tags()) if x != ''] for i,x in enumerate(config.list_allowed_tags()) if x != '']
json_dumps = restrict + allow json_dumps = restrict + allow
elif type == 1: # CustomC as template elif res_type == 1: # CustomC as template
restrict = [{'Element': x, 'type':_('Deny'), 'id': 'd'+str(i) } restrict = [{'Element': x, 'type':_('Deny'), 'id': 'd'+str(i) }
for i,x in enumerate(config.list_denied_column_values()) if x != '' ] for i,x in enumerate(config.list_denied_column_values()) if x != '' ]
allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) } allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) }
for i,x in enumerate(config.list_allowed_column_values()) if x != ''] for i,x in enumerate(config.list_allowed_column_values()) if x != '']
json_dumps = restrict + allow json_dumps = restrict + allow
elif type == 2: # Tags per user elif res_type == 2: # Tags per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id == usr_id).first() usr = ub.session.query(ub.User).filter(ub.User.id == usr_id).first()
@ -458,7 +457,7 @@ def list_restriction(type):
allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) } allow = [{'Element': x, 'type':_('Allow'), 'id': 'a'+str(i) }
for i,x in enumerate(usr.list_allowed_tags()) if x != ''] for i,x in enumerate(usr.list_allowed_tags()) if x != '']
json_dumps = restrict + allow json_dumps = restrict + allow
elif type == 3: # CustomC per user elif res_type == 3: # CustomC per user
usr_id = os.path.split(request.referrer)[-1] usr_id = os.path.split(request.referrer)[-1]
if usr_id.isdigit() == True: if usr_id.isdigit() == True:
usr = ub.session.query(ub.User).filter(ub.User.id==usr_id).first() usr = ub.session.query(ub.User).filter(ub.User.id==usr_id).first()
@ -540,8 +539,7 @@ def _configuration_update_helper():
_config_string("config_calibre") _config_string("config_calibre")
_config_string("config_converterpath") _config_string("config_converterpath")
if _config_int("config_login_type"): reboot_required |= _config_int("config_login_type")
reboot_required |= config.config_login_type != constants.LOGIN_STANDARD
#LDAP configurator, #LDAP configurator,
if config.config_login_type == constants.LOGIN_LDAP: if config.config_login_type == constants.LOGIN_LDAP:

@ -43,9 +43,9 @@ except ImportError as e:
def _extractCover(tmp_file_name, original_file_extension, rarExceutable): def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
cover_data = extension = None
if use_comic_meta: if use_comic_meta:
archive = ComicArchive(tmp_file_name) archive = ComicArchive(tmp_file_name)
cover_data = None
for index, name in enumerate(archive.getPageNameList()): for index, name in enumerate(archive.getPageNameList()):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
@ -81,7 +81,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension == '.jpg' or extension == '.jpeg': if extension == '.jpg' or extension == '.jpeg':
cover_data = cf.extractfile(name).read() cover_data = cf.read(name)
break break
except Exception as e: except Exception as e:
log.debug('Rarfile failed with error: %s', e) log.debug('Rarfile failed with error: %s', e)
@ -99,7 +99,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
def get_comic_info(tmp_file_path, original_file_name, original_file_extension, rarExceutable): def get_comic_info(tmp_file_path, original_file_name, original_file_extension, rarExceutable):
if use_comic_meta: if use_comic_meta:
archive = ComicArchive(tmp_file_path) archive = ComicArchive(tmp_file_path, rar_exe_path=rarExceutable)
if archive.seemsToBeAComicArchive(): if archive.seemsToBeAComicArchive():
if archive.hasMetadata(MetaDataStyle.CIX): if archive.hasMetadata(MetaDataStyle.CIX):
style = MetaDataStyle.CIX style = MetaDataStyle.CIX
@ -131,7 +131,6 @@ def get_comic_info(tmp_file_path, original_file_name, original_file_extension, r
series=loadedMetadata.series or "", series=loadedMetadata.series or "",
series_id=loadedMetadata.issue or "", series_id=loadedMetadata.issue or "",
languages=loadedMetadata.language) languages=loadedMetadata.language)
else:
return BookMeta( return BookMeta(
file_path=tmp_file_path, file_path=tmp_file_path,

@ -102,8 +102,8 @@ DEFAULT_MAIL_SERVER = "mail.example.org"
DEFAULT_PASSWORD = "admin123" DEFAULT_PASSWORD = "admin123"
DEFAULT_PORT = 8083 DEFAULT_PORT = 8083
try:
env_CALIBRE_PORT = os.environ.get("CALIBRE_PORT", DEFAULT_PORT) env_CALIBRE_PORT = os.environ.get("CALIBRE_PORT", DEFAULT_PORT)
try:
DEFAULT_PORT = int(env_CALIBRE_PORT) DEFAULT_PORT = int(env_CALIBRE_PORT)
except ValueError: except ValueError:
print('Environment variable CALIBRE_PORT has invalid value (%s), faling back to default (8083)' % env_CALIBRE_PORT) print('Environment variable CALIBRE_PORT has invalid value (%s), faling back to default (8083)' % env_CALIBRE_PORT)

@ -418,7 +418,7 @@ def dispose():
except: pass except: pass
if old_session.bind: if old_session.bind:
try: old_session.bind.dispose() try: old_session.bind.dispose()
except: pass except Exception: pass
for attr in list(Books.__dict__.keys()): for attr in list(Books.__dict__.keys()):
if attr.startswith("custom_column_"): if attr.startswith("custom_column_"):

@ -176,12 +176,12 @@ def delete_book(book_id, book_format):
if current_user.role_delete_books(): if current_user.role_delete_books():
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
if book: if book:
try:
helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper()) helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
if not book_format: if not book_format:
# delete book from Shelfs, Downloads, Read list # delete book from Shelfs, Downloads, Read list
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete() ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete()
ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete() ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete()
ub.session.query(ub.ArchivedBook).filter(ub.ArchivedBook.book_id == book_id).delete()
ub.delete_download(book_id) ub.delete_download(book_id)
ub.session.commit() ub.session.commit()
@ -193,7 +193,8 @@ def delete_book(book_id, book_format):
modify_database_object([u''], book.languages, db.Languages, db.session, 'languages') modify_database_object([u''], book.languages, db.Languages, db.session, 'languages')
modify_database_object([u''], book.publishers, db.Publishers, db.session, 'publishers') modify_database_object([u''], book.publishers, db.Publishers, db.session, 'publishers')
cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() cc = db.session.query(db.Custom_Columns).\
filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
for c in cc: for c in cc:
cc_string = "custom_column_" + str(c.id) cc_string = "custom_column_" + str(c.id)
if not c.is_multiple: if not c.is_multiple:
@ -201,23 +202,33 @@ def delete_book(book_id, book_format):
if c.datatype == 'bool' or c.datatype == 'integer' or c.datatype == 'float': if c.datatype == 'bool' or c.datatype == 'integer' or c.datatype == 'float':
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
log.debug('remove ' + str(c.id))
db.session.delete(del_cc) db.session.delete(del_cc)
db.session.commit()
elif c.datatype == 'rating': elif c.datatype == 'rating':
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0: if len(del_cc.books) == 0:
log.debug('remove ' + str(c.id))
db.session.delete(del_cc) db.session.delete(del_cc)
db.session.commit()
else: else:
del_cc = getattr(book, cc_string)[0] del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc) getattr(book, cc_string).remove(del_cc)
log.debug('remove ' + str(c.id))
db.session.delete(del_cc) db.session.delete(del_cc)
db.session.commit()
else: else:
modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id], modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id],
db.session, 'custom') db.session, 'custom')
db.session.query(db.Books).filter(db.Books.id == book_id).delete() db.session.query(db.Books).filter(db.Books.id == book_id).delete()
else: else:
db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format).delete() db.session.query(db.Data).filter(db.Data.book == book.id).\
filter(db.Data.format == book_format).delete()
db.session.commit() db.session.commit()
except Exception as e:
log.debug(e)
db.session.rollback()
else: else:
# book not found # book not found
log.error('Book with id "%s" could not be deleted: not found', book_id) log.error('Book with id "%s" could not be deleted: not found', book_id)
@ -524,11 +535,12 @@ def edit_book(book_id):
if not error: if not error:
if to_save["cover_url"]: if to_save["cover_url"]:
if helper.save_cover_from_url(to_save["cover_url"], book.path) is True: result, error = helper.save_cover_from_url(to_save["cover_url"], book.path)
if result is True:
book.has_cover = 1 book.has_cover = 1
modif_date = True modif_date = True
else: else:
flash(_(u"Cover is not a jpg file, can't save"), category="error") flash(error, category="error")
if book.series_index != to_save["series_index"]: if book.series_index != to_save["series_index"]:
book.series_index = to_save["series_index"] book.series_index = to_save["series_index"]

@ -298,6 +298,9 @@ def delete_book_file(book, calibrepath, book_format=None):
log.error("Deleting book %s failed, path has subfolders: %s", book.id, book.path) log.error("Deleting book %s failed, path has subfolders: %s", book.id, book.path)
return False return False
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
authorpath = os.path.join(calibrepath, os.path.split(book.path)[0])
if not os.listdir(authorpath):
shutil.rmtree(authorpath, ignore_errors=True)
return True return True
else: else:
log.error("Deleting book %s failed, book path not valid: %s", book.id, book.path) log.error("Deleting book %s failed, book path not valid: %s", book.id, book.path)
@ -318,8 +321,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
new_titledir = get_valid_filename(localbook.title) + " (" + str(book_id) + ")" new_titledir = get_valid_filename(localbook.title) + " (" + str(book_id) + ")"
if titledir != new_titledir: if titledir != new_titledir:
try:
new_title_path = os.path.join(os.path.dirname(path), new_titledir) new_title_path = os.path.join(os.path.dirname(path), new_titledir)
try:
if not os.path.exists(new_title_path): if not os.path.exists(new_title_path):
os.renames(path, new_title_path) os.renames(path, new_title_path)
else: else:
@ -336,8 +339,8 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
return _("Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s", return _("Rename title from: '%(src)s' to '%(dest)s' failed with error: %(error)s",
src=path, dest=new_title_path, error=str(ex)) src=path, dest=new_title_path, error=str(ex))
if authordir != new_authordir: if authordir != new_authordir:
try:
new_author_path = os.path.join(calibrepath, new_authordir, os.path.basename(path)) new_author_path = os.path.join(calibrepath, new_authordir, os.path.basename(path))
try:
os.renames(path, new_author_path) os.renames(path, new_author_path)
localbook.path = new_authordir + '/' + localbook.path.split('/')[1] localbook.path = new_authordir + '/' + localbook.path.split('/')[1]
except OSError as ex: except OSError as ex:
@ -530,12 +533,9 @@ def save_cover_from_filestorage(filepath, saved_filename, img):
return False, _(u"Failed to create path for cover") return False, _(u"Failed to create path for cover")
try: try:
img.save(os.path.join(filepath, saved_filename)) img.save(os.path.join(filepath, saved_filename))
except IOError: except (IOError, OSError):
log.error(u"Cover-file is not a valid image file") log.error(u"Cover-file is not a valid image file, or could not be stored")
return False, _(u"Cover-file is not a valid image file") return False, _(u"Cover-file is not a valid image file, or could not be stored")
except OSError:
log.error(u"Failed to store cover-file")
return False, _(u"Failed to store cover-file")
return True, None return True, None
@ -840,7 +840,7 @@ def get_search_results(term):
db.Books.authors.any(and_(*q)), db.Books.authors.any(and_(*q)),
db.Books.publishers.any(func.lower(db.Publishers.name).ilike("%" + term + "%")), db.Books.publishers.any(func.lower(db.Publishers.name).ilike("%" + term + "%")),
func.lower(db.Books.title).ilike("%" + term + "%") func.lower(db.Books.title).ilike("%" + term + "%")
)).all() )).order_by(db.Books.sort).all()
def get_cc_columns(): def get_cc_columns():

@ -66,8 +66,8 @@ log = logger.create()
def get_store_url_for_current_request(): def get_store_url_for_current_request():
# Programmatically modify the current url to point to the official Kobo store # Programmatically modify the current url to point to the official Kobo store
base, sep, request_path_with_auth_token = request.full_path.rpartition("/kobo/") __, __, request_path_with_auth_token = request.full_path.rpartition("/kobo/")
auth_token, sep, request_path = request_path_with_auth_token.rstrip("?").partition( __, __, request_path = request_path_with_auth_token.rstrip("?").partition(
"/" "/"
) )
return KOBO_STOREAPI_URL + "/" + request_path return KOBO_STOREAPI_URL + "/" + request_path

@ -62,7 +62,6 @@ particular calls to non-Kobo specific endpoints such as the CalibreWeb book down
from binascii import hexlify from binascii import hexlify
from datetime import datetime from datetime import datetime
from os import urandom from os import urandom
import os
from flask import g, Blueprint, url_for, abort, request from flask import g, Blueprint, url_for, abort, request
from flask_login import login_user, login_required from flask_login import login_user, login_required
@ -82,7 +81,7 @@ log = logger.create()
def register_url_value_preprocessor(kobo): def register_url_value_preprocessor(kobo):
@kobo.url_value_preprocessor @kobo.url_value_preprocessor
def pop_auth_token(endpoint, values): def pop_auth_token(__, values):
g.auth_token = values.pop("auth_token") g.auth_token = values.pop("auth_token")

@ -4,7 +4,7 @@ body {
overflow-y: auto; overflow-y: auto;
color: white; color: white;
font-family: sans-serif; font-family: sans-serif;
margin: 0px; margin: 0;
} }
#main { #main {
@ -13,7 +13,7 @@ body {
} }
.view { .view {
padding-top:0px; padding-top: 0;
} }
#sidebar a, #sidebar a,
@ -34,13 +34,13 @@ body {
cursor: pointer; cursor: pointer;
padding: 4px; padding: 4px;
transition: all .2s ease; transition: all 0.2s ease;
} }
#sidebar a:hover, #sidebar a:hover,
#sidebar a:focus { #sidebar a:focus {
outline: none; outline: none;
box-shadow: 0px 2px 8px 1px black; box-shadow: 0 2px 8px 1px black;
} }
#sidebar a.active, #sidebar a.active,
@ -79,7 +79,6 @@ body {
font-size: 10px; font-size: 10px;
line-height: 10px; line-height: 10px;
text-align: right; text-align: right;
transition: min-height 150ms ease-in-out; transition: min-height 150ms ease-in-out;
} }
@ -92,18 +91,17 @@ body {
top: 0; top: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
transition: width 150ms ease-in-out; transition: width 150ms ease-in-out;
} }
#progress .bar-load { #progress .bar-load {
color: #000; color: #000;
background-color: #CCC; background-color: #ccc;
} }
#progress .bar-read { #progress .bar-read {
color: #FFF; color: #fff;
background-color: #45B29D; background-color: #45b29d;
} }
#progress .text { #progress .text {
@ -152,7 +150,8 @@ body {
max-width: 70%; max-width: 70%;
} }
th, td { th,
td {
padding: 5px; padding: 5px;
} }
@ -206,18 +205,17 @@ th {
} }
.dark-theme #titlebar { .dark-theme #titlebar {
color: #DDD; color: #ddd;
} }
.dark-theme #titlebar a:active { .dark-theme #titlebar a:active {
color: #FFF; color: #fff;
} }
.dark-theme #progress .bar-read { .dark-theme #progress .bar-read {
background-color: red; background-color: red;
} }
.dark-theme .overlay { .dark-theme .overlay {
background-color: rgba(0, 0, 0, 0.8); background-color: rgba(0, 0, 0, 0.8);
} }

@ -7,9 +7,9 @@
} }
.sm2-progress .sm2-progress-ball { .sm2-progress .sm2-progress-ball {
width: .5333em; width: 0.5333em;
height: 1.9333em; height: 1.9333em;
border-radius: 0em; border-radius: 0;
} }
.sm2-progress .sm2-progress-track { .sm2-progress .sm2-progress-track {
@ -36,7 +36,7 @@
body { body {
overflow: hidden; overflow: hidden;
background: #272B30; background: #272b30;
color: #aaa; color: #aaa;
} }
@ -51,6 +51,7 @@
height: 80%; height: 80%;
margin: 5% auto; margin: 5% auto;
max-width: 1250px; max-width: 1250px;
overflow: hidden;
} }
#area iframe { #area iframe {
@ -74,10 +75,6 @@
margin: 1em 0; margin: 1em 0;
} }
#area {
overflow: hidden;
}
pre { pre {
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;

@ -1,7 +1,8 @@
@font-face { @font-face {
font-family: 'fontello'; font-family: 'fontello';
src: url('fonts/fontello.eot?60518104'); src: url('fonts/fontello.eot?60518104');
src: url('fonts/fontello.eot?60518104#iefix') format('embedded-opentype'), src:
url('fonts/fontello.eot?60518104#iefix') format('embedded-opentype'),
url('fonts/fontello.woff?60518104') format('woff'), url('fonts/fontello.woff?60518104') format('woff'),
url('fonts/fontello.ttf?60518104') format('truetype'), url('fonts/fontello.ttf?60518104') format('truetype'),
url('fonts/fontello.svg?60518104#fontello') format('svg'); url('fonts/fontello.svg?60518104#fontello') format('svg');
@ -22,17 +23,15 @@ body {
border-radius: 5px; border-radius: 5px;
background: #fff; background: #fff;
overflow: hidden; overflow: hidden;
-webkit-transition: -webkit-transform .4s, width .2s; -webkit-transition: -webkit-transform 0.4s, width 0.2s;
-moz-transition: -webkit-transform .4s, width .2s; -moz-transition: -webkit-transform 0.4s, width 0.2s;
-ms-transition: -webkit-transform .4s, width .2s; -ms-transition: -webkit-transform 0.4s, width 0.2s;
-moz-box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.1);
-moz-box-shadow: inset 0 0 50px rgba(0,0,0,.1); -webkit-box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: inset 0 0 50px rgba(0,0,0,.1); -ms-box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.1);
-ms-box-shadow: inset 0 0 50px rgba(0,0,0,.1); box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 0 50px rgba(0,0,0,.1);
} }
#titlebar { #titlebar {
height: 8%; height: 8%;
min-height: 20px; min-height: 20px;
@ -42,11 +41,11 @@ body {
color: #4f4f4f; color: #4f4f4f;
font-weight: 100; font-weight: 100;
font-family: Georgia, "Times New Roman", Times, serif; font-family: Georgia, "Times New Roman", Times, serif;
opacity: .5; opacity: 0.5;
text-align: center; text-align: center;
-webkit-transition: opacity .5s; -webkit-transition: opacity 0.5s;
-moz-transition: opacity .5s; -moz-transition: opacity 0.5s;
-ms-transition: opacity .5s; -ms-transition: opacity 0.5s;
z-index: 10; z-index: 10;
} }
@ -60,7 +59,7 @@ body {
line-height: 20px; line-height: 20px;
overflow: hidden; overflow: hidden;
display: inline-block; display: inline-block;
opacity: .5; opacity: 0.5;
padding: 4px; padding: 4px;
border-radius: 4px; border-radius: 4px;
} }
@ -70,35 +69,27 @@ body {
} }
#titlebar a:hover { #titlebar a:hover {
opacity: .8; opacity: 0.8;
border: 1px rgba(0,0,0,.2) solid; border: 1px rgba(0, 0, 0, 0.2) solid;
padding: 3px; padding: 3px;
} }
#titlebar a:active { #titlebar a:active {
opacity: 1; opacity: 1;
color: rgba(0,0,0,.6); color: rgba(0, 0, 0, 0.6);
/* margin: 1px -1px -1px 1px; */ -moz-box-shadow: inset 0 0 6px rgba(155, 155, 155, 0.8);
-moz-box-shadow: inset 0 0 6px rgba(155,155,155,.8); -webkit-box-shadow: inset 0 0 6px rgba(155, 155, 155, 0.8);
-webkit-box-shadow: inset 0 0 6px rgba(155,155,155,.8); -ms-box-shadow: inset 0 0 6px rgba(155, 155, 155, 0.8);
-ms-box-shadow: inset 0 0 6px rgba(155,155,155,.8); box-shadow: inset 0 0 6px rgba(155, 155, 155, 0.8);
box-shadow: inset 0 0 6px rgba(155,155,155,.8);
} }
#book-title { #book-title { font-weight: 600; }
font-weight: 600; #title-seperator { display: none; }
}
#title-seperator {
display: none;
}
#viewer { #viewer {
width: 80%; width: 80%;
height: 80%; height: 80%;
/* margin-left: 10%; */
margin: 0 auto; margin: 0 auto;
/* max-width: 1250px; */
z-index: 2; z-index: 2;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
@ -108,12 +99,14 @@ body {
border: none; border: none;
} }
#left,#prev { #left,
#prev {
left: 40px; left: 40px;
padding-right: 80px; padding-right: 80px;
} }
#right,#next { #right,
#next {
right: 40px; right: 40px;
padding-left: 80px; padding-left: 80px;
} }
@ -148,17 +141,13 @@ body {
#sidebar { #sidebar {
background: #6b6b6b; background: #6b6b6b;
position: absolute; position: absolute;
/* left: -260px; */
/* -webkit-transform: translate(-260px, 0);
-moz-transform: translate(-260px, 0); */
top: 0; top: 0;
min-width: 300px; min-width: 300px;
width: 25%; width: 25%;
height: 100%; height: 100%;
-webkit-transition: -webkit-transform .5s; -webkit-transition: -webkit-transform 0.5s;
-moz-transition: -moz-transform .5s; -moz-transition: -moz-transform 0.5s;
-ms-transition: -moz-transform .5s; -ms-transition: -moz-transform 0.5s;
overflow: hidden; overflow: hidden;
} }
@ -192,10 +181,10 @@ body {
width: 100%; width: 100%;
padding: 13px 0; padding: 13px 0;
height: 14px; height: 14px;
-moz-box-shadow: 0px 1px 3px rgba(0,0,0,.6); -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
-webkit-box-shadow: 0px 1px 3px rgba(0,0,0,.6); -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
-ms-box-shadow: 0px 1px 3px rgba(0,0,0,.6); -ms-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
box-shadow: 0px 1px 3px rgba(0,0,0,.6); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
} }
#opener { #opener {
@ -203,19 +192,13 @@ body {
float: left; float: left;
} }
/* #opener #slider {
width: 25px;
} */
#metainfo { #metainfo {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
max-width: 80%; max-width: 80%;
} }
#title-controls { #title-controls { float: right; }
float: right;
}
#panels a { #panels a {
visibility: hidden; visibility: hidden;
@ -227,22 +210,17 @@ body {
margin-left: 6px; margin-left: 6px;
} }
#panels a::before { #panels a::before { visibility: visible; }
visibility: visible; #panels a:hover { color: #aaa; }
}
#panels a:hover {
color: #AAA;
}
#panels a:active { #panels a:active {
color: #AAA; color: #aaa;
margin: 1px 0 -1px 6px; margin: 1px 0 -1px 6px;
} }
#panels a.active, #panels a.active,
#panels a.active:hover { #panels a.active:hover {
color: #AAA; color: #aaa;
} }
#searchBox { #searchBox {
@ -250,28 +228,11 @@ body {
float: left; float: left;
margin-left: 10px; margin-left: 10px;
margin-top: -1px; margin-top: -1px;
/*
border-radius: 5px;
background: #9b9b9b;
float: left;
margin-left: 5px;
margin-top: -5px;
padding: 3px 10px;
color: #000;
border: none;
outline: none; */
} }
input::-webkit-input-placeholder { input::-webkit-input-placeholder { color: #454545; }
color: #454545; input:-moz-placeholder { color: #454545; }
} input:-ms-placeholder { color: #454545; }
input:-moz-placeholder {
color: #454545;
}
input:-ms-placeholder {
color: #454545;
}
#divider { #divider {
position: absolute; position: absolute;
@ -307,13 +268,11 @@ input:-ms-placeholder {
width: 25%; width: 25%;
height: 100%; height: 100%;
visibility: hidden; visibility: hidden;
-webkit-transition: visibility 0 ease .5s; -webkit-transition: visibility 0 ease 0.5s;
-moz-transition: visibility 0 ease .5s; -moz-transition: visibility 0 ease 0.5s;
-ms-transition: visibility 0 ease .5s; -ms-transition: visibility 0 ease 0.5s;
} }
#sidebar.open #tocView, #sidebar.open #tocView,
#sidebar.open #bookmarksView { #sidebar.open #bookmarksView {
overflow-y: auto; overflow-y: auto;
@ -351,7 +310,7 @@ input:-ms-placeholder {
} }
.list_item a { .list_item a {
color: #AAA; color: #aaa;
text-decoration: none; text-decoration: none;
} }
@ -360,7 +319,7 @@ input:-ms-placeholder {
} }
.list_item a.section { .list_item a.section {
font-size: .8em; font-size: 0.8em;
} }
.list_item.currentChapter > a, .list_item.currentChapter > a,
@ -370,7 +329,7 @@ input:-ms-placeholder {
/* #tocView li.openChapter > a, */ /* #tocView li.openChapter > a, */
.list_item a:hover { .list_item a:hover {
color: #E2E2E2; color: #e2e2e2;
} }
.list_item ul { .list_item ul {
@ -431,7 +390,7 @@ input:-ms-placeholder {
} }
#searchResults a { #searchResults a {
color: #AAA; color: #aaa;
text-decoration: none; text-decoration: none;
} }
@ -447,11 +406,11 @@ input:-ms-placeholder {
} }
#searchResults li > p { #searchResults li > p {
color: #AAA; color: #aaa;
} }
#searchResults li a:hover { #searchResults li a:hover {
color: #E2E2E2; color: #e2e2e2;
} }
#searchView.shown { #searchView.shown {
@ -496,7 +455,7 @@ input:-ms-placeholder {
} }
#note-text[disabled], #note-text[disabled="disabled"]{ #note-text[disabled], #note-text[disabled="disabled"]{
opacity: .5; opacity: 0.5;
} }
#note-anchor { #note-anchor {
@ -823,23 +782,18 @@ and (orientation : landscape)
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
speak: none; speak: none;
display: inline-block; display: inline-block;
text-decoration: inherit; text-decoration: inherit;
width: 1em; width: 1em;
margin-right: .2em; margin-right: 0.2em;
text-align: center; text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/ /* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal; font-variant: normal;
text-transform: none; text-transform: none;
/* you can be more comfortable with increased icons size */ /* you can be more comfortable with increased icons size */
font-size: 112%; font-size: 112%;
} }
.icon-search:before { content: '\e807'; } /* '' */ .icon-search:before { content: '\e807'; } /* '' */
.icon-resize-full-1:before { content: '\e804'; } /* '' */ .icon-resize-full-1:before { content: '\e804'; } /* '' */
.icon-cancel-circled2:before { content: '\e80f'; } /* '' */ .icon-cancel-circled2:before { content: '\e80f'; } /* '' */

@ -1,4 +1,5 @@
/* http://davidwalsh.name/css-tooltips */ /* http://davidwalsh.name/css-tooltips */
/* base CSS element */ /* base CSS element */
.popup { .popup {
background: #eee; background: #eee;
@ -9,10 +10,8 @@
position: fixed; position: fixed;
max-width: 300px; max-width: 300px;
font-size: 12px; font-size: 12px;
display: none; display: none;
margin-left: 2px; margin-left: 2px;
margin-top: 30px; margin-top: 30px;
} }
@ -38,7 +37,7 @@
} }
/* below */ /* below */
.popup:before { .popup::before {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
border-bottom: 10px solid #eee; border-bottom: 10px solid #eee;
@ -51,7 +50,7 @@
content: ''; content: '';
} }
.popup:after { .popup::after {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
border-bottom: 9px solid #eee; border-bottom: 9px solid #eee;
@ -64,33 +63,31 @@
} }
/* above */ /* above */
.popup.above:before { .popup.above::before {
border-bottom: none; border-bottom: none;
border-top: 10px solid #eee; border-top: 10px solid #eee;
border-top-color: rgba(0, 0, 0, 0.2); border-top-color: rgba(0, 0, 0, 0.2);
top: 100%; top: 100%;
} }
.popup.above:after { .popup.above::after {
border-bottom: none; border-bottom: none;
border-top: 9px solid #eee; border-top: 9px solid #eee;
top: 100%; top: 100%;
} }
.popup.left:before, .popup.left::before,
.popup.left:after .popup.left::after {
{
left: 20px; left: 20px;
} }
.popup.right:before, .popup.right::before,
.popup.right:after .popup.right::after {
{
left: auto; left: auto;
right: 20px; right: 20px;
} }
.popup.show,
.popup.show, .popup.on { .popup.on {
display: block; display: block;
} }

@ -1,5 +1,20 @@
.tooltip.bottom .tooltip-inner{font-size:13px;font-family:Open Sans Semibold,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;padding:3px 10px;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 4px 10px 0 rgba(0,0,0,.35);box-shadow:0 4px 10px 0 rgba(0,0,0,.35);opacity:1;white-space:nowrap;margin-top:-16px!important;line-height:1.71428571;color:#ddd} .tooltip.bottom .tooltip-inner {
font-size: 13px;
font-family: Open Sans Semibold,Helvetica Neue,Helvetica,Arial,sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding: 3px 10px;
border-radius: 4px;
background-color: #fff;
-webkit-box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.35);
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.35);
opacity: 1;
white-space: nowrap;
margin-top: -16px !important;
line-height: 1.71428571;
color: #ddd;
}
@font-face { @font-face {
font-family: 'Grand Hotel'; font-family: 'Grand Hotel';
@ -12,49 +27,144 @@ html.http-error {
margin: 0; margin: 0;
height: 100%; height: 100%;
} }
.http-error body { .http-error body {
margin: 0; margin: 0;
height: 100%; height: 100%;
display: table; display: table;
width: 100%; width: 100%;
} }
.http-error body > div { .http-error body > div {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
text-align: center; text-align: center;
} }
body{background:#f2f2f2}body h2{font-weight:normal;color:#444} body {
body { margin-bottom: 40px;} background: #f2f2f2;
a{color: #45b29d} /*a:hover{color: #444;}*/ margin-bottom: 40px;
.navigation .nav-head{text-transform:uppercase;color:#999;margin:20px 0}.navigation .nav-head:nth-child(1n+2){border-top:1px solid #ccc;padding-top:20px} }
.navigation li a{color:#444;text-decoration:none;display:block;padding:10px}.navigation li a:hover{background:rgba(153,153,153,0.4);border-radius:5px}
.navigation li a span{margin-right:10px} body h2 {
.navigation .create-shelf{margin:30px 0;font-size:12px;text-align:center}.navigation .create-shelf a{color:#fff;background:#45b29d;padding:10px 20px;border-radius:5px;text-decoration:none} font-weight: normal;
.container-fluid img{display:block;max-width:100%;height:auto} color:#444;
.container-fluid .discover{margin-bottom:50px} }
.container-fluid .new-books{border-top:1px solid #ccc}.container-fluid .new-books h2{margin:50px 0 0 0}
.container-fluid .book{margin-top:20px}.container-fluid .book .cover{height:225px;position:relative}.container-fluid .book .cover img{border:1px solid #fff;/*border-radius:7px;*/box-sizeing:border-box;height:100%;bottom:0;position:absolute;-webkit-box-shadow: 0 5px 8px -6px #777;-moz-box-shadow: 0 5px 8px -6px #777;box-shadow: 0 5px 8px -6px #777;} a { color: #45b29d; }
.container-fluid .book .meta{margin-top:10px}.container-fluid .book .meta p{margin:0}
.container-fluid .book .meta .title{font-weight:bold;font-size:15px;color:#444} .navigation .nav-head {
.container-fluid .book .meta .author{font-size:12px;color:#999} text-transform: uppercase;
.container-fluid .book .meta .rating{margin-top:5px}.rating .glyphicon-star{color:#999}.rating .glyphicon-star.good{color:#45b29d} color: #999;
margin: 20px 0;
.container-fluid .author .author-hidden, .container-fluid .author .author-hidden-divider { }
display: none;
} .navigation .nav-head:nth-child(1n+2) {
border-top: 1px solid #ccc;
.navbar-brand{font-family: 'Grand Hotel', cursive; font-size: 35px; color: #45b29d !important;} padding-top: 20px;
.more-stuff{margin-top: 20px; padding-top: 20px; border-top: 1px solid #ccc} }
.navigation li a {
color: #444;
text-decoration: none;
display: block;
padding: 10px;
}
.navigation li a:hover {
background: rgba(153, 153, 153, 0.4);
border-radius: 5px;
}
.navigation li a span { margin-right: 10px; }
.navigation .create-shelf {
margin: 30px 0;
font-size: 12px;
text-align: center;
}
.navigation .create-shelf a {
color: #fff;
background: #45b29d;
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
}
.container-fluid img {
display: block;
max-width: 100%;
height: auto;
}
.container-fluid .discover{ margin-bottom: 50px; }
.container-fluid .new-books { border-top: 1px solid #ccc; }
.container-fluid .new-books h2 { margin: 50px 0 0 0; }
.container-fluid .book { margin-top: 20px; }
.container-fluid .book .cover {
height: 225px;
position: relative;
}
.container-fluid .book .cover img {
border: 1px solid #fff;
box-sizeing: border-box;
height: 100%;
bottom: 0;
position: absolute;
-webkit-box-shadow: 0 5px 8px -6px #777;
-moz-box-shadow: 0 5px 8px -6px #777;
box-shadow: 0 5px 8px -6px #777;
}
.container-fluid .book .meta { margin-top: 10px; }
.container-fluid .book .meta p { margin: 0; }
.container-fluid .book .meta .title {
font-weight: bold;
font-size: 15px;
color: #444;
}
.container-fluid .book .meta .author {
font-size: 12px;
color: #999;
}
.container-fluid .book .meta .rating { margin-top: 5px; }
.rating .glyphicon-star { color: #999; }
.rating .glyphicon-star.good { color: #45b29d; }
.container-fluid .author .author-hidden, .container-fluid .author .author-hidden-divider { display: none; }
.navbar-brand {
font-family: 'Grand Hotel', cursive;
font-size: 35px;
color: #45b29d !important;
}
.more-stuff {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ccc;
}
.more-stuff>li { margin-bottom: 10px; } .more-stuff>li { margin-bottom: 10px; }
.navbar-collapse.in .navbar-nav { margin: 0; } .navbar-collapse.in .navbar-nav { margin: 0; }
span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: text-top;}
span.glyphicon.glyphicon-tags {
padding-right: 5px;
color: #999;
vertical-align: text-top;
}
.book-meta { padding-bottom: 20px; } .book-meta { padding-bottom: 20px; }
.book-meta .tags a { display: inline; } .book-meta .tags a { display: inline; }
.book-meta .identifiers a { display: inline; } .book-meta .identifiers a { display: inline; }
.container-fluid .single .cover img { .container-fluid .single .cover img {
border: 1px solid #fff; border: 1px solid #fff;
/*border-radius: 7px;*/
box-sizeing: border-box; box-sizeing: border-box;
-webkit-box-shadow: 0 5px 8px -6px #777; -webkit-box-shadow: 0 5px 8px -6px #777;
-moz-box-shadow: 0 5px 8px -6px #777; -moz-box-shadow: 0 5px 8px -6px #777;
@ -65,13 +175,29 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te
.navbar-default .navbar-toggle {border-color: #000; } .navbar-default .navbar-toggle {border-color: #000; }
.cover { margin-bottom: 10px; } .cover { margin-bottom: 10px; }
.cover-height { max-height: 100px;} .cover-height { max-height: 100px;}
.col-sm-2 a .cover-small { .col-sm-2 a .cover-small {
margin: 5px; margin: 5px;
max-height: 200px; max-height: 200px;
} }
.btn-file {position: relative; overflow: hidden;} .btn-file {position: relative; overflow: hidden;}
.btn-file input[type=file] {position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; font-size: 100px; text-align: right; filter: alpha(opacity=0); opacity: 0; outline: none; background: white; cursor: inherit; display: block;}
.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
.btn-toolbar .btn,.discover .btn { margin-bottom: 5px; } .btn-toolbar .btn,.discover .btn { margin-bottom: 5px; }
.button-link {color: #fff; } .button-link {color: #fff; }
@ -87,11 +213,11 @@ table .bg-dark-danger a {color: #fff;}
table .bg-dark-danger:hover {background-color: #c9302c; } table .bg-dark-danger:hover {background-color: #c9302c; }
table .bg-primary:hover {background-color: #1C5484; } table .bg-primary:hover {background-color: #1C5484; }
table .bg-primary a {color: #fff; } table .bg-primary a {color: #fff; }
.block-label {display: block;} .block-label {display: block;}
.fake-input {position: absolute; pointer-events: none; top: 0; } .fake-input {position: absolute; pointer-events: none; top: 0; }
input.pill { position: absolute; opacity: 0; } input.pill { position: absolute; opacity: 0; }
input.pill + label { input.pill + label {
border: 2px solid #45b29d; border: 2px solid #45b29d;
border-radius: 15px; border-radius: 15px;
@ -103,23 +229,19 @@ input.pill + label {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
input.pill:checked + label { input.pill:checked + label {
background-color: #45b29d; background-color: #45b29d;
border-color: #fff; border-color: #fff;
color: #fff; color: #fff;
} }
input.pill:not(:checked) + label .glyphicon {
display: none; input.pill:not(:checked) + label .glyphicon { display: none; }
}
.author-bio img { margin: 0 1em 1em 0; } .author-bio img { margin: 0 1em 1em 0; }
.author-link { display: inline-block; margin-top: 10px; width: 100px; } .author-link { display: inline-block; margin-top: 10px; width: 100px; }
.author-link img { display: block; height: 100%; } .author-link img { display: block; height: 100%; }
#remove-from-shelves .btn, #shelf-action-errors { margin-left: 5px; }
#remove-from-shelves .btn,
#shelf-action-errors {
margin-left: 5px;
}
.tags_click, .serie_click, .language_click { margin-right: 5px; } .tags_click, .serie_click, .language_click { margin-right: 5px; }
@ -127,32 +249,38 @@ input.pill:not(:checked) + label .glyphicon {
height: 600px; height: 600px;
overflow-y: scroll; overflow-y: scroll;
} }
.media-list {
padding-right:15px; .media-list { padding-right: 15px; }
} .media-body p { text-align: justify; }
.media-body p {
text-align: justify; #meta-info img {
max-height: 150px;
max-width: 100px;
cursor: pointer;
} }
#meta-info img { max-height: 150px; max-width: 100px; cursor: pointer; }
.padded-bottom { margin-bottom: 15px; } .padded-bottom { margin-bottom: 15px; }
.upload-format-input-text { display: initial; } .upload-format-input-text { display: initial; }
#btn-upload-format { display: none; } #btn-upload-format { display: none; }
.upload-cover-input-text { display: initial; } .upload-cover-input-text { display: initial; }
#btn-upload-cover { display: none; } #btn-upload-cover { display: none; }
.panel-title > a { text-decoration: none; } .panel-title > a { text-decoration: none; }
.editable-buttons {
display:inline-block;
margin-left: 7px;
}
.editable-buttons { display:inline-block; margin-left: 7px ;}
.editable-input { display:inline-block; } .editable-input { display:inline-block; }
.editable-cancel { margin-bottom: 0px !important; margin-left: 7px !important;}
.editable-submit { margin-bottom: 0px !important;}
.filterheader { margin-bottom: 20px; } .editable-cancel {
margin-bottom: 0px !important;
margin-left: 7px !important;
}
.editable-submit { margin-bottom: 0px !important; }
.filterheader { margin-bottom: 20px; }
.errorlink { margin-top: 20px; } .errorlink { margin-top: 20px; }
.modal-body .comments { .modal-body .comments {
max-height: 300px; max-height: 300px;
overflow-y: auto; overflow-y: auto;

@ -28,7 +28,7 @@ $( 'body.mailset' ).addClass( 'admin' );
curHref = window.location.href.split('/'); curHref = window.location.href.split('/');
prevHref = document.referrer.split('/'); prevHref = document.referrer.split('/');
$( '.navbar-form.navbar-left' ) $( '.navbar-form.navbar-left' )
.before( '<div class="plexBack"><a href="' + document.referrer + '"></a></div>' ); .before( '<div class="plexBack"><a href="' + encodeURI(document.referrer) + '"></a></div>' );
if ( history.length === 1 || if ( history.length === 1 ||
curHref[0] + curHref[0] +
curHref[1] + curHref[1] +
@ -43,7 +43,7 @@ if ( history.length === 1 ||
//Weird missing a after pressing back from edit. //Weird missing a after pressing back from edit.
setTimeout(function() { setTimeout(function() {
if ( $( '.plexBack a').length < 1 ) { if ( $( '.plexBack a').length < 1 ) {
$( '.plexBack' ).append('<a href="' + document.referrer + '"></a>'); $( '.plexBack' ).append('<a href="' + encodeURI(document.referrer) + '"></a>');
} }
},10); },10);

@ -45,7 +45,7 @@ $("#sort_name").click(function() {
});*/ });*/
// Find count of middle element // Find count of middle element
if (count > 20) { if (count > 20) {
var middle = parseInt(count / 2) + (count % 2); var middle = parseInt(count / 2, 10) + (count % 2);
// search for the middle of all visibe elements // search for the middle of all visibe elements
$(".row").each(function() { $(".row").each(function() {
index++; index++;
@ -146,7 +146,7 @@ $("#all").click(function() {
// Find count of middle element // Find count of middle element
var listItems = $("#list").children(".row"); var listItems = $("#list").children(".row");
var listlength = listItems.length; var listlength = listItems.length;
var middle = parseInt(listlength / 2) + (listlength % 2); var middle = parseInt(listlength / 2, 10) + (listlength % 2);
// go through all elements and make them visible // go through all elements and make them visible
listItems.each(function() { listItems.each(function() {
$(this).show(); $(this).show();
@ -178,7 +178,7 @@ $(".char").click(function() {
}); });
if (count > 20) { if (count > 20) {
// Find count of middle element // Find count of middle element
var middle = parseInt(count / 2) + (count % 2); var middle = parseInt(count / 2, 10) + (count % 2);
// search for the middle of all visibe elements // search for the middle of all visibe elements
$(".row").each(function() { $(".row").each(function() {
index++; index++;

@ -112,7 +112,7 @@ $(function() {
return ""; return "";
}, },
url: path + "/../../ajax/listrestriction/" + type, url: path + "/../../ajax/listrestriction/" + type,
rowStyle: function(row, index) { rowStyle: function(row) {
// console.log('Reihe :' + row + " Index :" + index); // console.log('Reihe :' + row + " Index :" + index);
if (row.id.charAt(0) === "a") { if (row.id.charAt(0) === "a") {
return {classes: "bg-primary"}; return {classes: "bg-primary"};
@ -120,15 +120,15 @@ $(function() {
return {classes: "bg-dark-danger"}; return {classes: "bg-dark-danger"};
} }
}, },
onClickCell: function (field, value, row, $element) { onClickCell: function (field, value, row) {
if (field == 3) { if (field === 3) {
$.ajax ({ $.ajax ({
type: "Post", type: "Post",
data: "id=" + row.id + "&type=" + row.type + "&Element=" + row.Element, data: "id=" + row.id + "&type=" + row.type + "&Element=" + row.Element,
url: path + "/../../ajax/deleterestriction/" + type, url: path + "/../../ajax/deleterestriction/" + type,
async: true, async: true,
timeout: 900, timeout: 900,
success:function(data) { success:function() {
$.ajax({ $.ajax({
method:"get", method:"get",
url: path + "/../../ajax/listrestriction/" + type, url: path + "/../../ajax/listrestriction/" + type,
@ -145,14 +145,14 @@ $(function() {
striped: false striped: false
}); });
$("#restrict-elements-table").removeClass("table-hover"); $("#restrict-elements-table").removeClass("table-hover");
$("#restrict-elements-table").on("editable-save.bs.table", function (e, field, row, old, $el) { $("#restrict-elements-table").on("editable-save.bs.table", function (e, field, row) {
$.ajax({ $.ajax({
url: path + "/../../ajax/editrestriction/" + type, url: path + "/../../ajax/editrestriction/" + type,
type: "Post", type: "Post",
data: row data: row
}); });
}); });
$("[id^=submit_]").click(function(event) { $("[id^=submit_]").click(function() {
$(this)[0].blur(); $(this)[0].blur();
$.ajax({ $.ajax({
url: path + "/../../ajax/addrestriction/" + type, url: path + "/../../ajax/addrestriction/" + type,
@ -196,7 +196,7 @@ $(function() {
}); });
/* Function for deleting domain restrictions */ /* Function for deleting domain restrictions */
function TableActions (value, row, index) { function TableActions (value, row) {
return [ return [
"<a class=\"danger remove\" data-toggle=\"modal\" data-target=\"#DeleteDomain\" data-domain-id=\"" + row.id "<a class=\"danger remove\" data-toggle=\"modal\" data-target=\"#DeleteDomain\" data-domain-id=\"" + row.id
+ "\" title=\"Remove\">", + "\" title=\"Remove\">",
@ -206,7 +206,7 @@ function TableActions (value, row, index) {
} }
/* Function for deleting domain restrictions */ /* Function for deleting domain restrictions */
function RestrictionActions (value, row, index) { function RestrictionActions (value, row) {
return [ return [
"<div class=\"danger remove\" data-restriction-id=\"" + row.id + "\" title=\"Remove\">", "<div class=\"danger remove\" data-restriction-id=\"" + row.id + "\" title=\"Remove\">",
"<i class=\"glyphicon glyphicon-trash\"></i>", "<i class=\"glyphicon glyphicon-trash\"></i>",

@ -344,7 +344,7 @@
<input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off">
</div> </div>
</div> </div>
{% if rarfile_support %} {% if feature_support['rar'] %}
<div class="form-group"> <div class="form-group">
<label for="config_rarfile_location">{{_('Location of Unrar binary')}}</label> <label for="config_rarfile_location">{{_('Location of Unrar binary')}}</label>
<input type="text" class="form-control" name="config_rarfile_location" id="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off"> <input type="text" class="form-control" name="config_rarfile_location" id="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off">

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dcterms="http://purl.org/dc/terms/"> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/terms/">
<id>urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2</id> <id>urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2</id>
<updated>{{ current_time }}</updated> <updated>{{ current_time }}</updated>
<link rel="self" <link rel="self"

@ -42,7 +42,7 @@
<form class="navbar-form navbar-left" role="search" action="{{url_for('web.search')}}" method="GET"> <form class="navbar-form navbar-left" role="search" action="{{url_for('web.search')}}" method="GET">
<div class="form-group input-group input-group-sm"> <div class="form-group input-group input-group-sm">
<label for="query" class="sr-only">{{_('Search')}}</label> <label for="query" class="sr-only">{{_('Search')}}</label>
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}"> <input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" id="query_submit" class="btn btn-default">{{_('Search')}}</button> <button type="submit" id="query_submit" class="btn btn-default">{{_('Search')}}</button>
</span> </span>
@ -224,6 +224,11 @@
$("#btn-upload").change(function() { $("#btn-upload").change(function() {
$("#form-upload").submit(); $("#form-upload").submit();
}); });
$(document).ready(function() {
var inp = $('#query').first()
var val = inp.val()
inp.val('').blur().focus().val(val)
});
}); });
</script> </script>
{% block js %}{% endblock %} {% block js %}{% endblock %}

@ -2,10 +2,10 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
{% if entries|length < 1 %} {% if entries|length < 1 %}
<h2>{{_('No Results Found')}} {{searchterm}}</h2> <h2>{{_('No Results Found')}} {{adv_searchterm}}</h2>
<p>{{_('Search Term:')}} {{searchterm}}</p> <p>{{_('Search Term:')}} {{adv_searchterm}}</p>
{% else %} {% else %}
<h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2> <h2>{{entries|length}} {{_('Results for:')}} {{adv_searchterm}}</h2>
{% if g.user.is_authenticated %} {% if g.user.is_authenticated %}
{% if g.user.shelf.all() or g.shelves_access %} {% if g.user.shelf.all() or g.shelves_access %}
<div id="shelf-actions" class="btn-toolbar" role="toolbar"> <div id="shelf-actions" class="btn-toolbar" role="toolbar">
@ -28,8 +28,8 @@
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<div class="filterheader hidden-xs hidden-sm"><!-- ToDo: Implement filter for search results --> <!--div class="filterheader hidden-xs hidden-sm"--><!-- ToDo: Implement filter for search results -->
<a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='new')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a> <!--a id="new" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='new')}}"><span class="glyphicon glyphicon-sort-by-order"></span></a>
<a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='old')}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a> <a id="old" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='old')}}"><span class="glyphicon glyphicon-sort-by-order-alt"></span></a>
<a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a> <a id="asc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='abc')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet"></span></a>
<a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a> <a id="desc" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='zyx')}}"><span class="glyphicon glyphicon-font"></span><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
@ -39,7 +39,7 @@
<div class="btn-group character" role="group"> <div class="btn-group character" role="group">
<a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a> <a id="no_shelf" class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort='pubold')}}"><span class="glyphicon glyphicon-list"></span><b>?</b></a>
<div id="all" class="btn btn-primary">{{_('All')}}</div> <div id="all" class="btn btn-primary">{{_('All')}}</div>
</div> </div-->
{% endif %} {% endif %}
<div class="row"> <div class="row">

@ -62,7 +62,7 @@ def get_sidebar_config(kwargs=None):
sidebar = list() sidebar = list()
sidebar.append({"glyph": "glyphicon-book", "text": _('Recently Added'), "link": 'web.index', "id": "new", sidebar.append({"glyph": "glyphicon-book", "text": _('Recently Added'), "link": 'web.index', "id": "new",
"visibility": constants.SIDEBAR_RECENT, 'public': True, "page": "root", "visibility": constants.SIDEBAR_RECENT, 'public': True, "page": "root",
"show_text": _('Show recent books'), "config_show": True}) "show_text": _('Show recent books'), "config_show":False})
sidebar.append({"glyph": "glyphicon-fire", "text": _('Hot Books'), "link": 'web.books_list', "id": "hot", sidebar.append({"glyph": "glyphicon-fire", "text": _('Hot Books'), "link": 'web.books_list', "id": "hot",
"visibility": constants.SIDEBAR_HOT, 'public': True, "page": "hot", "visibility": constants.SIDEBAR_HOT, 'public': True, "page": "hot",
"show_text": _('Show Hot Books'), "config_show": True}) "show_text": _('Show Hot Books'), "config_show": True})
@ -161,6 +161,8 @@ class UserBase:
return self.default_language return self.default_language
def check_visibility(self, value): def check_visibility(self, value):
if value == constants.SIDEBAR_RECENT:
return True
return constants.has_flag(self.sidebar_view, value) return constants.has_flag(self.sidebar_view, value)
def show_detail_random(self): def show_detail_random(self):
@ -621,7 +623,7 @@ def create_anonymous_user(session):
session.add(user) session.add(user)
try: try:
session.commit() session.commit()
except Exception as e: except Exception:
session.rollback() session.rollback()

@ -85,7 +85,7 @@ def process(tmp_file_path, original_file_name, original_file_extension, rarExcec
meta = epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension) meta = epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension)
if ".FB2" == original_file_extension.upper() and use_fb2_meta is True: if ".FB2" == original_file_extension.upper() and use_fb2_meta is True:
meta = fb2.get_fb2_info(tmp_file_path, original_file_extension) meta = fb2.get_fb2_info(tmp_file_path, original_file_extension)
if original_file_extension.upper() in ['.CBZ', '.CBT', 'CBR']: if original_file_extension.upper() in ['.CBZ', '.CBT', '.CBR']:
meta = comic.get_comic_info(tmp_file_path, meta = comic.get_comic_info(tmp_file_path,
original_file_name, original_file_name,
original_file_extension, original_file_extension,

@ -38,8 +38,12 @@ from flask import render_template, request, redirect, send_from_directory, make_
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import login_user, logout_user, login_required, current_user from flask_login import login_user, logout_user, login_required, current_user
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import text, func, true, false, not_, and_, exists, or_ from sqlalchemy.sql.expression import text, func, true, false, not_, and_, or_
from werkzeug.exceptions import default_exceptions, FailedDependency from werkzeug.exceptions import default_exceptions
try:
from werkzeug.exceptions import FailedDependency
except ImportError:
from werkzeug.exceptions import UnprocessableEntity as FailedDependency
from werkzeug.datastructures import Headers from werkzeug.datastructures import Headers
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
@ -750,7 +754,7 @@ def render_category_books(page, book_id, order):
name = db.session.query(db.Tags).filter(db.Tags.id == book_id).first() name = db.session.query(db.Tags).filter(db.Tags.id == book_id).first()
if name: if name:
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id), entries, random, pagination = fill_indexpage(page, db.Books, db.Books.tags.any(db.Tags.id == book_id),
[db.Series.name, db.Books.series_index, order[0]], [order[0], db.Series.name, db.Books.series_index],
db.books_series_link, db.Series) db.books_series_link, db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id, 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")
@ -926,9 +930,17 @@ def search():
for element in entries: for element in entries:
ids.append(element.id) ids.append(element.id)
searched_ids[current_user.id] = ids searched_ids[current_user.id] = ids
return render_title_template('search.html', searchterm=term, entries=entries, title=_(u"Search"), page="search") return render_title_template('search.html',
searchterm=term,
adv_searchterm=term,
entries=entries,
title=_(u"Search"),
page="search")
else: else:
return render_title_template('search.html', searchterm="", title=_(u"Search"), page="search") return render_title_template('search.html',
searchterm="",
title=_(u"Search"),
page="search")
@web.route("/advanced_search", methods=['GET']) @web.route("/advanced_search", methods=['GET'])
@ -937,7 +949,7 @@ def advanced_search():
# Build custom columns names # Build custom columns names
cc = get_cc_columns() cc = get_cc_columns()
db.session.connection().connection.connection.create_function("lower", 1, lcase) db.session.connection().connection.connection.create_function("lower", 1, lcase)
q = db.session.query(db.Books).filter(common_filters()) q = db.session.query(db.Books).filter(common_filters()).order_by(db.Books.sort)
include_tag_inputs = request.args.getlist('include_tag') include_tag_inputs = request.args.getlist('include_tag')
exclude_tag_inputs = request.args.getlist('exclude_tag') exclude_tag_inputs = request.args.getlist('exclude_tag')
@ -1066,7 +1078,7 @@ def advanced_search():
for element in q: for element in q:
ids.append(element.id) ids.append(element.id)
searched_ids[current_user.id] = ids searched_ids[current_user.id] = ids
return render_title_template('search.html', searchterm=searchterm, return render_title_template('search.html', adv_searchterm=searchterm,
entries=q, title=_(u"search"), page="search") entries=q, title=_(u"search"), page="search")
# prepare data for search-form # prepare data for search-form
tags = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters()) \ tags = db.session.query(db.Tags).join(db.books_tags_link).join(db.Books).filter(common_filters()) \
@ -1319,16 +1331,11 @@ def login():
log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress) log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress)
flash(_(u"Wrong Username or Password"), category="error") flash(_(u"Wrong Username or Password"), category="error")
if feature_support['oauth']:
oauth_status = get_oauth_status()
else:
oauth_status = None
next_url = url_for('web.index') next_url = url_for('web.index')
return render_title_template('login.html', return render_title_template('login.html',
title=_(u"login"), title=_(u"login"),
next_url=next_url, next_url=next_url,
config=config, config=config,
# oauth_status=oauth_status,
oauth_check=oauth_check, oauth_check=oauth_check,
mail=config.get_mail_server_configured(), page="login") mail=config.get_mail_server_configured(), page="login")

@ -300,11 +300,6 @@ class WorkerThread(threading.Thread):
# check which converter to use kindlegen is "1" # check which converter to use kindlegen is "1"
if format_old_ext == '.epub' and format_new_ext == '.mobi': if format_old_ext == '.epub' and format_new_ext == '.mobi':
if config.config_ebookconverter == 1: if config.config_ebookconverter == 1:
'''if os.name == 'nt':
command = config.config_converterpath + u' "' + file_path + u'.epub"'
if sys.version_info < (3, 0):
command = command.encode(sys.getfilesystemencoding())
else:'''
command = [config.config_converterpath, file_path + u'.epub'] command = [config.config_converterpath, file_path + u'.epub']
quotes = [1] quotes = [1]
if config.config_ebookconverter == 2: if config.config_ebookconverter == 2:
@ -314,12 +309,6 @@ class WorkerThread(threading.Thread):
# windows py 3.x no encode and as string with quotes empty element for parameters is okay # windows py 3.x no encode and as string with quotes empty element for parameters is okay
# separate handling for windows and linux # separate handling for windows and linux
quotes = [1,2] quotes = [1,2]
'''if os.name == 'nt':
command = config.config_converterpath + u' "' + file_path + format_old_ext + u'" "' + \
file_path + format_new_ext + u'" ' + config.config_calibre
if sys.version_info < (3, 0):
command = command.encode(sys.getfilesystemencoding())
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)]
quotes_index = 3 quotes_index = 3

@ -31,8 +31,7 @@ rarfile>=2.7
# other # other
natsort>=2.2.0,<7.1.0 natsort>=2.2.0,<7.1.0
git+https://github.com/OzzieIsaacs/comicapi.git@ad8bfe5a1c31db882480433f86db2c5c57634a3f#egg=comicapi git+https://github.com/OzzieIsaacs/comicapi.git@15dff9ce4e1ffed29ba4a2feadfcdb6bed00bcad#egg=comicapi
#Kobo integration #Kobo integration
jsonschema>=3.2.0,<3.3.0 jsonschema>=3.2.0,<3.3.0

Loading…
Cancel
Save