Spaces:
Sleeping
Sleeping
File size: 5,821 Bytes
7dd982b d3a1880 7dd982b 106c061 7dd982b 911c558 6ac3507 da2d8c5 4a1e71c 7dd982b de17270 106c061 7dd982b cd2f783 7dd982b f06c20a fa05c61 2b8e4f0 eec85c6 f06c20a de17270 4a1e71c fa05c61 cd2f783 4a1e71c cd6257a 4a1e71c 473c60d 4a1e71c de17270 473c60d cd6257a 2d955c8 cd2f783 2d955c8 4a1e71c 911c558 fa05c61 4a1e71c 911c558 cd6257a 2d955c8 b7fb1f9 473c60d eefc307 2d955c8 eefc307 473c60d fa05c61 34d472d fa05c61 cd6257a 4a1e71c cd6257a 76e3793 eec85c6 106c061 8692d18 2b8e4f0 106c061 2b8e4f0 cd6257a d3a1880 cd6257a d3a1880 729f48e d3a1880 729f48e b82995c 7dd982b eec85c6 106c061 2d955c8 cd2f783 7dd982b 106c061 de17270 |
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
import gradio as gr
import tempfile, requests, os, subprocess
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, ImageEnhance
import ffmpeg
import textwrap
import random
from urllib.request import urlretrieve
UNSPLASH_KEY = "-7tFgMCy_pwrouZrC8mmEIBpyskEyP25e3_Y4vWSvBs"
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
summary_prompt = PromptTemplate.from_template("""
Provide a crisp, promotional-style summary (under 100 words) of the following:
{text}
Summary:
""")
summary_chain = LLMChain(llm=llm, prompt=summary_prompt)
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
def fetch_unsplash_image(query):
url = f"https://api.unsplash.com/photos/random?query={query}&orientation=landscape&client_id={UNSPLASH_KEY}"
try:
resp = requests.get(url).json()
return resp['urls']['regular']
except:
return "https://img.freepik.com/free-photo/blue-abstract-gradient-wave-wallpaper_53876-102605.jpg"
ASSETS = {
"logo": "https://huggingface.co/spaces/csccorner/Link-to-video/resolve/main/csharplogo.png",
"graphics": [
"https://img.freepik.com/free-vector/startup-launch-concept-with-rocket_23-2147866180.jpg",
"https://img.freepik.com/free-vector/artificial-intelligence-concept-illustration_114360-7307.jpg",
"https://img.freepik.com/free-vector/business-goal-achievement-banner_33099-1687.jpg"
]
}
def download_asset(url):
local_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
urlretrieve(url, local_path)
return local_path
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 = download_asset(ASSETS["logo"])
chunks = textwrap.wrap(text, width=36)
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):
query = random.choice(slide_text.split())
bg_path = download_asset(fetch_unsplash_image(query))
graphic_path = download_asset(random.choice(ASSETS["graphics"]))
bg = Image.open(bg_path).resize((1280, 720)).convert("RGBA")
enhancer = ImageEnhance.Brightness(bg)
bg = enhancer.enhance(0.3)
draw = ImageDraw.Draw(bg)
lines = slide_text.split("\n")
lines = [line.encode('utf-8', 'replace').decode('utf-8') for line in lines]
total_height = sum([font.getbbox(line)[3] - font.getbbox(line)[1] for line in lines]) + (len(lines)-1)*20
y = max((720 - total_height) // 2, 20)
for line in lines:
w = font.getbbox(line)[2] - font.getbbox(line)[0]
draw.text(((1280 - w) // 2, y), line, font=font, fill="white")
y += font.getbbox(line)[3] - font.getbbox(line)[1] + 20
logo = Image.open(logo_path).convert("RGBA")
logo = logo.resize((160, int(160 * logo.size[1] / logo.size[0])))
bg.paste(logo, (30, 630 - logo.size[1]), logo)
graphic = Image.open(graphic_path).convert("RGBA")
graphic = graphic.resize((200, 200))
bg.paste(graphic, (1040, 40), graphic)
frame_path = os.path.join(output_folder, f"slide_{i}.png")
bg.convert("RGB").save(frame_path)
slide_paths.append((frame_path, per_slide_time))
return slide_paths
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.invoke({"text": content[:3000]})["text"].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_txt_path = os.path.join(frame_dir, "slides.txt")
with open(concat_txt_path, "w") as f:
for path, t in slides:
f.write(f"file '{path}'\n")
f.write(f"duration {t}\n")
f.write(f"file '{slides[-1][0]}'\n")
concat_img = os.path.join(frame_dir, "video_input.mp4")
subprocess.run([
"ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", concat_txt_path,
"-vsync", "vfr", "-pix_fmt", "yuv420p", concat_img
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
final_video = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name
video_input = ffmpeg.input(concat_img)
audio_input = ffmpeg.input(audio_path)
ffmpeg.output(video_input, audio_input, final_video,
vcodec='libx264', acodec='aac', pix_fmt='yuv420p', shortest=None
).run(overwrite_output=True, quiet=True)
return summary, final_video
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 (Visual Promo Style)",
description="Generates a 5/10 sec video summary from article URL with clean typography, visuals, C# Corner logo, and themed illustrations."
)
if __name__ == '__main__':
iface.launch()
|