1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-11-28 20:39:59 +00:00

Merged master into 617 - fixed import copy conflict

This commit is contained in:
bodybybuddha 2018-09-30 11:37:21 -04:00
commit 1b363ac720
7 changed files with 254 additions and 211 deletions

View File

@ -123,9 +123,11 @@ def pdf_preview(tmp_file_path, tmp_dir):
def get_versions(): def get_versions():
if not use_generic_pdf_cover: if not use_generic_pdf_cover:
IVersion=ImageVersion.MAGICK_VERSION IVersion = ImageVersion.MAGICK_VERSION
WVersion = ImageVersion.VERSION
else: else:
IVersion = _(u'not installed') IVersion = _(u'not installed')
WVersion = _(u'not installed')
if use_pdf_meta: if use_pdf_meta:
PVersion='v'+PyPdfVersion PVersion='v'+PyPdfVersion
else: else:
@ -134,4 +136,4 @@ def get_versions():
XVersion = 'v'+'.'.join(map(str, lxmlversion)) XVersion = 'v'+'.'.join(map(str, lxmlversion))
else: else:
XVersion = _(u'not installed') XVersion = _(u'not installed')
return {'Image Magick': IVersion, 'PyPdf': PVersion, 'lxml':XVersion} return {'Image Magick': IVersion, 'PyPdf': PVersion, 'lxml':XVersion, 'Wand Version': WVersion}

View File

@ -45,5 +45,5 @@ def versioncheck():
elif ub.config.config_ebookconverter == 2: elif ub.config.config_ebookconverter == 2:
return versionCalibre() return versionCalibre()
else: else:
return {'ebook_converter':''} return {'ebook_converter':_(u'not configured')}

View File

@ -149,19 +149,19 @@ def getDrive(drive=None, gauth=None):
drive.auth.Refresh() drive.auth.Refresh()
return drive return drive
def listRootFolders(drive=None): def listRootFolders():
drive = getDrive(drive) drive = getDrive(Gdrive.Instance().drive)
folder = "'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" folder = "'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false"
fileList = drive.ListFile({'q': folder}).GetList() fileList = drive.ListFile({'q': folder}).GetList()
return fileList return fileList
def getEbooksFolder(drive=None): def getEbooksFolder(drive):
return getFolderInFolder('root',config.config_google_drive_folder,drive) return getFolderInFolder('root',config.config_google_drive_folder,drive)
def getFolderInFolder(parentId, folderName,drive=None): def getFolderInFolder(parentId, folderName, drive):
drive = getDrive(drive) # drive = getDrive(drive)
query="" query=""
if folderName: if folderName:
query = "title = '%s' and " % folderName.replace("'", "\\'") query = "title = '%s' and " % folderName.replace("'", "\\'")
@ -190,7 +190,6 @@ def getEbooksFolderId(drive=None):
def getFile(pathId, fileName, drive): def getFile(pathId, fileName, drive):
# drive = getDrive(Gdrive.Instance().drive)
metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'")) metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'"))
fileList = drive.ListFile({'q': metaDataFile}).GetList() fileList = drive.ListFile({'q': metaDataFile}).GetList()
@ -200,8 +199,8 @@ def getFile(pathId, fileName, drive):
return fileList[0] return fileList[0]
def getFolderId(path, drive=None): def getFolderId(path, drive):
drive = getDrive(drive) # drive = getDrive(drive)
currentFolderId = getEbooksFolderId(drive) currentFolderId = getEbooksFolderId(drive)
sqlCheckPath = path if path[-1] == '/' else path + '/' sqlCheckPath = path if path[-1] == '/' else path + '/'
storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first() storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first()
@ -249,7 +248,7 @@ def getFileFromEbooksFolder(path, fileName):
return None return None
def copyDriveFileRemote(drive, origin_file_id, copy_title): '''def copyDriveFileRemote(drive, origin_file_id, copy_title):
drive = getDrive(drive) drive = getDrive(drive)
copied_file = {'title': copy_title} copied_file = {'title': copy_title}
try: try:
@ -258,7 +257,7 @@ def copyDriveFileRemote(drive, origin_file_id, copy_title):
return drive.CreateFile({'id': file_data['id']}) return drive.CreateFile({'id': file_data['id']})
except errors.HttpError as error: except errors.HttpError as error:
print ('An error occurred: %s' % error) print ('An error occurred: %s' % error)
return None return None'''
# Download metadata.db from gdrive # Download metadata.db from gdrive
@ -347,7 +346,6 @@ def uploadFileToEbooksFolder(destFile, f):
def watchChange(drive, channel_id, channel_type, channel_address, def watchChange(drive, channel_id, channel_type, channel_address,
channel_token=None, expiration=None): channel_token=None, expiration=None):
# drive = getDrive(drive)
# Watch for all changes to a user's Drive. # Watch for all changes to a user's Drive.
# Args: # Args:
# service: Drive API service instance. # service: Drive API service instance.
@ -390,8 +388,6 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address,
Raises: Raises:
apiclient.errors.HttpError: if http request to create channel fails. apiclient.errors.HttpError: if http request to create channel fails.
""" """
# drive = getDrive(drive)
body = { body = {
'id': channel_id, 'id': channel_id,
'type': channel_type, 'type': channel_type,
@ -413,8 +409,6 @@ def stopChannel(drive, channel_id, resource_id):
Raises: Raises:
apiclient.errors.HttpError: if http request to create channel fails. apiclient.errors.HttpError: if http request to create channel fails.
""" """
# drive = getDrive(drive)
# service=drive.auth.service
body = { body = {
'id': channel_id, 'id': channel_id,
'resourceId': resource_id 'resourceId': resource_id
@ -423,7 +417,6 @@ def stopChannel(drive, channel_id, resource_id):
def getChangeById (drive, change_id): def getChangeById (drive, change_id):
# drive = getDrive(drive)
# Print a single Change resource information. # Print a single Change resource information.
# #
# Args: # Args:
@ -454,11 +447,13 @@ def updateDatabaseOnEdit(ID,newPath):
storedPathName.path = newPath storedPathName.path = newPath
session.commit() session.commit()
# Deletes the hashes in database of deleted book # Deletes the hashes in database of deleted book
def deleteDatabaseEntry(ID): def deleteDatabaseEntry(ID):
session.query(GdriveId).filter(GdriveId.gdrive_id == ID).delete() session.query(GdriveId).filter(GdriveId.gdrive_id == ID).delete()
session.commit() session.commit()
# Gets cover file from gdrive # Gets cover file from gdrive
def get_cover_via_gdrive(cover_path): def get_cover_via_gdrive(cover_path):
df = getFileFromEbooksFolder(cover_path, 'cover.jpg') df = getFileFromEbooksFolder(cover_path, 'cover.jpg')

39
cps/reverseproxy.py Normal file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class ReverseProxied(object):
"""Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is
different than what is used locally.
Code courtesy of: http://flask.pocoo.org/snippets/35/
In nginx:
location /myprefix {
proxy_pass http://127.0.0.1:8083;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Script-Name /myprefix;
}
"""
def __init__(self, application):
self.app = application
def __call__(self, environ, start_response):
script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
if script_name:
environ['SCRIPT_NAME'] = script_name
path_info = environ.get('PATH_INFO', '')
if path_info and path_info.startswith(script_name):
environ['PATH_INFO'] = path_info[len(script_name):]
scheme = environ.get('HTTP_X_SCHEME', '')
if scheme:
environ['wsgi.url_scheme'] = scheme
servr = environ.get('HTTP_X_FORWARDED_SERVER', '')
if servr:
environ['HTTP_HOST'] = servr
return self.app(environ, start_response)

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from socket import error as SocketError from socket import error as SocketError
import sys import sys
import os import os
import signal import signal
import web
try: try:
from gevent.pywsgi import WSGIServer from gevent.pywsgi import WSGIServer
@ -19,8 +19,6 @@ except ImportError:
from tornado import version as tornadoVersion from tornado import version as tornadoVersion
gevent_present = False gevent_present = False
import web
class server: class server:
@ -29,7 +27,7 @@ class server:
def __init__(self): def __init__(self):
signal.signal(signal.SIGINT, self.killServer) signal.signal(signal.SIGINT, self.killServer)
signal.signal(signal.SIGTERM, self.killServer) signal.signal(signal.SIGTERM, self.killServer)
def start_gevent(self): def start_gevent(self):
try: try:
@ -68,7 +66,8 @@ class server:
ssl_options=ssl) ssl_options=ssl)
http_server.listen(web.ub.config.config_port) http_server.listen(web.ub.config.config_port)
self.wsgiserver=IOLoop.instance() self.wsgiserver=IOLoop.instance()
self.wsgiserver.start() # wait for stop signal self.wsgiserver.start()
# wait for stop signal
self.wsgiserver.close(True) self.wsgiserver.close(True)
if self.restart == True: if self.restart == True:

View File

@ -1173,7 +1173,7 @@ msgstr "Pfad zu Konvertertool"
#: cps/templates/config_edit.html:199 #: cps/templates/config_edit.html:199
msgid "Location of Unrar binary" msgid "Location of Unrar binary"
msgstr "Ofad zum UnRar Programm" msgstr "Pfad zum UnRar Programm"
#: cps/templates/config_edit.html:215 cps/templates/layout.html:82 #: cps/templates/config_edit.html:215 cps/templates/layout.html:82
#: cps/templates/login.html:4 #: cps/templates/login.html:4

View File

@ -1,5 +1,61 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import mimetypes
import logging
from logging.handlers import RotatingFileHandler
from flask import (Flask, render_template, request, Response, redirect,
url_for, send_from_directory, make_response, g, flash,
abort, Markup)
from flask import __version__ as flaskVersion
from werkzeug import __version__ as werkzeugVersion
from jinja2 import __version__ as jinja2Version
import cache_buster
import ub
from ub import config
import helper
import os
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import false
from sqlalchemy.exc import IntegrityError
from sqlalchemy import __version__ as sqlalchemyVersion
from math import ceil
from flask_login import (LoginManager, login_user, logout_user,
login_required, current_user)
from flask_principal import Principal
from flask_principal import __version__ as flask_principalVersion
from flask_babel import Babel
from flask_babel import gettext as _
import requests
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.datastructures import Headers
from babel import Locale as LC
from babel import negotiate_locale
from babel import __version__ as babelVersion
from babel.dates import format_date, format_datetime
from babel.core import UnknownLocaleError
from functools import wraps
import base64
from sqlalchemy.sql import *
import json
import datetime
from iso639 import languages as isoLanguages
from iso639 import __version__ as iso639Version
from pytz import __version__ as pytzVersion
from uuid import uuid4
import os.path
import sys
import re
import db
from shutil import move, copyfile
import gdriveutils
import converter
import tempfile
from redirect import redirect_back
import time
import server
import copy
from reverseproxy import ReverseProxied
try: try:
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
except ImportError: except ImportError:
@ -33,59 +89,6 @@ try:
except ImportError: except ImportError:
sort=sorted # Just use regular sort then sort=sorted # Just use regular sort then
# may cause issues with badly named pages in cbz/cbr files # may cause issues with badly named pages in cbz/cbr files
import mimetypes
import logging
from logging.handlers import RotatingFileHandler
from flask import (Flask, render_template, request, Response, redirect,
url_for, send_from_directory, make_response, g, flash,
abort, Markup)
from flask import __version__ as flaskVersion
import cache_buster
import ub
from ub import config
import helper
import os
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import false
from sqlalchemy.exc import IntegrityError
from sqlalchemy import __version__ as sqlalchemyVersion
from math import ceil
from flask_login import (LoginManager, login_user, logout_user,
login_required, current_user)
from flask_principal import Principal
from flask_principal import __version__ as flask_principalVersion
from flask_babel import Babel
from flask_babel import gettext as _
import requests
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.datastructures import Headers
from babel import Locale as LC
from babel import negotiate_locale
from babel import __version__ as babelVersion
from babel.dates import format_date, format_datetime
from babel.core import UnknownLocaleError
from functools import wraps
import base64
from sqlalchemy.sql import *
import json
import datetime
from iso639 import languages as isoLanguages
from iso639 import __version__ as iso639Version
from uuid import uuid4
import os.path
import sys
import re
import db
from shutil import move, copyfile
import gdriveutils
import converter
import tempfile
import hashlib
from redirect import redirect_back
import time
import server
import copy
try: try:
import cPickle import cPickle
except ImportError: except ImportError:
@ -102,12 +105,10 @@ try:
except ImportError: except ImportError:
from flask_login.__about__ import __version__ as flask_loginVersion from flask_login.__about__ import __version__ as flask_loginVersion
current_milli_time = lambda: int(round(time.time() * 1000))
# Global variables # Global variables
current_milli_time = lambda: int(round(time.time() * 1000))
gdrive_watch_callback_token = 'target=calibreweb-watch_files' gdrive_watch_callback_token = 'target=calibreweb-watch_files'
EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx',
'fb2', 'html', 'rtf', 'odt'} 'fb2', 'html', 'rtf', 'odt'}
EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'html', 'rtf', 'odt'} EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'html', 'rtf', 'odt'}
@ -115,15 +116,7 @@ EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit'
# EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] + (['rar','cbr'] if rar_support else [])) # EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] + (['rar','cbr'] if rar_support else []))
def md5(fname): '''class ReverseProxied(object):
hash_md5 = hashlib.md5()
with open(fname, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
class ReverseProxied(object):
"""Wrap the application in this middleware and configure the """Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is this to a URL other than / and to an HTTP scheme that is
@ -158,7 +151,7 @@ class ReverseProxied(object):
servr = environ.get('HTTP_X_FORWARDED_SERVER', '') servr = environ.get('HTTP_X_FORWARDED_SERVER', '')
if servr: if servr:
environ['HTTP_HOST'] = servr environ['HTTP_HOST'] = servr
return self.app(environ, start_response) return self.app(environ, start_response)'''
# Main code # Main code
@ -1674,15 +1667,19 @@ def stats():
categorys = db.session.query(db.Tags).count() categorys = db.session.query(db.Tags).count()
series = db.session.query(db.Series).count() series = db.session.query(db.Series).count()
versions = uploader.book_formats.get_versions() versions = uploader.book_formats.get_versions()
versions['Babel'] = 'v'+babelVersion versions['Babel'] = 'v' + babelVersion
versions['Sqlalchemy'] = 'v'+sqlalchemyVersion versions['Sqlalchemy'] = 'v' + sqlalchemyVersion
versions['Flask'] = 'v'+flaskVersion versions['Werkzeug'] = 'v' + werkzeugVersion
versions['Flask Login'] = 'v'+flask_loginVersion versions['Jinja2'] = 'v' + jinja2Version
versions['Flask Principal'] = 'v'+flask_principalVersion versions['Flask'] = 'v' + flaskVersion
versions['Iso 639'] = 'v'+iso639Version versions['Flask Login'] = 'v' + flask_loginVersion
versions['Requests'] = 'v'+requests.__version__ versions['Flask Principal'] = 'v' + flask_principalVersion
versions['pySqlite'] = 'v'+db.engine.dialect.dbapi.version versions['Iso 639'] = 'v' + iso639Version
versions['Sqlite'] = 'v'+db.engine.dialect.dbapi.sqlite_version versions['pytz'] = 'v' + pytzVersion
versions['Requests'] = 'v' + requests.__version__
versions['pySqlite'] = 'v' + db.engine.dialect.dbapi.version
versions['Sqlite'] = 'v' + db.engine.dialect.dbapi.sqlite_version
versions.update(converter.versioncheck()) versions.update(converter.versioncheck())
versions.update(server.Server.getNameVersion()) versions.update(server.Server.getNameVersion())
versions['Python'] = sys.version versions['Python'] = sys.version
@ -3367,24 +3364,19 @@ def reset_password(user_id):
return redirect(url_for('admin')) return redirect(url_for('admin'))
@app.route("/admin/book/<int:book_id>", methods=['GET', 'POST']) def render_edit_book(book_id):
@login_required_if_no_ano
@edit_required
def edit_book(book_id):
# create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort) db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
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()
book = db.session.query(db.Books)\ book = db.session.query(db.Books)\
.filter(db.Books.id == book_id).filter(common_filters()).first() .filter(db.Books.id == book_id).filter(common_filters()).first()
author_names = []
# Book not found
if not book: if not book:
flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error") flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error")
return redirect(url_for("index")) return redirect(url_for("index"))
for indx in range(0, len(book.languages)): for indx in range(0, len(book.languages)):
book.languages[indx].language_name = language_table[get_locale()][book.languages[indx].lang_code] book.languages[indx].language_name = language_table[get_locale()][book.languages[indx].lang_code]
author_names = []
for authr in book.authors: for authr in book.authors:
author_names.append(authr.name.replace('|', ',')) author_names.append(authr.name.replace('|', ','))
@ -3403,14 +3395,92 @@ def edit_book(book_id):
except Exception: except Exception:
app.logger.warning(file.format.lower() + ' already removed from list.') app.logger.warning(file.format.lower() + ' already removed from list.')
app.logger.debug('Allowed conversion formats: '+ ', '.join(allowed_conversion_formats)) return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
title=_(u"edit metadata"), page="editbook",
conversion_formats=allowed_conversion_formats,
source_formats=valid_source_formats)
# Show form
if request.method != 'POST': def edit_cc_data(book_id, book, to_save):
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc, cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
title=_(u"edit metadata"), page="editbook", for c in cc:
conversion_formats=allowed_conversion_formats, cc_string = "custom_column_" + str(c.id)
source_formats=valid_source_formats) if not c.is_multiple:
if len(getattr(book, cc_string)) > 0:
cc_db_value = getattr(book, cc_string)[0].value
else:
cc_db_value = None
if to_save[cc_string].strip():
if c.datatype == 'bool':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
else:
to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
elif c.datatype == 'int':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
else:
if c.datatype == 'rating':
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2))
if to_save[cc_string].strip() != cc_db_value:
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
cc_class = db.cc_classes[c.id]
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# if no cc val is found add it
if new_cc is None:
new_cc = cc_class(value=to_save[cc_string].strip())
db.session.add(new_cc)
db.session.flush()
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# add cc value to book
getattr(book, cc_string).append(new_cc)
else:
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
else:
input_tags = to_save[cc_string].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags))
modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session,
'custom')
return cc
def upload_single_file(request, book, book_id):
# Check and handle Uploaded file # Check and handle Uploaded file
if 'btn-upload-format' in request.files: if 'btn-upload-format' in request.files:
requested_file = request.files['btn-upload-format'] requested_file = request.files['btn-upload-format']
@ -3457,9 +3527,10 @@ def edit_book(book_id):
# Queue uploader info # Queue uploader info
uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title) uploadText=_(u"File format %(ext)s added to %(book)s", ext=file_ext.upper(), book=book.title)
helper.global_WorkerThread.add_upload(current_user.nickname, helper.global_WorkerThread.add_upload(current_user.nickname,
"<a href=\"" + url_for('show_book', book_id=book.id) + "\">" + uploadText + "</a>") "<a href=\"" + url_for('show_book', book_id=book.id) + "\">" + uploadText + "</a>")
def upload_cover(request, book):
if 'btn-upload-cover' in request.files: if 'btn-upload-cover' in request.files:
requested_file = request.files['btn-upload-cover'] requested_file = request.files['btn-upload-cover']
# check for empty request # check for empty request
@ -3485,15 +3556,37 @@ def edit_book(book_id):
except IOError: except IOError:
flash(_(u"Cover-file is not a valid image file" % saved_filename), category="error") flash(_(u"Cover-file is not a valid image file" % saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id)) return redirect(url_for('show_book', book_id=book.id))
to_save = request.form.to_dict()
@app.route("/admin/book/<int:book_id>", methods=['GET', 'POST'])
@login_required_if_no_ano
@edit_required
def edit_book(book_id):
# Show form
if request.method != 'POST':
return render_edit_book(book_id)
# create the function for sorting...
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
book = db.session.query(db.Books)\
.filter(db.Books.id == book_id).filter(common_filters()).first()
# Book not found
if not book:
flash(_(u"Error opening eBook. File does not exist or file is not accessible"), category="error")
return redirect(url_for("index"))
upload_single_file(request, book, book_id)
upload_cover(request, book)
try: try:
to_save = request.form.to_dict()
# Update book # Update book
edited_books_id = set() edited_books_id = None
#handle book title #handle book title
if book.title != to_save["book_title"]: if book.title != to_save["book_title"]:
if to_save["book_title"] == '':
to_save["book_title"] = _(u'unknown')
book.title = to_save["book_title"] book.title = to_save["book_title"]
edited_books_id.add(book.id) edited_books_id = book.id
# handle author(s) # handle author(s)
input_authors = to_save["author_name"].split('&') input_authors = to_save["author_name"].split('&')
@ -3508,18 +3601,17 @@ def edit_book(book_id):
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if book.authors: if book.authors:
if author0_before_edit != book.authors[0].name: if author0_before_edit != book.authors[0].name:
edited_books_id.add(book.id) edited_books_id = book.id
book.author_sort = helper.get_sorted_author(input_authors[0]) book.author_sort = helper.get_sorted_author(input_authors[0])
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
error = False error = False
for b in edited_books_id: if edited_books_id:
error = helper.update_dir_stucture(b, config.config_calibre_dir) error = helper.update_dir_stucture(edited_books_id, config.config_calibre_dir)
if error: # stop on error if error: # stop on error
flash(error, category="error") flash(error, category="error")
break
if not error: if not error:
if to_save["cover_url"]: if to_save["cover_url"]:
@ -3562,7 +3654,6 @@ def edit_book(book_id):
# handle book languages # handle book languages
input_languages = to_save["languages"].split(',') input_languages = to_save["languages"].split(',')
# input_languages = list(map(lambda it: it.strip().lower(), input_languages))
input_languages = [x.strip().lower() for x in input_languages if x != ''] input_languages = [x.strip().lower() for x in input_languages if x != '']
input_l = [] input_l = []
invers_lang_table = [x.lower() for x in language_table[get_locale()].values()] invers_lang_table = [x.lower() for x in language_table[get_locale()].values()]
@ -3575,6 +3666,7 @@ def edit_book(book_id):
flash(_(u"%(langname)s is not a valid language", langname=lang), category="error") flash(_(u"%(langname)s is not a valid language", langname=lang), category="error")
modify_database_object(input_l, book.languages, db.Languages, db.session, 'languages') modify_database_object(input_l, book.languages, db.Languages, db.session, 'languages')
# handle book ratings
if to_save["rating"].strip(): if to_save["rating"].strip():
old_rating = False old_rating = False
if len(book.ratings) > 0: if len(book.ratings) > 0:
@ -3593,104 +3685,20 @@ def edit_book(book_id):
if len(book.ratings) > 0: if len(book.ratings) > 0:
book.ratings.remove(book.ratings[0]) book.ratings.remove(book.ratings[0])
for c in cc: # handle cc data
cc_string = "custom_column_" + str(c.id) edit_cc_data(book_id, book, to_save)
if not c.is_multiple:
if len(getattr(book, cc_string)) > 0:
cc_db_value = getattr(book, cc_string)[0].value
else:
cc_db_value = None
if to_save[cc_string].strip():
if c.datatype == 'bool':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
else:
to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
elif c.datatype == 'int':
if to_save[cc_string] == 'None':
to_save[cc_string] = None
if to_save[cc_string] != cc_db_value:
if cc_db_value is not None:
if to_save[cc_string] is not None:
setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string])
else:
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
db.session.delete(del_cc)
else:
cc_class = db.cc_classes[c.id]
new_cc = cc_class(value=to_save[cc_string], book=book_id)
db.session.add(new_cc)
else:
if c.datatype == 'rating':
to_save[cc_string] = str(int(float(to_save[cc_string]) * 2))
if to_save[cc_string].strip() != cc_db_value:
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
cc_class = db.cc_classes[c.id]
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# if no cc val is found add it
if new_cc is None:
new_cc = cc_class(value=to_save[cc_string].strip())
db.session.add(new_cc)
db.session.flush()
new_cc = db.session.query(cc_class).filter(
cc_class.value == to_save[cc_string].strip()).first()
# add cc value to book
getattr(book, cc_string).append(new_cc)
else:
if cc_db_value is not None:
# remove old cc_val
del_cc = getattr(book, cc_string)[0]
getattr(book, cc_string).remove(del_cc)
if len(del_cc.books) == 0:
db.session.delete(del_cc)
else:
input_tags = to_save[cc_string].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags))
modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session,
'custom')
db.session.commit() db.session.commit()
if config.config_use_google_drive: if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal() gdriveutils.updateGdriveCalibreFromLocal()
if "detail_view" in to_save: if "detail_view" in to_save:
return redirect(url_for('show_book', book_id=book.id)) return redirect(url_for('show_book', book_id=book.id))
else: else:
for indx in range(0, len(book.languages)): return render_edit_book(book_id)
try:
book.languages[indx].language_name = LC.parse(book.languages[indx].lang_code).get_language_name(
get_locale())
except UnknownLocaleError:
book.languages[indx].language_name = _(
isoLanguages.get(part3=book.languages[indx].lang_code).name)
author_names = []
for authr in book.authors:
author_names.append(authr.name)
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
title=_(u"edit metadata"), page="editbook")
else: else:
db.session.rollback() db.session.rollback()
flash(error, category="error") flash(error, category="error")
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc, return render_edit_book(book_id)
title=_(u"edit metadata"), page="editbook")
except Exception as e: except Exception as e:
app.logger.exception(e) app.logger.exception(e)
db.session.rollback() db.session.rollback()