Spaces:
Sleeping
Sleeping
import gradio as gr | |
import tempfile, requests | |
from langchain.chains import LLMChain | |
from langchain.prompts import PromptTemplate | |
from langchain_openai import ChatOpenAI | |
from gtts import gTTS | |
from bs4 import BeautifulSoup | |
from PIL import Image, ImageDraw | |
import ffmpeg | |
import textwrap | |
# Initialize OpenAI LLM | |
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.3) | |
summary_prompt = PromptTemplate.from_template(""" | |
Provide a crisp, promotional-style summary (under 50 words) of the following: | |
{text} | |
Summary: | |
""") | |
summary_chain = LLMChain(llm=llm, prompt=summary_prompt) | |
# Extract top article paragraphs | |
def extract_main_content(url): | |
resp = requests.get(url, timeout=10) | |
soup = BeautifulSoup(resp.content, "html.parser") | |
for tag in soup(["nav","header","footer","aside","script","style","noscript"]): tag.decompose() | |
paras = [p.get_text() for p in soup.find_all("p") if len(p.get_text()) > 60] | |
return "\n".join(paras[:20]) or None | |
# Gradient background | |
def create_background(path, size=(1280,720)): | |
img = Image.new("RGB", size) | |
draw = ImageDraw.Draw(img) | |
for y in range(size[1]): | |
draw.line([(0,y),(size[0],y)], fill=(10+y//10,20+y//12,50+y//15)) | |
img.save(path) | |
# Generate AV summary | |
def url_to_av_summary(url, duration): | |
content = extract_main_content(url) | |
if not content: | |
return "Failed to extract content.", None | |
summary = summary_chain.run(text=content[:3000]).replace('"','')[:300] | |
# Wrap long summary to multiline | |
wrapped_summary = textwrap.fill(summary, width=50).replace("\n", "\\n") | |
# TTS | |
audio = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name | |
gTTS(text=summary).save(audio) | |
# Background image | |
bg = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name | |
create_background(bg) | |
# Build video stream | |
video_stream = ffmpeg.input(bg, loop=1, framerate=1) | |
text_opts = dict( | |
fontfile="/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", | |
text=wrapped_summary, | |
fontcolor="white", | |
fontsize=48, | |
box=1, | |
boxcolor="[email protected]", | |
boxborderw=5, | |
x="(w-text_w)/2", | |
y=f"h-(t*(h+text_h)/{duration})" | |
) | |
video = video_stream.drawtext(**text_opts).setpts('PTS') | |
audio_stream = ffmpeg.input(audio) | |
out_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name | |
out = ffmpeg.output(video, audio_stream, out_path, | |
vcodec="libx264", acodec="aac", pix_fmt="yuv420p", t=duration) | |
out.run(quiet=True) | |
return summary, out_path | |
iface = gr.Interface( | |
fn=url_to_av_summary, | |
inputs=[gr.Textbox(label="Article URL"), gr.Radio([5,10], label="Duration (sec)", value=5)], | |
outputs=[gr.Textbox(label="Summary"), gr.Video(label="AV Summary")], | |
title="AV Summary Generator", | |
description="Generate a promo-style AV summary (5 or 10s) using OpenAI + gTTS + ffmpeg-python." | |
) | |
if __name__=='__main__': | |
iface.launch() | |