Commit b4324cd6 authored by mmonkey's avatar mmonkey

Merge remote-tracking branch 'upstream/master' into master

parents bc8bdfe3 ca212c87
......@@ -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:
......
This diff is collapsed.
......@@ -275,7 +275,7 @@ class _ConfigSQL(object):
def toDict(self):
storage = {}
for k, v in self.__dict__.items():
if k[0] != '_' or k.endswith("password"):
if k[0] != '_' and not k.endswith("password") and not k.endswith("secret"):
storage[k] = v
return storage
......
......@@ -240,7 +240,7 @@ def delete_book(book_id, book_format, jsonResponse):
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete()
ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete()
ub.delete_download(book_id)
ub.session.commit()
ub.session_commit()
# check if only this book links to:
# author, language, series, tags, custom columns
......
......@@ -58,7 +58,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)
......@@ -462,10 +462,7 @@ def HandleTagCreate():
items_unknown_to_calibre = add_items_to_shelf(items, shelf)
if items_unknown_to_calibre:
log.debug("Received request to add unknown books to a collection. Silently ignoring items.")
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return make_response(jsonify(str(shelf.uuid)), 201)
......@@ -497,10 +494,7 @@ def HandleTagUpdate(tag_id):
shelf.name = name
ub.session.merge(shelf)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return make_response(' ', 200)
......@@ -552,11 +546,7 @@ def HandleTagAddItem(tag_id):
log.debug("Received request to add an unknown book to a collection. Silently ignoring item.")
ub.session.merge(shelf)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return make_response('', 201)
......@@ -596,10 +586,7 @@ def HandleTagRemoveItem(tag_id):
shelf.books.filter(ub.BookShelf.book_id == book.id).delete()
except KeyError:
items_unknown_to_calibre.append(item)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
if items_unknown_to_calibre:
log.debug("Received request to remove an unknown book to a collecition. Silently ignoring item.")
......@@ -645,10 +632,7 @@ def sync_shelves(sync_token, sync_results):
"ChangedTag": tag
})
sync_token.tags_last_modified = new_tags_last_modified
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
# Creates a Kobo "Tag" object from a ub.Shelf object
......@@ -729,10 +713,7 @@ def HandleStateRequest(book_uuid):
abort(400, description="Malformed request data is missing 'ReadingStates' key")
ub.session.merge(kobo_reading_state)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return jsonify({
"RequestResult": "Success",
"UpdateResults": [update_results_response],
......@@ -770,10 +751,7 @@ def get_or_create_reading_state(book_id):
kobo_reading_state.statistics = ub.KoboStatistics()
book_read.kobo_reading_state = kobo_reading_state
ub.session.add(book_read)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return book_read.kobo_reading_state
......@@ -876,11 +854,7 @@ def HandleBookDeletionRequest(book_uuid):
archived_book.last_modified = datetime.datetime.utcnow()
ub.session.merge(archived_book)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return ("", 204)
......
......@@ -148,10 +148,7 @@ def generate_auth_token(user_id):
auth_token.token_type = 1
ub.session.add(auth_token)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return render_title_template(
"generate_kobo_auth_url.html",
title=_(u"Kobo Setup"),
......@@ -168,8 +165,5 @@ def delete_auth_token(user_id):
# Invalidate any prevously generated Kobo Auth token for this user.
ub.session.query(ub.RemoteAuthToken).filter(ub.RemoteAuthToken.user_id == user_id)\
.filter(ub.RemoteAuthToken.token_type==1).delete()
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
return ""
return ub.session_commit()
......@@ -49,6 +49,13 @@ class _Logger(logging.Logger):
else:
self.error(message, stacklevel=2, *args, **kwargs)
def debug_no_auth(self, message, *args, **kwargs):
if message.startswith("send: AUTH"):
self.debug(message[:16], stacklevel=2, *args, **kwargs)
else:
self.debug(message, stacklevel=2, *args, **kwargs)
def get(name=None):
return logging.getLogger(name)
......
......@@ -85,11 +85,7 @@ def register_user_with_oauth(user=None):
except NoResultFound:
# no found, return error
return
try:
ub.session.commit()
except Exception as e:
log.debug_or_exception(e)
ub.session.rollback()
ub.session_commit("User {} with OAuth for provider {} registered".format(user.nickname, oauth_key))
def logout_oauth_user():
......@@ -101,19 +97,12 @@ def logout_oauth_user():
if ub.oauth_support:
oauthblueprints = []
if not ub.session.query(ub.OAuthProvider).count():
oauthProvider = ub.OAuthProvider()
oauthProvider.provider_name = "github"
oauthProvider.active = False
ub.session.add(oauthProvider)
ub.session.commit()
oauthProvider = ub.OAuthProvider()
oauthProvider.provider_name = "google"
oauthProvider.active = False
ub.session.add(oauthProvider)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
for provider in ("github", "google"):
oauthProvider = ub.OAuthProvider()
oauthProvider.provider_name = provider
oauthProvider.active = False
ub.session.add(oauthProvider)
ub.session_commit("{} Blueprint Created".format(provider))
oauth_ids = ub.session.query(ub.OAuthProvider).all()
ele1 = dict(provider_name='github',
......@@ -203,12 +192,8 @@ if ub.oauth_support:
provider_user_id=provider_user_id,
token=token,
)
try:
ub.session.add(oauth_entry)
ub.session.commit()
except Exception as e:
log.debug_or_exception(e)
ub.session.rollback()
ub.session.add(oauth_entry)
ub.session_commit()
# Disable Flask-Dance's default behavior for saving the OAuth token
# Value differrs depending on flask-dance version
......
......@@ -59,8 +59,7 @@ def remote_login_required(f):
def remote_login():
auth_token = ub.RemoteAuthToken()
ub.session.add(auth_token)
ub.session.commit()
ub.session_commit()
verify_url = url_for('remotelogin.verify_token', token=auth_token.auth_token, _external=true)
log.debug(u"Remot Login request with token: %s", auth_token.auth_token)
return render_title_template('remote_login.html', title=_(u"login"), token=auth_token.auth_token,
......@@ -80,9 +79,9 @@ def verify_token(token):
return redirect(url_for('web.index'))
# Token expired
if datetime.now() > auth_token.expiration:
elif datetime.now() > auth_token.expiration:
ub.session.delete(auth_token)
ub.session.commit()
ub.session_commit()
flash(_(u"Token has expired"), category="error")
log.error(u"Remote Login token expired")
......@@ -91,7 +90,7 @@ def verify_token(token):
# Update token with user information
auth_token.user_id = current_user.id
auth_token.verified = True
ub.session.commit()
ub.session_commit()
flash(_(u"Success! Please return to your device"), category="success")
log.debug(u"Remote Login token for userid %s verified", auth_token.user_id)
......@@ -114,7 +113,7 @@ def token_verified():
# Token expired
elif datetime.now() > auth_token.expiration:
ub.session.delete(auth_token)
ub.session.commit()
ub.session_commit()
data['status'] = 'error'
data['message'] = _(u"Token has expired")
......@@ -127,7 +126,7 @@ def token_verified():
login_user(user)
ub.session.delete(auth_token)
ub.session.commit()
ub.session_commit("User {} logged in via remotelogin, token deleted".format(user.nickname))
data['status'] = 'success'
log.debug(u"Remote Login for userid %s succeded", user.id)
......
......@@ -27,7 +27,7 @@ import sys
from flask import Blueprint, request, flash, redirect, url_for
from flask_babel import gettext as _
from flask_login import login_required, current_user
from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import func, true
from sqlalchemy.exc import OperationalError, InvalidRequestError
from . import logger, ub, calibre_db, db
......@@ -221,96 +221,76 @@ def remove_from_shelf(shelf_id, book_id):
@login_required
def create_shelf():
shelf = ub.Shelf()
if request.method == "POST":
to_save = request.form.to_dict()
if "is_public" in to_save:
shelf.is_public = 1
shelf.name = to_save["title"]
shelf.user_id = int(current_user.id)
is_shelf_name_unique = False
if shelf.is_public == 1:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) &
(ub.Shelf.user_id == int(current_user.id)))\
.first() is None
if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
if is_shelf_name_unique:
try:
ub.session.add(shelf)
ub.session.commit()
flash(_(u"Shelf %(title)s created", title=to_save["title"]), category="success")
return redirect(url_for('shelf.show_shelf', shelf_id=shelf.id))
except (OperationalError, InvalidRequestError):
ub.session.rollback()
flash(_(u"Settings DB is not Writeable"), category="error")
except Exception:
ub.session.rollback()
flash(_(u"There was an error"), category="error")
return render_title_template('shelf_edit.html', shelf=shelf, title=_(u"Create a Shelf"), page="shelfcreate")
else:
return render_title_template('shelf_edit.html', shelf=shelf, title=_(u"Create a Shelf"), page="shelfcreate")
return create_edit_shelf(shelf, title=_(u"Create a Shelf"), page="shelfcreate")
@shelf.route("/shelf/edit/<int:shelf_id>", methods=["GET", "POST"])
@login_required
def edit_shelf(shelf_id):
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
if request.method == "POST":
to_save = request.form.to_dict()
return create_edit_shelf(shelf, title=_(u"Edit a shelf"), page="shelfedit", shelf_id=shelf_id)
is_shelf_name_unique = False
if shelf.is_public == 1:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \
.filter(ub.Shelf.id != shelf_id) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
# if shelf ID is set, we are editing a shelf
def create_edit_shelf(shelf, title, page, shelf_id=False):
if request.method == "POST":
to_save = request.form.to_dict()
if "is_public" in to_save:
shelf.is_public = 1
else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) &
(ub.Shelf.user_id == int(current_user.id)))\
.filter(ub.Shelf.id != shelf_id)\
.first() is None
if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
if is_shelf_name_unique:
shelf.is_public = 0
if check_shelf_is_unique(shelf, to_save, shelf_id):
shelf.name = to_save["title"]
shelf.last_modified = datetime.utcnow()
if "is_public" in to_save:
shelf.is_public = 1
# shelf.last_modified = datetime.utcnow()
if not shelf_id:
shelf.user_id = int(current_user.id)
ub.session.add(shelf)
shelf_action = "created"
flash_text = _(u"Shelf %(title)s created", title=to_save["title"])
else:
shelf.is_public = 0
shelf_action = "changed"
flash_text = _(u"Shelf %(title)s changed", title=to_save["title"])
try:
ub.session.commit()
flash(_(u"Shelf %(title)s changed", title=to_save["title"]), category="success")
except (OperationalError, InvalidRequestError):
log.info(u"Shelf {} {}".format(to_save["title"], shelf_action))
flash(flash_text, category="success")
return redirect(url_for('shelf.show_shelf', shelf_id=shelf.id))
except (OperationalError, InvalidRequestError) as e:
ub.session.rollback()
log.debug_or_exception(e)
flash(_(u"Settings DB is not Writeable"), category="error")
except Exception:
except Exception as e:
ub.session.rollback()
log.debug_or_exception(e)
flash(_(u"There was an error"), category="error")
return render_title_template('shelf_edit.html', shelf=shelf, title=_(u"Edit a shelf"), page="shelfedit")
return render_title_template('shelf_edit.html', shelf=shelf, title=title, page=page)
def check_shelf_is_unique(shelf, to_save, shelf_id=False):
if shelf_id:
ident = ub.Shelf.id != shelf_id
else:
return render_title_template('shelf_edit.html', shelf=shelf, title=_(u"Edit a shelf"), page="shelfedit")
ident = true()
if shelf.is_public == 1:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 1)) \
.filter(ident) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A public shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
else:
is_shelf_name_unique = ub.session.query(ub.Shelf) \
.filter((ub.Shelf.name == to_save["title"]) & (ub.Shelf.is_public == 0) &
(ub.Shelf.user_id == int(current_user.id))) \
.filter(ident) \
.first() is None
if not is_shelf_name_unique:
flash(_(u"A private shelf with the name '%(title)s' already exists.", title=to_save["title"]),
category="error")
return is_shelf_name_unique
def delete_shelf_helper(cur_shelf):
......@@ -320,12 +300,7 @@ def delete_shelf_helper(cur_shelf):
ub.session.delete(cur_shelf)
ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id).delete()
ub.session.add(ub.ShelfArchive(uuid=cur_shelf.uuid, user_id=cur_shelf.user_id))
try:
ub.session.commit()
log.info("successfully deleted %s", cur_shelf)
except OperationalError:
ub.session.rollback()
ub.session_commit("successfully deleted Shelf {}".format(cur_shelf.name))
@shelf.route("/shelf/delete/<int:shelf_id>")
......@@ -339,11 +314,13 @@ def delete_shelf(shelf_id):
flash(_(u"Settings DB is not Writeable"), category="error")
return redirect(url_for('web.index'))
@shelf.route("/simpleshelf/<int:shelf_id>")
@login_required_if_no_ano
def show_simpleshelf(shelf_id):
return render_show_shelf(2, shelf_id, 1, None)
@shelf.route("/shelf/<int:shelf_id>", defaults={"sort_param": "order", 'page': 1})
@shelf.route("/shelf/<int:shelf_id>/<sort_param>", defaults={'page': 1})
@shelf.route("/shelf/<int:shelf_id>/<sort_param>/<int:page>")
......@@ -381,6 +358,7 @@ def order_shelf(shelf_id):
title=_(u"Change order of Shelf: '%(name)s'", name=shelf.name),
shelf=shelf, page="shelforder")
def change_shelf_order(shelf_id, order):
result = calibre_db.session.query(db.Books).join(ub.BookShelf,ub.BookShelf.book_id == db.Books.id)\
.filter(ub.BookShelf.shelf == shelf_id).order_by(*order).all()
......@@ -388,10 +366,8 @@ def change_shelf_order(shelf_id, order):
book = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \
.filter(ub.BookShelf.book_id == entry.id).first()
book.order = index
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit("Shelf-id:{} - Order changed".format(shelf_id))
def render_show_shelf(shelf_type, shelf_id, page_no, sort_param):
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
......
......@@ -66,9 +66,15 @@ class TaskConvert(CalibreTask):
# if we're sending to kindle after converting, create a one-off task and run it immediately
# todo: figure out how to incorporate this into the progress
try:
worker_thread.add(self.user, TaskEmail(self.settings['subject'], self.results["path"],
filename, self.settings, self.kindle_mail,
self.settings['subject'], self.settings['body'], internal=True))
worker_thread.add(self.user, TaskEmail(self.settings['subject'],
self.results["path"],
filename,
self.settings,
self.kindle_mail,
self.settings['subject'],
self.settings['body'],
internal=True)
)
except Exception as e:
return self._handleError(str(e))
......
......@@ -44,7 +44,7 @@ class EmailBase:
def send(self, strg):
"""Send `strg' to the server."""
log.debug('send: %r', strg[:300])
log.debug_no_auth('send: {}'.format(strg[:300]))
if hasattr(self, 'sock') and self.sock:
try:
if self.transferSize:
......
......@@ -46,8 +46,9 @@ from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy.orm import backref, relationship, sessionmaker, Session, scoped_session
from werkzeug.security import generate_password_hash
from . import constants
from . import constants, logger
log = logger.create()
session = None
app_DB_path = None
......@@ -695,3 +696,13 @@ def dispose():
old_session.bind.dispose()
except Exception:
pass
def session_commit(success=None):
try:
session.commit()
if success:
log.info(success)
except (exc.OperationalError, exc.InvalidRequestError) as e:
session.rollback()
log.debug_or_exception(e)
return ""
......@@ -135,10 +135,7 @@ def bookmark(book_id, book_format):
ub.Bookmark.book_id == book_id,
ub.Bookmark.format == book_format)).delete()
if not bookmark_key:
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit()
return "", 204
lbookmark = ub.Bookmark(user_id=current_user.id,
......@@ -146,10 +143,7 @@ def bookmark(book_id, book_format):
format=book_format,
bookmark_key=bookmark_key)
ub.session.merge(lbookmark)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit("Bookmark for user {} in book {} created".format(current_user.id, book_id))
return "", 201
......@@ -174,10 +168,7 @@ def toggle_read(book_id):
kobo_reading_state.statistics = ub.KoboStatistics()
book.kobo_reading_state = kobo_reading_state
ub.session.merge(book)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit("Book {} readbit toggled".format(book_id))
else:
try:
calibre_db.update_title_sort(config)
......@@ -211,10 +202,7 @@ def toggle_archived(book_id):
archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id)
archived_book.is_archived = True
ub.session.merge(archived_book)
try:
ub.session.commit()
except OperationalError:
ub.session.rollback()
ub.session_commit("Book {} archivebit toggled".format(book_id))
return ""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment