| import os |
| import time |
| import asyncio |
| import logging |
| import threading |
| from typing import List, Optional, Any |
| from fastapi import FastAPI, Request |
| from fastapi.responses import HTMLResponse, FileResponse, JSONResponse |
| from fastapi.staticfiles import StaticFiles |
| from pydantic import BaseModel |
| import edge_tts |
| from deep_translator import GoogleTranslator |
| import uuid |
|
|
| |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
| AUDIO_DIR = "audio_cache" |
| os.makedirs(AUDIO_DIR, exist_ok=True) |
|
|
| |
| def cleanup_old_audio(): |
| while True: |
| try: |
| now = time.time() |
| for filename in os.listdir(AUDIO_DIR): |
| filepath = os.path.join(AUDIO_DIR, filename) |
| if os.stat(filepath).st_mtime < now - 3600: |
| os.remove(filepath) |
| except Exception as e: |
| logging.error(f"Cleanup error: {e}") |
| time.sleep(1800) |
|
|
| threading.Thread(target=cleanup_old_audio, daemon=True).start() |
|
|
| |
| |
| LANGUAGES_MAP = { |
| |
| "انگلیسی": {"code": "en", "voices": {'زن - جنی (آمریکا)': 'en-US-JennyNeural', 'مرد - گای (آمریکا)': 'en-US-GuyNeural', 'زن - آریا (آمریکا)': 'en-US-AriaNeural', 'مرد - کریستوفر (آمریکا)': 'en-US-ChristopherNeural', 'زن - لیبی (بریتانیا)': 'en-GB-LibbyNeural', 'مرد - رایان (بریتانیا)': 'en-GB-RyanNeural', 'زن - ناتاشا (استرالیا)': 'en-AU-NatashaNeural', 'مرد - ویلیام (استرالیا)': 'en-AU-WilliamNeural', 'مرد - لیام (کانادا)': 'en-CA-LiamNeural', 'زن - کلارا (کانادا)': 'en-CA-ClaraNeural', 'زن - نیرجا (هند)': 'en-IN-NeerjaNeural', 'مرد - پرابات (هند)': 'en-IN-PrabhatNeural', 'زن - آسیمی (نیجریه)': 'en-NG-EzinneNeural', 'مرد - آبه (نیجریه)': 'en-NG-AbeoNeural'}}, |
| "فارسی": {"code": "fa", "voices": {'زن - دلارا': 'fa-IR-DilaraNeural', 'مرد - فرید': 'fa-IR-FaridNeural'}}, |
| "عربی": {"code": "ar", "voices": {'زن - فاطمه (امارات)': 'ar-AE-FatimaNeural', 'مرد - حمدان (امارات)': 'ar-AE-HamdanNeural', 'زن - سلما (مصر)': 'ar-EG-SalmaNeural', 'مرد - شاکر (مصر)': 'ar-EG-ShakirNeural', 'مرد - حامد (عربستان)': 'ar-SA-HamedNeural', 'زن - زاریة (عربستان)': 'ar-SA-ZariyahNeural'}}, |
| "چینی": {"code": "zh-CN", "voices": {'مرد - یونشی': 'zh-CN-YunxiNeural', 'زن - شیائوشیا': 'zh-CN-XiaoxiaoNeural', 'زن - شیائویی': 'zh-CN-XiaoyiNeural', 'مرد - یونجیان': 'zh-CN-YunjianNeural'}}, |
| "چینی (سنتی)": {"code": "zh-TW", "voices": {'زن - هسیاوچن': 'zh-TW-HsiaoChenNeural', 'مرد - یونژه': 'zh-TW-YunJheNeural', 'زن - هسیاویو': 'zh-TW-HsiaoYuNeural'}}, |
| "ترکی": {"code": "tr", "voices": {'زن - امل': 'tr-TR-EmelNeural', 'مرد - احمد': 'tr-TR-AhmetNeural'}}, |
| "فرانسوی": {"code": "fr", "voices": {'زن - الویز (فرانسه)': 'fr-FR-EloiseNeural', 'مرد - هنری (فرانسه)': 'fr-FR-HenriNeural', 'زن - دنیس (فرانسه)': 'fr-FR-DeniseNeural', 'زن - سیلویا (کانادا)': 'fr-CA-SylvieNeural', 'مرد - آنتوان (کانادا)': 'fr-CA-AntoineNeural', 'زن - آریان (سوئیس)': 'fr-CH-ArianeNeural', 'مرد - فابریس (سوئیس)': 'fr-CH-FabriceNeural', 'زن - شارلین (بلژیک)': 'fr-BE-CharlineNeural', 'مرد - جرارد (بلژیک)': 'fr-BE-GerardNeural'}}, |
| "آلمانی": {"code": "de", "voices": {'زن - کاترین (آلمان)': 'de-DE-KatjaNeural', 'مرد - کنراد (آلمان)': 'de-DE-ConradNeural', 'زن - آملی (آلمان)': 'de-DE-AmelieNeural', 'زن - اینگرید (اتریش)': 'de-AT-IngridNeural', 'مرد - یوناس (اتریش)': 'de-AT-JonasNeural', 'زن - لنی (سوئیس)': 'de-CH-LeniNeural', 'مرد - یان (سوئیس)': 'de-CH-JanNeural'}}, |
| "اسپانیایی": {"code": "es", "voices": {'زن - الویرا (اسپانیا)': 'es-ES-ElviraNeural', 'مرد - آلوارو (اسپانیا)': 'es-ES-AlvaroNeural', 'زن - دالیا (مکزیک)': 'es-MX-DaliaNeural', 'مرد - خورخه (مکزیک)': 'es-MX-JorgeNeural', 'زن - النا (آرژانتین)': 'es-AR-ElenaNeural', 'مرد - توماس (آرژانتین)': 'es-AR-TomasNeural', 'زن - سالومه (کلمبیا)': 'es-CO-SalomeNeural', 'مرد - گونزالو (کلمبیا)': 'es-CO-GonzaloNeural'}}, |
| "روسی": {"code": "ru", "voices": {'زن - سوتلانا': 'ru-RU-SvetlanaNeural', 'مرد - دیمیتری': 'ru-RU-DmitryNeural', 'زن - داریا': 'ru-RU-DariyaNeural'}}, |
| "ژاپنی": {"code": "ja", "voices": {'زن - نانامی': 'ja-JP-NanamiNeural', 'مرد - کیتا': 'ja-JP-KeitaNeural', 'زن - آئوی': 'ja-JP-AoiNeural'}}, |
| "ایتالیایی": {"code": "it", "voices": {'زن - السا': 'it-IT-ElsaNeural', 'مرد - دیگو': 'it-IT-DiegoNeural', 'زن - ایزابلا': 'it-IT-IsabellaNeural'}}, |
| "کرهای": {"code": "ko", "voices": {'زن - سونوهی': 'ko-KR-SunHiNeural', 'مرد - اینجون': 'ko-KR-InJoonNeural', 'زن - جیمین': 'ko-KR-JiMinNeural'}}, |
| "هندی": {"code": "hi", "voices": {'زن - مادور': 'hi-IN-MadhurNeural', 'مرد - سوآرا': 'hi-IN-SwaraNeural'}}, |
| "هلندی": {"code": "nl", "voices": {'زن - کوئلت': 'nl-NL-ColetteNeural', 'مرد - مارتین': 'nl-NL-MaartenNeural', 'زن - دنا (بلژیک)': 'nl-BE-DenaNeural', 'مرد - آرنو (بلژیک)': 'nl-BE-ArnaudNeural'}}, |
| "سوئدی": {"code": "sv", "voices": {'زن - سوفی': 'sv-SE-SofieNeural', 'مرد - ماتیاس': 'sv-SE-MattiasNeural'}}, |
| "لهستانی": {"code": "pl", "voices": {'زن - زوفیا': 'pl-PL-ZofiaNeural', 'مرد - مارک': 'pl-PL-MarekNeural'}}, |
| "پرتغالی": {"code": "pt", "voices": {'زن - فرانسیسکا (برزیل)': 'pt-BR-FranciscaNeural', 'مرد - آنتونیو (برزیل)': 'pt-BR-AntonioNeural', 'زن - راکل (پرتغال)': 'pt-PT-RaquelNeural', 'مرد - دوارته (پرتغال)': 'pt-PT-DuarteNeural'}}, |
| |
| |
| "آفریقانس": {"code": "af", "voices": {'زن - آدریا': 'af-ZA-AdriNeural', 'مرد - ویلم': 'af-ZA-WillemNeural'}}, |
| "آلبانیایی": {"code": "sq", "voices": {'زن - آنیرا': 'sq-AL-AnilaNeural', 'مرد - ایلیر': 'sq-AL-IlirNeural'}}, |
| "آمهاری": {"code": "am", "voices": {'زن - مکدز': 'am-ET-MekdesNeural', 'مرد - آمها': 'am-ET-AmehaNeural'}}, |
| "ارمنی": {"code": "hy", "voices": {}}, |
| "آذربایجانی": {"code": "az", "voices": {'زن - بانو': 'az-AZ-BanuNeural', 'مرد - بابک': 'az-AZ-BabekNeural'}}, |
| "باسک": {"code": "eu", "voices": {'زن - آینهوا': 'eu-ES-AinhoaNeural', 'مرد - آندر': 'eu-ES-AnderNeural'}}, |
| "بلاروسی": {"code": "be", "voices": {}}, |
| "بنگالی": {"code": "bn", "voices": {'زن - نابانیا (بنگلادش)': 'bn-BD-NabanitaNeural', 'مرد - پردیپ (بنگلادش)': 'bn-BD-PradeepNeural', 'زن - تانیا (هند)': 'bn-IN-TanyaNeural', 'مرد - بشکر (هند)': 'bn-IN-BashkarNeural'}}, |
| "بوسنیایی": {"code": "bs", "voices": {'زن - وسنا': 'bs-BA-VesnaNeural', 'مرد - گوران': 'bs-BA-GoranNeural'}}, |
| "بلغاری": {"code": "bg", "voices": {'زن - کالینا': 'bg-BG-KalinaNeural', 'مرد - بوریس': 'bg-BG-BorislavNeural'}}, |
| "کاتالان": {"code": "ca", "voices": {'زن - جوآنا': 'ca-ES-JoanaNeural', 'مرد - انریک': 'ca-ES-EnricNeural'}}, |
| "سبوانو": {"code": "ceb", "voices": {}}, |
| "کورسی": {"code": "co", "voices": {}}, |
| "کروات": {"code": "hr", "voices": {'زن - گابریلا': 'hr-HR-GabrijelaNeural', 'مرد - سرکو': 'hr-HR-SreckoNeural'}}, |
| "چک": {"code": "cs", "voices": {'زن - ولاستا': 'cs-CZ-VlastaNeural', 'مرد - آنتونین': 'cs-CZ-AntoninNeural'}}, |
| "دانمارکی": {"code": "da", "voices": {'زن - کریستل': 'da-DK-ChristelNeural', 'مرد - جپ': 'da-DK-JeppeNeural'}}, |
| "اسپرانتو": {"code": "eo", "voices": {}}, |
| "استونیایی": {"code": "et", "voices": {'زن - آنو': 'et-EE-AnuNeural', 'مرد - کرت': 'et-EE-KertNeural'}}, |
| "فنلاندی": {"code": "fi", "voices": {'زن - سلما': 'fi-FI-SelmaNeural', 'مرد - هری': 'fi-FI-HarriNeural'}}, |
| "فریسی": {"code": "fy", "voices": {}}, |
| "گالیسیایی": {"code": "gl", "voices": {'زن - سابیا': 'gl-ES-SabelaNeural', 'مرد - روی': 'gl-ES-RoiNeural'}}, |
| "گرجی": {"code": "ka", "voices": {'زن - اکا': 'ka-GE-EkaNeural', 'مرد - گیورگی': 'ka-GE-GiorgiNeural'}}, |
| "یونانی": {"code": "el", "voices": {'زن - آتینا': 'el-GR-AthinaNeural', 'مرد - نستوراس': 'el-GR-NestorasNeural'}}, |
| "گجراتی": {"code": "gu", "voices": {'زن - دوانی': 'gu-IN-DhwaniNeural', 'مرد - نیرانجان': 'gu-IN-NiranjanNeural'}}, |
| "هائیتی": {"code": "ht", "voices": {}}, |
| "هاوسا": {"code": "ha", "voices": {}}, |
| "هاوایی": {"code": "haw", "voices": {}}, |
| "عبری": {"code": "iw", "voices": {'زن - هیلا': 'he-IL-HilaNeural', 'مرد - آوری': 'he-IL-AvriNeural'}}, |
| "همونگ": {"code": "hmn", "voices": {}}, |
| "مجاری": {"code": "hu", "voices": {'زن - نوئمی': 'hu-HU-NoemiNeural', 'مرد - تاماس': 'hu-HU-TamasNeural'}}, |
| "ایسلندی": {"code": "is", "voices": {'زن - گودرون': 'is-IS-GudrunNeural', 'مرد - گونار': 'is-IS-GunnarNeural'}}, |
| "ایگبو": {"code": "ig", "voices": {}}, |
| "اندونزیایی": {"code": "id", "voices": {'زن - گادیس': 'id-ID-GadisNeural', 'مرد - آردی': 'id-ID-ArdiNeural'}}, |
| "ایرلندی": {"code": "ga", "voices": {'زن - اورلا': 'ga-IE-OrlaNeural', 'مرد - کولم': 'ga-IE-ColmNeural'}}, |
| "جاوهای": {"code": "jw", "voices": {'زن - سیتی': 'jv-ID-SitiNeural', 'مرد - بیما': 'jv-ID-BimasenaNeural'}}, |
| "کانادا": {"code": "kn", "voices": {'زن - ساپانا': 'kn-IN-SapnaNeural', 'مرد - گاگان': 'kn-IN-GaganNeural'}}, |
| "قزاقی": {"code": "kk", "voices": {'زن - آیگول': 'kk-KZ-AigulNeural', 'مرد - دولت': 'kk-KZ-DauletNeural'}}, |
| "خمر": {"code": "km", "voices": {'زن - سریموچ': 'km-KH-SreymomNeural', 'مرد - پیسث': 'km-KH-PisethNeural'}}, |
| "کردی": {"code": "ku", "voices": {}}, |
| "قرقیزی": {"code": "ky", "voices": {}}, |
| "لائو": {"code": "lo", "voices": {'زن - چانتاوونگ': 'lo-LA-ChanthavongNeural', 'مرد - کئو': 'lo-LA-KeoNeural'}}, |
| "لاتین": {"code": "la", "voices": {}}, |
| "لتونیایی": {"code": "lv", "voices": {'زن - اورلیا': 'lv-LV-EveritaNeural', 'مرد - نیلس': 'lv-LV-NilsNeural'}}, |
| "لیتوانیایی": {"code": "lt", "voices": {'زن - اونا': 'lt-LT-OnaNeural', 'مرد - لئوناس': 'lt-LT-LeonasNeural'}}, |
| "لوکزامبورگی": {"code": "lb", "voices": {}}, |
| "مقدونی": {"code": "mk", "voices": {'زن - ماریا': 'mk-MK-MarijaNeural', 'مرد - الکساندر': 'mk-MK-AleksandarNeural'}}, |
| "مالاگاسی": {"code": "mg", "voices": {}}, |
| "مالایی": {"code": "ms", "voices": {'زن - یاسمین': 'ms-MY-YasminNeural', 'مرد - عثمان': 'ms-MY-OsmanNeural'}}, |
| "مالایالم": {"code": "ml", "voices": {'زن - سوبهانجا': 'ml-IN-SobhanaNeural', 'مرد - میدون': 'ml-IN-MidhunNeural'}}, |
| "مالتی": {"code": "mt", "voices": {'زن - گریس': 'mt-MT-GraceNeural', 'مرد - جوزف': 'mt-MT-JosephNeural'}}, |
| "مائوری": {"code": "mi", "voices": {}}, |
| "مراتی": {"code": "mr", "voices": {'زن - آروهی': 'mr-IN-AarohiNeural', 'مرد - مانوهر': 'mr-IN-ManoharNeural'}}, |
| "مغولی": {"code": "mn", "voices": {'زن - یسوی': 'mn-MN-YesuiNeural', 'مرد - باتا': 'mn-MN-BataaNeural'}}, |
| "میانماری (برمهای)": {"code": "my", "voices": {'زن - نیلار': 'my-MM-NilarNeural', 'مرد - تیها': 'my-MM-ThihaNeural'}}, |
| "نپالی": {"code": "ne", "voices": {'زن - هما': 'ne-NP-HemkalaNeural', 'مرد - ساگار': 'ne-NP-SagarNeural'}}, |
| "نروژی": {"code": "no", "voices": {'زن - پرنیله': 'nb-NO-PernilleNeural', 'مرد - فین': 'nb-NO-FinnNeural'}}, |
| "پشتو": {"code": "ps", "voices": {'زن - لطیفه': 'ps-AF-LatifaNeural', 'مرد - گلنواز': 'ps-AF-GulNawazNeural'}}, |
| "پنجابی": {"code": "pa", "voices": {}}, |
| "رومانیایی": {"code": "ro", "voices": {'زن - آلینا': 'ro-RO-AlinaNeural', 'مرد - امیل': 'ro-RO-EmilNeural'}}, |
| "صربی": {"code": "sr", "voices": {'زن - سوفیا': 'sr-RS-SophieNeural', 'مرد - نیکولا': 'sr-RS-NicholasNeural'}}, |
| "سسوتو": {"code": "st", "voices": {}}, |
| "سینهالی": {"code": "si", "voices": {'زن - تیمی': 'si-LK-ThiliniNeural', 'مرد - سامیرا': 'si-LK-SameeraNeural'}}, |
| "اسلواکی": {"code": "sk", "voices": {'زن - ویکتوریا': 'sk-SK-ViktoriaNeural', 'مرد - لوکاس': 'sk-SK-LukasNeural'}}, |
| "اسلوونیایی": {"code": "sl", "voices": {'زن - پترا': 'sl-SI-PetraNeural', 'مرد - روک': 'sl-SI-RokNeural'}}, |
| "سومالیایی": {"code": "so", "voices": {'زن - اوباخ': 'so-SO-UbaxNeural', 'مرد - موزه': 'so-SO-MuuseNeural'}}, |
| "سوندانی": {"code": "su", "voices": {'زن - توتی': 'su-ID-TutiNeural', 'مرد - جاجانگ': 'su-ID-JajangNeural'}}, |
| "سواحیلی": {"code": "sw", "voices": {'زن - زوری (کنیا)': 'sw-KE-ZuriNeural', 'مرد - رفیق (کنیا)': 'sw-KE-RafikiNeural', 'زن - ریما (تانزانیا)': 'sw-TZ-RehemaNeural', 'مرد - دادی (تانزانیا)': 'sw-TZ-DaudiNeural'}}, |
| "تاجیکی": {"code": "tg", "voices": {}}, |
| "تامیلی": {"code": "ta", "voices": {'زن - پالاوی (هند)': 'ta-IN-PallaviNeural', 'مرد - والان (هند)': 'ta-IN-ValluvarNeural', 'زن - کانی (سریلانکا)': 'ta-LK-KaniNeural', 'مرد - کومار (سریلانکا)': 'ta-LK-KumarNeural'}}, |
| "تلوگو": {"code": "te", "voices": {'زن - شروتی': 'te-IN-ShrutiNeural', 'مرد - موهان': 'te-IN-MohanNeural'}}, |
| "تایلندی": {"code": "th", "voices": {'زن - پرموادی': 'th-TH-PremwadeeNeural', 'مرد - نیوات': 'th-TH-NiwatNeural'}}, |
| "اوکراینی": {"code": "uk", "voices": {'زن - پولینا': 'uk-UA-PolinaNeural', 'مرد - اوستاپ': 'uk-UA-OstapNeural'}}, |
| "اردو": {"code": "ur", "voices": {'زن - گل (پاکستان)': 'ur-PK-UzmaNeural', 'مرد - اسد (پاکستان)': 'ur-PK-AsadNeural', 'زن - گلرخ (هند)': 'ur-IN-GulNeural', 'مرد - سلمان (هند)': 'ur-IN-SalmanNeural'}}, |
| "ازبکی": {"code": "uz", "voices": {'زن - مدینه': 'uz-UZ-MadinaNeural', 'مرد - سردار': 'uz-UZ-SardorNeural'}}, |
| "ویتنامی": {"code": "vi", "voices": {'زن - هوآیمی': 'vi-VN-HoaiMyNeural', 'مرد - ناممین': 'vi-VN-NamMinhNeural'}}, |
| "ولزی": {"code": "cy", "voices": {'زن - نیا': 'cy-GB-NiaNeural', 'مرد - آلد': 'cy-GB-AledNeural'}}, |
| "خوسا": {"code": "xh", "voices": {}}, |
| "یدی": {"code": "yi", "voices": {}}, |
| "یوروبا": {"code": "yo", "voices": {}}, |
| "زولو": {"code": "zu", "voices": {'زن - تاندکا': 'zu-ZA-ThandoNeural', 'مرد - تبوگو': 'zu-ZA-ThembaNeural'}} |
| } |
|
|
| |
| LEGACY_VOICES = { |
| 'انگلیسی (آمریکا) - جنی (زن)': 'en-US-JennyNeural', 'انگلیسی (آمریکا) - گای (مرد)': 'en-US-GuyNeural', |
| 'انگلیسی (آمریکا) - آنا (زن، صدای کودک)': 'en-US-AnaNeural', 'انگلیسی (آمریکا) - آریا (زن)': 'en-US-AriaNeural', |
| 'انگلیسی (بریتانیا) - لیبی (زن)': 'en-GB-LibbyNeural', 'انگلیسی (بریتانیا) - رایان (مرد)': 'en-GB-RyanNeural', |
| } |
|
|
| |
| app = FastAPI(title="Alpha Translator API") |
| app.mount("/audio_cache", StaticFiles(directory=AUDIO_DIR), name="audio_cache") |
|
|
| |
| async def translate_text(text: str, source: str, target: str) -> str: |
| def _do_translate(): |
| return GoogleTranslator(source=source, target=target).translate(text) |
| try: |
| return await asyncio.to_thread(_do_translate) |
| except Exception as e: |
| logging.error(f"Translation Error: {e}") |
| return f"خطا در ترجمه: {str(e)}" |
|
|
| async def generate_audio(text: str, voice_id: str, rate: int, volume: int, pitch: int) -> str: |
| filename = f"{uuid.uuid4().hex}.mp3" |
| filepath = os.path.join(AUDIO_DIR, filename) |
| rate_str, volume_str, pitch_str = f"{int(rate):+g}%", f"{int(volume):+g}%", f"{int(pitch):+g}Hz" |
| try: |
| communicate = edge_tts.Communicate(text, voice_id, rate=rate_str, volume=volume_str, pitch=pitch_str) |
| await communicate.save(filepath) |
| return f"/audio_cache/{filename}" |
| except Exception as e: |
| logging.error(f"TTS Error: {e}") |
| return "" |
|
|
| |
| class TranslateRequest(BaseModel): |
| text: str |
| source_lang: str |
| target_lang: str |
| voice_key: str |
| rate: int = 0 |
| volume: int = 0 |
| pitch: int = 0 |
|
|
| class GradioLegacyRequest(BaseModel): |
| data: List[Any] |
|
|
| |
| @app.get("/") |
| async def serve_index(): |
| return FileResponse("index.html") |
|
|
| @app.get("/api/config") |
| async def get_config(): |
| return {"languages": LANGUAGES_MAP} |
|
|
| @app.post("/api/translate_and_speak") |
| async def api_translate_speak(req: TranslateRequest): |
| src_code = "auto" if req.source_lang == "شناسایی خودکار" else LANGUAGES_MAP.get(req.source_lang, {}).get("code", "auto") |
| target_lang_data = LANGUAGES_MAP.get(req.target_lang, {}) |
| tgt_code = target_lang_data.get("code", "en") |
| |
| |
| voice_id = target_lang_data.get("voices", {}).get(req.voice_key) |
| if not voice_id: |
| voices = list(target_lang_data.get("voices", {}).values()) |
| voice_id = voices[0] if voices else "en-US-JennyNeural" |
|
|
| |
| translated_text = await translate_text(req.text, src_code, tgt_code) |
| if "خطا در ترجمه" in translated_text: |
| return {"success": False, "error": translated_text} |
|
|
| |
| audio_url = "" |
| if target_lang_data.get("voices"): |
| audio_url = await generate_audio(translated_text, voice_id, req.rate, req.volume, req.pitch) |
| |
| return { |
| "success": True, |
| "translated_text": translated_text, |
| "audio_url": audio_url |
| } |
|
|
| |
| @app.post("/run/translate_and_speak") |
| @app.post("/api/translate_and_speak_legacy") |
| async def legacy_api(req: GradioLegacyRequest, request: Request): |
| """ |
| این بخش دقیقا ساختار Gradio را شبیه سازی میکند تا هیچ اسکریپتی از کار نیفتد. |
| """ |
| try: |
| text, voice_key, rate, volume, pitch = req.data[0], req.data[1], req.data[2], req.data[3], req.data[4] |
| |
| |
| translated_text = await translate_text(text, "fa", "en") |
| |
| |
| voice_id = LEGACY_VOICES.get(voice_key, "en-US-JennyNeural") |
| |
| audio_url = await generate_audio(translated_text, voice_id, rate, volume, pitch) |
| |
| |
| base_url = str(request.base_url).rstrip("/") |
| full_audio_url = f"{base_url}{audio_url}" if audio_url else None |
| |
| |
| return JSONResponse(content={ |
| "data": [ |
| translated_text, |
| {"name": full_audio_url, "data": full_audio_url, "is_file": True} if full_audio_url else None |
| ] |
| }) |
| except Exception as e: |
| return JSONResponse(content={"error": str(e)}, status_code=500) |