aheedsajid commited on
Commit
af6d7d0
1 Parent(s): ac8cfb3

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +264 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import asyncio
3
+ import edge_tts
4
+ from pydub import AudioSegment
5
+ import os
6
+ import gradio as gr
7
+ from gradio_client import Client
8
+ import shutil
9
+ import uuid
10
+ from dotenv import load_dotenv
11
+ import re
12
+
13
+
14
+ load_dotenv()
15
+
16
+ def sanitize_filename(filename):
17
+ """Convert a string to a safe filename by removing special characters and spaces"""
18
+
19
+ safe_filename = re.sub(r'[^a-zA-Z0-9_-]', '', filename.replace(' ', '_'))
20
+
21
+ return safe_filename.lower()[:50]
22
+
23
+ async def get_voices():
24
+ """Get all available English voices from edge-tts"""
25
+ voices = await edge_tts.list_voices()
26
+ english_voices = [
27
+ voice for voice in voices
28
+ if voice["Locale"].startswith(("en-US", "en-GB", "en-AU", "en-CA", "en-IN"))
29
+ ]
30
+
31
+
32
+ formatted_voices = [
33
+ f"{voice['ShortName']} ({voice['Gender']}, {voice['Locale']})"
34
+ for voice in english_voices
35
+ ]
36
+
37
+ return formatted_voices
38
+
39
+ def extract_voice_name(voice_string):
40
+ """Extract the voice short name from the formatted string"""
41
+ return voice_string.split(" (")[0]
42
+
43
+ async def generate_audio(text, voice, filename):
44
+ communicate = edge_tts.Communicate(text, extract_voice_name(voice))
45
+ await communicate.save(filename)
46
+
47
+ async def create_podcast_versions(data, speaker1_name, speaker2_name, speaker1_voice, speaker2_voice, title):
48
+
49
+ session_id = str(uuid.uuid4())
50
+ temp_dir = f'temp_{session_id}'
51
+
52
+
53
+ safe_title = sanitize_filename(title)
54
+
55
+
56
+ if not os.path.exists(temp_dir):
57
+ os.makedirs(temp_dir)
58
+
59
+ try:
60
+
61
+ speaker1_version = AudioSegment.empty()
62
+ speaker2_version = AudioSegment.empty()
63
+ combined_version = AudioSegment.empty()
64
+
65
+
66
+ for i, entry in enumerate(data['conversation']):
67
+ if 'speaker1text' in entry:
68
+ temp_file = f'{temp_dir}/speaker1_{i}.mp3'
69
+ await generate_audio(entry['speaker1text'], speaker1_voice, temp_file)
70
+ audio = AudioSegment.from_file(temp_file)
71
+
72
+ speaker1_version += audio
73
+ speaker2_version += AudioSegment.silent(duration=len(audio))
74
+ combined_version += audio
75
+ os.remove(temp_file)
76
+
77
+ if 'speaker2text' in entry:
78
+ temp_file = f'{temp_dir}/speaker2_{i}.mp3'
79
+ await generate_audio(entry['speaker2text'], speaker2_voice, temp_file)
80
+ audio = AudioSegment.from_file(temp_file)
81
+
82
+ speaker2_version += audio
83
+ speaker1_version += AudioSegment.silent(duration=len(audio))
84
+ combined_version += audio
85
+ os.remove(temp_file)
86
+
87
+
88
+ speaker1_path = f"{safe_title}_{speaker1_name.lower()}_only.mp3"
89
+ speaker2_path = f"{safe_title}_{speaker2_name.lower()}_only.mp3"
90
+ combined_path = f"{safe_title}_combined.mp3"
91
+
92
+ speaker1_version.export(speaker1_path, format="mp3")
93
+ speaker2_version.export(speaker2_path, format="mp3")
94
+ combined_version.export(combined_path, format="mp3")
95
+
96
+ return speaker1_path, speaker2_path, combined_path, temp_dir
97
+
98
+ except Exception as e:
99
+ if os.path.exists(temp_dir):
100
+ shutil.rmtree(temp_dir)
101
+ raise e
102
+
103
+ def generate_podcast(title, channel_name, speaker1_name, speaker2_name, speaker1_voice, speaker2_voice):
104
+ try:
105
+
106
+ if not all([title, channel_name, speaker1_name, speaker2_name, speaker1_voice, speaker2_voice]):
107
+ raise ValueError("All fields must be filled out")
108
+
109
+
110
+ client = Client(os.getenv('API_URL'))
111
+ result = client.predict(
112
+ message=f"""{os.getenv('API_MESSAGE')} {{
113
+ "title": "{title}",
114
+ "channel": "{channel_name}",
115
+ "speaker1": "{speaker1_name}",
116
+ "speaker2": "{speaker2_name}",
117
+ "conversation": [
118
+ {{
119
+ "speaker1text": ""
120
+ }},
121
+ {{
122
+ "speaker2text": ""
123
+ }}
124
+ ]
125
+ }}
126
+
127
+ give 42 sentences for both.
128
+ """,
129
+ request=os.getenv('API_REQUEST'),
130
+ param_3=0.5,
131
+ param_4=8100,
132
+ param_5=0.5,
133
+ param_6=0,
134
+ api_name="/chat"
135
+ )
136
+
137
+
138
+ try:
139
+
140
+ podcast_data = json.loads(result)
141
+ except json.JSONDecodeError:
142
+
143
+ json_start = result.find('```') + 3
144
+ json_end = result.rfind('```')
145
+
146
+ if json_start > 2 and json_end > json_start:
147
+ if result[json_start:json_start+4] == 'json':
148
+ json_start = result.find('\n', json_start) + 1
149
+ json_str = result[json_start:json_end].strip()
150
+ podcast_data = json.loads(json_str)
151
+ else:
152
+ raise ValueError("Could not parse JSON from response")
153
+
154
+
155
+ speaker1_path, speaker2_path, combined_path, temp_dir = asyncio.run(
156
+ create_podcast_versions(
157
+ podcast_data,
158
+ speaker1_name,
159
+ speaker2_name,
160
+ speaker1_voice,
161
+ speaker2_voice,
162
+ title
163
+ )
164
+ )
165
+
166
+
167
+ if os.path.exists(temp_dir):
168
+ shutil.rmtree(temp_dir)
169
+
170
+ return [
171
+ speaker1_path,
172
+ speaker2_path,
173
+ combined_path,
174
+ podcast_data
175
+ ]
176
+
177
+ except Exception as e:
178
+ return [
179
+ None,
180
+ None,
181
+ None,
182
+ f"Error: {str(e)}"
183
+ ]
184
+
185
+
186
+ with gr.Blocks(theme=gr.themes.Soft()) as interface:
187
+
188
+ available_voices = asyncio.run(get_voices())
189
+
190
+ gr.Markdown("# Easy Podcast")
191
+ gr.Markdown("Generate a podcast conversation between two speakers on any topic. Choose voices and customize speaker details to create your perfect podcast.<br>To use elevelabs voices or cloned voices contact me at aheedsajid@gmail.com<br>Support me USDT (TRC-20) (TAe7hsSVWtMEYz3G5V1UiUdYPQVqm28bKx)")
192
+
193
+ with gr.Row():
194
+ with gr.Column():
195
+ title = gr.Textbox(
196
+ label="Podcast Topic",
197
+ placeholder="e.g., The Future of AI",
198
+ show_label=True
199
+ )
200
+ channel_name = gr.Textbox(
201
+ label="Channel Name",
202
+ placeholder="e.g., TechTalks",
203
+ value="WeePakistan",
204
+ show_label=True
205
+ )
206
+ with gr.Column():
207
+ speaker1_name = gr.Textbox(
208
+ label="First Speaker Name",
209
+ placeholder="e.g., John",
210
+ value="Andrew",
211
+ show_label=True
212
+ )
213
+ speaker2_name = gr.Textbox(
214
+ label="Second Speaker Name",
215
+ placeholder="e.g., Sarah",
216
+ value="Priya",
217
+ show_label=True
218
+ )
219
+
220
+ with gr.Row():
221
+ with gr.Column():
222
+ speaker1_voice = gr.Dropdown(
223
+ choices=available_voices,
224
+ value=next((v for v in available_voices if "Christopher" in v), available_voices[0]),
225
+ label="First Speaker Voice",
226
+ info="Select voice for the first speaker"
227
+ )
228
+ with gr.Column():
229
+ speaker2_voice = gr.Dropdown(
230
+ choices=available_voices,
231
+ value=next((v for v in available_voices if "Neerja" in v), available_voices[0]),
232
+ label="Second Speaker Voice",
233
+ info="Select voice for the second speaker"
234
+ )
235
+
236
+ generate_btn = gr.Button("Generate Podcast", variant="primary")
237
+
238
+ with gr.Row():
239
+ speaker1_audio = gr.Audio(label="First Speaker Audio")
240
+ speaker2_audio = gr.Audio(label="Second Speaker Audio")
241
+ combined_audio = gr.Audio(label="Combined Audio")
242
+
243
+ conversation_json = gr.JSON(label="Generated Conversation")
244
+
245
+ generate_btn.click(
246
+ fn=generate_podcast,
247
+ inputs=[
248
+ title,
249
+ channel_name,
250
+ speaker1_name,
251
+ speaker2_name,
252
+ speaker1_voice,
253
+ speaker2_voice
254
+ ],
255
+ outputs=[
256
+ speaker1_audio,
257
+ speaker2_audio,
258
+ combined_audio,
259
+ conversation_json
260
+ ]
261
+ )
262
+
263
+ if __name__ == "__main__":
264
+ interface.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ edge-tts
2
+ pydub
3
+ gradio
4
+ gradio-client
5
+ python-dotenv
6
+ uuid