Update optional-requirements

Bugfix with serializing tasks
Bugfix order of tasks (id was used instead of task_id)
Code cosmetics
This commit is contained in:
Ozzie Isaacs 2022-04-23 19:56:02 +02:00
parent 2f5b9e41ac
commit 069dc2766f
8 changed files with 79 additions and 70 deletions

View File

@ -241,7 +241,7 @@ def delete_book_ajax(book_id, book_format):
def delete_whole_book(book_id, book): def delete_whole_book(book_id, book):
# delete book from Shelfs, Downloads, Read list # delete book from shelves, Downloads, Read list
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete() 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.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete()
ub.delete_download(book_id) ub.delete_download(book_id)
@ -383,7 +383,7 @@ def render_edit_book(book_id):
for authr in book.authors: for authr in book.authors:
author_names.append(authr.name.replace('|', ',')) author_names.append(authr.name.replace('|', ','))
# Option for showing convertbook button # Option for showing convert_book button
valid_source_formats = list() valid_source_formats = list()
allowed_conversion_formats = list() allowed_conversion_formats = list()
kepub_possible = None kepub_possible = None
@ -413,11 +413,11 @@ def render_edit_book(book_id):
def edit_book_ratings(to_save, book): def edit_book_ratings(to_save, book):
changed = False changed = False
if to_save.get("rating","").strip(): if to_save.get("rating", "").strip():
old_rating = False old_rating = False
if len(book.ratings) > 0: if len(book.ratings) > 0:
old_rating = book.ratings[0].rating old_rating = book.ratings[0].rating
rating_x2 = int(float(to_save.get("rating","")) * 2) rating_x2 = int(float(to_save.get("rating", "")) * 2)
if rating_x2 != old_rating: if rating_x2 != old_rating:
changed = True changed = True
is_rating = calibre_db.session.query(db.Ratings).filter(db.Ratings.rating == rating_x2).first() is_rating = calibre_db.session.query(db.Ratings).filter(db.Ratings.rating == rating_x2).first()
@ -622,8 +622,9 @@ def edit_cc_data(book_id, book, to_save, cc):
'custom') 'custom')
return changed return changed
# returns None if no file is uploaded # returns None if no file is uploaded
# returns False if an error occours, in all other cases the ebook metadata is returned # returns False if an error occurs, in all other cases the ebook metadata is returned
def upload_single_file(file_request, book, book_id): def upload_single_file(file_request, book, book_id):
# Check and handle Uploaded file # Check and handle Uploaded file
requested_file = file_request.files.get('btn-upload-format', None) requested_file = file_request.files.get('btn-upload-format', None)
@ -688,6 +689,7 @@ def upload_single_file(file_request, book, book_id):
rarExecutable=config.config_rarfile_location) rarExecutable=config.config_rarfile_location)
return None return None
def upload_cover(cover_request, book): def upload_cover(cover_request, book):
requested_file = cover_request.files.get('btn-upload-cover', None) requested_file = cover_request.files.get('btn-upload-cover', None)
if requested_file: if requested_file:
@ -698,7 +700,7 @@ def upload_cover(cover_request, book):
return False return False
ret, message = helper.save_cover(requested_file, book.path) ret, message = helper.save_cover(requested_file, book.path)
if ret is True: if ret is True:
helper.clear_cover_thumbnail_cache(book.id) helper.replace_cover_thumbnail_cache(book.id)
return True return True
else: else:
flash(message, category="error") flash(message, category="error")
@ -739,6 +741,7 @@ def handle_author_on_edit(book, author_name, update_stored=True):
change = True change = True
return input_authors, change, renamed return input_authors, change, renamed
@EditBook.route("/admin/book/<int:book_id>", methods=['GET']) @EditBook.route("/admin/book/<int:book_id>", methods=['GET'])
@login_required_if_no_ano @login_required_if_no_ano
@edit_required @edit_required
@ -815,6 +818,7 @@ def edit_book(book_id):
if result is True: if result is True:
book.has_cover = 1 book.has_cover = 1
modify_date = True modify_date = True
helper.replace_cover_thumbnail_cache(book.id)
else: else:
flash(error, category="error") flash(error, category="error")
@ -986,7 +990,7 @@ def create_book_on_upload(modify_date, meta):
try: try:
pubdate = datetime.strptime(meta.pubdate[:10], "%Y-%m-%d") pubdate = datetime.strptime(meta.pubdate[:10], "%Y-%m-%d")
except: except ValueError:
pubdate = datetime(101, 1, 1) pubdate = datetime(101, 1, 1)
# Calibre adds books with utc as timezone # Calibre adds books with utc as timezone
@ -1063,18 +1067,18 @@ def file_handling_on_upload(requested_file):
def move_coverfile(meta, db_book): def move_coverfile(meta, db_book):
# move cover to final directory, including book id # move cover to final directory, including book id
if meta.cover: if meta.cover:
coverfile = meta.cover cover_file = meta.cover
else: else:
coverfile = os.path.join(constants.STATIC_DIR, 'generic_cover.jpg') cover_file = os.path.join(constants.STATIC_DIR, 'generic_cover.jpg')
new_coverpath = os.path.join(config.config_calibre_dir, db_book.path) new_cover_path = os.path.join(config.config_calibre_dir, db_book.path)
try: try:
os.makedirs(new_coverpath, exist_ok=True) os.makedirs(new_cover_path, exist_ok=True)
copyfile(coverfile, os.path.join(new_coverpath, "cover.jpg")) copyfile(cover_file, os.path.join(new_cover_path, "cover.jpg"))
if meta.cover: if meta.cover:
os.unlink(meta.cover) os.unlink(meta.cover)
except OSError as e: except OSError as e:
log.error("Failed to move cover file %s: %s", new_coverpath, e) log.error("Failed to move cover file %s: %s", new_cover_path, e)
flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_coverpath, flash(_(u"Failed to Move Cover File %(file)s: %(error)s", file=new_cover_path,
error=e), error=e),
category="error") category="error")
@ -1193,7 +1197,7 @@ def edit_list_book(param):
vals = request.form.to_dict() vals = request.form.to_dict()
book = calibre_db.get_book(vals['pk']) book = calibre_db.get_book(vals['pk'])
sort_param = "" sort_param = ""
# ret = "" ret = ""
try: try:
if param == 'series_index': if param == 'series_index':
edit_book_series_index(vals['value'], book) edit_book_series_index(vals['value'], book)

View File

@ -820,9 +820,6 @@ def save_cover_from_url(url, book_path):
log.error("python modul advocate is not installed but is needed") log.error("python modul advocate is not installed but is needed")
return False, _("Python modul 'advocate' is not installed but is needed for cover downloads") return False, _("Python modul 'advocate' is not installed but is needed for cover downloads")
img.raise_for_status() img.raise_for_status()
# # cover_processing()
# move_coverfile(meta, db_book)
return save_cover(img, book_path) return save_cover(img, book_path)
except (socket.gaierror, except (socket.gaierror,
requests.exceptions.HTTPError, requests.exceptions.HTTPError,
@ -1020,7 +1017,7 @@ def render_task_status(tasklist):
ret['user'] = escape(user) # prevent xss ret['user'] = escape(user) # prevent xss
# Hidden fields # Hidden fields
ret['id'] = task.id ret['task_id'] = task.id
ret['stat'] = task.stat ret['stat'] = task.stat
ret['is_cancellable'] = task.is_cancellable ret['is_cancellable'] = task.is_cancellable
@ -1078,8 +1075,12 @@ def get_download_link(book_id, book_format, client):
def clear_cover_thumbnail_cache(book_id): def clear_cover_thumbnail_cache(book_id):
WorkerThread.add(None, TaskClearCoverThumbnailCache(book_id, _("Replace Thumbnail for book {}".format(book_id))), WorkerThread.add(None, TaskClearCoverThumbnailCache(book_id), hidden=True)
hidden=True)
def replace_cover_thumbnail_cache(book_id):
WorkerThread.add(None, TaskClearCoverThumbnailCache(book_id), hidden=True)
WorkerThread.add(None, TaskGenerateCoverThumbnails(book_id), hidden=True)
def delete_thumbnail_cache(): def delete_thumbnail_cache():

View File

@ -186,8 +186,7 @@ def HandleSyncRequest():
.join(ub.Shelf) .join(ub.Shelf)
.filter(ub.Shelf.user_id == current_user.id) .filter(ub.Shelf.user_id == current_user.id)
.filter(ub.Shelf.kobo_sync) .filter(ub.Shelf.kobo_sync)
.distinct() .distinct())
)
else: else:
if sqlalchemy_version2: if sqlalchemy_version2:
changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.ArchivedBook.is_archived) changed_entries = select(db.Books, ub.ArchivedBook.last_modified, ub.ArchivedBook.is_archived)
@ -203,9 +202,7 @@ def HandleSyncRequest():
.filter(calibre_db.common_filters(allow_show_archived=True)) .filter(calibre_db.common_filters(allow_show_archived=True))
.filter(db.Data.format.in_(KOBO_FORMATS)) .filter(db.Data.format.in_(KOBO_FORMATS))
.order_by(db.Books.last_modified) .order_by(db.Books.last_modified)
.order_by(db.Books.id) .order_by(db.Books.id))
)
reading_states_in_new_entitlements = [] reading_states_in_new_entitlements = []
if sqlalchemy_version2: if sqlalchemy_version2:
@ -215,7 +212,7 @@ def HandleSyncRequest():
log.debug("Books to Sync: {}".format(len(books.all()))) log.debug("Books to Sync: {}".format(len(books.all())))
for book in books: for book in books:
formats = [data.format for data in book.Books.data] formats = [data.format for data in book.Books.data]
if not 'KEPUB' in formats and config.config_kepubifypath and 'EPUB' in formats: if 'KEPUB' not in formats and config.config_kepubifypath and 'EPUB' in formats:
helper.convert_book_format(book.Books.id, config.config_calibre_dir, 'EPUB', 'KEPUB', current_user.name) helper.convert_book_format(book.Books.id, config.config_calibre_dir, 'EPUB', 'KEPUB', current_user.name)
kobo_reading_state = get_or_create_reading_state(book.Books.id) kobo_reading_state = get_or_create_reading_state(book.Books.id)
@ -262,7 +259,7 @@ def HandleSyncRequest():
.columns(db.Books).first() .columns(db.Books).first()
else: else:
max_change = changed_entries.from_self().filter(ub.ArchivedBook.is_archived)\ max_change = changed_entries.from_self().filter(ub.ArchivedBook.is_archived)\
.filter(ub.ArchivedBook.user_id==current_user.id) \ .filter(ub.ArchivedBook.user_id == current_user.id) \
.order_by(func.datetime(ub.ArchivedBook.last_modified).desc()).first() .order_by(func.datetime(ub.ArchivedBook.last_modified).desc()).first()
max_change = max_change.last_modified if max_change else new_archived_last_modified max_change = max_change.last_modified if max_change else new_archived_last_modified
@ -425,9 +422,9 @@ def get_author(book):
author_list = [] author_list = []
autor_roles = [] autor_roles = []
for author in book.authors: for author in book.authors:
autor_roles.append({"Name":author.name}) #.encode('unicode-escape').decode('latin-1') autor_roles.append({"Name": author.name})
author_list.append(author.name) author_list.append(author.name)
return {"ContributorRoles": autor_roles, "Contributors":author_list} return {"ContributorRoles": autor_roles, "Contributors": author_list}
def get_publisher(book): def get_publisher(book):
@ -441,6 +438,7 @@ def get_series(book):
return None return None
return book.series[0].name return book.series[0].name
def get_seriesindex(book): def get_seriesindex(book):
return book.series_index or 1 return book.series_index or 1
@ -485,7 +483,7 @@ def get_metadata(book):
"Language": "en", "Language": "en",
"PhoneticPronunciations": {}, "PhoneticPronunciations": {},
"PublicationDate": convert_to_kobo_timestamp_string(book.pubdate), "PublicationDate": convert_to_kobo_timestamp_string(book.pubdate),
"Publisher": {"Imprint": "", "Name": get_publisher(book),}, "Publisher": {"Imprint": "", "Name": get_publisher(book), },
"RevisionId": book_uuid, "RevisionId": book_uuid,
"Title": book.title, "Title": book.title,
"WorkId": book_uuid, "WorkId": book_uuid,
@ -504,6 +502,7 @@ def get_metadata(book):
return metadata return metadata
@csrf.exempt @csrf.exempt
@kobo.route("/v1/library/tags", methods=["POST", "DELETE"]) @kobo.route("/v1/library/tags", methods=["POST", "DELETE"])
@requires_kobo_auth @requires_kobo_auth
@ -718,7 +717,6 @@ def sync_shelves(sync_token, sync_results, only_kobo_shelves=False):
*extra_filters *extra_filters
).distinct().order_by(func.datetime(ub.Shelf.last_modified).asc()) ).distinct().order_by(func.datetime(ub.Shelf.last_modified).asc())
for shelf in shelflist: for shelf in shelflist:
if not shelf_lib.check_shelf_view_permissions(shelf): if not shelf_lib.check_shelf_view_permissions(shelf):
continue continue
@ -764,6 +762,7 @@ def create_kobo_tag(shelf):
) )
return {"Tag": tag} return {"Tag": tag}
@csrf.exempt @csrf.exempt
@kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"]) @kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"])
@requires_kobo_auth @requires_kobo_auth
@ -912,6 +911,7 @@ def get_current_bookmark_response(current_bookmark):
} }
return resp return resp
@kobo.route("/<book_uuid>/<width>/<height>/<isGreyscale>/image.jpg", defaults={'Quality': ""}) @kobo.route("/<book_uuid>/<width>/<height>/<isGreyscale>/image.jpg", defaults={'Quality': ""})
@kobo.route("/<book_uuid>/<width>/<height>/<Quality>/<isGreyscale>/image.jpg") @kobo.route("/<book_uuid>/<width>/<height>/<Quality>/<isGreyscale>/image.jpg")
@requires_kobo_auth @requires_kobo_auth
@ -989,8 +989,8 @@ def handle_getests():
if config.config_kobo_proxy: if config.config_kobo_proxy:
return redirect_or_proxy_request() return redirect_or_proxy_request()
else: else:
testkey = request.headers.get("X-Kobo-userkey","") testkey = request.headers.get("X-Kobo-userkey", "")
return make_response(jsonify({"Result": "Success", "TestKey":testkey, "Tests": {}})) return make_response(jsonify({"Result": "Success", "TestKey": testkey, "Tests": {}}))
@csrf.exempt @csrf.exempt
@ -1158,14 +1158,16 @@ def NATIVE_KOBO_RESOURCES():
"eula_page": "https://www.kobo.com/termsofuse?style=onestore", "eula_page": "https://www.kobo.com/termsofuse?style=onestore",
"exchange_auth": "https://storeapi.kobo.com/v1/auth/exchange", "exchange_auth": "https://storeapi.kobo.com/v1/auth/exchange",
"external_book": "https://storeapi.kobo.com/v1/products/books/external/{Ids}", "external_book": "https://storeapi.kobo.com/v1/products/books/external/{Ids}",
"facebook_sso_page": "https://authorize.kobo.com/signin/provider/Facebook/login?returnUrl=http://store.kobobooks.com/", "facebook_sso_page":
"https://authorize.kobo.com/signin/provider/Facebook/login?returnUrl=http://store.kobobooks.com/",
"featured_list": "https://storeapi.kobo.com/v1/products/featured/{FeaturedListId}", "featured_list": "https://storeapi.kobo.com/v1/products/featured/{FeaturedListId}",
"featured_lists": "https://storeapi.kobo.com/v1/products/featured", "featured_lists": "https://storeapi.kobo.com/v1/products/featured",
"free_books_page": { "free_books_page": {
"EN": "https://www.kobo.com/{region}/{language}/p/free-ebooks", "EN": "https://www.kobo.com/{region}/{language}/p/free-ebooks",
"FR": "https://www.kobo.com/{region}/{language}/p/livres-gratuits", "FR": "https://www.kobo.com/{region}/{language}/p/livres-gratuits",
"IT": "https://www.kobo.com/{region}/{language}/p/libri-gratuiti", "IT": "https://www.kobo.com/{region}/{language}/p/libri-gratuiti",
"NL": "https://www.kobo.com/{region}/{language}/List/bekijk-het-overzicht-van-gratis-ebooks/QpkkVWnUw8sxmgjSlCbJRg", "NL": "https://www.kobo.com/{region}/{language}/"
"List/bekijk-het-overzicht-van-gratis-ebooks/QpkkVWnUw8sxmgjSlCbJRg",
"PT": "https://www.kobo.com/{region}/{language}/p/livros-gratis", "PT": "https://www.kobo.com/{region}/{language}/p/livros-gratis",
}, },
"fte_feedback": "https://storeapi.kobo.com/v1/products/ftefeedback", "fte_feedback": "https://storeapi.kobo.com/v1/products/ftefeedback",
@ -1190,7 +1192,8 @@ def NATIVE_KOBO_RESOURCES():
"library_stack": "https://storeapi.kobo.com/v1/user/library/stacks/{LibraryItemId}", "library_stack": "https://storeapi.kobo.com/v1/user/library/stacks/{LibraryItemId}",
"library_sync": "https://storeapi.kobo.com/v1/library/sync", "library_sync": "https://storeapi.kobo.com/v1/library/sync",
"love_dashboard_page": "https://store.kobobooks.com/{culture}/kobosuperpoints", "love_dashboard_page": "https://store.kobobooks.com/{culture}/kobosuperpoints",
"love_points_redemption_page": "https://store.kobobooks.com/{culture}/KoboSuperPointsRedemption?productId={ProductId}", "love_points_redemption_page":
"https://store.kobobooks.com/{culture}/KoboSuperPointsRedemption?productId={ProductId}",
"magazine_landing_page": "https://store.kobobooks.com/emagazines", "magazine_landing_page": "https://store.kobobooks.com/emagazines",
"notifications_registration_issue": "https://storeapi.kobo.com/v1/notifications/registration", "notifications_registration_issue": "https://storeapi.kobo.com/v1/notifications/registration",
"oauth_host": "https://oauth.kobo.com", "oauth_host": "https://oauth.kobo.com",
@ -1206,7 +1209,8 @@ def NATIVE_KOBO_RESOURCES():
"product_recommendations": "https://storeapi.kobo.com/v1/products/{ProductId}/recommendations", "product_recommendations": "https://storeapi.kobo.com/v1/products/{ProductId}/recommendations",
"product_reviews": "https://storeapi.kobo.com/v1/products/{ProductIds}/reviews", "product_reviews": "https://storeapi.kobo.com/v1/products/{ProductIds}/reviews",
"products": "https://storeapi.kobo.com/v1/products", "products": "https://storeapi.kobo.com/v1/products",
"provider_external_sign_in_page": "https://authorize.kobo.com/ExternalSignIn/{providerName}?returnUrl=http://store.kobobooks.com/", "provider_external_sign_in_page":
"https://authorize.kobo.com/ExternalSignIn/{providerName}?returnUrl=http://store.kobobooks.com/",
"purchase_buy": "https://www.kobo.com/checkout/createpurchase/", "purchase_buy": "https://www.kobo.com/checkout/createpurchase/",
"purchase_buy_templated": "https://www.kobo.com/{culture}/checkout/createpurchase/{ProductId}", "purchase_buy_templated": "https://www.kobo.com/{culture}/checkout/createpurchase/{ProductId}",
"quickbuy_checkout": "https://storeapi.kobo.com/v1/store/quickbuy/{PurchaseId}/checkout", "quickbuy_checkout": "https://storeapi.kobo.com/v1/store/quickbuy/{PurchaseId}/checkout",

View File

@ -45,7 +45,7 @@ def get_scheduled_tasks(reconnect=True):
def end_scheduled_tasks(): def end_scheduled_tasks():
worker = WorkerThread.get_instance() worker = WorkerThread.get_instance()
for __, __, __, task in worker.tasks: for __, __, __, task, __ in worker.tasks:
if task.scheduled and task.is_cancellable: if task.scheduled and task.is_cancellable:
worker.end_task(task.id) worker.end_task(task.id)

View File

@ -156,7 +156,7 @@ class WorkerThread(threading.Thread):
def end_task(self, task_id): def end_task(self, task_id):
ins = self.get_instance() ins = self.get_instance()
for __, __, __, task in ins.tasks: for __, __, __, task, __ in ins.tasks:
if str(task.id) == str(task_id) and task.is_cancellable: if str(task.id) == str(task_id) and task.is_cancellable:
task.stat = STAT_CANCELLED if task.stat == STAT_WAITING else STAT_ENDED task.stat = STAT_CANCELLED if task.stat == STAT_WAITING else STAT_ENDED

View File

@ -641,9 +641,9 @@ function UserActions (value, row) {
/* Function for cancelling tasks */ /* Function for cancelling tasks */
function TaskActions (value, row) { function TaskActions (value, row) {
var cancellableStats = [0, 1, 2]; var cancellableStats = [0, 1, 2];
if (row.id && row.is_cancellable && cancellableStats.includes(row.stat)) { if (row.task_id && row.is_cancellable && cancellableStats.includes(row.stat)) {
return [ return [
"<div class=\"task-cancel\" data-toggle=\"modal\" data-target=\"#cancelTaskModal\" data-task-id=\"" + row.id + "\" title=\"Cancel\">", "<div class=\"task-cancel\" data-toggle=\"modal\" data-target=\"#cancelTaskModal\" data-task-id=\"" + row.task_id + "\" title=\"Cancel\">",
"<i class=\"glyphicon glyphicon-ban-circle\"></i>", "<i class=\"glyphicon glyphicon-ban-circle\"></i>",
"</div>" "</div>"
].join(""); ].join("");

View File

@ -50,7 +50,7 @@ def get_best_fit(width, height, image_width, image_height):
resize_height = int(height / 2.0) resize_height = int(height / 2.0)
aspect_ratio = image_width / image_height aspect_ratio = image_width / image_height
# If this image's aspect ratio is different than the first image, then resize this image # If this image's aspect ratio is different from the first image, then resize this image
# to fill the width and height of the first image # to fill the width and height of the first image
if aspect_ratio < width / height: if aspect_ratio < width / height:
resize_width = int(width / 2.0) resize_width = int(width / 2.0)
@ -225,7 +225,7 @@ class TaskGenerateCoverThumbnails(CalibreTask):
def __str__(self): def __str__(self):
if self.book_id > 0: if self.book_id > 0:
return "Add Thumbnail for book {}".format(self.book_id) return "Add Cover Thumbnails for Book {}".format(self.book_id)
else: else:
return "Generate Cover Thumbnails" return "Generate Cover Thumbnails"
@ -501,7 +501,7 @@ class TaskClearCoverThumbnailCache(CalibreTask):
# needed for logging # needed for logging
def __str__(self): def __str__(self):
if self.book_id > 0: if self.book_id > 0:
return "Replace Thumbnail cache for book " + str(self.book_id) return "Replace/Delete Cover Thumbnails for book " + str(self.book_id)
else: else:
return "Delete Thumbnail cache directory" return "Delete Thumbnail cache directory"

View File

@ -1,5 +1,5 @@
# GDrive Integration # GDrive Integration
google-api-python-client>=1.7.11,<2.44.0 google-api-python-client>=1.7.11,<2.46.0
gevent>20.6.0,<22.0.0 gevent>20.6.0,<22.0.0
greenlet>=0.4.17,<1.2.0 greenlet>=0.4.17,<1.2.0
httplib2>=0.9.2,<0.21.0 httplib2>=0.9.2,<0.21.0
@ -13,7 +13,7 @@ rsa>=3.4.2,<4.9.0
# Gmail # Gmail
google-auth-oauthlib>=0.4.3,<0.6.0 google-auth-oauthlib>=0.4.3,<0.6.0
google-api-python-client>=1.7.11,<2.44.0 google-api-python-client>=1.7.11,<2.46.0
# goodreads # goodreads
goodreads>=0.3.2,<0.4.0 goodreads>=0.3.2,<0.4.0