Spaces:
Runtime error
Runtime error
""" | |
Adapted from: | |
https://github.com/genmoai/mochi/blob/main/demos/fine_tuner/trim_and_crop_videos.py | |
""" | |
from pathlib import Path | |
import shutil | |
import click | |
from moviepy.editor import VideoFileClip | |
from tqdm import tqdm | |
def truncate_videos(folder, output_folder, num_frames, resolution, force_upsample): | |
"""Truncate all MP4 and MOV files in FOLDER to specified number of frames and resolution""" | |
input_path = Path(folder) | |
output_path = Path(output_folder) | |
output_path.mkdir(parents=True, exist_ok=True) | |
# Parse target resolution | |
target_height, target_width = map(int, resolution.split("x")) | |
# Calculate duration | |
duration = (num_frames / 30) + 0.09 | |
# Find all MP4 and MOV files | |
video_files = ( | |
list(input_path.rglob("*.mp4")) | |
+ list(input_path.rglob("*.MOV")) | |
+ list(input_path.rglob("*.mov")) | |
+ list(input_path.rglob("*.MP4")) | |
) | |
for file_path in tqdm(video_files): | |
try: | |
relative_path = file_path.relative_to(input_path) | |
output_file = output_path / relative_path.with_suffix(".mp4") | |
output_file.parent.mkdir(parents=True, exist_ok=True) | |
click.echo(f"Processing: {file_path}") | |
video = VideoFileClip(str(file_path)) | |
# Skip if video is too short | |
if video.duration < duration: | |
click.echo(f"Skipping {file_path} as it is too short") | |
continue | |
# Skip if target resolution is larger than input | |
if target_width > video.w or target_height > video.h: | |
if force_upsample: | |
click.echo( | |
f"{file_path} as target resolution {resolution} is larger than input {video.w}x{video.h}. So, upsampling the video." | |
) | |
video = video.resize(width=target_width, height=target_height) | |
else: | |
click.echo( | |
f"Skipping {file_path} as target resolution {resolution} is larger than input {video.w}x{video.h}" | |
) | |
continue | |
# First truncate duration | |
truncated = video.subclip(0, duration) | |
# Calculate crop dimensions to maintain aspect ratio | |
target_ratio = target_width / target_height | |
current_ratio = truncated.w / truncated.h | |
if current_ratio > target_ratio: | |
# Video is wider than target ratio - crop width | |
new_width = int(truncated.h * target_ratio) | |
x1 = (truncated.w - new_width) // 2 | |
final = truncated.crop(x1=x1, width=new_width).resize((target_width, target_height)) | |
else: | |
# Video is taller than target ratio - crop height | |
new_height = int(truncated.w / target_ratio) | |
y1 = (truncated.h - new_height) // 2 | |
final = truncated.crop(y1=y1, height=new_height).resize((target_width, target_height)) | |
# Set output parameters for consistent MP4 encoding | |
output_params = { | |
"codec": "libx264", | |
"audio": False, # Disable audio | |
"preset": "medium", # Balance between speed and quality | |
"bitrate": "5000k", # Adjust as needed | |
} | |
# Set FPS to 30 | |
final = final.set_fps(30) | |
# Check for a corresponding .txt file | |
txt_file_path = file_path.with_suffix(".txt") | |
if txt_file_path.exists(): | |
output_txt_file = output_path / relative_path.with_suffix(".txt") | |
output_txt_file.parent.mkdir(parents=True, exist_ok=True) | |
shutil.copy(txt_file_path, output_txt_file) | |
click.echo(f"Copied {txt_file_path} to {output_txt_file}") | |
else: | |
# Print warning in bold yellow with a warning emoji | |
click.echo( | |
f"\033[1;33m⚠️ Warning: No caption found for {file_path}, using an empty caption. This may hurt fine-tuning quality.\033[0m" | |
) | |
output_txt_file = output_path / relative_path.with_suffix(".txt") | |
output_txt_file.parent.mkdir(parents=True, exist_ok=True) | |
output_txt_file.touch() | |
# Write the output file | |
final.write_videofile(str(output_file), **output_params) | |
# Clean up | |
video.close() | |
truncated.close() | |
final.close() | |
except Exception as e: | |
click.echo(f"\033[1;31m Error processing {file_path}: {str(e)}\033[0m", err=True) | |
raise | |
if __name__ == "__main__": | |
truncate_videos() | |