sreepathi-ravikumar commited on
Commit
71970fd
·
verified ·
1 Parent(s): 4fb75d6

Create video.py

Browse files
Files changed (1) hide show
  1. video.py +148 -0
video.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ import aiohttp
4
+ import random
5
+ from PIL import Image, ImageDraw, ImageFont
6
+ from io import BytesIO
7
+ from duckduckgo_search import DDGS
8
+ import glob
9
+ from moviepy.editor import *
10
+ def video_generation(text, prompts):
11
+
12
+ # -------- TEXT SPLITTING --------
13
+ def split_text(text, word_count=30):
14
+ words = text.split()
15
+ lines = []
16
+ for i in range(0, len(words) // word_count):
17
+ line = " ".join(words[i * word_count:(i + 1) * word_count])
18
+ lines.append(line)
19
+ if len(words) % word_count != 0:
20
+ lines.append(" ".join(words[(len(words) // word_count) * word_count:]))
21
+ return lines
22
+
23
+ # -------- ASYNC IMAGE SEARCH --------
24
+ def get_headers():
25
+ user_agents = [
26
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
27
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
28
+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:90.0)"
29
+ ]
30
+ return {"User-Agent": random.choice(user_agents)}
31
+
32
+ def is_valid_image(image):
33
+ width, height = image.size
34
+ aspect_ratio = round(width / height, 2)
35
+ return width >= 854 and height >= 480 and abs(aspect_ratio - (16 / 9)) <= 0.2
36
+
37
+ async def fetch_image(session, url, name):
38
+ try:
39
+ async with session.get(url, timeout=10) as response:
40
+ content = await response.read()
41
+ image = Image.open(BytesIO(content)).convert("RGB")
42
+ if not is_valid_image(image):
43
+ return None
44
+ path = os.path.join("images", f"{name}.jpg")
45
+ image.save(path)
46
+ return path
47
+ except:
48
+ return None
49
+
50
+ async def search_and_download(session, prompt):
51
+ name = prompt.replace(" ", "_").lower()
52
+ try:
53
+ ddgs = DDGS()
54
+ results = ddgs.images(prompt, max_results=15)
55
+ for item in results:
56
+ url = item.get("image")
57
+ result = await fetch_image(session, url, name)
58
+ if result:
59
+ return result
60
+ return None
61
+ except:
62
+ return None
63
+
64
+ async def run_image_search(prompts):
65
+ os.makedirs("images", exist_ok=True)
66
+ async with aiohttp.ClientSession(headers=get_headers()) as session:
67
+ tasks = [search_and_download(session, prompt) for prompt in prompts]
68
+ return await asyncio.gather(*tasks)
69
+
70
+ # -------- TEXT ON IMAGE --------
71
+ def get_text_size(text, font):
72
+ bbox = font.getbbox(text)
73
+ width = bbox[2] - bbox[0]
74
+ height = bbox[3] - bbox[1]
75
+ return width, height
76
+
77
+ def create_text_image(text, image_link):
78
+ image = Image.open(image_link).convert("RGB")
79
+ draw = ImageDraw.Draw(image)
80
+ image_size = image.size
81
+
82
+ font_path = "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"
83
+ max_font_size = 80
84
+ min_font_size = 10
85
+ best_font, best_lines = None, []
86
+
87
+ for font_size in range(max_font_size, min_font_size, -2):
88
+ font = ImageFont.truetype(font_path, font_size)
89
+ words = text.split()
90
+ lines, current_line = [], ""
91
+
92
+ for word in words:
93
+ test_line = f"{current_line} {word}" if current_line else word
94
+ w, _ = get_text_size(test_line, font)
95
+ if w <= image_size[0] * 0.95:
96
+ current_line = test_line
97
+ else:
98
+ lines.append(current_line)
99
+ current_line = word
100
+ if current_line:
101
+ lines.append(current_line)
102
+
103
+ total_height = sum(get_text_size(line, font)[1] + 10 for line in lines)
104
+ if total_height <= image_size[1] * 0.95:
105
+ best_font, best_lines = font, lines
106
+ break
107
+
108
+ y = (image_size[1] - total_height) // 2
109
+ for line in best_lines:
110
+ w, h = get_text_size(line, best_font)
111
+ x = (image_size[0] - w) // 2
112
+
113
+ for dx in range(-2, 3):
114
+ for dy in range(-2, 3):
115
+ if dx != 0 or dy != 0:
116
+ draw.text((x + dx, y + dy), line, font=best_font, fill='black')
117
+ draw.text((x, y), line, font=best_font, fill='white')
118
+ y += h + 10
119
+
120
+ image.save(image_link)
121
+
122
+ # -------- VIDEO GENERATION --------
123
+ def create_video_from_images(folder="images", output="output_video.mp4", duration=3):
124
+ image_files = sorted(glob.glob(os.path.join(folder, "*.jpg")))
125
+ if not image_files:
126
+ raise ValueError("No images found!")
127
+ clips = [ImageClip(m).set_duration(duration) for m in image_files]
128
+ video = concatenate_videoclips(clips, method="compose")
129
+ video.write_videofile(output, fps=24, audio=False)
130
+ return output
131
+
132
+ # -------- RUN COMPLETE PIPELINE --------
133
+ lines = split_text(text)
134
+ if len(lines) != len(prompts):
135
+ print(len(lines),len(prompts))
136
+ raise ValueError("The number of text chunks must match the number of prompts!")
137
+
138
+ image_paths = asyncio.run(run_image_search(prompts))
139
+ image_paths = [p for p in image_paths if p is not None]
140
+
141
+ if len(image_paths) != len(lines):
142
+ raise ValueError("Mismatch in number of images downloaded and text chunks!")
143
+
144
+ for i in range(len(lines)):
145
+ create_text_image(lines[i], image_paths[i])
146
+
147
+ video_path = create_video_from_images()
148
+ return video_path