aboutsummaryrefslogtreecommitdiff
path: root/app/backends/qwen.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-24 20:17:11 -0400
committerhistoria <historiavg@proton.me>2026-08-24 20:17:11 -0400
commitcf24fa74188cee498eeb7b94422371c952278d4a (patch)
tree302244a8b09b9173fbde542874cfa549f26be7a2 /app/backends/qwen.py
parent0522e73b68291af62e43c387ab8f9b8ffa2cab47 (diff)
downloadtts-audiobook-generator-cf24fa74188cee498eeb7b94422371c952278d4a.tar.gz
fix: stepping back steps in tui
Diffstat (limited to 'app/backends/qwen.py')
-rw-r--r--app/backends/qwen.py128
1 files changed, 92 insertions, 36 deletions
diff --git a/app/backends/qwen.py b/app/backends/qwen.py
index b170eb8..3416ebe 100644
--- a/app/backends/qwen.py
+++ b/app/backends/qwen.py
@@ -58,59 +58,98 @@ def _config_port(url: str, fallback: int) -> int:
def _wizard(stdscr, args: argparse.Namespace) -> Optional[dict]:
- """Linear TUI wizard collecting every qwen-setup decision."""
+ """Linear TUI wizard collecting every qwen-setup decision.
+
+ Driven by ``tui.Wizard`` as a stack of screen closures: each screen
+ shows one widget and returns the next screen, ``Wizard.BACK`` (Esc/q —
+ pop to the previous screen), or the settings dict. Steps whose value is
+ already provided by a flag (``--port-custom``, ``--port-clone``,
+ ``--speaker``, ``--skip-install``) are folded into the ``_after_*``
+ guards and never become screens, so Esc always lands on the previous
+ real screen. Esc on the first screen aborts the wizard.
+ """
_GO_BACK = object()
-
- def confirm(question: str, default: bool = True) -> Optional[bool]:
- res = tui.confirm(stdscr, question, default=default,
- cancel_value=_GO_BACK)
- return None if res is _GO_BACK else res
-
- # Step 0: pip install (if not installed and not skipped).
- do_install = False
- if not _is_installed() and not args.skip_install:
- choice = confirm("qwen-tts is not installed. pip install it now?",
- default=True)
- if choice is None:
- return None
- do_install = choice
-
- # Step 1: ports.
- custom_port = args.port_custom
- if custom_port is None:
+ s: dict = {}
+
+ def screen_install():
+ choice = tui.confirm(stdscr, "qwen-tts is not installed. "
+ "pip install it now?", default=True,
+ cancel_value=_GO_BACK)
+ if choice is _GO_BACK:
+ return tui.Wizard.BACK
+ s["do_install"] = choice
+ return _after_install()
+
+ def _after_install():
+ if args.port_custom is None:
+ return screen_custom_port
+ s["custom_port"] = args.port_custom
+ return _after_custom_port()
+
+ def screen_custom_port():
port_text = tui.line_edit(
stdscr, "CustomVoice (built-in speaker) port",
str(_config_port(config.QWEN_API_URL, DEFAULT_CUSTOM_PORT)),
validate=lambda s: None if (s.isdigit() and 1 <= int(s) <= 65535)
else "Enter a port number between 1 and 65535",
- help_lines=["The port for qwen-tts-demo CustomVoice (speaker mode)"])
- custom_port = int(port_text)
- clone_port = args.port_clone
- if clone_port is None:
+ help_lines=["The port for qwen-tts-demo CustomVoice (speaker mode)"],
+ back_value=_GO_BACK)
+ if port_text is _GO_BACK:
+ return tui.Wizard.BACK
+ s["custom_port"] = int(port_text)
+ return _after_custom_port()
+
+ def _after_custom_port():
+ if args.port_clone is None:
+ return screen_clone_port
+ s["clone_port"] = args.port_clone
+ return _after_clone_port()
+
+ def screen_clone_port():
port_text = tui.line_edit(
stdscr, "Base (voice clone) port",
str(_config_port(config.CLONE_API_URL, DEFAULT_CLONE_PORT)),
validate=lambda s: None if (s.isdigit() and 1 <= int(s) <= 65535)
else "Enter a port number between 1 and 65535",
- help_lines=["The port for qwen-tts-demo Base (voice cloning)"])
- clone_port = int(port_text)
-
- # Step 2: built-in speaker.
- speaker = args.speaker
- if speaker is None:
+ help_lines=["The port for qwen-tts-demo Base (voice cloning)"],
+ back_value=_GO_BACK)
+ if port_text is _GO_BACK:
+ return tui.Wizard.BACK
+ s["clone_port"] = int(port_text)
+ return _after_clone_port()
+
+ def _after_clone_port():
+ if args.speaker is None:
+ return screen_speaker
+ s["speaker"] = args.speaker
+ return _finalize()
+
+ def screen_speaker():
speaker = tui.menu(
stdscr, "Built-in CustomVoice speaker",
[(s, s) for s in QWEN_SPEAKERS],
default_index=max(0, QWEN_SPEAKERS.index(config.SPEAKER)
if config.SPEAKER in QWEN_SPEAKERS else 0),
- help_lines=["Used by audiobook.py --backend qwen without --clone"])
+ help_lines=["Used by audiobook.py --backend qwen without --clone"],
+ back_value=_GO_BACK)
+ if speaker is _GO_BACK:
+ return tui.Wizard.BACK
+ s["speaker"] = speaker
+ return _finalize()
+
+ def _finalize() -> dict:
+ return {
+ "do_install": s.get("do_install", False),
+ "custom_port": s["custom_port"],
+ "clone_port": s["clone_port"],
+ "speaker": s["speaker"],
+ }
- return {
- "do_install": do_install,
- "custom_port": custom_port,
- "clone_port": clone_port,
- "speaker": speaker,
- }
+ if not _is_installed() and not args.skip_install:
+ first = screen_install
+ else:
+ first = _after_install()
+ return tui.Wizard().run(first)
def _execute(settings: dict) -> int:
@@ -160,6 +199,23 @@ def _print_launch_hint(custom_port: int, clone_port: int) -> None:
print("Then run: python audiobook.py --backend qwen")
+def setup_screen(stdscr) -> int:
+ """Run the setup wizard on an existing curses screen (the hub's).
+
+ The hub drives this as one screen of its own ``tui.Wizard`` stack, so
+ Esc on the wizard's first screen simply returns here and the hub pops
+ back to the menu that launched it. The console tail (pip install /
+ config sync) runs under ``tui.suspend`` so the hub's curses session
+ stays intact. Returns 0 on completion, 1 when the user aborted.
+ """
+ args = build_parser().parse_args([])
+ settings = _wizard(stdscr, args)
+ if settings is None:
+ return 1
+ with tui.suspend(stdscr):
+ return _execute(settings)
+
+
def run_tui(args: Optional[argparse.Namespace] = None) -> int:
"""Run the qwen setup wizard end-to-end."""
import curses