This commit is contained in:
bodybybuddha 2018-09-30 10:58:38 -04:00
commit 93a9f65198
7 changed files with 253 additions and 210 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:
@ -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,60 @@
#!/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
from reverseproxy import ReverseProxied
try: try:
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
except ImportError: except ImportError:
@ -33,58 +88,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
try: try:
import cPickle import cPickle
except ImportError: except ImportError:
@ -101,12 +104,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'}
@ -114,15 +115,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
@ -157,7 +150,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
@ -1666,15 +1659,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
@ -3359,24 +3356,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('|', ','))
@ -3395,196 +3387,14 @@ 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))
# Show form
if request.method != 'POST':
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc, return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
title=_(u"edit metadata"), page="editbook", title=_(u"edit metadata"), page="editbook",
conversion_formats=allowed_conversion_formats, conversion_formats=allowed_conversion_formats,
source_formats=valid_source_formats) source_formats=valid_source_formats)
# Check and handle Uploaded file
if 'btn-upload-format' in request.files:
requested_file = request.files['btn-upload-format']
# check for empty request
if requested_file.filename != '':
if '.' in requested_file.filename:
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
if file_ext not in EXTENSIONS_UPLOAD:
flash(_("File extension '%(ext)s' is not allowed to be uploaded to this server", ext=file_ext),
category="error")
return redirect(url_for('show_book', book_id=book.id))
else:
flash(_('File to be uploaded must have an extension'), category="error")
return redirect(url_for('show_book', book_id=book.id))
file_name = book.path.rsplit('/', 1)[-1]
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
saved_filename = os.path.join(filepath, file_name + '.' + file_ext)
# 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:
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
return redirect(url_for('show_book', book_id=book.id))
try:
requested_file.save(saved_filename)
except OSError:
flash(_(u"Failed to store file %(file)s.", file=saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
file_size = os.path.getsize(saved_filename)
is_format = db.session.query(db.Data).filter(db.Data.book == book_id).filter(db.Data.format == file_ext.upper()).first()
# Format entry already exists, no need to update the database
if is_format:
app.logger.info('Book format already existing')
else:
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
db.session.add(db_format)
db.session.commit()
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
# Queue uploader info
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,
"<a href=\"" + url_for('show_book', book_id=book.id) + "\">" + uploadText + "</a>")
if 'btn-upload-cover' in request.files:
requested_file = request.files['btn-upload-cover']
# check for empty request
if requested_file.filename != '':
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
saved_filename = os.path.join(filepath, 'cover.' + file_ext)
# 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:
flash(_(u"Failed to create path for cover %(path)s (Permission denied).", cover=filepath), category="error")
return redirect(url_for('show_book', book_id=book.id))
try:
requested_file.save(saved_filename)
# im=Image.open(saved_filename)
book.has_cover = 1
except OSError:
flash(_(u"Failed to store cover-file %(cover)s.", cover=saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
except IOError:
flash(_(u"Cover-file is not a valid image file" % saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
to_save = request.form.to_dict()
try:
# Update book
edited_books_id = set()
#handle book title
if book.title != to_save["book_title"]:
book.title = to_save["book_title"]
edited_books_id.add(book.id)
# handle author(s)
input_authors = to_save["author_name"].split('&')
input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
# we have all author names now
if input_authors == ['']:
input_authors = [_(u'unknown')] # prevent empty Author
if book.authors:
author0_before_edit = book.authors[0].name
else:
author0_before_edit = db.Authors(_(u'unknown'), '', 0)
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if book.authors:
if author0_before_edit != book.authors[0].name:
edited_books_id.add(book.id)
book.author_sort = helper.get_sorted_author(input_authors[0])
if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal()
error = False
for b in edited_books_id:
error = helper.update_dir_stucture(b, config.config_calibre_dir)
if error: # stop on error
flash(error, category="error")
break
if not error:
if to_save["cover_url"]:
if helper.save_cover(to_save["cover_url"], book.path) is True:
book.has_cover = 1
else:
flash(_(u"Cover is not a jpg file, can't save"), category="error")
if book.series_index != to_save["series_index"]:
book.series_index = to_save["series_index"]
# Handle book comments/description
if len(book.comments):
book.comments[0].text = to_save["description"]
else:
book.comments.append(db.Comments(text=to_save["description"], book=book.id))
# Handle book tags
input_tags = to_save["tags"].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags))
modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags')
# Handle book series
input_series = [to_save["series"].strip()]
input_series = [x for x in input_series if x != '']
modify_database_object(input_series, book.series, db.Series, db.session, 'series')
if to_save["pubdate"]:
try:
book.pubdate = datetime.datetime.strptime(to_save["pubdate"], "%Y-%m-%d")
except ValueError:
book.pubdate = db.Books.DEFAULT_PUBDATE
else:
book.pubdate = db.Books.DEFAULT_PUBDATE
'''if len(book.publishers):
if to_save["publisher"] != book.publishers[0].name:
modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series')
else:
modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series')'''
# handle book languages
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_l = []
invers_lang_table = [x.lower() for x in language_table[get_locale()].values()]
for lang in input_languages:
try:
res = list(language_table[get_locale()].keys())[invers_lang_table.index(lang)]
input_l.append(res)
except ValueError:
app.logger.error('%s is not a valid language' % lang)
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')
if to_save["rating"].strip():
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating:
is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
if is_rating:
book.ratings.append(is_rating)
else:
new_rating = db.Ratings(rating=ratingx2)
book.ratings.append(new_rating)
if old_rating:
book.ratings.remove(book.ratings[0])
else:
if len(book.ratings) > 0:
book.ratings.remove(book.ratings[0])
def edit_cc_data(book_id, book, to_save):
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:
@ -3660,29 +3470,227 @@ def edit_book(book_id):
input_tags = list(map(lambda it: it.strip(), input_tags)) 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, modify_database_object(input_tags, getattr(book, cc_string), db.cc_classes[c.id], db.session,
'custom') 'custom')
return cc
def upload_single_file(request, book, book_id):
# Check and handle Uploaded file
if 'btn-upload-format' in request.files:
requested_file = request.files['btn-upload-format']
# check for empty request
if requested_file.filename != '':
if '.' in requested_file.filename:
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
if file_ext not in EXTENSIONS_UPLOAD:
flash(_("File extension '%(ext)s' is not allowed to be uploaded to this server", ext=file_ext),
category="error")
return redirect(url_for('show_book', book_id=book.id))
else:
flash(_('File to be uploaded must have an extension'), category="error")
return redirect(url_for('show_book', book_id=book.id))
file_name = book.path.rsplit('/', 1)[-1]
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
saved_filename = os.path.join(filepath, file_name + '.' + file_ext)
# 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:
flash(_(u"Failed to create path %(path)s (Permission denied).", path=filepath), category="error")
return redirect(url_for('show_book', book_id=book.id))
try:
requested_file.save(saved_filename)
except OSError:
flash(_(u"Failed to store file %(file)s.", file=saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
file_size = os.path.getsize(saved_filename)
is_format = db.session.query(db.Data).filter(db.Data.book == book_id).filter(db.Data.format == file_ext.upper()).first()
# Format entry already exists, no need to update the database
if is_format:
app.logger.info('Book format already existing')
else:
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
db.session.add(db_format)
db.session.commit()
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
# Queue uploader info
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,
"<a href=\"" + url_for('show_book', book_id=book.id) + "\">" + uploadText + "</a>")
def upload_cover(request, book):
if 'btn-upload-cover' in request.files:
requested_file = request.files['btn-upload-cover']
# check for empty request
if requested_file.filename != '':
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
saved_filename = os.path.join(filepath, 'cover.' + file_ext)
# 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:
flash(_(u"Failed to create path for cover %(path)s (Permission denied).", cover=filepath), category="error")
return redirect(url_for('show_book', book_id=book.id))
try:
requested_file.save(saved_filename)
# im=Image.open(saved_filename)
book.has_cover = 1
except OSError:
flash(_(u"Failed to store cover-file %(cover)s.", cover=saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
except IOError:
flash(_(u"Cover-file is not a valid image file" % saved_filename), category="error")
return redirect(url_for('show_book', book_id=book.id))
@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:
to_save = request.form.to_dict()
# Update book
edited_books_id = None
#handle 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"]
edited_books_id = book.id
# handle author(s)
input_authors = to_save["author_name"].split('&')
input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
# we have all author names now
if input_authors == ['']:
input_authors = [_(u'unknown')] # prevent empty Author
if book.authors:
author0_before_edit = book.authors[0].name
else:
author0_before_edit = db.Authors(_(u'unknown'), '', 0)
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if book.authors:
if author0_before_edit != book.authors[0].name:
edited_books_id = book.id
book.author_sort = helper.get_sorted_author(input_authors[0])
if config.config_use_google_drive:
gdriveutils.updateGdriveCalibreFromLocal()
error = False
if edited_books_id:
error = helper.update_dir_stucture(edited_books_id, config.config_calibre_dir)
if error: # stop on error
flash(error, category="error")
if not error:
if to_save["cover_url"]:
if helper.save_cover(to_save["cover_url"], book.path) is True:
book.has_cover = 1
else:
flash(_(u"Cover is not a jpg file, can't save"), category="error")
if book.series_index != to_save["series_index"]:
book.series_index = to_save["series_index"]
# Handle book comments/description
if len(book.comments):
book.comments[0].text = to_save["description"]
else:
book.comments.append(db.Comments(text=to_save["description"], book=book.id))
# Handle book tags
input_tags = to_save["tags"].split(',')
input_tags = list(map(lambda it: it.strip(), input_tags))
modify_database_object(input_tags, book.tags, db.Tags, db.session, 'tags')
# Handle book series
input_series = [to_save["series"].strip()]
input_series = [x for x in input_series if x != '']
modify_database_object(input_series, book.series, db.Series, db.session, 'series')
if to_save["pubdate"]:
try:
book.pubdate = datetime.datetime.strptime(to_save["pubdate"], "%Y-%m-%d")
except ValueError:
book.pubdate = db.Books.DEFAULT_PUBDATE
else:
book.pubdate = db.Books.DEFAULT_PUBDATE
'''if len(book.publishers):
if to_save["publisher"] != book.publishers[0].name:
modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series')
else:
modify_database_object(to_save["publisher"], book.publishers, db.Publishers, db.session, 'series')'''
# handle book languages
input_languages = to_save["languages"].split(',')
input_languages = [x.strip().lower() for x in input_languages if x != '']
input_l = []
invers_lang_table = [x.lower() for x in language_table[get_locale()].values()]
for lang in input_languages:
try:
res = list(language_table[get_locale()].keys())[invers_lang_table.index(lang)]
input_l.append(res)
except ValueError:
app.logger.error('%s is not a valid language' % lang)
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')
# handle book ratings
if to_save["rating"].strip():
old_rating = False
if len(book.ratings) > 0:
old_rating = book.ratings[0].rating
ratingx2 = int(float(to_save["rating"]) * 2)
if ratingx2 != old_rating:
is_rating = db.session.query(db.Ratings).filter(db.Ratings.rating == ratingx2).first()
if is_rating:
book.ratings.append(is_rating)
else:
new_rating = db.Ratings(rating=ratingx2)
book.ratings.append(new_rating)
if old_rating:
book.ratings.remove(book.ratings[0])
else:
if len(book.ratings) > 0:
book.ratings.remove(book.ratings[0])
# handle cc data
edit_cc_data(book_id, book, to_save)
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()