1
0
mirror of https://github.com/janeczku/calibre-web synced 2025-10-23 11:27:41 +00:00

Merge branch Develop into thumbnails

This commit is contained in:
mmonkey
2022-01-26 23:51:50 -06:00
253 changed files with 30444 additions and 32573 deletions

View File

@@ -20,7 +20,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, print_function, unicode_literals
import os
from datetime import datetime
import json
@@ -30,7 +29,6 @@ import copy
from babel.dates import format_date
from babel import Locale as LC
from babel.core import UnknownLocaleError
from flask import Blueprint, jsonify
from flask import request, redirect, send_from_directory, make_response, flash, abort, url_for
from flask import session as flask_session
@@ -48,7 +46,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
from . import calibre_db, kobo_sync_status
from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download
from .helper import check_valid_domain, render_task_status, check_email, check_username, get_cc_columns, \
get_book_cover, get_series_cover_thumbnail, get_download_link, send_mail, generate_random_password, \
@@ -56,7 +54,9 @@ from .helper import check_valid_domain, render_task_status, check_email, check_u
from .pagination import Pagination
from .redirect import redirect_back
from .usermanagement import login_required_if_no_ano
from .kobo_sync_status import remove_synced_book
from .render_template import render_title_template
from .kobo_sync_status import change_archived_books
feature_support = {
'ldap': bool(services.ldap),
@@ -84,12 +84,15 @@ except ImportError:
@app.after_request
def add_security_headers(resp):
resp.headers['Content-Security-Policy']= "default-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src * data:"
resp.headers['Content-Security-Policy'] = "default-src 'self'" + ''.join([' '+host for host in config.config_trustedhosts.strip().split(',')]) + " 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; img-src 'self' data:"
if request.endpoint == "editbook.edit_book" or config.config_use_google_drive:
resp.headers['Content-Security-Policy'] += " *"
elif request.endpoint == "web.read_book":
resp.headers['Content-Security-Policy'] += " blob:;style-src-elem 'self' blob: 'unsafe-inline';"
resp.headers['X-Content-Type-Options'] = 'nosniff'
resp.headers['X-Frame-Options'] = 'SAMEORIGIN'
resp.headers['X-XSS-Protection'] = '1; mode=block'
resp.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
# log.debug(request.full_path)
resp.headers['Strict-Transport-Security'] = 'max-age=31536000;'
return resp
web = Blueprint('web', __name__)
@@ -188,23 +191,16 @@ def toggle_read(book_id):
return "Custom Column No.{} is not existing in calibre database".format(config.config_read_column), 400
except (OperationalError, InvalidRequestError) as e:
calibre_db.session.rollback()
log.error(u"Read status could not set: %e", e)
log.error(u"Read status could not set: {}".format(e))
return "Read status could not set: {}".format(e), 400
return ""
@web.route("/ajax/togglearchived/<int:book_id>", methods=['POST'])
@login_required
def toggle_archived(book_id):
archived_book = ub.session.query(ub.ArchivedBook).filter(and_(ub.ArchivedBook.user_id == int(current_user.id),
ub.ArchivedBook.book_id == book_id)).first()
if archived_book:
archived_book.is_archived = not archived_book.is_archived
archived_book.last_modified = datetime.utcnow()
else:
archived_book = ub.ArchivedBook(user_id=current_user.id, book_id=book_id)
archived_book.is_archived = True
ub.session.merge(archived_book)
ub.session_commit("Book {} archivebit toggled".format(book_id))
is_archived = change_archived_books(book_id, message="Book {} archivebit toggled".format(book_id))
if is_archived:
remove_synced_book(book_id)
return ""
@@ -260,10 +256,7 @@ def get_comic_book(book_id, book_format, page):
log.error('unsupported comic format')
return "", 204
if sys.version_info.major >= 3:
b64 = codecs.encode(extract(page), 'base64').decode()
else:
b64 = extract(page).encode('base64')
b64 = codecs.encode(extract(page), 'base64').decode()
ext = names[page].rpartition('.')[-1]
if ext not in ('png', 'gif', 'jpg', 'jpeg', 'webp'):
ext = 'png'
@@ -273,7 +266,6 @@ def get_comic_book(book_id, book_format, page):
return "", 204
'''
# ################################### Typeahead ##################################################################
@@ -367,7 +359,13 @@ def get_sort_function(sort, data):
order = [db.Books.series_index.asc()]
if sort == 'seriesdesc':
order = [db.Books.series_index.desc()]
return order
if sort == 'hotdesc':
order = [func.count(ub.Downloads.book_id).desc()]
if sort == 'hotasc':
order = [func.count(ub.Downloads.book_id).asc()]
if sort is None:
sort = "new"
return order, sort
def render_books_list(data, sort, book_id, page):
@@ -381,7 +379,7 @@ def render_books_list(data, sort, book_id, page):
elif data == "read":
return render_read_books(page, True, order=order)
elif data == "hot":
return render_hot_books(page)
return render_hot_books(page, order)
elif data == "download":
return render_downloaded_books(page, order, book_id)
elif data == "author":
@@ -410,12 +408,13 @@ def render_books_list(data, sort, book_id, page):
return render_adv_search_results(term, offset, order, config.config_books_per_page)
else:
website = data or "newest"
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order,
entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, True, order[0],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Books"), page=website)
title=_(u"Books"), page=website, order=order[1])
def render_rated_books(page, book_id, order):
@@ -423,13 +422,14 @@ def render_rated_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.ratings.any(db.Ratings.rating > 9),
order,
order[0],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
id=book_id, title=_(u"Top Rated Books"), page="rated")
id=book_id, title=_(u"Top Rated Books"), page="rated", order=order[1])
else:
abort(404)
@@ -443,16 +443,21 @@ def render_discover_books(page, book_id):
else:
abort(404)
def render_hot_books(page):
def render_hot_books(page, order):
if current_user.check_visibility(constants.SIDEBAR_HOT):
if order[1] not in ['hotasc', 'hotdesc']:
# Unary expression comparsion only working (for this expression) in sqlalchemy 1.4+
#if not (order[0][0].compare(func.count(ub.Downloads.book_id).desc()) or
# order[0][0].compare(func.count(ub.Downloads.book_id).asc())):
order = [func.count(ub.Downloads.book_id).desc()], 'hotdesc'
if current_user.show_detail_random():
random = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \
.order_by(func.random()).limit(config.config_random_books)
else:
random = false()
off = int(int(config.config_books_per_page) * (page - 1))
all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id)).order_by(
func.count(ub.Downloads.book_id).desc()).group_by(ub.Downloads.book_id)
all_books = ub.session.query(ub.Downloads, func.count(ub.Downloads.book_id))\
.order_by(*order[0]).group_by(ub.Downloads.book_id)
hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list()
for book in hot_books:
@@ -465,7 +470,7 @@ def render_hot_books(page):
numBooks = entries.__len__()
pagination = Pagination(page, config.config_books_per_page, numBooks)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
title=_(u"Hot Books (Most Downloaded)"), page="hot")
title=_(u"Hot Books (Most Downloaded)"), page="hot", order=order[1])
else:
abort(404)
@@ -486,7 +491,11 @@ def render_downloaded_books(page, order, user_id):
0,
db.Books,
ub.Downloads.user_id == user_id,
order,
order[0],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
ub.Downloads, db.Books.id == ub.Downloads.book_id)
for book in entries:
if not calibre_db.session.query(db.Books).filter(calibre_db.common_filters()) \
@@ -499,7 +508,8 @@ def render_downloaded_books(page, order, user_id):
pagination=pagination,
id=user_id,
title=_(u"Downloaded books by %(user)s",user=user.name),
page="download")
page="download",
order=order[1])
else:
abort(404)
@@ -508,7 +518,8 @@ def render_author_books(page, author_id, order):
entries, __, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.authors.any(db.Authors.id == author_id),
[order[0], db.Series.name, db.Books.series_index],
[order[0][0], db.Series.name, db.Books.series_index],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
@@ -527,10 +538,9 @@ def render_author_books(page, author_id, order):
if services.goodreads_support and config.config_use_goodreads:
author_info = services.goodreads_support.get_author_info(author_name)
other_books = services.goodreads_support.get_other_books(author_info, entries)
return render_title_template('author.html', entries=entries, pagination=pagination, id=author_id,
title=_(u"Author: %(name)s", name=author_name), author=author_info,
other_books=other_books, page="author")
other_books=other_books, page="author", order=order[1])
def render_publisher_books(page, book_id, order):
@@ -539,12 +549,15 @@ def render_publisher_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.publishers.any(db.Publishers.id == book_id),
[db.Series.name, order[0], db.Books.series_index],
[db.Series.name, order[0][0], db.Books.series_index],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
title=_(u"Publisher: %(name)s", name=publisher.name), page="publisher")
title=_(u"Publisher: %(name)s", name=publisher.name),
page="publisher",
order=order[1])
else:
abort(404)
@@ -555,9 +568,9 @@ def render_series_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.series.any(db.Series.id == book_id),
[order[0]])
[order[0][0]])
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"Series: %(serie)s", serie=name.name), page="series")
title=_(u"Series: %(serie)s", serie=name.name), page="series", order=order[1])
else:
abort(404)
@@ -567,10 +580,12 @@ def render_ratings_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.ratings.any(db.Ratings.id == book_id),
[order[0]])
[order[0][0]])
if name and name.rating <= 10:
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)), page="ratings")
title=_(u"Rating: %(rating)s stars", rating=int(name.rating / 2)),
page="ratings",
order=order[1])
else:
abort(404)
@@ -581,9 +596,11 @@ def render_formats_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.data.any(db.Data.format == book_id.upper()),
[order[0]])
[order[0][0]])
return render_title_template('index.html', random=random, pagination=pagination, entries=entries, id=book_id,
title=_(u"File format: %(format)s", format=name.format), page="formats")
title=_(u"File format: %(format)s", format=name.format),
page="formats",
order=order[1])
else:
abort(404)
@@ -594,35 +611,33 @@ def render_category_books(page, book_id, order):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.tags.any(db.Tags.id == book_id),
[order[0], db.Series.name, db.Books.series_index],
[order[0][0], db.Series.name, db.Books.series_index],
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series)
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=book_id,
title=_(u"Category: %(name)s", name=name.name), page="category")
title=_(u"Category: %(name)s", name=name.name), page="category", order=order[1])
else:
abort(404)
def render_language_books(page, name, order):
try:
cur_l = LC.parse(name)
lang_name = cur_l.get_language_name(get_locale())
except UnknownLocaleError:
try:
lang_name = _(isoLanguages.get(part3=name).name)
except KeyError:
abort(404)
lang_name = isoLanguages.get_language_name(get_locale(), name)
except KeyError:
abort(404)
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db.Books.languages.any(db.Languages.lang_code == name),
[order[0]])
[order[0][0]])
return render_title_template('index.html', random=random, entries=entries, pagination=pagination, id=name,
title=_(u"Language: %(name)s", name=lang_name), page="language")
title=_(u"Language: %(name)s", name=lang_name), page="language", order=order[1])
def render_read_books(page, are_read, as_xml=False, order=None):
order = order or []
sort = order[0] if order else []
if not config.config_read_column:
if are_read:
db_filter = and_(ub.ReadBook.user_id == int(current_user.id),
@@ -632,7 +647,8 @@ def render_read_books(page, are_read, as_xml=False, order=None):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db_filter,
order,
sort,
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
@@ -646,7 +662,8 @@ def render_read_books(page, are_read, as_xml=False, order=None):
entries, random, pagination = calibre_db.fill_indexpage(page, 0,
db.Books,
db_filter,
order,
sort,
False, 0,
db.books_series_link,
db.Books.id == db.books_series_link.c.book,
db.Series,
@@ -669,11 +686,11 @@ def render_read_books(page, are_read, as_xml=False, order=None):
name = _(u'Unread Books') + ' (' + str(pagination.total_count) + ')'
pagename = "unread"
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
title=name, page=pagename)
title=name, page=pagename, order=order[1])
def render_archived_books(page, order):
order = order or []
def render_archived_books(page, sort):
order = sort[0] or []
archived_books = (
ub.session.query(ub.ArchivedBook)
.filter(ub.ArchivedBook.user_id == int(current_user.id))
@@ -684,16 +701,17 @@ def render_archived_books(page, order):
archived_filter = db.Books.id.in_(archived_book_ids)
entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, 0,
db.Books,
entries, random, pagination = calibre_db.fill_indexpage_with_archived_books(page, db.Books,
0,
archived_filter,
order,
allow_show_archived=True)
True,
False, 0)
name = _(u'Archived Books') + ' (' + str(len(archived_book_ids)) + ')'
pagename = "archived"
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
title=name, page=pagename)
title=name, page=pagename, order=sort[1])
def render_prepare_search_form(cc):
@@ -729,7 +747,13 @@ def render_prepare_search_form(cc):
def render_search_results(term, offset=None, order=None, limit=None):
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
entries, result_count, pagination = calibre_db.get_search_results(term, offset, order, limit, *join)
entries, result_count, pagination = calibre_db.get_search_results(term,
offset,
order,
limit,
False,
config.config_read_column,
*join)
return render_title_template('search.html',
searchterm=term,
pagination=pagination,
@@ -738,7 +762,8 @@ def render_search_results(term, offset=None, order=None, limit=None):
entries=entries,
result_count=result_count,
title=_(u"Search"),
page="search")
page="search",
order=order[1])
# ################################### View Books list ##################################################################
@@ -765,7 +790,8 @@ def books_list(data, sort_param, book_id, page):
@login_required
def books_table():
visibility = current_user.view_settings.get('table', {})
return render_title_template('book_table.html', title=_(u"Books List"), page="book_table",
cc = get_cc_columns(filter_config_custom_read=True)
return render_title_template('book_table.html', title=_(u"Books List"), cc=cc, page="book_table",
visiblility=visibility)
@@ -784,13 +810,13 @@ def list_books():
state = json.loads(request.args.get("state", "[]"))
elif sort == "tags":
order = [db.Tags.name.asc()] if order == "asc" else [db.Tags.name.desc()]
join = db.books_tags_link,db.Books.id == db.books_tags_link.c.book, db.Tags
join = db.books_tags_link, db.Books.id == db.books_tags_link.c.book, db.Tags
elif sort == "series":
order = [db.Series.name.asc()] if order == "asc" else [db.Series.name.desc()]
join = db.books_series_link,db.Books.id == db.books_series_link.c.book, db.Series
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
elif sort == "publishers":
order = [db.Publishers.name.asc()] if order == "asc" else [db.Publishers.name.desc()]
join = db.books_publishers_link,db.Books.id == db.books_publishers_link.c.book, db.Publishers
join = db.books_publishers_link, db.Books.id == db.books_publishers_link.c.book, db.Publishers
elif sort == "authors":
order = [db.Authors.name.asc(), db.Series.name, db.Books.series_index] if order == "asc" \
else [db.Authors.name.desc(), db.Series.name.desc(), db.Books.series_index.desc()]
@@ -804,29 +830,62 @@ def list_books():
elif not state:
order = [db.Books.timestamp.desc()]
total_count = filtered_count = calibre_db.session.query(db.Books).count()
if state:
total_count = filtered_count = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(allow_show_archived=True)).count()
if state is not None:
if search:
books = calibre_db.search_query(search).all()
books = calibre_db.search_query(search, config.config_read_column).all()
filtered_count = len(books)
else:
books = calibre_db.session.query(db.Books).filter(calibre_db.common_filters()).all()
entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order)
if not config.config_read_column:
books = (calibre_db.session.query(db.Books, ub.ReadBook.read_status, ub.ArchivedBook.is_archived)
.select_from(db.Books)
.outerjoin(ub.ReadBook,
and_(ub.ReadBook.user_id == int(current_user.id),
ub.ReadBook.book_id == db.Books.id)))
else:
try:
read_column = db.cc_classes[config.config_read_column]
books = (calibre_db.session.query(db.Books, read_column.value, ub.ArchivedBook.is_archived)
.select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", read_column)
# Skip linking read column and return None instead of read status
books =calibre_db.session.query(db.Books, None, ub.ArchivedBook.is_archived)
books = (books.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id))
.filter(calibre_db.common_filters(allow_show_archived=True)).all())
entries = calibre_db.get_checkbox_sorted(books, state, off, limit, order, True)
elif search:
entries, filtered_count, __ = calibre_db.get_search_results(search, off, order, limit, *join)
entries, filtered_count, __ = calibre_db.get_search_results(search,
off,
[order,''],
limit,
True,
config.config_read_column,
*join)
else:
entries, __, __ = calibre_db.fill_indexpage((int(off) / (int(limit)) + 1), limit, db.Books, True, order, *join)
entries, __, __ = calibre_db.fill_indexpage_with_archived_books((int(off) / (int(limit)) + 1),
db.Books,
limit,
True,
order,
True,
True,
config.config_read_column,
*join)
result = list()
for entry in entries:
for index in range(0, len(entry.languages)):
try:
entry.languages[index].language_name = LC.parse(entry.languages[index].lang_code)\
.get_language_name(get_locale())
except UnknownLocaleError:
entry.languages[index].language_name = _(
isoLanguages.get(part3=entry.languages[index].lang_code).name)
table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": entries}
val = entry[0]
val.read_status = entry[1] == ub.ReadBook.STATUS_FINISHED
val.is_archived = entry[2] is True
for index in range(0, len(val.languages)):
val.languages[index].language_name = isoLanguages.get_language_name(get_locale(), val.languages[
index].lang_code)
result.append(val)
table_entries = {'totalNotFiltered': total_count, 'total': filtered_count, "rows": result}
js_list = json.dumps(table_entries, cls=db.AlchemyEncoder)
response = make_response(js_list)
@@ -837,8 +896,6 @@ def list_books():
@web.route("/ajax/table_settings", methods=['POST'])
@login_required
def update_table_settings():
# vals = request.get_json()
# ToDo: Save table settings
current_user.view_settings['table'] = json.loads(request.data)
try:
try:
@@ -940,10 +997,11 @@ def series_list():
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
.group_by(func.upper(func.substr(db.Series.sort, 1, 1))).all()
return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=charlist,
title=_(u"Series"), page="serieslist", data="series")
title=_(u"Series"), page="serieslist", data="series", order=order_no)
else:
entries = calibre_db.session.query(db.Books, func.count('books_series_link').label('count')) \
.join(db.books_series_link).join(db.Series).filter(calibre_db.common_filters()) \
entries = calibre_db.session.query(db.Books, func.count('books_series_link').label('count'),
func.max(db.Books.series_index), db.Books.id) \
.join(db.books_series_link).join(db.Series).filter(calibre_db.common_filters())\
.group_by(text('books_series_link.series')).order_by(order).all()
charlist = calibre_db.session.query(func.upper(func.substr(db.Series.sort, 1, 1)).label('char')) \
.join(db.books_series_link).join(db.Books).filter(calibre_db.common_filters()) \
@@ -1000,28 +1058,17 @@ def formats_list():
@web.route("/language")
@login_required_if_no_ano
def language_overview():
if current_user.check_visibility(constants.SIDEBAR_LANGUAGE):
if current_user.check_visibility(constants.SIDEBAR_LANGUAGE) and current_user.filter_language() == u"all":
order_no = 0 if current_user.get_view_property('language', 'dir') == 'desc' else 1
charlist = list()
if current_user.filter_language() == u"all":
languages = calibre_db.speaking_language()
# ToDo: generate first character list for languages
else:
try:
cur_l = LC.parse(current_user.filter_language())
except UnknownLocaleError:
cur_l = None
languages = calibre_db.session.query(db.Languages).filter(
db.Languages.lang_code == current_user.filter_language()).all()
if cur_l:
languages[0].name = cur_l.get_language_name(get_locale())
else:
languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name)
lang_counter = calibre_db.session.query(db.books_languages_link,
func.count('books_languages_link.book').label('bookcount')).group_by(
text('books_languages_link.lang_code')).all()
return render_title_template('languages.html', languages=languages, lang_counter=lang_counter,
languages = calibre_db.speaking_language(reverse_order=not order_no, with_count=True)
for lang in languages:
upper_lang = lang[0].name[0].upper()
if upper_lang not in charlist:
charlist.append(upper_lang)
return render_title_template('languages.html', languages=languages,
charlist=charlist, title=_(u"Languages"), page="langlist",
data="language")
data="language", order=order_no)
else:
abort(404)
@@ -1060,7 +1107,8 @@ def get_tasks_status():
return render_title_template('tasks.html', entries=answer, title=_(u"Tasks"), page="tasks")
@app.route("/reconnect")
# method is available without login and not protected by CSRF to make it easy reachable
@app.route("/reconnect", methods=['GET'])
def reconnect():
calibre_db.reconnect_db(config, ub.app_DB_path)
return json.dumps({})
@@ -1073,7 +1121,7 @@ def reconnect():
def search():
term = request.args.get("query")
if term:
return redirect(url_for('web.books_list', data="search", sort_param='stored', query=term))
return redirect(url_for('web.books_list', data="search", sort_param='stored', query=term.strip()))
else:
return render_title_template('search.html',
searchterm="",
@@ -1227,7 +1275,7 @@ def extend_search_term(searchterm,
format_date(datetime.strptime(pub_end, "%Y-%m-%d"),
format='medium', locale=get_locale())])
except ValueError:
pub_start = u""
pub_end = u""
elements = {'tag': db.Tags, 'serie':db.Series, 'shelf':ub.Shelf}
for key, db_element in elements.items():
tag_names = calibre_db.session.query(db_element).filter(db_element.id.in_(tags['include_' + key])).all()
@@ -1258,12 +1306,29 @@ def extend_search_term(searchterm,
def render_adv_search_results(term, offset=None, order=None, limit=None):
order = order or [db.Books.sort]
sort = order[0] if order else [db.Books.sort]
pagination = None
cc = get_cc_columns(filter_config_custom_read=True)
calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase)
q = calibre_db.session.query(db.Books).outerjoin(db.books_series_link, db.Books.id == db.books_series_link.c.book)\
if not config.config_read_column:
query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, ub.ReadBook).select_from(db.Books)
.outerjoin(ub.ReadBook, and_(db.Books.id == ub.ReadBook.book_id,
int(current_user.id) == ub.ReadBook.user_id)))
else:
try:
read_column = cc[config.config_read_column]
query = (calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, read_column.value)
.select_from(db.Books)
.outerjoin(read_column, read_column.book == db.Books.id))
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
# Skip linking read column
query = calibre_db.session.query(db.Books, ub.ArchivedBook.is_archived, None)
query = query.outerjoin(ub.ArchivedBook, and_(db.Books.id == ub.ArchivedBook.book_id,
int(current_user.id) == ub.ArchivedBook.user_id))
q = query.outerjoin(db.books_series_link, db.Books.id == db.books_series_link.c.book)\
.outerjoin(db.Series)\
.filter(calibre_db.common_filters(True))
@@ -1298,7 +1363,7 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
column_end = term.get('custom_column_' + str(c.id) + '_end')
if column_start:
searchterm.extend([u"{} >= {}".format(c.name,
format_date(datetime.strptime(column_start, "%Y-%m-%d"),
format_date(datetime.strptime(column_start, "%Y-%m-%d").date(),
format='medium',
locale=get_locale())
)])
@@ -1327,7 +1392,7 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
rating_high,
rating_low,
read_status)
q = q.filter()
# q = q.filter()
if author_name:
q = q.filter(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + author_name + "%")))
if book_title:
@@ -1356,24 +1421,26 @@ def render_adv_search_results(term, offset=None, order=None, limit=None):
log.debug_or_exception(ex)
flash(_("Error on search for custom columns, please restart Calibre-Web"), category="error")
q = q.order_by(*order).all()
q = q.order_by(*sort).all()
flask_session['query'] = json.dumps(term)
ub.store_ids(q)
ub.store_combo_ids(q)
result_count = len(q)
if offset != None and limit != None:
if offset is not None and limit is not None:
offset = int(offset)
limit_all = offset + int(limit)
pagination = Pagination((offset / (int(limit)) + 1), limit, result_count)
else:
offset = 0
limit_all = result_count
entries = calibre_db.order_authors(q[offset:limit_all], list_return=True, combined=True)
return render_title_template('search.html',
adv_searchterm=searchterm,
pagination=pagination,
entries=q[offset:limit_all],
entries=entries,
result_count=result_count,
title=_(u"Advanced Search"),
page="advsearch")
page="advsearch",
order=order[1])
@web.route("/advsearch", methods=['GET'])
@@ -1447,7 +1514,7 @@ def serve_book(book_id, book_format, anyname):
"rb").read()
result = chardet.detect(rawdata)
return make_response(
rawdata.decode(result['encoding']).encode('utf-8'))
rawdata.decode(result['encoding'], 'surrogatepass').encode('utf-8', 'surrogatepass'))
except FileNotFoundError:
log.error("File Not Found")
return "File Not Found"
@@ -1463,7 +1530,7 @@ def download_link(book_id, book_format, anyname):
return get_download_link(book_id, book_format, client)
@web.route('/send/<int:book_id>/<book_format>/<int:convert>')
@web.route('/send/<int:book_id>/<book_format>/<int:convert>', methods=["POST"])
@login_required
@download_required
def send_to_kindle(book_id, book_format, convert):
@@ -1544,9 +1611,6 @@ def register():
@web.route('/login', methods=['GET', 'POST'])
def login():
#if not config.db_configured:
# log.debug(u"Redirect to initial configuration")
# return redirect(url_for('admin.basic_configuration'))
if current_user is not None and current_user.is_authenticated:
return redirect(url_for('web.index'))
if config.config_login_type == constants.LOGIN_LDAP and not services.ldap:
@@ -1584,7 +1648,7 @@ def login():
else:
ip_Address = request.headers.get('X-Forwarded-For', request.remote_addr)
if 'forgot' in form and form['forgot'] == 'forgot':
if user != None and user.name != "Guest":
if user is not None and user.name != "Guest":
ret, __ = reset_password(user.id)
if ret == 1:
flash(_(u"New Password was send to your email address"), category="info")
@@ -1651,7 +1715,13 @@ def change_profile(kobo_support, local_oauth_check, oauth_status, translations,
current_user.default_language = to_save["default_language"]
if to_save.get("locale"):
current_user.locale = to_save["locale"]
old_state = current_user.kobo_only_shelves_sync
# 1 -> 0: nothing has to be done
# 0 -> 1: all synced books have to be added to archived books, + currently synced shelfs which
# don't have to be synced have to be removed (added to Shelf archive)
current_user.kobo_only_shelves_sync = int(to_save.get("kobo_only_shelves_sync") == "on") or 0
if old_state == 0 and current_user.kobo_only_shelves_sync == 1:
kobo_sync_status.update_on_sync_shelfs(current_user.id)
except Exception as ex:
flash(str(ex), category="error")
@@ -1772,67 +1842,40 @@ def read_book(book_id, book_format):
@web.route("/book/<int:book_id>")
@login_required_if_no_ano
def show_book(book_id):
entries = calibre_db.get_filtered_book(book_id, allow_show_archived=True)
entries = calibre_db.get_book_read_archived(book_id, config.config_read_column, allow_show_archived=True)
if entries:
for index in range(0, len(entries.languages)):
try:
entries.languages[index].language_name = LC.parse(entries.languages[index].lang_code)\
.get_language_name(get_locale())
except UnknownLocaleError:
entries.languages[index].language_name = _(
isoLanguages.get(part3=entries.languages[index].lang_code).name)
read_book = entries[1]
archived_book = entries[2]
entry = entries[0]
entry.read_status = read_book == ub.ReadBook.STATUS_FINISHED
entry.is_archived = archived_book
for index in range(0, len(entry.languages)):
entry.languages[index].language_name = isoLanguages.get_language_name(get_locale(), entry.languages[
index].lang_code)
cc = get_cc_columns(filter_config_custom_read=True)
book_in_shelfs = []
shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).all()
for entry in shelfs:
book_in_shelfs.append(entry.shelf)
for sh in shelfs:
book_in_shelfs.append(sh.shelf)
if not current_user.is_anonymous:
if not config.config_read_column:
matching_have_read_book = ub.session.query(ub.ReadBook). \
filter(and_(ub.ReadBook.user_id == int(current_user.id), ub.ReadBook.book_id == book_id)).all()
have_read = len(
matching_have_read_book) > 0 and matching_have_read_book[0].read_status == ub.ReadBook.STATUS_FINISHED
else:
try:
matching_have_read_book = getattr(entries, 'custom_column_' + str(config.config_read_column))
have_read = len(matching_have_read_book) > 0 and matching_have_read_book[0].value
except (KeyError, AttributeError):
log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column)
have_read = None
entry.tags = sort(entry.tags, key=lambda tag: tag.name)
archived_book = ub.session.query(ub.ArchivedBook).\
filter(and_(ub.ArchivedBook.user_id == int(current_user.id),
ub.ArchivedBook.book_id == book_id)).first()
is_archived = archived_book and archived_book.is_archived
entry.authors = calibre_db.order_authors([entry])
else:
have_read = None
is_archived = None
entry.kindle_list = check_send_to_kindle(entry)
entry.reader_list = check_read_formats(entry)
entries.tags = sort(entries.tags, key=lambda tag: tag.name)
entries = calibre_db.order_authors(entries)
kindle_list = check_send_to_kindle(entries)
reader_list = check_read_formats(entries)
audioentries = []
for media_format in entries.data:
entry.audioentries = []
for media_format in entry.data:
if media_format.format.lower() in constants.EXTENSIONS_AUDIO:
audioentries.append(media_format.format.lower())
entry.audioentries.append(media_format.format.lower())
return render_title_template('detail.html',
entry=entries,
audioentries=audioentries,
entry=entry,
cc=cc,
is_xhr=request.headers.get('X-Requested-With')=='XMLHttpRequest',
title=entries.title,
title=entry.title,
books_shelfs=book_in_shelfs,
have_read=have_read,
is_archived=is_archived,
kindle_list=kindle_list,
reader_list=reader_list,
page="book")
else:
log.debug(u"Oops! Selected book title is unavailable. File does not exist or is not accessible")