mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-28 12:30:00 +00:00
Merged master into 617 - fixed import copy conflict
This commit is contained in:
commit
1b363ac720
@ -123,9 +123,11 @@ def pdf_preview(tmp_file_path, tmp_dir):
|
||||
|
||||
def get_versions():
|
||||
if not use_generic_pdf_cover:
|
||||
IVersion=ImageVersion.MAGICK_VERSION
|
||||
IVersion = ImageVersion.MAGICK_VERSION
|
||||
WVersion = ImageVersion.VERSION
|
||||
else:
|
||||
IVersion = _(u'not installed')
|
||||
WVersion = _(u'not installed')
|
||||
if use_pdf_meta:
|
||||
PVersion='v'+PyPdfVersion
|
||||
else:
|
||||
@ -134,4 +136,4 @@ def get_versions():
|
||||
XVersion = 'v'+'.'.join(map(str, lxmlversion))
|
||||
else:
|
||||
XVersion = _(u'not installed')
|
||||
return {'Image Magick': IVersion, 'PyPdf': PVersion, 'lxml':XVersion}
|
||||
return {'Image Magick': IVersion, 'PyPdf': PVersion, 'lxml':XVersion, 'Wand Version': WVersion}
|
||||
|
@ -45,5 +45,5 @@ def versioncheck():
|
||||
elif ub.config.config_ebookconverter == 2:
|
||||
return versionCalibre()
|
||||
else:
|
||||
return {'ebook_converter':''}
|
||||
return {'ebook_converter':_(u'not configured')}
|
||||
|
||||
|
@ -149,19 +149,19 @@ def getDrive(drive=None, gauth=None):
|
||||
drive.auth.Refresh()
|
||||
return drive
|
||||
|
||||
def listRootFolders(drive=None):
|
||||
drive = getDrive(drive)
|
||||
def listRootFolders():
|
||||
drive = getDrive(Gdrive.Instance().drive)
|
||||
folder = "'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false"
|
||||
fileList = drive.ListFile({'q': folder}).GetList()
|
||||
return fileList
|
||||
|
||||
|
||||
def getEbooksFolder(drive=None):
|
||||
def getEbooksFolder(drive):
|
||||
return getFolderInFolder('root',config.config_google_drive_folder,drive)
|
||||
|
||||
|
||||
def getFolderInFolder(parentId, folderName,drive=None):
|
||||
drive = getDrive(drive)
|
||||
def getFolderInFolder(parentId, folderName, drive):
|
||||
# drive = getDrive(drive)
|
||||
query=""
|
||||
if folderName:
|
||||
query = "title = '%s' and " % folderName.replace("'", "\\'")
|
||||
@ -190,7 +190,6 @@ def getEbooksFolderId(drive=None):
|
||||
|
||||
|
||||
def getFile(pathId, fileName, drive):
|
||||
# drive = getDrive(Gdrive.Instance().drive)
|
||||
metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'"))
|
||||
|
||||
fileList = drive.ListFile({'q': metaDataFile}).GetList()
|
||||
@ -200,8 +199,8 @@ def getFile(pathId, fileName, drive):
|
||||
return fileList[0]
|
||||
|
||||
|
||||
def getFolderId(path, drive=None):
|
||||
drive = getDrive(drive)
|
||||
def getFolderId(path, drive):
|
||||
# drive = getDrive(drive)
|
||||
currentFolderId = getEbooksFolderId(drive)
|
||||
sqlCheckPath = path if path[-1] == '/' else path + '/'
|
||||
storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first()
|
||||
@ -249,7 +248,7 @@ def getFileFromEbooksFolder(path, fileName):
|
||||
return None
|
||||
|
||||
|
||||
def copyDriveFileRemote(drive, origin_file_id, copy_title):
|
||||
'''def copyDriveFileRemote(drive, origin_file_id, copy_title):
|
||||
drive = getDrive(drive)
|
||||
copied_file = {'title': copy_title}
|
||||
try:
|
||||
@ -258,7 +257,7 @@ def copyDriveFileRemote(drive, origin_file_id, copy_title):
|
||||
return drive.CreateFile({'id': file_data['id']})
|
||||
except errors.HttpError as error:
|
||||
print ('An error occurred: %s' % error)
|
||||
return None
|
||||
return None'''
|
||||
|
||||
|
||||
# Download metadata.db from gdrive
|
||||
@ -347,7 +346,6 @@ def uploadFileToEbooksFolder(destFile, f):
|
||||
|
||||
def watchChange(drive, channel_id, channel_type, channel_address,
|
||||
channel_token=None, expiration=None):
|
||||
# drive = getDrive(drive)
|
||||
# Watch for all changes to a user's Drive.
|
||||
# Args:
|
||||
# service: Drive API service instance.
|
||||
@ -390,8 +388,6 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address,
|
||||
Raises:
|
||||
apiclient.errors.HttpError: if http request to create channel fails.
|
||||
"""
|
||||
# drive = getDrive(drive)
|
||||
|
||||
body = {
|
||||
'id': channel_id,
|
||||
'type': channel_type,
|
||||
@ -413,8 +409,6 @@ def stopChannel(drive, channel_id, resource_id):
|
||||
Raises:
|
||||
apiclient.errors.HttpError: if http request to create channel fails.
|
||||
"""
|
||||
# drive = getDrive(drive)
|
||||
# service=drive.auth.service
|
||||
body = {
|
||||
'id': channel_id,
|
||||
'resourceId': resource_id
|
||||
@ -423,7 +417,6 @@ def stopChannel(drive, channel_id, resource_id):
|
||||
|
||||
|
||||
def getChangeById (drive, change_id):
|
||||
# drive = getDrive(drive)
|
||||
# Print a single Change resource information.
|
||||
#
|
||||
# Args:
|
||||
@ -454,11 +447,13 @@ def updateDatabaseOnEdit(ID,newPath):
|
||||
storedPathName.path = newPath
|
||||
session.commit()
|
||||
|
||||
|
||||
# Deletes the hashes in database of deleted book
|
||||
def deleteDatabaseEntry(ID):
|
||||
session.query(GdriveId).filter(GdriveId.gdrive_id == ID).delete()
|
||||
session.commit()
|
||||
|
||||
|
||||
# Gets cover file from gdrive
|
||||
def get_cover_via_gdrive(cover_path):
|
||||
df = getFileFromEbooksFolder(cover_path, 'cover.jpg')
|
||||
|
39
cps/reverseproxy.py
Normal file
39
cps/reverseproxy.py
Normal 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)
|
@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from socket import error as SocketError
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
import web
|
||||
|
||||
try:
|
||||
from gevent.pywsgi import WSGIServer
|
||||
@ -19,8 +19,6 @@ except ImportError:
|
||||
from tornado import version as tornadoVersion
|
||||
gevent_present = False
|
||||
|
||||
import web
|
||||
|
||||
|
||||
class server:
|
||||
|
||||
@ -68,7 +66,8 @@ class server:
|
||||
ssl_options=ssl)
|
||||
http_server.listen(web.ub.config.config_port)
|
||||
self.wsgiserver=IOLoop.instance()
|
||||
self.wsgiserver.start() # wait for stop signal
|
||||
self.wsgiserver.start()
|
||||
# wait for stop signal
|
||||
self.wsgiserver.close(True)
|
||||
|
||||
if self.restart == True:
|
||||
|
@ -1173,7 +1173,7 @@ msgstr "Pfad zu Konvertertool"
|
||||
|
||||
#: cps/templates/config_edit.html:199
|
||||
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/login.html:4
|
||||
|
568
cps/web.py
568
cps/web.py
@ -1,5 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- 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:
|
||||
from googleapiclient.errors import HttpError
|
||||
except ImportError:
|
||||
@ -33,59 +89,6 @@ try:
|
||||
except ImportError:
|
||||
sort=sorted # Just use regular sort then
|
||||
# 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:
|
||||
import cPickle
|
||||
except ImportError:
|
||||
@ -102,12 +105,10 @@ try:
|
||||
except ImportError:
|
||||
from flask_login.__about__ import __version__ as flask_loginVersion
|
||||
|
||||
current_milli_time = lambda: int(round(time.time() * 1000))
|
||||
|
||||
|
||||
# Global variables
|
||||
current_milli_time = lambda: int(round(time.time() * 1000))
|
||||
gdrive_watch_callback_token = 'target=calibreweb-watch_files'
|
||||
|
||||
EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx',
|
||||
'fb2', '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 []))
|
||||
|
||||
|
||||
def md5(fname):
|
||||
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):
|
||||
'''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
|
||||
@ -158,7 +151,7 @@ class ReverseProxied(object):
|
||||
servr = environ.get('HTTP_X_FORWARDED_SERVER', '')
|
||||
if servr:
|
||||
environ['HTTP_HOST'] = servr
|
||||
return self.app(environ, start_response)
|
||||
return self.app(environ, start_response)'''
|
||||
|
||||
|
||||
# Main code
|
||||
@ -1674,15 +1667,19 @@ def stats():
|
||||
categorys = db.session.query(db.Tags).count()
|
||||
series = db.session.query(db.Series).count()
|
||||
versions = uploader.book_formats.get_versions()
|
||||
versions['Babel'] = 'v'+babelVersion
|
||||
versions['Sqlalchemy'] = 'v'+sqlalchemyVersion
|
||||
versions['Flask'] = 'v'+flaskVersion
|
||||
versions['Flask Login'] = 'v'+flask_loginVersion
|
||||
versions['Flask Principal'] = 'v'+flask_principalVersion
|
||||
versions['Iso 639'] = 'v'+iso639Version
|
||||
versions['Requests'] = 'v'+requests.__version__
|
||||
versions['pySqlite'] = 'v'+db.engine.dialect.dbapi.version
|
||||
versions['Sqlite'] = 'v'+db.engine.dialect.dbapi.sqlite_version
|
||||
versions['Babel'] = 'v' + babelVersion
|
||||
versions['Sqlalchemy'] = 'v' + sqlalchemyVersion
|
||||
versions['Werkzeug'] = 'v' + werkzeugVersion
|
||||
versions['Jinja2'] = 'v' + jinja2Version
|
||||
versions['Flask'] = 'v' + flaskVersion
|
||||
versions['Flask Login'] = 'v' + flask_loginVersion
|
||||
versions['Flask Principal'] = 'v' + flask_principalVersion
|
||||
versions['Iso 639'] = 'v' + iso639Version
|
||||
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(server.Server.getNameVersion())
|
||||
versions['Python'] = sys.version
|
||||
@ -3367,24 +3364,19 @@ def reset_password(user_id):
|
||||
return redirect(url_for('admin'))
|
||||
|
||||
|
||||
@app.route("/admin/book/<int:book_id>", methods=['GET', 'POST'])
|
||||
@login_required_if_no_ano
|
||||
@edit_required
|
||||
def edit_book(book_id):
|
||||
# create the function for sorting...
|
||||
def render_edit_book(book_id):
|
||||
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()
|
||||
book = db.session.query(db.Books)\
|
||||
.filter(db.Books.id == book_id).filter(common_filters()).first()
|
||||
author_names = []
|
||||
|
||||
# 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"))
|
||||
|
||||
for indx in range(0, len(book.languages)):
|
||||
book.languages[indx].language_name = language_table[get_locale()][book.languages[indx].lang_code]
|
||||
author_names = []
|
||||
for authr in book.authors:
|
||||
author_names.append(authr.name.replace('|', ','))
|
||||
|
||||
@ -3403,196 +3395,14 @@ def edit_book(book_id):
|
||||
except Exception:
|
||||
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,
|
||||
title=_(u"edit metadata"), page="editbook",
|
||||
conversion_formats=allowed_conversion_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:
|
||||
cc_string = "custom_column_" + str(c.id)
|
||||
if not c.is_multiple:
|
||||
@ -3668,29 +3478,227 @@ def edit_book(book_id):
|
||||
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
|
||||
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()
|
||||
if config.config_use_google_drive:
|
||||
gdriveutils.updateGdriveCalibreFromLocal()
|
||||
if "detail_view" in to_save:
|
||||
return redirect(url_for('show_book', book_id=book.id))
|
||||
else:
|
||||
for indx in range(0, len(book.languages)):
|
||||
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")
|
||||
return render_edit_book(book_id)
|
||||
else:
|
||||
db.session.rollback()
|
||||
flash(error, category="error")
|
||||
return render_title_template('book_edit.html', book=book, authors=author_names, cc=cc,
|
||||
title=_(u"edit metadata"), page="editbook")
|
||||
return render_edit_book(book_id)
|
||||
except Exception as e:
|
||||
app.logger.exception(e)
|
||||
db.session.rollback()
|
||||
|
Loading…
Reference in New Issue
Block a user