Files

162 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Calibre-Web Automated fork of Calibre-Web
# Copyright (C) 2018-2026 Calibre-Web contributors
# Copyright (C) 2024-2026 Calibre-Web Automated contributors
# SPDX-License-Identifier: GPL-3.0-or-later
# See CONTRIBUTORS for full list of authors.
import json
import os
import shutil
import sqlite3
import sys
import subprocess
def main():
auto_lib = AutoLibrary()
auto_lib.check_for_app_db()
if auto_lib.check_for_existing_library():
auto_lib.set_library_location()
else: # No existing library found
auto_lib.make_new_library()
auto_lib.set_library_location()
print(f"[cwa-auto-library] Library location successfully set to: {auto_lib.lib_path}")
sys.exit(0)
class AutoLibrary:
def __init__(self):
self.config_dir = "/config"
self.library_dir = "/calibre-library"
self.dirs_path = "/app/calibre-web-automated/dirs.json"
self.app_db = "/config/app.db"
self.empty_appdb = "/app/calibre-web-automated/empty_library/app.db"
self.empty_metadb = "/app/calibre-web-automated/empty_library/metadata.db"
self.metadb_path = None
self.lib_path = None
@property #getter
def metadb_path(self):
return self._metadb_path
@metadb_path.setter
def metadb_path(self, path):
if path is None:
self._metadb_path = None
self.lib_path = None
else:
self._metadb_path = path
self.lib_path = os.path.dirname(path)
# Checks config_dir for an existing app.db, if one doesn't already exist it copies an empty one from /app/calibre-web-automated/empty_library/app.db and sets the permissions
def check_for_app_db(self):
files_in_config = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(self.config_dir) for f in filenames]
db_files = [f for f in files_in_config if "app.db" in f]
if len(db_files) == 0:
print(f"[cwa-auto-library] No app.db found in {self.config_dir}, copying from /app/calibre-web-automated/empty_library/app.db")
shutil.copyfile(self.empty_appdb, f"{self.config_dir}/app.db")
try:
nsm = os.getenv("NETWORK_SHARE_MODE", "false").strip().lower() in ("1", "true", "yes", "on")
if not nsm:
subprocess.run(["chown", "-R", "abc:abc", self.config_dir], check=True)
else:
print(f"[cwa-auto-library] NETWORK_SHARE_MODE=true detected; skipping chown of {self.config_dir}", flush=True)
except subprocess.CalledProcessError as e:
print(f"[cwa-auto-library] An error occurred while attempting to recursively set ownership of {self.config_dir} to abc:abc. See the following error:\n{e}", flush=True)
print(f"[cwa-auto-library] app.db successfully copied to {self.config_dir}")
else:
return
# Check for a metadata.db file in the given library dir and returns False if one doesn't exist
# and True if one does exist, while also updating metadb_path to the path of the found metadata.db file
# In the case of multiple metadata.db files, the user is notified and the one with the largest filesize is chosen
def check_for_existing_library(self) -> bool:
files_in_library = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(self.library_dir) for f in filenames]
# Consider metadata.db files across subfolders, but ignore SQLite sidecars created by WAL/journal modes
db_files = []
for f in files_in_library:
base = os.path.basename(f)
if "metadata.db" in base and not (base.endswith("-wal") or base.endswith("-shm") or base.endswith("-journal")):
db_files.append(f)
if len(db_files) == 1:
self.metadb_path = db_files[0]
print(f"[cwa-auto-library]: Existing library found at {self.lib_path}, mounting now...")
return True
elif len(db_files) > 1:
print("[cwa-auto-library]: Multiple metadata.db files found in library directory:\n")
for db in db_files:
print(f" - {db} | Size: {os.path.getsize(db)}")
db_sizes = [os.path.getsize(f) for f in db_files]
index_of_biggest_db = max(range(len(db_sizes)), key=db_sizes.__getitem__)
self.metadb_path = db_files[index_of_biggest_db]
print(f"\n[cwa-auto-library]: Automatically mounting the largest database using the following db file - {db_files[index_of_biggest_db]} ...")
print("\n[cwa-auto-library]: If this is unwanted, please ensure only 1 metadata.db file / only your desired Calibre Database exists in '/calibre-library', then restart the container")
return True
else:
return False
# Sets the library's location in both dirs.json and the CW db
def set_library_location(self):
if self.metadb_path is not None and os.path.exists(self.metadb_path):
self.update_dirs_json()
self.update_calibre_web_db()
return
else:
print("[cwa-auto-library]: ERROR: metadata.db found but not mounted")
sys.exit(1)
# Uses sql to update CW's app.db with the correct library location (config_calibre_dir in the settings table)
def update_calibre_web_db(self):
if os.path.exists(self.metadb_path): # type: ignore
try:
print("[cwa-auto-library]: Updating Settings Database with library location...")
con = sqlite3.connect(self.app_db, timeout=30)
cur = con.cursor()
cur.execute(f'UPDATE settings SET config_calibre_dir="{self.lib_path}";')
con.commit()
return
except Exception as e:
print("[cwa-auto-library]: ERROR: Could not update Calibre Web Database")
print(e)
sys.exit(1)
else:
print(f"[cwa-auto-library]: ERROR: app.db in {self.app_db} not found")
sys.exit(1)
# Update the dirs.json file with the new library location (lib_path))
def update_dirs_json(self):
"""Updates the location of the calibre library stored in dirs.json with the found library"""
try:
print("[cwa-auto-library] Updating dirs.json with new library location...")
with open(self.dirs_path) as f:
dirs = json.load(f)
dirs["calibre_library_dir"] = self.lib_path
with open(self.dirs_path, 'w') as f:
json.dump(dirs, f, indent=4)
return
except Exception as e:
print("[cwa-auto-library]: ERROR: Could not update dirs.json")
print(e)
sys.exit(1)
# Uses the empty metadata.db in /app/calibre-web-automated to create a new library
def make_new_library(self):
print("[cwa-auto-library]: No existing library found. Creating new library...")
shutil.copyfile(self.empty_metadb, f"{self.library_dir}/metadata.db")
try:
nsm = os.getenv("NETWORK_SHARE_MODE", "false").strip().lower() in ("1", "true", "yes", "on")
if not nsm:
subprocess.run(["chown", "-R", "abc:abc", self.library_dir], check=True)
else:
print(f"[cwa-auto-library] NETWORK_SHARE_MODE=true detected; skipping chown of {self.library_dir}", flush=True)
except subprocess.CalledProcessError as e:
print(f"[cwa-auto-library] An error occurred while attempting to recursively set ownership of {self.library_dir} to abc:abc. See the following error:\n{e}", flush=True)
self.metadb_path = f"{self.library_dir}/metadata.db"
return
if __name__ == '__main__':
main()