Merge branch 'master' into development

This commit is contained in:
Ozzie Isaacs 2021-01-30 14:41:46 +01:00
commit e0b8fe3b1a
66 changed files with 8120 additions and 7946 deletions

View File

@ -31,7 +31,7 @@ If applicable, add screenshots to help explain your problem.
- OS: [e.g. Windows 10/Raspberry Pi OS]
- Python version: [e.g. python2.7]
- Calibre-Web version: [e.g. 0.6.8 or 087c4c59 (git rev-parse --short HEAD)]:
- Docker container: [None/Technosoft2000/Linuxuser]:
- Docker container: [None/Technosoft2000/LinuxServer]:
- Special Hardware: [e.g. Rasperry Pi Zero]
- Browser: [e.g. Chrome 83.0.4103.97, Safari 13.3.7, Firefox 68.0.1 ESR]

View File

@ -32,8 +32,8 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d
## Quick start
1. Install dependencies by running `pip3 install --target vendor -r requirements.txt` (python3.x) or `pip install --target vendor -r requirements.txt` (python2.7).
2. Execute the command: `python cps.py` (or `nohup python cps.py` - recommended if you want to exit the terminal window)
1. Install dependencies by running `pip3 install --target vendor -r requirements.txt` (python3.x). Alternativly set up a python virtual environment.
2. Execute the command: `python3 cps.py` (or `nohup python3 cps.py` - recommended if you want to exit the terminal window)
3. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog
4. Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button\
Optionally a Google Drive can be used to host the calibre library [-> Using Google Drive integration](https://github.com/janeczku/calibre-web/wiki/Configuration#using-google-drive-integration)
@ -48,7 +48,7 @@ Please note that running the above install command can fail on some versions of
## Requirements
python 3.x+, (Python 2.7+)
python 3.x+
Optionally, to enable on-the-fly conversion from one ebook format to another when using the send-to-kindle feature, or during editing of ebooks metadata:

2
cps.py
View File

@ -31,7 +31,7 @@ else:
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'vendor'))
from cps import create_app, config
from cps import create_app
from cps import web_server
from cps.opds import opds
from cps.web import web

View File

@ -45,6 +45,7 @@ mimetypes.add_type('application/fb2+zip', '.fb2')
mimetypes.add_type('application/x-mobipocket-ebook', '.mobi')
mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
mimetypes.add_type('application/vnd.amazon.ebook', '.azw')
mimetypes.add_type('application/x-mobi8-ebook', '.azw3')
mimetypes.add_type('application/x-cbr', '.cbr')
mimetypes.add_type('application/x-cbz', '.cbz')
mimetypes.add_type('application/x-cbt', '.cbt')
@ -98,6 +99,9 @@ def create_app():
cache_buster.init_cache_busting(app)
log.info('Starting Calibre Web...')
if sys.version_info < (3, 0):
log.info('Python2 is EOL since end of 2019, this version of Calibre-Web supporting Python2 please consider upgrading to Python3')
print('Python2 is EOL since end of 2019, this version of Calibre-Web supporting Python2 please consider upgrading to Python3')
Principal(app)
lm.init_app(app)
app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))

View File

@ -636,7 +636,7 @@ def pathchooser():
"parentdir": parentdir,
"type": browse_for,
"oldfile": oldfile,
"absolute": abs,
"absolute": absolute,
}
return json.dumps(context)

View File

@ -110,7 +110,10 @@ if ipadress:
sys.exit(1)
# handle and check user password argument
user_password = args.s or None
user_credentials = args.s or None
if user_credentials and ":" not in user_credentials:
print("No valid username:password format")
sys.exit(3)
# Handles enableing of filepicker
filepicker = args.f or None

View File

@ -21,6 +21,7 @@ import sys
import os
from collections import namedtuple
# if installed via pip this variable is set to true
HOME_CONFIG = False
# Base dir is parent of current file, necessary if called from different folder
@ -40,7 +41,7 @@ if HOME_CONFIG:
os.makedirs(home_dir)
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', home_dir)
else:
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', BASE_DIR)
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', BASE_DIR)
ROLE_USER = 0 << 0
@ -130,7 +131,7 @@ def selected_roles(dictionary):
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
'series_id, languages')
STABLE_VERSION = {'version': '0.6.10 Beta'}
STABLE_VERSION = {'version': '0.6.11 Beta'}
NIGHTLY_VERSION = {}
NIGHTLY_VERSION[0] = '$Format:%H$'

View File

@ -50,6 +50,8 @@ try:
except ImportError:
use_unidecode = False
log = logger.create()
cc_exceptions = ['datetime', 'comments', 'composite', 'series']
cc_classes = {}
@ -402,7 +404,10 @@ class AlchemyEncoder(json.JSONEncoder):
el.append(ele.get())
else:
el.append(json.dumps(ele, cls=AlchemyEncoder))
data = ",".join(el)
if field == 'authors':
data = " & ".join(el)
else:
data = ",".join(el)
if data == '[]':
data = ""
else:
@ -619,11 +624,14 @@ class CalibreDB():
.join(*join, isouter=True) \
.filter(db_filter) \
.filter(self.common_filters(allow_show_archived))
pagination = Pagination(page, pagesize,
len(query.all()))
entries = query.order_by(*order).offset(off).limit(pagesize).all()
for book in entries:
book = self.order_authors(book)
try:
pagination = Pagination(page, pagesize,
len(query.all()))
entries = query.order_by(*order).offset(off).limit(pagesize).all()
except Exception as e:
log.debug_or_exception(e)
#for book in entries:
# book = self.order_authors(book)
return entries, randm, pagination
# Orders all Authors in the list according to authors sort

View File

@ -30,8 +30,8 @@ from uuid import uuid4
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
from flask_babel import gettext as _
from flask_login import current_user, login_required
from sqlalchemy.exc import OperationalError
from sqlalchemy.exc import OperationalError, IntegrityError
from sqlite3 import OperationalError as sqliteOperationalError
from . import constants, logger, isoLanguages, gdriveutils, uploader, helper
from . import config, get_locale, ub, db
from . import calibre_db
@ -310,7 +310,6 @@ def delete_book(book_id, book_format, jsonResponse):
def render_edit_book(book_id):
calibre_db.update_title_sort(config)
cc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
if not book:
@ -570,7 +569,7 @@ def upload_single_file(request, book, book_id):
calibre_db.session.add(db_format)
calibre_db.session.commit()
calibre_db.update_title_sort(config)
except OperationalError as e:
except (OperationalError, IntegrityError) as e:
calibre_db.session.rollback()
log.error('Database error: %s', e)
flash(_(u"Database error: %(error)s.", error=e), category="error")
@ -607,12 +606,19 @@ def upload_cover(request, book):
@edit_required
def edit_book(book_id):
modif_date = False
# create the function for sorting...
try:
calibre_db.update_title_sort(config)
except sqliteOperationalError as e:
log.debug_or_exception(e)
calibre_db.session.rollback()
# Show form
if request.method != 'POST':
return render_edit_book(book_id)
# create the function for sorting...
calibre_db.update_title_sort(config)
book = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
# Book not found
@ -925,7 +931,7 @@ def upload():
else:
resp = {"location": url_for('web.show_book', book_id=book_id)}
return Response(json.dumps(resp), mimetype='application/json')
except OperationalError as e:
except (OperationalError, IntegrityError) as e:
calibre_db.session.rollback()
log.error("Database error: %s", e)
flash(_(u"Database error: %(error)s.", error=e), category="error")

View File

@ -394,7 +394,8 @@ def uploadFileToEbooksFolder(destFile, f):
if len(existingFiles) > 0:
driveFile = existingFiles[0]
else:
driveFile = drive.CreateFile({'title': x, 'parents': [{"kind": "drive#fileLink", 'id': parent['id']}],})
driveFile = drive.CreateFile({'title': x,
'parents': [{"kind": "drive#fileLink", 'id': parent['id']}], })
driveFile.SetContentFile(f)
driveFile.Upload()
else:

View File

@ -32,7 +32,7 @@ from tempfile import gettempdir
import requests
from babel.dates import format_datetime
from babel.units import format_unit
from flask import send_from_directory, make_response, redirect, abort, url_for, send_file
from flask import send_from_directory, make_response, redirect, abort, url_for, request
from flask_babel import gettext as _
from flask_login import current_user
from sqlalchemy.sql.expression import true, false, and_, text
@ -68,6 +68,7 @@ try:
except (ImportError, RuntimeError) as e:
log.debug('Cannot import Image, generating covers from non jpg files will not work: %s', e)
use_IM = False
MissingDelegateError = BaseException
# Convert existing book entry to new format

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,6 @@ from flask_login import current_user
from werkzeug.datastructures import Headers
from sqlalchemy import func
from sqlalchemy.sql.expression import and_, or_
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import load_only
from sqlalchemy.exc import StatementError
import requests
@ -58,7 +57,7 @@ KOBO_FORMATS = {"KEPUB": ["KEPUB"], "EPUB": ["EPUB3", "EPUB"]}
KOBO_STOREAPI_URL = "https://storeapi.kobo.com"
KOBO_IMAGEHOST_URL = "https://kbimages1-a.akamaihd.net"
SYNC_ITEM_LIMIT = 5
SYNC_ITEM_LIMIT = 100
kobo = Blueprint("kobo", __name__, url_prefix="/kobo/<auth_token>")
kobo_auth.disable_failed_auth_redirect_for_blueprint(kobo)

View File

@ -66,7 +66,6 @@ from os import urandom
from flask import g, Blueprint, url_for, abort, request
from flask_login import login_user, login_required
from flask_babel import gettext as _
from sqlalchemy.exc import OperationalError
from . import logger, ub, lm
from .render_template import render_title_template

View File

@ -44,10 +44,22 @@ logging.addLevelName(logging.CRITICAL, "CRIT")
class _Logger(logging.Logger):
def debug_or_exception(self, message, *args, **kwargs):
if is_debug_enabled():
self.exception(message, stacklevel=2, *args, **kwargs)
if sys.version_info > (3, 7):
if is_debug_enabled():
self.exception(message, stacklevel=2, *args, **kwargs)
else:
self.error(message, stacklevel=2, *args, **kwargs)
elif sys.version_info > (3, 0):
if is_debug_enabled():
self.exception(message, stack_info=True, *args, **kwargs)
else:
self.error(message, *args, **kwargs)
else:
self.error(message, stacklevel=2, *args, **kwargs)
if is_debug_enabled():
self.exception(message, *args, **kwargs)
else:
self.error(message, *args, **kwargs)
def debug_no_auth(self, message, *args, **kwargs):
if message.startswith("send: AUTH"):

View File

@ -32,7 +32,6 @@ from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.contrib.google import make_google_blueprint, google
from flask_login import login_user, current_user, login_required
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import OperationalError
from . import constants, logger, config, app, ub

View File

@ -430,7 +430,12 @@ def check_auth(username, password):
username = username.encode('utf-8')
user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) ==
username.decode('utf-8').lower()).first()
return bool(user and check_password_hash(str(user.password), password))
if bool(user and check_password_hash(str(user.password), password)):
return True
else:
ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr)
log.warning('OPDS Login failed for user "%s" IP-address: %s', username.decode('utf-8'), ipAdress)
return False
def authenticate():

View File

@ -635,6 +635,14 @@ function init(filename) {
// Focus the scrollable area so that keyboard scrolling work as expected
$("#mainContent").focus();
$("#mainContent").swipe( {
swipeRight:function() {
showLeftPage();
},
swipeLeft:function() {
showRightPage();
},
});
$("#mainImage").click(function(evt) {
// Firefox does not support offsetX/Y so we have to manually calculate
// where the user clicked in the image.

View File

@ -82,7 +82,6 @@ $(".container-fluid").bind('drop', function (e) {
var files = e.originalEvent.dataTransfer.files;
var test = $("#btn-upload")[0].accept;
$(this).css('background', '');
// var final = [];
const dt = new DataTransfer()
jQuery.each(files, function (index, item) {
if (test.indexOf(item.name.substr(item.name.lastIndexOf('.'))) !== -1) {

View File

@ -20,6 +20,34 @@ var reader;
$("#bookmark, #show-Bookmarks").remove();
}
// Enable swipe support
// I have no idea why swiperRight/swiperLeft from plugins is not working, events just don't get fired
var touchStart = 0;
var touchEnd = 0;
reader.rendition.on('touchstart', function(event) {
touchStart = event.changedTouches[0].screenX;
});
reader.rendition.on('touchend', function(event) {
touchEnd = event.changedTouches[0].screenX;
if (touchStart < touchEnd) {
if(reader.book.package.metadata.direction === "rtl") {
reader.rendition.next();
} else {
reader.rendition.prev();
}
// Swiped Right
}
if (touchStart > touchEnd) {
if(reader.book.package.metadata.direction === "rtl") {
reader.rendition.prev();
} else {
reader.rendition.next();
}
// Swiped Left
}
});
/**
* @param {string} action - Add or remove bookmark
* @param {string|int} location - Location or zero
@ -43,3 +71,5 @@ var reader;
});
}
})();

View File

@ -26,7 +26,7 @@
</div>
{% if not filepicker %}
<div class="form-group">
<label id="filepicker-hint">{{_('To activate serverside filepicker start Calibre-Web with -f optionn')}}</label>
<label id="filepicker-hint">{{_('To activate serverside filepicker start Calibre-Web with -f option')}}</label>
</div>
{% endif %}
{% if feature_support['gdrive'] %}

View File

@ -78,7 +78,7 @@
</div>
</div>
<div class="overlay"></div>
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/jszip.min.js') }}">
</script> <script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
<script type="text/javascript">
@ -91,10 +91,7 @@
useBookmarks: "{{ g.user.is_authenticated | tojson }}"
};
</script>
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/jszip.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/reading/epub.js') }}"></script>
</body>

View File

@ -13,14 +13,10 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/kthoom.css') }}" type="text/css"/>
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/plugins.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/io/bytestream.js') }}"></script>
<script src="{{ url_for('static', filename='js/io/bytebuffer.js') }}"></script>
<script src="{{ url_for('static', filename='js/io/bitstream.js') }}"></script>
<script src="{{ url_for('static', filename='js/archive/archive.js') }}"></script>
<script src="{{ url_for('static', filename='js/archive/rarvm.js') }}"></script>
<script src="{{ url_for('static', filename='js/archive/unrar5.js') }}"></script>
<script src="{{ url_for('static', filename='js/kthoom.js') }}"></script>
<script src="{{ url_for('static', filename='js/archive/archive.js') }}"></script>
<script>
var updateArrows = function() {
if ($('input[name="direction"]:checked').val() === "0") {

View File

@ -12,6 +12,7 @@
<!-- EPUBJS Renderer -->
<!--<script src="../build/epub.js"></script>-->
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/libs/plugins.js') }}"></script>
<style type="text/css">
@ -97,6 +98,14 @@
$( "#right" ).click(function() {
nextPage();
});
$("#readmain").swipe( {
swipeRight:function() {
prevPage();
},
swipeLeft:function() {
nextPage();
},
});
//bind mouse
$(window).bind('DOMMouseScroll mousewheel', function(event) {
var delta = 0;

View File

@ -98,7 +98,7 @@
<div class="row">
<div class="form-group col-sm-6">
<div><label for="include_extension">{{_('Extensions')}}</label></div>
<select id="include_extension" class="selectpicker" name="include_extension" id="include_extension" data-live-search="true" data-style="btn-primary" multiple>
<select class="selectpicker" name="include_extension" id="include_extension" data-live-search="true" data-style="btn-primary" multiple>
{% for extension in extensions %}
<option value="{{extension.format}}">{{extension.format}}</option>
{% endfor %}
@ -106,7 +106,7 @@
</div>
<div class="form-group col-sm-6">
<div><label for="exclude_extension">{{_('Exclude Extensions')}}</label></div>
<select id="exclude_extension" class="selectpicker" name="exclude_extension" id="exclude_extension" data-live-search="true" data-style="btn-danger" multiple>
<select class="selectpicker" name="exclude_extension" id="exclude_extension" data-live-search="true" data-style="btn-danger" multiple>
{% for extension in extensions %}
<option value="{{extension.format}}">{{extension.format}}</option>
{% endfor %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,10 +43,11 @@ from sqlalchemy import Column, ForeignKey
from sqlalchemy import String, Integer, SmallInteger, Boolean, DateTime, Float, JSON
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy.sql.expression import func
from sqlalchemy.orm import backref, relationship, sessionmaker, Session, scoped_session
from werkzeug.security import generate_password_hash
from . import constants, logger
from . import constants, logger, cli
log = logger.create()
@ -226,9 +227,6 @@ class Anonymous(AnonymousUserMixin, UserBase):
self.denied_column_value = data.denied_column_value
self.allowed_column_value = data.allowed_column_value
self.view_settings = data.view_settings
# Initialize flask_session once
if 'view' not in flask_session:
flask_session['view']={}
def role_admin(self):
@ -247,14 +245,18 @@ class Anonymous(AnonymousUserMixin, UserBase):
return False
def get_view_property(self, page, prop):
if not flask_session['view'].get(page):
return None
return flask_session['view'][page].get(prop)
if 'view' in flask_session:
if not flask_session['view'].get(page):
return None
return flask_session['view'][page].get(prop)
return None
def set_view_property(self, page, prop, value):
if not flask_session['view'].get(page):
flask_session['view'][page] = dict()
flask_session['view'][page][prop] = value
if 'view' in flask_session:
if not flask_session['view'].get(page):
flask_session['view'][page] = dict()
flask_session['view'][page][prop] = value
return None
# Baseclass representing Shelfs in calibre-web in app.db
@ -680,6 +682,21 @@ def init_db(app_db_path):
create_admin_user(session)
create_anonymous_user(session)
if cli.user_credentials:
username, password = cli.user_credentials.split(':')
user = session.query(User).filter(func.lower(User.nickname) == username.lower()).first()
if user:
user.password = generate_password_hash(password)
if session_commit() == "":
print("Password for user '{}' changed".format(username))
sys.exit(0)
else:
print("Failed changing password")
sys.exit(3)
else:
print("Username '{}' not valid, can't change password".format(username))
sys.exit(3)
def dispose():
global session

View File

@ -24,6 +24,7 @@ from __future__ import division, print_function, unicode_literals
import os
from datetime import datetime
import json
import re
import mimetypes
import chardet # dependency of requests
@ -47,7 +48,7 @@ from werkzeug.security import generate_password_hash, check_password_hash
from . import constants, logger, isoLanguages, services
from . import babel, db, ub, config, get_locale, app
from . import calibre_db, shelf
from . import calibre_db
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
from .helper import check_valid_domain, render_task_status, \
get_cc_columns, get_book_cover, get_download_link, send_mail, generate_random_password, \
@ -184,7 +185,7 @@ def toggle_read(book_id):
calibre_db.session.commit()
except (KeyError, AttributeError):
log.error(u"Custom Column No.%d is not exisiting in calibre database", config.config_read_column)
except OperationalError as e:
except (OperationalError, InvalidRequestError) as e:
calibre_db.session.rollback()
log.error(u"Read status could not set: %e", e)
@ -1198,7 +1199,7 @@ def serve_book(book_id, book_format, anyname):
book = calibre_db.get_book(book_id)
data = calibre_db.get_book_format(book_id, book_format.upper())
if not data:
abort(404)
return "File not in Database"
log.info('Serving book: %s', data.name)
if config.config_use_google_drive:
headers = Headers()
@ -1207,11 +1208,14 @@ def serve_book(book_id, book_format, anyname):
return do_gdrive_download(df, headers, (book_format.upper() == 'TXT'))
else:
if book_format.upper() == 'TXT':
rawdata = open(os.path.join(config.config_calibre_dir, book.path, data.name + "." + book_format),
"rb").read()
result = chardet.detect(rawdata)
return make_response(
rawdata.decode(result['encoding']).encode('utf-8'))
try:
rawdata = open(os.path.join(config.config_calibre_dir, book.path, data.name + "." + book_format),
"rb").read()
result = chardet.detect(rawdata)
return make_response(
rawdata.decode(result['encoding']).encode('utf-8'))
except FileNotFoundError:
return "File Not Found"
return send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format)
@ -1270,11 +1274,17 @@ def register():
if config.config_register_email:
nickname = to_save["email"]
else:
nickname = to_save["nickname"]
if not nickname or not to_save["email"]:
nickname = to_save.get('nickname', None)
if not nickname or not to_save.get("email", None):
flash(_(u"Please fill out all fields!"), category="error")
return render_title_template('register.html', title=_(u"register"), page="register")
#if to_save["email"].count("@") != 1 or not \
# Regex according to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#validation
if not re.search(r"^[\w.!#$%&'*+\\/=?^_`{|}~-]+@[\w](?:[\w-]{0,61}[\w])?(?:\.[\w](?:[\w-]{0,61}[\w])?)*$",
to_save["email"]):
flash(_(u"Invalid e-mail address format"), category="error")
log.warning('Registering failed for user "%s" e-mail address: %s', nickname, to_save["email"])
return render_title_template('register.html', title=_(u"register"), page="register")
existing_user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == nickname
.lower()).first()
@ -1300,7 +1310,7 @@ def register():
return render_title_template('register.html', title=_(u"register"), page="register")
else:
flash(_(u"Your e-mail is not allowed to register"), category="error")
log.warning('Registering failed for user "%s" e-mail address: %s', to_save['nickname'], to_save["email"])
log.warning('Registering failed for user "%s" e-mail address: %s', nickname, to_save["email"])
return render_title_template('register.html', title=_(u"register"), page="register")
flash(_(u"Confirmation e-mail was send to your e-mail account."), category="success")
return redirect(url_for('web.login'))

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
# GDrive Integration
google-api-python-client==1.7.11,<1.8.0
google-api-python-client>=1.7.11,<1.8.0
gevent>=1.2.1,<20.6.0
greenlet>=0.4.12,<0.4.17
httplib2>=0.9.2,<0.18.0
@ -9,7 +9,7 @@ pyasn1-modules>=0.0.8,<0.3.0
pyasn1>=0.1.9,<0.5.0
PyDrive2>=1.3.1,<1.8.0
PyYAML>=3.12
rsa==3.4.2,<4.1.0
rsa>=3.4.2,<4.1.0
six>=1.10.0,<1.15.0
# goodreads
@ -30,7 +30,7 @@ rarfile>=2.7
# other
natsort>=2.2.0,<7.1.0
git+https://github.com/OzzieIsaacs/comicapi.git@7aa91ea95ea7a919df6c6cac817865434a31c228#egg=comicapi
comicapi>= 2.1.3,<2.2.0
#Kobo integration
jsonschema>=3.2.0,<3.3.0

View File

@ -6,7 +6,7 @@ singledispatch>=3.4.0.0,<3.5.0.0
backports_abc>=0.4
Flask>=1.0.2,<1.2.0
iso-639>=0.4.5,<0.5.0
PyPDF2==1.26.0,<1.27.0
PyPDF2>=1.26.0,<1.27.0
pytz>=2016.10
requests>=2.11.1,<2.25.0
SQLAlchemy>=1.3.0,<1.4.0

View File

@ -1,6 +1,3 @@
[bdist_wheel]
universal = 1
[metadata]
name = calibreweb
url = https://github.com/janeczku/calibre-web
@ -20,8 +17,6 @@ license_file = LICENSE
classifiers =
Development Status :: 5 - Production/Stable
License :: OSI Approved :: GNU Affero General Public License v3
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
@ -31,57 +26,61 @@ keywords =
calibre
calibre-web
library
python_requires = >=2.6
python_requires = >=3.0
[options.entry_points]
console_scripts =
cps = calibreweb:main
[options]
include_package_data = True
dependency_links = comicapi @ git+https://github.com/OzzieIsaacs/comicapi.git@5346716578b2843f54d522f44d01bc8d25001d24#egg=comicapi
install_requires =
Babel >= 1.3
Flask-Babel >= 0.11.1
Flask-Login >= 0.3.2
Flask-Principal >= 0.3.2
singledispatch >= 3.4.0.0
backports_abc >= 0.4
Flask >= 0.11
iso-639 >= 0.4.5
PyPDF2 == 1.26.0
pytz >= 2016.10
requests >= 2.11.1
SQLAlchemy >= 1.1.0
tornado >= 4.1
unidecode >= 0.04.19
Babel>=1.3, <2.9
Flask-Babel>=0.11.1,<2.1.0
Flask-Login>=0.3.2,<0.5.1
Flask-Principal>=0.3.2,<0.5.1
singledispatch>=3.4.0.0,<3.5.0.0
backports_abc>=0.4
Flask>=1.0.2,<1.2.0
iso-639>=0.4.5,<0.5.0
PyPDF2>=1.26.0,<1.27.0
pytz>=2016.10
requests>=2.11.1,<2.25.0
SQLAlchemy>=1.3.0,<1.4.0
tornado>=4.1,<6.2
Wand>=0.4.4,<0.7.0
unidecode>=0.04.19,<1.2.0
[options.extras_require]
gdrive =
google-api-python-client == 1.6.1
google-api-python-client==1.6.1
gevent==1.2.1
greenlet==0.4.12
httplib2==0.9.2
oauth2client==4.0.0
uritemplate==3.0.0
pyasn1-modules==0.0.8
pyasn1==0.1.9
PyDrive==1.3.1
PyYAML==3.12
rsa==3.4.2
six==1.10.0
google-api-python-client>=1.7.11,<1.8.0
gevent>=1.2.1,<20.6.0
greenlet>=0.4.12,<0.4.17
httplib2>=0.9.2,<0.18.0
oauth2client>=4.0.0,<4.1.4
uritemplate>=3.0.0,<3.1.0
pyasn1-modules>=0.0.8,<0.3.0
pyasn1>=0.1.9,<0.5.0
PyDrive2>=1.3.1,<1.8.0
PyYAML>=3.12
rsa>=3.4.2,<4.1.0
six>=1.10.0,<1.15.0
goodreads =
goodreads >= 0.3.2
python-Levenshtein>=0.12.0
goodreads>=0.3.2,<0.4.0
python-Levenshtein>=0.12.0,<0.13.0
ldap =
python-ldap>=3.0.0,<3.3.0
Flask-SimpleLDAP>=1.4.0,<1.5.0
oauth =
Flask-Dance>=1.4.0,<3.1.0
SQLAlchemy-Utils>=0.33.5,<0.37.0
metadata =
lxml>=3.8.0
Pillow>=4.0.0
rarfile>=2.7
Wand >= 0.4.4
comics=
natsort>=2.2.0
# find solution for this should belong to comics
# comicapi @ git+https://github.com/OzzieIsaacs/comicapi/archive/5346716578b2843f54d522f44d01bc8d25001d24.zip#egg=comicapi
lxml>=3.8.0,<4.6.0
rarfile>=2.7
comics =
natsort>=2.2.0
comicapi>= 2.1.3,<2.2.0
kobo =
jsonschema>=3.2.0,<3.3.0

File diff suppressed because it is too large Load Diff