Spaces:
Running
Running
Upload app.py
Browse files
app.py
CHANGED
|
@@ -117,6 +117,10 @@ PRESET_VOICES = [
|
|
| 117 |
# YourVoic voices mapped by language
|
| 118 |
# Only include CONFIRMED working voice names
|
| 119 |
YOURVOIC_VOICE_MAP = {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
# Indian - confirmed working
|
| 121 |
"Hindi": ["Rahul", "Deepika", "Aditya"],
|
| 122 |
"Bengali": ["Sneha", "Aryan"],
|
|
@@ -127,10 +131,6 @@ YOURVOIC_VOICE_MAP = {
|
|
| 127 |
"Kannada": ["Divya", "Karthik"],
|
| 128 |
"Malayalam": ["Nikhil", "Ammu"],
|
| 129 |
"Punjabi": ["Vikram", "Simran"],
|
| 130 |
-
# African - NOT confirmed, will use API lookup
|
| 131 |
-
# "Afrikaans": [], # unknown - use API
|
| 132 |
-
# "Amharic": [], # unknown - use API
|
| 133 |
-
# "Swahili": [], # unknown - use API
|
| 134 |
# English fallback
|
| 135 |
"English": ["Peter", "Sarah", "Caleb"],
|
| 136 |
}
|
|
@@ -224,25 +224,33 @@ def generate_speech_yourvoic_with_retry(client, text, voice, yv_model, emotion,
|
|
| 224 |
# Get list of candidate voices
|
| 225 |
candidates = []
|
| 226 |
|
| 227 |
-
# 1. Try
|
| 228 |
-
user_voice = get_voice_name(voice)
|
| 229 |
-
candidates.append(user_voice)
|
| 230 |
-
|
| 231 |
-
# 2. Try hardcoded voices
|
| 232 |
hardcoded = YOURVOIC_VOICE_MAP.get(language, [])
|
| 233 |
candidates.extend(hardcoded)
|
| 234 |
|
| 235 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
api_voices = _fetch_yourvoic_voice(yourvoic_lang, yv_model)
|
| 237 |
if api_voices:
|
| 238 |
-
|
|
|
|
|
|
|
| 239 |
|
| 240 |
# Deduplicate preserving order
|
| 241 |
seen = set()
|
| 242 |
candidates = [x for x in candidates if not (x in seen or seen.add(x))]
|
| 243 |
|
| 244 |
# Try each candidate until one works
|
| 245 |
-
for i, candidate_voice in enumerate(candidates[:
|
| 246 |
print(f"[YourVoic] Trying voice '{candidate_voice}' for {language} (attempt {i+1})")
|
| 247 |
wav_path, transcript, error = generate_speech_yourvoic(
|
| 248 |
client, text, candidate_voice, yv_model, emotion, language, lang_config,
|
|
@@ -252,12 +260,14 @@ def generate_speech_yourvoic_with_retry(client, text, voice, yv_model, emotion,
|
|
| 252 |
# Cache this working voice for future chunks
|
| 253 |
if language not in YOURVOIC_VOICE_MAP or not YOURVOIC_VOICE_MAP.get(language):
|
| 254 |
YOURVOIC_VOICE_MAP[language] = [candidate_voice]
|
|
|
|
|
|
|
| 255 |
return wav_path, transcript, None
|
| 256 |
if error and "Invalid voice name" not in str(error):
|
| 257 |
# Non-voice error (credits, etc) - don't try more voices
|
| 258 |
return None, transcript, error
|
| 259 |
|
| 260 |
-
return None, text, f"No valid voice found for {language}. Tried: {candidates[:
|
| 261 |
|
| 262 |
YOURVOIC_MODELS = [
|
| 263 |
"aura-prime -- Balanced quality and speed (recommended)",
|
|
@@ -774,15 +784,6 @@ And he would smile - that slow, careful smile that seemed to cost him something
|
|
| 774 |
DESCRIPTION = """
|
| 775 |
# Audiobook Generator
|
| 776 |
### English Text to Multi-Language Audiobook
|
| 777 |
-
**Three Voice Engines**
|
| 778 |
-
|
| 779 |
-
| Engine | Languages | Best for |
|
| 780 |
-
|--------|-----------|----------|
|
| 781 |
-
| **Qwen Preset** (20 voices) | English, Chinese, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian, Arabic | General audiobooks |
|
| 782 |
-
| **Qwen Clone** (your voice) | Same 10 core (excl. Arabic) | Personalized narration |
|
| 783 |
-
| **YourVoic** (1000+ emotional voices) | Hindi, Bengali, Marathi, Telugu, Tamil, Gujarati, Kannada, Malayalam, Punjabi, Swahili, Amharic, Afrikaans | Indian/African languages |
|
| 784 |
-
|
| 785 |
-
The app automatically selects the right engine based on your chosen language.
|
| 786 |
"""
|
| 787 |
|
| 788 |
# Build language dropdown
|
|
@@ -936,13 +937,7 @@ with gr.Blocks(title="Audiobook Generator") as demo:
|
|
| 936 |
|
| 937 |
gr.Markdown(
|
| 938 |
"---\n"
|
| 939 |
-
|
| 940 |
-
"**Qwen Preset:** 11 languages (EN, ZH, JA, KO, DE, FR, RU, PT, ES, IT, AR) via Qwen3.5-Omni-Plus\n\n"
|
| 941 |
-
"**Qwen Clone:** 10 languages (same minus Arabic) via Qwen3-TTS-VC\n\n"
|
| 942 |
-
"**YourVoic:** Hindi, Bengali, Marathi, Telugu, Tamil, Gujarati, Kannada, Malayalam, Punjabi, "
|
| 943 |
-
"Swahili, Amharic, Afrikaans via YourVoic API with emotional voices\n\n"
|
| 944 |
-
"Built with Gradio | Qwen by Alibaba | YourVoic by YourVoic Private Limited"
|
| 945 |
-
)
|
| 946 |
|
| 947 |
if __name__ == "__main__":
|
| 948 |
demo.launch()
|
|
|
|
| 117 |
# YourVoic voices mapped by language
|
| 118 |
# Only include CONFIRMED working voice names
|
| 119 |
YOURVOIC_VOICE_MAP = {
|
| 120 |
+
# African - Peter works as universal voice
|
| 121 |
+
"Afrikaans": ["Peter", "Sarah"],
|
| 122 |
+
"Amharic": ["Peter", "Sarah"],
|
| 123 |
+
"Swahili": ["Peter", "Sarah"],
|
| 124 |
# Indian - confirmed working
|
| 125 |
"Hindi": ["Rahul", "Deepika", "Aditya"],
|
| 126 |
"Bengali": ["Sneha", "Aryan"],
|
|
|
|
| 131 |
"Kannada": ["Divya", "Karthik"],
|
| 132 |
"Malayalam": ["Nikhil", "Ammu"],
|
| 133 |
"Punjabi": ["Vikram", "Simran"],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
# English fallback
|
| 135 |
"English": ["Peter", "Sarah", "Caleb"],
|
| 136 |
}
|
|
|
|
| 224 |
# Get list of candidate voices
|
| 225 |
candidates = []
|
| 226 |
|
| 227 |
+
# 1. Try hardcoded voices for this language
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
hardcoded = YOURVOIC_VOICE_MAP.get(language, [])
|
| 229 |
candidates.extend(hardcoded)
|
| 230 |
|
| 231 |
+
# 2. Try user-selected voice
|
| 232 |
+
user_voice = get_voice_name(voice)
|
| 233 |
+
if user_voice not in candidates:
|
| 234 |
+
candidates.insert(0, user_voice)
|
| 235 |
+
|
| 236 |
+
# 3. Try universal English voices (work for many languages like Swahili)
|
| 237 |
+
for universal in ["Peter", "Sarah", "Caleb"]:
|
| 238 |
+
if universal not in candidates:
|
| 239 |
+
candidates.append(universal)
|
| 240 |
+
|
| 241 |
+
# 4. Try API-fetched voices last
|
| 242 |
api_voices = _fetch_yourvoic_voice(yourvoic_lang, yv_model)
|
| 243 |
if api_voices:
|
| 244 |
+
for av in api_voices:
|
| 245 |
+
if av not in candidates:
|
| 246 |
+
candidates.append(av)
|
| 247 |
|
| 248 |
# Deduplicate preserving order
|
| 249 |
seen = set()
|
| 250 |
candidates = [x for x in candidates if not (x in seen or seen.add(x))]
|
| 251 |
|
| 252 |
# Try each candidate until one works
|
| 253 |
+
for i, candidate_voice in enumerate(candidates[:8]): # try up to 8
|
| 254 |
print(f"[YourVoic] Trying voice '{candidate_voice}' for {language} (attempt {i+1})")
|
| 255 |
wav_path, transcript, error = generate_speech_yourvoic(
|
| 256 |
client, text, candidate_voice, yv_model, emotion, language, lang_config,
|
|
|
|
| 260 |
# Cache this working voice for future chunks
|
| 261 |
if language not in YOURVOIC_VOICE_MAP or not YOURVOIC_VOICE_MAP.get(language):
|
| 262 |
YOURVOIC_VOICE_MAP[language] = [candidate_voice]
|
| 263 |
+
elif candidate_voice not in YOURVOIC_VOICE_MAP[language]:
|
| 264 |
+
YOURVOIC_VOICE_MAP[language].insert(0, candidate_voice)
|
| 265 |
return wav_path, transcript, None
|
| 266 |
if error and "Invalid voice name" not in str(error):
|
| 267 |
# Non-voice error (credits, etc) - don't try more voices
|
| 268 |
return None, transcript, error
|
| 269 |
|
| 270 |
+
return None, text, f"No valid voice found for {language}. This language may not be supported on your plan. Tried: {candidates[:8]}"
|
| 271 |
|
| 272 |
YOURVOIC_MODELS = [
|
| 273 |
"aura-prime -- Balanced quality and speed (recommended)",
|
|
|
|
| 784 |
DESCRIPTION = """
|
| 785 |
# Audiobook Generator
|
| 786 |
### English Text to Multi-Language Audiobook
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
"""
|
| 788 |
|
| 789 |
# Build language dropdown
|
|
|
|
| 937 |
|
| 938 |
gr.Markdown(
|
| 939 |
"---\n"
|
| 940 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 941 |
|
| 942 |
if __name__ == "__main__":
|
| 943 |
demo.launch()
|