"""diora-tui: terminal EPUB reader for diora, syncing books + reading position against a diora server (see project README).""" from __future__ import annotations import argparse import asyncio from pathlib import Path from textual.app import App from . import config as config_mod from . import login as login_mod, remote from .library_screen import LibraryScreen DEFAULT_LIBRARY = Path.home() / "Books" class DioraTuiApp(App): CSS_PATH = "app.tcss" TITLE = "diora-tui" def __init__(self, library_dir: Path) -> None: super().__init__() self.library_dir = library_dir def on_mount(self) -> None: self.push_screen(LibraryScreen(self.library_dir)) async def action_quit(self) -> None: cfg = config_mod.load() if cfg is not None: try: # Sends what is queued, then pulls: nothing read stays only here. await asyncio.to_thread(remote.sync_library, self.library_dir, cfg) except remote.SyncError: pass # best-effort — don't block quitting on a sync failure self.exit() def run_login(server: str | None, *, paste_key: bool) -> None: try: cfg = login_mod.login(server, paste_key=paste_key) except login_mod.LoginError as e: raise SystemExit(f"Fehler: {e}") config_mod.save(cfg) print("Gespeichert (Token und Schlüssel geschützt). Weiter mit diora-tui oder diora-tui sync.") def run_sync(library_dir: Path) -> None: cfg = config_mod.load() if cfg is None: raise SystemExit("Nicht angemeldet: erst diora-tui login") print(f"Verbinde zu {cfg.server_url} …") try: result = remote.sync_library(library_dir, cfg) except remote.SyncError as e: raise SystemExit(f"Fehler: {e}") print(f"{result.books} EPUB(s) auf dem Server (geladen wird beim Öffnen)." ) if result.pushed: print(f"Lesestand von {result.pushed} Buch/Bücher an den Server geschickt.") for title in result.new_titles: print(f" + {title}") if result.skipped_pdf: print(f"{len(result.skipped_pdf)} PDF(s) übersprungen (TUI liest nur EPUB): {', '.join(result.skipped_pdf)}") if result.failed: print(f"{len(result.failed)} Buch/Bücher nicht entschlüsselbar (falscher Schlüssel?): {', '.join(result.failed)}") def main() -> None: parser = argparse.ArgumentParser(description="diora-tui — EPUB-Reader im Terminal, synct mit diora") parser.add_argument( "--library", type=Path, default=DEFAULT_LIBRARY, help=f"Verzeichnis mit EPUB-Dateien (Standard: {DEFAULT_LIBRARY})", ) subparsers = parser.add_subparsers(dest="command") login_parser = subparsers.add_parser("login", help="Einmal anmelden (Token + Schlüssel, geschützt gespeichert)") login_parser.add_argument("--server", help=f"Server-URL (Standard: {login_mod.DEFAULT_SERVER})") login_parser.add_argument("--paste-key", action="store_true", help="Buchschlüssel einfügen statt aus dem Passwort ableiten") subparsers.add_parser("logout", help="Gespeicherte Zugangsdaten löschen") subparsers.add_parser("sync", help="Bücherliste und Lesestand abgleichen (ohne die Oberfläche)") args = parser.parse_args() library_dir = args.library.expanduser() if args.command == "login": run_login(args.server, paste_key=args.paste_key) return if args.command == "logout": print("Zugangsdaten gelöscht." if config_mod.forget() else "Keine Zugangsdaten gespeichert.") return if args.command == "sync": run_sync(library_dir) return DioraTuiApp(library_dir).run() if __name__ == "__main__": main()