diff options
Diffstat (limited to 'app/backends')
| -rw-r--r-- | app/backends/audiocpp/voices.py | 43 | ||||
| -rw-r--r-- | app/backends/audiocpp/wizard.py | 13 |
2 files changed, 42 insertions, 14 deletions
diff --git a/app/backends/audiocpp/voices.py b/app/backends/audiocpp/voices.py index b26a0be..d19cb18 100644 --- a/app/backends/audiocpp/voices.py +++ b/app/backends/audiocpp/voices.py @@ -2,19 +2,22 @@ import argparse from pathlib import Path -from typing import Dict, List, Optional, Tuple +from typing import Dict, Optional, Tuple from backends.common import (PROMPT_TEXT_FILENAME, find_wav_files, read_prompt_text) -from converter.clients import (transcribe_reference_audio, - whisper_backend_available) +from converter.clients import (transcribe_reference_audio_detailed, + whisper_backend_problem) def transcribe_wav_dir(wav_files: list, whisper_model: str, cancel=None) -> Dict[str, str]: - """Transcribe each wav file and return a mapping of stem -> transcript. + """Transcribe each wav file and return a stem -> transcript mapping. CANCEL (a ``threading.Event``) is checked between files so the in-TUI - task view can stop a long transcription early. + task view can stop a long transcription early. A wav that yields no + transcript keeps its '' entry, and the per-file warning says WHY + (backend missing, import broken, transcribe error, or no speech) + instead of a bare 'no transcript' line. """ transcripts: Dict[str, str] = {} for wav_file in wav_files: @@ -23,13 +26,15 @@ def transcribe_wav_dir(wav_files: list, whisper_model: str, break name = wav_file.stem print(f"[INFO] Transcribing {wav_file.name} (voice '{name}')...") - text = transcribe_reference_audio(str(wav_file), model_name=whisper_model) + text, reason = transcribe_reference_audio_detailed( + str(wav_file), model_name=whisper_model) if text: print(f"[OK] {name}: {text}") else: - print(f"[WARNING] No transcript for '{name}'; cloning works best " - "with an accurate transcript — consider editing prompt_text " - "by hand before starting the server") + print(f"[WARNING] No transcript for '{name}': {reason}") + print("[WARNING] Cloning works best with an accurate transcript; " + "consider editing prompt_text by hand before starting " + "the server") transcripts[name] = text or "" return transcripts @@ -79,11 +84,10 @@ def _transcribe(args: argparse.Namespace, plan: Optional[dict], "already transcribed, nothing new to transcribe") return existing, False - if whisper_backend_available() is None: - print("[WARNING] Neither faster_whisper nor whisper was found, so " - "reference .wav files cannot be transcribed automatically and " - "every transcript will be empty.") - print(" Install whisper (or faster_whisper) in your " + problem = whisper_backend_problem() + if problem is not None: + print(f"[WARNING] No usable Whisper backend: {problem}") + print(" Install faster-whisper (or openai-whisper) in your " "audiobook environment to transcribe automatically; otherwise " "transcripts must be added by hand (see the warning at the end).") @@ -95,6 +99,17 @@ def _transcribe(args: argparse.Namespace, plan: Optional[dict], else: transcripts = transcribe_wav_dir(wav_files, args.whisper_model, cancel=cancel) + + # A failed transcription must never wipe a good transcript: keep the + # existing text for any voice whose new transcription came back empty. + # A blank prompt_text entry makes the server reject every clone request + # for that voice, so overwriting known-good text with '' is always a + # regression, whatever went wrong with the re-transcription. + for name, text in transcripts.items(): + if not text.strip() and existing.get(name, "").strip(): + transcripts[name] = existing[name] + print(f"[WARNING] Kept the existing transcript for '{name}' " + "(the new transcription came back empty)") return transcripts, True diff --git a/app/backends/audiocpp/wizard.py b/app/backends/audiocpp/wizard.py index 91a8522..46b6638 100644 --- a/app/backends/audiocpp/wizard.py +++ b/app/backends/audiocpp/wizard.py @@ -600,6 +600,19 @@ def _execute_lanes(settings: dict, transcripts, write_prompt = {}, False state["transcripts"] = transcripts state["write_prompt"] = write_prompt + # A blank transcript means the affected clone voices cannot work: + # transcript-conditioned families (Qwen3-TTS Base ICL) reject every + # request without it. Report the step as failed instead of a silent + # [OK]; the setup continues (warn-and-continue) and still writes + # server.json and prompt_text. + blank = sorted(name for name, text in transcripts.items() + if not text.strip()) + if blank: + print("[ERROR] No transcript for: " + ", ".join(blank)) + print("[ERROR] Those clone voices will NOT work until prompt_text " + "carries an accurate transcript for each one; see the " + "summary below for how to fix them by hand.") + return 1 return 0 def write(emit, cancel): |
