Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,239 +1,132 @@
|
|
1 |
-
import streamlit as st
|
2 |
import requests
|
3 |
import json
|
4 |
import os
|
5 |
-
import io
|
6 |
-
import base64
|
7 |
|
8 |
-
#
|
9 |
-
|
10 |
-
|
11 |
-
DEEPSEEK_API_KEY = "sk-4fb61f56acfccf731e801b904cd89f5" # This key is publicly visible, consider environment variables for production!
|
12 |
|
13 |
-
#
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
"""Fetches a WAV audio URL from Talkbot TTS."""
|
18 |
-
try:
|
19 |
-
params = {"text": text}
|
20 |
-
response = requests.get(TALKBOT_TTS_URL, params=params)
|
21 |
-
response.raise_for_status() # Raise an exception for bad status codes
|
22 |
-
return response.text.strip()
|
23 |
-
except requests.exceptions.RequestException as e:
|
24 |
-
st.error(f"Error fetching TTS audio URL for '{text}': {e}")
|
25 |
-
return ""
|
26 |
-
|
27 |
-
@st.cache_data
|
28 |
-
def generate_podcast_script_ai(topic: str, num_sections: int = 3) -> str:
|
29 |
-
"""Generates a podcast script using the Talkbot Deepseek API."""
|
30 |
-
prompt = (
|
31 |
-
f"Generate a concise podcast script about '{topic}'. "
|
32 |
-
f"The script should have {num_sections} main sections, each starting with a clear heading. "
|
33 |
-
"Each section should contain interesting facts or engaging discussions. "
|
34 |
-
"Conclude with a short outro. The script should be conversational and engaging. "
|
35 |
-
"Keep the total length reasonable for a short podcast (e.g., 300-500 words)."
|
36 |
-
"\n\nFormat your response as follows:\n\n"
|
37 |
-
"Section 1: [Title of Section 1]\n[Content of Section 1]\n\n"
|
38 |
-
"Section 2: [Title of Section 2]\n[Content of Section 2]\n\n"
|
39 |
-
# Add more sections as needed based on num_sections if hardcoding
|
40 |
-
# For simplicity, we'll let the AI decide the exact number based on prompt
|
41 |
-
"..."
|
42 |
-
"Outro:\n[Outro content]"
|
43 |
-
)
|
44 |
|
|
|
|
|
|
|
|
|
45 |
headers = {
|
46 |
-
"
|
47 |
-
"
|
48 |
}
|
49 |
|
50 |
payload = {
|
51 |
-
"model":
|
52 |
"messages": [
|
53 |
-
{"role": "system", "content": "You are a helpful podcast
|
54 |
-
{"role": "user", "content":
|
55 |
],
|
56 |
-
"max_tokens":
|
57 |
-
"temperature": 0.7
|
58 |
-
"top_p": 1,
|
59 |
-
"frequency_penalty": 0,
|
60 |
-
"presence_penalty": 0
|
61 |
}
|
62 |
|
63 |
try:
|
64 |
-
response = requests.post(
|
65 |
-
response.raise_for_status()
|
66 |
-
|
67 |
-
if
|
68 |
-
return
|
69 |
else:
|
70 |
-
|
71 |
-
return
|
72 |
except requests.exceptions.RequestException as e:
|
73 |
-
|
74 |
-
return
|
75 |
except json.JSONDecodeError:
|
76 |
-
|
77 |
-
return
|
78 |
-
|
79 |
-
def
|
80 |
-
"""
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
def get_audio_from_url_as_download(audio_url: str, filename: str):
|
87 |
-
"""Fetches audio from a URL and returns it as a bytes object for download."""
|
88 |
try:
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
except requests.exceptions.RequestException as e:
|
94 |
-
|
95 |
return None
|
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 |
-
# Voice 1 takes the first half, Voice 2 takes the second half.
|
152 |
-
# This can be made more sophisticated (e.g., based on paragraphs, specific markers).
|
153 |
-
lines = edited_script.split('\n')
|
154 |
-
mid_point = len(lines) // 2
|
155 |
-
voice1_text = "\n".join(lines[:mid_point])
|
156 |
-
voice2_text = "\n".join(lines[mid_point:])
|
157 |
-
|
158 |
-
st.subheader("۳. تولید صدا")
|
159 |
-
|
160 |
-
col1, col2 = st.columns(2)
|
161 |
-
|
162 |
-
with col1:
|
163 |
-
st.write("**صدای اول:**")
|
164 |
-
st.write(voice1_text)
|
165 |
-
if st.button("تولید صدای اول"):
|
166 |
-
with st.spinner("در حال تولید صدای اول..."):
|
167 |
-
audio1_url = get_tts_audio_url(voice1_text)
|
168 |
-
st.session_state['audio1_url'] = audio1_url
|
169 |
-
if audio1_url:
|
170 |
-
st.session_state['podcast_generated'] = True
|
171 |
-
st.success("صدای اول با موفقیت تولید شد!")
|
172 |
-
else:
|
173 |
-
st.error("خطا در تولید صدای اول. لطفاً دوباره تلاش کنید.")
|
174 |
-
|
175 |
-
with col2:
|
176 |
-
st.write("**صدای دوم:**")
|
177 |
-
st.write(voice2_text)
|
178 |
-
if st.button("تولید صدای دوم"):
|
179 |
-
with st.spinner("در حال تولید صدای دوم..."):
|
180 |
-
audio2_url = get_tts_audio_url(voice2_text)
|
181 |
-
st.session_state['audio2_url'] = audio2_url
|
182 |
-
if audio2_url:
|
183 |
-
st.session_state['podcast_generated'] = True
|
184 |
-
st.success("صدای دوم با موفقیت تولید شد!")
|
185 |
-
else:
|
186 |
-
st.error("خطا در تولید صدای دوم. لطفاً دوباره تلاش کنید.")
|
187 |
-
|
188 |
-
elif script_content: # This means an error message was returned
|
189 |
-
st.error("خطا در تولید متن پادکست.")
|
190 |
-
|
191 |
-
# --- Display Section (rendered after the script is generated) ---
|
192 |
-
if 'podcast_generated' in st.session_state and st.session_state['podcast_generated']:
|
193 |
-
st.subheader("۴. پادکست شما آماده است!")
|
194 |
-
|
195 |
-
col1_player, col2_player = st.columns(2)
|
196 |
-
|
197 |
-
with col1_player:
|
198 |
-
if st.session_state['audio1_url']:
|
199 |
-
st.markdown("### صدای اول")
|
200 |
-
create_audio_player_from_url(st.session_state['audio1_url'], "صدای اول")
|
201 |
-
audio1_bytes = get_audio_from_url_as_download(st.session_state['audio1_url'], "podcast_voice1.wav")
|
202 |
-
if audio1_bytes:
|
203 |
-
st.download_button(
|
204 |
-
label="دانلود صدای اول",
|
205 |
-
data=audio1_bytes,
|
206 |
-
file_name="podcast_voice1.wav",
|
207 |
-
mime="audio/wav"
|
208 |
-
)
|
209 |
else:
|
210 |
-
|
211 |
-
|
212 |
-
with col2_player:
|
213 |
-
if st.session_state['audio2_url']:
|
214 |
-
st.markdown("### صدای دوم")
|
215 |
-
create_audio_player_from_url(st.session_state['audio2_url'], "صدای دوم")
|
216 |
-
audio2_bytes = get_audio_from_url_as_download(st.session_state['audio2_url'], "podcast_voice2.wav")
|
217 |
-
if audio2_bytes:
|
218 |
-
st.download_button(
|
219 |
-
label="دانلود صدای دوم",
|
220 |
-
data=audio2_bytes,
|
221 |
-
file_name="podcast_voice2.wav",
|
222 |
-
mime="audio/wav"
|
223 |
-
)
|
224 |
-
else:
|
225 |
-
st.warning("صدای دوم هنوز تولید نشده یا با خطا مواجه شده است.")
|
226 |
-
|
227 |
-
st.markdown("---")
|
228 |
-
st.info("برای تولید پادکست جدید، موضوع جدیدی وارد کنید و دکمه 'تولید پادکست' را بزنید.")
|
229 |
|
230 |
-
|
231 |
-
# 1. Save the code as `app.py`.
|
232 |
-
# 2. Make sure you have Streamlit installed: `pip install streamlit requests`
|
233 |
-
# 3. Run from your terminal: `streamlit run app.py`
|
234 |
|
235 |
-
|
236 |
-
|
237 |
-
# For production applications, it's highly recommended to use environment variables
|
238 |
-
# (e.g., `os.getenv("DEEPSEEK_API_KEY")`) and set them outside of your code
|
239 |
-
# for security and flexibility. For this simple example as requested, it's left as is.
|
|
|
|
|
1 |
import requests
|
2 |
import json
|
3 |
import os
|
|
|
|
|
4 |
|
5 |
+
# اطلاعات API TTS
|
6 |
+
TTS_API_BASE_URL = "https://talkbot.ir/TTS-tkun"
|
7 |
+
TTS_API_FORMAT = "?text="
|
|
|
8 |
|
9 |
+
# اطلاعات API OpenAI (برای Talkbot.ir)
|
10 |
+
OPENAI_API_BASE_URL = "https://talkbot.ir/api/v1/chat/completions"
|
11 |
+
OPENAI_API_KEY = "sk-4fb61f56acfcc731e801b904cd89f5" # دقت کنید که این کلید API از Talkbot.ir است و ممکن است با OpenAI اصلی متفاوت باشد.
|
12 |
+
OPENAI_MODEL = "deepseek-v3-0324"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
def get_text_from_ai(prompt_text):
|
15 |
+
"""
|
16 |
+
تولید متن پادکست با استفاده از API هوش مصنوعی.
|
17 |
+
"""
|
18 |
headers = {
|
19 |
+
"Authorization": f"Bearer {OPENAI_API_KEY}",
|
20 |
+
"Content-Type": "application/json"
|
21 |
}
|
22 |
|
23 |
payload = {
|
24 |
+
"model": OPENAI_MODEL,
|
25 |
"messages": [
|
26 |
+
{"role": "system", "content": "You are a helpful assistant that generates podcast scripts."},
|
27 |
+
{"role": "user", "content": f"Generate a short podcast script about: {prompt_text}"}
|
28 |
],
|
29 |
+
"max_tokens": 500,
|
30 |
+
"temperature": 0.7
|
|
|
|
|
|
|
31 |
}
|
32 |
|
33 |
try:
|
34 |
+
response = requests.post(OPENAI_API_BASE_URL, headers=headers, json=payload)
|
35 |
+
response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
|
36 |
+
data = response.json()
|
37 |
+
if data and data.get("choices"):
|
38 |
+
return data["choices"][0]["message"]["content"].strip()
|
39 |
else:
|
40 |
+
print("AI response did not contain expected data.")
|
41 |
+
return None
|
42 |
except requests.exceptions.RequestException as e:
|
43 |
+
print(f"Error connecting to AI API: {e}")
|
44 |
+
return None
|
45 |
except json.JSONDecodeError:
|
46 |
+
print("Error decoding JSON response from AI API.")
|
47 |
+
return None
|
48 |
+
|
49 |
+
def get_audio_wav_link(text):
|
50 |
+
"""
|
51 |
+
دریافت لینک فایل WAV از API تبدیل متن به گفتار.
|
52 |
+
"""
|
53 |
+
encoded_text = requests.utils.quote(text) # Encode URL-unsafe characters
|
54 |
+
tts_url = f"{TTS_API_BASE_URL}{TTS_API_FORMAT}{encoded_text}"
|
55 |
+
|
|
|
|
|
56 |
try:
|
57 |
+
# برای این API، فقط ساختار URL مهم است، نیازی به درخواست مستقیم برای دریافت لینک نیست.
|
58 |
+
# فرض میشود که API بلافاصله لینک wav را به عنوان پاسخ GET برمیگرداند.
|
59 |
+
# اما طبق توضیحات شما، خروجی به صورت لینک wav است.
|
60 |
+
# این یعنی درخواست GET به آن URL، مستقیماً لینک را برمیگرداند.
|
61 |
+
# اگر API مستقیماً لینک WAV را در بدنه پاسخ برمیگرداند، میتوان از request.text استفاده کرد.
|
62 |
+
# در غیر این صورت، شاید باید یک درخواست واقعی انجام داد.
|
63 |
+
# با توجه به "خروجی صوت به صورت لینک wav است"، فرض میشود که خود URL ورودی، لینک WAV خروجی است.
|
64 |
+
# اگر API یک JSON با لینک wav برمیگرداند، باید کد را تغییر داد.
|
65 |
+
return tts_url
|
66 |
+
|
67 |
except requests.exceptions.RequestException as e:
|
68 |
+
print(f"Error generating TTS link: {e}")
|
69 |
return None
|
70 |
|
71 |
+
def main():
|
72 |
+
print("--------------------------------------------------")
|
73 |
+
print(" Talkbot Podcast Generator ")
|
74 |
+
print("--------------------------------------------------")
|
75 |
+
print("این اسکریپت یک متن پادکست تولید و لینکهای صوتی را فراهم میکند.")
|
76 |
+
print(" برای خروج، 'exit' را تایپ کنید.")
|
77 |
+
print("--------------------------------------------------")
|
78 |
+
|
79 |
+
while True:
|
80 |
+
topic = input("\nموضوع پادکست را وارد کنید (یا 'exit' برای خروج): ")
|
81 |
+
if topic.lower() == 'exit':
|
82 |
+
break
|
83 |
+
|
84 |
+
print(f"\nدر حال تولید اسکریپت پادکست برای '{topic}' با هوش مصنوعی...")
|
85 |
+
podcast_script = get_text_from_ai(topic)
|
86 |
+
|
87 |
+
if podcast_script:
|
88 |
+
print("\n------------------- اسکریپت پادکست -------------------")
|
89 |
+
print(podcast_script)
|
90 |
+
print("------------------------------------------------------")
|
91 |
+
|
92 |
+
# تقسیم متن به دو بخش برای صدای اول و صدای دوم
|
93 |
+
# این یک تقسیمبندی ساده است. ممکن است نیاز به منطق دقیقتری داشته باشید.
|
94 |
+
sentences = [s.strip() for s in podcast_script.split('.') if s.strip()]
|
95 |
+
|
96 |
+
if len(sentences) >= 2:
|
97 |
+
speaker1_text = sentences[0]
|
98 |
+
speaker2_text = sentences[1]
|
99 |
+
elif len(sentences) == 1:
|
100 |
+
speaker1_text = sentences[0]
|
101 |
+
speaker2_text = "" # یا بخشی از جمله اول
|
102 |
+
else:
|
103 |
+
print("متن کافی برای تقسیم بین دو گوینده وجود ندارد.")
|
104 |
+
speaker1_text = podcast_script
|
105 |
+
speaker2_text = ""
|
106 |
+
|
107 |
+
print("\nدر حال ساخت لینکهای صوتی...")
|
108 |
+
|
109 |
+
audio_link_speaker1 = get_audio_wav_link(speaker1_text)
|
110 |
+
audio_link_speaker2 = get_audio_wav_link(speaker2_text) # اگر speaker2_text خالی باشد، لینک معناداری تولید نمیشود.
|
111 |
+
|
112 |
+
if audio_link_speaker1:
|
113 |
+
print(f"لینک صدای اول (Speaker 1): {audio_link_speaker1}")
|
114 |
+
else:
|
115 |
+
print("خطا در تولید لینک صدای اول.")
|
116 |
+
|
117 |
+
if audio_link_speaker2 and speaker2_text: # فقط اگر متن برای گوینده دوم وجود داشته باشد
|
118 |
+
print(f"لینک صدای دوم (Speaker 2): {audio_link_speaker2}")
|
119 |
+
elif speaker2_text: # اگر speaker2_text داشتیم و لینکی تولید نشد
|
120 |
+
print("خطا در تولید لینک صدای دوم.")
|
121 |
+
else:
|
122 |
+
print("متن کافی برای تولید صدای دوم وجود نداشت.")
|
123 |
+
|
124 |
+
print("\n------------------------------------------------------")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
else:
|
126 |
+
print("متن پادکست از هوش مصنوعی دریافت نشد.")
|
127 |
+
print("لطفاً اتصال به اینترنت و کلید API را بررسی کنید.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
|
129 |
+
print("\nخروج از برنامه. موفق باشید!")
|
|
|
|
|
|
|
130 |
|
131 |
+
if __name__ == "__main__":
|
132 |
+
main()
|
|
|
|
|
|