Spaces:
Sleeping
Sleeping
| """ | |
| AI News Presenter - Minimal Gradio app that speaks one latest AI news item. | |
| Uses Perplexity API for news and Edge TTS (or gTTS fallback) for speech. | |
| """ | |
| import tempfile | |
| from pathlib import Path | |
| from dotenv import load_dotenv | |
| import gradio as gr | |
| import re | |
| import traceback | |
| # Load environment variables | |
| load_dotenv() | |
| # Get the directory where this script is located | |
| SCRIPT_DIR = Path(__file__).parent | |
| CSS_PATH = SCRIPT_DIR / "styles.css" | |
| def fetch_ai_news(): | |
| """Fetch latest AI news using Perplexity API.""" | |
| try: | |
| from perplexity import Perplexity | |
| client = Perplexity() | |
| completion = client.chat.completions.create( | |
| model="sonar", | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": ( | |
| "You are a news presenter. " | |
| "Return ONLY one brief, engaging sentence describing the single most important AI news headline from today. " | |
| "Maximum 50 words. " | |
| "Do NOT include any citations, reference numbers like [1], footnotes, URLs, or source lists. " | |
| "Output plain text only." | |
| ), | |
| }, | |
| { | |
| "role": "user", | |
| "content": ( | |
| "What is the most important AI news today? " | |
| "Answer with exactly one short sentence suitable for voice narration." | |
| ), | |
| }, | |
| ], | |
| ) | |
| news_content = completion.choices[0].message.content | |
| # Remove Perplexity-style numeric citations or trailing source markers | |
| news_content = re.sub(r"\[\d+\]", "", news_content) | |
| news_content = re.sub(r"\s{2,}", " ", news_content).strip() | |
| return news_content | |
| except Exception as e: | |
| traceback.print_exc() | |
| return ( | |
| f"Error fetching news: {str(e)}. " | |
| f"Please check your PERPLEXITY_API_KEY in the .env file." | |
| ) | |
| def generate_speech(text: str): | |
| """Generate speech from text. Uses temp files (auto-cleanup by OS).""" | |
| # Use temp file (auto-deleted by OS, works with Gradio) | |
| temp_audio = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) | |
| temp_path = temp_audio.name | |
| temp_audio.close() | |
| # OPTION 1: Edge TTS (Microsoft) - FASTEST and FREE | |
| try: | |
| import edge_tts | |
| import asyncio | |
| async def generate_edge_tts(): | |
| communicate = edge_tts.Communicate(text, "en-US-AriaNeural") | |
| await communicate.save(temp_path) | |
| asyncio.run(generate_edge_tts()) | |
| return temp_path | |
| except Exception as e: | |
| traceback.print_exc() | |
| # OPTION 2: gTTS (Google) - Fallback | |
| try: | |
| from gtts import gTTS | |
| tts = gTTS(text=text, lang="en", slow=False) | |
| tts.save(temp_path) | |
| return temp_path | |
| except Exception as e: | |
| traceback.print_exc() | |
| print(f"[ERROR] gTTS also failed: {e}") | |
| return None | |
| def get_news_and_speak(): | |
| """Main function to fetch news and generate speech.""" | |
| # Fetch news | |
| news_text = fetch_ai_news() | |
| audio_path = generate_speech(news_text) | |
| return news_text, audio_path | |
| def create_interface( | |
| head=""" | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <meta name="mobile-web-app-capable" content="yes"> | |
| <link rel="apple-touch-icon" href="https://humanizetech.ai/favicon.ico"> | |
| """, | |
| ): | |
| """Create the Gradio interface.""" | |
| with gr.Blocks() as demo: | |
| # 1) Listen to News (audio) at the top | |
| audio_output = gr.Audio( | |
| label="🔊 AI News - Presented by humanize", | |
| type="filepath", | |
| autoplay=True, # mobile may still require a tap on the player | |
| elem_id="listen-audio", | |
| ) | |
| # 2) News Transcript (below, with light background via CSS) | |
| news_text = gr.Textbox( | |
| label="📰 News Transcript", | |
| value="humanize is fetching today’s AI headline…", | |
| lines=6, | |
| max_lines=10, | |
| elem_classes=["news-transcript"], | |
| interactive=False, | |
| ) | |
| # 3) Custom Footer | |
| gr.HTML( | |
| """ | |
| <div style="text-align: center; padding: 20px; margin-top: 20px;"> | |
| <p style="color: rgba(255, 255, 255, 0.8); font-size: 22px; margin: 0;"> | |
| Shared with ❤️ by | |
| <a href="https://humanizetech.ai/" target="_blank" | |
| style="color: #ffffff; text-decoration: none; font-weight: 600; | |
| transition: opacity 0.2s;" | |
| onmouseover="this.style.opacity='0.7'" | |
| onmouseout="this.style.opacity='1'"> | |
| humanize | |
| </a> | |
| </p> | |
| </div> | |
| """ | |
| ) | |
| # Auto-run on page load (no button) | |
| demo.load( | |
| fn=get_news_and_speak, | |
| inputs=None, | |
| outputs=[news_text, audio_output], | |
| ) | |
| print("[INFO] UI components created successfully!") | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| demo.launch(css_paths=[str(CSS_PATH)]) | |