Spaces:
Sleeping
Sleeping
""" | |
project @ images_to_video | |
created @ 2024-12-17 | |
author @ github.com/ishworrsubedii | |
""" | |
import os | |
from moviepy.audio.io.AudioFileClip import AudioFileClip | |
from moviepy.video.VideoClip import ImageClip, ColorClip, TextClip | |
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip | |
from moviepy.video.compositing.concatenate import concatenate_videoclips | |
from moviepy.video.fx.all import resize | |
from moviepy.video.io.VideoFileClip import VideoFileClip | |
class EachVideoCreator: | |
def __init__(self, necklace_title, nto_title, cto_title, makeup_title, intro_video_path=None, necklace_image=None, | |
nto_outputs=None, | |
nto_cto_outputs=None, makeup_outputs=None, font_path=None, output_path=None, | |
audio_path=None, image_display_duration=2.5, box_color=(131, 42, 48), box_opacity=0.8, | |
font_size=28, text_color="white", fps=1): | |
self.intro_video_path = intro_video_path | |
self.necklace_images = necklace_image if necklace_image else [] | |
self.nto_outputs = nto_outputs if nto_outputs else [] | |
self.nto_cto_outputs = nto_cto_outputs if nto_cto_outputs else [] | |
self.makeup_outputs = makeup_outputs if makeup_outputs else [] | |
self.output_video_path = output_path | |
self.font_path = font_path | |
self.audio_path = audio_path | |
self.image_display_duration = image_display_duration | |
self.box_color = box_color | |
self.box_opacity = box_opacity | |
self.font_size = font_size | |
self.text_color = text_color | |
self.fps = fps | |
self.necklace_title = necklace_title | |
self.nto_title = nto_title | |
self.cto_title = cto_title | |
self.makeup_title = makeup_title | |
def create_necklace_clips(self, necklace_image, index, label): | |
if not necklace_image: | |
print(f"Skipping necklace {index + 1}: No image provided.") | |
return [] | |
backgrounds = [ | |
(245, 245, 245), # Soft White | |
(220, 245, 245), # Light Blue | |
(230, 230, 235) # Pearl Gray | |
] | |
necklace_clips = [] | |
for bg_color in backgrounds: | |
bg_clip = ColorClip((1080, 1080), col=bg_color, duration=self.image_display_duration) | |
necklace = resize(ImageClip(necklace_image), width=650) | |
necklace = necklace.set_duration(self.image_display_duration).set_position('center') | |
txt_overlay = self.create_text_overlay(f"{label}") | |
final_clip = CompositeVideoClip([bg_clip, necklace, txt_overlay.set_position(('center', 'bottom'))]) | |
necklace_clips.append(final_clip) | |
return necklace_clips | |
def create_grouped_clips(self, grouped_images, label): | |
clips = [] | |
for idx, group in enumerate(grouped_images): | |
for img_path in group: | |
if os.path.exists(img_path) and img_path.lower().endswith(('.png', '.jpg', '.jpeg')): | |
print(f"Processing {label} image: {img_path}") | |
img_clip = resize(ImageClip(img_path), (1080, 1080)) | |
txt_overlay = self.create_text_overlay(f"{label} {idx + 1}") | |
final_clip = CompositeVideoClip([ | |
img_clip.set_duration(self.image_display_duration), | |
txt_overlay.set_position(('center', 'bottom')) | |
]) | |
clips.append(final_clip) | |
return clips | |
def create_text_overlay(self, text, duration=None): | |
box = ColorClip((1080, 80), col=self.box_color, duration=duration or self.image_display_duration) | |
box = box.set_opacity(self.box_opacity) | |
txt = TextClip(text, font=self.font_path, fontsize=self.font_size, color=self.text_color) | |
return CompositeVideoClip([box, txt.set_position('center')]) | |
def create_final_video(self): | |
try: | |
print("Starting video creation...") | |
clips = [] | |
# Step 1: Process Intro Video | |
if self.intro_video_path and os.path.exists(self.intro_video_path): | |
print(f"Adding intro video from path: {self.intro_video_path}") | |
intro_clip = resize(VideoFileClip(self.intro_video_path), (1080, 1080)) | |
clips.append(intro_clip) | |
else: | |
print("Skipping intro video: Path not provided or invalid.") | |
# Step 2: Process Necklaces and Associated Outputs | |
for idx, necklace_image in enumerate(self.necklace_images): | |
print(f"Processing Necklace {idx + 1}...") | |
# Necklace preview clips | |
necklace_clips = self.create_necklace_clips(necklace_image, idx, self.necklace_title[idx]) | |
if necklace_clips: | |
clips.extend(necklace_clips) | |
else: | |
print(f"Skipping Necklace {idx + 1} preview: No valid clips created.") | |
# NTO outputs | |
if idx < len(self.nto_outputs): | |
print(f"Adding NTO outputs for Necklace {idx + 1}") | |
nto_clips = self.create_grouped_clips([self.nto_outputs[idx]], self.nto_title[idx]) | |
if nto_clips: | |
clips.extend(nto_clips) | |
else: | |
print(f"No valid NTO clips for Necklace {idx + 1}") | |
# CTO outputs | |
if idx < len(self.nto_cto_outputs): | |
print(f"Adding CTO outputs for Necklace {idx + 1}") | |
cto_clips = self.create_grouped_clips([self.nto_cto_outputs[idx]], self.cto_title[idx]) | |
if cto_clips: | |
clips.extend(cto_clips) | |
else: | |
print(f"No valid CTO clips for Necklace {idx + 1}") | |
# Makeup outputs | |
if idx < len(self.makeup_outputs): | |
print(f"Adding Makeup outputs for Necklace {idx + 1}") | |
makeup_clips = self.create_grouped_clips([self.makeup_outputs[idx]], self.makeup_title[idx]) | |
if makeup_clips: | |
clips.extend(makeup_clips) | |
else: | |
print(f"No valid Makeup clips for Necklace {idx + 1}") | |
final_clips = [] | |
for clip in clips: | |
final_clips.append(clip.set_duration(self.image_display_duration)) | |
clips = final_clips | |
if not clips: | |
print("No valid clips to combine. Exiting.") | |
return | |
print(f"Total clips to concatenate: {len(clips)}") | |
final_video = concatenate_videoclips(clips, method="compose") | |
# Step 4: Add Background Audio | |
if self.audio_path and os.path.exists(self.audio_path): | |
print(f"Adding background audio from path: {self.audio_path}") | |
try: | |
audio = AudioFileClip(self.audio_path).subclip(0, final_video.duration) | |
final_video = final_video.set_audio(audio) | |
except Exception as e: | |
print(f"Error adding audio: {e}") | |
else: | |
print("Skipping background audio: Path not provided or invalid.") | |
# Step 5: Export Final Video | |
print("Rendering final video...") | |
final_video.write_videofile( | |
self.output_video_path, | |
fps=self.fps, | |
codec="libx264", | |
audio_codec="aac", | |
threads=4, | |
preset="ultrafast" | |
) | |
print(f"Video successfully saved to: {self.output_video_path}") | |
except Exception as e: | |
print(f"An error occurred during video creation: {e}") | |
# if __name__ == "__main__": | |
# creator = EachVideoCreator( | |
# intro_video_path="intro.mp4", | |
# necklace_image=["necklace.jpg", "necklace2.png"], | |
# nto_outputs=[["nto1.jpg", "nto2.jpg"], ["nto3.jpg", "nto4.jpg"]], | |
# nto_cto_outputs=[["cto1.jpg", "cto2.jpg"], ["cto3.jpg", "cto4.jpg"]], | |
# makeup_outputs=[["makeup1.jpg", "makeup2.jpg"], ["makeup3.jpg", "makeup4.jpg"]], | |
# font_path="font.ttf", | |
# output_path="output/final_video.mp4", | |
# audio_path="audio.mp3" | |
# ) | |
# creator.create_final_video() | |