Spaces:
Running
Running
| import os | |
| import sys | |
| import subprocess | |
| import tempfile | |
| import soundfile as sf | |
| import librosa | |
| from fastapi import FastAPI, File, Form, UploadFile | |
| from fastapi.responses import FileResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| # 1. АВТОМАТИЧЕСКОЕ СКАЧИВАНИЕ ОРИГИНАЛЬНОГО РЕПОЗИТОРИЯ | |
| REPO_DIR = "MOSS-TTS-Nano" | |
| if not os.path.exists(REPO_DIR): | |
| print("Клонируем оригинальный репозиторий MOSS-TTS-Nano...") | |
| subprocess.run(["git", "clone", "https://github.com/OpenMOSS/MOSS-TTS-Nano.git"]) | |
| # Добавляем скачанную папку в пути Python, чтобы импорты работали | |
| sys.path.append(REPO_DIR) | |
| # 2. ИМПОРТИРУЕМ ОРИГИНАЛЬНЫЙ ДВИЖОК | |
| from moss_tts_nano_runtime import NanoTTSService | |
| app = FastAPI() | |
| # Разрешаем CORS для твоего React-интерфейса | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # 3. ИНИЦИАЛИЗАЦИЯ МОДЕЛИ (скачает веса при первом запуске) | |
| print("Загрузка весов MOSS-TTS-Nano (это может занять пару минут)...") | |
| tts_service = NanoTTSService( | |
| checkpoint_path="OpenMOSS-Team/MOSS-TTS-Nano", | |
| audio_tokenizer_path="OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano", | |
| device="auto" # Автоматически включит GPU, если он есть | |
| ) | |
| print("Модель успешно загружена и готова к работе!") | |
| async def root(): | |
| return {"message": "Бэкенд MOSS-TTS-Nano работает! Жду запросов от React UI на /api/predict"} | |
| async def predict( | |
| text: str = Form(...), | |
| speed: float = Form(1.0), | |
| mode: str = Form("standard"), | |
| speaker: str = Form("default"), | |
| reference_audio: UploadFile = File(None) | |
| ): | |
| prompt_audio_path = None | |
| try: | |
| # Если загружен файл для клонирования голоса | |
| if mode == "clone" and reference_audio is not None: | |
| temp_audio = tempfile.NamedTemporaryFile(delete=False, suffix=".wav") | |
| content = await reference_audio.read() | |
| temp_audio.write(content) | |
| temp_audio.close() | |
| prompt_audio_path = temp_audio.name | |
| # Выбор стандартного голоса | |
| voice_preset = "Junhao" # Дефолтный голос в MOSS | |
| # 4. ЗАПУСК ОРИГИНАЛЬНОГО СИНТЕЗА | |
| result = tts_service.synthesize( | |
| text=text, | |
| voice=voice_preset if not prompt_audio_path else None, | |
| prompt_audio_path=prompt_audio_path, | |
| mode="voice_clone", # В MOSS это универсальный режим | |
| ) | |
| audio_data = result['audio'] | |
| sample_rate = result['sample_rate'] | |
| # 5. ОБРАБОТКА СКОРОСТИ (если ползунок скорости изменен) | |
| if speed != 1.0: | |
| audio_data = librosa.effects.time_stretch(audio_data, rate=speed) | |
| # 6. СОХРАНЕНИЕ И ОТПРАВКА РЕЗУЛЬТАТА | |
| output_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav") | |
| sf.write(output_file.name, audio_data, sample_rate) | |
| return FileResponse( | |
| output_file.name, | |
| media_type="audio/wav", | |
| filename="output.wav" | |
| ) | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| return {"error": str(e)} | |
| finally: | |
| # Убираем за собой временные файлы | |
| if prompt_audio_path and os.path.exists(prompt_audio_path): | |
| os.remove(prompt_audio_path) | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |