ShortsAI / video_editing.py
gamora's picture
files
dde1cc7
Raw
History Blame Contribute Delete
5.12 kB
from moviepy.editor import VideoFileClip, concatenate_videoclips,ColorClip,CompositeVideoClip
from find_steps import find_all_steps
import json
import random
import string
step={
"path" :"/Users/georgia.bucea/products/ShortsAI/21_aug/IMG_8299.MOV",
"start":0.0,
"end": '',
}
def crop_video(step, target_resolution=(1080, 1920), target_fps=30):
"""
Crop a video clip to the specified start and end times, and resize to target resolution
while maintaining the original aspect ratio with padding if necessary.
Args:
step (dict): Dictionary with 'path', 'start', and 'end' keys.
target_resolution (tuple): Desired output resolution (width, height).
target_fps (int): Desired frame rate for the output video.
Returns:
VideoClip: Cropped and resized video clip with preserved audio.
"""
video = VideoFileClip(step["path"])
# Crop the video based on start and end times
if step.get("end"):
video = video.subclip(step["start"], step["end"])
else:
video = video.subclip(step["start"])
# Set consistent frame rate
video = video.set_fps(target_fps)
# Calculate target aspect ratio
target_width, target_height = target_resolution
target_aspect = target_width / target_height
video_aspect = video.w / video.h
# Resize or pad to match target resolution while preserving aspect ratio
if abs(video_aspect - target_aspect) > 0.01: # Allow small tolerance
print("I'm here in abs")
if video_aspect > target_aspect:
# Video is wider than target: scale to match height, add black bars on sides
print("I'm here in video aspect >")
new_width = int(target_height * video_aspect)
video = video.resize(height=target_height)
# Create a background clip with black padding
background = ColorClip(size=(target_width, target_height), color=(0, 0, 0))
video = video.set_position(("center", "center")).on_color(size=(target_width, target_height), color=(0, 0, 0))
else:
print("I'm here in video aspect")
# Video is taller than target: scale to match width, add black bars on top/bottom
new_height = int(target_width / video_aspect)
video = video.resize(width=target_width)
# Create a background clip with black padding
background = ColorClip(size=(target_width, target_height), color=(0, 0, 0))
video = video.set_position(("center", "center")).on_color(size=(target_width, target_height), color=(0, 0, 0))
else:
print("I'm here in video aspect")
# Aspect ratio matches, resize directly
video = video.resize(target_resolution)
# Ensure audio is preserved
if video.audio is None:
print(f"Warning: No audio in {step['path']}")
return video
def get_final_video(subclips, aspect_ratio="9:16"):
"""
Concatenate video clips and save the final video with consistent resolution and audio.
Args:
subclips (list): List of VideoClip objects.
aspect_ratio (str): Desired aspect ratio (e.g., '9:16').
Returns:
str: Path to the saved video file.
"""
# Parse aspect ratio
width_ratio, height_ratio = map(int, aspect_ratio.split(":"))
target_aspect = width_ratio / height_ratio
# Set target resolution (e.g., 1080x1920 for 9:16)
target_height = 1920 # Standard for vertical videos
target_width = int(target_height * target_aspect)
target_resolution = (target_width, target_height)
# Concatenate clips
final_clip = concatenate_videoclips(subclips, method="compose")
# Generate unique filename
random_string = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
file_name = f"edited_videos/video_edited{random_string}.mp4" # Changed to .mp4 for compatibility
# Write the final video file
final_clip.write_videofile(
file_name,
codec='libx264',
audio_codec='aac',
fps=30, # Consistent frame rate
preset='medium',
ffmpeg_params=['-pix_fmt', 'yuv420p', '-aspect', aspect_ratio], # Ensure compatibility with most players
verbose=False,
temp_audiofile=f"temp_audio_{random_string}.m4a", # Explicit temp audio file
)
# Close clips to free memory
final_clip.close()
# for clip in sub:
# clip.close()
return file_name
if __name__ == "__main__":
file_path = '/Users/georgia.bucea/products/ShortsAI/all_files_metadata.json' # Update this with the actual file path
context = """I am putting stuff in a car, with pipes and a guy in a red shirt. I am walking past a band with a DJ. I am explaining something on the phone"""
with open(file_path, 'r') as file:
data = json.load(file)
steps=find_all_steps(data,context)
all_videos=[]
for step in steps:
v=crop_video(step)
all_videos.append(v)
# v=crop_video(step)
get_final_video(all_videos)