Hamed744 commited on
Commit
50ab425
·
verified ·
1 Parent(s): 11bff8c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -35
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - نسخه کامل و نهایی برای تمام اسپیس‌های Hugging Face
2
 
3
  import os
4
  import sys
@@ -15,8 +15,6 @@ from fastapi import FastAPI, HTTPException
15
  from pydantic import BaseModel
16
  from google import genai
17
  from google.genai import types
18
-
19
- # اضافه کردن uvicorn برای اجرا از داخل اسکریپت
20
  import uvicorn
21
 
22
  try:
@@ -25,12 +23,11 @@ try:
25
  except ImportError:
26
  PYDUB_AVAILABLE = False
27
 
28
- # --- پیکربندی لاگینگ ---
29
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
30
 
31
- # --- START: تعریف تمام توابع کمکی ---
 
32
 
33
- # --- منطق مدیریت API Key ---
34
  ALL_API_KEYS: list[str] = []
35
  NEXT_KEY_INDEX: int = 0
36
  KEY_LOCK: threading.Lock = threading.Lock()
@@ -42,14 +39,33 @@ def _init_api_keys():
42
  ALL_API_KEYS = [key.strip() for key in all_keys_string.split(',') if key.strip()]
43
  logging.info(f"✅ تعداد {len(ALL_API_KEYS)} کلید API جیمینای بارگذاری شد.")
44
  if not ALL_API_KEYS:
45
- logging.warning("⛔️ هشدار: هیچ Secret با نام ALL_GEMINI_API_KEYS یافت نشد! برنامه بدون کلید API کار نخواهد کرد.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # --- ثابت‌ها ---
48
  FIXED_MODEL_NAME = "gemini-2.5-flash-preview-tts"
49
  DEFAULT_MAX_CHUNK_SIZE = 3800
50
  DEFAULT_SLEEP_BETWEEN_REQUESTS = 8
51
 
52
- # --- توابع کمکی فایل و صدا ---
53
  def save_binary_file(file_name, data):
54
  try:
55
  with open(file_name, "wb") as f: f.write(data)
@@ -107,35 +123,33 @@ def merge_audio_files_func(file_paths, output_path):
107
  return True
108
  except Exception as e: logging.error(f"❌ خطا در ادغام فایل‌های صوتی: {e}"); return False
109
 
110
- def get_next_api_key():
111
- global NEXT_KEY_INDEX, ALL_API_KEYS, KEY_LOCK
112
- with KEY_LOCK:
113
- if not ALL_API_KEYS: return None, None
114
- key_to_use = ALL_API_KEYS[NEXT_KEY_INDEX % len(ALL_API_KEYS)]
115
- key_display_index = (NEXT_KEY_INDEX % len(ALL_API_KEYS)) + 1
116
- NEXT_KEY_INDEX += 1
117
- return key_to_use, key_display_index
118
-
119
- # --- منطق اصلی تولید صدا ---
120
  def generate_audio_chunk_with_retry(chunk_text, prompt_text, voice, temp, session_id):
121
  if not ALL_API_KEYS: raise Exception("هیچ کلید API برای پردازش در دسترس نیست.")
 
122
  for _ in range(len(ALL_API_KEYS)):
123
- selected_api_key, key_idx_display = get_next_api_key()
124
- if not selected_api_key: break
 
 
125
  logging.info(f"[{session_id}] ⚙️ تلاش برای تولید قطعه با کلید API شماره {key_idx_display}")
126
  try:
127
- client = genai.Client(api_key=selected_api_key)
128
  final_text = f'"{prompt_text}"\n{chunk_text}' if prompt_text and prompt_text.strip() else chunk_text
129
  contents = [types.Content(role="user", parts=[types.Part.from_text(text=final_text)])]
130
  config = types.GenerateContentConfig(temperature=temp, response_modalities=["audio"],
131
  speech_config=types.SpeechConfig(voice_config=types.VoiceConfig(
132
  prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name=voice))))
133
  response = client.models.generate_content(model=FIXED_MODEL_NAME, contents=contents, config=config)
 
134
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts and response.candidates[0].content.parts[0].inline_data:
135
  logging.info(f"[{session_id}] ✅ قطعه با موفقیت توسط کلید شماره {key_idx_display} تولید شد.")
136
  return response.candidates[0].content.parts[0].inline_data
137
  except Exception as e:
138
  logging.error(f"[{session_id}] ❌ خطا در تولید قطعه با کلید شماره {key_idx_display}: {e}.")
 
 
 
 
 
139
  return None
140
 
141
  def core_generate_audio(text_input, prompt_input, selected_voice, temperature_val, session_id):
@@ -179,13 +193,8 @@ def core_generate_audio(text_input, prompt_input, selected_voice, temperature_va
179
  if os.path.exists(temp_dir):
180
  shutil.rmtree(temp_dir)
181
 
182
- # --- END: تعریف تمام توابع کمکی ---
183
-
184
-
185
- # --- اجرای کدهای اولیه برنامه ---
186
  _init_api_keys()
187
 
188
- # --- تعریف اپلیکیشن FastAPI ---
189
  app = FastAPI(title="Alpha TTS Worker API")
190
 
191
  class TTSRequest(BaseModel):
@@ -194,8 +203,12 @@ class TTSRequest(BaseModel):
194
  speaker: str
195
  temperature: float
196
 
 
 
 
197
  @app.post("/generate")
198
- async def generate_audio_endpoint(request: TTSRequest):
 
199
  session_id = str(uuid.uuid4())[:8]
200
  logging.info(f"[{session_id}] 🏁 درخواست جدید API در این Worker دریافت شد.")
201
  try:
@@ -221,12 +234,6 @@ def health_check():
221
 
222
  logging.info("✅✅✅ Application logic initialized successfully. Starting Uvicorn server...")
223
 
224
- # --- START: بخش جدید برای اجرای سرور ---
225
  if __name__ == "__main__":
226
- # پورت را از متغیرهای محیطی هاگینگ فیس یا به صورت پیش‌فرض 7860 بخوان
227
  port = int(os.environ.get("PORT", 7860))
228
-
229
- # اجرای سرور Uvicorn از داخل کد پایتون
230
- # reload=False برای محیط production مهم است
231
- uvicorn.run(app, host="0.0.0.0", port=port, reload=False)
232
- # --- END: بخش جدید برای اجرای سرور ---
 
1
+ # app.py - نسخه نهایی با اجرای همزمان واقعی برای حداکثر پایداری
2
 
3
  import os
4
  import sys
 
15
  from pydantic import BaseModel
16
  from google import genai
17
  from google.genai import types
 
 
18
  import uvicorn
19
 
20
  try:
 
23
  except ImportError:
24
  PYDUB_AVAILABLE = False
25
 
 
26
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
27
 
28
+ GEMINI_CLIENTS_CACHE = {}
29
+ CLIENT_CACHE_LOCK = threading.Lock()
30
 
 
31
  ALL_API_KEYS: list[str] = []
32
  NEXT_KEY_INDEX: int = 0
33
  KEY_LOCK: threading.Lock = threading.Lock()
 
39
  ALL_API_KEYS = [key.strip() for key in all_keys_string.split(',') if key.strip()]
40
  logging.info(f"✅ تعداد {len(ALL_API_KEYS)} کلید API جیمینای بارگذاری شد.")
41
  if not ALL_API_KEYS:
42
+ logging.warning("⛔️ هشدار: هیچ Secret با نام ALL_GEMINI_API_KEYS یافت نشد!")
43
+
44
+ def get_next_api_key_and_client():
45
+ global NEXT_KEY_INDEX
46
+ with KEY_LOCK:
47
+ if not ALL_API_KEYS:
48
+ return None, None, -1
49
+
50
+ current_index = NEXT_KEY_INDEX % len(ALL_API_KEYS)
51
+ key_to_use = ALL_API_KEYS[current_index]
52
+ key_display_index = current_index + 1
53
+ NEXT_KEY_INDEX += 1
54
+
55
+ with CLIENT_CACHE_LOCK:
56
+ if key_to_use in GEMINI_CLIENTS_CACHE:
57
+ client = GEMINI_CLIENTS_CACHE[key_to_use]
58
+ else:
59
+ logging.info(f"Creating new Gemini client for key ending in ...{key_to_use[-4:]}")
60
+ client = genai.Client(api_key=key_to_use)
61
+ GEMINI_CLIENTS_CACHE[key_to_use] = client
62
+
63
+ return key_to_use, client, key_display_index
64
 
 
65
  FIXED_MODEL_NAME = "gemini-2.5-flash-preview-tts"
66
  DEFAULT_MAX_CHUNK_SIZE = 3800
67
  DEFAULT_SLEEP_BETWEEN_REQUESTS = 8
68
 
 
69
  def save_binary_file(file_name, data):
70
  try:
71
  with open(file_name, "wb") as f: f.write(data)
 
123
  return True
124
  except Exception as e: logging.error(f"❌ خطا در ادغام فایل‌های صوتی: {e}"); return False
125
 
 
 
 
 
 
 
 
 
 
 
126
  def generate_audio_chunk_with_retry(chunk_text, prompt_text, voice, temp, session_id):
127
  if not ALL_API_KEYS: raise Exception("هیچ کلید API برای پردازش در دسترس نیست.")
128
+
129
  for _ in range(len(ALL_API_KEYS)):
130
+ selected_api_key, client, key_idx_display = get_next_api_key_and_client()
131
+ if not client:
132
+ break
133
+
134
  logging.info(f"[{session_id}] ⚙️ تلاش برای تولید قطعه با کلید API شماره {key_idx_display}")
135
  try:
 
136
  final_text = f'"{prompt_text}"\n{chunk_text}' if prompt_text and prompt_text.strip() else chunk_text
137
  contents = [types.Content(role="user", parts=[types.Part.from_text(text=final_text)])]
138
  config = types.GenerateContentConfig(temperature=temp, response_modalities=["audio"],
139
  speech_config=types.SpeechConfig(voice_config=types.VoiceConfig(
140
  prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name=voice))))
141
  response = client.models.generate_content(model=FIXED_MODEL_NAME, contents=contents, config=config)
142
+
143
  if response.candidates and response.candidates[0].content and response.candidates[0].content.parts and response.candidates[0].content.parts[0].inline_data:
144
  logging.info(f"[{session_id}] ✅ قطعه با موفقیت توسط کلید شماره {key_idx_display} تولید شد.")
145
  return response.candidates[0].content.parts[0].inline_data
146
  except Exception as e:
147
  logging.error(f"[{session_id}] ❌ خطا در تولید قطعه با کلید شماره {key_idx_display}: {e}.")
148
+ if "authentication" in str(e).lower():
149
+ with CLIENT_CACHE_LOCK:
150
+ if selected_api_key in GEMINI_CLIENTS_CACHE:
151
+ del GEMINI_CLIENTS_CACHE[selected_api_key]
152
+ logging.warning(f"Client for key ...{selected_api_key[-4:]} removed from cache due to auth error.")
153
  return None
154
 
155
  def core_generate_audio(text_input, prompt_input, selected_voice, temperature_val, session_id):
 
193
  if os.path.exists(temp_dir):
194
  shutil.rmtree(temp_dir)
195
 
 
 
 
 
196
  _init_api_keys()
197
 
 
198
  app = FastAPI(title="Alpha TTS Worker API")
199
 
200
  class TTSRequest(BaseModel):
 
203
  speaker: str
204
  temperature: float
205
 
206
+ # --- START: تغییر اصلی برای اجرای همزمان واقعی ---
207
+ # کلمه کلیدی async از تعریف تابع حذف شده است.
208
+ # این به FastAPI می‌گوید که این تابع سنگین را در یک thread جداگانه اجرا کند.
209
  @app.post("/generate")
210
+ def generate_audio_endpoint(request: TTSRequest):
211
+ # --- END: تغییر اصلی ---
212
  session_id = str(uuid.uuid4())[:8]
213
  logging.info(f"[{session_id}] 🏁 درخواست جدید API در این Worker دریافت شد.")
214
  try:
 
234
 
235
  logging.info("✅✅✅ Application logic initialized successfully. Starting Uvicorn server...")
236
 
 
237
  if __name__ == "__main__":
 
238
  port = int(os.environ.get("PORT", 7860))
239
+ uvicorn.run(app, host="0.0.0.0", port=port, reload=False)