From 6ccb6d443d2fb871b43d96ea61a95bc3e6a92355 Mon Sep 17 00:00:00 2001 From: historia Date: Wed, 26 Aug 2026 17:46:48 -0400 Subject: feat: combined install/configure tui screens into one menu, removed extraneous wizard screens --- app/backends/audiocpp/wizard.py | 550 ++++++++++++++++++++-------------------- 1 file changed, 280 insertions(+), 270 deletions(-) (limited to 'app/backends/audiocpp/wizard.py') diff --git a/app/backends/audiocpp/wizard.py b/app/backends/audiocpp/wizard.py index dcab273..2034827 100644 --- a/app/backends/audiocpp/wizard.py +++ b/app/backends/audiocpp/wizard.py @@ -6,6 +6,10 @@ import sys from pathlib import Path from typing import Callable, Dict, List, Optional, Tuple +# Alias kept on this module: main()'s tty check and its tests patch it +# here. +from backends.setup import interactive as _interactive + from backends import common from backends.common import ( APP_DIR, @@ -16,8 +20,6 @@ from backends.common import ( find_wav_files, read_prompt_text, resolve_wav_dir_arg, - wav_dir_info as _wav_dir_info, - wav_dir_preview as _wav_dir_preview, write_prompt_text, ) from converter import config @@ -26,12 +28,12 @@ from . import build as _build from . import configsync as _configsync from . import models as _models from . import voices as _voices -from .catalog import (BACKENDS, DEFAULT_HOST, _backend_options, - build_model_entry, build_server_config, detect_backend, +from .catalog import (_backend_options, build_model_entry, + build_server_config, detect_backend, load_model_catalog, load_server_config, package_dir_options, server_config_selections) -from .constants import (AUDIOCPP_DIR_NAME, AUDIOCPP_GIT_URL, - TASK_TTS, TASK_VDES) +from .constants import (AUDIOCPP_DIR_NAME, AUDIOCPP_GIT_URL, BACKENDS, + DEFAULT_HOST, TASK_TTS, TASK_VDES) _GO_BACK = object() @@ -58,10 +60,6 @@ class _TuiError(Exception): """ -# Alias kept on this module: main()'s tty check and its tests patch it -# here. -from backends.setup import interactive as _interactive - def _build_entries(family_keys: List[str], chosen: Dict[str, List[dict]], catalog_by_family: Dict[str, dict], @@ -147,38 +145,66 @@ def _write_and_advise(audiocpp_dir: Path, wav_dir: Optional[Path], f"{'entry' if count == 1 else 'entries'}.") +def _transcription_choices(wav_files: list, existing: Dict[str, str], + prompt_exists: bool) -> Tuple[list, str]: + """Shape the transcription question for the setup form. + + Returns ``(choices, default_mode)`` where MODE is ``"all"`` + (re-transcribe everything), ``"missing"`` (only .wavs without an + existing transcript) or ``"keep"`` (reuse prompt_text untouched). + Plain choice pairs the combined config form can show on one row. + """ + if not prompt_exists: + return [("Re-transcribe all", "all")], "all" + missing = [wav for wav in wav_files + if not existing.get(wav.stem, "").strip()] + if not missing: + return ([("Keep the existing transcripts", "keep"), + ("Re-transcribe all", "all")], "keep") + return ([("Only transcribe new voices", "missing"), + ("Re-transcribe all", "all")], "missing") + + +def _plan_from_mode(mode: str, wav_files: list, + existing: Dict[str, str]) -> dict: + """Build the transcription PLAN for the chosen form MODE. + + The plan dict is what ``voices._transcribe`` consumes: "missing" + carries the .wavs lacking a transcript plus the existing mapping; + "all"/"keep" name the mode and reuse the mapping read while asking. + """ + if mode == "missing": + missing = [wav for wav in wav_files + if not existing.get(wav.stem, "").strip()] + return {"mode": mode, "missing": missing, "existing": dict(existing)} + return {"mode": mode, "missing": [], "existing": dict(existing)} + + def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser ) -> Optional[dict]: """Run every TUI screen; return the collected settings, or None to abort. - The wizard is driven by ``tui.Wizard`` as a stack of screen closures: - each screen shows one interactive widget and returns the next screen - (a closure), ``Wizard.BACK`` (Esc/q pressed — pop to the previous - screen), or the final settings dict. Only screens that actually render - are pushed, so Esc always lands on the previous real screen. A step - whose value is already provided by a flag (``--host``, ``--port``, - ``--families``, ...) or does not apply (e.g. the port-sync prompt when - the port did not change) is folded into the ``_after_*`` guards and - never becomes a screen. Esc on the first screen aborts the whole - wizard. + The wizard has two screens: the model tree ("Select TTS models to + host") and one combined configuration form (backend choice when it is + ambiguous, build offer when needed, clone-voice directory, + transcription plan, model download/defaults/cleanup), laid out like + the Generate-audiobooks screen — every option appears on one screen, + and options that do not apply are hidden instead of asked separately. + The bind host is always 127.0.0.1 and the port comes from + AUDIOCPP_API_URL in app/converter/config.py (the Settings screen), + so neither is ever asked. Esc on the first screen aborts the whole + wizard; Esc on the form pops back to the model tree. """ s: dict = {} - def ask_confirm(question: str, default: bool) -> bool: - result = tui.confirm(stdscr, question, default=default, - cancel_value=_GO_BACK) - if result is _GO_BACK: - raise _GoBack() - return result - def resolve_checkout(audiocpp_dir: Path) -> None: """Validate the audio.cpp checkout and populate the wizard state ``s``.""" audiocpp_dir = Path(audiocpp_dir).resolve() try: catalog = load_model_catalog(audiocpp_dir) except NotADirectoryError as exc: - raise _TuiError(str(exc)) + raise _TuiError(str(exc)) from exc if not catalog: raise _TuiError(f"No TTS model families found in " f"{audiocpp_dir}/model_specs; check the " @@ -204,10 +230,6 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser "existing_config": existing_config, "existing_selected": existing_selected, "existing_tasks": existing_tasks, - "existing_host": existing_config.get("host") - if existing_config else None, - "existing_port": existing_config.get("port") - if existing_config else None, "existing_backend": existing_config.get("backend") if existing_config else None, "existing_voice_dir": existing_config.get("voice_dir") @@ -262,6 +284,13 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser }) def _finalize() -> dict: + host = DEFAULT_HOST + port = _configsync.config_port() + backend = s["backend"] + # Build decision: --build-backend builds when no single-backend + # binary was detected; a plain --backend or a detected build never + # rebuilds; the interactive answer comes from the form. + build = s["build"] return { "audiocpp_dir": s["audiocpp_dir"], "catalog": s["catalog"], @@ -274,12 +303,12 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser "install_guidance": s["install_guidance"], "design_entry_ids": s["design_entry_ids"], "include_clone": s["include_clone"], - "host": s["host"], - "port": s["port"], - "backend": s["backend"], - "build": s["build"], - "lazy_load": s["lazy_load"], - "sync_port": s["sync_port"], + "host": host, + "port": port, + "backend": backend, + "build": build, + "lazy_load": True, + "sync_port": None, "sync_model_ids": s["sync_model_ids"], "wav_dir": s["wav_dir"], "plan": s["plan"], @@ -305,7 +334,7 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser if target in valid_dirs: checked_set.add((family_index, target)) picked = tui.checkbox_tree( - stdscr, "Select TTS model families to host", + stdscr, "Select TTS models to host", tree_families, expand_all=args.all_packages, back_value=_GO_BACK, checked=checked_set) if picked is _GO_BACK: @@ -325,243 +354,229 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser chosen[family] = [keyed[key] for key in chosen[family]] s["chosen"] = chosen s["family_keys"] = family_keys - return screen_host + return screen_config def _after_families(): if args.families is not None: _families_from_flag() - return screen_host + return screen_config return screen_families - def screen_host(): - """Build the model entries, then ask the bind host. + def _field_val(fields_list, key, default=None): + return next((f["value"] for f in fields_list + if f.get("key") == key), default) + + def _transcription_state(wav_dir): + """(wav_files, existing transcripts, prompt_text exists) or None.""" + if wav_dir is None: + return None + wav_files = find_wav_files(Path(wav_dir)) + if not wav_files: + return None + prompt_path = Path(wav_dir) / PROMPT_TEXT_FILENAME + prompt_exists = bool(prompt_path.exists()) and not args.force + existing = read_prompt_text(prompt_path) if prompt_exists else {} + return wav_files, existing, prompt_exists + + def _apply_form(result: dict) -> dict: + """Fold the form's answers into the settings and finalize.""" + # Backend/build: the interactive combination. A backend whose + # binary already exists (switching to an already-built one) hides + # the build row — honor that by re-checking at apply time. + if s["backend"] is None: + s["backend"] = result["backend"] + s["build"] = bool(result.get("build")) and ( + _build.built_server_binary(s["audiocpp_dir"], + s["backend"]) is None) + + # Clone-voice directory: only meaningful for clone-capable picks. + if args.input_dir is not None: + s["wav_dir"] = args.input_dir + elif s["include_clone"]: + raw = result.get("wav_dir") + s["wav_dir"] = Path(raw) if raw else None + else: + s["wav_dir"] = None + + # Transcription plan (transcription itself runs in the tail). + s["plan"] = None + if s["include_clone"]: + state = _transcription_state(s["wav_dir"]) + if state is not None: + wav_files, existing, prompt_exists = state + choices, default_mode = _transcription_choices( + wav_files, existing, prompt_exists) + mode = result.get("transcription") + if mode not in [candidate for _label, candidate in choices]: + mode = default_mode + s["plan"] = _plan_from_mode(mode, wav_files, existing) + + s["download"] = bool(result.get("download")) and ( + _models.download_applicable(s["audiocpp_dir"], + s["model_entries"])) + s["sync_model_ids"] = result.get("sync_model_ids") + s["delete_unused"] = bool(result.get("delete_unused")) \ + and bool(s["unused_entries"]) + return _finalize() + + def screen_config(): + """One combined configuration screen for everything else. - The task/id pickers (when any) run here too and are grouped with - this screen: Esc on one of them (or on the host field) returns to - the families tree. + The Generate-audiobooks-style form replaces the old one-question- + per-screen chain (host, port, port sync, backend, build offer, + wav directory, transcription plan, model-id sync, delete unused, + download). Rows whose question does not apply are hidden rather + than skipped silently. Esc or Cancel pops back to the model tree. """ try: _compute_entries() except _GoBack: return tui.Wizard.BACK - if args.host is not None: - s["host"] = args.host - return _after_host() - host = tui.line_edit( - stdscr, "Bind host", - s["existing_host"] if isinstance(s["existing_host"], str) - else DEFAULT_HOST, - help_lines=["The IP address audiocpp will be hosted on", - "127.0.0.1 (this machine) is probably " - "correct"], back_value=_GO_BACK) - if host is _GO_BACK: - return tui.Wizard.BACK - s["host"] = host - return _after_host() - - def _after_host(): - if args.port is None: - return screen_port - s["port"] = args.port - return _after_port() - - def screen_port(): - port_text = tui.line_edit( - stdscr, "Port", - str(s["existing_port"]) if isinstance(s["existing_port"], int) - else str(_configsync.config_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 audiocpp will be hosted on"], - back_value=_GO_BACK) - if port_text is _GO_BACK: - return tui.Wizard.BACK - s["port"] = int(port_text) - return _after_port() - - def _after_port(): - s["sync_port"] = None - if s["port"] != _configsync.config_port(): - return screen_sync_port - return _after_sync() - - def screen_sync_port(): - sync_port = tui.confirm( - stdscr, "Update AUDIOCPP_API_URL in app/converter/config.py " - f"to port {s['port']} so audiobook.py talks to this server", - default=True, cancel_value=_GO_BACK) - if sync_port is _GO_BACK: - return tui.Wizard.BACK - s["sync_port"] = sync_port - return _after_sync() - def _after_sync(): - if args.build_backend: + # Backend: pinned by a flag or an existing build when possible; + # only otherwise does it become a form question. Not built for any + # pinned backend yet still asks — even on a modify run, so a user + # who declined the build the first time is never stranded without + # a way to build from the TUI. + if args.build_backend is not None: s["backend"] = args.build_backend s["build"] = s["detected_backend"] is None - return _after_backend() - if args.backend: + elif args.backend is not None: s["backend"] = args.backend s["build"] = False - return _after_backend() - if s["detected_backend"] is not None: + elif s["detected_backend"] is not None: # Already built: use the detected backend, no menu, no build. s["backend"] = s["detected_backend"] s["build"] = False - return _after_backend() - # Not built for any backend yet: always ask which backend the server - # should use and offer to build it — even on a modify run, so a user - # who declined the build the first time is never stranded without a - # way to build from the TUI. - return screen_backend - - def screen_backend(): - # Pre-select the backend an existing server.json records (modify - # flow), so re-running setup lands on the previous choice. - backend_options, backend_default = _backend_options(None) - if s["existing_backend"] in BACKENDS: - backend_default = next( - (index for index, (_label, value) in enumerate(backend_options) - if value == s["existing_backend"]), backend_default) - backend = tui.menu( - stdscr, "Which inference backend should audiocpp_server " - "use?", backend_options, - default_index=backend_default, back_value=_GO_BACK) - if backend is _GO_BACK: - return tui.Wizard.BACK - s["backend"] = backend - if _build.built_server_binary(s["audiocpp_dir"], backend) is not None: - # A checkout with builds for several backends: this one is - # already built, so there is nothing to build. - s["build"] = False - return _after_backend() - return screen_build - - def screen_build(): - # Not built for the chosen backend yet: offer to build it now. The - # build itself runs in the TUI task view (or the console tail for - # CLI runs) after the wizard. - build = tui.confirm( - stdscr, f"audiocpp_server is not built for {s['backend']}. " - f"Build it now (runs scripts/build_*)?", - default=True, cancel_value=_GO_BACK) - if build is _GO_BACK: - return tui.Wizard.BACK - s["build"] = build - return _after_backend() - - def _after_backend(): - s["lazy_load"] = True - return _after_lazy() + else: + s["backend"] = None # decided by the form + s["build"] = None - def _after_lazy(): - if args.input_dir is not None: - s["wav_dir"] = args.input_dir - return _after_wav() + # Clone-voice directory seed: the project voices/ dir (detected), + # or the voice_dir recorded by the server.json being modified. + wav_start = None if s["include_clone"]: - return screen_wav - s["wav_dir"] = None - return _after_wav() - - def screen_wav(): - wav_start = detect_wav_dir(s["audiocpp_dir"], TTS_ROOT) - # Modify flow: an existing voice_dir seeds the browser so the user - # can accept it on Enter instead of re-navigating. - if isinstance(s["existing_voice_dir"], str) and s["existing_voice_dir"]: - wav_start = Path(s["existing_voice_dir"]) - wav_dir = tui.browse_directory( - stdscr, "Select the directory with your .wav voices", - info=_wav_dir_info, preview=_wav_dir_preview, - start=wav_start if wav_start is not None else VOICES_DIR, - back_value=_GO_BACK) - if wav_dir is _GO_BACK: - return tui.Wizard.BACK - s["wav_dir"] = wav_dir - return _after_wav() - - def _after_wav(): - s["plan"] = None - if s["include_clone"] and s["wav_dir"] is not None: - wav_files = find_wav_files(s["wav_dir"]) - if wav_files: - prompt_path = s["wav_dir"] / PROMPT_TEXT_FILENAME - if prompt_path.exists() and not args.force: - return screen_transcription - existing = read_prompt_text(prompt_path) if ( - prompt_path.exists() and not args.force) else {} - s["plan"] = _voices._decide_transcription( - wav_files, existing, prompt_path.exists(), - args.force, ask_confirm) - return _after_transcription() - - def screen_transcription(): - # Transcription plan (questions only; transcription runs after). - wav_files = find_wav_files(s["wav_dir"]) - prompt_path = s["wav_dir"] / PROMPT_TEXT_FILENAME - existing = read_prompt_text(prompt_path) if ( - prompt_path.exists() and not args.force) else {} - try: - s["plan"] = _voices._decide_transcription( - wav_files, existing, prompt_path.exists(), - args.force, ask_confirm) - except _GoBack: - return tui.Wizard.BACK - return _after_transcription() - - def _after_transcription(): - s["sync_model_ids"] = None - if len(s["entry_ids"]) == 1 and not ( - config.AUDIOCPP_MODEL_ID == s["entry_ids"][0] - and config.AUDIOCPP_CLONE_MODEL_ID == s["entry_ids"][0]): - return screen_model_sync - return _after_model_sync() - - def screen_model_sync(): - sync_model_ids = tui.confirm( - stdscr, "Update AUDIOCPP_MODEL_ID and " - "AUDIOCPP_CLONE_MODEL_ID in app/converter/config.py to " - f"'{s['entry_ids'][0]}' so audiobook.py uses this model", - default=True, cancel_value=_GO_BACK) - if sync_model_ids is _GO_BACK: - return tui.Wizard.BACK - s["sync_model_ids"] = sync_model_ids - return _after_model_sync() + wav_start = detect_wav_dir(s["audiocpp_dir"], TTS_ROOT) + if isinstance(s["existing_voice_dir"], str) \ + and s["existing_voice_dir"]: + wav_start = Path(s["existing_voice_dir"]) + s["wav_dir"] = wav_start + + fields: List[dict] = [] + if s["backend"] is None: + options, default_index = _backend_options(None) + default_backend = options[default_index][1] + if s["existing_backend"] in BACKENDS: + default_backend = next( + (value for _label, value in options + if value == s["existing_backend"]), default_backend) + + def needs_build(fs) -> bool: + chosen = _field_val(fs, "backend", default_backend) + return _build.built_server_binary( + s["audiocpp_dir"], chosen) is None + + fields.append({ + "key": "backend", "label": "Inference backend", + "kind": "choice", "value": default_backend, + "choices": options, + "note": "audiocpp_server is not built yet.", + }) + fields.append({ + "key": "build", "label": "Build audiocpp_server now?", + "kind": "bool", "value": True, + "visible": needs_build, + }) + + wav_field = { + "key": "wav_dir", "label": "Voice clone .wav directory", + "kind": "dir", "value": Path(wav_start) if wav_start else None, + "visible": lambda fs: bool(s["include_clone"]), + "note": "Published as the server-level voice presets " + "(prompt_text transcribed with whisper).", + } + fields.append(wav_field) + + def state_of(fs): + return _transcription_state(_field_val(fs, "wav_dir")) + + initial_state = state_of([wav_field]) + initial_default = _transcription_choices(*initial_state)[1] \ + if initial_state is not None else "missing" + + def transcription_choices(fs): + state = state_of(fs) + if state is None: + return [("Re-transcribe all", "all")] + return _transcription_choices(*state)[0] + + def transcription_visible(fs) -> bool: + return state_of(fs) is not None + + def reset_transcription(fs_list) -> None: + # The directory changed: snap the stale choice to a valid one. + field = next((f for f in fs_list + if f.get("key") == "transcription"), None) + if field is not None: + modes = [mode for _label, mode in transcription_choices( + fs_list)] + if field["value"] not in modes: + state = state_of(fs_list) + field["value"] = _transcription_choices(*state)[1] \ + if state is not None else "missing" + + fields.append({ + "key": "transcription", "label": "Voice transcripts", + "kind": "choice", "value": initial_default, + "choices": transcription_choices, + "visible": transcription_visible, + }) + wav_field["on_change"] = reset_transcription + + if _models.download_applicable(s["audiocpp_dir"], s["model_entries"]): + fields.append({ + "key": "download", + "label": "Download the selected models automatically?", + "kind": "bool", "value": True, + "note": "No prints the model_manager_v2.py install " + "commands for any models not already installed.", + }) + + model_sync_relevant = len(s["entry_ids"]) == 1 and not ( + config.AUDIOCPP_MODEL_ID == s["entry_ids"][0] + and config.AUDIOCPP_CLONE_MODEL_ID == s["entry_ids"][0]) + if model_sync_relevant: + fields.append({ + "key": "sync_model_ids", + "label": f"Make '{s['entry_ids'][0]}' the default model?", + "kind": "bool", "value": True, + "note": "Writes AUDIOCPP_MODEL_ID/AUDIOCPP_CLONE_MODEL_ID " + "to app/converter/config.py.", + }) - def _after_model_sync(): new_paths = {entry["path"] for entry in s["model_entries"]} s["unused_entries"] = _models.unused_installed_entries( s["output_path"], new_paths) \ if s["existing_config"] is not None else [] s["delete_unused"] = False if s["unused_entries"]: - return screen_delete_unused - return _after_delete() - - def screen_delete_unused(): - delete_unused = tui.confirm( - stdscr, "Delete unused models?", default=False, - cancel_value=_GO_BACK) - if delete_unused is _GO_BACK: + count = len(s["unused_entries"]) + fields.append({ + "key": "delete_unused", + "label": f"Delete {count} unused downloaded model " + f"{'entry' if count == 1 else 'entries'} from disk?", + "kind": "bool", "value": False, + "note": "Selected models were removed above but their " + "downloads are still on disk.", + }) + + result = tui.form( + stdscr, "Configure audio.cpp", fields, + buttons=("Continue!", "Cancel"), + start_on_buttons=False, back_value=tui.Wizard.BACK) + if result is tui.Wizard.BACK: return tui.Wizard.BACK - s["delete_unused"] = delete_unused - return _after_delete() - - def _after_delete(): - manager = s["audiocpp_dir"] / "tools" / "model_manager_v2.py" - if manager.is_file(): - return screen_download - s["download"] = False - return _finalize() - - def screen_download(): - # Automatic model download (or print the install commands). - try: - s["download"] = _models._decide_download( - s["audiocpp_dir"], s["model_entries"], ask_confirm) - except _GoBack: - return tui.Wizard.BACK - return _finalize() + return _apply_form(result) # First screen: resolve the checkout directly when it already exists # (the modify flow), so the wizard starts on a real screen. When no @@ -648,12 +663,6 @@ def _execute_lanes(settings: dict, return 0 def write(emit, cancel): - # Port sync (applied now that the terminal is back). - if settings["sync_port"] is True: - _configsync._apply_port_sync(settings["port"], True) - elif settings["sync_port"] is False: - _configsync._apply_port_sync(settings["port"], False) - _write_and_advise( audiocpp_dir, settings["wav_dir"], settings["output_path"], settings["model_entries"], settings["install_guidance"], @@ -677,11 +686,21 @@ def _execute_lanes(settings: dict, def install(emit, cancel): _models._install_models(audiocpp_dir, settings["install_guidance"], - settings["download"], emit=emit, cancel=cancel) + settings["download"], emit=emit, cancel=cancel, + model_entries=settings["model_entries"]) _build._print_launch_hint(audiocpp_dir, settings["output_path"]) return 0 - install_title = "Download models" if settings.get("download") \ - else "Print model install commands" + # Everything already on disk: the install step just reports it, so the + # step title says so instead of promising a download. + everything_installed = bool(settings["model_entries"]) \ + and _models._all_models_present(audiocpp_dir, + settings["model_entries"]) + if everything_installed: + install_title = "Verify models" + elif settings.get("download"): + install_title = "Download models" + else: + install_title = "Print model install commands" lanes.append(taskview.TaskLane( "Configure & download", @@ -919,8 +938,10 @@ def _collect_from_flags(args: argparse.Namespace, _build_entries(family_keys, chosen, catalog_by_family, task_picker) - # Server settings. - host = args.host or DEFAULT_HOST + # Server settings. Host is always 127.0.0.1 and the port comes from + # AUDIOCPP_API_URL in app/converter/config.py (the Settings screen) — + # neither is a CLI option. + host = DEFAULT_HOST detected_backend = detect_backend(audiocpp_dir) if args.build_backend: backend = args.build_backend @@ -934,7 +955,7 @@ def _collect_from_flags(args: argparse.Namespace, else: backend = "cuda" build = False - port = args.port if args.port is not None else _configsync.config_port() + port = _configsync.config_port() lazy_load = True # Output path / overwrite (decline falls back to cwd, then aborts). @@ -951,9 +972,6 @@ def _collect_from_flags(args: argparse.Namespace, return None # Config sync decisions (auto-apply unless explicitly declined). - sync_port: Optional[bool] = None - if port != _configsync.config_port(): - sync_port = not args.no_sync_port sync_model_ids: Optional[bool] = None if len(entry_ids) == 1 and not ( config.AUDIOCPP_MODEL_ID == entry_ids[0] @@ -986,7 +1004,7 @@ def _collect_from_flags(args: argparse.Namespace, "backend": backend, "build": build, "lazy_load": lazy_load, - "sync_port": sync_port, + "sync_port": None, "sync_model_ids": sync_model_ids, "wav_dir": wav_dir, "plan": plan, @@ -1024,11 +1042,6 @@ def build_parser() -> argparse.ArgumentParser: "family (distinct target_directory) instead of " "only the recommended one. Voice-design packages " "are hosted with task 'vdes'") - parser.add_argument("--host", type=str, default=None, - help="Bind host for the server (default: 127.0.0.1)") - parser.add_argument("--port", type=int, default=None, - help="Port for the server (default: the port in " - "AUDIOCPP_API_URL from app/converter/config.py)") parser.add_argument("--backend", choices=BACKENDS, default=None, help="Inference backend recorded in server.json " "(default: auto-detected from the checkout's " @@ -1048,9 +1061,6 @@ def build_parser() -> argparse.ArgumentParser: help="Run model_manager_v2.py install for each hosted " "model automatically (default: print the commands " "only)") - parser.add_argument("--no-sync-port", action="store_true", - help="Do not rewrite AUDIOCPP_API_URL in " - "app/converter/config.py when --port differs") parser.add_argument("--no-sync-model-ids", action="store_true", help="Do not rewrite AUDIOCPP_MODEL_ID/" "AUDIOCPP_CLONE_MODEL_ID for a single-entry server") -- cgit v1.2.3