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/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/cps/editbooks.py b/cps/editbooks.py index 40f62713..b8f6363f 100755 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -1214,7 +1214,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 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',""), } ) ) 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/") 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/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/cps/web.py b/cps/web.py index 9793f01a..95b1222c 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) diff --git a/optional-requirements.txt b/optional-requirements.txt index 45f842eb..6e82fd60 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.13.0 faust-cchardet>=2.1.18,<2.1.20 +py7zr>=0.15.0,<0.21.0 # Comics natsort>=2.2.0,<8.4.0 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..66d4df88 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-23 21:16:31

-

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

+

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

-

Duration: 5h 21 min

+

Duration: 5h 34 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,21 +2006,47 @@ AssertionError: 'Started' != 'Finished' - +
TestLoadMetadata - test_load_metadata
- PASS + +
+ FAIL +
+ + + + - + TestEditBooksOnGdrive 18 - 17 - 1 + 18 + 0 0 0 @@ -2135,31 +2209,11 @@ AssertionError: 'Started' != 'Finished' - +
TestEditBooksOnGdrive - test_watch_metadata
- -
- FAIL -
- - - - + PASS @@ -3606,11 +3660,11 @@ AssertionError: False is not true - + TestReader 6 - 5 - 1 + 6 + 0 0 0 @@ -3656,37 +3710,11 @@ AssertionError: False is not true - +
TestReader - test_sound_listener
- -
- FAIL -
- - - - + PASS @@ -4054,11 +4082,11 @@ AssertionError: '0:03' != '0:02' - + TestThumbnails 8 - 6 - 1 + 7 + 0 0 1 @@ -4095,31 +4123,11 @@ AssertionError: '0:03' != '0:02' - +
TestThumbnails - test_cover_change_on_upload_new_cover
- -
- FAIL -
- - - - + PASS @@ -5229,11 +5237,11 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 Total - 457 - 443 - 5 + 461 + 448 1 - 8 + 3 + 9   @@ -5261,13 +5269,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 +5287,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 APScheduler - 3.10.1 + 3.10.4 Basic @@ -5297,7 +5305,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 Flask - 2.3.2 + 2.3.3 Basic @@ -5405,13 +5413,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 +5437,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestBackupMetadataGdrive @@ -5441,7 +5449,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 +5467,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestCliGdrivedb @@ -5471,7 +5479,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 +5497,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEbookConvertCalibreGDrive @@ -5501,7 +5509,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 +5527,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEbookConvertGDriveKepubify @@ -5535,15 +5543,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 +5581,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEditAuthorsGdrive @@ -5579,7 +5599,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 +5617,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestEditBooksOnGdrive @@ -5621,7 +5641,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 +5659,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 PyDrive2 - 1.16.1 + 1.17.0 TestSetupGdrive @@ -5663,13 +5683,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 +5701,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03 jsonschema - 4.18.4 + 4.19.0 TestLdapLogin @@ -5711,7 +5731,7 @@ AssertionError: 0.0288805190529425 not greater than or equal to 0.03