Spaces:
Sleeping
Sleeping
File size: 4,290 Bytes
7dd982b 106c061 7dd982b 106c061 7dd982b cd6257a 6ac3507 7dd982b 106c061 7dd982b f06c20a 7dd982b 106c061 7dd982b f06c20a 106c061 2b8e4f0 eec85c6 f06c20a 6e4263d cd6257a 6e4263d cd6257a 6e4263d cd6257a 76e3793 6ac3507 eec85c6 106c061 eec85c6 2b8e4f0 106c061 2b8e4f0 cd6257a 2b8e4f0 106c061 cd6257a 76e3793 106c061 b82995c 7dd982b eec85c6 106c061 6e4263d 7dd982b 106c061 6e4263d |
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
import gradio as gr
import tempfile, requests, os
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
from gtts import gTTS
from bs4 import BeautifulSoup
from PIL import Image, ImageDraw, ImageFont
import ffmpeg
import textwrap
# OpenAI LLM
llm = ChatOpenAI(model="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 article content
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
# Convert uploaded PNG logo to local use (as SVG substitute if needed)
def get_uploaded_logo():
from_path = "/mnt/data/CSHARP logo.png"
logo_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
with open(from_path, 'rb') as src, open(logo_path, 'wb') as dst:
dst.write(src.read())
return logo_path
# Create image slides from text chunks
def create_slides(text, duration, output_folder, max_lines=6):
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
font = ImageFont.truetype(font_path, 48)
logo_path = get_uploaded_logo()
chunks = textwrap.wrap(text, width=40)
slides = ["\n".join(chunks[i:i+max_lines]) for i in range(0, len(chunks), max_lines)]
per_slide_time = duration / len(slides)
slide_paths = []
for i, slide_text in enumerate(slides):
img = Image.new("RGB", (1280, 720), color=(20, 30, 60))
draw = ImageDraw.Draw(img)
lines = slide_text.split("\n")
total_height = sum([font.getsize(line)[1] for line in lines]) + (len(lines)-1)*10
y = (720 - total_height) // 2
for line in lines:
w, h = draw.textsize(line, font=font)
draw.text(((1280 - w) // 2, y), line, font=font, fill="white")
y += h + 10
logo = Image.open(logo_path).convert("RGBA").resize((120, 120))
img.paste(logo, (40, 40), logo)
frame_path = os.path.join(output_folder, f"slide_{i}.png")
img.save(frame_path)
slide_paths.append((frame_path, per_slide_time))
return slide_paths
# Generate AV summary
def url_to_av_summary(url, duration):
content = extract_main_content(url)
if not content:
return "Failed to extract article content.", None
summary = summary_chain.run(text=content[:3000]).replace('"','')[:300]
audio_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
gTTS(text=summary).save(audio_path)
frame_dir = tempfile.mkdtemp()
slides = create_slides(summary, duration, frame_dir)
concat_file = os.path.join(frame_dir, "frames.txt")
with open(concat_file, "w") as f:
for path, t in slides:
f.write(f"file '{path}'\n")
f.write(f"duration {t}\n")
concat_img = os.path.join(frame_dir, "video_input.mp4")
ffmpeg.input(concat_file, format='concat', safe=0).output(
concat_img,
r=1,
vcodec='libx264', pix_fmt='yuv420p'
).run(overwrite_output=True, quiet=True)
video_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name
ffmpeg.input(concat_img).output(
video_path,
i=audio_path,
vcodec='libx264', acodec='aac', pix_fmt='yuv420p', shortest=None
).run(overwrite_output=True, quiet=True)
return summary, video_path
iface = gr.Interface(
fn=url_to_av_summary,
inputs=[
gr.Textbox(label="Article URL"),
gr.Radio([5, 10], label="Video Duration (sec)", value=5)
],
outputs=[
gr.Textbox(label="Summary"),
gr.Video(label="Generated AV Summary")
],
title="๐๏ธ AV Summary Generator (Multislide with Uploaded Logo)",
description="Generates a 5/10 sec video summary from article URL with large text, uploaded logo, and slide animation."
)
if __name__ == '__main__':
iface.launch()
|