RohitCSharp commited on
Commit
dd4e7fa
·
verified ·
1 Parent(s): cd2f783

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -48
app.py CHANGED
@@ -5,17 +5,14 @@ from langchain.prompts import PromptTemplate
5
  from langchain.chat_models import ChatOpenAI
6
  from gtts import gTTS
7
  from bs4 import BeautifulSoup
8
- from PIL import Image, ImageDraw, ImageFont, ImageEnhance
9
  import ffmpeg
10
  import textwrap
11
- import random
12
- from urllib.request import urlretrieve
13
-
14
- UNSPLASH_KEY = "-7tFgMCy_pwrouZrC8mmEIBpyskEyP25e3_Y4vWSvBs"
15
 
16
  llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
17
  summary_prompt = PromptTemplate.from_template("""
18
- Provide a crisp, promotional-style summary (under 100 words) of the following:
19
 
20
  {text}
21
 
@@ -23,39 +20,32 @@ Summary:
23
  """)
24
  summary_chain = LLMChain(llm=llm, prompt=summary_prompt)
25
 
 
26
  def extract_main_content(url):
27
  resp = requests.get(url, timeout=10)
28
  soup = BeautifulSoup(resp.content, "html.parser")
29
- for tag in soup(["nav", "header", "footer", "aside", "script", "style", "noscript"]): tag.decompose()
 
30
  paras = [p.get_text() for p in soup.find_all("p") if len(p.get_text()) > 60]
31
  return "\n".join(paras[:20]) or None
32
 
33
- def fetch_unsplash_image(query):
34
- url = f"https://api.unsplash.com/photos/random?query={query}&orientation=landscape&client_id={UNSPLASH_KEY}"
 
 
35
  try:
36
- resp = requests.get(url).json()
37
- return resp['urls']['regular']
 
 
38
  except:
39
- return "https://img.freepik.com/free-photo/blue-abstract-gradient-wave-wallpaper_53876-102605.jpg"
40
-
41
- ASSETS = {
42
- "logo": "https://huggingface.co/spaces/csccorner/Link-to-video/resolve/main/csharplogo.png",
43
- "graphics": [
44
- "https://img.freepik.com/free-vector/startup-launch-concept-with-rocket_23-2147866180.jpg",
45
- "https://img.freepik.com/free-vector/artificial-intelligence-concept-illustration_114360-7307.jpg",
46
- "https://img.freepik.com/free-vector/business-goal-achievement-banner_33099-1687.jpg"
47
- ]
48
- }
49
-
50
- def download_asset(url):
51
- local_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
52
- urlretrieve(url, local_path)
53
- return local_path
54
 
55
  def create_slides(text, duration, output_folder, max_lines=6):
56
  font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
57
  font = ImageFont.truetype(font_path, 48)
58
- logo_path = download_asset(ASSETS["logo"])
59
 
60
  chunks = textwrap.wrap(text, width=36)
61
  slides = ["\n".join(chunks[i:i+max_lines]) for i in range(0, len(chunks), max_lines)]
@@ -63,43 +53,34 @@ def create_slides(text, duration, output_folder, max_lines=6):
63
  slide_paths = []
64
 
65
  for i, slide_text in enumerate(slides):
66
- query = random.choice(slide_text.split())
67
- bg_path = download_asset(fetch_unsplash_image(query))
68
- graphic_path = download_asset(random.choice(ASSETS["graphics"]))
69
-
70
- bg = Image.open(bg_path).resize((1280, 720)).convert("RGBA")
71
- enhancer = ImageEnhance.Brightness(bg)
72
- bg = enhancer.enhance(0.3)
73
  draw = ImageDraw.Draw(bg)
74
 
75
  lines = slide_text.split("\n")
76
- lines = [line.encode('utf-8', 'replace').decode('utf-8') for line in lines]
77
- total_height = sum([font.getbbox(line)[3] - font.getbbox(line)[1] for line in lines]) + (len(lines)-1)*20
78
  y = max((720 - total_height) // 2, 20)
79
  for line in lines:
80
  w = font.getbbox(line)[2] - font.getbbox(line)[0]
81
  draw.text(((1280 - w) // 2, y), line, font=font, fill="white")
82
  y += font.getbbox(line)[3] - font.getbbox(line)[1] + 20
83
 
84
- logo = Image.open(logo_path).convert("RGBA")
85
- logo = logo.resize((160, int(160 * logo.size[1] / logo.size[0])))
86
- bg.paste(logo, (30, 630 - logo.size[1]), logo)
87
-
88
- graphic = Image.open(graphic_path).convert("RGBA")
89
- graphic = graphic.resize((200, 200))
90
- bg.paste(graphic, (1040, 40), graphic)
91
 
92
  frame_path = os.path.join(output_folder, f"slide_{i}.png")
93
- bg.convert("RGB").save(frame_path)
94
  slide_paths.append((frame_path, per_slide_time))
95
 
96
  return slide_paths
97
 
 
98
  def url_to_av_summary(url, duration):
99
  content = extract_main_content(url)
100
  if not content:
101
  return "Failed to extract article content.", None
102
- summary = summary_chain.invoke({"text": content[:3000]})["text"].replace('"','')[:300]
103
 
104
  audio_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
105
  gTTS(text=summary).save(audio_path)
@@ -126,10 +107,11 @@ def url_to_av_summary(url, duration):
126
 
127
  ffmpeg.output(video_input, audio_input, final_video,
128
  vcodec='libx264', acodec='aac', pix_fmt='yuv420p', shortest=None
129
- ).run(overwrite_output=True, quiet=True)
130
 
131
  return summary, final_video
132
 
 
133
  iface = gr.Interface(
134
  fn=url_to_av_summary,
135
  inputs=[
@@ -140,8 +122,8 @@ iface = gr.Interface(
140
  gr.Textbox(label="Summary"),
141
  gr.Video(label="Generated AV Summary")
142
  ],
143
- title="🎮 AV Summary Generator (Visual Promo Style)",
144
- description="Generates a 5/10 sec video summary from article URL with clean typography, visuals, C# Corner logo, and themed illustrations."
145
  )
146
 
147
  if __name__ == '__main__':
 
5
  from langchain.chat_models import ChatOpenAI
6
  from gtts import gTTS
7
  from bs4 import BeautifulSoup
8
+ from PIL import Image, ImageDraw, ImageFont
9
  import ffmpeg
10
  import textwrap
11
+ import shutil
 
 
 
12
 
13
  llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
14
  summary_prompt = PromptTemplate.from_template("""
15
+ Provide a crisp, promotional-style summary (under 50 words) of the following:
16
 
17
  {text}
18
 
 
20
  """)
21
  summary_chain = LLMChain(llm=llm, prompt=summary_prompt)
22
 
23
+
24
  def extract_main_content(url):
25
  resp = requests.get(url, timeout=10)
26
  soup = BeautifulSoup(resp.content, "html.parser")
27
+ for tag in soup(["nav", "header", "footer", "aside", "script", "style", "noscript"]):
28
+ tag.decompose()
29
  paras = [p.get_text() for p in soup.find_all("p") if len(p.get_text()) > 60]
30
  return "\n".join(paras[:20]) or None
31
 
32
+
33
+ def download_logo():
34
+ logo_url = "https://huggingface.co/spaces/csccorner/Link-to-video/resolve/main/csharplogo.png"
35
+ local_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
36
  try:
37
+ r = requests.get(logo_url, stream=True)
38
+ with open(local_path, 'wb') as f:
39
+ shutil.copyfileobj(r.raw, f)
40
+ return local_path
41
  except:
42
+ return None
43
+
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  def create_slides(text, duration, output_folder, max_lines=6):
46
  font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
47
  font = ImageFont.truetype(font_path, 48)
48
+ logo_path = download_logo()
49
 
50
  chunks = textwrap.wrap(text, width=36)
51
  slides = ["\n".join(chunks[i:i+max_lines]) for i in range(0, len(chunks), max_lines)]
 
53
  slide_paths = []
54
 
55
  for i, slide_text in enumerate(slides):
56
+ bg = Image.new("RGB", (1280, 720), color=(10, 20, 40))
 
 
 
 
 
 
57
  draw = ImageDraw.Draw(bg)
58
 
59
  lines = slide_text.split("\n")
60
+ total_height = sum([font.getbbox(line)[3] - font.getbbox(line)[1] for line in lines]) + (len(lines) - 1) * 20
 
61
  y = max((720 - total_height) // 2, 20)
62
  for line in lines:
63
  w = font.getbbox(line)[2] - font.getbbox(line)[0]
64
  draw.text(((1280 - w) // 2, y), line, font=font, fill="white")
65
  y += font.getbbox(line)[3] - font.getbbox(line)[1] + 20
66
 
67
+ if logo_path:
68
+ logo = Image.open(logo_path).convert("RGBA")
69
+ logo = logo.resize((160, int(160 * logo.size[1] / logo.size[0])))
70
+ bg.paste(logo, (30, 630 - logo.size[1]), logo)
 
 
 
71
 
72
  frame_path = os.path.join(output_folder, f"slide_{i}.png")
73
+ bg.save(frame_path)
74
  slide_paths.append((frame_path, per_slide_time))
75
 
76
  return slide_paths
77
 
78
+
79
  def url_to_av_summary(url, duration):
80
  content = extract_main_content(url)
81
  if not content:
82
  return "Failed to extract article content.", None
83
+ summary = summary_chain.invoke({"text": content[:3000]})["text"].replace('"', '')[:300]
84
 
85
  audio_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
86
  gTTS(text=summary).save(audio_path)
 
107
 
108
  ffmpeg.output(video_input, audio_input, final_video,
109
  vcodec='libx264', acodec='aac', pix_fmt='yuv420p', shortest=None
110
+ ).run(overwrite_output=True, quiet=True)
111
 
112
  return summary, final_video
113
 
114
+
115
  iface = gr.Interface(
116
  fn=url_to_av_summary,
117
  inputs=[
 
122
  gr.Textbox(label="Summary"),
123
  gr.Video(label="Generated AV Summary")
124
  ],
125
+ title="\U0001F3AE AV Summary Generator (Visual Promo Style)",
126
+ description="Generates a 5/10 sec video summary from article URL with clean typography, audio voiceover, and C# Corner logo."
127
  )
128
 
129
  if __name__ == '__main__':