Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save drorhilman/6f92e0d6dd24e4876298347d6cf7c94c to your computer and use it in GitHub Desktop.
Save drorhilman/6f92e0d6dd24e4876298347d6cf7c94c to your computer and use it in GitHub Desktop.
import os
import subprocess
from moviepy.editor import VideoFileClip
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
from tqdm import tqdm
import imageio
from rich.progress import track, SpinnerColumn, Progress
from rich import print
def convert_video_to_gif(input_file='input.mp4', output_file='output.gif', fps=10, scale=0.75, start_time=None, end_time=None, quality=5):
"""
Convert an MP4 video to a GIF with options for quality, resolution, and length.
Args:
input_file (str): Path to the input MP4 file.
output_file (str): Path to the output GIF file.
fps (int): Frames per second for the output GIF.
scale (float): Scale factor for resizing (e.g., 0.5 for half size).
start_time (float): Start time in seconds for trimming the video.
end_time (float): End time in seconds for trimming the video.
quality (int): Quality of the output GIF (1-10, higher is better quality).
"""
try:
with VideoFileClip(input_file) as video:
# Trim video if start_time and end_time are specified
if start_time is not None or end_time is not None:
video = video.subclip(start_time, end_time)
# Resize video to specified scale
video = video.resize(scale if scale is not None else 1.0)
frames = []
# Calculate total frames and ensure it does not lead to an overly large number
total_frames = min(int(video.duration * fps), 10000) # Limit to 10,000 frames to avoid memory overload
# Use rich's track to display progress bar during frame extraction
for i, frame in track(enumerate(video.iter_frames(fps=fps, dtype='uint8')), total=total_frames, description="Processing frames"):
if i >= total_frames:
break
frames.append(frame)
# Use a spinner to indicate progress during GIF creation
with Progress(SpinnerColumn(), transient=True) as progress:
print(f"[cyan]Creating GIF...")
task = progress.add_task("[cyan]Creating GIF...", total=None)
# Write frames to GIF using imageio with further compression options
# Set palettesize to 32 for better compression and optimize with quantizer='median_cut'
imageio.mimsave(output_file, frames, fps=fps, palettesize=32, loop=0, quantizer='median_cut', quality=quality)
progress.update(task, completed=True)
# Further compress the GIF using gifsicle
optimized_output_file = f"optimized_{output_file}"
subprocess.run(["gifsicle", "--optimize=3", "--colors", "32", output_file, "-o", optimized_output_file])
print(f"[green]Optimized GIF saved to {optimized_output_file}[/green]")
except FileNotFoundError:
print(f"[red]Error: File '{input_file}' not found. Please check the file path.[/red]")
except ValueError as e:
print(f"[red]Error: Invalid value encountered. Details: {e}[/red]")
except OSError as e:
print(f"[red]Error: Unable to open video file '{input_file}'. Please check the file format and path.\nDetails: {e}[/red]")
except Exception as e:
print(f"[red]An unexpected error occurred: {e}[/red]")
def main():
input_file = 'input.mp4'
output_file = 'output.gif'
fps = 8 # Lower fps to reduce size
scale = 0.75
start_time = None
end_time = None
quality = 4 # Lower quality for compression
if not os.path.isfile(input_file):
print(f"[red]Error: File '{input_file}' not found.[/red]")
return
convert_video_to_gif(
input_file=input_file,
output_file=output_file,
fps=fps,
scale=scale,
start_time=start_time,
end_time=end_time,
quality=quality
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment