diff --git a/MANIFEST.in b/MANIFEST.in index b667159c..f07c4d83 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,3 @@ graft src/calibreweb +global-exclude __pycache__ +global-exclude *.pyc diff --git a/cps/__init__.py b/cps/__init__.py index 7f2a6b13..31614c2a 100644 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -31,12 +31,10 @@ from flask_principal import Principal from . import logger from .cli import CliParameter -from .constants import CONFIG_DIR from .reverseproxy import ReverseProxied from .server import WebServer from .dep_check import dependency_check from .updater import Updater -from .babel import babel, get_locale from . import config_sql from . import cache_buster from . import ub, db @@ -183,6 +181,7 @@ def create_app(): app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session)) web_server.init_app(app, config) + from .cw_babel import babel, get_locale if hasattr(babel, "localeselector"): babel.init_app(app) babel.localeselector(get_locale) diff --git a/cps/about.py b/cps/about.py index fb031343..b43c97b9 100644 --- a/cps/about.py +++ b/cps/about.py @@ -42,19 +42,17 @@ opt = dep_check.load_dependencies(True) for i in (req + opt): modules[i[1]] = i[0] modules['Jinja2'] = importlib.metadata.version("jinja2") -try: +if sys.version_info < (3, 12): modules['pySqlite'] = sqlite3.version -except Exception: - pass modules['SQLite'] = sqlite3.sqlite_version sorted_modules = OrderedDict((sorted(modules.items(), key=lambda x: x[0].casefold()))) def collect_stats(): if constants.NIGHTLY_VERSION[0] == "$Format:%H$": - calibre_web_version = constants.STABLE_VERSION['version'].replace("b", " Beta") + calibre_web_version = constants.STABLE_VERSION.replace("b", " Beta") else: - calibre_web_version = (constants.STABLE_VERSION['version'].replace("b", " Beta") + ' - ' + calibre_web_version = (constants.STABLE_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 08972292..6252b317 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -52,7 +52,7 @@ from .gdriveutils import is_gdrive_ready, gdrive_support from .render_template import render_title_template, get_sidebar_config from .services.worker import WorkerThread from .usermanagement import user_login_required -from .babel import get_available_translations, get_available_locale, get_user_locale_language +from .cw_babel import get_available_translations, get_available_locale, get_user_locale_language from . import debug_info from .string_helper import strip_whitespaces @@ -219,7 +219,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'].replace("b", " Beta") + commit = version.replace("b", " Beta") all_user = ub.session.query(ub.User).all() # email_settings = mail_config.get_mail_settings() diff --git a/cps/cli.py b/cps/cli.py index 5bf289b8..3f21a17e 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'].replace("b", " Beta") - return "Calibre-Web version: %s -%s" % (_STABLE_VERSION['version'].replace("b", " Beta"), _NIGHTLY_VERSION[1]) + return "Calibre-Web version: %s - unknown git-clone" % _STABLE_VERSION.replace("b", " Beta") + return "Calibre-Web version: %s -%s" % (_STABLE_VERSION.replace("b", " Beta"), _NIGHTLY_VERSION[1]) class CliParameter(object): diff --git a/cps/constants.py b/cps/constants.py index 3d1f0dfe..0f03364a 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -19,9 +19,6 @@ import sys import os from collections import namedtuple -from sqlalchemy import __version__ as sql_version - -sqlalchemy_version2 = ([int(x) for x in sql_version.split('.')] >= [2, 0, 0]) # APP_MODE - production, development, or test APP_MODE = os.environ.get('APP_MODE', 'production') @@ -175,7 +172,8 @@ 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.24b'} +STABLE_VERSION = '0.6.24b' +# _version = STABLE_VERSION['version'] NIGHTLY_VERSION = dict() NIGHTLY_VERSION[0] = '$Format:%H$' diff --git a/cps/babel.py b/cps/cw_babel.py similarity index 100% rename from cps/babel.py rename to cps/cw_babel.py diff --git a/cps/dep_check.py b/cps/dep_check.py index b1917f7a..2f156913 100644 --- a/cps/dep_check.py +++ b/cps/dep_check.py @@ -63,7 +63,7 @@ def dependency_check(optional=False): deps = load_dependencies(optional) for dep in deps: try: - dep_version_int = [int(x) if x.isnumeric() else 0 for x in dep[0].split('.')] + dep_version_int = [int(x) if x.isnumeric() else 0 for x in dep[0].split('.')[:3]] low_check = [int(x) for x in dep[3].split('.')] high_check = [int(x) for x in dep[5].split('.')] except AttributeError: diff --git a/cps/isoLanguages.py b/cps/isoLanguages.py index bbab7089..a34cd34a 100644 --- a/cps/isoLanguages.py +++ b/cps/isoLanguages.py @@ -25,20 +25,7 @@ log = logger.create() try: - from iso639 import languages - # iso_version = importlib.metadata.version("iso639") - get = languages.get - try: - if sys.version_info >= (3, 12): - import pkg_resources - except ImportError: - print("Python 3.12 isn't compatible with iso-639. Please install pycountry.") -except ImportError as ex: from pycountry import languages as pyc_languages - #try: - # iso_version = importlib.metadata.version("pycountry") + ' (PyCountry)' - #except (ImportError, Exception): - # iso_version = "?" + ' (PyCountry)' def _copy_fields(l): l.part1 = getattr(l, 'alpha_2', None) @@ -52,6 +39,11 @@ except ImportError as ex: return _copy_fields(pyc_languages.get(alpha_2=part1)) if name is not None: return _copy_fields(pyc_languages.get(name=name)) +except ImportError as ex: + if sys.version_info >= (3, 12): + print("Python 3.12 isn't compatible with iso-639. Please install pycountry.") + from iso639 import languages + get = languages.get def get_language_names(locale): @@ -109,6 +101,6 @@ def get_lang3(lang): ret_value = lang else: ret_value = "" - except KeyError: + except (KeyError, AttributeError): ret_value = lang return ret_value diff --git a/cps/services/background_scheduler.py b/cps/services/background_scheduler.py index 72991559..55b12df3 100644 --- a/cps/services/background_scheduler.py +++ b/cps/services/background_scheduler.py @@ -42,6 +42,7 @@ class BackgroundScheduler: if cls._instance is None: cls._instance = super(BackgroundScheduler, cls).__new__(cls) cls.log = logger.create() + logger.logging.getLogger('tzlocal').setLevel(logger.logging.WARNING) cls.scheduler = BScheduler() cls.scheduler.start() diff --git a/cps/updater.py b/cps/updater.py index ab67e7a6..e55feb6b 100644 --- a/cps/updater.py +++ b/cps/updater.py @@ -614,7 +614,7 @@ class Updater(threading.Thread): if version is False: status['current_commit_hash'] = _(u'Unknown') else: - status['current_commit_hash'] = version['version'] + status['current_commit_hash'] = version try: headers = {'Accept': 'application/vnd.github.v3+json'} r = requests.get(repository_url, headers=headers, timeout=10) diff --git a/cps/web.py b/cps/web.py index 8277a763..ab2b54e3 100644 --- a/cps/web.py +++ b/cps/web.py @@ -23,6 +23,7 @@ import json import mimetypes import chardet # dependency of requests import copy +import importlib from flask import Blueprint, jsonify from flask import request, redirect, send_from_directory, make_response, flash, abort, url_for, Response @@ -36,7 +37,6 @@ from sqlalchemy.exc import IntegrityError, InvalidRequestError, OperationalError from sqlalchemy.sql.expression import text, func, false, not_, and_, or_ from sqlalchemy.orm.attributes import flag_modified from sqlalchemy.sql.functions import coalesce - from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash, check_password_hash @@ -51,7 +51,7 @@ from .helper import check_valid_domain, check_email, check_username, \ edit_book_read_status, valid_password from .pagination import Pagination from .redirect import get_redirect_location -from .babel import get_available_locale +from .cw_babel import get_available_locale from .usermanagement import login_required_if_no_ano from .kobo_sync_status import remove_synced_book from .render_template import render_title_template @@ -86,6 +86,10 @@ except ImportError: sort = sorted # Just use regular sort then, may cause issues with badly named pages in cbz/cbr files +sql_version = importlib.metadata.version("sqlalchemy") +sqlalchemy_version2 = ([int(x) for x in sql_version.split('.')] >= [2, 0, 0]) + + @app.after_request def add_security_headers(resp): default_src = ([host.strip() for host in config.config_trustedhosts.split(',') if host] + @@ -532,7 +536,7 @@ def render_author_books(page, author_id, order): flash(_("Oops! Selected book is unavailable. File does not exist or is not accessible"), category="error") return redirect(url_for("web.index")) - if constants.sqlalchemy_version2: + if sqlalchemy_version2: author = calibre_db.session.get(db.Authors, author_id) else: author = calibre_db.session.query(db.Authors).get(author_id) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..9ff8bb1f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,127 @@ +[build-system] +requires = ["setuptools>=61.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "calibreweb" +description = "Web app for browsing, reading and downloading eBooks stored in a Calibre database." +authors = [{name = "@OzzieIsaacs", email = "Ozzie.Fernandez.Isaacs@googlemail.com"}] +maintainers = [{name = "@OzzieIsaacs"}] +license = {text = "GPLv3+"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: GNU Affero General Public License v3", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Operating System :: OS Independent", +] +keywords = [ + "calibre", + "calibre-web", + "library", +] +requires-python = ">=3.6" +dependencies = [ + "APScheduler>=3.6.3,<3.11.0", + "Babel>=1.3,<3.0", + "Flask-Babel>=0.11.1,<4.1.0", + "Flask-Principal>=0.3.2,<0.5.1", + "Flask>=1.0.2,<3.1.0", + "iso-639>=0.4.5,<0.5.0", + "PyPDF>=3.15.6,<5.1.0", + "pytz>=2016.10", + "requests>=2.32.0,<2.33.0", + "SQLAlchemy>=1.3.0,<2.1.0", + "tornado>=6.3,<6.5", + "Wand>=0.4.4,<0.7.0", + "unidecode>=0.04.19,<1.4.0", + "lxml>=4.9.1,<5.3.0", + "flask-wtf>=0.14.2,<1.3.0", + "chardet>=3.0.0,<5.3.0", + "netifaces-plus>=0.12.0,<0.13.0", + "urllib3>=1.22,<3.0", + "Flask-Limiter>=2.3.0,<3.9.0", + "regex>=2022.3.2,<2024.6.25", + "bleach>=6.0.0,<6.2.0", + "python-magic>=0.4.27,<0.5.0", + "flask-httpAuth>=4.4.0,<5.0.0", + "cryptography>=30.0.0,<44.0.0", +] +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/janeczku/calibre-web" +"Bug Tracker" = "https://github.com/janeczku/calibre-web/issues" +"Release Management" = "https://github.com/janeczku/calibre-web/releases" +Documentation = "https://github.com/janeczku/calibre-web/wiki" +"Source Code" = "https://github.com/janeczku/calibre-web" + +[project.readme] +file = "README.md" +content-type = "text/markdown" + +[project.optional-dependencies] +gdrive = [ + "google-api-python-client>=1.7.11,<2.200.0", + "gevent>20.6.0,<24.3.0", + "greenlet>=0.4.17,<3.1.0", + "httplib2>=0.9.2,<0.23.0", + "oauth2client>=4.0.0,<4.1.4", + "uritemplate>=3.0.0,<4.2.0", + "pyasn1-modules>=0.0.8,<0.5.0", + "pyasn1>=0.1.9,<0.7.0", + "PyDrive2>=1.3.1,<1.20.0", + "PyYAML>=3.12,<6.1", + "rsa>=3.4.2,<4.10.0", +] +gmail = [ + "google-auth-oauthlib>=0.4.3,<1.3.0", + "google-api-python-client>=1.7.11,<2.200.0", +] +goodreads = [ + "goodreads>=0.3.2,<0.4.0", + "python-Levenshtein>=0.12.0,<0.27.0", +] +ldap = [ + "python-ldap>=3.0.0,<3.5.0", + "Flask-SimpleLDAP>=1.4.0,<2.1.0", +] +oauth = [ + "Flask-Dance>=2.0.0,<7.1.0", + "SQLAlchemy-Utils>=0.33.5,<0.42.0", +] +metadata = [ + "rarfile>=3.2,<5.0", + "scholarly>=1.2.0,<1.8", + "markdown2>=2.0.0,<2.5.0", + "html2text>=2020.1.16,<2024.2.26", + "python-dateutil>=2.1,<2.10.0", + "beautifulsoup4>=4.0.1,<4.13.0", + "faust-cchardet>=2.1.18,<2.1.20", + "py7zr>=0.15.0,<0.21.0", + "mutagen>=1.40.0,<1.50.0", + "pycountry>=20.0.0,<25.0.0", +] +comics = [ + "natsort>=2.2.0,<8.5.0", + "comicapi>=2.2.0,<3.3.0", +] +kobo = [ + "jsonschema>=3.2.0,<4.24.0", +] + +[project.scripts] +cps = "calibreweb:main" + +[tool.setuptools] +include-package-data = true +license-files = ["LICENSE"] + +[tool.setuptools.dynamic] +version = {attr = "calibreweb.cps.constants.STABLE_VERSION"} + diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ee1c63c5..00000000 --- a/setup.cfg +++ /dev/null @@ -1,110 +0,0 @@ -[metadata] -name = calibreweb -url = https://github.com/janeczku/calibre-web -project_urls = - Bug Tracker = https://github.com/janeczku/calibre-web/issues - Release Management = https://github.com/janeczku/calibre-web/releases - Documentation = https://github.com/janeczku/calibre-web/wiki - Source Code = https://github.com/janeczku/calibre-web -description = Web app for browsing, reading and downloading eBooks stored in a Calibre database. -long_description = file: README.md -long_description_content_type = text/markdown -author = @OzzieIsaacs -author_email = Ozzie.Fernandez.Isaacs@googlemail.com -maintainer = @OzzieIsaacs -license = GPLv3+ -license_files = LICENSE -classifiers = - Development Status :: 5 - Production/Stable - License :: OSI Approved :: GNU Affero General Public License v3 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Operating System :: OS Independent -keywords = - calibre - calibre-web - library -python_requires = >=3.5 - -[options.entry_points] -console_scripts = - cps = calibreweb:main - -[options] -include_package_data = True -install_requires = - APScheduler>=3.6.3,<3.11.0 - Babel>=1.3,<3.0 - Flask-Babel>=0.11.1,<4.1.0 - Flask-Principal>=0.3.2,<0.5.1 - Flask>=1.0.2,<3.1.0 - iso-639>=0.4.5,<0.5.0 - PyPDF>=3.15.6,<4.3.0 - pytz>=2016.10 - requests>=2.28.0,<2.32.0 - SQLAlchemy>=1.3.0,<2.1.0 - tornado>=6.3,<6.5 - Wand>=0.4.4,<0.7.0 - unidecode>=0.04.19,<1.4.0 - lxml>=4.9.1,<5.3.0 - flask-wtf>=0.14.2,<1.3.0 - chardet>=3.0.0,<5.3.0 - advocate>=1.0.0,<1.1.0 - Flask-Limiter>=2.3.0,<3.9.0 - regex>=2022.3.2,<2024.6.25 - bleach>=6.0.0,<6.2.0 - python-magic>=0.4.27,<0.5.0 - flask-httpAuth>=4.4.0,<5.0.0 - - -[options.packages.find] -where = src -include = cps/services* - -[options.extras_require] -gdrive = - google-api-python-client>=1.7.11,<2.200.0 - gevent>20.6.0,<24.3.0 - greenlet>=0.4.17,<3.1.0 - httplib2>=0.9.2,<0.23.0 - oauth2client>=4.0.0,<4.1.4 - uritemplate>=3.0.0,<4.2.0 - pyasn1-modules>=0.0.8,<0.5.0 - pyasn1>=0.1.9,<0.7.0 - PyDrive2>=1.3.1,<1.20.0 - PyYAML>=3.12,<6.1 - rsa>=3.4.2,<4.10.0 -gmail = - google-auth-oauthlib>=0.4.3,<1.3.0 - google-api-python-client>=1.7.11,<2.200.0 -goodreads = - goodreads>=0.3.2,<0.4.0 - python-Levenshtein>=0.12.0,<0.26.0 -ldap = - python-ldap>=3.0.0,<3.5.0 - Flask-SimpleLDAP>=1.4.0,<2.1.0 -oauth = - Flask-Dance>=2.0.0,<7.1.0 - SQLAlchemy-Utils>=0.33.5,<0.42.0 -metadata = - rarfile>=3.2,<5.0 - scholarly>=1.2.0,<1.8 - markdown2>=2.0.0,<2.5.0 - html2text>=2020.1.16,<2024.2.26 - python-dateutil>=2.1,<2.10.0 - beautifulsoup4>=4.0.1,<4.13.0 - faust-cchardet>=2.1.18,<2.1.20 - py7zr>=0.15.0,<0.21.0 - mutagen>=1.40.0,<1.50.0 - pycountry>=20.0.0,<25.0.0 -comics = - natsort>=2.2.0,<8.5.0 - comicapi>=2.2.0,<3.3.0 -kobo = - jsonschema>=3.2.0,<4.24.0 - diff --git a/setup.py b/setup.py deleted file mode 100644 index 8dcbee25..00000000 --- a/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web) -# Copyright (C) 2019 decentral1se -# -# 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 . -# -# """Calibre-web distribution package setuptools installer.""" - -from setuptools import setup -import os -import re -import codecs - -here = os.path.abspath(os.path.dirname(__file__)) - -def read(*parts): - with codecs.open(os.path.join(here, *parts), 'r') as fp: - return fp.read() - -def find_version(*file_paths): - version_file = read(*file_paths) - version_match = re.search(r"^STABLE_VERSION\s+=\s+{['\"]version['\"]:\s*['\"](.*)['\"]}", - version_file, re.M) - if version_match: - return version_match.group(1) - raise RuntimeError("Unable to find version string.") - -setup( - version=find_version("src", "calibreweb", "cps", "constants.py") -) diff --git a/test/Calibre-Web TestSummary_Linux.html b/test/Calibre-Web TestSummary_Linux.html index f49d0366..93e4728c 100644 --- a/test/Calibre-Web TestSummary_Linux.html +++ b/test/Calibre-Web TestSummary_Linux.html @@ -37,20 +37,20 @@
-

Start Time: 2024-10-31 19:45:00

+

Start Time: 2024-11-02 08:09:02

-

Stop Time: 2024-11-01 03:02:38

+

Stop Time: 2024-11-02 15:03:00

-

Duration: 6h 11 min

+

Duration: 5h 47 min

@@ -2732,11 +2732,11 @@ IndexError: list index out of range - + TestKoboSync 12 - 12 - 0 + 11 + 1 0 0 @@ -2827,11 +2827,31 @@ IndexError: list index out of range - +
TestKoboSync - test_sync_shelf
- PASS + +
+ FAIL +
+ + + + @@ -3913,49 +3933,43 @@ IndexError: list index out of range - - _ErrorHolder - 1 + + TestPipInstall + 3 + 3 0 0 - 1 0 - Detail + Detail - + -
setUpClass (test_pip_install)
+
TestPipInstall - test_command_start
- -
- ERROR -
- - - + PASS + + + + + + +
TestPipInstall - test_foldername_database_location
+ PASS + + + + + + +
TestPipInstall - test_module_start
+ + PASS @@ -4641,12 +4655,12 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop - + TestUpdater 9 - 8 - 0 - 0 + 3 + 1 + 4 1 Detail @@ -4673,38 +4687,154 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop - +
TestUpdater - test_check_update_stable_errors
- PASS + +
+ ERROR +
+ + + + - +
TestUpdater - test_check_update_stable_versions
- PASS + +
+ ERROR +
+ + + + - +
TestUpdater - test_perform_update
- PASS + +
+ ERROR +
+ + + + - +
TestUpdater - test_perform_update_stable_errors
- PASS + +
+ ERROR +
+ + + + @@ -4744,11 +4874,31 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop - +
TestUpdater - test_update_write_protect
- PASS + +
+ FAIL +
+ + + + @@ -4877,11 +5027,11 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop - + TestUploadEPubs 6 - 6 - 0 + 5 + 1 0 0 @@ -4936,11 +5086,33 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop - +
TestUploadEPubs - test_upload_epub_lang
- PASS + +
+ FAIL +
+ + + + @@ -5841,10 +6013,10 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop Total - 523 - 512 - 0 - 2 + 525 + 508 + 3 + 5 9   @@ -6041,7 +6213,7 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop Werkzeug - 3.1.0 + 3.1.1 Basic @@ -6389,7 +6561,7 @@ FileNotFoundError: [Errno 2] No such file or directory: '/home/ozzie/Develop