mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-19 06:30:29 +00:00
Merge branch 'master' of https://github.com/janeczku/calibre-web into 621
This commit is contained in:
commit
93a9f65198
@ -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}
|
||||||
|
@ -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')}
|
||||||
|
|
||||||
|
@ -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
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
|
#!/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:
|
||||||
|
@ -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
|
||||||
|
378
cps/web.py
378
cps/web.py
@ -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,14 +3387,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']
|
||||||
@ -3449,9 +3519,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
|
||||||
@ -3477,15 +3548,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('&')
|
||||||
@ -3500,18 +3593,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"]:
|
||||||
@ -3554,7 +3646,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()]
|
||||||
@ -3567,6 +3658,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:
|
||||||
@ -3585,104 +3677,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()
|
||||||
|
Loading…
Reference in New Issue
Block a user