from moviepy.editor import *
from moviepy.video.fx.all import resize
import numpy as np
import textwrap
from PIL import Image, ImageDraw, ImageFilter
import os
import shutil
import subprocess
# Define the audio file path
audio_path = "/Users/jitendersingh/Downloads/heading2.mp3"
# Text and parameters
font_path = "/System/Library/Fonts/Supplemental/Arial Bold.ttf" # Update font if needed
title_font_size = 60
font_size = 40
feature_text_margin = 40 #Margin for sides
title_margin= 40 #Margin for sides
# Load the audio file
audio_clip = AudioFileClip(audio_path)
# Paths to your audio files
heading_audio_path = "/Users/jitendersingh/Downloads/heading2.mp3"
content_audio_path = "/Users/jitendersingh/Downloads/content2.mp3"
combined_audio_path = "/Users/jitendersingh/Downloads/combined_audio.mp3"
background_video_path = "/Users/jitendersingh/Downloads/bg2.mp4"
output_video_path = "/Users/jitendersingh/Downloads/ffmpeg_final_video.mp4"
# Text and parameters
title_text = "LG 20 L with i-wave Technology & Quartz Heater Grill Microwave Oven"
feature_heading_text = "6th Sense Soft Move Technology 6th Sense Soft Move Technology"
content_text = "Innovative performance for all your needs."
font_path = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"
# Load the audio files
heading_audio_clip = AudioFileClip(heading_audio_path)
content_audio_clip = AudioFileClip(content_audio_path)
# Set the video duration to match the audio duration
audio_duration = audio_clip.duration
# Combine audio files
subprocess.run([
"ffmpeg", "-i", f"concat:{heading_audio_path}|{content_audio_path}",
"-acodec", "copy", combined_audio_path
], check=True)
combine_audio_clip = AudioFileClip(combined_audio_path)
# Calculate total audio duration
total_audio_duration = combine_audio_clip.duration
image_paths = [
"/Users/jitendersingh/Downloads/fkimages/8.jpg",
"/Users/jitendersingh/Downloads/fkimages/bbb.png",
# "/Users/jitendersingh/Downloads/myimages/3.jpeg",
# "/Users/jitendersingh/Downloads/myimages/4.jpeg",
# "/Users/jitendersingh/Downloads/myimages/5.jpeg"
]
# Directory to save modified images
temp_dir = "/Users/jitendersingh/Downloads/temp_images"
shutil.rmtree(temp_dir)
os.makedirs(temp_dir, exist_ok=True)
# Function to add shadow and rounded corners (unchanged)
def add_shadow_with_rounded_corners(image_path, output_path, corner_radius, shadow_size):
img = Image.open(image_path).convert("RGBA")
original_size = img.size
scaled_size = (original_size[0] - 2 * shadow_size, original_size[1] - 2 * shadow_size)
img = img.resize(scaled_size, Image.ANTIALIAS)
mask = Image.new("L", scaled_size, 0)
draw = ImageDraw.Draw(mask)
draw.rounded_rectangle((0, 0, scaled_size[0], scaled_size[1]), radius=corner_radius, fill=255)
rounded_img = Image.new("RGBA", scaled_size)
rounded_img.paste(img, (0, 0), mask=mask)
shadow = Image.new("RGBA", original_size, (0, 0, 0, 0))
draw = ImageDraw.Draw(shadow)
draw.rounded_rectangle(
(shadow_size, shadow_size, original_size[0] - shadow_size, original_size[1] - shadow_size),
radius=corner_radius,
fill=(0, 0, 0, 128)
)
shadow = shadow.filter(ImageFilter.GaussianBlur(radius=shadow_size // 2))
output = Image.new("RGBA", original_size, (0, 0, 0, 0))
output.paste(shadow, (0, 0))
output.paste(rounded_img, (shadow_size, shadow_size), mask=mask)
output.save(output_path, format="PNG")
# Modify images (unchanged)
corner_radius = 50
shadow_size = 50
modified_image_paths = []
for image_path in image_paths:
output_path = os.path.join(temp_dir, os.path.basename(image_path))
add_shadow_with_rounded_corners(image_path, output_path, corner_radius, shadow_size)
modified_image_paths.append(output_path)
# Load the video
background_video = VideoFileClip(background_video_path)
# Animation properties (added out animation details)
images_properties = [
{"size": (830, 700), "direction": "second_half", "position": {"x": 0, "y": 0}, "start_time": 0},
{"size": (830, 700), "direction": "first_half", "position": {"x": 0, "y": 0}, "start_time": 0},
# {"size": (380, 350), "direction": "right_to_left", "position": {"x": 1000, "y": 180}, "start_time": 0.5},
# {"size": (380, 350), "direction": "right_to_left", "position": {"x": 1400, "y": 180}, "start_time": 0.8},
# {"size": (380, 350), "direction": "bottom_to_top", "position": {"x": 1000, "y": 620}, "start_time": 1.1},
# {"size": (380, 350), "direction": "bottom_to_top", "position": {"x": 1400, "y": 620}, "start_time": 1.3}
]
# Resize function (unchanged)
def resize_image(image, max_width, max_height):
image_width, image_height = image.size
if image_width > max_width or image_height > max_height:
aspect_ratio = image_width / image_height
if image_width > max_width:
new_width = max_width
new_height = int(new_width / aspect_ratio)
else:
new_height = max_height
new_width = int(new_height * aspect_ratio)
image = image.resize(newsize=(new_width, new_height))
return image
def image_animation(image, direction, position, start_time):
def animate(t):
duration = 0.5 # Animation duration
out_start_time = background_video.duration - 0.5 # Start out animation 1 second before the video ends
# Out animation logic: Trigger if the current time (`t`) is after `out_start_time`
if t >= out_start_time:
out_duration = 0.5
out_t_offset = t - out_start_time
x = position['x'] # Use the final x position from the in animation
y_start = position['y'] # Use the final y position from the in animation
y_end = background_video.h + image.size[1] # Move downward off-screen
y = y_start + (y_end - y_start) * min(out_t_offset / out_duration, 1)
return x, y
# Existing animations
t_offset = t - start_time
if direction == "left_to_right":
x_start = -image.size[0]
x_end = position['x']
y = position['y']
x = x_start + (x_end - x_start) * min(t_offset / duration, 1)
elif direction == "right_to_left":
x_start = background_video.w
x_end = position['x']
y = position['y']
x = x_start - (x_start - x_end) * min(t_offset / duration, 1)
elif direction == "top_to_bottom":
y_start = -image.size[1]
y_end = position['y']
x = position['x']
y = y_start + (y_end - y_start) * min(t_offset / duration, 1)
elif direction == "bottom_to_top":
y_start = background_video.h
y_end = position['y']
x = position['x']
y = y_start - (y_start - y_end) * min(t_offset / duration, 1)
elif direction == "first_half":
image_width, image_height = image.size
# Calculate the x positions
start_x = -background_video.w
end_x = (background_video.w // 4) - (image_width // 2)
x = start_x + (end_x - start_x) * min(t_offset / duration, 1)
# Set position for consistency
position['x'] = end_x # Store end_x for out animation
y = (background_video.h - image_height) // 2 + 130 # First half top margin
position['y'] = y # Store y for out animation
elif direction == "second_half":
image_width, image_height = image.size
# Calculate the x positions
start_x = background_video.w
end_x = (3 * background_video.w // 4) - (image_width // 2)
x = start_x - (start_x - end_x) * min(t_offset / duration, 1)
# Set position for consistency
position['x'] = end_x # Store end_x for out animation
y = (background_video.h - image_height) // 2 + 130 # Second half top margin
position['y'] = y # Store y for out animation
else:
x, y = position['x'], position['y'] # Default position
return x, y
return animate
if background_video.duration > total_audio_duration:
background_video = background_video.subclip(0, total_audio_duration)
print(f"Background video trimmed to: {background_video.duration} seconds")
# Process images and create animated images
animated_images = []
for i, image_path in enumerate(modified_image_paths):
# Load each image
image = ImageClip(image_path).set_duration(total_audio_duration) # Set duration equal to audio duration
image = image.set_start(0) # Ensure all images start at the beginning
# Resize the image according to specified properties
resized_image = resize_image(image.copy(), images_properties[i]["size"][0], images_properties[i]["size"][1])
# Create an animation for the resized image
animation = image_animation(
resized_image,
images_properties[i]["direction"],
images_properties[i]["position"],
images_properties[i]["start_time"]
)
# Set the position of the animated image
animated_image = resized_image.set_position(animation)
# Append the animated image to the list
animated_images.append(animated_image)
def create_rounded_rectangle(size, color, radius):
"""Creates a rounded rectangle as a NumPy array."""
width, height = size
img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
draw.rounded_rectangle(
[(0, 0), (width, height)],
radius=radius,
fill=color,
)
return np.array(img)
def wrap_text(text, font_size, max_width, font_path):
"""Wrap text to fit within max_width."""
text_clip = TextClip(text, fontsize=font_size, color="white", font=font_path)
line_width = text_clip.w
char_count = len(text)
avg_char_width = line_width / char_count
max_chars_per_line = int(max_width / avg_char_width)
wrapped_text = "\n".join(textwrap.wrap(text, width=max_chars_per_line))
return wrapped_text
video = background_video
if video.duration > total_audio_duration:
video = video.subclip(0, total_audio_duration)
print(f"Video trimmed to: {video.duration} seconds")
feature_text_max_text_width = video.w - 2 * feature_text_margin # Maximum width available for the text
# Wrap the text to fit within the frame, only applying margin on width
wrapped_text = wrap_text(feature_heading_text, font_size, feature_text_max_text_width, font_path)
# Create the text clip with the wrapped text
text_clip = TextClip(wrapped_text, fontsize=font_size, color='white', font=font_path, align='center').set_duration(total_audio_duration)
# Create a rounded rectangle background for the text (apply margin only in width)
bg_size = (text_clip.w + 60, text_clip.h + 20) # Add 20px padding for background
bg_color = (0, 0, 0, 255) # Blue background
rounded_rect = create_rounded_rectangle(bg_size, bg_color, radius=15)
background_clip = ImageClip(rounded_rect).set_duration(total_audio_duration)
# Combine text and background
text_with_bg = CompositeVideoClip([
background_clip.set_position('center'),
text_clip.set_position('center')
]).set_duration(total_audio_duration)
# Add animation: move from top center to 200px below the top (entry animation)
def animated_position_in(t):
final_y = 190 # Final Y position
initial_y = -text_with_bg.h # Start off-screen
return ('center', initial_y + (final_y - initial_y) * min(t / 0.7, 1)) # 0.5 seconds for animation
# Add animation: move from 200px below the top to off-screen top (exit animation)
def animated_position_out(t):
start_y = 190 # Start at 200px below the top
final_y = -text_with_bg.h - 10 # Move off-screen top
animation_duration = 0.7 # 0.5 seconds for animation
total_duration = total_audio_duration
# Reverse animation for the last 0.5 seconds
return ('center', start_y + (final_y - start_y) * min((t - (total_duration - animation_duration)) / animation_duration, 1))
# Combine both animations using a lambda function
title_animated_position = lambda t: (animated_position_in(t) if t < total_audio_duration - 0.5 else animated_position_out(t))
# Apply the animated position to the text clip
animated_text1 = text_with_bg.set_position(title_animated_position)
heading_max_text_width = video.w - 2 * title_margin # Maximum width available for the text
title_wrapped_text = wrap_text(title_text, title_font_size, heading_max_text_width, font_path)
title_text_clip = TextClip(title_wrapped_text, fontsize=title_font_size, color='white', font=font_path, align='center').set_duration(total_audio_duration)
# Create a rounded rectangle background for the text (apply margin only in width)
bg_size = (title_text_clip.w + 60, title_text_clip.h + 20) # Add 20px padding for background
bg_color = (0, 0, 255, 255) # Blue background
rounded_rect = create_rounded_rectangle(bg_size, bg_color, radius=15)
background_clip = ImageClip(rounded_rect).set_duration(total_audio_duration)
# Combine text and background
title_text_with_bg = CompositeVideoClip([
background_clip.set_position('center'),
title_text_clip.set_position('center')
]).set_duration(total_audio_duration)
# Add animation: move from top center to 200px below the top
def title_animated_position(t):
final_y = 20 # Final Y position
initial_y = -title_text_with_bg.h # Start off-screen
return ('center', initial_y + (final_y - initial_y)) # 0.5 seconds for animation if want to turn off animation then remove this:- * min(t / 0.5, 1)
animated_text2 = title_text_with_bg.set_position(title_animated_position)
# Combine the background video with all animated images
final_video = CompositeVideoClip([background_video] + animated_images + [animated_text2, animated_text1])
# Set the final video duration to match the audio's duration (ensures consistency)
final_video = final_video.set_duration(total_audio_duration)
# Write the output video to a file with proper audio codec
output_path = "/Users/jitendersingh/Downloads/output_for_feature.mp4"
final_video.write_videofile(
output_path,
codec="libx264",
fps=30,
audio_codec="aac" # Ensure compatibility for audio playback
)
print(f"Video with trimmed background and audio completed! Output saved to: {output_path}")
# Add combined audio to the video
subprocess.run([
"ffmpeg", "-i", output_path, "-i", combined_audio_path,
"-c:v", "copy", "-map", "0:v:0", "-map", "1:a:0", output_video_path
], check=True)
0 comments:
Post a Comment