Hamed744 commited on
Commit
59d09f0
·
verified ·
1 Parent(s): 3090f63

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -129
app.py CHANGED
@@ -1,198 +1,150 @@
1
  # app.py
2
  import gradio as gr
3
  import google.generativeai as genai
 
4
  import os
5
  import io
6
- # از scipy.io.wavfile استفاده نمی‌کنیم اگر API مستقیم بایت‌های WAV برمی‌گرداند
7
- # یا اگر فرمت دیگری برمی‌گرداند و ما با روش دیگری به WAV تبدیل می‌کنیم.
8
- # فعلاً آن را نگه می‌داریم تا ببینیم خروجی API چیست.
9
  from scipy.io.wavfile import write as write_wav
10
- import numpy as np # برای کار با آرایه‌های صوتی اگر لازم شد
11
 
12
  GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
13
  if not GOOGLE_API_KEY:
14
  raise ValueError("GOOGLE_API_KEY not found in environment variables.")
15
  genai.configure(api_key=GOOGLE_API_KEY)
16
 
17
- TTS_MODEL_NAME = "gemini-2.5-flash-preview-tts" # بر اساس پیام خطا، پیشوند models/ لازم است
18
 
19
- def generate_audio(text_to_speak):
 
 
 
 
 
 
 
 
20
  if not text_to_speak:
21
  raise gr.Error("لطفاً متنی را برای تبدیل به صدا وارد کنید.")
22
- print(f"درخواست TTS برای متن: '{text_to_speak[:50]}...'")
23
 
24
  try:
25
- model = genai.GenerativeModel(f"models/{TTS_MODEL_NAME}") # اضافه کردن پیشوند models/
26
 
27
  # --- اصلاح کلیدی: تنظیم صریح response_modalities ---
28
- # مستندات اخیر برای Live API از response_modalities در config اصلی استفاده می‌کرد.
29
- # اما برای generate_content، معمولاً در generation_config است.
30
- # همچنین، پیام خطا به "combination of response modalities" اشاره دارد.
31
- # SDK پایتون ممکن است به response_mime_type یا چیز مشابهی نیاز داشته باشد.
32
-
33
- # بر اساس پیام خطا، مدل فقط AUDIO را به عنوان response modality می‌پذیرد.
34
- # نحوه دقیق تنظیم این در SDK پایتون برای TTS API باید از مستندات TTS API بررسی شود.
35
- # یک تلاش ممکن:
36
- # response = model.generate_content(text_to_speak, request_options={"response_mime_type": "audio/wav"})
37
- # یا اگر generation_config پارامتر خاصی برای این دارد.
38
-
39
- # با توجه به پیام خطا، به نظر می‌رسد API انتظار دارد که بداند خروجی فقط AUDIO است.
40
- # ممکن است نیازی به generation_config نباشد اگر مدل ذاتاً فقط صدا تولید می‌کند.
41
- # اما اگر API عمومی generate_content استفاده می‌شود، باید مشخص کنیم.
42
-
43
- # === شروع بخش مهم برای اصلاح ===
44
- # تلاش برای استفاده از ساختاری که در مستندات Live API برای response_modalities دیدیم،
45
- # اما اعمال آن به generate_content ممکن است نیاز به تطبیق داشته باشد.
46
- # کتابخانه google-generativeai برای TTS ممکن است روش خاص خود را داشته باشد.
47
- #
48
- # یک فرض این است که اگر مدل فقط صدا تولید می‌کند، نیازی به تنظیم خاصی نیست
49
- # و مشکل از جای دیگری است. اما پیام خطا صریحاً به response_modalities اشاره دارد.
50
- #
51
- # بیایید سعی کنیم generation_config را با response_mime_type تنظیم کنیم،
52
- # چون این روشی رایج برای درخواست فرمت خاص خروجی در API های گوگل است.
53
- # مقدار دقیق mime_type (audio/wav, audio/mpeg, audio/opus) باید از مستندات TTS API بیاید.
54
- # فرض می‌کنیم WAV:
55
-
56
- # --- تلاش 1: استفاده از generation_config با response_mime_type ---
57
- # این یک حدس است و باید مستندات API را برای TTS بررسی کنید.
58
- # response = model.generate_content(
59
- # text_to_speak,
60
- # generation_config=genai.types.GenerationConfig(
61
- # response_mime_type="audio/wav" # یا "audio/mpeg" یا هر فرمتی که مدل پشتیبانی می‌کند
62
- # )
63
- # )
64
- # اگر این کار نکرد، شاید مدل TTS یک متد خاص به جز generate_content دارد، مثلاً model.synthesize_speech()
65
-
66
- # --- تلاش 2: ساده‌ترین حالت، با فرض اینکه مدل خودش می‌داند باید صدا تولید کند ---
67
- # و مشکل از پیشوند models/ بوده.
68
- # در پیام خطا آمده: "models/gemini-2.5-flash-preview-tts accepts..."
69
- # پس نام مدل باید با models/ باشد.
70
-
71
- response = model.generate_content(text_to_speak) # بدون generation_config خاص فعلاً
72
-
73
-
74
- # --- پردازش پاسخ ---
75
- # این بخش همچنان حدسی است و به ساختار واقعی پاسخ API بستگی دارد.
76
  audio_bytes = None
77
  generated_mime_type = None
 
78
 
79
  if hasattr(response, 'candidates') and response.candidates and \
80
- hasattr(response.candidates[0], 'content') and response.candidates[0].content and \
81
- hasattr(response.candidates[0].content, 'parts') and response.candidates[0].content.parts:
82
  for part in response.candidates[0].content.parts:
83
  if hasattr(part, 'inline_data') and part.inline_data and \
84
- hasattr(part.inline_data, 'mime_type') and part.inline_data.mime_type.startswith("audio/"):
85
  audio_bytes = part.inline_data.data
86
  generated_mime_type = part.inline_data.mime_type
 
 
 
 
 
 
 
87
  print(f"داده صوتی با MIME type: {generated_mime_type} دریافت شد.")
88
- break # اولین بخش صوتی را می‌گیریم
89
 
90
- if audio_bytes is None and hasattr(response, 'audio_content'): # برخی API ها ممکن است این فیلد را داشته باشند
91
- audio_bytes = response.audio_content
92
- generated_mime_type = "audio/wav" # فرض می‌کنیم WAV است اگر mime_type مشخص نیست
93
- print("داده صوتی از فیلد audio_content دریافت شد.")
94
-
95
-
96
- if audio_bytes is None:
97
- print("پاسخ کامل مدل (برای دیباگ):", response)
98
- try:
99
- # اگر پاسخ خطا بود، متن خطا را نشان بده
100
  error_text = response.prompt_feedback if hasattr(response, 'prompt_feedback') else str(response)
101
  raise gr.Error(f"پاسخ صوتی از مدل دریافت نشد. پاسخ مدل: {error_text}")
102
- except Exception as e_resp:
103
- raise gr.Error(f"پاسخ صوتی از مدل دریافت نشد یا فرمت ناشناخته است. خطای داخلی: {e_resp}")
104
-
105
-
106
- # --- ذخیره و برگرداندن فایل صوتی ---
107
- # نرخ نمونه‌برداری پیش‌فرض برای اکثر مدل‌های TTS گوگل 24000 هرتز است.
108
- # این را از مستندات دقیق مدل خود چک کنید.
109
- sample_rate = 24000
110
 
111
- output_filename = "output.wav" # همیشه با پسوند wav ذخیره می‌کنیم
112
-
113
- # اگر API بایت‌های خام PCM برمی‌گرداند، باید آنها را به WAV تبدیل کنیم.
114
- # فرض می‌کنیم audio_bytes بایت‌های یک فایل WAV کامل است،
115
- # یا بایت‌های خام PCM که نیاز به تبدیل به WAV با هدر مناسب دارند.
116
- # برای سادگی فعلاً مستقیم می‌نویسیم، اگر فرمت فایل صوتی کامل باشد.
117
  if "pcm" in (generated_mime_type or "").lower():
118
- # اگر PCM خام است، باید هدر WAV را اضافه کنیم
119
- print("داده PCM خام دریافت شد، در حال تبدیل به WAV...")
120
- # فرض می‌کنیم 16-bit mono PCM
121
- audio_np = np.frombuffer(audio_bytes, dtype=np.int16)
122
  wav_io = io.BytesIO()
123
  write_wav(wav_io, sample_rate, audio_np)
124
  wav_io.seek(0)
125
  with open(output_filename, "wb") as f:
126
  f.write(wav_io.read())
127
- else:
128
- # اگر فرمت دیگری است (مثلاً خود WAV یا MP3)، مستقیم می‌نویسیم
129
- print(f"داده صوتی با فرمت {generated_mime_type} دریافت شد، مستقیم ذخیره می‌شود.")
130
- with open(output_filename, "wb") as f:
131
  f.write(audio_bytes)
 
 
 
132
 
133
  print(f"فایل صوتی در {output_filename} ذخیره شد.")
134
  return output_filename
135
 
136
- except genai.types.BlockedPromptException as bpe:
137
- print(f"درخواست توسط مدل بلاک شد: {bpe}")
138
- raise gr.Error(f"محتوای شما توسط مدل پذیرفته نشد. لطفاً متن دیگری را امتحان کنید. دلیل: {bpe}")
139
- except Exception as e:
140
- print(f"خطا در تولید صدا: {e}")
141
- import traceback
142
- traceback.print_exc()
143
- # نمایش پیام خطای اصلی از API اگر موجود است
144
- error_message_from_api = ""
145
- if hasattr(e, 'message') and isinstance(e.message, str) and "HttpError" in e.message: # نمونه
146
- try:
147
- # تلاش برای استخراج جزئیات بیشتر از خطای API
148
- import json
149
- details_start = e.message.find('{')
150
- if details_start != -1:
151
- error_details_json = e.message[details_start:]
152
- error_obj = json.loads(error_details_json.strip().replace('\n', '').replace('\\n', ''))
153
- if 'error' in error_obj and 'message' in error_obj['error']:
154
- error_message_from_api = error_obj['error']['message']
155
- except:
156
- pass # اگر نشد، مهم نیست
157
-
158
- final_error_message = f"خطا در ارتباط با Gemini API یا پردازش صدا: {str(e)}"
159
- if error_message_from_api:
160
- final_error_message += f" | پیام API: {error_message_from_api}"
161
-
162
- raise gr.Error(final_error_message)
163
 
164
 
 
165
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
166
- # ... (بقیه کد رابط کاربری Gradio بدون تغییر) ...
167
  gr.Markdown("# تبدیل متن به صدا با Gemini ♊")
168
  gr.Markdown("متن خود را وارد کنید تا با استفاده از مدل‌های جدید Gemini به صدا تبدیل شود.")
169
 
170
  with gr.Row():
171
  with gr.Column(scale=2):
172
  text_input = gr.Textbox(lines=5, label="متن ورودی", placeholder="متن خود را اینجا بنویسید...")
 
173
  submit_button = gr.Button("🔊 تبدیل به صدا", variant="primary")
174
  with gr.Column(scale=1):
175
  audio_output = gr.Audio(label="خروجی صدا", type="filepath")
176
 
177
- gr.Examples(
178
- examples=[
179
- ["سلام، حال شما چطور است؟"],
180
- ["به دنیای هوش مصنوعی خوش آمدید."],
181
- ["این یک تست برای تبدیل متن به صدا با استفاده از جیمینای است."]
182
- ],
183
- inputs=[text_input]
184
- )
185
 
186
  submit_button.click(
187
  fn=generate_audio,
 
188
  inputs=[text_input],
189
  outputs=[audio_output],
190
  api_name="text_to_speech"
191
  )
192
 
193
  gr.Markdown("---")
194
- # نمایش نام مدل با پیشوند models/ بر اساس پیام خطا
195
  gr.Markdown(f"مدل مورد استفاده: `models/{TTS_MODEL_NAME}`")
 
196
 
197
 
198
  if __name__ == "__main__":
 
1
  # app.py
2
  import gradio as gr
3
  import google.generativeai as genai
4
+ from google.generativeai import types # برای استفاده از types.GenerationConfig
5
  import os
6
  import io
 
 
 
7
  from scipy.io.wavfile import write as write_wav
8
+ import numpy as np
9
 
10
  GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
11
  if not GOOGLE_API_KEY:
12
  raise ValueError("GOOGLE_API_KEY not found in environment variables.")
13
  genai.configure(api_key=GOOGLE_API_KEY)
14
 
15
+ TTS_MODEL_NAME = "gemini-2.5-flash-preview-tts"
16
 
17
+ # نام‌های گوینده‌ها باید از مستندات دقیق مدل TTS گرفته شود.
18
+ # این‌ها فقط مثال هستند و ممکن است برای این مدل معتبر نباشند.
19
+ # فعلاً یک لیست ساده با "پیش‌فرض" می‌گذاریم.
20
+ AVAILABLE_VOICES = ["پیش‌فرض (مدل انتخاب کند)"]
21
+ # اگر نام‌های واقعی را پیدا کردید، اینجا اضافه کنید:
22
+ # AVAILABLE_VOICES.extend(["voice-name-1", "voice-name-2"])
23
+
24
+
25
+ def generate_audio(text_to_speak, selected_voice_name="پیش‌فرض (مدل انتخاب کند)"):
26
  if not text_to_speak:
27
  raise gr.Error("لطفاً متنی را برای تبدیل به صدا وارد کنید.")
28
+ print(f"درخواست TTS برای متن: '{text_to_speak[:50]}...' با گوینده: {selected_voice_name}")
29
 
30
  try:
31
+ model = genai.GenerativeModel(f"models/{TTS_MODEL_NAME}")
32
 
33
  # --- اصلاح کلیدی: تنظیم صریح response_modalities ---
34
+ generation_config_params = {
35
+ "response_modalities": ["AUDIO"] # درخواست صریح خروجی صوتی
36
+ }
37
+
38
+ # اگر کاربر یک گوینده خاص انتخاب کرده (و "پیش‌فرض" نیست)
39
+ # و ما نام پارامتر صحیح را برای voice در generation_config بدانیم:
40
+ if selected_voice_name != "پیش‌فرض دل انتخاب کند)":
41
+ # نام پارامتر برای voice ممکن است "voice_name", "voice", "speaker" یا چیز دیگری باشد.
42
+ # این را باید از مستندات TTS API برای این مدل پیدا کنید.
43
+ # فرض می‌کنیم "voice" است:
44
+ # generation_config_params["voice"] = selected_voice_name
45
+ # یا اگر ساختار speech_config مانند Live API است:
46
+ # generation_config_params["speech_config"] = types.SpeechConfig(
47
+ # voice_config=types.VoiceConfig(
48
+ # prebuilt_voice_config=types.PrebuiltVoiceConfig(voice_name=selected_voice_name)
49
+ # )
50
+ # )
51
+ print(f"توجه: انتخاب گوینده هنوز پیاده‌سازی نشده است. از گوینده پیش‌فرض مدل استفاده می‌شود.")
52
+
53
+
54
+ generation_config = genai.types.GenerationConfig(**generation_config_params)
55
+
56
+ print(f"ارسال درخواست به Gemini با generation_config: {generation_config_params}")
57
+
58
+ response = model.generate_content(
59
+ text_to_speak,
60
+ generation_config=generation_config
61
+ )
62
+ # --- پایان اصلاح ---
63
+
64
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  audio_bytes = None
66
  generated_mime_type = None
67
+ sample_rate = 24000 # پیش‌فرض برای TTS گوگل، از مستندات چک شود
68
 
69
  if hasattr(response, 'candidates') and response.candidates and \
70
+ response.candidates[0].content and response.candidates[0].content.parts:
 
71
  for part in response.candidates[0].content.parts:
72
  if hasattr(part, 'inline_data') and part.inline_data and \
73
+ part.inline_data.mime_type.startswith("audio/"):
74
  audio_bytes = part.inline_data.data
75
  generated_mime_type = part.inline_data.mime_type
76
+ # برخی API ها ممکن است نرخ نمونه‌برداری را در mime_type بفرستند
77
+ if ";rate=" in generated_mime_type:
78
+ try:
79
+ sample_rate = int(generated_mime_type.split(";rate=")[1])
80
+ print(f"نرخ نمونه‌برداری از MIME type استخراج شد: {sample_rate} Hz")
81
+ except:
82
+ print(f"خطا در استخراج نرخ نمونه‌برداری از MIME type: {generated_mime_type}. از پیش‌فرض {sample_rate} Hz استفاده می‌شود.")
83
  print(f"داده صوتی با MIME type: {generated_mime_type} دریافت شد.")
84
+ break
85
 
86
+ if audio_bytes is None: # fallback اگر ساختار بالا نبود
87
+ if hasattr(response, 'audio_content'):
88
+ audio_bytes = response.audio_content
89
+ generated_mime_type = "audio/wav" # فرض
90
+ print("داده صوتی از فیلد audio_content دریافت شد.")
91
+ else:
92
+ print("پاسخ کامل مدل (برای دیباگ):", response)
 
 
 
93
  error_text = response.prompt_feedback if hasattr(response, 'prompt_feedback') else str(response)
94
  raise gr.Error(f"پاسخ صوتی از مدل دریافت نشد. پاسخ مدل: {error_text}")
 
 
 
 
 
 
 
 
95
 
96
+ output_filename = "output.wav"
97
+ # فرض می‌کنیم API بایت‌های خام PCM برمی‌گرداند اگر mime_type شامل pcm باشد
98
+ # یا یک فایل WAV کامل.
 
 
 
99
  if "pcm" in (generated_mime_type or "").lower():
100
+ print(f"داده PCM خام ({len(audio_bytes)} بایت) با نرخ نمونه‌برداری {sample_rate} Hz دریافت شد، در حال تبدیل به WAV...")
101
+ audio_np = np.frombuffer(audio_bytes, dtype=np.int16) # فرض بر 16-bit PCM
 
 
102
  wav_io = io.BytesIO()
103
  write_wav(wav_io, sample_rate, audio_np)
104
  wav_io.seek(0)
105
  with open(output_filename, "wb") as f:
106
  f.write(wav_io.read())
107
+ elif audio_bytes: # اگر PCM نیست، فرض می‌کنیم خود فایل صوتی است (مثلاً WAV از API)
108
+ print(f"داده صوتی با فرمت {generated_mime_type} ({len(audio_bytes)} بایت) دریافت شد، مستقیم ذخیره می‌شود.")
109
+ with open(output_filename, "wb") as f:
 
110
  f.write(audio_bytes)
111
+ else:
112
+ raise gr.Error("هیچ داده صوتی برای ذخیره وجود ندارد.")
113
+
114
 
115
  print(f"فایل صوتی در {output_filename} ذخیره شد.")
116
  return output_filename
117
 
118
+ except genai.types.BlockedPromptException as bpe: # ... بدون تغییر ...
119
+ except Exception as e: # ... بدون تغییر ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
 
122
+ # ایجاد رابط کاربری Gradio
123
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
124
  gr.Markdown("# تبدیل متن به صدا با Gemini ♊")
125
  gr.Markdown("متن خود را وارد کنید تا با استفاده از مدل‌های جدید Gemini به صدا تبدیل شود.")
126
 
127
  with gr.Row():
128
  with gr.Column(scale=2):
129
  text_input = gr.Textbox(lines=5, label="متن ورودی", placeholder="متن خود را اینجا بنویسید...")
130
+ # voice_dropdown = gr.Dropdown(choices=AVAILABLE_VOICES, value=AVAILABLE_VOICES[0], label="انتخاب گوینده") # فعال کردن Dropdown
131
  submit_button = gr.Button("🔊 تبدیل به صدا", variant="primary")
132
  with gr.Column(scale=1):
133
  audio_output = gr.Audio(label="خروجی صدا", type="filepath")
134
 
135
+ gr.Examples( /* ... بدون تغییر ... */ )
 
 
 
 
 
 
 
136
 
137
  submit_button.click(
138
  fn=generate_audio,
139
+ # inputs=[text_input, voice_dropdown], # اگر voice_dropdown فعال است
140
  inputs=[text_input],
141
  outputs=[audio_output],
142
  api_name="text_to_speech"
143
  )
144
 
145
  gr.Markdown("---")
 
146
  gr.Markdown(f"مدل مورد استفاده: `models/{TTS_MODEL_NAME}`")
147
+ gr.Markdown("توجه: برای انتخاب گوینده‌های مختلف، نیاز به بررسی مستندات دقیق مدل TTS و بروزرسانی کد است.")
148
 
149
 
150
  if __name__ == "__main__":