VSPAN commited on
Commit
d7cf28b
·
verified ·
1 Parent(s): 5334ac4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +174 -52
app.py CHANGED
@@ -4,69 +4,191 @@ import asyncio
4
  import tempfile
5
  import re
6
  import emoji
7
- # Функция для очистки текста от нежелательных символов и эмодзи
 
 
 
 
 
 
 
 
 
8
  def clean_text(text):
9
- # Удаление указанных символов
 
 
 
 
 
10
  text = re.sub(r'[*_~><]', '', text)
11
- # Удаление эмодзи
12
  text = emoji.replace_emoji(text, replace='')
 
 
13
  return text
14
- # Get all available voices
15
- async def get_voices():
16
- voices = await edge_tts.list_voices()
17
- return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
18
- # Text-to-speech function
19
- async def text_to_speech(text, voice, rate, pitch):
20
- if not text.strip():
21
- return None, gr.Warning("Please enter text to convert.")
22
- if not voice:
23
- return None, gr.Warning("Please select a voice.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  # Очистка текста
26
- text = clean_text(text)
27
 
28
- voice_short_name = voice.split(" - ")[0]
29
  rate_str = f"{rate:+d}%"
30
  pitch_str = f"{pitch:+d}Hz"
31
- communicate = edge_tts.Communicate(text, voice_short_name, rate=rate_str, pitch=pitch_str)
32
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
33
- tmp_path = tmp_file.name
34
- try:
35
- await communicate.save(tmp_path)
36
- except Exception as e:
37
- return None, gr.Warning(f"An error occurred during text-to-speech conversion: {str(e)}")
38
- return tmp_path, None
39
- # Gradio interface function
40
- def tts_interface(text, voice, rate, pitch):
41
- audio, warning = asyncio.run(text_to_speech(text, voice, rate, pitch))
42
- return audio, warning
43
- # Create Gradio application
44
- async def create_demo():
45
- voices = await get_voices()
46
 
47
- description = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  """
49
-
50
- demo = gr.Interface(
51
- fn=tts_interface,
52
- inputs=[
53
- gr.Textbox(label="Input Text", lines=5),
54
- gr.Dropdown(choices=[""] + list(voices.keys()), label="Select Voice", value=""),
55
- gr.Slider(minimum=-50, maximum=50, value=0, label="Speech Rate Adjustment (%)", step=1),
56
- gr.Slider(minimum=-20, maximum=20, value=0, label="Pitch Adjustment (Hz)", step=1)
57
- ],
58
- outputs=[
59
- gr.Audio(label="Generated Audio", type="filepath"),
60
- gr.Markdown(label="Warning", visible=False)
61
- ],
62
- title="Edge TTS Text-to-Speech",
63
- description=description,
64
- article="",
65
- analytics_enabled=False,
66
- allow_flagging="manual"
67
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  return demo
69
- # Run the application
 
70
  if __name__ == "__main__":
71
- demo = asyncio.run(create_demo())
72
- demo.launch()
 
 
 
4
  import tempfile
5
  import re
6
  import emoji
7
+ import os
8
+ import uuid
9
+
10
+ # Глобальные переменные для хранения голосов
11
+ VOICES_DATA = []
12
+ VOICES_BY_GENDER = {}
13
+ LANGUAGES = []
14
+
15
+ # --- Вспомогательные функции ---
16
+
17
  def clean_text(text):
18
+ """
19
+ Очищает текст от спецсимволов и эмодзи для корректного озвучивания.
20
+ """
21
+ if not text:
22
+ return ""
23
+ # Удаление указанных спецсимволов
24
  text = re.sub(r'[*_~><]', '', text)
25
+ # Удаление эмодзи (библиотека emoji 2.6.0 использует replace_emoji)
26
  text = emoji.replace_emoji(text, replace='')
27
+ # Удаление лишних пробелов
28
+ text = re.sub(r'\s+', ' ', text).strip()
29
  return text
30
+
31
+ async def load_voices_async():
32
+ """
33
+ Асинхронная загрузка списка голосов при старте.
34
+ """
35
+ global VOICES_DATA, LANGUAGES
36
+ try:
37
+ voices = await edge_tts.list_voices()
38
+ # Сортируем для удобства
39
+ VOICES_DATA = sorted(voices, key=lambda x: x['Locale'])
40
+
41
+ # Собираем уникальные языки (Locales)
42
+ seen_langs = set()
43
+ langs_list = []
44
+ for v in VOICES_DATA:
45
+ locale = v['Locale']
46
+ if locale not in seen_langs:
47
+ seen_langs.add(locale)
48
+ langs_list.append(locale)
49
+ LANGUAGES = sorted(langs_list)
50
+
51
+ print(f"✅ Успешно загружено {len(VOICES_DATA)} голосов и {len(LANGUAGES)} языков.")
52
+ except Exception as e:
53
+ print(f"❌ Ошибка при загрузке голосов: {e}")
54
+
55
+ def filter_voices_by_language(language):
56
+ """
57
+ Возвращает список читаемых имен голосов для выбранного языка.
58
+ Формат: "ShortName (Gender)"
59
+ """
60
+ if not language:
61
+ return gr.Dropdown(choices=[])
62
+
63
+ filtered_voices = [
64
+ f"{v['ShortName']} ({v['Gender']})"
65
+ for v in VOICES_DATA
66
+ if v['Locale'] == language
67
+ ]
68
+ # Выбираем первый голос по умолчанию, если список не пуст
69
+ first_value = filtered_voices[0] if filtered_voices else None
70
+ return gr.Dropdown(choices=filtered_voices, value=first_value, interactive=True)
71
+
72
+ # --- Основная функция генерации (Async) ---
73
+
74
+ async def generate_speech(text, voice_friendly_name, rate, pitch):
75
+ """
76
+ Генерирует аудио из текста. Функция асинхронна, Gradio 4.x поддерживает это нативно.
77
+ """
78
+ if not text or not text.strip():
79
+ raise gr.Error("Пожалуйста, введите текст для озвучивания.")
80
+
81
+ if not voice_friendly_name:
82
+ raise gr.Error("Пожалуйста, выберите голос.")
83
+
84
+ # Извлекаем реальное имя голоса (ShortName) из строки "ShortName (Gender)"
85
+ voice_short_name = voice_friendly_name.split(" (")[0]
86
 
87
  # Очистка текста
88
+ clean_input = clean_text(text)
89
 
90
+ # Форматирование параметров
91
  rate_str = f"{rate:+d}%"
92
  pitch_str = f"{pitch:+d}Hz"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ print(f"🔄 Генерация: Голос={voice_short_name}, Скорость={rate_str}, Тон={pitch_str}")
95
+
96
+ try:
97
+ communicate = edge_tts.Communicate(clean_input, voice_short_name, rate=rate_str, pitch=pitch_str)
98
+
99
+ # Создаем уникальное имя файла во временной директории
100
+ filename = f"tts_{uuid.uuid4()}.mp3"
101
+ output_path = os.path.join(tempfile.gettempdir(), filename)
102
+
103
+ await communicate.save(output_path)
104
+
105
+ return output_path
106
+ except Exception as e:
107
+ raise gr.Error(f"Ошибка генерации: {str(e)}")
108
+
109
+ # --- Построение Интерфейса Gradio (Blocks) ---
110
+
111
+ def create_demo():
112
+ # Запускаем цикл загрузки голосов синхронно перед отрисовкой UI
113
+ asyncio.run(load_voices_async())
114
+
115
+ css = """
116
+ .container { max-width: 900px; margin: auto; }
117
+ h1 { text-align: center; margin-bottom: 20px; }
118
  """
119
+
120
+ theme = gr.themes.Soft(
121
+ primary_hue="blue",
122
+ secondary_hue="indigo",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  )
124
+
125
+ with gr.Blocks(theme=theme, css=css, title="Ultra TTS") as demo:
126
+
127
+ gr.Markdown("# 🎧 Edge TTS: Генератор речи (High Quality)")
128
+ gr.Markdown("Преобразуйте текст в реалистичную речь бесплатно, используя Microsoft Edge Online Voices.")
129
+
130
+ with gr.Row():
131
+ with gr.Column(scale=1):
132
+ # Левая колонка: Настройки голоса
133
+ gr.Markdown("### 1. Выбор голоса")
134
+
135
+ lang_dropdown = gr.Dropdown(
136
+ choices=LANGUAGES,
137
+ label="Язык / Регион",
138
+ value="ru-RU" if "ru-RU" in LANGUAGES else LANGUAGES[0] if LANGUAGES else None,
139
+ interactive=True
140
+ )
141
+
142
+ # Инициализируем список голосов для дефолтного языка
143
+ initial_voices = [f"{v['ShortName']} ({v['Gender']})" for v in VOICES_DATA if v['Locale'] == lang_dropdown.value]
144
+
145
+ voice_dropdown = gr.Dropdown(
146
+ choices=initial_voices,
147
+ value=initial_voices[0] if initial_voices else None,
148
+ label="Голос",
149
+ interactive=True
150
+ )
151
+
152
+ gr.Markdown("### 3. Настройки звучания")
153
+ rate_slider = gr.Slider(minimum=-50, maximum=50, value=0, step=1, label="Скорость (%)")
154
+ pitch_slider = gr.Slider(minimum=-20, maximum=20, value=0, step=1, label="Высота тона (Hz)")
155
+
156
+ with gr.Column(scale=2):
157
+ # Правая колонка: Текст и результат
158
+ gr.Markdown("### 2. Текст")
159
+ text_input = gr.Textbox(
160
+ label="Введите текст здесь",
161
+ placeholder="Привет! Это пример текста для озвучивания.",
162
+ lines=8,
163
+ max_lines=20
164
+ )
165
+
166
+ generate_btn = gr.Button("🔊 Создать аудио", variant="primary", size="lg")
167
+
168
+ gr.Markdown("### 4. Результат")
169
+ audio_output = gr.Audio(label="Сгенерированное аудио", type="filepath", interactive=False)
170
+
171
+ # --- Логика взаимодействия ---
172
+
173
+ # Обновление списка голосов при смене языка
174
+ lang_dropdown.change(
175
+ fn=filter_voices_by_language,
176
+ inputs=[lang_dropdown],
177
+ outputs=[voice_dropdown]
178
+ )
179
+
180
+ # Генерация аудио
181
+ generate_btn.click(
182
+ fn=generate_speech,
183
+ inputs=[text_input, voice_dropdown, rate_slider, pitch_slider],
184
+ outputs=[audio_output]
185
+ )
186
+
187
  return demo
188
+
189
+ # --- Запуск приложения ---
190
  if __name__ == "__main__":
191
+ # Создаем демо
192
+ demo_app = create_demo()
193
+ # Запускаем сервер
194
+ demo_app.launch(server_name="0.0.0.0", server_port=7860, show_error=True)