aboutsummaryrefslogtreecommitdiff
path: root/app/converter/clients/qwen.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/converter/clients/qwen.py')
-rw-r--r--app/converter/clients/qwen.py29
1 files changed, 17 insertions, 12 deletions
diff --git a/app/converter/clients/qwen.py b/app/converter/clients/qwen.py
index ed3149b..17f14c5 100644
--- a/app/converter/clients/qwen.py
+++ b/app/converter/clients/qwen.py
@@ -15,7 +15,7 @@ from .base import (BaseTTSClient, ConversionCancelled, resolve_request_seed,
VOICE_MODE_CLONE, VOICE_MODE_CUSTOM, VOICE_MODE_DESIGN,
VOICE_MODES)
from .languages import normalize_language
-from .speakers import speaker_display_name
+from .speakers import QWEN3_TTS_SPEAKERS, speaker_display_name_for
logger = logging.getLogger(__name__)
@@ -32,7 +32,8 @@ class QwenTTSClient(BaseTTSClient):
voice_mode: str = "custom_voice", voice_clone_ref_audio: Optional[str] = None,
voice_clone_ref_text: Optional[str] = None, skip_transcription: bool = False,
language: Optional[str] = None, api_url: Optional[str] = None,
- instructions: Optional[str] = None, quiet: bool = False):
+ instructions: Optional[str] = None, quiet: bool = False,
+ voice: Optional[str] = None):
super().__init__(chunks_dir, quiet=quiet)
if voice_mode not in VOICE_MODES:
raise ValueError(
@@ -42,11 +43,17 @@ class QwenTTSClient(BaseTTSClient):
self.voice_clone_ref_audio = voice_clone_ref_audio
self.voice_clone_ref_text = (voice_clone_ref_text or "").strip()
self.skip_transcription = skip_transcription
+ # Built-in CustomVoice speaker (VOICE_MODE_CUSTOM): the --voice
+ # value / the Generate form's Speaker pick. Required there — there
+ # is no configured default speaker.
+ self.speaker = (voice or "").strip() or None
+ if voice_mode == VOICE_MODE_CUSTOM and not self.speaker:
+ raise ValueError(
+ "CustomVoice mode requires a speaker: pass --voice SPEAKER "
+ f"(one of {', '.join(QWEN3_TTS_SPEAKERS)})")
# Voice design / style instruction (VoiceDesign mode): describes the
- # voice to design. Defaults to the configured CustomVoice INSTRUCT so
- # a run never sends an empty design prompt.
- self.instructions = (instructions if instructions is not None
- else config.INSTRUCT).strip()
+ # voice to design. Required there (validated by the converter).
+ self.instructions = (instructions or "").strip()
# api_url overrides the configured endpoint for the active voice mode
# (used by the hub's "[remote]" backend entries and --api-url).
self.api_url = (api_url or "").strip() or None
@@ -261,21 +268,19 @@ class QwenTTSClient(BaseTTSClient):
# ------------------------------------------------------------------
def _generate_custom_voice(self, text: str) -> Tuple:
- """Generate audio using CustomVoice mode."""
+ """Generate audio using CustomVoice mode with the run's speaker."""
custom_api = self._resolve_api_name("/run_instruct", "/run_custom_voice", "/generate_custom_voice")
if custom_api == "/run_instruct":
payload = dict(
text=text,
lang_disp=self.language,
- spk_disp=speaker_display_name(),
- instruct=config.INSTRUCT,
+ spk_disp=speaker_display_name_for(self.speaker),
)
else:
payload = dict(
text=text,
language=self.language,
- speaker=config.SPEAKER,
- instruct=config.INSTRUCT,
+ speaker=self.speaker,
)
if self._endpoint_accepts_param(custom_api, "model_id_cv"):
payload["model_id_cv"] = CUSTOM_VOICE_MODEL_ID
@@ -323,7 +328,7 @@ class QwenTTSClient(BaseTTSClient):
clone_api = self._resolve_api_name("/run_voice_clone", "/generate_voice_clone",
api_info=self.clone_api_info)
- use_xvector = config.XVECTOR_ONLY or not self.voice_clone_ref_text
+ use_xvector = not self.voice_clone_ref_text
if clone_api == "/run_voice_clone":
payload = dict(