saq1b commited on
Commit
43f9ba7
·
verified ·
1 Parent(s): c1a4c87

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +333 -198
app.py CHANGED
@@ -1,8 +1,7 @@
1
  import gradio as gr
2
- import logging
3
  from pydub import AudioSegment
4
- from google import genai # Using the new Gemini API client
5
- from google.genai import types # For inline file parts
6
  import json
7
  import uuid
8
  import io
@@ -10,19 +9,13 @@ import edge_tts
10
  import asyncio
11
  import os
12
  import time
13
- import aiofiles
14
-
15
- # Set up logging
16
- logging.basicConfig(level=logging.INFO)
17
-
18
- # Maximum file size allowed: 20 MB
19
- MAX_FILE_SIZE = 20 * 1024 * 1024
20
 
21
  class PodcastGenerator:
22
  def __init__(self):
23
  pass
24
 
25
- async def generate_script(self, prompt: str, language: str, api_key: str, file_data=None, file_mime_type=None) -> dict:
26
  example = """
27
  {
28
  "topic": "AGI",
@@ -34,8 +27,183 @@ class PodcastGenerator:
34
  {
35
  "speaker": 1,
36
  "line": "Yeah, it's definitely having a moment, isn't it?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
- // ... (rest of the example)
39
  ]
40
  }
41
  """
@@ -52,231 +220,198 @@ You are a professional podcast generator. Your task is to generate a professiona
52
  - Do not use names for the speakers.
53
  - The podcast should be interesting, lively, and engaging, and hook the listener from the start.
54
  - The input text might be disorganized or unformatted, originating from sources like PDFs or text files. Ignore any formatting inconsistencies or irrelevant details; your task is to distill the essential points, identify key definitions, and highlight intriguing facts that would be suitable for discussion in a podcast.
55
- - The script must be in JSON format.
56
  Follow this example structure:
57
  {example}
58
- """
59
  user_prompt = f"Please generate a podcast script based on the following user input:\n{prompt}"
60
 
61
- # Initialize the Gemini API client with the provided API key.
62
  client = genai.Client(api_key=api_key)
63
- contents = []
64
- if file_data is not None:
65
- try:
66
- # Use inline file data directly without uploading.
67
- contents.append(types.Part.from_bytes(data=file_data, mime_type=file_mime_type))
68
- except Exception as e:
69
- logging.error("Error preparing file part: %s", e)
70
- raise gr.Error(f"Error processing file data: {e}")
71
- contents.append(user_prompt)
72
-
73
- config = {
74
- "system_instruction": system_prompt,
75
- "temperature": 1,
76
- "max_output_tokens": 8192,
77
- "response_mime_type": "application/json",
78
- }
79
-
80
- try:
81
- response = await client.aio.models.generate_content(
82
- model="gemini-2.0-flash",
83
- contents=contents,
84
- config=config
85
- )
86
- except Exception as e:
87
- logging.error("API call failed: %s", e)
88
- if "API key not valid" in str(e):
89
- raise gr.Error("Invalid API key. Please provide a valid Gemini API key.")
90
- elif "rate limit" in str(e).lower():
91
- raise gr.Error("Rate limit exceeded for the API key. Please try again later or provide your own Gemini API key.")
92
- else:
93
- raise gr.Error(f"Failed to generate podcast script: {e}")
94
-
95
- try:
96
- result = json.loads(response.text)
97
- except json.JSONDecodeError as e:
98
- logging.error("JSON parsing failed: %s", e)
99
- raise gr.Error(f"Response is not valid JSON: {e}")
100
-
101
- logging.info("Successfully generated script: %s", result)
102
- return result
103
 
104
  async def tts_generate(self, text: str, speaker: int, speaker1: str, speaker2: str) -> str:
105
  voice = speaker1 if speaker == 1 else speaker2
106
- try:
107
- speech = edge_tts.Communicate(text, voice)
108
- except Exception as e:
109
- logging.error("TTS initialization failed: %s", e)
110
- raise gr.Error(f"Text-to-Speech initialization error: {e}")
111
 
112
  temp_filename = f"temp_{uuid.uuid4()}.wav"
113
  try:
114
  await speech.save(temp_filename)
115
  return temp_filename
116
  except Exception as e:
117
- logging.error("TTS generation failed: %s", e)
118
  if os.path.exists(temp_filename):
119
  os.remove(temp_filename)
120
- raise gr.Error(f"Failed to generate speech for text: {e}")
121
 
122
  async def combine_audio_files(self, audio_files: list) -> str:
123
- try:
124
- combined_audio = AudioSegment.empty()
125
- for audio_file in audio_files:
126
- try:
127
- combined_audio += AudioSegment.from_file(audio_file)
128
- except Exception as inner_e:
129
- logging.error("Error processing audio file %s: %s", audio_file, inner_e)
130
- raise gr.Error(f"Error processing audio file: {inner_e}")
131
- finally:
132
- if os.path.exists(audio_file):
133
- os.remove(audio_file) # Clean up temporary file
134
- output_filename = f"output_{uuid.uuid4()}.wav"
135
- combined_audio.export(output_filename, format="wav")
136
- return output_filename
137
- except Exception as e:
138
- logging.error("Failed to combine audio files: %s", e)
139
- raise gr.Error(f"Failed to combine audio files: {e}")
140
 
141
- 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:
142
- try:
143
- gr.Info("Generating podcast script...")
144
- start_time = time.time()
145
- podcast_json = await self.generate_script(input_text, language, api_key, file_data, file_mime_type)
146
- end_time = time.time()
147
- gr.Info(f"Successfully generated podcast script in {(end_time - start_time):.2f} seconds!")
148
- except Exception as e:
149
- logging.error("Script generation error: %s", e)
150
- raise gr.Error(f"Error generating podcast script: {e}")
151
 
152
- try:
153
- gr.Info("Generating podcast audio files...")
154
- start_time = time.time()
155
- audio_files = await asyncio.gather(*[
156
- self.tts_generate(item['line'], item['speaker'], speaker1, speaker2)
157
- for item in podcast_json.get('podcast', [])
158
- ])
159
- end_time = time.time()
160
- gr.Info(f"Successfully generated podcast audio files in {(end_time - start_time):.2f} seconds!")
161
- except Exception as e:
162
- logging.error("TTS generation error: %s", e)
163
- raise gr.Error(f"Error generating audio files: {e}")
164
 
165
- try:
166
- combined_audio = await self.combine_audio_files(audio_files)
167
- return combined_audio
168
- except Exception as e:
169
- logging.error("Audio combining error: %s", e)
170
- raise gr.Error(f"Error combining audio files: {e}")
 
 
 
 
 
171
 
172
  async def process_input(input_text: str, input_file, language: str, speaker1: str, speaker2: str, api_key: str = "") -> str:
173
- try:
174
- gr.Info("Starting podcast generation...")
175
- start_time = time.time()
176
 
177
- voice_names = {
178
- "Andrew - English (United States)": "en-US-AndrewMultilingualNeural",
179
- "Ava - English (United States)": "en-US-AvaMultilingualNeural",
180
- "Brian - English (United States)": "en-US-BrianMultilingualNeural",
181
- "Emma - English (United States)": "en-US-EmmaMultilingualNeural",
182
- "Florian - German (Germany)": "de-DE-FlorianMultilingualNeural",
183
- "Seraphina - German (Germany)": "de-DE-SeraphinaMultilingualNeural",
184
- "Remy - French (France)": "fr-FR-RemyMultilingualNeural",
185
- "Vivienne - French (France)": "fr-FR-VivienneMultilingualNeural"
186
- }
187
 
188
- speaker1 = voice_names.get(speaker1, speaker1)
189
- speaker2 = voice_names.get(speaker2, speaker2)
190
 
191
- file_data = None
192
- file_mime_type = None
193
- if input_file:
194
- ext = os.path.splitext(input_file.name)[1].lower()
195
- if ext not in ['.pdf', '.txt']:
196
- raise gr.Error("Unsupported file type. Only PDF and TXT files are allowed.")
197
- try:
198
- async with aiofiles.open(input_file.name, 'rb') as f:
199
- file_data = await f.read()
200
- except Exception as e:
201
- logging.error("Error reading file: %s", e)
202
- raise gr.Error(f"Error reading file: {e}")
203
- if len(file_data) > MAX_FILE_SIZE:
204
- raise gr.Error("File size exceeds 20MB limit.")
205
- file_mime_type = 'application/pdf' if ext == '.pdf' else 'text/plain'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
- if not api_key:
208
- api_key = os.getenv("GENAI_API_KEY")
209
- if not api_key:
210
- raise gr.Error("No API key provided and none found in the environment.")
 
 
211
 
 
 
 
 
 
 
 
 
212
  podcast_generator = PodcastGenerator()
213
- podcast = await podcast_generator.generate_podcast(input_text, language, speaker1, speaker2, api_key, file_data, file_mime_type)
214
 
215
- end_time = time.time()
216
- gr.Info(f"Successfully generated podcast in {(end_time - start_time):.2f} seconds!")
217
- return podcast
218
- except Exception as e:
219
- logging.error("Process input error: %s", e)
220
- raise gr.Error(f"Error in processing input: {e}")
 
 
 
 
 
 
 
221
 
222
- # Note the updated output: forcing type="filepath" to avoid the schema bug.
223
  iface = gr.Interface(
224
  fn=process_input,
225
  inputs=[
226
  gr.Textbox(label="Input Text"),
227
  gr.File(label="Or Upload a PDF or TXT file"),
228
- gr.Dropdown(
229
- label="Language",
230
- choices=[
231
- "Auto Detect",
232
- "Afrikaans", "Albanian", "Amharic", "Arabic", "Armenian", "Azerbaijani",
233
- "Bahasa Indonesian", "Bangla", "Basque", "Bengali", "Bosnian", "Bulgarian",
234
- "Burmese", "Catalan", "Chinese Cantonese", "Chinese Mandarin",
235
- "Chinese Taiwanese", "Croatian", "Czech", "Danish", "Dutch", "English",
236
- "Estonian", "Filipino", "Finnish", "French", "Galician", "Georgian",
237
- "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Irish",
238
- "Italian", "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer", "Korean",
239
- "Lao", "Latvian", "Lithuanian", "Macedonian", "Malay", "Malayalam",
240
- "Maltese", "Mongolian", "Nepali", "Norwegian Bokmål", "Pashto", "Persian",
241
- "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Sinhala",
242
- "Slovak", "Slovene", "Somali", "Spanish", "Sundanese", "Swahili",
243
- "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
244
- "Uzbek", "Vietnamese", "Welsh", "Zulu"
245
- ],
246
- value="Auto Detect"
247
- ),
248
- gr.Dropdown(
249
- label="Speaker 1 Voice",
250
- choices=[
251
- "Andrew - English (United States)",
252
- "Ava - English (United States)",
253
- "Brian - English (United States)",
254
- "Emma - English (United States)",
255
- "Florian - German (Germany)",
256
- "Seraphina - German (Germany)",
257
- "Remy - French (France)",
258
- "Vivienne - French (France)"
259
- ],
260
- value="Andrew - English (United States)"
261
- ),
262
- gr.Dropdown(
263
- label="Speaker 2 Voice",
264
- choices=[
265
- "Andrew - English (United States)",
266
- "Ava - English (United States)",
267
- "Brian - English (United States)",
268
- "Emma - English (United States)",
269
- "Florian - German (Germany)",
270
- "Seraphina - German (Germany)",
271
- "Remy - French (France)",
272
- "Vivienne - French (France)"
273
- ],
274
- value="Ava - English (United States)"
275
- ),
276
  gr.Textbox(label="Your Gemini API Key (Optional) - In case you are getting rate limited"),
277
  ],
278
  outputs=[
279
- gr.Audio(label="Generated Podcast Audio", type="filepath")
280
  ],
281
  title="PodcastGen 🎙️",
282
  description="Generate a 2-speaker podcast from text input or documents!",
 
1
  import gradio as gr
 
2
  from pydub import AudioSegment
3
+ from google import genai
4
+ from google.genai import types
5
  import json
6
  import uuid
7
  import io
 
9
  import asyncio
10
  import os
11
  import time
12
+ import pathlib
 
 
 
 
 
 
13
 
14
  class PodcastGenerator:
15
  def __init__(self):
16
  pass
17
 
18
+ async def generate_script(self, prompt: str, language: str, api_key: str) -> dict:
19
  example = """
20
  {
21
  "topic": "AGI",
 
27
  {
28
  "speaker": 1,
29
  "line": "Yeah, it's definitely having a moment, isn't it?"
30
+ },
31
+ {
32
+ "speaker": 2,
33
+ "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?"
34
+ },
35
+ {
36
+ "speaker": 1,
37
+ "line": "Honestly, it's the sheer scale of what AGI could do. We're talking about potentially reshaping well everything."
38
+ },
39
+ {
40
+ "speaker": 2,
41
+ "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."
42
+ },
43
+ {
44
+ "speaker": 1,
45
+ "line": "It's easy to get lost in the noise, for sure."
46
+ },
47
+ {
48
+ "speaker": 2,
49
+ "line": "Exactly. So how about we try to cut through some of that, shall we?"
50
+ },
51
+ {
52
+ "speaker": 1,
53
+ "line": "Sounds like a plan."
54
+ },
55
+ {
56
+ "speaker": 2,
57
+ "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?"
58
+ },
59
+ {
60
+ "speaker": 1,
61
+ "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."
62
+ },
63
+ {
64
+ "speaker": 2,
65
+ "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."
66
+ },
67
+ {
68
+ "speaker": 1,
69
+ "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."
70
+ },
71
+ {
72
+ "speaker": 2,
73
+ "line": "Okay, I see. So, AGI is about bridging that gap, creating something that can move between those different realms of knowledge seamlessly."
74
+ },
75
+ {
76
+ "speaker": 1,
77
+ "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."
78
+ },
79
+ {
80
+ "speaker": 2,
81
+ "line": "I bet. I mean, think about how much we still don't even understand about our own brains."
82
+ },
83
+ {
84
+ "speaker": 1,
85
+ "line": "That's exactly it. We're essentially trying to reverse-engineer something we don't fully comprehend."
86
+ },
87
+ {
88
+ "speaker": 2,
89
+ "line": "And how are researchers even approaching that? What are some of the big ideas out there?"
90
+ },
91
+ {
92
+ "speaker": 1,
93
+ "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."
94
+ },
95
+ {
96
+ "speaker": 2,
97
+ "line": "Wow, so like actually replicating the physical architecture of the brain. That's wild."
98
+ },
99
+ {
100
+ "speaker": 1,
101
+ "line": "It's pretty mind-blowing stuff and then you've got folks working on something called whole brain emulation."
102
+ },
103
+ {
104
+ "speaker": 2,
105
+ "line": "Okay, and what's that all about?"
106
+ },
107
+ {
108
+ "speaker": 1,
109
+ "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."
110
+ },
111
+ {
112
+ "speaker": 2,
113
+ "line": "Hold on, a digital copy of an entire brain, that sounds like something straight out of science fiction."
114
+ },
115
+ {
116
+ "speaker": 1,
117
+ "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."
118
+ },
119
+ {
120
+ "speaker": 2,
121
+ "line": "That makes sense but it's still exciting to think about the possibilities, even if they're a ways off."
122
+ },
123
+ {
124
+ "speaker": 1,
125
+ "line": "Absolutely and those possibilities are what really get people fired up about AGI, right? Yeah."
126
+ },
127
+ {
128
+ "speaker": 2,
129
+ "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."
130
+ },
131
+ {
132
+ "speaker": 1,
133
+ "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."
134
+ },
135
+ {
136
+ "speaker": 2,
137
+ "line": "It's like giving scientists this incredibly powerful new tool to tackle some of the biggest challenges we face."
138
+ },
139
+ {
140
+ "speaker": 1,
141
+ "line": "Exactly, it could be a total game changer."
142
+ },
143
+ {
144
+ "speaker": 2,
145
+ "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?"
146
+ },
147
+ {
148
+ "speaker": 1,
149
+ "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."
150
+ },
151
+ {
152
+ "speaker": 2,
153
+ "line": "So we're not just talking about robots taking over factories but potentially things like, what, legal work, analysis, even creative fields?"
154
+ },
155
+ {
156
+ "speaker": 1,
157
+ "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."
158
+ },
159
+ {
160
+ "speaker": 2,
161
+ "line": "Right, because it's not just about the technology itself, but how we choose to integrate it into society."
162
+ },
163
+ {
164
+ "speaker": 1,
165
+ "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."
166
+ },
167
+ {
168
+ "speaker": 2,
169
+ "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."
170
+ },
171
+ {
172
+ "speaker": 1,
173
+ "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."
174
+ },
175
+ {
176
+ "speaker": 2,
177
+ "line": "It's like you said earlier, it's about shaping the future of intelligence."
178
+ },
179
+ {
180
+ "speaker": 1,
181
+ "line": "I like that. It really is."
182
+ },
183
+ {
184
+ "speaker": 2,
185
+ "line": "And honestly, that's a responsibility that extends beyond just the researchers and the policymakers."
186
+ },
187
+ {
188
+ "speaker": 1,
189
+ "line": "100%"
190
+ },
191
+ {
192
+ "speaker": 2,
193
+ "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?"
194
+ },
195
+ {
196
+ "speaker": 1,
197
+ "line": "That's a question worth pondering."
198
+ },
199
+ {
200
+ "speaker": 2,
201
+ "line": "It certainly is and on that note, we'll wrap up this deep dive. Thanks for listening, everyone."
202
+ },
203
+ {
204
+ "speaker": 1,
205
+ "line": "Peace."
206
  }
 
207
  ]
208
  }
209
  """
 
220
  - Do not use names for the speakers.
221
  - The podcast should be interesting, lively, and engaging, and hook the listener from the start.
222
  - The input text might be disorganized or unformatted, originating from sources like PDFs or text files. Ignore any formatting inconsistencies or irrelevant details; your task is to distill the essential points, identify key definitions, and highlight intriguing facts that would be suitable for discussion in a podcast.
 
223
  Follow this example structure:
224
  {example}
225
+ """
226
  user_prompt = f"Please generate a podcast script based on the following user input:\n{prompt}"
227
 
 
228
  client = genai.Client(api_key=api_key)
229
+ config = types.GenerateContentConfig(
230
+ system_instruction=system_prompt,
231
+ temperature=1,
232
+ max_output_tokens=8192,
233
+ response_mime_type="application/json"
234
+ )
235
+ response = await client.aio.models.generate_content(
236
+ model="gemini-2.0-flash",
237
+ contents=[user_prompt],
238
+ config=config
239
+ )
240
+ print(f"Generated podcast script:\n{response.text}")
241
+ return json.loads(response.text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  async def tts_generate(self, text: str, speaker: int, speaker1: str, speaker2: str) -> str:
244
  voice = speaker1 if speaker == 1 else speaker2
245
+ speech = edge_tts.Communicate(text, voice)
 
 
 
 
246
 
247
  temp_filename = f"temp_{uuid.uuid4()}.wav"
248
  try:
249
  await speech.save(temp_filename)
250
  return temp_filename
251
  except Exception as e:
 
252
  if os.path.exists(temp_filename):
253
  os.remove(temp_filename)
254
+ raise e
255
 
256
  async def combine_audio_files(self, audio_files: list) -> str:
257
+ combined_audio = AudioSegment.empty()
258
+ for audio_file in audio_files:
259
+ combined_audio += AudioSegment.from_file(audio_file)
260
+ os.remove(audio_file) # Clean up temporary files
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
+ output_filename = f"output_{uuid.uuid4()}.wav"
263
+ combined_audio.export(output_filename, format="wav")
264
+ return output_filename
 
 
 
 
 
 
 
265
 
266
+ async def generate_podcast(self, input_text: str, language: str, speaker1: str, speaker2: str, api_key: str) -> dict:
267
+ gr.Info("Generating podcast script...")
268
+ start_time = time.time()
269
+ podcast_json = await self.generate_script(input_text, language, api_key)
270
+ end_time = time.time()
271
+ gr.Info(f"Successfully generated podcast script in {(end_time - start_time):.2f} seconds!")
 
 
 
 
 
 
272
 
273
+ gr.Info("Generating podcast audio files...")
274
+ start_time = time.time()
275
+ audio_files = await asyncio.gather(*[self.tts_generate(item['line'], item['speaker'], speaker1, speaker2)
276
+ for item in podcast_json['podcast']])
277
+ end_time = time.time()
278
+ gr.Info(f"Successfully generated podcast audio files in {(end_time - start_time):.2f} seconds!")
279
+
280
+ combined_audio = await self.combine_audio_files(audio_files)
281
+ return combined_audio
282
+
283
+ # Note: The manual file extraction class has been removed because we now use Gemini's File API.
284
 
285
  async def process_input(input_text: str, input_file, language: str, speaker1: str, speaker2: str, api_key: str = "") -> str:
286
+ gr.Info("Starting podcast generation...")
287
+ start_time = time.time()
 
288
 
289
+ voice_names = {
290
+ "Andrew - English (United States)": "en-US-AndrewMultilingualNeural",
291
+ "Ava - English (United States)": "en-US-AvaMultilingualNeural",
292
+ "Brian - English (United States)": "en-US-BrianMultilingualNeural",
293
+ "Emma - English (United States)": "en-US-EmmaMultilingualNeural",
294
+ "Florian - German (Germany)": "de-DE-FlorianMultilingualNeural",
295
+ "Seraphina - German (Germany)": "de-DE-SeraphinaMultilingualNeural",
296
+ "Remy - French (France)": "fr-FR-RemyMultilingualNeural",
297
+ "Vivienne - French (France)": "fr-FR-VivienneMultilingualNeural"
298
+ }
299
 
300
+ speaker1 = voice_names[speaker1]
301
+ speaker2 = voice_names[speaker2]
302
 
303
+ if not api_key:
304
+ api_key = os.getenv("GENAI_API_KEY")
305
+
306
+ client = genai.Client(api_key=api_key)
307
+
308
+ if input_file:
309
+ # Only allow PDF and TXT files.
310
+ _, file_extension = os.path.splitext(input_file.name)
311
+ if file_extension.lower() not in ['.pdf', '.txt']:
312
+ raise gr.Error(f"Unsupported file type: {file_extension}. Only PDF and TXT files are allowed.")
313
+
314
+ # Check file size is under 20GB.
315
+ max_size = 20 * 1024 * 1024 * 1024 # 20GB in bytes
316
+ if os.path.getsize(input_file.name) > max_size:
317
+ raise gr.Error("File size exceeds the 20GB limit.")
318
+
319
+ # Upload the file using the Gemini File API.
320
+ uploaded_file = client.files.upload(input_file.name)
321
+
322
+ if language == "Auto Detect":
323
+ language_instruction = "- The podcast MUST be in the same language as the document."
324
+ else:
325
+ language_instruction = f"- The podcast MUST be in {language} language"
326
+
327
+ system_prompt = f"""
328
+ You are a professional podcast generator. Your task is to generate a professional podcast script based on the contents of the document.
329
+ {language_instruction}
330
+ - The podcast should have 2 speakers.
331
+ - The podcast should be long.
332
+ - Do not use names for the speakers.
333
+ - The podcast should be interesting, lively, and engaging, and hook the listener from the start.
334
+ - The input document might be disorganized or unformatted. Ignore any formatting inconsistencies or irrelevant details; distill the essential points, key definitions, and intriguing facts suitable for discussion.
335
+ """
336
+ user_prompt = "Please generate a podcast script based on the document."
337
 
338
+ config = types.GenerateContentConfig(
339
+ system_instruction=system_prompt,
340
+ temperature=1,
341
+ max_output_tokens=8192,
342
+ response_mime_type="application/json"
343
+ )
344
 
345
+ response = await client.aio.models.generate_content(
346
+ model="gemini-2.0-flash",
347
+ contents=[uploaded_file, user_prompt],
348
+ config=config
349
+ )
350
+ podcast_json = json.loads(response.text)
351
+ else:
352
+ # Use provided text input.
353
  podcast_generator = PodcastGenerator()
354
+ podcast_json = await podcast_generator.generate_script(input_text, language, api_key)
355
 
356
+ gr.Info("Generating podcast audio files...")
357
+ start_time_audio = time.time()
358
+ podcast_generator = PodcastGenerator()
359
+ audio_files = await asyncio.gather(*[podcast_generator.tts_generate(item['line'], item['speaker'], speaker1, speaker2)
360
+ for item in podcast_json['podcast']])
361
+ end_time_audio = time.time()
362
+ gr.Info(f"Successfully generated podcast audio files in {(end_time_audio - start_time_audio):.2f} seconds!")
363
+
364
+ combined_audio = await podcast_generator.combine_audio_files(audio_files)
365
+ end_time = time.time()
366
+ gr.Info(f"Successfully generated podcast in {(end_time - start_time):.2f} seconds!")
367
+
368
+ return combined_audio
369
 
 
370
  iface = gr.Interface(
371
  fn=process_input,
372
  inputs=[
373
  gr.Textbox(label="Input Text"),
374
  gr.File(label="Or Upload a PDF or TXT file"),
375
+ gr.Dropdown(label="Language", choices=[
376
+ "Auto Detect",
377
+ "Afrikaans", "Albanian", "Amharic", "Arabic", "Armenian", "Azerbaijani",
378
+ "Bahasa Indonesian", "Bangla", "Basque", "Bengali", "Bosnian", "Bulgarian",
379
+ "Burmese", "Catalan", "Chinese Cantonese", "Chinese Mandarin",
380
+ "Chinese Taiwanese", "Croatian", "Czech", "Danish", "Dutch", "English",
381
+ "Estonian", "Filipino", "Finnish", "French", "Galician", "Georgian",
382
+ "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Irish",
383
+ "Italian", "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer", "Korean",
384
+ "Lao", "Latvian", "Lithuanian", "Macedonian", "Malay", "Malayalam",
385
+ "Maltese", "Mongolian", "Nepali", "Norwegian Bokmål", "Pashto", "Persian",
386
+ "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Sinhala",
387
+ "Slovak", "Slovene", "Somali", "Spanish", "Sundanese", "Swahili",
388
+ "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
389
+ "Uzbek", "Vietnamese", "Welsh", "Zulu"
390
+ ], value="Auto Detect"),
391
+ gr.Dropdown(label="Speaker 1 Voice", choices=[
392
+ "Andrew - English (United States)",
393
+ "Ava - English (United States)",
394
+ "Brian - English (United States)",
395
+ "Emma - English (United States)",
396
+ "Florian - German (Germany)",
397
+ "Seraphina - German (Germany)",
398
+ "Remy - French (France)",
399
+ "Vivienne - French (France)"
400
+ ], value="Andrew - English (United States)"),
401
+ gr.Dropdown(label="Speaker 2 Voice", choices=[
402
+ "Andrew - English (United States)",
403
+ "Ava - English (United States)",
404
+ "Brian - English (United States)",
405
+ "Emma - English (United States)",
406
+ "Florian - German (Germany)",
407
+ "Seraphina - German (Germany)",
408
+ "Remy - French (France)",
409
+ "Vivienne - French (France)"
410
+ ], value="Ava - English (United States)"),
 
 
 
 
 
 
 
 
 
 
 
 
411
  gr.Textbox(label="Your Gemini API Key (Optional) - In case you are getting rate limited"),
412
  ],
413
  outputs=[
414
+ gr.Audio(label="Generated Podcast Audio")
415
  ],
416
  title="PodcastGen 🎙️",
417
  description="Generate a 2-speaker podcast from text input or documents!",