saq1b commited on
Commit
8adf2ce
·
verified ·
1 Parent(s): 77ae41c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -93
app.py CHANGED
@@ -1,20 +1,23 @@
1
  import gradio as gr
2
  from pydub import AudioSegment
3
- from google import genai # Using the new Gemini API client
 
4
  import json
5
  import uuid
6
  import io
7
  import edge_tts
8
  import asyncio
 
 
9
  import os
10
  import time
11
- import aiofiles
12
 
13
  class PodcastGenerator:
14
  def __init__(self):
15
  pass
16
 
17
- async def generate_script(self, prompt: str, language: str, api_key: str, file_data=None, file_mime_type=None) -> dict:
18
  example = """
19
  {
20
  "topic": "AGI",
@@ -26,11 +29,187 @@ class PodcastGenerator:
26
  {
27
  "speaker": 1,
28
  "line": "Yeah, it's definitely having a moment, isn't it?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  }
30
- // ... (rest of the example)
31
  ]
32
  }
33
  """
 
34
  if language == "Auto Detect":
35
  language_instruction = "- The podcast MUST be in the same language as the user input."
36
  else:
@@ -50,33 +229,32 @@ Follow this example structure:
50
  """
51
  user_prompt = f"Please generate a podcast script based on the following user input:\n{prompt}"
52
 
53
- # Initialize the client (it will pick up the provided API key)
54
- client = genai.Client(api_key=api_key)
55
- contents = []
56
- if file_data is not None:
57
- try:
58
- uploaded_file = await client.aio.files.upload(
59
- path=io.BytesIO(file_data),
60
- config={"mime_type": file_mime_type}
61
- )
62
- except Exception as e:
63
- raise gr.Error(f"File upload failed: {e}")
64
- contents.append(uploaded_file)
65
- contents.append(user_prompt)
66
-
67
- config = {
68
- "system_instruction": system_prompt,
69
- "temperature": 1,
70
- "max_output_tokens": 8192,
71
- "response_mime_type": "application/json",
72
  }
73
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  try:
75
- response = await client.aio.models.generate_content(
76
- model="gemini-2.0-flash",
77
- contents=contents,
78
- config=config
79
- )
80
  except Exception as e:
81
  if "API key not valid" in str(e):
82
  raise gr.Error("Invalid API key. Please provide a valid Gemini API key.")
@@ -86,6 +264,7 @@ Follow this example structure:
86
  raise gr.Error(f"Failed to generate podcast script: {e}")
87
 
88
  print(f"Generated podcast script:\n{response.text}")
 
89
  return json.loads(response.text)
90
 
91
  async def tts_generate(self, text: str, speaker: int, speaker1: str, speaker2: str) -> str:
@@ -101,7 +280,7 @@ Follow this example structure:
101
  os.remove(temp_filename)
102
  raise e
103
 
104
- async def combine_audio_files(self, audio_files: list) -> str:
105
  combined_audio = AudioSegment.empty()
106
  for audio_file in audio_files:
107
  combined_audio += AudioSegment.from_file(audio_file)
@@ -111,24 +290,44 @@ Follow this example structure:
111
  combined_audio.export(output_filename, format="wav")
112
  return output_filename
113
 
114
- async def generate_podcast(self, input_text: str, language: str, speaker1: str, speaker2: str, api_key: str, file_data=None, file_mime_type=None) -> str:
115
  gr.Info("Generating podcast script...")
116
  start_time = time.time()
117
- podcast_json = await self.generate_script(input_text, language, api_key, file_data, file_mime_type)
118
  end_time = time.time()
119
  gr.Info(f"Successfully generated podcast script in {(end_time - start_time):.2f} seconds!")
120
 
121
  gr.Info("Generating podcast audio files...")
122
  start_time = time.time()
123
- audio_files = await asyncio.gather(*[
124
- self.tts_generate(item['line'], item['speaker'], speaker1, speaker2)
125
- for item in podcast_json['podcast']
126
- ])
127
  end_time = time.time()
128
  gr.Info(f"Successfully generated podcast audio files in {(end_time - start_time):.2f} seconds!")
129
 
130
  combined_audio = await self.combine_audio_files(audio_files)
131
  return combined_audio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
  async def process_input(input_text: str, input_file, language: str, speaker1: str, speaker2: str, api_key: str = "") -> str:
134
  gr.Info("Starting podcast generation...")
@@ -148,24 +347,18 @@ async def process_input(input_text: str, input_file, language: str, speaker1: st
148
  speaker1 = voice_names[speaker1]
149
  speaker2 = voice_names[speaker2]
150
 
151
- file_data = None
152
- file_mime_type = None
153
  if input_file:
154
- ext = os.path.splitext(input_file.name)[1].lower()
155
- if ext not in ['.pdf', '.txt']:
156
- raise gr.Error("Unsupported file type. Only PDF and TXT files are allowed.")
157
- async with aiofiles.open(input_file.name, 'rb') as f:
158
- file_data = await f.read()
159
- file_mime_type = 'application/pdf' if ext == '.pdf' else 'text/plain'
160
 
161
  if not api_key:
162
  api_key = os.getenv("GENAI_API_KEY")
163
 
164
  podcast_generator = PodcastGenerator()
165
- podcast = await podcast_generator.generate_podcast(input_text, language, speaker1, speaker2, api_key, file_data, file_mime_type)
166
 
167
  end_time = time.time()
168
  gr.Info(f"Successfully generated podcast in {(end_time - start_time):.2f} seconds!")
 
169
  return podcast
170
 
171
  # Define Gradio interface
@@ -174,54 +367,45 @@ iface = gr.Interface(
174
  inputs=[
175
  gr.Textbox(label="Input Text"),
176
  gr.File(label="Or Upload a PDF or TXT file"),
177
- gr.Dropdown(
178
- label="Language",
179
- choices=[
180
- "Auto Detect",
181
- "Afrikaans", "Albanian", "Amharic", "Arabic", "Armenian", "Azerbaijani",
182
- "Bahasa Indonesian", "Bangla", "Basque", "Bengali", "Bosnian", "Bulgarian",
183
- "Burmese", "Catalan", "Chinese Cantonese", "Chinese Mandarin",
184
- "Chinese Taiwanese", "Croatian", "Czech", "Danish", "Dutch", "English",
185
- "Estonian", "Filipino", "Finnish", "French", "Galician", "Georgian",
186
- "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Irish",
187
- "Italian", "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer", "Korean",
188
- "Lao", "Latvian", "Lithuanian", "Macedonian", "Malay", "Malayalam",
189
- "Maltese", "Mongolian", "Nepali", "Norwegian Bokmål", "Pashto", "Persian",
190
- "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Sinhala",
191
- "Slovak", "Slovene", "Somali", "Spanish", "Sundanese", "Swahili",
192
- "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
193
- "Uzbek", "Vietnamese", "Welsh", "Zulu"
194
- ],
195
- value="Auto Detect"
196
- ),
197
- gr.Dropdown(
198
- label="Speaker 1 Voice",
199
- choices=[
200
- "Andrew - English (United States)",
201
- "Ava - English (United States)",
202
- "Brian - English (United States)",
203
- "Emma - English (United States)",
204
- "Florian - German (Germany)",
205
- "Seraphina - German (Germany)",
206
- "Remy - French (France)",
207
- "Vivienne - French (France)"
208
- ],
209
- value="Andrew - English (United States)"
210
- ),
211
- gr.Dropdown(
212
- label="Speaker 2 Voice",
213
- choices=[
214
- "Andrew - English (United States)",
215
- "Ava - English (United States)",
216
- "Brian - English (United States)",
217
- "Emma - English (United States)",
218
- "Florian - German (Germany)",
219
- "Seraphina - German (Germany)",
220
- "Remy - French (France)",
221
- "Vivienne - French (France)"
222
- ],
223
- value="Ava - English (United States)"
224
- ),
225
  gr.Textbox(label="Your Gemini API Key (Optional) - In case you are getting rate limited"),
226
  ],
227
  outputs=[
@@ -233,4 +417,4 @@ iface = gr.Interface(
233
  )
234
 
235
  if __name__ == "__main__":
236
- iface.launch()
 
1
  import gradio as gr
2
  from pydub import AudioSegment
3
+ import google.generativeai as genai
4
+ from google.generativeai.types import HarmCategory, HarmBlockThreshold
5
  import json
6
  import uuid
7
  import io
8
  import edge_tts
9
  import asyncio
10
+ import aiofiles
11
+ import pypdf
12
  import os
13
  import time
14
+ from typing import List, Dict, Tuple
15
 
16
  class PodcastGenerator:
17
  def __init__(self):
18
  pass
19
 
20
+ async def generate_script(self, prompt: str, language: str, api_key: str) -> Dict:
21
  example = """
22
  {
23
  "topic": "AGI",
 
29
  {
30
  "speaker": 1,
31
  "line": "Yeah, it's definitely having a moment, isn't it?"
32
+ },
33
+ {
34
+ "speaker": 2,
35
+ "line": "It is and for good reason, right? I mean, you've been digging into this stuff, listening to the podcasts and everything. What really stood out to you? What got you hooked?"
36
+ },
37
+ {
38
+ "speaker": 1,
39
+ "line": "Honestly, it's the sheer scale of what AGI could do. We're talking about potentially reshaping well everything."
40
+ },
41
+ {
42
+ "speaker": 2,
43
+ "line": "No kidding, but let's be real. Sometimes it feels like every other headline is either hyping AGI up as this technological utopia or painting it as our inevitable robot overlords."
44
+ },
45
+ {
46
+ "speaker": 1,
47
+ "line": "It's easy to get lost in the noise, for sure."
48
+ },
49
+ {
50
+ "speaker": 2,
51
+ "line": "Exactly. So how about we try to cut through some of that, shall we?"
52
+ },
53
+ {
54
+ "speaker": 1,
55
+ "line": "Sounds like a plan."
56
+ },
57
+ {
58
+ "speaker": 2,
59
+ "line": "Okay, so first things first, AGI, what is it really? And I don't just mean some dictionary definition, we're talking about something way bigger than just a super smart computer, right?"
60
+ },
61
+ {
62
+ "speaker": 1,
63
+ "line": "Right, it's not just about more processing power or better algorithms, it's about a fundamental shift in how we think about intelligence itself."
64
+ },
65
+ {
66
+ "speaker": 2,
67
+ "line": "So like, instead of programming a machine for a specific task, we're talking about creating something that can learn and adapt like we do."
68
+ },
69
+ {
70
+ "speaker": 1,
71
+ "line": "Exactly, think of it this way: Right now, we've got AI that can beat a grandmaster at chess but ask that same AI to, say, write a poem or compose a symphony. No chance."
72
+ },
73
+ {
74
+ "speaker": 2,
75
+ "line": "Okay, I see. So, AGI is about bridging that gap, creating something that can move between those different realms of knowledge seamlessly."
76
+ },
77
+ {
78
+ "speaker": 1,
79
+ "line": "Precisely. It's about replicating that uniquely human ability to learn something new and apply that knowledge in completely different contexts and that's a tall order, let me tell you."
80
+ },
81
+ {
82
+ "speaker": 2,
83
+ "line": "I bet. I mean, think about how much we still don't even understand about our own brains."
84
+ },
85
+ {
86
+ "speaker": 1,
87
+ "line": "That's exactly it. We're essentially trying to reverse-engineer something we don't fully comprehend."
88
+ },
89
+ {
90
+ "speaker": 2,
91
+ "line": "And how are researchers even approaching that? What are some of the big ideas out there?"
92
+ },
93
+ {
94
+ "speaker": 1,
95
+ "line": "Well, there are a few different schools of thought. One is this idea of neuromorphic computing where they're literally trying to build computer chips that mimic the structure and function of the human brain."
96
+ },
97
+ {
98
+ "speaker": 2,
99
+ "line": "Wow, so like actually replicating the physical architecture of the brain. That's wild."
100
+ },
101
+ {
102
+ "speaker": 1,
103
+ "line": "It's pretty mind-blowing stuff and then you've got folks working on something called whole brain emulation."
104
+ },
105
+ {
106
+ "speaker": 2,
107
+ "line": "Okay, and what's that all about?"
108
+ },
109
+ {
110
+ "speaker": 1,
111
+ "line": "The basic idea there is to create a complete digital copy of a human brain down to the last neuron and synapse and run it on a sufficiently powerful computer simulation."
112
+ },
113
+ {
114
+ "speaker": 2,
115
+ "line": "Hold on, a digital copy of an entire brain, that sounds like something straight out of science fiction."
116
+ },
117
+ {
118
+ "speaker": 1,
119
+ "line": "It does, doesn't it? But it gives you an idea of the kind of ambition we're talking about here and the truth is we're still a long way off from truly achieving AGI, no matter which approach you look at."
120
+ },
121
+ {
122
+ "speaker": 2,
123
+ "line": "That makes sense but it's still exciting to think about the possibilities, even if they're a ways off."
124
+ },
125
+ {
126
+ "speaker": 1,
127
+ "line": "Absolutely and those possibilities are what really get people fired up about AGI, right? Yeah."
128
+ },
129
+ {
130
+ "speaker": 2,
131
+ "line": "For sure. In fact, I remember you mentioning something in that podcast about AGI's potential to revolutionize scientific research. Something about supercharging breakthroughs."
132
+ },
133
+ {
134
+ "speaker": 1,
135
+ "line": "Oh, absolutely. Imagine an AI that doesn't just crunch numbers but actually understands scientific data the way a human researcher does. We're talking about potential breakthroughs in everything from medicine and healthcare to material science and climate change."
136
+ },
137
+ {
138
+ "speaker": 2,
139
+ "line": "It's like giving scientists this incredibly powerful new tool to tackle some of the biggest challenges we face."
140
+ },
141
+ {
142
+ "speaker": 1,
143
+ "line": "Exactly, it could be a total game changer."
144
+ },
145
+ {
146
+ "speaker": 2,
147
+ "line": "Okay, but let's be real, every coin has two sides. What about the potential downsides of AGI? Because it can't all be sunshine and roses, right?"
148
+ },
149
+ {
150
+ "speaker": 1,
151
+ "line": "Right, there are definitely valid concerns. Probably the biggest one is the impact on the job market. As AGI gets more sophisticated, there's a real chance it could automate a lot of jobs that are currently done by humans."
152
+ },
153
+ {
154
+ "speaker": 2,
155
+ "line": "So we're not just talking about robots taking over factories but potentially things like, what, legal work, analysis, even creative fields?"
156
+ },
157
+ {
158
+ "speaker": 1,
159
+ "line": "Potentially, yes. And that raises a whole host of questions about what happens to those workers, how we retrain them, how we ensure that the benefits of AGI are shared equitably."
160
+ },
161
+ {
162
+ "speaker": 2,
163
+ "line": "Right, because it's not just about the technology itself, but how we choose to integrate it into society."
164
+ },
165
+ {
166
+ "speaker": 1,
167
+ "line": "Absolutely. We need to be having these conversations now about ethics, about regulation, about how to make sure AGI is developed and deployed responsibly."
168
+ },
169
+ {
170
+ "speaker": 2,
171
+ "line": "So it's less about preventing some kind of sci-fi robot apocalypse and more about making sure we're steering this technology in the right direction from the get-go."
172
+ },
173
+ {
174
+ "speaker": 1,
175
+ "line": "Exactly, AGI has the potential to be incredibly beneficial, but it's not going to magically solve all our problems. It's on us to make sure we're using it for good."
176
+ },
177
+ {
178
+ "speaker": 2,
179
+ "line": "It's like you said earlier, it's about shaping the future of intelligence."
180
+ },
181
+ {
182
+ "speaker": 1,
183
+ "line": "I like that. It really is."
184
+ },
185
+ {
186
+ "speaker": 2,
187
+ "line": "And honestly, that's a responsibility that extends beyond just the researchers and the policymakers."
188
+ },
189
+ {
190
+ "speaker": 1,
191
+ "line": "100%"
192
+ },
193
+ {
194
+ "speaker": 2,
195
+ "line": "So to everyone listening out there I'll leave you with this. As AGI continues to develop, what role do you want to play in shaping its future?"
196
+ },
197
+ {
198
+ "speaker": 1,
199
+ "line": "That's a question worth pondering."
200
+ },
201
+ {
202
+ "speaker": 2,
203
+ "line": "It certainly is and on that note, we'll wrap up this deep dive. Thanks for listening, everyone."
204
+ },
205
+ {
206
+ "speaker": 1,
207
+ "line": "Peace."
208
  }
 
209
  ]
210
  }
211
  """
212
+
213
  if language == "Auto Detect":
214
  language_instruction = "- The podcast MUST be in the same language as the user input."
215
  else:
 
229
  """
230
  user_prompt = f"Please generate a podcast script based on the following user input:\n{prompt}"
231
 
232
+ messages = [
233
+ {"role": "user", "parts": [user_prompt]}
234
+ ]
235
+
236
+ genai.configure(api_key=api_key)
237
+
238
+ generation_config = {
239
+ "temperature": 1,
240
+ "max_output_tokens": 8192,
241
+ "response_mime_type": "application/json",
 
 
 
 
 
 
 
 
 
242
  }
243
 
244
+ model = genai.GenerativeModel(
245
+ model_name="gemini-2.0-flash",
246
+ generation_config=generation_config,
247
+ safety_settings={
248
+ HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
249
+ HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
250
+ HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
251
+ HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE
252
+ },
253
+ system_instruction=system_prompt
254
+ )
255
+
256
  try:
257
+ response = await model.generate_content_async(messages)
 
 
 
 
258
  except Exception as e:
259
  if "API key not valid" in str(e):
260
  raise gr.Error("Invalid API key. Please provide a valid Gemini API key.")
 
264
  raise gr.Error(f"Failed to generate podcast script: {e}")
265
 
266
  print(f"Generated podcast script:\n{response.text}")
267
+
268
  return json.loads(response.text)
269
 
270
  async def tts_generate(self, text: str, speaker: int, speaker1: str, speaker2: str) -> str:
 
280
  os.remove(temp_filename)
281
  raise e
282
 
283
+ async def combine_audio_files(self, audio_files: List[str]) -> str:
284
  combined_audio = AudioSegment.empty()
285
  for audio_file in audio_files:
286
  combined_audio += AudioSegment.from_file(audio_file)
 
290
  combined_audio.export(output_filename, format="wav")
291
  return output_filename
292
 
293
+ async def generate_podcast(self, input_text: str, language: str, speaker1: str, speaker2: str, api_key: str) -> str:
294
  gr.Info("Generating podcast script...")
295
  start_time = time.time()
296
+ podcast_json = await self.generate_script(input_text, language, api_key)
297
  end_time = time.time()
298
  gr.Info(f"Successfully generated podcast script in {(end_time - start_time):.2f} seconds!")
299
 
300
  gr.Info("Generating podcast audio files...")
301
  start_time = time.time()
302
+ audio_files = await asyncio.gather(*[self.tts_generate(item['line'], item['speaker'], speaker1, speaker2) for item in podcast_json['podcast']])
 
 
 
303
  end_time = time.time()
304
  gr.Info(f"Successfully generated podcast audio files in {(end_time - start_time):.2f} seconds!")
305
 
306
  combined_audio = await self.combine_audio_files(audio_files)
307
  return combined_audio
308
+
309
+ class TextExtractor:
310
+ @staticmethod
311
+ async def extract_from_pdf(file_path: str) -> str:
312
+ async with aiofiles.open(file_path, 'rb') as file:
313
+ content = await file.read()
314
+ pdf_reader = pypdf.PdfReader(io.BytesIO(content))
315
+ return "\n\n".join(page.extract_text() for page in pdf_reader.pages if page.extract_text())
316
+
317
+ @staticmethod
318
+ async def extract_from_txt(file_path: str) -> str:
319
+ async with aiofiles.open(file_path, 'r') as file:
320
+ return await file.read()
321
+
322
+ @classmethod
323
+ async def extract_text(cls, file_path: str) -> str:
324
+ _, file_extension = os.path.splitext(file_path)
325
+ if file_extension.lower() == '.pdf':
326
+ return await cls.extract_from_pdf(file_path)
327
+ elif file_extension.lower() == '.txt':
328
+ return await cls.extract_from_txt(file_path)
329
+ else:
330
+ raise gr.Error(f"Unsupported file type: {file_extension}")
331
 
332
  async def process_input(input_text: str, input_file, language: str, speaker1: str, speaker2: str, api_key: str = "") -> str:
333
  gr.Info("Starting podcast generation...")
 
347
  speaker1 = voice_names[speaker1]
348
  speaker2 = voice_names[speaker2]
349
 
 
 
350
  if input_file:
351
+ input_text = await TextExtractor.extract_text(input_file.name)
 
 
 
 
 
352
 
353
  if not api_key:
354
  api_key = os.getenv("GENAI_API_KEY")
355
 
356
  podcast_generator = PodcastGenerator()
357
+ podcast = await podcast_generator.generate_podcast(input_text, language, speaker1, speaker2, api_key)
358
 
359
  end_time = time.time()
360
  gr.Info(f"Successfully generated podcast in {(end_time - start_time):.2f} seconds!")
361
+
362
  return podcast
363
 
364
  # Define Gradio interface
 
367
  inputs=[
368
  gr.Textbox(label="Input Text"),
369
  gr.File(label="Or Upload a PDF or TXT file"),
370
+ gr.Dropdown(label="Language", choices=[
371
+ "Auto Detect",
372
+ "Afrikaans", "Albanian", "Amharic", "Arabic", "Armenian", "Azerbaijani",
373
+ "Bahasa Indonesian", "Bangla", "Basque", "Bengali", "Bosnian", "Bulgarian",
374
+ "Burmese", "Catalan", "Chinese Cantonese", "Chinese Mandarin",
375
+ "Chinese Taiwanese", "Croatian", "Czech", "Danish", "Dutch", "English",
376
+ "Estonian", "Filipino", "Finnish", "French", "Galician", "Georgian",
377
+ "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Irish",
378
+ "Italian", "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer", "Korean",
379
+ "Lao", "Latvian", "Lithuanian", "Macedonian", "Malay", "Malayalam",
380
+ "Maltese", "Mongolian", "Nepali", "Norwegian Bokmål", "Pashto", "Persian",
381
+ "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Sinhala",
382
+ "Slovak", "Slovene", "Somali", "Spanish", "Sundanese", "Swahili",
383
+ "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
384
+ "Uzbek", "Vietnamese", "Welsh", "Zulu"
385
+ ],
386
+ value="Auto Detect"),
387
+ gr.Dropdown(label="Speaker 1 Voice", choices=[
388
+ "Andrew - English (United States)",
389
+ "Ava - English (United States)",
390
+ "Brian - English (United States)",
391
+ "Emma - English (United States)",
392
+ "Florian - German (Germany)",
393
+ "Seraphina - German (Germany)",
394
+ "Remy - French (France)",
395
+ "Vivienne - French (France)"
396
+ ],
397
+ value="Andrew - English (United States)"),
398
+ gr.Dropdown(label="Speaker 2 Voice", choices=[
399
+ "Andrew - English (United States)",
400
+ "Ava - English (United States)",
401
+ "Brian - English (United States)",
402
+ "Emma - English (United States)",
403
+ "Florian - German (Germany)",
404
+ "Seraphina - German (Germany)",
405
+ "Remy - French (France)",
406
+ "Vivienne - French (France)"
407
+ ],
408
+ value="Ava - English (United States)"),
 
 
 
 
 
 
 
 
 
409
  gr.Textbox(label="Your Gemini API Key (Optional) - In case you are getting rate limited"),
410
  ],
411
  outputs=[
 
417
  )
418
 
419
  if __name__ == "__main__":
420
+ iface.launch()