File size: 5,118 Bytes
dde1cc7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 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)
|