RohitCSharp commited on
Commit
cd6257a
·
verified ·
1 Parent(s): 106c061

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -26
app.py CHANGED
@@ -5,7 +5,7 @@ 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
9
  import ffmpeg
10
  import textwrap
11
 
@@ -28,17 +28,46 @@ def extract_main_content(url):
28
  paras = [p.get_text() for p in soup.find_all("p") if len(p.get_text()) > 60]
29
  return "\n".join(paras[:20]) or None
30
 
31
- # Create a single-slide background
32
- def create_background(text, path, size=(1280,720)):
33
- img = Image.new("RGB", size, color=(20, 30, 60))
34
- draw = ImageDraw.Draw(img)
35
- wrapped = textwrap.fill(text, width=40)
36
- lines = wrapped.split('\n')
37
- y_text = (size[1] - len(lines)*60) // 2
38
- for line in lines:
39
- draw.text(((size[0]-draw.textlength(line))/2, y_text), line, fill="white")
40
- y_text += 60
41
- img.save(path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  # Generate AV summary
44
  def url_to_av_summary(url, duration):
@@ -47,22 +76,31 @@ def url_to_av_summary(url, duration):
47
  return "Failed to extract article content.", None
48
  summary = summary_chain.run(text=content[:3000]).replace('"','')[:300]
49
 
50
- # Audio
51
  audio_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
52
  gTTS(text=summary).save(audio_path)
53
 
54
- # Image with multiline summary
55
- bg_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
56
- create_background(summary, bg_path)
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- # Final video
59
  video_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name
60
- cmd = [
61
- 'ffmpeg', '-y', '-loop', '1', '-i', bg_path, '-i', audio_path,
62
- '-c:v', 'libx264', '-c:a', 'aac', '-b:a', '192k', '-pix_fmt', 'yuv420p',
63
- '-t', str(duration), '-shortest', video_path
64
- ]
65
- os.system(' '.join(cmd))
66
 
67
  return summary, video_path
68
 
@@ -76,9 +114,9 @@ iface = gr.Interface(
76
  gr.Textbox(label="Summary"),
77
  gr.Video(label="Generated AV Summary")
78
  ],
79
- title="🎞️ AV Summary Generator (Multiline Slide Version)",
80
- description="Generate a short 5/10 sec AV with multiline centered text. CPU only, OpenAI + gTTS + FFmpeg."
81
  )
82
 
83
  if __name__ == '__main__':
84
- iface.launch()
 
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
 
 
28
  paras = [p.get_text() for p in soup.find_all("p") if len(p.get_text()) > 60]
29
  return "\n".join(paras[:20]) or None
30
 
31
+ # Download C# Corner logo
32
+ LOGO_URL = "https://csharpcorner-mindcrackerinc.netdna-ssl.com/App_Themes/Default/images/c-sharp-corner-logo.png"
33
+ def download_logo():
34
+ logo_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png").name
35
+ with open(logo_path, 'wb') as f:
36
+ f.write(requests.get(LOGO_URL).content)
37
+ return logo_path
38
+
39
+ # Create image slides from text chunks
40
+ def create_slides(text, duration, output_folder, max_lines=6):
41
+ font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
42
+ font = ImageFont.truetype(font_path, 48)
43
+ logo_path = download_logo()
44
+
45
+ chunks = textwrap.wrap(text, width=40)
46
+ slides = ["\n".join(chunks[i:i+max_lines]) for i in range(0, len(chunks), max_lines)]
47
+ per_slide_time = duration / len(slides)
48
+ slide_paths = []
49
+
50
+ for i, slide_text in enumerate(slides):
51
+ img = Image.new("RGB", (1280, 720), color=(20, 30, 60))
52
+ draw = ImageDraw.Draw(img)
53
+
54
+ lines = slide_text.split("\n")
55
+ total_height = sum([font.getsize(line)[1] for line in lines]) + (len(lines)-1)*10
56
+ y = (720 - total_height) // 2
57
+
58
+ for line in lines:
59
+ w, h = draw.textsize(line, font=font)
60
+ draw.text(((1280 - w) // 2, y), line, font=font, fill="white")
61
+ y += h + 10
62
+
63
+ logo = Image.open(logo_path).convert("RGBA").resize((120, 120))
64
+ img.paste(logo, (40, 40), logo)
65
+
66
+ frame_path = os.path.join(output_folder, f"slide_{i}.png")
67
+ img.save(frame_path)
68
+ slide_paths.append((frame_path, per_slide_time))
69
+
70
+ return slide_paths
71
 
72
  # Generate AV summary
73
  def url_to_av_summary(url, duration):
 
76
  return "Failed to extract article content.", None
77
  summary = summary_chain.run(text=content[:3000]).replace('"','')[:300]
78
 
 
79
  audio_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3").name
80
  gTTS(text=summary).save(audio_path)
81
 
82
+ frame_dir = tempfile.mkdtemp()
83
+ slides = create_slides(summary, duration, frame_dir)
84
+
85
+ concat_file = os.path.join(frame_dir, "frames.txt")
86
+ with open(concat_file, "w") as f:
87
+ for path, t in slides:
88
+ f.write(f"file '{path}'\n")
89
+ f.write(f"duration {t}\n")
90
+
91
+ concat_img = os.path.join(frame_dir, "video_input.mp4")
92
+ ffmpeg.input(concat_file, format='concat', safe=0).output(
93
+ concat_img,
94
+ r=1,
95
+ vcodec='libx264', pix_fmt='yuv420p'
96
+ ).run(overwrite_output=True, quiet=True)
97
 
 
98
  video_path = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4").name
99
+ ffmpeg.input(concat_img).output(
100
+ video_path,
101
+ i=audio_path,
102
+ vcodec='libx264', acodec='aac', pix_fmt='yuv420p', shortest=None
103
+ ).run(overwrite_output=True, quiet=True)
 
104
 
105
  return summary, video_path
106
 
 
114
  gr.Textbox(label="Summary"),
115
  gr.Video(label="Generated AV Summary")
116
  ],
117
+ title="🎞️ AV Summary Generator (Multislide with Logo)",
118
+ description="Generates a 5/10 sec video summary from article URL with large text, logo, and slide animation."
119
  )
120
 
121
  if __name__ == '__main__':
122
+ iface.launch()