diff --git a/cps/kobo.py b/cps/kobo.py index ee2317ba..3d2af7f4 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -22,6 +22,7 @@ import datetime import os import uuid from time import gmtime, strftime +import json try: from urllib import unquote @@ -102,6 +103,8 @@ def make_request_to_kobo_store(sync_token=None): allow_redirects=False, timeout=(2, 10) ) + log.debug("Content: " + str(store_response.content)) + log.debug("StatusCode: " + str(store_response.status_code)) return store_response @@ -110,7 +113,8 @@ def redirect_or_proxy_request(): if request.method == "GET": return redirect(get_store_url_for_current_request(), 307) else: - # The Kobo device turns other request types into GET requests on redirects, so we instead proxy to the Kobo store ourselves. + # The Kobo device turns other request types into GET requests on redirects, + # so we instead proxy to the Kobo store ourselves. store_response = make_request_to_kobo_store() response_headers = store_response.headers @@ -205,8 +209,8 @@ def HandleSyncRequest(): books = calibre_db.session.execute(changed_entries.limit(SYNC_ITEM_LIMIT)) else: books = changed_entries.limit(SYNC_ITEM_LIMIT) + log.debug("Books to Sync: {}".format(books.count())) for book in books: - kobo_sync_status.add_synced_books(book.Books.id) formats = [data.format for data in book.Books.data] if not 'KEPUB' 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) @@ -245,6 +249,7 @@ def HandleSyncRequest(): pass new_books_last_created = max(ts_created, new_books_last_created) + kobo_sync_status.add_synced_books(book.Books.id) if sqlalchemy_version2: max_change = calibre_db.session.execute(changed_entries @@ -330,9 +335,10 @@ def generate_sync_response(sync_token, sync_results, set_cont=False): extra_headers["x-kobo-sync"] = "continue" sync_token.to_headers(extra_headers) - # log.debug("Kobo Sync Content: {}".format(sync_results)) - response = make_response(jsonify(sync_results), extra_headers) - + log.debug("Kobo Sync Content: {}".format(sync_results)) + # jsonify decodes the unicode string different to what kobo expects + response = make_response(json.dumps(sync_results), extra_headers) + response.headers["Content-Type"] = "application/json; charset=utf-8" return response @@ -377,7 +383,7 @@ def get_download_url_for_book(book, book_format): def create_book_entitlement(book, archived): - book_uuid = book.uuid + book_uuid = str(book.uuid) return { "Accessibility": "Full", "ActivePeriod": {"From": convert_to_kobo_timestamp_string(datetime.datetime.now())}, @@ -404,7 +410,6 @@ def get_description(book): return book.comments[0].text -# TODO handle multiple authors def get_author(book): if not book.authors: return {"Contributors": None} @@ -412,10 +417,11 @@ def get_author(book): author_list = [] autor_roles = [] for author in book.authors: - autor_roles.append({"Name":author.name, "Role":"Author"}) + autor_roles.append({"Name":author.name}) #.encode('unicode-escape').decode('latin-1') author_list.append(author.name) return {"ContributorRoles": autor_roles, "Contributors":author_list} - return {"ContributorRoles": [{"Name":book.authors[0].name, "Role":"Author"}], "Contributors": book.authors[0].name} + return {"ContributorRoles": [{"Name":book.authors[0].name}], + "Contributors": book.authors[0].name} def get_publisher(book): @@ -472,9 +478,7 @@ def get_metadata(book): "IsSocialEnabled": True, "Language": "en", "PhoneticPronunciations": {}, - # TODO: Fix book.pubdate to return a datetime object so that we can easily - # convert it to the format Kobo devices expect. - "PublicationDate": book.pubdate, + "PublicationDate": convert_to_kobo_timestamp_string(book.pubdate), "Publisher": {"Imprint": "", "Name": get_publisher(book),}, "RevisionId": book_uuid, "Title": book.title, @@ -489,7 +493,7 @@ def get_metadata(book): "Number": get_seriesindex(book), # ToDo Check int() ? "NumberFloat": float(get_seriesindex(book)), # Get a deterministic id based on the series name. - "Id": uuid.uuid3(uuid.NAMESPACE_DNS, name), + "Id": str(uuid.uuid3(uuid.NAMESPACE_DNS, name)), } return metadata @@ -958,6 +962,8 @@ def HandleBookDeletionRequest(book_uuid): ub.session.merge(archived_book) ub.session_commit() + if archived_book.is_archived: + kobo_sync_status.remove_synced_book(book_id) return "", 204 @@ -986,11 +992,16 @@ def HandleUserRequest(dummy=None): @kobo.route("/v1/products//recommendations", methods=["GET", "POST"]) @kobo.route("/v1/products//nextread", methods=["GET", "POST"]) @kobo.route("/v1/products//reviews", methods=["GET", "POST"]) +@kobo.route("/v1/products/featured/", methods=["GET", "POST"]) +@kobo.route("/v1/products/featured/", methods=["GET", "POST"]) @kobo.route("/v1/products/books/external/", methods=["GET", "POST"]) @kobo.route("/v1/products/books/series/", methods=["GET", "POST"]) @kobo.route("/v1/products/books/", methods=["GET", "POST"]) +@kobo.route("/v1/products/books//", methods=["GET", "POST"]) @kobo.route("/v1/products/dailydeal", methods=["GET", "POST"]) +@kobo.route("/v1/products/deals", methods=["GET", "POST"]) @kobo.route("/v1/products", methods=["GET", "POST"]) +@kobo.route("/v1/affiliate", methods=["GET", "POST"]) def HandleProductsRequest(dummy=None): log.debug("Unimplemented Products Request received: %s", request.base_url) return redirect_or_proxy_request() diff --git a/cps/web.py b/cps/web.py index c061af30..82f86488 100644 --- a/cps/web.py +++ b/cps/web.py @@ -54,6 +54,7 @@ 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 feature_support = { @@ -206,6 +207,8 @@ def toggle_archived(book_id): archived_book.is_archived = True ub.session.merge(archived_book) ub.session_commit("Book {} archivebit toggled".format(book_id)) + if archived_book.is_archived: + remove_synced_book(book_id) return ""