Spaces:
Sleeping
Sleeping
import streamlit as st | |
from pydub import AudioSegment | |
import google.generativeai as genai | |
from google.generativeai.types import HarmCategory, HarmBlockThreshold | |
import json | |
import uuid | |
import io | |
import edge_tts | |
import asyncio | |
import aiofiles | |
import pypdf | |
import os | |
import time | |
from typing import List, Dict | |
class PodcastGenerator: | |
def __init__(self): | |
print("PodcastGenerator initialized.") | |
async def generate_script(self, prompt: str, language: str, api_key: str) -> Dict: | |
print(f"Generating podcast script with prompt: {prompt[:50]}... and language: {language}") | |
# Example structure for the podcast script | |
example = """ | |
{ | |
"topic": "Zoho Schools", | |
"podcast": [ | |
{ | |
"speaker": 1, | |
"line": "Okay, so like imagine this. What if there was a place where you didn't have to pay for college?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Hmm. Intriguing." | |
}, | |
{ | |
"speaker": 1, | |
"line": "And on top of that, they, like, actually paid you to go there. Paid you to go to college." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Okay, now you've got my attention, right?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "It sounds kind of crazy, but it gets even better. They guarantee you a job at a global tech company after you graduate." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Wow. Hold on. Tuition free, paid stipend, guaranteed job. There's gotta be a catch." | |
}, | |
{ | |
"speaker": 1, | |
"line": "I know, that's exactly what we were thinking. It's like someone took every complaint about student debt and useless college courses and just, like, flipped the script." | |
}, | |
{ | |
"speaker": 2, | |
"line": "So where are we diving in today?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "We're doing a deep dive into Zoho Schools." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Zoho Schools?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Yeah, these schools created by Zoho, you know, the tech company. And they’re doing things completely differently." | |
}, | |
{ | |
"speaker": 2, | |
"line": "I'm familiar with Zoho, but this is the first I'm hearing about their schools." | |
}, | |
{ | |
"speaker": 1, | |
"line": "It's certainly a fascinating case study, right? It really challenges assumptions about the traditional education system, especially for, well, anyone really, but particularly for people looking to break into the tech world." | |
}, | |
{ | |
"speaker": 2, | |
"line": "So they've got their whole website, right. Like, it's wild. You go on there, and it's like, boom, alternate education universe." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Okay, I'm pulling it up now. What am I looking for first thing?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Right. They don’t just have one Zoho School. They’ve got five different things within Zoho Schools." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Five different schools?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Yeah. Technology, design, business, advanced studies, even graduate studies." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Wow. So they’ve really got the full spectrum of education covered then." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Yeah, that’s really interesting. It seems much more thought out than just a quick coding bootcamp or something." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Right. And get this, it’s in India, too. But they take applications from anywhere in India. So they’re really looking to cultivate talent nationally." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Yeah, like their own little tech talent incubator." | |
}, | |
{ | |
"speaker": 1, | |
"line": "That's a great way to put it. So they’re not just looking locally for people who already have some skills. They’re trying to find people with potential all over." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Exactly. And the thing is, they’ve been doing this for a while." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Oh yeah, 19 years, over 1600 graduates." | |
}, | |
{ | |
"speaker": 2, | |
"line": "That’s not like a new experiment." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Yeah, that’s a pretty solid track record, right? Especially if you consider they make up 15% of Zoho’s workforce." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Now that’s a serious commitment. It’s not just like, “Oh, look at us. We’re being charitable.”" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Right. Exactly. They’re putting their money where their mouth is." | |
}, | |
{ | |
"speaker": 2, | |
"line": "It makes you wonder though, like, how do they actually measure if this is working long-term? You know, do these graduates stay with the company for years and years? Or if they leave, are they competitive in the job market?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Those are good questions. That would tell us a lot more, I think, than just the number of graduates." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Totally. Because they’re so upfront about it. Even on their website, they have this whole thing with Zoho Schools. And they start off with, “How useful was college?”" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Ooh, that’s a loaded question, isn’t it?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "I know, right? It’s like they’re already challenging the whole system." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Yeah, because who hasn’t asked themselves that at some point, right?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "No, seriously. Especially when you’re, you know, maybe struggling to pay off loans or whatever." | |
}, | |
{ | |
"speaker": 1, | |
"line": "So they actually asked their employees, “How useful was college?”" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Oh, really?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "And most of them were like, “Nah, I learned what I actually use on the job.”" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Interesting. See that? To me, that really highlights the disconnect between what traditional education is giving us and what employers actually need." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Yeah, and it’s not just in tech. I mean, we see this everywhere. The skills gap is real." | |
}, | |
{ | |
"speaker": 2, | |
"line": "It’s like instead of all that theory, they’re like, “Nope, hands-on, let’s go.”" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Practical experience from day one. Makes sense." | |
}, | |
{ | |
"speaker": 2, | |
"line": "And the curriculum is always changing, keeping up with all the latest stuff." | |
}, | |
{ | |
"speaker": 1, | |
"line": "And the teachers aren’t like professors who’ve never left a classroom. They’re pulling in actual professionals." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Yeah, people from the industry. So they know what actually works, what you actually need." | |
}, | |
{ | |
"speaker": 1, | |
"line": "That’s a huge difference. I mean, I remember some of my professors—bless their hearts—but they hadn’t worked in the field for like decades." | |
}, | |
{ | |
"speaker": 2, | |
"line": "It’s like an apprenticeship but supercharged." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Definitely. And honestly, that’s what employers are looking for now, right? Someone who can just jump in and get going." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Totally. And it’s not even just the technical stuff, right? They’re teaching you how to think, how to solve problems." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Right, how to communicate." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Yeah, because that’s huge. Stuff you need anywhere, not just in a tech job." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Absolutely. Being able to communicate your ideas clearly, work on a team, that’s valuable no matter what you do." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Okay, so they had this whole section—\"What We Do Differently.”" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Mhm." | |
}, | |
{ | |
"speaker": 2, | |
"line": "And it gets into the details. Like, they talk a lot about communication skills." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Oh, interesting. What do they say?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Just that they want students to be able to clearly communicate ideas, which, I mean, duh. But it’s true. A lot of programs just skip over that part." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Exactly. You could be the best coder, but if you can’t explain what you’re doing to the team, you’re stuck." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Right. And they do have those testimonials, you know, from alumni." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Right." | |
}, | |
{ | |
"speaker": 2, | |
"line": "And those are great, but we have to acknowledge it’s always going to be the success stories." | |
}, | |
{ | |
"speaker": 1, | |
"line": "Exactly. It’s important to find other perspectives too—maybe people who went through the program and didn’t love it, or haven’t had the same positive experience." | |
}, | |
{ | |
"speaker": 2, | |
"line": "So for everyone listening, really think about this—would you consider this at 18?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Yeah, because this isn’t just about one school in India, right? This gets at something bigger." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Like what if this is the future of education?" | |
}, | |
{ | |
"speaker": 1, | |
"line": "Seriously. What if this is how we start to fix all those problems—student debt, skills not matching up with jobs?" | |
}, | |
{ | |
"speaker": 2, | |
"line": "Exactly. Imagine you graduate, you’re ready to work, and you don’t have those loans hanging over you. That’s life-changing." | |
}, | |
{ | |
"speaker": 1, | |
"line": "It’s like they found a way to make everyone happy—the students, the company—well, at least on the surface." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Right. It’s a really interesting model, and it’s definitely worth keeping an eye on." | |
}, | |
{ | |
"speaker": 1, | |
"line": "But—and this is important—we can’t just ignore those potential downsides." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Absolutely not. There are always trade-offs, and it’s about weighing those and figuring out what matters most to you." | |
}, | |
{ | |
"speaker": 1, | |
"line": "This whole thing with Zoho Schools, I think, is a good reminder that we can always rethink things." | |
}, | |
{ | |
"speaker": 2, | |
"line": "Totally. The system isn’t set in stone. We can challenge it, we can experiment, and we can find better ways to do things." | |
}, | |
{ | |
"speaker": 1, | |
"line": "So that wraps up our deep dive into Zoho Schools. It was fascinating to learn more about this." | |
}, | |
{ | |
"speaker": 2, | |
"line": "And for everyone listening, thanks for joining us. Don’t forget to check out our show notes for links and more info, and we’ll catch you next time." | |
} | |
] | |
} | |
""" | |
if language == "Auto Detect": | |
language_instruction = "- The podcast MUST be in the same language as the user input." | |
else: | |
language_instruction = f"- The podcast MUST be in {language} language" | |
system_prompt = f""" | |
You are a highly engaging highly convesational with real emotionas between speakers podcast generator. Your task is to generate a engaging podcast script based on the user input. | |
{language_instruction} | |
- The podcast should have 2 speakers. | |
- The podcast should be long and engaging. | |
- Follow this example structure: | |
- It shouild be in emotional tone | |
- IT shouild be as a realistic as engaging as possible | |
- It should be as engaging as possible | |
- It shouild not bore the listener | |
- Add some humor and alos sarcasm to it to make it more engaging and interesting | |
- Add some real life examples to make it more engaging | |
- Never ever be boring | |
- never ever be robotic | |
- add emotions to it | |
- make it as engaging as emotional cono bewtwen the speakers | |
- make sure dont mke it look like a promotional podcast | |
{example} | |
# """ | |
user_prompt = f"Please generate a realistic conversational enaganing emotional podcast script based on the following user input:\n{prompt}" | |
messages = [{"role": "user", "parts": [user_prompt]}] | |
genai.configure(api_key=api_key) # Use the provided API key | |
generation_config = { | |
"temperature": 1, | |
"max_output_tokens": 8192, | |
"response_mime_type": "application/json", | |
} | |
model = genai.GenerativeModel( | |
model_name="gemini-1.5-flash-002", | |
generation_config=generation_config, | |
safety_settings={ | |
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE, | |
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE, | |
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE, | |
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE | |
}, | |
system_instruction=system_prompt | |
) | |
try: | |
print("Sending request to generate podcast script...") | |
response = await model.generate_content_async(messages) | |
print("Podcast script generated successfully.") | |
except Exception as e: | |
print(f"Error occurred while generating script: {e}") | |
raise Exception(f"Failed to generate podcast script: {e}") | |
open("podcast_script.json", "w").write(response.text) | |
return json.loads(response.text) | |
async def tts_generate(self, text: str, speaker: int, speaker1: str, speaker2: str) -> str: | |
print(f"Generating TTS for speaker {speaker}: {text[:50]}...") | |
voice = speaker1 if speaker == 1 else speaker2 | |
speech = edge_tts.Communicate(text, voice) | |
temp_filename = f"temp_{uuid.uuid4()}.wav" | |
try: | |
await speech.save(temp_filename) | |
print(f"TTS generated and saved as {temp_filename}") | |
return temp_filename | |
except Exception as e: | |
print(f"Error during TTS generation: {e}") | |
if os.path.exists(temp_filename): | |
os.remove(temp_filename) | |
raise e | |
async def combine_audio_files(self, audio_files: List[str]) -> str: | |
print("Combining audio files...") | |
combined_audio = AudioSegment.empty() | |
for audio_file in audio_files: | |
print(f"Adding {audio_file} to combined audio.") | |
combined_audio += AudioSegment.from_file(audio_file) | |
os.remove(audio_file) # Clean up temporary files | |
output_filename = f"output_{uuid.uuid4()}.wav" | |
combined_audio.export(output_filename, format="wav") | |
print(f"Combined audio saved as {output_filename}") | |
return output_filename | |
async def generate_podcast(self, input_text: str, language: str, speaker1: str, speaker2: str, api_key: str) -> str: | |
print("Generating podcast...") | |
start_time = time.time() | |
podcast_json = await self.generate_script(input_text, language, api_key) | |
print(f"Podcast script generated in {time.time() - start_time:.2f} seconds.") | |
print("Generating podcast audio files...") | |
start_time = time.time() | |
audio_files = await asyncio.gather( | |
*[self.tts_generate(item['line'], item['speaker'], speaker1, speaker2) for item in podcast_json['podcast']] | |
) | |
print(f"Podcast audio files generated in {time.time() - start_time:.2f} seconds.") | |
combined_audio = await self.combine_audio_files(audio_files) | |
return combined_audio | |
class TextExtractor: | |
async def extract_from_pdf(file_path: str) -> str: | |
print(f"Extracting text from PDF: {file_path}") | |
async with aiofiles.open(file_path, 'rb') as file: | |
content = await file.read() | |
pdf_reader = pypdf.PdfReader(io.BytesIO(content)) | |
extracted_text = "\n\n".join(page.extract_text() for page in pdf_reader.pages if page.extract_text()) | |
print("Text extracted from PDF successfully.") | |
return extracted_text | |
async def extract_from_txt(file_path: str) -> str: | |
print(f"Extracting text from TXT file: {file_path}") | |
async with aiofiles.open(file_path, 'r') as file: | |
extracted_text = await file.read() | |
print("Text extracted from TXT successfully.") | |
return extracted_text | |
async def extract_text(cls, file_path: str) -> str: | |
_, file_extension = os.path.splitext(file_path) | |
if file_extension.lower() == '.pdf': | |
return await cls.extract_from_pdf(file_path) | |
elif file_extension.lower() == '.txt': | |
return await cls.extract_from_txt(file_path) | |
else: | |
raise Exception(f"Unsupported file type: {file_extension}") | |
async def process_input(input_text: str, input_file, language: str, speaker1: str, speaker2: str, api_key: str = "") -> str: | |
print("Starting podcast generation process...") | |
start_time = time.time() | |
voice_names = { | |
"Andrew - English (United States)": "en-US-AndrewMultilingualNeural", | |
"Ava - English (United States)": "en-US-AvaMultilingualNeural", | |
"Brian - English (United States)": "en-US-BrianMultilingualNeural", | |
"Emma - English (United States)": "en-US-EmmaMultilingualNeural", | |
"Florian - German (Germany)": "de-DE-FlorianMultilingualNeural", | |
"Seraphina - German (Germany)": "de-DE-SeraphinaMultilingualNeural", | |
"Remy - French (France)": "fr-FR-RemyMultilingualNeural", | |
"Vivienne - French (France)": "fr-FR-VivienneMultilingualNeural" | |
} | |
speaker1 = voice_names[speaker1] | |
speaker2 = voice_names[speaker2] | |
if input_file: | |
print("Extracting text from uploaded file...") | |
input_text = await TextExtractor.extract_text(input_file.name) | |
if not api_key: | |
api_key = os.getenv("GENAI_API_KEY") | |
podcast_generator = PodcastGenerator() | |
podcast = await podcast_generator.generate_podcast(input_text, language, speaker1, speaker2, api_key) | |
print(f"Podcast generated in {time.time() - start_time:.2f} seconds.") | |
return podcast | |
# Define Streamlit interface | |
def main(): | |
st.title("Podcast Generator") | |
input_text = st.text_area("Enter Input Text", "") | |
input_file = st.file_uploader("Or Upload a PDF or TXT file", type=["pdf", "txt"]) | |
language = st.selectbox("Select Language", ["Auto Detect", "English", "German", "French"]) | |
speaker1 = st.selectbox("Select Speaker 1 Voice", ["Andrew - English (United States)", "Ava - English (United States)"]) | |
speaker2 = st.selectbox("Select Speaker 2 Voice", ["Brian - English (United States)", "Emma - English (United States)"]) | |
api_key = st.text_input("Enter Google API Key", "") | |
if st.button("Generate Podcast"): | |
if not input_text and not input_file: | |
st.error("Please enter text or upload a file.") | |
else: | |
st.write("Processing...") | |
podcast = asyncio.run(process_input(input_text, input_file, language, speaker1, speaker2, api_key)) | |
st.audio(podcast, format="audio/wav") | |
if __name__ == "__main__": | |
main() | |