Merge branch 'master' into Develop
This commit is contained in:
commit
f066926fc9
|
@ -409,7 +409,8 @@ def edit_book_publisher(to_save, book):
|
||||||
if to_save["publisher"]:
|
if to_save["publisher"]:
|
||||||
publisher = to_save["publisher"].rstrip().strip()
|
publisher = to_save["publisher"].rstrip().strip()
|
||||||
if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name):
|
if len(book.publishers) == 0 or (len(book.publishers) > 0 and publisher != book.publishers[0].name):
|
||||||
changed |= modify_database_object([publisher], book.publishers, db.Publishers, calibre_db.session, 'publisher')
|
changed |= modify_database_object([publisher], book.publishers, db.Publishers, calibre_db.session,
|
||||||
|
'publisher')
|
||||||
elif len(book.publishers):
|
elif len(book.publishers):
|
||||||
changed |= modify_database_object([], book.publishers, db.Publishers, calibre_db.session, 'publisher')
|
changed |= modify_database_object([], book.publishers, db.Publishers, calibre_db.session, 'publisher')
|
||||||
return changed
|
return changed
|
||||||
|
|
|
@ -22,6 +22,7 @@ import zipfile
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from . import isoLanguages
|
from . import isoLanguages
|
||||||
|
from .helper import split_authors
|
||||||
from .constants import BookMeta
|
from .constants import BookMeta
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,9 +65,9 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
|
||||||
tmp = p.xpath('dc:%s/text()' % s, namespaces=ns)
|
tmp = p.xpath('dc:%s/text()' % s, namespaces=ns)
|
||||||
if len(tmp) > 0:
|
if len(tmp) > 0:
|
||||||
if s == 'creator':
|
if s == 'creator':
|
||||||
epub_metadata[s] = ' & '.join(p.xpath('dc:%s/text()' % s, namespaces=ns))
|
epub_metadata[s] = ' & '.join(split_authors(p.xpath('dc:%s/text()' % s, namespaces=ns)))
|
||||||
elif s == 'subject':
|
elif s == 'subject':
|
||||||
epub_metadata[s] = ', '.join(p.xpath('dc:%s/text()' % s, namespaces=ns))
|
epub_metadata[s] = ', '.join(p.xpath('dc:%s/text()' % s, namespaces=ns))
|
||||||
else:
|
else:
|
||||||
epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0]
|
epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -21,7 +21,6 @@ from __future__ import division, print_function, unicode_literals
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
import json
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -36,7 +35,7 @@ from babel.units import format_unit
|
||||||
from flask import send_from_directory, make_response, redirect, abort
|
from flask import send_from_directory, make_response, redirect, abort
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy.sql.expression import true, false, and_, or_, text, func
|
from sqlalchemy.sql.expression import true, false, and_, text, func
|
||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
from . import calibre_db
|
from . import calibre_db
|
||||||
|
@ -59,10 +58,9 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
use_PIL = False
|
use_PIL = False
|
||||||
|
|
||||||
from . import logger, config, get_locale, db, ub, isoLanguages, worker
|
from . import logger, config, get_locale, db, ub, worker
|
||||||
from . import gdriveutils as gd
|
from . import gdriveutils as gd
|
||||||
from .constants import STATIC_DIR as _STATIC_DIR
|
from .constants import STATIC_DIR as _STATIC_DIR
|
||||||
from .pagination import Pagination
|
|
||||||
from .subproc_wrapper import process_wait
|
from .subproc_wrapper import process_wait
|
||||||
from .worker import STAT_WAITING, STAT_FAIL, STAT_STARTED, STAT_FINISH_SUCCESS
|
from .worker import STAT_WAITING, STAT_FAIL, STAT_STARTED, STAT_FINISH_SUCCESS
|
||||||
from .worker import TASK_EMAIL, TASK_CONVERT, TASK_UPLOAD, TASK_CONVERT_ANY
|
from .worker import TASK_EMAIL, TASK_CONVERT, TASK_UPLOAD, TASK_CONVERT_ANY
|
||||||
|
@ -100,10 +98,10 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
|
||||||
# text = _(u"%(format)s: %(book)s", format=new_book_format, book=book.title)
|
# text = _(u"%(format)s: %(book)s", format=new_book_format, book=book.title)
|
||||||
else:
|
else:
|
||||||
settings = dict()
|
settings = dict()
|
||||||
text = (u"%s -> %s: %s" % (old_book_format, new_book_format, book.title))
|
txt = (u"%s -> %s: %s" % (old_book_format, new_book_format, book.title))
|
||||||
settings['old_book_format'] = old_book_format
|
settings['old_book_format'] = old_book_format
|
||||||
settings['new_book_format'] = new_book_format
|
settings['new_book_format'] = new_book_format
|
||||||
worker.add_convert(file_path, book.id, user_id, text, settings, kindle_mail)
|
worker.add_convert(file_path, book.id, user_id, txt, settings, kindle_mail)
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
error_message = _(u"%(format)s not found: %(fn)s",
|
error_message = _(u"%(format)s not found: %(fn)s",
|
||||||
|
@ -239,22 +237,22 @@ def get_valid_filename(value, replace_whitespace=True):
|
||||||
value = value[:-1]+u'_'
|
value = value[:-1]+u'_'
|
||||||
value = value.replace("/", "_").replace(":", "_").strip('\0')
|
value = value.replace("/", "_").replace(":", "_").strip('\0')
|
||||||
if use_unidecode:
|
if use_unidecode:
|
||||||
value = (unidecode.unidecode(value)).strip()
|
value = (unidecode.unidecode(value))
|
||||||
else:
|
else:
|
||||||
value = value.replace(u'§', u'SS')
|
value = value.replace(u'§', u'SS')
|
||||||
value = value.replace(u'ß', u'ss')
|
value = value.replace(u'ß', u'ss')
|
||||||
value = unicodedata.normalize('NFKD', value)
|
value = unicodedata.normalize('NFKD', value)
|
||||||
re_slugify = re.compile(r'[\W\s-]', re.UNICODE)
|
re_slugify = re.compile(r'[\W\s-]', re.UNICODE)
|
||||||
if isinstance(value, str): # Python3 str, Python2 unicode
|
if isinstance(value, str): # Python3 str, Python2 unicode
|
||||||
value = re_slugify.sub('', value).strip()
|
value = re_slugify.sub('', value)
|
||||||
else:
|
else:
|
||||||
value = unicode(re_slugify.sub('', value).strip())
|
value = unicode(re_slugify.sub('', value))
|
||||||
if replace_whitespace:
|
if replace_whitespace:
|
||||||
# *+:\"/<>? are replaced by _
|
# *+:\"/<>? are replaced by _
|
||||||
value = re.sub(r'[\*\+:\\\"/<>\?]+', u'_', value, flags=re.U)
|
value = re.sub(r'[*+:\\\"/<>?]+', u'_', value, flags=re.U)
|
||||||
# pipe has to be replaced with comma
|
# pipe has to be replaced with comma
|
||||||
value = re.sub(r'[\|]+', u',', value, flags=re.U)
|
value = re.sub(r'[|]+', u',', value, flags=re.U)
|
||||||
value = value[:128]
|
value = value[:128].strip()
|
||||||
if not value:
|
if not value:
|
||||||
raise ValueError("Filename cannot be empty")
|
raise ValueError("Filename cannot be empty")
|
||||||
if sys.version_info.major == 3:
|
if sys.version_info.major == 3:
|
||||||
|
@ -263,6 +261,22 @@ def get_valid_filename(value, replace_whitespace=True):
|
||||||
return value.decode('utf-8')
|
return value.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def split_authors(values):
|
||||||
|
authors_list = []
|
||||||
|
for value in values:
|
||||||
|
authors = re.split('[&;]', value)
|
||||||
|
for author in authors:
|
||||||
|
commas = author.count(',')
|
||||||
|
if commas == 1:
|
||||||
|
author_split = author.split(',')
|
||||||
|
authors_list.append(author_split[1].strip() + ' ' + author_split[0].strip())
|
||||||
|
elif commas > 1:
|
||||||
|
authors_list.extend([x.strip() for x in author.split(',')])
|
||||||
|
else:
|
||||||
|
authors_list.append(author.strip())
|
||||||
|
return authors_list
|
||||||
|
|
||||||
|
|
||||||
def get_sorted_author(value):
|
def get_sorted_author(value):
|
||||||
try:
|
try:
|
||||||
if ',' not in value:
|
if ',' not in value:
|
||||||
|
@ -270,7 +284,10 @@ def get_sorted_author(value):
|
||||||
combined = "(" + ")|(".join(regexes) + ")"
|
combined = "(" + ")|(".join(regexes) + ")"
|
||||||
value = value.split(" ")
|
value = value.split(" ")
|
||||||
if re.match(combined, value[-1].upper()):
|
if re.match(combined, value[-1].upper()):
|
||||||
value2 = value[-2] + ", " + " ".join(value[:-2]) + " " + value[-1]
|
if len(value) > 1:
|
||||||
|
value2 = value[-2] + ", " + " ".join(value[:-2]) + " " + value[-1]
|
||||||
|
else:
|
||||||
|
value2 = value[0]
|
||||||
elif len(value) == 1:
|
elif len(value) == 1:
|
||||||
value2 = value[0]
|
value2 = value[0]
|
||||||
else:
|
else:
|
||||||
|
@ -279,7 +296,10 @@ def get_sorted_author(value):
|
||||||
value2 = value
|
value2 = value
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
log.error("Sorting author %s failed: %s", value, ex)
|
log.error("Sorting author %s failed: %s", value, ex)
|
||||||
value2 = value
|
if isinstance(list, value2):
|
||||||
|
value2 = value[0]
|
||||||
|
else:
|
||||||
|
value2 = value
|
||||||
return value2
|
return value2
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,8 +323,8 @@ def delete_book_file(book, calibrepath, book_format=None):
|
||||||
log.warning("Deleting book {} failed, path {} has subfolders: {}".format(book.id,
|
log.warning("Deleting book {} failed, path {} has subfolders: {}".format(book.id,
|
||||||
book.path, folders))
|
book.path, folders))
|
||||||
return True, _("Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s",
|
return True, _("Deleting bookfolder for book %(id)s failed, path has subfolders: %(path)s",
|
||||||
id=book.id,
|
id=book.id,
|
||||||
path=book.path)
|
path=book.path)
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
except (IOError, OSError) as e:
|
except (IOError, OSError) as e:
|
||||||
log.error("Deleting book %s failed: %s", book.id, e)
|
log.error("Deleting book %s failed: %s", book.id, e)
|
||||||
|
@ -319,8 +339,8 @@ def delete_book_file(book, calibrepath, book_format=None):
|
||||||
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)
|
||||||
return True, _("Deleting book %(id)s, book path not valid: %(path)s",
|
return True, _("Deleting book %(id)s, book path not valid: %(path)s",
|
||||||
id=book.id,
|
id=book.id,
|
||||||
path=book.path)
|
path=book.path)
|
||||||
|
|
||||||
|
|
||||||
def update_dir_structure_file(book_id, calibrepath, first_author):
|
def update_dir_structure_file(book_id, calibrepath, first_author):
|
||||||
|
@ -366,6 +386,7 @@ def update_dir_structure_file(book_id, calibrepath, first_author):
|
||||||
src=path, dest=new_author_path, error=str(ex))
|
src=path, dest=new_author_path, error=str(ex))
|
||||||
# Rename all files from old names to new names
|
# Rename all files from old names to new names
|
||||||
if authordir != new_authordir or titledir != new_titledir:
|
if authordir != new_authordir or titledir != new_titledir:
|
||||||
|
new_name = ""
|
||||||
try:
|
try:
|
||||||
new_name = get_valid_filename(localbook.title) + ' - ' + get_valid_filename(new_authordir)
|
new_name = get_valid_filename(localbook.title) + ' - ' + get_valid_filename(new_authordir)
|
||||||
path_name = os.path.join(calibrepath, new_authordir, os.path.basename(path))
|
path_name = os.path.join(calibrepath, new_authordir, os.path.basename(path))
|
||||||
|
@ -474,14 +495,14 @@ def generate_random_password():
|
||||||
return "".join(s[c % len(s)] for c in os.urandom(passlen))
|
return "".join(s[c % len(s)] for c in os.urandom(passlen))
|
||||||
|
|
||||||
|
|
||||||
def uniq(input):
|
def uniq(inpt):
|
||||||
output = []
|
output = []
|
||||||
for x in input:
|
for x in inpt:
|
||||||
if x not in output:
|
if x not in output:
|
||||||
output.append(x)
|
output.append(x)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
################################## External interface
|
# ################################# External interface #################################
|
||||||
|
|
||||||
|
|
||||||
def update_dir_stucture(book_id, calibrepath, first_author=None):
|
def update_dir_stucture(book_id, calibrepath, first_author=None):
|
||||||
|
@ -558,7 +579,6 @@ def save_cover_from_url(url, book_path):
|
||||||
return False, _("Cover Format Error")
|
return False, _("Cover Format Error")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def save_cover_from_filestorage(filepath, saved_filename, img):
|
def save_cover_from_filestorage(filepath, saved_filename, img):
|
||||||
if hasattr(img, '_content'):
|
if hasattr(img, '_content'):
|
||||||
f = open(os.path.join(filepath, saved_filename), "wb")
|
f = open(os.path.join(filepath, saved_filename), "wb")
|
||||||
|
@ -617,7 +637,6 @@ def save_cover(img, book_path):
|
||||||
return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img)
|
return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def do_download_file(book, book_format, client, data, headers):
|
def do_download_file(book, book_format, client, data, headers):
|
||||||
if config.config_use_google_drive:
|
if config.config_use_google_drive:
|
||||||
startTime = time.time()
|
startTime = time.time()
|
||||||
|
@ -718,7 +737,7 @@ def render_task_status(tasklist):
|
||||||
task['runtime'] = format_runtime(task['formRuntime'])
|
task['runtime'] = format_runtime(task['formRuntime'])
|
||||||
|
|
||||||
# localize the task status
|
# localize the task status
|
||||||
if isinstance( task['stat'], int):
|
if isinstance(task['stat'], int):
|
||||||
if task['stat'] == STAT_WAITING:
|
if task['stat'] == STAT_WAITING:
|
||||||
task['status'] = _(u'Waiting')
|
task['status'] = _(u'Waiting')
|
||||||
elif task['stat'] == STAT_FAIL:
|
elif task['stat'] == STAT_FAIL:
|
||||||
|
@ -731,7 +750,7 @@ def render_task_status(tasklist):
|
||||||
task['status'] = _(u'Unknown Status')
|
task['status'] = _(u'Unknown Status')
|
||||||
|
|
||||||
# localize the task type
|
# localize the task type
|
||||||
if isinstance( task['taskType'], int):
|
if isinstance(task['taskType'], int):
|
||||||
if task['taskType'] == TASK_EMAIL:
|
if task['taskType'] == TASK_EMAIL:
|
||||||
task['taskMessage'] = _(u'E-mail: ') + task['taskMess']
|
task['taskMessage'] = _(u'E-mail: ') + task['taskMess']
|
||||||
elif task['taskType'] == TASK_CONVERT:
|
elif task['taskType'] == TASK_CONVERT:
|
||||||
|
@ -787,6 +806,7 @@ def get_cc_columns(filter_config_custom_read=False):
|
||||||
|
|
||||||
return cc
|
return cc
|
||||||
|
|
||||||
|
|
||||||
def get_download_link(book_id, book_format, client):
|
def get_download_link(book_id, book_format, client):
|
||||||
book_format = book_format.split(".")[0]
|
book_format = book_format.split(".")[0]
|
||||||
book = calibre_db.get_filtered_book(book_id)
|
book = calibre_db.get_filtered_book(book_id)
|
||||||
|
|
|
@ -24,6 +24,7 @@ from flask_babel import gettext as _
|
||||||
|
|
||||||
from . import logger, comic
|
from . import logger, comic
|
||||||
from .constants import BookMeta
|
from .constants import BookMeta
|
||||||
|
from .helper import split_authors
|
||||||
|
|
||||||
|
|
||||||
log = logger.create()
|
log = logger.create()
|
||||||
|
@ -131,7 +132,7 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
|
||||||
file_path=tmp_file_path,
|
file_path=tmp_file_path,
|
||||||
extension=original_file_extension,
|
extension=original_file_extension,
|
||||||
title=title,
|
title=title,
|
||||||
author=author,
|
author=' & '.join(split_authors([author])),
|
||||||
cover=pdf_preview(tmp_file_path, original_file_name),
|
cover=pdf_preview(tmp_file_path, original_file_name),
|
||||||
description=subject,
|
description=subject,
|
||||||
tags="",
|
tags="",
|
||||||
|
|
12
cps/web.py
12
cps/web.py
|
@ -1457,7 +1457,7 @@ def register():
|
||||||
return render_title_template('register.html', title=_(u"register"), page="register")
|
return render_title_template('register.html', title=_(u"register"), page="register")
|
||||||
else:
|
else:
|
||||||
flash(_(u"Your e-mail is not allowed to register"), category="error")
|
flash(_(u"Your e-mail is not allowed to register"), category="error")
|
||||||
log.info('Registering failed for user "%s" e-mail adress: %s', to_save['nickname'], to_save["email"])
|
log.warning('Registering failed for user "%s" e-mail address: %s', to_save['nickname'], to_save["email"])
|
||||||
return render_title_template('register.html', title=_(u"register"), page="register")
|
return render_title_template('register.html', title=_(u"register"), page="register")
|
||||||
flash(_(u"Confirmation e-mail was send to your e-mail account."), category="success")
|
flash(_(u"Confirmation e-mail was send to your e-mail account."), category="success")
|
||||||
return redirect(url_for('web.login'))
|
return redirect(url_for('web.login'))
|
||||||
|
@ -1505,7 +1505,7 @@ def login():
|
||||||
flash(_(u"Could not login: %(message)s", message=error), category="error")
|
flash(_(u"Could not login: %(message)s", message=error), category="error")
|
||||||
else:
|
else:
|
||||||
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
log.info('LDAP Login failed for user "%s" IP-adress: %s', form['username'], ipAdress)
|
log.warning('LDAP Login failed for user "%s" IP-address: %s', form['username'], ipAdress)
|
||||||
flash(_(u"Wrong Username or Password"), category="error")
|
flash(_(u"Wrong Username or Password"), category="error")
|
||||||
else:
|
else:
|
||||||
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
|
@ -1514,13 +1514,13 @@ def login():
|
||||||
ret, __ = reset_password(user.id)
|
ret, __ = reset_password(user.id)
|
||||||
if ret == 1:
|
if ret == 1:
|
||||||
flash(_(u"New Password was send to your email address"), category="info")
|
flash(_(u"New Password was send to your email address"), category="info")
|
||||||
log.info('Password reset for user "%s" IP-adress: %s', form['username'], ipAdress)
|
log.info('Password reset for user "%s" IP-address: %s', form['username'], ipAdress)
|
||||||
else:
|
else:
|
||||||
log.info(u"An unknown error occurred. Please try again later")
|
log.error(u"An unknown error occurred. Please try again later")
|
||||||
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
||||||
else:
|
else:
|
||||||
flash(_(u"Please enter valid username to reset password"), category="error")
|
flash(_(u"Please enter valid username to reset password"), category="error")
|
||||||
log.info('Username missing for password reset IP-adress: %s', ipAdress)
|
log.warning('Username missing for password reset IP-address: %s', ipAdress)
|
||||||
else:
|
else:
|
||||||
if user and check_password_hash(str(user.password), form['password']) and user.nickname != "Guest":
|
if user and check_password_hash(str(user.password), form['password']) and user.nickname != "Guest":
|
||||||
login_user(user, remember=bool(form.get('remember_me')))
|
login_user(user, remember=bool(form.get('remember_me')))
|
||||||
|
@ -1529,7 +1529,7 @@ def login():
|
||||||
config.config_is_initial = False
|
config.config_is_initial = False
|
||||||
return redirect_back(url_for("web.index"))
|
return redirect_back(url_for("web.index"))
|
||||||
else:
|
else:
|
||||||
log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress)
|
log.warning('Login failed for user "%s" IP-address: %s', form['username'], ipAdress)
|
||||||
flash(_(u"Wrong Username or Password"), category="error")
|
flash(_(u"Wrong Username or Password"), category="error")
|
||||||
|
|
||||||
next_url = request.args.get('next', default=url_for("web.index"), type=str)
|
next_url = request.args.get('next', default=url_for("web.index"), type=str)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user