diff options
| author | historia <historiavg@proton.me> | 2026-08-28 14:57:48 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-28 14:57:48 -0400 |
| commit | db38085d07ce75f8961eecdc1919e98748254c53 (patch) | |
| tree | beaea2ba84de05bfc2fa90e9b92a24568d3b4cb9 /app/converter/converter.py | |
| parent | afb2c2d5b297c5aa28bcced0e3f90e207d799c2a (diff) | |
| download | tts-audiobook-generator-db38085d07ce75f8961eecdc1919e98748254c53.tar.gz | |
refactor: overhaul config.py, remove cli default options
Diffstat (limited to 'app/converter/converter.py')
| -rw-r--r-- | app/converter/converter.py | 50 |
1 files changed, 30 insertions, 20 deletions
diff --git a/app/converter/converter.py b/app/converter/converter.py index 5235991..a9ea7eb 100644 --- a/app/converter/converter.py +++ b/app/converter/converter.py @@ -31,7 +31,7 @@ from .clients import ( FasterTTSClient, QwenTTSClient, normalize_language, - speaker_display_name, + speaker_display_name_for, ) logger = logging.getLogger(__name__) @@ -207,7 +207,7 @@ class AudiobookConverter: def __init__(self, voice_mode: str = VOICE_MODE_CUSTOM, voice_clone_ref_audio: Optional[str] = None, voice_clone_ref_text: Optional[str] = None, skip_transcription: bool = False, speed: float = 1.0, single_file: bool = False, output_format: str = config.AUDIO_FORMAT, - language: Optional[str] = None, backend: str = config.BACKEND, + language: Optional[str] = None, backend: str = None, voice: Optional[str] = None, debug: bool = False, model_id: Optional[str] = None, instructions: Optional[str] = None, @@ -219,6 +219,8 @@ class AudiobookConverter: raise ValueError(f"Speed must be a positive number, got {speed}") if output_format not in AUDIO_FORMATS: raise ValueError(f"Unsupported output format: {output_format}") + if backend is None: + raise ValueError("backend is required (pass --backend)") if backend not in BACKENDS: raise ValueError( f"Unknown backend: {backend!r} (expected one of {BACKENDS})" @@ -257,10 +259,10 @@ class AudiobookConverter: elif backend == BACKEND_AUDIOCPP: # --voice picks the voice: a built-in speaker name on the # CustomVoice entry, or a server-side preset (cloning) - # elsewhere. model_id overrides AUDIOCPP_MODEL_ID for - # multi-model servers; instructions describe or style the - # voice, request_options pass per-model controls through to - # the server. + # elsewhere. model_id picks the server entry per run + # (auto-selected on single-entry servers); instructions + # describe or style the voice, request_options pass + # per-model controls through to the server. self.tts = AudioCppTTSClient(chunks_dir=CHUNKS_FOLDER, voice=voice, language=self.language, model_id=model_id, @@ -270,6 +272,7 @@ class AudiobookConverter: else: # Qwen: the voice mode picks the request shape (built-in # speaker, clone from a reference .wav, or a designed voice); + # --voice names the built-in speaker in speaker mode and # instructions describe the voice in design mode. self.tts = QwenTTSClient( chunks_dir=CHUNKS_FOLDER, @@ -281,6 +284,7 @@ class AudiobookConverter: instructions=self.instructions, api_url=api_url, quiet=quiet, + voice=voice, ) self._progress = progress self.tts.cancel = cancel @@ -314,6 +318,13 @@ class AudiobookConverter: "Voice Design mode requires a voice description. " "Use --instructions \"...\" to describe the voice to synthesize with." ) + if self.backend == BACKEND_QWEN and self.voice_mode == VOICE_MODE_CUSTOM \ + and not (self.voice or "").strip(): + raise ValueError( + "CustomVoice mode requires a speaker. Use --voice SPEAKER " + "(e.g. Vivian) to pick one, or --clone / --instructions " + "for the other voice modes." + ) if self.voice_mode == VOICE_MODE_CLONE and self.backend == BACKEND_QWEN: if not self.voice_clone_ref_audio: raise ValueError( @@ -348,19 +359,18 @@ class AudiobookConverter: Custom voice mode uses the built-in speaker's display name; voice clone mode uses the reference audio file's stem; the faster and - audiocpp backends use the server-side voice name (or, for audiocpp's - speaker mode, the selected built-in CustomVoice speaker, falling back - to the configured one). An instruction without a voice (voice design, - or instruction-defined voices on families without built-in speakers) - uses "designed". Spaces become underscores (e.g. "Uncle Fu" -> - "Uncle_Fu"). + audiocpp backends use the server-side voice name (for audiocpp's + speaker mode, the selected built-in CustomVoice speaker). An + instruction without a voice (voice design, or instruction-defined + voices on families without built-in speakers) uses "designed". + Spaces become underscores (e.g. "Uncle Fu" -> "Uncle_Fu"). Pure (no I/O, no server) so the pre-flight overwrite check can compute the exact output names a run would produce before spending time connecting to a TTS server. """ if backend == BACKEND_FASTER: - narrator = voice or config.FASTER_VOICE + narrator = voice or "default" elif backend == BACKEND_AUDIOCPP: if voice: narrator = voice @@ -368,7 +378,10 @@ class AudiobookConverter: # The voice comes from the instruction, not a speaker name. narrator = "designed" else: - narrator = speaker_display_name() + # Unreachable in a valid run (the audiocpp client refuses a + # speaker-capable entry without --voice); keep a stable tag + # for the pre-flight of runs that will fail at connect time. + narrator = "narrator" elif voice_mode == VOICE_MODE_DESIGN: # Qwen's VoiceDesign model: the voice is described by an # instruction and has no speaker name. @@ -376,7 +389,7 @@ class AudiobookConverter: elif voice_mode == VOICE_MODE_CLONE: narrator = Path(voice_clone_ref_audio).stem else: - narrator = speaker_display_name() + narrator = speaker_display_name_for(voice or "") return AudiobookConverter._sanitize_filename( narrator, fallback="narrator").replace(" ", "_") @@ -736,7 +749,7 @@ class AudiobookConverter: if self.backend == BACKEND_FASTER: self._say(f"Faster TTS endpoint: {config.FASTER_API_URL}") self._say("Backend: faster (voice cloning, reference configured on server)") - self._say(f"Voice: {self.voice or config.FASTER_VOICE}") + self._say(f"Voice: {self.voice}") elif self.backend == BACKEND_AUDIOCPP: self._say(f"audio.cpp endpoint: {config.AUDIOCPP_API_URL}") self._say(f"Model id: {self.tts.model_id}") @@ -750,9 +763,6 @@ class AudiobookConverter: elif self.instructions: self._say("Backend: audio.cpp (voice from --instructions description)") self._say(f"Instruction: {self.instructions}") - else: - self._say("Backend: audio.cpp (custom voice, built-in speaker)") - self._say(f"Speaker: {config.SPEAKER}") if self.request_options: self._say(f"Request options: {self.request_options}") self._say(f"Language: {self.language}") @@ -764,7 +774,7 @@ class AudiobookConverter: self._say(f"Voice mode: {self.voice_mode}") self._say(f"Model size: {MODEL_SIZE} (always)") if self.voice_mode == VOICE_MODE_CUSTOM: - self._say(f"Speaker: {config.SPEAKER}") + self._say(f"Speaker: {self.voice}") self._say(f"Language: {self.language}") elif self.voice_mode == VOICE_MODE_CLONE: self._say(f"Reference audio: {Path(self.voice_clone_ref_audio).name}") |
