| 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_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', '')) |
| |
| 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() |
| |
| 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: |
| |
| webpage_content = scrape_url(content) |
| if not webpage_content: |
| return "Failed to extract content from URL", None |
|
|
| |
| script = await self.router_client.generate_script(webpage_content, prompt, model_id) |
| |
| |
| 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, |
| 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) |
| |
| |
| loop = asyncio.get_event_loop() |
| loop.run_until_complete(app.initialize()) |
| |
| |
| interface = app.create_ui() |
| interface.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=True |
| ) |
|
|
| if __name__ == "__main__": |
| main() |