234 lines
8.2 KiB
Python
234 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Calibre-Web Automated – fork of Calibre-Web
|
||
# Copyright (C) 2018-2025 Calibre-Web contributors
|
||
# Copyright (C) 2024-2025 Calibre-Web Automated contributors
|
||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||
# See CONTRIBUTORS for full list of authors.
|
||
|
||
import sys
|
||
import os
|
||
from collections import namedtuple
|
||
|
||
from flask_babel import gettext as _
|
||
|
||
# APP_MODE - production, development, or test
|
||
APP_MODE = os.environ.get('APP_MODE', 'production')
|
||
|
||
# if installed via pip this variable is set to true (empty file with name .HOMEDIR present)
|
||
HOME_CONFIG = os.path.isfile(os.path.join(os.path.dirname(os.path.abspath(__file__)), '.HOMEDIR'))
|
||
|
||
# In executables updater is not available, so variable is set to False there
|
||
UPDATER_AVAILABLE = False
|
||
|
||
# Base dir is parent of current file, necessary if called from different folder
|
||
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
|
||
# if executable file the files should be placed in the parent dir (parallel to the exe file)
|
||
|
||
STATIC_DIR = os.path.join(BASE_DIR, 'cps', 'static')
|
||
TEMPLATES_DIR = os.path.join(BASE_DIR, 'cps', 'templates')
|
||
TRANSLATIONS_DIR = os.path.join(BASE_DIR, 'cps', 'translations')
|
||
|
||
# Cache dir - use CACHE_DIR environment variable, otherwise use the default directory: cps/cache
|
||
DEFAULT_CACHE_DIR = os.path.join(BASE_DIR, 'cps', 'cache')
|
||
CACHE_DIR = os.environ.get('CACHE_DIR', DEFAULT_CACHE_DIR)
|
||
|
||
OAUTH_SSL_STRICT = os.environ.get('OAUTH_SSL_STRICT', "1").lower() in ("true", "1")
|
||
|
||
if HOME_CONFIG:
|
||
home_dir = os.path.join(os.path.expanduser("~"), ".calibre-web-automated")
|
||
if not os.path.exists(home_dir):
|
||
os.makedirs(home_dir)
|
||
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', home_dir)
|
||
else:
|
||
CONFIG_DIR = os.environ.get('CALIBRE_DBPATH', BASE_DIR)
|
||
if getattr(sys, 'frozen', False):
|
||
CONFIG_DIR = os.path.abspath(os.path.join(CONFIG_DIR, os.pardir))
|
||
|
||
|
||
DEFAULT_SETTINGS_FILE = "app.db"
|
||
DEFAULT_GDRIVE_FILE = "gdrive.db"
|
||
|
||
ROLE_USER = 0 << 0
|
||
ROLE_ADMIN = 1 << 0
|
||
ROLE_DOWNLOAD = 1 << 1
|
||
ROLE_UPLOAD = 1 << 2
|
||
ROLE_EDIT = 1 << 3
|
||
ROLE_PASSWD = 1 << 4
|
||
ROLE_ANONYMOUS = 1 << 5
|
||
ROLE_EDIT_SHELFS = 1 << 6
|
||
ROLE_DELETE_BOOKS = 1 << 7
|
||
ROLE_VIEWER = 1 << 8
|
||
|
||
ALL_ROLES = {
|
||
"admin_role": ROLE_ADMIN,
|
||
"download_role": ROLE_DOWNLOAD,
|
||
"upload_role": ROLE_UPLOAD,
|
||
"edit_role": ROLE_EDIT,
|
||
"passwd_role": ROLE_PASSWD,
|
||
"edit_shelf_role": ROLE_EDIT_SHELFS,
|
||
"delete_role": ROLE_DELETE_BOOKS,
|
||
"viewer_role": ROLE_VIEWER,
|
||
}
|
||
|
||
DETAIL_RANDOM = 1 << 0
|
||
SIDEBAR_LANGUAGE = 1 << 1
|
||
SIDEBAR_SERIES = 1 << 2
|
||
SIDEBAR_CATEGORY = 1 << 3
|
||
SIDEBAR_HOT = 1 << 4
|
||
SIDEBAR_RANDOM = 1 << 5
|
||
SIDEBAR_AUTHOR = 1 << 6
|
||
SIDEBAR_BEST_RATED = 1 << 7
|
||
SIDEBAR_READ_AND_UNREAD = 1 << 8
|
||
SIDEBAR_RECENT = 1 << 9
|
||
SIDEBAR_SORTED = 1 << 10
|
||
MATURE_CONTENT = 1 << 11
|
||
SIDEBAR_PUBLISHER = 1 << 12
|
||
SIDEBAR_RATING = 1 << 13
|
||
SIDEBAR_FORMAT = 1 << 14
|
||
SIDEBAR_ARCHIVED = 1 << 15
|
||
SIDEBAR_DOWNLOAD = 1 << 16
|
||
SIDEBAR_LIST = 1 << 17
|
||
SIDEBAR_DUPLICATES = 1 << 18
|
||
|
||
sidebar_settings = {
|
||
"detail_random": DETAIL_RANDOM,
|
||
"sidebar_language": SIDEBAR_LANGUAGE,
|
||
"sidebar_series": SIDEBAR_SERIES,
|
||
"sidebar_category": SIDEBAR_CATEGORY,
|
||
"sidebar_random": SIDEBAR_RANDOM,
|
||
"sidebar_author": SIDEBAR_AUTHOR,
|
||
"sidebar_best_rated": SIDEBAR_BEST_RATED,
|
||
"sidebar_read_and_unread": SIDEBAR_READ_AND_UNREAD,
|
||
"sidebar_recent": SIDEBAR_RECENT,
|
||
"sidebar_sorted": SIDEBAR_SORTED,
|
||
"sidebar_publisher": SIDEBAR_PUBLISHER,
|
||
"sidebar_rating": SIDEBAR_RATING,
|
||
"sidebar_format": SIDEBAR_FORMAT,
|
||
"sidebar_archived": SIDEBAR_ARCHIVED,
|
||
"sidebar_download": SIDEBAR_DOWNLOAD,
|
||
"sidebar_list": SIDEBAR_LIST,
|
||
"sidebar_duplicates": SIDEBAR_DUPLICATES,
|
||
}
|
||
|
||
|
||
ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS
|
||
ADMIN_USER_SIDEBAR = (SIDEBAR_DUPLICATES << 1) - 1
|
||
|
||
UPDATE_STABLE = 0 << 0
|
||
AUTO_UPDATE_STABLE = 1 << 0
|
||
UPDATE_NIGHTLY = 1 << 1
|
||
AUTO_UPDATE_NIGHTLY = 1 << 2
|
||
|
||
LOGIN_STANDARD = 0
|
||
LOGIN_LDAP = 1
|
||
LOGIN_OAUTH = 2
|
||
|
||
LDAP_AUTH_ANONYMOUS = 0
|
||
LDAP_AUTH_UNAUTHENTICATE = 1
|
||
LDAP_AUTH_SIMPLE = 0
|
||
|
||
DEFAULT_MAIL_SERVER = "mail.example.org"
|
||
|
||
DEFAULT_PASSWORD = "admin123" # nosec
|
||
DEFAULT_PORT = 8083
|
||
env_CWA_PORT_OVERRIDE = os.environ.get("CWA_PORT_OVERRIDE")
|
||
if env_CWA_PORT_OVERRIDE:
|
||
try:
|
||
DEFAULT_PORT = int(env_CWA_PORT_OVERRIDE)
|
||
except (ValueError, TypeError):
|
||
print(f"Environment variable CWA_PORT_OVERRIDE has invalid value ('{env_CWA_PORT_OVERRIDE}'), falling back to default (8083)")
|
||
DEFAULT_PORT = 8083
|
||
|
||
|
||
EXTENSIONS_AUDIO = {'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac', 'm4a', 'm4b'}
|
||
EXTENSIONS_CONVERT_FROM = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf',
|
||
'txt', 'htmlz', 'rtf', 'odt', 'cbz', 'cbr', 'prc', 'acsm']
|
||
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', 'cb7', 'djvu', 'djv',
|
||
'prc', 'doc', 'docx', 'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg',
|
||
'opus', 'wav', 'flac', 'm4a', 'm4b', 'acsm'}
|
||
|
||
_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))
|
||
|
||
|
||
def selected_roles(dictionary):
|
||
return sum(v for k, v in ALL_ROLES.items() if k in dictionary)
|
||
|
||
|
||
# :rtype: BookMeta
|
||
BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, description, tags, series, '
|
||
'series_id, languages, publisher, pubdate, identifiers')
|
||
|
||
def _read_text(path: str, default: str = "") -> str:
|
||
try:
|
||
with open(path, 'r') as f:
|
||
return f.read().strip()
|
||
except Exception:
|
||
return default
|
||
|
||
# Versions are resolved at container startup by cwa-init and provided via env and persisted files.
|
||
# Avoid any network or slow I/O during module import.
|
||
INSTALLED_VERSION = os.environ.get("CWA_INSTALLED_VERSION") or _read_text("/app/CWA_RELEASE", "V0.0.0")
|
||
STABLE_VERSION = os.environ.get("CWA_STABLE_VERSION") or _read_text("/app/CWA_STABLE_RELEASE", "V0.0.0")
|
||
|
||
USER_AGENT = f"Calibre-Web-Automated/{INSTALLED_VERSION}"
|
||
|
||
NIGHTLY_VERSION = dict()
|
||
NIGHTLY_VERSION[0] = '0af52f205358b0147ee3430f9e6c8fe007c0ea77'
|
||
NIGHTLY_VERSION[1] = '2024-11-16T07:21:28+01:00'
|
||
|
||
# CACHE
|
||
CACHE_TYPE_THUMBNAILS = 'thumbnails'
|
||
|
||
# Thumbnail Types
|
||
THUMBNAIL_TYPE_COVER = 1
|
||
THUMBNAIL_TYPE_SERIES = 2
|
||
THUMBNAIL_TYPE_AUTHOR = 3
|
||
|
||
# Thumbnails Sizes
|
||
COVER_THUMBNAIL_ORIGINAL = 0
|
||
COVER_THUMBNAIL_SMALL = 1
|
||
COVER_THUMBNAIL_MEDIUM = 2
|
||
COVER_THUMBNAIL_LARGE = 4
|
||
|
||
# clean-up the module namespace
|
||
del sys, os, namedtuple
|
||
|
||
# Mapping of language codes to full language names
|
||
LANGUAGE_NAMES = {
|
||
"cs": _("Czech"),
|
||
"de": _("German"),
|
||
"el": _("Greek"),
|
||
"es": _("Spanish"),
|
||
"fi": _("Finnish"),
|
||
"fr": _("French"),
|
||
"gl": _("Galician"),
|
||
"hu": _("Hungarian"),
|
||
"id": _("Indonesian"),
|
||
"it": _("Italian"),
|
||
"ja": _("Japanese"),
|
||
"km": _("Khmer"),
|
||
"ko": _("Korean"),
|
||
"nl": _("Dutch"),
|
||
"no": _("Norwegian"),
|
||
"pl": _("Polish"),
|
||
"pt": _("Portuguese"),
|
||
"pt_BR": _("Portuguese (Brazil)"),
|
||
"ru": _("Russian"),
|
||
"sk": _("Slovak"),
|
||
"sl": _("Slovenian"),
|
||
"sv": _("Swedish"),
|
||
"tr": _("Turkish"),
|
||
"uk": _("Ukrainian"),
|
||
"vi": _("Vietnamese"),
|
||
"zh_Hans_CN": _("Chinese (Simplified, China)"),
|
||
"zh_Hant_TW": _("Chinese (Traditional, Taiwan)"),
|
||
} |