ainews / app.py
humanizetech's picture
readme updated
04592f1
"""
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)])