| | 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() |