diff options
| author | historia <historiavg@proton.me> | 2026-08-24 04:53:43 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-24 04:53:43 -0400 |
| commit | 0512a932bbf3b87619b6a250f87eb7904d22d1f7 (patch) | |
| tree | dd2b0eb45823c0690ad2bc86f5aa9be2771f5b17 /app/ui/hub.py | |
| parent | 9fb2434dcc8a1ed7bd085b453859d473de64448a (diff) | |
| download | tts-audiobook-generator-0512a932bbf3b87619b6a250f87eb7904d22d1f7.tar.gz | |
feat: hide convert/config/start-stop menus if no valid installation
Diffstat (limited to 'app/ui/hub.py')
| -rw-r--r-- | app/ui/hub.py | 92 |
1 files changed, 62 insertions, 30 deletions
diff --git a/app/ui/hub.py b/app/ui/hub.py index c3efce6..5f3d039 100644 --- a/app/ui/hub.py +++ b/app/ui/hub.py @@ -79,20 +79,20 @@ def _hub_menu(stdscr) -> Optional[tuple]: while True: statuses = detect_all() options = [("Set up a backend", "setup")] + # Converting works against an external (remote) server too, but + # configuring one and starting/stopping its servers need it on + # this machine. if any(st.installed or st.running for st in statuses): options.insert(0, ("Convert books", "convert")) + if any(st.installed for st in statuses): options.append(("Configure a backend", "configure")) options.append(("Start/Stop Backend Servers", "server")) options.append(("Settings", "settings")) options.append(("Quit", "quit")) - rows = [(st.label, *_status_mark(st)) for st in statuses] - notice_lines = None - if shutil.which("ffmpeg") is None: - notice_lines = [("Warning: ffmpeg not installed!", "err")] choice = tui.menu( stdscr, "tts-audiobook-generator", options, - table_title="Backend status", table_rows=rows, - notice_lines=notice_lines) + table_title="Backend status", table_rows=_status_rows(statuses), + notice_lines=_notice_lines()) if choice is None or choice == "quit": return None if choice == "convert": @@ -117,13 +117,14 @@ def _hub_menu(stdscr) -> Optional[tuple]: def _setup_menu(stdscr, statuses) -> Optional[tuple]: """Pick a backend to set up. Returns ("setup", key) or None to go back.""" - by_key = {st.key: st for st in statuses} - options = [(f"{info.label} ({_status_mark(by_key.get(info.key))[0]})", - info.key) for info in REGISTRY] + options = [(info.label, info.key) for info in REGISTRY] choice = tui.menu(stdscr, "Set up a backend", options, back_value=_GO_BACK, help_lines=["Clone/build/install a backend so you can " - "convert with it."]) + "convert with it."], + table_title="Backend status", + table_rows=_status_rows(statuses), + notice_lines=_notice_lines()) if choice is _GO_BACK or choice is None: return None return ("setup", choice) @@ -140,7 +141,11 @@ def _configure_menu(stdscr, statuses) -> Optional[tuple]: "backend' first.") return None options = [(info.label, info.key) for info in installed] - key = tui.menu(stdscr, "Configure a backend", options, back_value=_GO_BACK) + key = tui.menu(stdscr, "Configure a backend", options, + back_value=_GO_BACK, + table_title="Backend status", + table_rows=_status_rows(statuses), + notice_lines=_notice_lines()) if key is _GO_BACK or key is None: return None info = get(key) @@ -161,36 +166,57 @@ def _status_mark(status: Optional[BackendStatus]) -> Tuple[str, str, str]: otherwise 'installed' (orange/warn) when the backend is present on disk, or 'unavailable' (red/err). A backend that is neither installed nor running is unusable, so its name is dimmed (NAME_KIND). + A running server the hub did not start itself (no live pid file for any + of its specs — see ``servers.manages``) is tagged "[remote]"; a + multi-model backend (qwen) also names which models answered in + parentheses, e.g. "running [remote] (Base, CustomVoice)". CURSES has no true orange, so the theme's yellow 'warn' is used; it renders amber/orange on most terminals. """ if status is not None and status.running: - return ("running", "ok", "body") + text = "running" + if not status.managed: + text += " [remote]" + if status.running_models: + text += " (" + ", ".join(status.running_models) + ")" + return (text, "ok", "body") if status is not None and status.installed: return ("installed", "warn", "body") return ("unavailable", "err", "dim") +def _status_rows(statuses) -> list: + """Status-table rows for tui.menu: (label, status, kind, name_kind). + + One row per detected backend, in detect order — the same table the + main menu shows, reused on each flow's first picker screen so the + backend states stay visible there. + """ + return [(st.label, *_status_mark(st)) for st in statuses] + + +def _notice_lines() -> Optional[list]: + """Warning lines shown above the status table, or None when all good.""" + if shutil.which("ffmpeg") is None: + return [("Warning: ffmpeg not installed!", "err")] + return None + + def _convert_menu(stdscr, statuses) -> Optional[tuple]: """Pick an available backend and collect per-backend run settings.""" available = [st for st in statuses if st.ready or st.running] - options = [(st.label, st.key) for st in available] if not available: - choice = tui.menu( - stdscr, "No backend is available", - [("Set up a backend", "__setup__")], - help_lines=["Set up a backend (clone/build/configure) before " - "converting."]) - if choice == "__setup__": - return _setup_menu(stdscr, statuses) - return None - options.append(("Set up a backend", "__setup__")) + tui.flash(stdscr, "No backend is ready to convert with yet — use " + "'Set up a backend' first.") + return None + options = [(st.label, st.key) for st in available] + table = {"table_title": "Backend status", + "table_rows": _status_rows(statuses), + "notice_lines": _notice_lines()} key = tui.menu(stdscr, "Convert books with...", options, - back_value=_GO_BACK) + back_value=_GO_BACK, **table) if key is _GO_BACK or key is None: return None - if key == "__setup__": - return _setup_menu(stdscr, statuses) if key == BACKEND_AUDIOCPP: cmd = _convert_audiocpp(stdscr, statuses) elif key == BACKEND_QWEN: @@ -406,7 +432,7 @@ def _settings_menu(stdscr) -> None: "validate": _validate_port, "note": "Ports apply to servers this tool starts and detecting " "local servers"}, - {"key": "qwen_clone_port", "label": "qwen-tts Base (clone) port", + {"key": "qwen_clone_port", "label": "qwen-tts Base port", "kind": "text", "value": str(_port_from_url(config.CLONE_API_URL, 7861)), "validate": _validate_port}, @@ -661,14 +687,20 @@ def _run_server_action(spec_name: str, action: str) -> None: def _server_menu(stdscr, statuses) -> Optional[tuple]: """Pick a backend, then one of its servers and a Start/Stop action.""" - candidates = [st for st in statuses if st.servers or st.running] + # Only backends installed on this machine: a merely-running external + # server cannot be stopped from here (stop() refuses without our pid + # file), so listing it would dead-end. + candidates = [st for st in statuses if st.installed] if not candidates: - tui.flash(stdscr, "No backend with a server is available. " - "Set one up first.") + tui.flash(stdscr, "No backend is installed yet — use 'Set up a " + "backend' first.") return None options = [(st.label, st.key) for st in candidates] key = tui.menu(stdscr, "Start / Stop a server", options, - back_value=_GO_BACK) + back_value=_GO_BACK, + table_title="Backend status", + table_rows=_status_rows(statuses), + notice_lines=_notice_lines()) if key is _GO_BACK or key is None: return None status = next((s for s in statuses if s.key == key), None) |
