Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py - نسخه
|
| 2 |
|
| 3 |
import os
|
| 4 |
import sys
|
|
@@ -42,13 +42,7 @@ def _init_api_keys():
|
|
| 42 |
logging.warning("⛔️ هشدار: هیچ Secret با نام ALL_GEMINI_API_KEYS یافت نشد!")
|
| 43 |
|
| 44 |
def get_next_api_key_and_client():
|
| 45 |
-
|
| 46 |
-
تغییر اصلی: این تابع هم کلید و هم یک client کششده یا جدید را برمیگرداند.
|
| 47 |
-
"""
|
| 48 |
-
# --- START: خط اصلاح شده اینجاست ---
|
| 49 |
-
global NEXT_KEY_INDEX
|
| 50 |
-
# --- END: خط اصلاح شده ---
|
| 51 |
-
|
| 52 |
with KEY_LOCK:
|
| 53 |
if not ALL_API_KEYS:
|
| 54 |
return None, None, -1
|
|
@@ -68,7 +62,6 @@ def get_next_api_key_and_client():
|
|
| 68 |
|
| 69 |
return key_to_use, client, key_display_index
|
| 70 |
|
| 71 |
-
# --- ثابتها ---
|
| 72 |
FIXED_MODEL_NAME = "gemini-2.5-flash-preview-tts"
|
| 73 |
DEFAULT_MAX_CHUNK_SIZE = 3800
|
| 74 |
DEFAULT_SLEEP_BETWEEN_REQUESTS = 8
|
|
@@ -80,6 +73,7 @@ def save_binary_file(file_name, data):
|
|
| 80 |
except Exception as e:
|
| 81 |
logging.error(f"❌ خطا در ذخیره فایل {file_name}: {e}")
|
| 82 |
return None
|
|
|
|
| 83 |
def convert_to_wav(audio_data: bytes, mime_type: str) -> bytes:
|
| 84 |
parameters = parse_audio_mime_type(mime_type)
|
| 85 |
bits_per_sample, rate = parameters["bits_per_sample"], parameters["rate"]
|
|
@@ -88,6 +82,7 @@ def convert_to_wav(audio_data: bytes, mime_type: str) -> bytes:
|
|
| 88 |
byte_rate, chunk_size = rate * block_align, 36 + data_size
|
| 89 |
header = struct.pack("<4sI4s4sIHHIIHH4sI", b"RIFF", chunk_size, b"WAVE", b"fmt ", 16, 1, num_channels, rate, byte_rate, block_align, bits_per_sample, b"data", data_size)
|
| 90 |
return header + audio_data
|
|
|
|
| 91 |
def parse_audio_mime_type(mime_type: str) -> dict[str, int]:
|
| 92 |
bits, rate = 16, 24000
|
| 93 |
for param in mime_type.split(";"):
|
|
@@ -99,6 +94,7 @@ def parse_audio_mime_type(mime_type: str) -> dict[str, int]:
|
|
| 99 |
try: bits = int(param.split("L", 1)[1])
|
| 100 |
except: pass
|
| 101 |
return {"bits_per_sample": bits, "rate": rate}
|
|
|
|
| 102 |
def smart_text_split(text, max_size=3800):
|
| 103 |
if len(text) <= max_size: return [text]
|
| 104 |
chunks, current_chunk = [], ""
|
|
@@ -115,6 +111,7 @@ def smart_text_split(text, max_size=3800):
|
|
| 115 |
if current_chunk: chunks.append(current_chunk.strip())
|
| 116 |
final_chunks = [c for c in chunks if c]
|
| 117 |
return final_chunks
|
|
|
|
| 118 |
def merge_audio_files_func(file_paths, output_path):
|
| 119 |
if not PYDUB_AVAILABLE: logging.warning("⚠️ pydub برای ادغام در دسترس نیست."); return False
|
| 120 |
try:
|
|
@@ -206,8 +203,12 @@ class TTSRequest(BaseModel):
|
|
| 206 |
speaker: str
|
| 207 |
temperature: float
|
| 208 |
|
|
|
|
|
|
|
|
|
|
| 209 |
@app.post("/generate")
|
| 210 |
-
|
|
|
|
| 211 |
session_id = str(uuid.uuid4())[:8]
|
| 212 |
logging.info(f"[{session_id}] 🏁 درخواست جدید API در این Worker دریافت شد.")
|
| 213 |
try:
|
|
|
|
| 1 |
+
# app.py - نسخه نهایی با اجرای همزمان واقعی برای حداکثر پایداری
|
| 2 |
|
| 3 |
import os
|
| 4 |
import sys
|
|
|
|
| 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
|
|
|
|
| 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
|
|
|
|
| 73 |
except Exception as e:
|
| 74 |
logging.error(f"❌ خطا در ذخیره فایل {file_name}: {e}")
|
| 75 |
return None
|
| 76 |
+
|
| 77 |
def convert_to_wav(audio_data: bytes, mime_type: str) -> bytes:
|
| 78 |
parameters = parse_audio_mime_type(mime_type)
|
| 79 |
bits_per_sample, rate = parameters["bits_per_sample"], parameters["rate"]
|
|
|
|
| 82 |
byte_rate, chunk_size = rate * block_align, 36 + data_size
|
| 83 |
header = struct.pack("<4sI4s4sIHHIIHH4sI", b"RIFF", chunk_size, b"WAVE", b"fmt ", 16, 1, num_channels, rate, byte_rate, block_align, bits_per_sample, b"data", data_size)
|
| 84 |
return header + audio_data
|
| 85 |
+
|
| 86 |
def parse_audio_mime_type(mime_type: str) -> dict[str, int]:
|
| 87 |
bits, rate = 16, 24000
|
| 88 |
for param in mime_type.split(";"):
|
|
|
|
| 94 |
try: bits = int(param.split("L", 1)[1])
|
| 95 |
except: pass
|
| 96 |
return {"bits_per_sample": bits, "rate": rate}
|
| 97 |
+
|
| 98 |
def smart_text_split(text, max_size=3800):
|
| 99 |
if len(text) <= max_size: return [text]
|
| 100 |
chunks, current_chunk = [], ""
|
|
|
|
| 111 |
if current_chunk: chunks.append(current_chunk.strip())
|
| 112 |
final_chunks = [c for c in chunks if c]
|
| 113 |
return final_chunks
|
| 114 |
+
|
| 115 |
def merge_audio_files_func(file_paths, output_path):
|
| 116 |
if not PYDUB_AVAILABLE: logging.warning("⚠️ pydub برای ادغام در دسترس نیست."); return False
|
| 117 |
try:
|
|
|
|
| 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:
|