import asyncio import os import gradio as gr from api_clients import OpenRouterClient, ElevenLabsClient from logger import setup_logger from config import Config from scraper import scrape_url logger = setup_logger("app") # Default choices for dropdowns default_voices = [("", "Enter API key to load voices")] default_models = [("", "Enter API key to load models")] class PodcasterUI: def __init__(self, config: Config): self.config = config self.router_client = OpenRouterClient(os.getenv('OPENROUTER_API_KEY', '')) self.elevenlabs_client = ElevenLabsClient(os.getenv('ELEVENLABS_API_KEY', '')) # Store models and voices as instance variables self.models = default_models self.voices = default_voices async def initialize(self): """Initialize API clients and fetch models/voices""" try: self.models = await self.router_client.get_models() # Since get_voices() might not be async, remove await self.voices = self.elevenlabs_client.get_voices() logger.info(f"Initialized with {len(self.voices)} voices and {len(self.models)} models") except Exception as e: logger.error("Failed to initialize API clients", exc_info=True) raise async def on_submit(self, content: str, model_id: str, voice_id: str, prompt: str = "") -> tuple: """Handle form submission with async API calls""" try: # First scrape the webpage content webpage_content = scrape_url(content) if not webpage_content: return "Failed to extract content from URL", None # Generate script using the scraped content script = await self.router_client.generate_script(webpage_content, prompt, model_id) # Generate audio from the script (now synchronous) audio = self.elevenlabs_client.generate_audio(script, voice_id) return script, audio except Exception as e: logger.error("Failed to generate podcast", exc_info=True) return str(e), None def create_ui(self) -> gr.Interface: with gr.Blocks(title='URL to Podcast Generator', theme='huggingface') as interface: gr.Markdown('# URL to Podcast Generator') gr.Markdown('Enter a URL to generate a podcast episode based on its content.') with gr.Row(): with gr.Column(scale=2): url_input = gr.Textbox( label="Website URL", placeholder="Enter the URL of the website you want to convert to a podcast" ) with gr.Row(): with gr.Column(): openrouter_model = gr.Dropdown( label='AI Model', choices=self.models, # Each choice now has same id/display value value=self.models[0][0] if len(self.models) > 1 else None, ) with gr.Column(): voice_model = gr.Dropdown( label='Voice', choices=[(id, name) for id, name in self.voices], value=self.voices[0][0] if len(self.voices) > 1 else None, ) prompt_input = gr.Textbox( label="Custom Prompt", placeholder="Enter a custom prompt to guide the podcast generation (optional)", lines=3 ) submit_btn = gr.Button('Generate Podcast', variant='primary') with gr.Column(scale=1): script_output = gr.Textbox(label="Generated Script", interactive=False) audio_output = gr.Audio(label="Generated Podcast") status = gr.Textbox(label='Status', interactive=False) submit_btn.click( fn=self.on_submit, inputs=[url_input, openrouter_model, voice_model, prompt_input], outputs=[script_output, audio_output] ) return interface def main(): config = Config() app = PodcasterUI(config) # Initialize before creating UI loop = asyncio.get_event_loop() loop.run_until_complete(app.initialize()) # Create UI with populated data interface = app.create_ui() interface.launch( server_name="0.0.0.0", server_port=7860, share=True ) if __name__ == "__main__": main()