mirror of
https://github.com/janeczku/calibre-web
synced 2025-10-28 05:47:43 +00:00
Merge branch 'master' into Develop
# Conflicts: # cps/editbooks.py
This commit is contained in:
347
cps/editbooks.py
347
cps/editbooks.py
@@ -30,6 +30,7 @@ from uuid import uuid4
|
||||
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy.exc import OperationalError
|
||||
|
||||
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper
|
||||
from . import config, get_locale, db, ub, worker
|
||||
@@ -444,10 +445,16 @@ def upload_single_file(request, book, book_id):
|
||||
if is_format:
|
||||
log.warning('Book format %s already existing', file_ext.upper())
|
||||
else:
|
||||
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
|
||||
db.session.add(db_format)
|
||||
db.session.commit()
|
||||
db.update_title_sort(config)
|
||||
try:
|
||||
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
|
||||
db.session.add(db_format)
|
||||
db.session.commit()
|
||||
db.update_title_sort(config)
|
||||
except OperationalError as e:
|
||||
db.session.rollback()
|
||||
log.error('Database error: %s', e)
|
||||
flash(_(u"Database error: %(error)s.", error=e), category="error")
|
||||
return redirect(url_for('web.show_book', book_id=book.id))
|
||||
|
||||
# Queue uploader info
|
||||
uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title)
|
||||
@@ -455,7 +462,8 @@ def upload_single_file(request, book, book_id):
|
||||
"<a href=\"" + url_for('web.show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
||||
|
||||
return uploader.process(
|
||||
saved_filename, *os.path.splitext(requested_file.filename))
|
||||
saved_filename, *os.path.splitext(requested_file.filename),
|
||||
rarExcecutable=config.config_rarfile_location)
|
||||
|
||||
|
||||
def upload_cover(request, book):
|
||||
@@ -653,181 +661,188 @@ def upload():
|
||||
abort(404)
|
||||
if request.method == 'POST' and 'btn-upload' in request.files:
|
||||
for requested_file in request.files.getlist("btn-upload"):
|
||||
# create the function for sorting...
|
||||
db.update_title_sort(config)
|
||||
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
||||
|
||||
# check if file extension is correct
|
||||
if '.' in requested_file.filename:
|
||||
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
|
||||
if file_ext not in constants.EXTENSIONS_UPLOAD:
|
||||
flash(
|
||||
_("File extension '%(ext)s' is not allowed to be uploaded to this server",
|
||||
ext=file_ext), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
else:
|
||||
flash(_('File to be uploaded must have an extension'), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
# extract metadata from file
|
||||
try:
|
||||
meta = uploader.upload(requested_file, config.config_rarfile_location)
|
||||
except (IOError, OSError):
|
||||
log.error("File %s could not saved to temp dir", requested_file.filename)
|
||||
flash(_(u"File %(filename)s could not saved to temp dir",
|
||||
filename= requested_file.filename), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
title = meta.title
|
||||
authr = meta.author
|
||||
tags = meta.tags
|
||||
series = meta.series
|
||||
series_index = meta.series_id
|
||||
# create the function for sorting...
|
||||
db.update_title_sort(config)
|
||||
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
||||
|
||||
if title != _(u'Unknown') and authr != _(u'Unknown'):
|
||||
entry = helper.check_exists_book(authr, title)
|
||||
if entry:
|
||||
log.info("Uploaded book probably exists in library")
|
||||
flash(_(u"Uploaded book probably exists in the library, consider to change before upload new: ")
|
||||
+ Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning")
|
||||
# check if file extension is correct
|
||||
if '.' in requested_file.filename:
|
||||
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
|
||||
if file_ext not in constants.EXTENSIONS_UPLOAD:
|
||||
flash(
|
||||
_("File extension '%(ext)s' is not allowed to be uploaded to this server",
|
||||
ext=file_ext), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
else:
|
||||
flash(_('File to be uploaded must have an extension'), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
# handle authors
|
||||
is_author = db.session.query(db.Authors).filter(db.Authors.name == authr).first()
|
||||
if is_author:
|
||||
db_author = is_author
|
||||
authr= is_author.name
|
||||
else:
|
||||
db_author = db.Authors(authr, helper.get_sorted_author(authr), "")
|
||||
db.session.add(db_author)
|
||||
|
||||
title_dir = helper.get_valid_filename(title)
|
||||
author_dir = helper.get_valid_filename(authr)
|
||||
filepath = os.path.join(config.config_calibre_dir, author_dir, title_dir)
|
||||
saved_filename = os.path.join(filepath, title_dir + meta.extension.lower())
|
||||
|
||||
# check if file path exists, otherwise create it, copy file to calibre path and delete temp file
|
||||
if not os.path.exists(filepath):
|
||||
# extract metadata from file
|
||||
try:
|
||||
os.makedirs(filepath)
|
||||
except OSError:
|
||||
log.error("Failed to create path %s (Permission denied)", filepath)
|
||||
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
|
||||
meta = uploader.upload(requested_file, config.config_rarfile_location)
|
||||
except (IOError, OSError):
|
||||
log.error("File %s could not saved to temp dir", requested_file.filename)
|
||||
flash(_(u"File %(filename)s could not saved to temp dir",
|
||||
filename= requested_file.filename), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
try:
|
||||
copyfile(meta.file_path, saved_filename)
|
||||
except OSError:
|
||||
log.error("Failed to store file %s (Permission denied)", saved_filename)
|
||||
flash(_(u"Failed to store file %(file)s (Permission denied).", file=saved_filename), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
try:
|
||||
os.unlink(meta.file_path)
|
||||
except OSError:
|
||||
log.error("Failed to delete file %(file)s (Permission denied)", meta.file_path)
|
||||
flash(_(u"Failed to delete file %(file)s (Permission denied).", file= meta.file_path),
|
||||
category="warning")
|
||||
title = meta.title
|
||||
authr = meta.author
|
||||
tags = meta.tags
|
||||
series = meta.series
|
||||
series_index = meta.series_id
|
||||
|
||||
if meta.cover is None:
|
||||
has_cover = 0
|
||||
copyfile(os.path.join(constants.STATIC_DIR, 'generic_cover.jpg'),
|
||||
os.path.join(filepath, "cover.jpg"))
|
||||
else:
|
||||
has_cover = 1
|
||||
if title != _(u'Unknown') and authr != _(u'Unknown'):
|
||||
entry = helper.check_exists_book(authr, title)
|
||||
if entry:
|
||||
log.info("Uploaded book probably exists in library")
|
||||
flash(_(u"Uploaded book probably exists in the library, consider to change before upload new: ")
|
||||
+ Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning")
|
||||
|
||||
# handle series
|
||||
db_series = None
|
||||
is_series = db.session.query(db.Series).filter(db.Series.name == series).first()
|
||||
if is_series:
|
||||
db_series = is_series
|
||||
elif series != '':
|
||||
db_series = db.Series(series, "")
|
||||
db.session.add(db_series)
|
||||
|
||||
# add language actually one value in list
|
||||
input_language = meta.languages
|
||||
db_language = None
|
||||
if input_language != "":
|
||||
input_language = isoLanguages.get(name=input_language).part3
|
||||
hasLanguage = db.session.query(db.Languages).filter(db.Languages.lang_code == input_language).first()
|
||||
if hasLanguage:
|
||||
db_language = hasLanguage
|
||||
# handle authors
|
||||
is_author = db.session.query(db.Authors).filter(db.Authors.name == authr).first()
|
||||
if is_author:
|
||||
db_author = is_author
|
||||
authr= is_author.name
|
||||
else:
|
||||
db_language = db.Languages(input_language)
|
||||
db.session.add(db_language)
|
||||
db_author = db.Authors(authr, helper.get_sorted_author(authr), "")
|
||||
db.session.add(db_author)
|
||||
|
||||
# If the language of the file is excluded from the users view, it's not imported, to allow the user to view
|
||||
# the book it's language is set to the filter language
|
||||
if db_language != current_user.filter_language() and current_user.filter_language() != "all":
|
||||
db_language = db.session.query(db.Languages).\
|
||||
filter(db.Languages.lang_code == current_user.filter_language()).first()
|
||||
title_dir = helper.get_valid_filename(title)
|
||||
author_dir = helper.get_valid_filename(authr)
|
||||
filepath = os.path.join(config.config_calibre_dir, author_dir, title_dir)
|
||||
saved_filename = os.path.join(filepath, title_dir + meta.extension.lower())
|
||||
|
||||
# combine path and normalize path from windows systems
|
||||
path = os.path.join(author_dir, title_dir).replace('\\', '/')
|
||||
# Calibre adds books with utc as timezone
|
||||
db_book = db.Books(title, "", db_author.sort, datetime.utcnow(), datetime(101, 1, 1),
|
||||
series_index, datetime.utcnow(), path, has_cover, db_author, [], db_language)
|
||||
db_book.authors.append(db_author)
|
||||
if db_series:
|
||||
db_book.series.append(db_series)
|
||||
if db_language is not None:
|
||||
db_book.languages.append(db_language)
|
||||
file_size = os.path.getsize(saved_filename)
|
||||
db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir)
|
||||
# check if file path exists, otherwise create it, copy file to calibre path and delete temp file
|
||||
if not os.path.exists(filepath):
|
||||
try:
|
||||
os.makedirs(filepath)
|
||||
except OSError:
|
||||
log.error("Failed to create path %s (Permission denied)", filepath)
|
||||
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
try:
|
||||
copyfile(meta.file_path, saved_filename)
|
||||
os.unlink(meta.file_path)
|
||||
except OSError as e:
|
||||
log.error("Failed to move file %s: %s", saved_filename, e)
|
||||
flash(_(u"Failed to Move File %(file)s: %(error)s", file=saved_filename, error=e), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
# handle tags
|
||||
input_tags = tags.split(',')
|
||||
input_tags = list(map(lambda it: it.strip(), input_tags))
|
||||
if input_tags[0] !="":
|
||||
modify_database_object(input_tags, db_book.tags, db.Tags, db.session, 'tags')
|
||||
|
||||
# flush content, get db_book.id available
|
||||
db_book.data.append(db_data)
|
||||
db.session.add(db_book)
|
||||
db.session.flush()
|
||||
|
||||
# add comment
|
||||
book_id = db_book.id
|
||||
upload_comment = Markup(meta.description).unescape()
|
||||
if upload_comment != "":
|
||||
db.session.add(db.Comments(upload_comment, book_id))
|
||||
|
||||
# save data to database, reread data
|
||||
db.session.commit()
|
||||
db.update_title_sort(config)
|
||||
# Reread book. It's important not to filter the result, as it could have language which hide it from
|
||||
# current users view (tags are not stored/extracted from metadata and could also be limited)
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
# upload book to gdrive if nesseccary and add "(bookid)" to folder name
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
error = helper.update_dir_stucture(book.id, config.config_calibre_dir)
|
||||
|
||||
# move cover to final directory, including book id
|
||||
if has_cover:
|
||||
move(meta.cover, os.path.join(filepath+ ' ({})'.format(book_id), "cover.jpg"))
|
||||
db.session.commit()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
if error:
|
||||
flash(error, category="error")
|
||||
uploadText=_(u"File %(file)s uploaded", file=book.title)
|
||||
worker.add_upload(current_user.nickname,
|
||||
"<a href=\"" + url_for('web.show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
||||
|
||||
# create data for displaying display Full language name instead of iso639.part3language
|
||||
if db_language is not None:
|
||||
book.languages[0].language_name = _(meta.languages)
|
||||
author_names = []
|
||||
for author in db_book.authors:
|
||||
author_names.append(author.name)
|
||||
if len(request.files.getlist("btn-upload")) < 2:
|
||||
if current_user.role_edit() or current_user.role_admin():
|
||||
resp = {"location": url_for('editbook.edit_book', book_id=db_book.id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
if meta.cover is None:
|
||||
has_cover = 0
|
||||
copyfile(os.path.join(constants.STATIC_DIR, 'generic_cover.jpg'),
|
||||
os.path.join(filepath, "cover.jpg"))
|
||||
else:
|
||||
resp = {"location": url_for('web.show_book', book_id=db_book.id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
has_cover = 1
|
||||
|
||||
# handle series
|
||||
db_series = None
|
||||
is_series = db.session.query(db.Series).filter(db.Series.name == series).first()
|
||||
if is_series:
|
||||
db_series = is_series
|
||||
elif series != '':
|
||||
db_series = db.Series(series, "")
|
||||
db.session.add(db_series)
|
||||
|
||||
# add language actually one value in list
|
||||
input_language = meta.languages
|
||||
db_language = None
|
||||
if input_language != "":
|
||||
input_language = isoLanguages.get(name=input_language).part3
|
||||
hasLanguage = db.session.query(db.Languages).filter(db.Languages.lang_code == input_language).first()
|
||||
if hasLanguage:
|
||||
db_language = hasLanguage
|
||||
else:
|
||||
db_language = db.Languages(input_language)
|
||||
db.session.add(db_language)
|
||||
|
||||
# If the language of the file is excluded from the users view, it's not imported, to allow the user to view
|
||||
# the book it's language is set to the filter language
|
||||
if db_language != current_user.filter_language() and current_user.filter_language() != "all":
|
||||
db_language = db.session.query(db.Languages).\
|
||||
filter(db.Languages.lang_code == current_user.filter_language()).first()
|
||||
|
||||
# combine path and normalize path from windows systems
|
||||
path = os.path.join(author_dir, title_dir).replace('\\', '/')
|
||||
# Calibre adds books with utc as timezone
|
||||
db_book = db.Books(title, "", db_author.sort, datetime.utcnow(), datetime(101, 1, 1),
|
||||
series_index, datetime.utcnow(), path, has_cover, db_author, [], db_language)
|
||||
db_book.authors.append(db_author)
|
||||
if db_series:
|
||||
db_book.series.append(db_series)
|
||||
if db_language is not None:
|
||||
db_book.languages.append(db_language)
|
||||
file_size = os.path.getsize(saved_filename)
|
||||
db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir)
|
||||
|
||||
# handle tags
|
||||
input_tags = tags.split(',')
|
||||
input_tags = list(map(lambda it: it.strip(), input_tags))
|
||||
if input_tags[0] !="":
|
||||
modify_database_object(input_tags, db_book.tags, db.Tags, db.session, 'tags')
|
||||
|
||||
# flush content, get db_book.id available
|
||||
db_book.data.append(db_data)
|
||||
db.session.add(db_book)
|
||||
db.session.flush()
|
||||
|
||||
# add comment
|
||||
book_id = db_book.id
|
||||
upload_comment = Markup(meta.description).unescape()
|
||||
if upload_comment != "":
|
||||
db.session.add(db.Comments(upload_comment, book_id))
|
||||
|
||||
# save data to database, reread data
|
||||
db.session.commit()
|
||||
db.update_title_sort(config)
|
||||
# Reread book. It's important not to filter the result, as it could have language which hide it from
|
||||
# current users view (tags are not stored/extracted from metadata and could also be limited)
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
# upload book to gdrive if nesseccary and add "(bookid)" to folder name
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
error = helper.update_dir_stucture(book.id, config.config_calibre_dir)
|
||||
|
||||
# move cover to final directory, including book id
|
||||
if has_cover:
|
||||
try:
|
||||
new_coverpath = os.path.join(filepath+ ' ({})'.format(book_id), "cover.jpg")
|
||||
copyfile(meta.cover, new_coverpath)
|
||||
os.unlink(meta.cover)
|
||||
except OSError as e:
|
||||
log.error("Failed to move cover file %s: %s", new_coverpath, e)
|
||||
flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath,
|
||||
error=e),
|
||||
category="error")
|
||||
db.session.commit()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
if error:
|
||||
flash(error, category="error")
|
||||
uploadText=_(u"File %(file)s uploaded", file=book.title)
|
||||
worker.add_upload(current_user.nickname,
|
||||
"<a href=\"" + url_for('web.show_book', book_id=book.id) + "\">" + uploadText + "</a>")
|
||||
|
||||
# create data for displaying display Full language name instead of iso639.part3language
|
||||
if db_language is not None:
|
||||
book.languages[0].language_name = _(meta.languages)
|
||||
author_names = []
|
||||
for author in db_book.authors:
|
||||
author_names.append(author.name)
|
||||
if len(request.files.getlist("btn-upload")) < 2:
|
||||
if current_user.role_edit() or current_user.role_admin():
|
||||
resp = {"location": url_for('editbook.edit_book', book_id=db_book.id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
else:
|
||||
resp = {"location": url_for('web.show_book', book_id=db_book.id)}
|
||||
return Response(json.dumps(resp), mimetype='application/json')
|
||||
except OperationalError as e:
|
||||
db.session.rollback()
|
||||
log.error("Database error: %s", e)
|
||||
flash(_(u"Database error: %(error)s.", error=e), category="error")
|
||||
return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json')
|
||||
|
||||
|
||||
@editbook.route("/admin/book/convert/<int:book_id>", methods=['POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
|
||||
Reference in New Issue
Block a user