diff --git a/cps/about.py b/cps/about.py
index 7b6cc71af..1d081fe27 100644
--- a/cps/about.py
+++ b/cps/about.py
@@ -49,9 +49,9 @@ sorted_modules = OrderedDict((sorted(modules.items(), key=lambda x: x[0].casefol
def collect_stats():
if constants.NIGHTLY_VERSION[0] == "$Format:%H$":
- calibre_web_version = constants.STABLE_VERSION['version']
+ calibre_web_version = constants.STABLE_VERSION['version'].replace("b", " Beta")
else:
- calibre_web_version = (constants.STABLE_VERSION['version'] + ' - '
+ calibre_web_version = (constants.STABLE_VERSION['version'].replace("b", " Beta") + ' - '
+ constants.NIGHTLY_VERSION[0].replace('%', '%%') + ' - '
+ constants.NIGHTLY_VERSION[1].replace('%', '%%'))
diff --git a/cps/admin.py b/cps/admin.py
index 20e901e32..fa29759e2 100644
--- a/cps/admin.py
+++ b/cps/admin.py
@@ -47,7 +47,7 @@ from . import constants, logger, helper, services, cli_param
from . import db, calibre_db, ub, web_server, config, updater_thread, gdriveutils, \
kobo_sync_status, schedule
from .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash, check_email, \
- valid_email, check_username
+ valid_email, check_username, get_calibre_binarypath
from .gdriveutils import is_gdrive_ready, gdrive_support
from .render_template import render_title_template, get_sidebar_config
from .services.worker import WorkerThread
@@ -217,7 +217,7 @@ def admin():
form_date += timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
commit = format_datetime(form_date - tz, format='short')
else:
- commit = version['version']
+ commit = version['version'].replace("b", " Beta")
all_user = ub.session.query(ub.User).all()
# email_settings = mail_config.get_mail_settings()
@@ -1705,7 +1705,7 @@ def _db_configuration_update_helper():
return _db_configuration_result('{}'.format(ex), gdrive_error)
if db_change or not db_valid or not config.db_configured \
- or config.config_calibre_dir != to_save["config_calibre_dir"]:
+ or config.config_calibre_dir != to_save["config_calibre_dir"]:
if not os.path.exists(metadata_db) or not to_save['config_calibre_dir']:
return _db_configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), gdrive_error)
else:
@@ -1728,6 +1728,9 @@ def _db_configuration_update_helper():
calibre_db.update_config(config)
if not os.access(os.path.join(config.config_calibre_dir, "metadata.db"), os.W_OK):
flash(_("DB is not Writeable"), category="warning")
+ _config_string(to_save, "config_calibre_split_dir")
+ config.config_calibre_split = to_save.get('config_calibre_split', 0) == "on"
+ calibre_db.update_config(config)
config.save()
return _db_configuration_result(None, gdrive_error)
@@ -1748,6 +1751,7 @@ def _configuration_update_helper():
_config_checkbox_int(to_save, "config_uploading")
_config_checkbox_int(to_save, "config_unicode_filename")
+ _config_checkbox_int(to_save, "config_embed_metadata")
# Reboot on config_anonbrowse with enabled ldap, as decoraters are changed in this case
reboot_required |= (_config_checkbox_int(to_save, "config_anonbrowse")
and config.config_login_type == constants.LOGIN_LDAP)
@@ -1764,8 +1768,14 @@ def _configuration_update_helper():
constants.EXTENSIONS_UPLOAD = config.config_upload_formats.split(',')
_config_string(to_save, "config_calibre")
- _config_string(to_save, "config_converterpath")
+ _config_string(to_save, "config_binariesdir")
_config_string(to_save, "config_kepubifypath")
+ if "config_binariesdir" in to_save:
+ calibre_status = helper.check_calibre(config.config_binariesdir)
+ if calibre_status:
+ return _configuration_result(calibre_status)
+ to_save["config_converterpath"] = get_calibre_binarypath("ebook-convert")
+ _config_string(to_save, "config_converterpath")
reboot_required |= _config_int(to_save, "config_login_type")
diff --git a/cps/cli.py b/cps/cli.py
index e9b97b9de..855ad8990 100644
--- a/cps/cli.py
+++ b/cps/cli.py
@@ -29,8 +29,8 @@ from .constants import DEFAULT_SETTINGS_FILE, DEFAULT_GDRIVE_FILE
def version_info():
if _NIGHTLY_VERSION[1].startswith('$Format'):
- return "Calibre-Web version: %s - unknown git-clone" % _STABLE_VERSION['version']
- return "Calibre-Web version: %s -%s" % (_STABLE_VERSION['version'], _NIGHTLY_VERSION[1])
+ return "Calibre-Web version: %s - unknown git-clone" % _STABLE_VERSION['version'].replace("b", " Beta")
+ return "Calibre-Web version: %s -%s" % (_STABLE_VERSION['version'].replace("b", " Beta"), _NIGHTLY_VERSION[1])
class CliParameter(object):
diff --git a/cps/config_sql.py b/cps/config_sql.py
index 21644ccd6..c4f94b4e0 100644
--- a/cps/config_sql.py
+++ b/cps/config_sql.py
@@ -34,6 +34,7 @@ except ImportError:
from sqlalchemy.ext.declarative import declarative_base
from . import constants, logger
+from .subproc_wrapper import process_wait
log = logger.create()
@@ -69,6 +70,8 @@ class _Settings(_Base):
config_calibre_dir = Column(String)
config_calibre_uuid = Column(String)
+ config_calibre_split = Column(Boolean, default=False)
+ config_calibre_split_dir = Column(String)
config_port = Column(Integer, default=constants.DEFAULT_PORT)
config_external_port = Column(Integer, default=constants.DEFAULT_PORT)
config_certfile = Column(String)
@@ -138,10 +141,12 @@ class _Settings(_Base):
config_kepubifypath = Column(String, default=None)
config_converterpath = Column(String, default=None)
+ config_binariesdir = Column(String, default=None)
config_calibre = Column(String)
config_rarfile_location = Column(String, default=None)
config_upload_formats = Column(String, default=','.join(constants.EXTENSIONS_UPLOAD))
config_unicode_filename = Column(Boolean, default=False)
+ config_embed_metadata = Column(Boolean, default=True)
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
@@ -184,9 +189,11 @@ class ConfigSQL(object):
self.load()
change = False
- if self.config_converterpath == None: # pylint: disable=access-member-before-definition
+
+ if self.config_binariesdir == None: # pylint: disable=access-member-before-definition
change = True
- self.config_converterpath = autodetect_calibre_binary()
+ self.config_binariesdir = autodetect_calibre_binaries()
+ self.config_converterpath = autodetect_converter_binary(self.config_binariesdir)
if self.config_kepubifypath == None: # pylint: disable=access-member-before-definition
change = True
@@ -389,6 +396,9 @@ class ConfigSQL(object):
self.db_configured = False
self.save()
+ def get_book_path(self):
+ return self.config_calibre_split_dir if self.config_calibre_split_dir else self.config_calibre_dir
+
def store_calibre_uuid(self, calibre_db, Library_table):
try:
calibre_uuid = calibre_db.session.query(Library_table).one_or_none()
@@ -469,17 +479,32 @@ def _migrate_table(session, orm_class, secret_key=None):
session.rollback()
-def autodetect_calibre_binary():
+def autodetect_calibre_binaries():
if sys.platform == "win32":
- calibre_path = ["C:\\program files\\calibre\\ebook-convert.exe",
- "C:\\program files(x86)\\calibre\\ebook-convert.exe",
- "C:\\program files(x86)\\calibre2\\ebook-convert.exe",
- "C:\\program files\\calibre2\\ebook-convert.exe"]
+ calibre_path = ["C:\\program files\\calibre\\",
+ "C:\\program files(x86)\\calibre\\",
+ "C:\\program files(x86)\\calibre2\\",
+ "C:\\program files\\calibre2\\"]
else:
- calibre_path = ["/opt/calibre/ebook-convert"]
+ calibre_path = ["/opt/calibre/"]
for element in calibre_path:
- if os.path.isfile(element) and os.access(element, os.X_OK):
- return element
+ supported_binary_paths = [os.path.join(element, binary) for binary in constants.SUPPORTED_CALIBRE_BINARIES.values()]
+ if all(os.path.isfile(binary_path) and os.access(binary_path, os.X_OK) for binary_path in supported_binary_paths):
+ values = [process_wait([binary_path, "--version"], pattern='\(calibre (.*)\)') for binary_path in supported_binary_paths]
+ if all(values):
+ version = values[0].group(1)
+ log.debug("calibre version %s", version)
+ return element
+ return ""
+
+
+def autodetect_converter_binary(calibre_path):
+ if sys.platform == "win32":
+ converter_path = os.path.join(calibre_path, "ebook-convert.exe")
+ else:
+ converter_path = os.path.join(calibre_path, "ebook-convert")
+ if calibre_path and os.path.isfile(converter_path) and os.access(converter_path, os.X_OK):
+ return converter_path
return ""
@@ -521,6 +546,7 @@ def load_configuration(session, secret_key):
session.commit()
+
def get_flask_session_key(_session):
flask_settings = _session.query(_Flask_Settings).one_or_none()
if flask_settings == None:
diff --git a/cps/constants.py b/cps/constants.py
index 87ce6f59f..ef207e02b 100644
--- a/cps/constants.py
+++ b/cps/constants.py
@@ -156,6 +156,11 @@ EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'kepub', 'mobi', 'azw', 'azw3', 'cbr'
'prc', 'doc', 'docx', 'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg',
'opus', 'wav', 'flac', 'm4a', 'm4b'}
+_extension = ""
+if sys.platform == "win32":
+ _extension = ".exe"
+SUPPORTED_CALIBRE_BINARIES = {binary:binary + _extension for binary in ["ebook-convert", "calibredb"]}
+
def has_flag(value, bit_flag):
return bit_flag == (bit_flag & (value or 0))
@@ -169,13 +174,11 @@ BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, d
'series_id, languages, publisher, pubdate, identifiers')
# python build process likes to have x.y.zbw -> b for beta and w a counting number
-STABLE_VERSION = {'version': '0.6.22 Beta'}
+STABLE_VERSION = {'version': '0.6.22b'}
NIGHTLY_VERSION = dict()
NIGHTLY_VERSION[0] = '$Format:%H$'
NIGHTLY_VERSION[1] = '$Format:%cI$'
-# NIGHTLY_VERSION[0] = 'bb7d2c6273ae4560e83950d36d64533343623a57'
-# NIGHTLY_VERSION[1] = '2018-09-09T10:13:08+02:00'
# CACHE
CACHE_TYPE_THUMBNAILS = 'thumbnails'
diff --git a/cps/editbooks.py b/cps/editbooks.py
index 4b33f15e7..4d195eb7f 100644
--- a/cps/editbooks.py
+++ b/cps/editbooks.py
@@ -137,7 +137,7 @@ def edit_book(book_id):
edited_books_id = book.id
modify_date = True
title_author_error = helper.update_dir_structure(edited_books_id,
- config.config_calibre_dir,
+ config.get_book_path(),
input_authors[0],
renamed_author=renamed)
if title_author_error:
@@ -282,7 +282,7 @@ def upload():
meta.extension.lower())
else:
error = helper.update_dir_structure(book_id,
- config.config_calibre_dir,
+ config.get_book_path(),
input_authors[0],
meta.file_path,
title_dir + meta.extension.lower(),
@@ -332,7 +332,7 @@ def convert_bookformat(book_id):
return redirect(url_for('edit-book.show_edit_book', book_id=book_id))
log.info('converting: book id: %s from: %s to: %s', book_id, book_format_from, book_format_to)
- rtn = helper.convert_book_format(book_id, config.config_calibre_dir, book_format_from.upper(),
+ rtn = helper.convert_book_format(book_id, config.get_book_path(), book_format_from.upper(),
book_format_to.upper(), current_user.name)
if rtn is None:
@@ -402,7 +402,7 @@ def edit_list_book(param):
elif param == 'title':
sort_param = book.sort
if handle_title_on_edit(book, vals.get('value', "")):
- rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir)
+ rename_error = helper.update_dir_structure(book.id, config.get_book_path())
if not rename_error:
ret = Response(json.dumps({'success': True, 'newValue': book.title}),
mimetype='application/json')
@@ -420,7 +420,7 @@ def edit_list_book(param):
mimetype='application/json')
elif param == 'authors':
input_authors, __, renamed = handle_author_on_edit(book, vals['value'], vals.get('checkA', None) == "true")
- rename_error = helper.update_dir_structure(book.id, config.config_calibre_dir, input_authors[0],
+ rename_error = helper.update_dir_structure(book.id, config.get_book_path(), input_authors[0],
renamed_author=renamed)
if not rename_error:
ret = Response(json.dumps({
@@ -524,10 +524,10 @@ def merge_list_book():
for element in from_book.data:
if element.format not in to_file:
# create new data entry with: book_id, book_format, uncompressed_size, name
- filepath_new = os.path.normpath(os.path.join(config.config_calibre_dir,
+ filepath_new = os.path.normpath(os.path.join(config.get_book_path(),
to_book.path,
to_name + "." + element.format.lower()))
- filepath_old = os.path.normpath(os.path.join(config.config_calibre_dir,
+ filepath_old = os.path.normpath(os.path.join(config.get_book_path(),
from_book.path,
element.name + "." + element.format.lower()))
copyfile(filepath_old, filepath_new)
@@ -567,7 +567,7 @@ def table_xchange_author_title():
if edited_books_id:
# toDo: Handle error
- edit_error = helper.update_dir_structure(edited_books_id, config.config_calibre_dir, input_authors[0],
+ edit_error = helper.update_dir_structure(edited_books_id, config.get_book_path(), input_authors[0],
renamed_author=renamed)
if modify_date:
book.last_modified = datetime.utcnow()
@@ -764,7 +764,7 @@ def move_coverfile(meta, db_book):
cover_file = meta.cover
else:
cover_file = os.path.join(constants.STATIC_DIR, 'generic_cover.jpg')
- new_cover_path = os.path.join(config.config_calibre_dir, db_book.path)
+ new_cover_path = os.path.join(config.get_book_path(), db_book.path)
try:
os.makedirs(new_cover_path, exist_ok=True)
copyfile(cover_file, os.path.join(new_cover_path, "cover.jpg"))
@@ -850,7 +850,7 @@ def delete_book_from_table(book_id, book_format, json_response):
book = calibre_db.get_book(book_id)
if book:
try:
- result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
+ result, error = helper.delete_book(book, config.get_book_path(), book_format=book_format.upper())
if not result:
if json_response:
return json.dumps([{"location": url_for("edit-book.show_edit_book", book_id=book_id),
@@ -1190,7 +1190,7 @@ def upload_single_file(file_request, book, book_id):
return False
file_name = book.path.rsplit('/', 1)[-1]
- filepath = os.path.normpath(os.path.join(config.config_calibre_dir, book.path))
+ filepath = os.path.normpath(os.path.join(config.get_book_path(), book.path))
saved_filename = os.path.join(filepath, file_name + '.' + file_ext)
# check if file path exists, otherwise create it, copy file to calibre path and delete temp file
diff --git a/cps/epub.py b/cps/epub.py
index ca6820b1c..a45fb9268 100644
--- a/cps/epub.py
+++ b/cps/epub.py
@@ -23,10 +23,12 @@ from lxml import etree
from . import isoLanguages, cover
from . import config, logger
from .helper import split_authors
+from .epub_helper import get_content_opf, default_ns
from .constants import BookMeta
log = logger.create()
+
def _extract_cover(zip_file, cover_file, cover_path, tmp_file_name):
if cover_file is None:
return None
@@ -44,23 +46,14 @@ def _extract_cover(zip_file, cover_file, cover_path, tmp_file_name):
return cover.cover_processing(tmp_file_name, cf, extension)
def get_epub_layout(book, book_data):
- ns = {
- 'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
- 'pkg': 'http://www.idpf.org/2007/opf',
- }
- file_path = os.path.normpath(os.path.join(config.config_calibre_dir, book.path, book_data.name + "." + book_data.format.lower()))
+ file_path = os.path.normpath(os.path.join(config.get_book_path(),
+ book.path, book_data.name + "." + book_data.format.lower()))
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)
+ tree, __ = get_content_opf(file_path, default_ns)
+ p = tree.xpath('/pkg:package/pkg:metadata', namespaces=default_ns)[0]
- 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)
+ layout = p.xpath('pkg:meta[@property="rendition:layout"]/text()', namespaces=default_ns)
except (etree.XMLSyntaxError, KeyError, IndexError) as e:
log.error("Could not parse epub metadata of book {} during kobo sync: {}".format(book.id, e))
layout = []
@@ -78,13 +71,7 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
'dc': 'http://purl.org/dc/elements/1.1/'
}
- epub_zip = zipfile.ZipFile(tmp_file_path)
-
- txt = epub_zip.read('META-INF/container.xml')
- tree = etree.fromstring(txt)
- cf_name = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0]
- cf = epub_zip.read(cf_name)
- tree = etree.fromstring(cf)
+ tree, cf_name = get_content_opf(tmp_file_path, ns)
cover_path = os.path.dirname(cf_name)
@@ -127,6 +114,7 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
epub_metadata = parse_epub_series(ns, tree, epub_metadata)
+ epub_zip = zipfile.ZipFile(tmp_file_path)
cover_file = parse_epub_cover(ns, tree, epub_zip, cover_path, tmp_file_path)
identifiers = []
diff --git a/cps/epub_helper.py b/cps/epub_helper.py
new file mode 100644
index 000000000..603ccc3dc
--- /dev/null
+++ b/cps/epub_helper.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+
+# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
+# Copyright (C) 2018 lemmsh, Kennyl, Kyosfonica, matthazinski
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
Start Time: 2023-10-16 19:38:22
+Start Time: 2024-01-17 20:30:42
Stop Time: 2023-10-17 02:18:49
+Stop Time: 2024-01-18 03:25:01
Duration: 5h 37 min
+Duration: 5h 47 min
Traceback (most recent call last): + File "/home/ozzie/Development/calibre-web-test/test/test_backup_metadata.py", line 132, in test_backup_change_book_series_index + self.assertEqual(metadata['series']['content'], "tEst") +AssertionError: 'test' != 'tEst' +- test +? ^ ++ tEst +? ^+
Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn
+ conn = connection.create_connection(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 95, in create_connection
+ raise err
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 85, in create_connection
+ sock.connect(sa)
+ConnectionRefusedError: [Errno 111] Connection refused
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 703, in urlopen
+ httplib_response = self._make_request(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 398, in _make_request
+ conn.request(method, url, **httplib_request_kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 239, in request
+ super(HTTPConnection, self).request(method, url, body=body, headers=headers)
+ File "/usr/lib/python3.10/http/client.py", line 1283, in request
+ self._send_request(method, url, body, headers, encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1329, in _send_request
+ self.endheaders(body, encode_chunked=encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1278, in endheaders
+ self._send_output(message_body, encode_chunked=encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1038, in _send_output
+ self.send(msg)
+ File "/usr/lib/python3.10/http/client.py", line 976, in send
+ self.connect()
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 205, in connect
+ conn = self._new_conn()
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 186, in _new_conn
+ raise NewConnectionError(
+urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7fbaf3c23910>: Failed to establish a new connection: [Errno 111] Connection refused
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/test/test_backup_metadata_gdrive.py", line 105, in test_backup_gdrive
+ self.queue_metadata_backup()
+ File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 1630, in queue_metadata_backup
+ self.check_element_on_page((By.ID, "metadata_backup")).click()
+ File "/home/ozzie/Development/calibre-web-test/test/helper_ui.py", line 751, in check_element_on_page
+ el = WebDriverWait(cls.driver, timeout).until(EC.presence_of_element_located(element))
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/support/wait.py", line 86, in until
+ value = method(self._driver)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/support/expected_conditions.py", line 69, in _predicate
+ return driver.find_element(*locator)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 830, in find_element
+ return self.execute(Command.FIND_ELEMENT, {"using": by, "value": value})["value"]
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 438, in execute
+ response = self.command_executor.execute(driver_command, params)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py", line 290, in execute
+ return self._request(command_info[0], url, body=data)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py", line 311, in _request
+ response = self._conn.request(method, url, body=body, headers=headers)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/request.py", line 78, in request
+ return self.request_encode_body(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/request.py", line 170, in request_encode_body
+ return self.urlopen(method, url, **extra_kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/poolmanager.py", line 376, in urlopen
+ response = conn.urlopen(method, u.request_uri, **kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 787, in urlopen
+ retries = retries.increment(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
+ raise MaxRetryError(_pool, url, error or ResponseError(cause))
+urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=36553): Max retries exceeded with url: /session/21a363dd-f6ec-48cc-95a7-772f18f01f8d/element (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fbaf3c23910>: Failed to establish a new connection: [Errno 111] Connection refused'))
+ Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn
+ conn = connection.create_connection(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 95, in create_connection
+ raise err
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/connection.py", line 85, in create_connection
+ sock.connect(sa)
+ConnectionRefusedError: [Errno 111] Connection refused
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 703, in urlopen
+ httplib_response = self._make_request(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 398, in _make_request
+ conn.request(method, url, **httplib_request_kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 239, in request
+ super(HTTPConnection, self).request(method, url, body=body, headers=headers)
+ File "/usr/lib/python3.10/http/client.py", line 1283, in request
+ self._send_request(method, url, body, headers, encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1329, in _send_request
+ self.endheaders(body, encode_chunked=encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1278, in endheaders
+ self._send_output(message_body, encode_chunked=encode_chunked)
+ File "/usr/lib/python3.10/http/client.py", line 1038, in _send_output
+ self.send(msg)
+ File "/usr/lib/python3.10/http/client.py", line 976, in send
+ self.connect()
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 205, in connect
+ conn = self._new_conn()
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connection.py", line 186, in _new_conn
+ raise NewConnectionError(
+urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7fbaf3c22e90>: Failed to establish a new connection: [Errno 111] Connection refused
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "/home/ozzie/Development/calibre-web-test/test/test_backup_metadata_gdrive.py", line 78, in tearDownClass
+ cls.driver.get("http://127.0.0.1:"+ PORTS[0])
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 449, in get
+ self.execute(Command.GET, {"url": url})
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 438, in execute
+ response = self.command_executor.execute(driver_command, params)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py", line 290, in execute
+ return self._request(command_info[0], url, body=data)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/selenium/webdriver/remote/remote_connection.py", line 311, in _request
+ response = self._conn.request(method, url, body=body, headers=headers)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/request.py", line 78, in request
+ return self.request_encode_body(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/request.py", line 170, in request_encode_body
+ return self.urlopen(method, url, **extra_kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/poolmanager.py", line 376, in urlopen
+ response = conn.urlopen(method, u.request_uri, **kw)
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 815, in urlopen
+ return self.urlopen(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/connectionpool.py", line 787, in urlopen
+ retries = retries.increment(
+ File "/home/ozzie/Development/calibre-web-test/venv/lib/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
+ raise MaxRetryError(_pool, url, error or ResponseError(cause))
+urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=36553): Max retries exceeded with url: /session/21a363dd-f6ec-48cc-95a7-772f18f01f8d/url (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fbaf3c22e90>: Failed to establish a new connection: [Errno 111] Connection refused'))
+