From 5f0c7737fe820d9d2d8efbc5d48833af5ca89300 Mon Sep 17 00:00:00 2001 From: Thomas de Ruiter Date: Tue, 28 Mar 2023 15:56:02 +0200 Subject: [PATCH 01/18] Fix proxy cover images to Kobo store --- cps/helper.py | 5 +++-- cps/kobo.py | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cps/helper.py b/cps/helper.py index 9871af0c..dce1a280 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -747,9 +747,10 @@ def get_book_cover(book_id, resolution=None): return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution) -# Called only by kobo sync -> cover not found should be answered with 404 and not with default cover -def get_book_cover_with_uuid(book_uuid, resolution=None): +def get_book_cover_with_uuid(book_uuid, resolution=None, none_on_missing=False): book = calibre_db.get_book_by_uuid(book_uuid) + if not book and none_on_missing: + return # allows kobo.HandleCoverImageRequest to proxy request return get_book_cover_internal(book, use_generic_cover_on_failure=False, resolution=resolution) diff --git a/cps/kobo.py b/cps/kobo.py index de5d3235..8afadafd 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -930,7 +930,10 @@ def get_current_bookmark_response(current_bookmark): @kobo.route("//////image.jpg") @requires_kobo_auth def HandleCoverImageRequest(book_uuid, width, height, Quality, isGreyscale): - book_cover = helper.get_book_cover_with_uuid(book_uuid, resolution=COVER_THUMBNAIL_SMALL) + book_cover = helper.get_book_cover_with_uuid( + book_uuid, + resolution=COVER_THUMBNAIL_SMALL, + none_on_missing=config.config_kobo_proxy) if not book_cover: if config.config_kobo_proxy: log.debug("Cover for unknown book: %s proxied to kobo" % book_uuid) @@ -941,7 +944,7 @@ def HandleCoverImageRequest(book_uuid, width, height, Quality, isGreyscale): else: log.debug("Cover for unknown book: %s requested" % book_uuid) # additional proxy request make no sense, -> direct return - return make_response(jsonify({})) + return abort(404) log.debug("Cover request received for book %s" % book_uuid) return book_cover From a952c36ab75e1c09af6a321d4ed15f5d7eeaeb3e Mon Sep 17 00:00:00 2001 From: Thomas de Ruiter Date: Tue, 28 Mar 2023 16:13:10 +0200 Subject: [PATCH 02/18] Remove unused fallback cover handling --- cps/helper.py | 36 +++++++++++++++++------------------- cps/kobo.py | 32 +++++++++++++++----------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/cps/helper.py b/cps/helper.py index dce1a280..536add1e 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -732,29 +732,27 @@ def delete_book(book, calibrepath, book_format): return delete_book_file(book, calibrepath, book_format) -def get_cover_on_failure(use_generic_cover): - if use_generic_cover: - try: - return send_from_directory(_STATIC_DIR, "generic_cover.jpg") - except PermissionError: - log.error("No permission to access generic_cover.jpg file.") - abort(403) - abort(404) +def get_cover_on_failure(): + try: + return send_from_directory(_STATIC_DIR, "generic_cover.jpg") + except PermissionError: + log.error("No permission to access generic_cover.jpg file.") + abort(403) def get_book_cover(book_id, resolution=None): book = calibre_db.get_filtered_book(book_id, allow_show_archived=True) - return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution) + return get_book_cover_internal(book, resolution=resolution) -def get_book_cover_with_uuid(book_uuid, resolution=None, none_on_missing=False): +def get_book_cover_with_uuid(book_uuid, resolution=None): book = calibre_db.get_book_by_uuid(book_uuid) - if not book and none_on_missing: + if not book: return # allows kobo.HandleCoverImageRequest to proxy request - return get_book_cover_internal(book, use_generic_cover_on_failure=False, resolution=resolution) + return get_book_cover_internal(book, resolution=resolution) -def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None): +def get_book_cover_internal(book, resolution=None): if book and book.has_cover: # Send the book cover thumbnail if it exists in cache @@ -770,16 +768,16 @@ def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None) if config.config_use_google_drive: try: if not gd.is_gdrive_ready(): - return get_cover_on_failure(use_generic_cover_on_failure) + return get_cover_on_failure() path = gd.get_cover_via_gdrive(book.path) if path: return redirect(path) else: log.error('{}/cover.jpg not found on Google Drive'.format(book.path)) - return get_cover_on_failure(use_generic_cover_on_failure) + return get_cover_on_failure() except Exception as ex: log.error_or_exception(ex) - return get_cover_on_failure(use_generic_cover_on_failure) + return get_cover_on_failure() # Send the book cover from the Calibre directory else: @@ -787,9 +785,9 @@ def get_book_cover_internal(book, use_generic_cover_on_failure, resolution=None) if os.path.isfile(os.path.join(cover_file_path, "cover.jpg")): return send_from_directory(cover_file_path, "cover.jpg") else: - return get_cover_on_failure(use_generic_cover_on_failure) + return get_cover_on_failure() else: - return get_cover_on_failure(use_generic_cover_on_failure) + return get_cover_on_failure() def get_book_cover_thumbnail(book, resolution): @@ -812,7 +810,7 @@ def get_series_thumbnail_on_failure(series_id, resolution): .filter(db.Books.has_cover == 1) \ .first() - return get_book_cover_internal(book, use_generic_cover_on_failure=True, resolution=resolution) + return get_book_cover_internal(book, resolution=resolution) def get_series_cover_thumbnail(series_id, resolution=None): diff --git a/cps/kobo.py b/cps/kobo.py index 8afadafd..6c4b14c3 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -930,23 +930,21 @@ def get_current_bookmark_response(current_bookmark): @kobo.route("//////image.jpg") @requires_kobo_auth def HandleCoverImageRequest(book_uuid, width, height, Quality, isGreyscale): - book_cover = helper.get_book_cover_with_uuid( - book_uuid, - resolution=COVER_THUMBNAIL_SMALL, - none_on_missing=config.config_kobo_proxy) - if not book_cover: - if config.config_kobo_proxy: - log.debug("Cover for unknown book: %s proxied to kobo" % book_uuid) - return redirect(KOBO_IMAGEHOST_URL + - "/{book_uuid}/{width}/{height}/false/image.jpg".format(book_uuid=book_uuid, - width=width, - height=height), 307) - else: - log.debug("Cover for unknown book: %s requested" % book_uuid) - # additional proxy request make no sense, -> direct return - return abort(404) - log.debug("Cover request received for book %s" % book_uuid) - return book_cover + book_cover = helper.get_book_cover_with_uuid(book_uuid, resolution=COVER_THUMBNAIL_SMALL) + if book_cover: + log.debug("Serving local cover image of book %s" % book_uuid) + return book_cover + + if not config.config_kobo_proxy: + log.debug("Returning 404 for cover image of unknown book %s" % book_uuid) + # additional proxy request make no sense, -> direct return + return abort(404) + + log.debug("Redirecting request for cover image of unknown book %s to Kobo" % book_uuid) + return redirect(KOBO_IMAGEHOST_URL + + "/{book_uuid}/{width}/{height}/false/image.jpg".format(book_uuid=book_uuid, + width=width, + height=height), 307) @kobo.route("") From 940544577a68383e543c1aa5a122a52e0b5499fe Mon Sep 17 00:00:00 2001 From: whilenot Date: Sun, 30 Apr 2023 13:37:08 +0200 Subject: [PATCH 03/18] don't mutate meta into a str, keep it a namedtuple --- cps/uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/uploader.py b/cps/uploader.py index 3cd7b21a..c5556481 100644 --- a/cps/uploader.py +++ b/cps/uploader.py @@ -88,7 +88,7 @@ def process(tmp_file_path, original_file_name, original_file_extension, rarExecu log.warning('cannot parse metadata, using default: %s', ex) if not meta.title.strip(): - meta = original_file_name + meta = meta._replace(title=original_file_name) if not meta.author.strip() or meta.author.lower() == 'unknown': meta = meta._replace(author=_('Unknown')) return meta From 2f7b175dda552e4eae63c02559d1c516a962157d Mon Sep 17 00:00:00 2001 From: archont Date: Sun, 16 Jul 2023 18:50:54 +0200 Subject: [PATCH 04/18] Fix tags xpath --- cps/metadata_provider/lubimyczytac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/metadata_provider/lubimyczytac.py b/cps/metadata_provider/lubimyczytac.py index e4abe9db..4644cad9 100644 --- a/cps/metadata_provider/lubimyczytac.py +++ b/cps/metadata_provider/lubimyczytac.py @@ -102,7 +102,7 @@ class LubimyCzytac(Metadata): PUBLISH_DATE = "//dt[contains(@title,'Data pierwszego wydania" FIRST_PUBLISH_DATE = f"{DETAILS}{PUBLISH_DATE} oryginalnego')]{SIBLINGS}[1]/text()" FIRST_PUBLISH_DATE_PL = f"{DETAILS}{PUBLISH_DATE} polskiego')]{SIBLINGS}[1]/text()" - TAGS = "//nav[@aria-label='breadcrumb']//a[contains(@href,'/ksiazki/k/')]/text()" + TAGS = "//nav[@aria-label='breadcrumbs']//a[contains(@href,'/ksiazki/k/')]/span/text()" RATING = "//meta[@property='books:rating:value']/@content" COVER = "//meta[@property='og:image']/@content" From ee5cfa1f3638516947b9f8cd8b127f148e2b048f Mon Sep 17 00:00:00 2001 From: Ghighi Eftimie Date: Wed, 26 Jul 2023 15:11:04 +0300 Subject: [PATCH 05/18] fix for #2838 --- cps/static/css/caliBlur.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cps/static/css/caliBlur.css b/cps/static/css/caliBlur.css index dbbea88e..cf743761 100644 --- a/cps/static/css/caliBlur.css +++ b/cps/static/css/caliBlur.css @@ -7279,6 +7279,11 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. float: right } + body.blur #main-nav + #scnd-nav .create-shelf, body.blur #main-nav + .col-sm-2 #scnd-nav .create-shelf { + float: none; + margin: 5px 0 10px -10px; + } + #main-nav + #scnd-nav .nav-head.hidden-xs { display: list-item !important; width: 225px From 279f0569e4038f4fd1af6293ae4fead96cc7f006 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sat, 29 Jul 2023 15:15:38 +0200 Subject: [PATCH 06/18] Fix visibility for sending to reader without download permissions (fix for #2847) --- cps/static/js/details.js | 1 + cps/templates/detail.html | 48 +++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/cps/static/js/details.js b/cps/static/js/details.js index 24b98437..b8b8b21e 100644 --- a/cps/static/js/details.js +++ b/cps/static/js/details.js @@ -40,6 +40,7 @@ $(".sendbtn-form").click(function() { $.ajax({ method: 'post', url: $(this).data('href'), + data: {csrf_token: $("input[name='csrf_token']").val()}, success: function (data) { handleResponse(data) } diff --git a/cps/templates/detail.html b/cps/templates/detail.html index 7a1b3376..30430663 100755 --- a/cps/templates/detail.html +++ b/cps/templates/detail.html @@ -43,30 +43,30 @@ {% endif %} {% endif %} - {% endif %} - {% if current_user.kindle_mail and entry.email_share_list %} - - {% if entry.email_share_list.__len__() == 1 %} -
- -
- {% else %} -
- - -
+ {% if current_user.kindle_mail and entry.email_share_list %} + + {% if entry.email_share_list.__len__() == 1 %} +
+ +
+ {% else %} +
+ + +
+ {% endif %} {% endif %} {% endif %} {% if entry.reader_list and current_user.role_viewer() %} From 2f12b2e315d3a7cc811a6e8dfbbcbc0893018036 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sat, 29 Jul 2023 15:32:35 +0200 Subject: [PATCH 07/18] Handle invalid or missing container.xml during kobo sync (Fix for #2840) --- cps/epub.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cps/epub.py b/cps/epub.py index c22bad7b..22652c63 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -21,10 +21,11 @@ import zipfile from lxml import etree from . import isoLanguages, cover -from . import config +from . import config, logger from .helper import split_authors from .constants import BookMeta +log = logger.create() def _extract_cover(zip_file, cover_file, cover_path, tmp_file_name): if cover_file is None: @@ -49,15 +50,20 @@ def get_epub_layout(book, book_data): } file_path = os.path.normpath(os.path.join(config.config_calibre_dir, book.path, book_data.name + "." + book_data.format.lower())) - epubZip = zipfile.ZipFile(file_path) - txt = epubZip.read('META-INF/container.xml') - tree = etree.fromstring(txt) - cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0] - cf = epubZip.read(cfname) - tree = etree.fromstring(cf) - p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] + try: + epubZip = zipfile.ZipFile(file_path) + txt = epubZip.read('META-INF/container.xml') + tree = etree.fromstring(txt) + cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0] + cf = epubZip.read(cfname) - layout = p.xpath('pkg:meta[@property="rendition:layout"]/text()', namespaces=ns) + tree = etree.fromstring(cf) + p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] + + layout = p.xpath('pkg:meta[@property="rendition:layout"]/text()', namespaces=ns) + except (etree.XMLSyntaxError, KeyError) as e: + log.error("Could not parse epub metadata of book {} during kobo sync: {}".format(book.id, e)) + layout = [] if len(layout) == 0: return None From 3efcbcc6797e4e4faf4b611985087b247552ccc1 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 30 Jul 2023 07:44:16 +0200 Subject: [PATCH 08/18] Fix deprecated text parameter --- cps/metadata_provider/amazon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/metadata_provider/amazon.py b/cps/metadata_provider/amazon.py index a83747e6..30291a3f 100644 --- a/cps/metadata_provider/amazon.py +++ b/cps/metadata_provider/amazon.py @@ -98,7 +98,7 @@ class Amazon(Metadata): try: match.authors = [next( filter(lambda i: i != " " and i != "\n" and not i.startswith("{"), - x.findAll(text=True))).strip() + x.findAll(string=True))).strip() for x in soup2.findAll("span", attrs={"class": "author"})] except (AttributeError, TypeError, StopIteration): match.authors = "" From 34c6010ad0613e374f4835417192dda799f706d5 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 30 Jul 2023 19:57:42 +0200 Subject: [PATCH 09/18] Catch additional error during kobo detect layout --- cps/epub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/epub.py b/cps/epub.py index 22652c63..50adba59 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -61,7 +61,7 @@ def get_epub_layout(book, book_data): p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] layout = p.xpath('pkg:meta[@property="rendition:layout"]/text()', namespaces=ns) - except (etree.XMLSyntaxError, KeyError) as e: + except (etree.XMLSyntaxError, KeyError, IndexError) as e: log.error("Could not parse epub metadata of book {} during kobo sync: {}".format(book.id, e)) layout = [] From 7818c4a7b0f6ca133c04185bd3c520e6bca7d6ba Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Tue, 8 Aug 2023 19:21:47 +0200 Subject: [PATCH 10/18] Adapt cover size to kobo sync requested sze --- cps/kobo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cps/kobo.py b/cps/kobo.py index dfda6483..c8fecd1c 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -930,7 +930,8 @@ def get_current_bookmark_response(current_bookmark): @kobo.route("//////image.jpg") @requires_kobo_auth def HandleCoverImageRequest(book_uuid, width, height, Quality, isGreyscale): - book_cover = helper.get_book_cover_with_uuid(book_uuid, resolution=COVER_THUMBNAIL_SMALL) + resolution = None if height > 1000 else COVER_THUMBNAIL_SMALL + book_cover = helper.get_book_cover_with_uuid(book_uuid, resolution=resolution) if book_cover: log.debug("Serving local cover image of book %s" % book_uuid) return book_cover From d253804a50fd6bb8d8348d61f65a2cd9c50747a7 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Wed, 16 Aug 2023 18:04:45 +0200 Subject: [PATCH 11/18] fix for #2865 (Kobo sync fails during cover request) --- cps/kobo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cps/kobo.py b/cps/kobo.py index c8fecd1c..1655fb5e 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -930,7 +930,11 @@ def get_current_bookmark_response(current_bookmark): @kobo.route("//////image.jpg") @requires_kobo_auth def HandleCoverImageRequest(book_uuid, width, height, Quality, isGreyscale): - resolution = None if height > 1000 else COVER_THUMBNAIL_SMALL + try: + resolution = None if int(height) > 1000 else COVER_THUMBNAIL_SMALL + except ValueError: + log.error("Requested height %s of book %s is invalid" % (book_uuid, height)) + resolution = COVER_THUMBNAIL_SMALL book_cover = helper.get_book_cover_with_uuid(book_uuid, resolution=resolution) if book_cover: log.debug("Serving local cover image of book %s" % book_uuid) From 3a08b91ffa686c6c8e23ccd1e14d1d2142e8b7d6 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Wed, 16 Aug 2023 18:44:03 +0200 Subject: [PATCH 12/18] Added cb7 to supported comic files for upload and metadata extraction --- cps/__init__.py | 3 ++- cps/comic.py | 22 ++++++++++++++++++++-- cps/uploader.py | 2 +- optional-requirements.txt | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cps/__init__.py b/cps/__init__.py index 8b0b86ad..f4f8dbf2 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -64,7 +64,8 @@ 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') -mimetypes.add_type('image/vnd.djvu', '.djvu') +mimetypes.add_type('application/x-cb7', '.cb7') +mimetypes.add_type('image/vnd.djv', '.djv') mimetypes.add_type('application/mpeg', '.mpeg') mimetypes.add_type('application/mpeg', '.mp3') mimetypes.add_type('application/mp4', '.m4a') diff --git a/cps/comic.py b/cps/comic.py index 13774756..4242bb2f 100644 --- a/cps/comic.py +++ b/cps/comic.py @@ -52,6 +52,12 @@ except (ImportError, LookupError) as e: except (ImportError, SyntaxError) as e: log.debug('Cannot import rarfile, extracting cover files from rar files will not work: %s', e) use_rarfile = False + try: + import py7zr + use_7zip = True + except (ImportError, SyntaxError) as e: + log.debug('Cannot import py7zr, extracting cover files from CB7 files will not work: %s', e) + use_7zip = False use_comic_meta = False @@ -84,10 +90,22 @@ def _extract_cover_from_archive(original_file_extension, tmp_file_name, rar_exec if len(ext) > 1: extension = ext[1].lower() if extension in cover.COVER_EXTENSIONS: - cover_data = cf.read(name) + cover_data = cf.read([name]) break except Exception as ex: - log.debug('Rarfile failed with error: {}'.format(ex)) + log.error('Rarfile failed with error: {}'.format(ex)) + elif original_file_extension.upper() == '.CB7' and use_7zip: + cf = py7zr.SevenZipFile(tmp_file_name) + for name in cf.getnames(): + ext = os.path.splitext(name) + if len(ext) > 1: + extension = ext[1].lower() + if extension in cover.COVER_EXTENSIONS: + try: + cover_data = cf.read(name)[name].read() + except (py7zr.Bad7zFile, OSError) as ex: + log.error('7Zip file failed with error: {}'.format(ex)) + break return cover_data, extension diff --git a/cps/uploader.py b/cps/uploader.py index bf30094d..23dfc4a6 100644 --- a/cps/uploader.py +++ b/cps/uploader.py @@ -79,7 +79,7 @@ def process(tmp_file_path, original_file_name, original_file_extension, rar_exec meta = epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension) elif ".FB2" == extension_upper and use_fb2_meta is True: meta = fb2.get_fb2_info(tmp_file_path, original_file_extension) - elif extension_upper in ['.CBZ', '.CBT', '.CBR']: + elif extension_upper in ['.CBZ', '.CBT', '.CBR', ".CB7"]: meta = comic.get_comic_info(tmp_file_path, original_file_name, original_file_extension, diff --git a/optional-requirements.txt b/optional-requirements.txt index ff02d04f..d34d09aa 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -35,6 +35,7 @@ html2text>=2020.1.16,<2022.1.1 python-dateutil>=2.1,<2.9.0 beautifulsoup4>=4.0.1,<4.12.0 faust-cchardet>=2.1.18 +py7zr>=0.15.0,<0.21.0 # Comics natsort>=2.2.0,<8.4.0 From f7ff3e7cbae4daa7e2b514a30e200902b3225566 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Wed, 23 Aug 2023 20:50:39 +0200 Subject: [PATCH 13/18] Added py7zr to setup.cfg --- setup.cfg | 1 + test/Calibre-Web TestSummary_Linux.html | 501 +++++++++++++----------- 2 files changed, 280 insertions(+), 222 deletions(-) diff --git a/setup.cfg b/setup.cfg index b445eb5e..1f617648 100644 --- a/setup.cfg +++ b/setup.cfg @@ -92,6 +92,7 @@ metadata = python-dateutil>=2.1,<2.9.0 beautifulsoup4>=4.0.1,<4.12.0 faust-cchardet>=2.1.18 + py7zr>=0.15.0,<0.21.0 comics = natsort>=2.2.0,<8.4.0 comicapi>=2.2.0,<3.3.0 diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index 309c9a25..83cb4395 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
-

Start Time: 2023-07-26 21:47:14

+

Start Time: 2023-08-17 20:59:16

-

Stop Time: 2023-07-27 04:10:01

+

Stop Time: 2023-08-18 03:31:30

-

Duration: 5h 21 min

+

Duration: 5h 32 min

@@ -234,12 +234,12 @@ - + TestBackupMetadata 22 - 20 - 1 - 1 + 22 + 0 + 0 0 Detail @@ -293,32 +293,11 @@ - +
TestBackupMetadata - test_backup_change_book_publisher
- -
- FAIL -
- - - - + PASS @@ -395,33 +374,11 @@ AssertionError: '' != 'Lo,执|1u' - +
TestBackupMetadata - test_backup_change_custom_categories
- -
- ERROR -
- - - - + PASS @@ -1015,11 +972,11 @@ TypeError: 'NoneType' object is not iterable - + TestEbookConvertGDriveKepubify 3 - 2 - 1 + 3 + 0 0 0 @@ -1038,33 +995,11 @@ TypeError: 'NoneType' object is not iterable - +
TestEbookConvertGDriveKepubify - test_convert_only
- -
- FAIL -
- - - - + PASS @@ -1079,15 +1014,15 @@ AssertionError: 'Started' != 'Finished' - + TestEditAdditionalBooks + 20 17 - 16 - 0 0 1 + 2 - Detail + Detail @@ -1201,7 +1136,36 @@ AssertionError: 'Started' != 'Finished' - + + +
TestEditAdditionalBooks - test_upload_metadata_cb7
+ + +
+ ERROR +
+ + + + + + + + +
TestEditAdditionalBooks - test_upload_metadata_cbr
@@ -1210,7 +1174,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditAdditionalBooks - test_upload_metadata_cbt
@@ -1219,7 +1183,42 @@ AssertionError: 'Started' != 'Finished' - + + +
TestEditAdditionalBooks - test_writeonly_calibre_database
+ + +
+ SKIP +
+ + + + + + + + + + +
TestEditAdditionalBooks - test_writeonly_path
+ + PASS + + + + +
TestEditAdditionalBooks - test_xss_author_edit
@@ -1228,7 +1227,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditAdditionalBooks - test_xss_comment_edit
@@ -1237,7 +1236,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditAdditionalBooks - test_xss_custom_comment_edit
@@ -1247,15 +1246,15 @@ AssertionError: 'Started' != 'Finished' - + TestEditBooks - 37 - 35 - 0 + 38 + 34 0 2 + 2 - Detail + Detail @@ -1538,7 +1537,36 @@ AssertionError: 'Started' != 'Finished' - + + +
TestEditBooks - test_upload_book_cb7
+ + +
+ ERROR +
+ + + + + + + + +
TestEditBooks - test_upload_book_cbr
@@ -1547,7 +1575,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_cbt
@@ -1556,7 +1584,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_cbz
@@ -1565,7 +1593,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_epub
@@ -1574,7 +1602,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_fb2
@@ -1583,7 +1611,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_lit
@@ -1592,7 +1620,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_mobi
@@ -1601,7 +1629,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_book_pdf
@@ -1610,7 +1638,7 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_cbz_coverformats
@@ -1619,11 +1647,31 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooks - test_upload_cover_hdd
- PASS + +
+ ERROR +
+ + + + @@ -1944,11 +1992,11 @@ AssertionError: 'Started' != 'Finished' - + TestLoadMetadata 1 - 1 0 + 1 0 0 @@ -1958,22 +2006,42 @@ AssertionError: 'Started' != 'Finished' - +
TestLoadMetadata - test_load_metadata
- PASS + +
+ FAIL +
+ + + + - + TestEditBooksOnGdrive 18 - 17 + 16 + 1 1 - 0 0 Detail @@ -1991,11 +2059,34 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooksOnGdrive - test_edit_author
- PASS + +
+ FAIL +
+ + + + @@ -2117,11 +2208,31 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooksOnGdrive - test_edit_title
- PASS + +
+ ERROR +
+ + + + @@ -2135,31 +2246,11 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooksOnGdrive - test_watch_metadata
- -
- FAIL -
- - - - + PASS @@ -3606,11 +3697,11 @@ AssertionError: False is not true - + TestReader 6 - 5 - 1 + 6 + 0 0 0 @@ -3656,37 +3747,11 @@ AssertionError: False is not true - +
TestReader - test_sound_listener
- -
- FAIL -
- - - - + PASS @@ -4054,11 +4119,11 @@ AssertionError: '0:03' != '0:02' - + TestThumbnails 8 - 6 - 1 + 7 + 0 0 1 @@ -4095,31 +4160,11 @@ AssertionError: '0:03' != '0:02' - +
TestThumbnails - test_cover_change_on_upload_new_cover
- -
- FAIL -
- - - - + PASS @@ -5229,11 +5274,11 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 Total - 457 - 443 - 5 - 1 - 8 + 461 + 446 + 2 + 4 + 9   @@ -5261,13 +5306,13 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 Platform - Linux 6.2.0-25-generic #25~22.04.2-Ubuntu SMP PREEMPT_DYNAMIC Wed Jun 28 09:55:23 UTC 2 x86_64 x86_64 + Linux 6.2.0-26-generic #26~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jul 13 16:27:29 UTC 2 x86_64 x86_64 Basic Python - 3.10.6 + 3.10.12 Basic @@ -5279,7 +5324,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 APScheduler - 3.10.1 + 3.10.3 Basic @@ -5405,13 +5450,13 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 Werkzeug - 2.3.6 + 2.3.7 Basic google-api-python-client - 2.95.0 + 2.97.0 TestBackupMetadataGdrive @@ -5429,7 +5474,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestBackupMetadataGdrive @@ -5441,7 +5486,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 google-api-python-client - 2.95.0 + 2.97.0 TestCliGdrivedb @@ -5459,7 +5504,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestCliGdrivedb @@ -5471,7 +5516,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 google-api-python-client - 2.95.0 + 2.97.0 TestEbookConvertCalibreGDrive @@ -5489,7 +5534,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEbookConvertCalibreGDrive @@ -5501,7 +5546,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 google-api-python-client - 2.95.0 + 2.97.0 TestEbookConvertGDriveKepubify @@ -5519,7 +5564,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEbookConvertGDriveKepubify @@ -5535,15 +5580,27 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 TestEditAdditionalBooks + + py7zr + 0.20.6 + TestEditAdditionalBooks + + rarfile 4.0 TestEditAdditionalBooks + + py7zr + 0.20.6 + TestEditBooks + + google-api-python-client - 2.95.0 + 2.97.0 TestEditAuthorsGdrive @@ -5561,7 +5618,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEditAuthorsGdrive @@ -5579,7 +5636,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 google-api-python-client - 2.95.0 + 2.97.0 TestEditBooksOnGdrive @@ -5597,7 +5654,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEditBooksOnGdrive @@ -5621,7 +5678,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 google-api-python-client - 2.95.0 + 2.97.0 TestSetupGdrive @@ -5639,7 +5696,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestSetupGdrive @@ -5663,13 +5720,13 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 jsonschema - 4.18.4 + 4.19.0 TestKoboSync jsonschema - 4.18.4 + 4.19.0 TestKoboSyncBig @@ -5681,7 +5738,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 jsonschema - 4.18.4 + 4.19.0 TestLdapLogin @@ -5711,7 +5768,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 From a1899bf582c5de73fd0b5d27c0b51f8c6718a4fa Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Wed, 23 Aug 2023 21:12:59 +0200 Subject: [PATCH 14/18] Fix for #2603 (Kobo UserKey in request missing due to no kobo account) --- cps/kobo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/kobo.py b/cps/kobo.py index 1655fb5e..47cc4bda 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -1047,7 +1047,7 @@ def make_calibre_web_auth_response(): "RefreshToken": RefreshToken, "TokenType": "Bearer", "TrackingId": str(uuid.uuid4()), - "UserKey": content['UserKey'], + "UserKey": content.get('UserKey',""), } ) ) From b3a85ffcbbc333a00f22ac69f3a21d220769d268 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 24 Aug 2023 10:51:16 +0200 Subject: [PATCH 15/18] Added CB7 to supported upload formats --- cps/constants.py | 2 +- test/Calibre-Web TestSummary_Linux.html | 89 ++++++++----------------- 2 files changed, 27 insertions(+), 64 deletions(-) diff --git a/cps/constants.py b/cps/constants.py index 069630b6..c7d3a6ce 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -147,7 +147,7 @@ EXTENSIONS_CONVERT_FROM = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'txt', 'htmlz', 'rtf', 'odt', 'cbz', 'cbr'] EXTENSIONS_CONVERT_TO = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'htmlz', 'rtf', 'odt'] -EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'kepub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'djv', +EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'kepub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'cb7', 'djvu', 'djv', 'prc', 'doc', 'docx', 'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac', 'm4a', 'm4b'} diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index 83cb4395..66d4df88 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
-

Start Time: 2023-08-17 20:59:16

+

Start Time: 2023-08-23 21:16:31

-

Stop Time: 2023-08-18 03:31:30

+

Stop Time: 2023-08-24 03:51:45

-

Duration: 5h 32 min

+

Duration: 5h 34 min

@@ -2023,9 +2023,15 @@ NameError: name 'details' is not defined
Traceback (most recent call last):
-  File "/home/ozzie/Development/calibre-web-test/test/test_edit_books_metadata.py", line 167, in test_load_metadata
-    self.assertGreaterEqual(diff(BytesIO(cover), BytesIO(original_cover), delete_diff_file=True), 0.05)
-AssertionError: 0.0 not greater than or equal to 0.05
+ File "/home/ozzie/Development/calibre-web-test/test/test_edit_books_metadata.py", line 209, in test_load_metadata + self.assertEqual(old_results, results) +AssertionError: Lists differ: [] != [{'cover_element': <selenium.webdriver.rem[10121 chars]4/'}] + +Second list contains 20 additional elements. +First extra element 0: +{'cover_element': <selenium.webdriver.remote.webelement.WebElement (session="34034d2d-f804-47c1-b9ad-fcf09f75f812", element="6dfe81e2-4752-4f1f-bd33-9388d0d529c1")>, 'cover': 'https://books.google.com/books/content?id=Ub8TAQAAIAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api&fife=w800-h900', 'source': 'https://books.google.com/', 'author': 'Martin Vogt', 'publisher': '', 'title': 'Der Buchtitel in der römischen Poesie', 'title_link': 'https://books.google.com/books?id=Ub8TAQAAIAAJ'} + +Diff is 10795 characters long. Set self.maxDiff to None to see it.
@@ -2036,12 +2042,12 @@ AssertionError: 0.0 not greater than or equal to 0.05 - + TestEditBooksOnGdrive 18 - 16 - 1 - 1 + 18 + 0 + 0 0 Detail @@ -2059,34 +2065,11 @@ AssertionError: 0.0 not greater than or equal to 0.05 - +
TestEditBooksOnGdrive - test_edit_author
- -
- FAIL -
- - - - + PASS @@ -2208,31 +2191,11 @@ AssertionError: 'O0ü name' != ' O0ü name ' - +
TestEditBooksOnGdrive - test_edit_title
- -
- ERROR -
- - - - + PASS @@ -5275,9 +5238,9 @@ KeyError: 'title' Total 461 - 446 - 2 - 4 + 448 + 1 + 3 9   @@ -5324,7 +5287,7 @@ KeyError: 'title' APScheduler - 3.10.3 + 3.10.4 Basic @@ -5342,7 +5305,7 @@ KeyError: 'title' Flask - 2.3.2 + 2.3.3 Basic @@ -5768,7 +5731,7 @@ KeyError: 'title' From 0499e578cdd45db656da34cd2d7152c8d88ceb23 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 24 Aug 2023 13:49:22 +0200 Subject: [PATCH 16/18] Added /opds/stats route --- cps/opds.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cps/opds.py b/cps/opds.py index 074a9b73..4067712f 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -21,9 +21,10 @@ # along with this program. If not, see . import datetime +import json from urllib.parse import unquote_plus -from flask import Blueprint, request, render_template, make_response, abort +from flask import Blueprint, request, render_template, make_response, abort, Response from flask_login import current_user from flask_babel import get_locale from flask_babel import gettext as _ @@ -412,6 +413,17 @@ def get_metadata_calibre_companion(uuid, library): return "" +@opds.route("/opds/stats") +@requires_basic_auth_if_no_ano +def get_database_stats(): + stat = dict() + stat['books'] = calibre_db.session.query(db.Books).count() + stat['authors'] = calibre_db.session.query(db.Authors).count() + stat['categories'] = calibre_db.session.query(db.Tags).count() + stat['series'] = calibre_db.session.query(db.Series).count() + return Response(json.dumps(stat), mimetype="application/json") + + @opds.route("/opds/thumb_240_240/") @opds.route("/opds/cover_240_240/") @opds.route("/opds/cover_90_90/") From 8535bb5821ca868a3187dce59121c019902b8066 Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 27 Aug 2023 11:20:53 +0200 Subject: [PATCH 17/18] Fix "got an unexpected keyword argument 'rarExecutable'" during format upload --- cps/editbooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 5a15740c..f52f08aa 100755 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -1215,7 +1215,7 @@ def upload_single_file(file_request, book, book_id): return uploader.process( saved_filename, *os.path.splitext(requested_file.filename), - rarExecutable=config.config_rarfile_location) + rar_executable=config.config_rarfile_location) return None From 6a14e2cf687c908cb1a7a288f052e569ba58bd8b Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 27 Aug 2023 12:00:15 +0200 Subject: [PATCH 18/18] Next try showing last book of series in grid view --- cps/templates/grid.html | 2 +- cps/web.py | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/cps/templates/grid.html b/cps/templates/grid.html index 1905d52d..3fa6958f 100644 --- a/cps/templates/grid.html +++ b/cps/templates/grid.html @@ -28,7 +28,7 @@
- {{ image.series(entry[0].series[0], alt=entry[0].series[0].name|shortentitle) }} + {{ image.book_cover(entry[0])}} {{entry.count}} diff --git a/cps/web.py b/cps/web.py index 51ff32b3..9f94d1b4 100755 --- a/cps/web.py +++ b/cps/web.py @@ -1002,13 +1002,21 @@ def series_list(): if no_series_count: entries.append([db.Category(_("None"), "-1"), no_series_count]) entries = sorted(entries, key=lambda x: x[0].name.lower(), reverse=not order_no) - return render_title_template('list.html', entries=entries, folder='web.books_list', charlist=char_list, - title=_("Series"), page="serieslist", data="series", order=order_no) + return render_title_template('list.html', + entries=entries, + folder='web.books_list', + charlist=char_list, + title=_("Series"), + page="serieslist", + data="series", order=order_no) else: - 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() + 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')) + .having(func.max(db.Books.series_index)) + .order_by(order) + .all()) return render_title_template('grid.html', entries=entries, folder='web.books_list', charlist=char_list, title=_("Series"), page="serieslist", data="series", bodyClass="grid-view", order=order_no)