|
|
@ -8,6 +8,7 @@ from logging.handlers import RotatingFileHandler
|
|
|
|
import textwrap
|
|
|
|
import textwrap
|
|
|
|
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \
|
|
|
|
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \
|
|
|
|
make_response, g, flash, abort, send_file
|
|
|
|
make_response, g, flash, abort, send_file
|
|
|
|
|
|
|
|
from flask import __version__ as flaskVersion
|
|
|
|
import ub
|
|
|
|
import ub
|
|
|
|
from ub import config
|
|
|
|
from ub import config
|
|
|
|
import helper
|
|
|
|
import helper
|
|
|
@ -16,9 +17,12 @@ import errno
|
|
|
|
from sqlalchemy.sql.expression import func
|
|
|
|
from sqlalchemy.sql.expression import func
|
|
|
|
from sqlalchemy.sql.expression import false
|
|
|
|
from sqlalchemy.sql.expression import false
|
|
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
from sqlalchemy.exc import IntegrityError
|
|
|
|
|
|
|
|
from sqlalchemy import __version__ as sqlalchemyVersion
|
|
|
|
from math import ceil
|
|
|
|
from math import ceil
|
|
|
|
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
|
|
|
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
|
|
|
|
|
|
|
from flask_login import __version__ as flask_loginVersion
|
|
|
|
from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed
|
|
|
|
from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed
|
|
|
|
|
|
|
|
from flask_login import __version__ as flask_principalVersion
|
|
|
|
from flask_babel import Babel
|
|
|
|
from flask_babel import Babel
|
|
|
|
from flask_babel import gettext as _
|
|
|
|
from flask_babel import gettext as _
|
|
|
|
import requests
|
|
|
|
import requests
|
|
|
@ -26,6 +30,7 @@ import zipfile
|
|
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
|
|
|
from babel import Locale as LC
|
|
|
|
from babel import Locale as LC
|
|
|
|
from babel import negotiate_locale
|
|
|
|
from babel import negotiate_locale
|
|
|
|
|
|
|
|
from babel import __version__ as babelVersion
|
|
|
|
from babel.dates import format_date
|
|
|
|
from babel.dates import format_date
|
|
|
|
from functools import wraps
|
|
|
|
from functools import wraps
|
|
|
|
import base64
|
|
|
|
import base64
|
|
|
@ -34,13 +39,13 @@ import json
|
|
|
|
import urllib
|
|
|
|
import urllib
|
|
|
|
import datetime
|
|
|
|
import datetime
|
|
|
|
from iso639 import languages as isoLanguages
|
|
|
|
from iso639 import languages as isoLanguages
|
|
|
|
|
|
|
|
from iso639 import __version__ as iso639Version
|
|
|
|
from uuid import uuid4
|
|
|
|
from uuid import uuid4
|
|
|
|
import os.path
|
|
|
|
import os.path
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
import subprocess
|
|
|
|
import subprocess
|
|
|
|
import re
|
|
|
|
import re
|
|
|
|
import db
|
|
|
|
import db
|
|
|
|
import thread
|
|
|
|
|
|
|
|
from shutil import move, copyfile
|
|
|
|
from shutil import move, copyfile
|
|
|
|
from tornado.ioloop import IOLoop
|
|
|
|
from tornado.ioloop import IOLoop
|
|
|
|
import shutil
|
|
|
|
import shutil
|
|
|
@ -50,6 +55,8 @@ import io
|
|
|
|
import hashlib
|
|
|
|
import hashlib
|
|
|
|
import threading
|
|
|
|
import threading
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from tornado import version as tornadoVersion
|
|
|
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
current_milli_time = lambda: int(round(time.time() * 1000))
|
|
|
|
current_milli_time = lambda: int(round(time.time() * 1000))
|
|
|
@ -66,6 +73,8 @@ from cgi import escape
|
|
|
|
gdrive_watch_callback_token='target=calibreweb-watch_files'
|
|
|
|
gdrive_watch_callback_token='target=calibreweb-watch_files'
|
|
|
|
global_task = None
|
|
|
|
global_task = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', 'fb2'])
|
|
|
|
|
|
|
|
|
|
|
|
def md5(fname):
|
|
|
|
def md5(fname):
|
|
|
|
hash_md5 = hashlib.md5()
|
|
|
|
hash_md5 = hashlib.md5()
|
|
|
|
with open(fname, "rb") as f:
|
|
|
|
with open(fname, "rb") as f:
|
|
|
@ -1162,6 +1171,17 @@ def stats():
|
|
|
|
if re.search('Amazon kindlegen\(', lines):
|
|
|
|
if re.search('Amazon kindlegen\(', lines):
|
|
|
|
versions['KindlegenVersion'] = lines
|
|
|
|
versions['KindlegenVersion'] = lines
|
|
|
|
versions['PythonVersion'] = sys.version
|
|
|
|
versions['PythonVersion'] = sys.version
|
|
|
|
|
|
|
|
versions['babel'] = babelVersion
|
|
|
|
|
|
|
|
versions['sqlalchemy'] = sqlalchemyVersion
|
|
|
|
|
|
|
|
versions['flask'] = flaskVersion
|
|
|
|
|
|
|
|
versions['flasklogin'] = flask_loginVersion
|
|
|
|
|
|
|
|
versions['flask_principal'] = flask_principalVersion
|
|
|
|
|
|
|
|
versions['tornado'] = tornadoVersion
|
|
|
|
|
|
|
|
versions['iso639'] = iso639Version
|
|
|
|
|
|
|
|
versions['requests'] = requests.__version__
|
|
|
|
|
|
|
|
versions['pysqlite'] = db.engine.dialect.dbapi.version
|
|
|
|
|
|
|
|
versions['sqlite'] = db.engine.dialect.dbapi.sqlite_version
|
|
|
|
|
|
|
|
|
|
|
|
return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=versions,
|
|
|
|
return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=versions,
|
|
|
|
categorycounter=categorys, seriecounter=series, title=_(u"Statistics"))
|
|
|
|
categorycounter=categorys, seriecounter=series, title=_(u"Statistics"))
|
|
|
|
|
|
|
|
|
|
|
@ -1457,7 +1477,7 @@ def unread_books(page):
|
|
|
|
return render_read_books(page, False)
|
|
|
|
return render_read_books(page, False)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route("/read/<int:book_id>/<format>")
|
|
|
|
@app.route("/read/<int:book_id>/<format>")
|
|
|
|
@login_required
|
|
|
|
@login_required_if_no_ano
|
|
|
|
def read_book(book_id, format):
|
|
|
|
def read_book(book_id, format):
|
|
|
|
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
|
|
|
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
|
|
|
if book:
|
|
|
|
if book:
|
|
|
@ -2105,6 +2125,8 @@ def edit_mailsettings():
|
|
|
|
category="success")
|
|
|
|
category="success")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error")
|
|
|
|
flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
flash(_(u"E-Mail settings updated"), category="success")
|
|
|
|
return render_title_template("email_edit.html", content=content, title=_(u"Edit mail settings"))
|
|
|
|
return render_title_template("email_edit.html", content=content, title=_(u"Edit mail settings"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -2448,6 +2470,18 @@ def upload():
|
|
|
|
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
|
|
|
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
|
|
|
|
if request.method == 'POST' and 'btn-upload' in request.files:
|
|
|
|
if request.method == 'POST' and 'btn-upload' in request.files:
|
|
|
|
file = request.files['btn-upload']
|
|
|
|
file = request.files['btn-upload']
|
|
|
|
|
|
|
|
if '.' in file.filename:
|
|
|
|
|
|
|
|
file_ext = file.filename.rsplit('.', 1)[-1].lower()
|
|
|
|
|
|
|
|
if file_ext not in ALLOWED_EXTENSIONS:
|
|
|
|
|
|
|
|
flash(
|
|
|
|
|
|
|
|
_('File extension "%s" is not allowed to be uploaded to this server' %
|
|
|
|
|
|
|
|
file_ext),
|
|
|
|
|
|
|
|
category="error"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
flash(_('File to be uploaded must have an extension'), category="error")
|
|
|
|
|
|
|
|
return redirect(url_for('index'))
|
|
|
|
meta = uploader.upload(file)
|
|
|
|
meta = uploader.upload(file)
|
|
|
|
|
|
|
|
|
|
|
|
title = meta.title
|
|
|
|
title = meta.title
|
|
|
|