Spaces:
Sleeping
Sleeping
File size: 6,711 Bytes
63f1d6d b6e79a7 9795009 192a34f ddd6448 743c0e7 ddd6448 f81803f 743c0e7 ddd6448 e40a9fa d7cf28b 743c0e7 ddd6448 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa 2cbf263 e40a9fa 2cbf263 e40a9fa 2cbf263 e40a9fa b2496fc e40a9fa 355ee60 e40a9fa ddd6448 e40a9fa a322c69 e40a9fa b6e79a7 e40a9fa 7e3a62b e40a9fa 743c0e7 e40a9fa 743c0e7 e40a9fa ddd6448 743c0e7 e40a9fa ddd6448 7e3a62b 743c0e7 7e3a62b e40a9fa 7e3a62b e40a9fa b6e79a7 e40a9fa ddd6448 e40a9fa 90c0176 e40a9fa 743c0e7 e40a9fa b2496fc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
import gradio as gr
import edge_tts
import asyncio
import tempfile
import os
import uuid
import re
import shutil
import emoji
from pydub import AudioSegment
# --- ПРОВЕРКА СЕРВЕРА ---
if not shutil.which("ffmpeg"):
print("⚠️ FFmpeg не найден! Убедитесь, что он установлен на хостинге.")
# --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ---
VOICES_CACHE = []
LANGUAGES_CACHE = []
TEMP_DIR = tempfile.gettempdir()
# --- ОЧИСТКА ТЕКСТА ---
def clean_text_server_side(text):
"""
Удаляет эмодзи и спецсимволы, чтобы робот их не читал.
Выполняется на сервере.
"""
if not text: return ""
# Удаляем звездочки, тильды и прочий мусор форматирования
text = re.sub(r'[*_~><^]', '', text)
# Удаляем эмодзи (превращаем их в пустоту)
text = emoji.replace_emoji(text, replace='')
# Убираем лишние пробелы
text = re.sub(r'\s+', ' ', text).strip()
return text
# --- ЗАГРУЗКА ГОЛОСОВ ---
async def load_voices_init():
global VOICES_CACHE, LANGUAGES_CACHE
try:
voices = await edge_tts.list_voices()
VOICES_CACHE = sorted(voices, key=lambda x: x['Locale'])
seen = set()
LANGUAGES_CACHE = []
for v in VOICES_CACHE:
if v['Locale'] not in seen:
seen.add(v['Locale'])
LANGUAGES_CACHE.append(v['Locale'])
LANGUAGES_CACHE.sort()
print(f"✅ Голоса загружены: {len(VOICES_CACHE)}")
except Exception as e:
print(f"❌ Ошибка: {e}")
LANGUAGES_CACHE = ["ru-RU", "en-US"]
# --- ФИЛЬТР ГОЛОСОВ (UI) ---
def update_voice_list(language):
if not language: return gr.Dropdown(choices=[])
filtered = [f"{v['ShortName']} ({v['Gender']})" for v in VOICES_CACHE if v['Locale'] == language]
# Ищем Светлану по дефолту
default_val = filtered[0] if filtered else None
for v in filtered:
if "Svetlana" in v:
default_val = v
break
return gr.Dropdown(choices=filtered, value=default_val)
# --- ГЕНЕРАЦИЯ (SERVER ENGINE) ---
async def generate_server_audio(text, voice_raw, rate, pitch):
if not text.strip():
raise gr.Warning("Текст пуст!")
if not voice_raw:
raise gr.Warning("Выберите голос!")
# Очистка
clean_txt = clean_text_server_side(text)
voice = voice_raw.split(" (")[0]
# Параметры
rate_str = f"{int(rate):+d}%"
pitch_str = f"{int(pitch):+d}Hz"
# Пути
temp_filename = f"raw_{uuid.uuid4().hex}.mp3"
temp_path = os.path.join(TEMP_DIR, temp_filename)
final_filename = f"RESULT_{uuid.uuid4().hex}.mp3"
final_path = os.path.join(TEMP_DIR, final_filename)
print(f"⚙️ [Server] Генерация: {voice} | Тон: {pitch_str}")
try:
# 1. Скачиваем аудио от Microsoft на диск сервера
comm = edge_tts.Communicate(clean_txt, voice, rate=rate_str, pitch=pitch_str)
await comm.save(temp_path)
# 2. Обрабатываем через Pydub (чтобы задействовать CPU сервера и проверить файл)
if os.path.exists(temp_path) and os.path.getsize(temp_path) > 0:
audio = AudioSegment.from_mp3(temp_path)
audio.export(final_path, format="mp3")
# Удаляем черновик
os.remove(temp_path)
return final_path
else:
raise Exception("Файл не создался (пустой).")
except Exception as e:
# Ловим ошибки 403 и прочие
if "403" in str(e):
raise gr.Error("Ошибка доступа (403). Сервер Microsoft временно недоступен.")
raise gr.Error(f"Ошибка сервера: {str(e)}")
# --- ЗАПУСК ---
# Грузим голоса перед стартом
asyncio.run(load_voices_init())
# НАСТРОЙКИ ПО УМОЛЧАНИЮ
DEFAULT_LANG = "ru-RU"
# Фильтруем список для русского языка
START_VOICES = [f"{v['ShortName']} ({v['Gender']})" for v in VOICES_CACHE if v['Locale'] == DEFAULT_LANG]
# Ставим Светлану
DEFAULT_VOICE = next((v for v in START_VOICES if "Svetlana" in v), START_VOICES[0] if START_VOICES else None)
# Стилизация
css = """
body {background-color: #111827; color: #e5e7eb;}
.container {max-width: 850px; margin: auto;}
h1 {color: #fbbf24; text-align: center; font-family: serif;}
"""
theme = gr.themes.Soft(primary_hue="amber", secondary_hue="slate")
with gr.Blocks(theme=theme, css=css, title="TTS Server Classic") as demo:
gr.Markdown("# 🎙️ TTS Server Classic")
with gr.Row():
# КОЛОНКА НАСТРОЕК
with gr.Column(scale=1):
gr.Markdown("### ⚙️ Параметры")
lang_dr = gr.Dropdown(
choices=LANGUAGES_CACHE,
value=DEFAULT_LANG,
label="Язык",
interactive=True
)
voice_dr = gr.Dropdown(
choices=START_VOICES,
value=DEFAULT_VOICE,
label="Голос",
interactive=True
)
gr.Markdown("---")
# Дефолт: -7 Hz, как ты просил
rate_sl = gr.Slider(-50, 50, value=0, step=1, label="Скорость (%)")
pitch_sl = gr.Slider(-20, 20, value=-7, step=1, label="Тон (Hz)")
# КОЛОНКА ТЕКСТА
with gr.Column(scale=2):
gr.Markdown("### 📝 Текст")
text_in = gr.Textbox(
label="",
lines=10,
placeholder="Введите текст...",
value=""
)
btn = gr.Button("🔊 Озвучить (Server)", variant="primary", size="lg")
audio_out = gr.Audio(label="Готовый файл", type="filepath")
# Логика интерфейса
lang_dr.change(update_voice_list, inputs=lang_dr, outputs=voice_dr)
btn.click(generate_server_audio, inputs=[text_in, voice_dr, rate_sl, pitch_sl], outputs=audio_out)
if __name__ == "__main__":
demo.queue().launch() |