""" project @ images_to_video created @ 2024-12-12 author @ github.com/ishworrsubedii """ 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 VideoCreator: def __init__(self, intro_video_path, necklace_image, nto_outputs: list, nto_cto_outputs: list, makeup_outputs: list, font_path, output_path, audio_path, image_display_duration=2.5, box_color: tuple = (131, 42, 48), box_opacity=0.8, font_size=28, necklace_display_duration=2.0, text_color="white", fps=1, necklace_image_title="Necklace Preview", nto_image_title="Necklace Try-On", nto_cto_image_title="Clothing Try-On", makeup_image_title="Makeup Try-On", outro_video_path=""): self.intro_video_path = intro_video_path self.nto_images_dir = nto_outputs self.nto_cto_images_dir = nto_cto_outputs self.makeup_images_dir = makeup_outputs self.output_video_path = output_path self.font_path = font_path self.necklace_image = necklace_image self.audio_path = audio_path self.image_display_duration = image_display_duration self.text_color = text_color self.box_color = box_color self.box_opacity = box_opacity self.font_size = font_size self.category_font_size = font_size self.necklace_display_duration = necklace_display_duration self.fps = fps self.necklace_image_title = necklace_image_title self.makeup_image_title = makeup_image_title self.nto_image_title = necklace_image_title self.nto_cto_image_title = nto_cto_image_title self.outro_video_path = outro_video_path def create_necklace_clips(self, necklace_image_path, backgrounds=None): if backgrounds is None: backgrounds = [ # Add to your configurations (245, 245, 245), # Soft White (Perfect for Gold) (220, 245, 245), # Rich Black (Premium look) (230, 230, 235), # Pearl Gray (Elegant) # Alternative premium colors: # (25, 25, 112), # Midnight Blue # (44, 49, 51), # Charcoal # (189, 172, 152), # Champagne # (241, 235, 218), # Ivory ] necklace_clips = [] # Create a clip for each background color for bg_color in backgrounds: # Create background # bg_clip = ColorClip((1080, 1080), col=bg_color) # bg_clip = bg_clip.set_duration(self.image_display_duration) # # # Create necklace clip # necklace = ImageClip(necklace_image_path) # w, h = necklace.size # new_size = (w * 0.5, h * 0.15) bg_clip = ColorClip((1080, 1080), col=bg_color) bg_clip = bg_clip.set_duration(self.image_display_duration) # Create necklace clip necklace = ImageClip(necklace_image_path) w, h = necklace.size # Calculate the scaling factor based on the desired width of 1080 scaling_factor = 1080 / w # Calculate the new size to maintain the aspect ratio, then reduce it by 20% new_size = (1080, int(h * scaling_factor)) new_size = (int(new_size[0] * 0.6), int(new_size[1] * 0.6)) necklace = resize(necklace, (new_size)) # Adjust size as needed necklace = necklace.set_duration(self.image_display_duration) # Center the necklace necklace = necklace.set_position('center') # Composite necklace over background final_clip = CompositeVideoClip([bg_clip, necklace]) # Add text overlay txt_overlay = self.create_text_overlay("Necklace Preview", (1080, 80), self.image_display_duration) txt_overlay = txt_overlay.set_position(('center', 'bottom')) final_clip = CompositeVideoClip([final_clip, txt_overlay]) necklace_clips.append(final_clip) return necklace_clips def create_text_overlay(self, text, size, duration, is_category=False): """Create a professional text overlay with background box""" # Create background box w, h = 1080, 120 if is_category else 80 box = ColorClip((w, h), col=self.box_color, duration=duration) print("box_opacity", self.box_opacity) box = box.set_opacity(self.box_opacity) # Create text using TextClip with method='label' instead of default txt = TextClip( text, font=self.font_path, fontsize=self.category_font_size if is_category else self.font_size, color=self.text_color, size=(w, h), method='label' # Use 'label' method instead of default ).set_position('center').set_duration(duration) return CompositeVideoClip([box, txt]) def add_text_to_image(self, image_path, text, font_path, output_path): # Create image clip img_clip = ImageClip(image_path).resize((1080, 1080)) # Create text overlay txt_overlay = self.create_text_overlay(text, (1080, 80), img_clip.duration) txt_overlay = txt_overlay.set_position(('center', 'bottom')) # Composite the clips final_clip = CompositeVideoClip([img_clip, txt_overlay]) # Save as image final_clip.save_frame(output_path, t=0) return output_path def create_image_clip(self, image_path, text, duration): img_clip = ImageClip(image_path) img_clip = resize(img_clip, (1080, 1080)) # Using resize from fx.all img_clip = img_clip.set_duration(duration) txt_overlay = self.create_text_overlay(text, (1080, 80), duration) txt_overlay = txt_overlay.set_position(('center', 'bottom')) final_clip = CompositeVideoClip([img_clip, txt_overlay]) return final_clip def process_images_in_directory(self, directory, duration, text): clips = [] print(directory) for image_file in sorted(directory): if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")): print(f"Processing image: {image_file}") # image_path = os.path.join(directory, image_file) clip = self.create_image_clip(image_file, text, duration) clips.append(clip) return clips def create_final_video(self): print("Loading and processing main videos...") intro_clip = resize(VideoFileClip(self.intro_video_path), (1080, 1080)) outro_clip = resize(VideoFileClip(self.outro_video_path), (1080, 1080)) # outro_clip = resize(VideoFileClip(outro_video_p/ath), (1080, 1080)) # outro_clip_1 = resize(VideoFileClip(outro_video_path1), (1080, 1080)) # Create necklace preview clips with different backgrounds necklace_clips = self.create_necklace_clips(self.necklace_image) # Process images with categories nto_image_clips = self.process_images_in_directory( self.nto_images_dir, self.image_display_duration, self.nto_image_title) nto_cto_image_clips = self.process_images_in_directory( self.nto_cto_images_dir, self.image_display_duration, self.nto_cto_image_title) makeup_image_clips = self.process_images_in_directory( self.makeup_images_dir, self.image_display_duration, self.makeup_image_title) # Combine all clips all_clips = [intro_clip] all_clips.extend(necklace_clips) all_clips.extend(nto_image_clips) all_clips.extend(nto_cto_image_clips) all_clips.extend(makeup_image_clips) all_clips.append(outro_clip) # all_clips.append(outro_clip_1) # Create final temp_video without transitions final_video = concatenate_videoclips(all_clips, method="compose") # Add audio try: print("Adding audio...") audio = AudioFileClip(self.audio_path) video_duration = final_video.duration if audio.duration > video_duration: audio = audio.subclip(0, video_duration) final_video = final_video.set_audio(audio) print("Audio added successfully") except Exception as e: print(f"Error adding audio: {str(e)}") # Write the final temp_video with progress bar print("Rendering final temp_video...") final_video.write_videofile( self.output_video_path, fps=self.fps, codec="libx264", audio_codec="aac", bitrate="8000k", threads=4, preset='ultrafast' # ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow ) print(f"Video saved to: {self.output_video_path}")