Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# app.py
|
2 |
+
import gradio as gr
|
3 |
+
import google.generativeai as genai
|
4 |
+
import os
|
5 |
+
import io
|
6 |
+
from scipy.io.wavfile import write as write_wav # برای ذخیره فایل صوتی
|
7 |
+
|
8 |
+
# دریافت API Key از Secrets هاگینگ فیس
|
9 |
+
# مطمئن شوید که یک Secret به نام GOOGLE_API_KEY در اسپیس خود تعریف کردهاید
|
10 |
+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
|
11 |
+
|
12 |
+
if not GOOGLE_API_KEY:
|
13 |
+
raise ValueError("GOOGLE_API_KEY not found in environment variables. Please set it in Hugging Face Secrets.")
|
14 |
+
|
15 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
16 |
+
|
17 |
+
# انتخاب مدل TTS
|
18 |
+
# گزینه ها: "gemini-2.5-flash-preview-tts" یا "gemini-2.5-pro-preview-tts"
|
19 |
+
# برای شروع، از flash استفاده میکنیم
|
20 |
+
TTS_MODEL_NAME = "gemini-2.5-flash-preview-tts" # یا "tts-1" اگر نامهای سادهتر هم کار میکنند (باید مستندات دقیق را چک کرد)
|
21 |
+
# بر اساس مستندات جدید، نام دقیق مدلها به این شکل است.
|
22 |
+
|
23 |
+
# لیستی از صداهای موجود (این لیست ممکن است نیاز به بروزرسانی بر اساس مستندات دقیق مدل TTS داشته باشد)
|
24 |
+
# این فقط یک مثال است، باید نامهای دقیق voice ها را از مستندات پیدا کنید.
|
25 |
+
# مستندات قبلی برای Live API صداهایی مانند Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, and Zephyr را ذکر کرده بود.
|
26 |
+
# اما برای مدلهای TTS اختصاصی، ممکن است لیست متفاوت باشد یا اصلاً نیازی به انتخاب voice نباشد و مدل خودش بهینه عمل کند.
|
27 |
+
# فعلاً این بخش را ساده نگه میداریم و به مدل اجازه میدهیم voice را انتخاب کند.
|
28 |
+
AVAILABLE_VOICES = ["Default"] # یا لیستی از نامهای واقعی voice اگر دارید
|
29 |
+
|
30 |
+
def generate_audio(text_to_speak, voice_selection="Default"):
|
31 |
+
"""
|
32 |
+
متن را به صدا تبدیل میکند با استفاده از Gemini API.
|
33 |
+
"""
|
34 |
+
if not text_to_speak:
|
35 |
+
raise gr.Error("لطفاً متنی را برای تبدیل به صدا وارد کنید.")
|
36 |
+
print(f"درخواست TTS برای متن: '{text_to_speak[:50]}...' با voice: {voice_selection}")
|
37 |
+
|
38 |
+
try:
|
39 |
+
# ایجاد مدل
|
40 |
+
# توجه: نحوه فراخوانی مدل TTS ممکن است با generate_content متفاوت باشد.
|
41 |
+
# باید مستندات دقیق را برای "Text-to-speech (TTS)" با Gemini 2.5 Flash/Pro بررسی کنیم.
|
42 |
+
# فرض میکنیم که میتوانیم با generate_content و ارسال متن، خروجی صوتی بگیریم.
|
43 |
+
# این بخش احتمالاً نیاز به اصلاح بر اساس API دقیق TTS دارد.
|
44 |
+
|
45 |
+
# بر اساس مستندات قیمتگذاری، مدلهای TTS ورودی متن و خروجی صدا دارند.
|
46 |
+
# نحوه دقیق فراخوانی برای دریافت بایتهای صوتی ممکن است به این شکل باشد:
|
47 |
+
model = genai.GenerativeModel(TTS_MODEL_NAME)
|
48 |
+
|
49 |
+
# برای مدلهای TTS، "prompt" همان متنی است که میخواهیم به صدا تبدیل شود.
|
50 |
+
# ممکن است نیاز به پارامترهای خاصی در generation_config برای صدا باشد.
|
51 |
+
response = model.generate_content(
|
52 |
+
text_to_speak,
|
53 |
+
# generation_config=genai.types.GenerationConfig(
|
54 |
+
# # پارامترهای خاص TTS در اینجا قرار میگیرند، اگر وجود داشته باشد
|
55 |
+
# # مثلاً voice، سرعت، لحن و ...
|
56 |
+
# # response_mime_type="audio/wav" or "audio/mp3" ???
|
57 |
+
# )
|
58 |
+
)
|
59 |
+
|
60 |
+
# پاسخ مدلهای TTS معمولاً شامل بایتهای صوتی است.
|
61 |
+
# باید بررسی کنیم که پاسخ در چه فرمتی است.
|
62 |
+
# فرض میکنیم پاسخ دارای یک پراپرتی audio_content یا مشابه است که بایتها را دارد.
|
63 |
+
# این بخش کاملاً به خروجی واقعی API بستگی دارد.
|
64 |
+
|
65 |
+
# --- این بخش حدسی است و باید با مستندات API تطبیق داده شود ---
|
66 |
+
if hasattr(response, 'audio_content') and response.audio_content:
|
67 |
+
audio_bytes = response.audio_content
|
68 |
+
elif hasattr(response, 'candidates') and response.candidates[0].content.parts[0].inline_data:
|
69 |
+
# این ساختار برای inline_data در پاسخهای چندوجهی است
|
70 |
+
audio_part = response.candidates[0].content.parts[0]
|
71 |
+
if audio_part.inline_data.mime_type.startswith("audio/"):
|
72 |
+
audio_bytes = audio_part.inline_data.data
|
73 |
+
else:
|
74 |
+
raise gr.Error(f"ف��مت پاسخ صوتی نامعتبر: {audio_part.inline_data.mime_type}")
|
75 |
+
elif hasattr(response, 'text'): # اگر به اشتباه پاسخ متنی گرفتیم
|
76 |
+
raise gr.Error(f"مدل پاسخ متنی برگرداند به جای صدا: {response.text}")
|
77 |
+
else:
|
78 |
+
print("پاسخ کامل مدل:", response) # برای دیباگ
|
79 |
+
raise gr.Error("پاسخ صوتی از مدل دریافت نشد یا فرمت آن ناشناخته است.")
|
80 |
+
# --- پایان بخش حدسی ---
|
81 |
+
|
82 |
+
# ذخیره بایتهای صوتی در یک فایل WAV موقت
|
83 |
+
# ما به نرخ نمونهبرداری (sample rate) صدای خروجی نیاز داریم.
|
84 |
+
# مدلهای TTS معمولاً با نرخ نمونهبرداری مشخصی خروجی میدهند (مثلاً 24000 Hz).
|
85 |
+
# این مقدار باید از مستندات API گرفته شود. فرض میکنیم 24000 Hz است.
|
86 |
+
sample_rate = 24000 # هرتز - این را از مستندات API برای مدل TTS خود چک کنید!
|
87 |
+
|
88 |
+
# تبدیل بایتها به فرمتی که Gradio بتواند پخش کند (فایل WAV)
|
89 |
+
# کتابخانه google-generativeai ممکن است مستقیماً فایل صوتی برنگرداند، بلکه بایتهای خام PCM.
|
90 |
+
# یا ممکن است یک آبجکت خاص Audio برگرداند.
|
91 |
+
# سادهترین حالت این است که API مستقیماً بایتهای یک فایل WAV را برگرداند.
|
92 |
+
# اگر بایتهای خام PCM برمیگرداند، باید آنها را به WAV تبدیل کنیم.
|
93 |
+
|
94 |
+
# فرض میکنیم audio_bytes حاوی دادههای یک فایل WAV کامل است
|
95 |
+
# یا باید با استفاده از scipy.io.wavfile یا wave آن را بسازیم.
|
96 |
+
# اگر audio_bytes داده خام PCM16 است:
|
97 |
+
# import numpy as np
|
98 |
+
# audio_np = np.frombuffer(audio_bytes, dtype=np.int16)
|
99 |
+
# wav_io = io.BytesIO()
|
100 |
+
# write_wav(wav_io, sample_rate, audio_np)
|
101 |
+
# output_audio_path = wav_io # Gradio میتواند BytesIO را به عنوان فایل صوتی بپذیرد
|
102 |
+
|
103 |
+
# برای سادگی، فرض میکنیم audio_bytes بایتهای یک فایل صوتی قابل پخش است (مثلاً WAV)
|
104 |
+
# و Gradio میتواند آن را مستقیماً به عنوان (sample_rate, np_array) یا مسیر فایل یا BytesIO بپذیرد.
|
105 |
+
# اگر API یک آبجکت خاص برمیگرداند، باید آن را مطابق مستندات پردازش کنید.
|
106 |
+
|
107 |
+
# برای اینکه Gradio بتواند پخش کند، ما به (sample_rate, numpy_array) نیاز داریم
|
108 |
+
# یا مسیر یک فایل. اگر بایتهای خام PCM داریم:
|
109 |
+
# این بخش نیاز به کار بیشتری دارد اگر API بایتهای خام PCM برمیگرداند.
|
110 |
+
# فعلاً فرض میکنیم API یک فرمت قابل قبول برای Gradio برمیگرداند یا ما آن را تبدیل میکنیم.
|
111 |
+
|
112 |
+
# سادهترین راه برای تست اولیه: ذخیره بایتها در فایل و برگرداندن مسیر فایل
|
113 |
+
output_filename = "output_audio.wav"
|
114 |
+
with open(output_filename, "wb") as f:
|
115 |
+
f.write(audio_bytes)
|
116 |
+
|
117 |
+
print(f"فایل صوتی در {output_filename} ذخیره شد.")
|
118 |
+
return output_filename # Gradio میتواند مسیر فایل را برای Audio output بگیرد
|
119 |
+
|
120 |
+
except Exception as e:
|
121 |
+
print(f"خطا در تولید صدا: {e}")
|
122 |
+
# نمایش جزئیات بیشتر خطا برای دیباگ
|
123 |
+
import traceback
|
124 |
+
traceback.print_exc()
|
125 |
+
raise gr.Error(f"خطا در ارتباط با Gemini API یا پردازش صدا: {str(e)}")
|
126 |
+
|
127 |
+
# ایجاد رابط کاربری Gradio
|
128 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
129 |
+
gr.Markdown("# تبدیل متن به صدا با Gemini ♊")
|
130 |
+
gr.Markdown("متن خود را وارد کنید تا با استفاده از مدلهای جدید Gemini به صدا تبدیل شود.")
|
131 |
+
|
132 |
+
with gr.Row():
|
133 |
+
with gr.Column(scale=2):
|
134 |
+
text_input = gr.Textbox(lines=5, label="متن ورودی", placeholder="متن خود را اینجا بنویسید...")
|
135 |
+
# voice_dropdown = gr.Dropdown(choices=AVAILABLE_VOICES, value="Default", label="انتخاب صدا (اختیاری)") # فعلاً ساده
|
136 |
+
submit_button = gr.Button("🔊 تبدیل به صدا", variant="primary")
|
137 |
+
with gr.Column(scale=1):
|
138 |
+
audio_output = gr.Audio(label="خروجی صدا", type="filepath") # یا type="numpy" اگر آرایه برمیگردانید
|
139 |
+
|
140 |
+
gr.Examples(
|
141 |
+
examples=[
|
142 |
+
["سلام، حال شما چطور است؟"],
|
143 |
+
["به دنیای هوش مصنوعی خوش آمدید."],
|
144 |
+
["این یک تست برای تبدیل متن به صدا با استفاده از جیمینای است."]
|
145 |
+
],
|
146 |
+
inputs=[text_input]
|
147 |
+
)
|
148 |
+
|
149 |
+
submit_button.click(
|
150 |
+
fn=generate_audio,
|
151 |
+
inputs=[text_input], # voice_dropdown اگر فعال بود
|
152 |
+
outputs=[audio_output],
|
153 |
+
api_name="text_to_speech"
|
154 |
+
)
|
155 |
+
|
156 |
+
gr.Markdown("---")
|
157 |
+
gr.Markdown(f"مدل مورد استفاده: `{TTS_MODEL_NAME}`")
|
158 |
+
|
159 |
+
if __name__ == "__main__":
|
160 |
+
demo.launch(debug=True) # debug=True برای دیدن لاگهای بیشتر در کنسول
|