File size: 2,988 Bytes
7dd982b
6ac3507
7dd982b
 
6ac3507
7dd982b
 
f06c20a
6ac3507
 
7dd982b
6ac3507
 
7dd982b
f06c20a
7dd982b
 
 
 
 
 
 
6ac3507
7dd982b
f06c20a
 
6ac3507
2b8e4f0
eec85c6
f06c20a
eec85c6
6ac3507
f06c20a
76e3793
eec85c6
6ac3507
 
76e3793
6ac3507
eec85c6
 
 
6ac3507
eec85c6
2b8e4f0
6ac3507
 
2b8e4f0
6ac3507
 
 
2b8e4f0
6ac3507
 
 
76e3793
6ac3507
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b82995c
7dd982b
eec85c6
6ac3507
 
 
 
7dd982b
 
6ac3507
eec85c6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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()