File size: 21,272 Bytes
ba4a16b
327b95d
7c3976d
 
327b95d
7c3976d
 
 
 
 
 
bc0e481
7c3976d
b5d553f
7c3976d
 
 
 
bbf0e20
7c3976d
 
 
 
 
 
 
 
 
 
 
 
f6f8a7e
7c3976d
59637c2
06d0b49
 
755f714
06d0b49
 
7c3976d
06d0b49
 
 
7c3976d
06d0b49
 
 
 
 
 
 
7c3976d
06d0b49
7c3976d
 
06d0b49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755f714
 
7c3976d
 
 
 
 
 
755f714
7c3976d
 
 
755f714
7c3976d
 
 
 
d6e8044
7c3976d
d6e8044
7c3976d
 
755f714
7c3976d
 
 
 
d6e8044
7c3976d
 
 
d6e8044
7c3976d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755f714
7c3976d
 
755f714
7c3976d
 
59637c2
7c3976d
 
 
 
59637c2
06d0b49
 
 
 
59637c2
7c3976d
 
 
 
 
755f714
7c3976d
 
 
 
 
 
 
 
 
755f714
7c3976d
 
59637c2
7c3976d
 
755f714
7c3976d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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'}}, # کد ترجمه به iw تغییر کرد
    "همونگ": {"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'}}, # کد ترجمه به jw تغییر کرد
    "کانادا": {"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'}}
}

# --- دیتابیس قدیمی برای پشتیبانی API دوستان شما ---
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] # [text, voice, rate, volume, pitch]

# --- Endpoints API وب‌سایت ---
@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"

    # 1. ترجمه
    translated_text = await translate_text(req.text, src_code, tgt_code)
    if "خطا در ترجمه" in translated_text:
        return {"success": False, "error": translated_text}

    # 2. تولید صدا (اگر زبانی صدا نداشته باشد، این بخش نادیده گرفته می‌شود)
    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
    }

# --- شبیه‌ساز API گاردین برای ربات‌ها/اسکریپت‌های دوستان شما ---
@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
        
        # خروجی دقیقاً با ساختار پاسخ Gradio
        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)