Skip to content

Instantly share code, notes, and snippets.

@ScribbleGhost
Last active August 28, 2023 12:18
Show Gist options
  • Save ScribbleGhost/b15084955edd7d9adc0efbda3e42466b to your computer and use it in GitHub Desktop.
Save ScribbleGhost/b15084955edd7d9adc0efbda3e42466b to your computer and use it in GitHub Desktop.
Generate GIFs from either PNG image sequences or video files.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script to generate GIFs from PNG sequences, video files, or folders containing PNGs.
Requirements:
- FFmpeg and Gifski in PATH.
Usage:
1. Drag and drop files/folders onto this script.
2. Follow the on-screen instructions.
3. A GIF will be generated in the script's directory.
"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
# Script version
__version__ = '1.1.0'
def is_tool_available(name):
"""Check if a given tool is available."""
return shutil.which(name) is not None
def is_png(file):
"""Check if a file is a PNG."""
return file.lower().endswith('.png')
def is_video(file):
"""Check if a file is a video."""
exts = ['.mp4', '.avi', '.mkv', '.flv', '.mov', '.wmv', '.webm']
return any(file.lower().endswith(ext) for ext in exts)
def find_png_files(folder):
"""Find all PNG files in a given folder."""
return [str(f) for f in Path(folder).rglob("*.png")]
def generate_gif(files, fps, width, quality, output):
"""Generate a GIF using gifski."""
cmd = [
'gifski', '-o', output, '--fps', str(fps), '--quality', str(quality)
]
if width: # If width is not blank
cmd.extend(['--width', str(width)])
cmd.extend(files)
subprocess.run(cmd)
print(f"Generated {output}")
def process_folder(folder, fps, width, quality):
"""Handle folder input."""
png_files = find_png_files(folder)
if png_files:
folder_name = os.path.basename(folder)
output = f"{folder_name}_gifski-q{quality}.gif"
generate_gif(png_files, fps, width, quality, output)
else:
print(f"No PNG files found in folder {folder}")
def get_common_inputs():
"""Get common inputs for generating GIFs."""
quality = get_integer_input("Enter gifski quality (1-100): ", 1, 100)
fps = get_integer_input("Enter FPS: ", 1, 60)
width = input("Enter width (leave blank for original): ").strip()
return quality, fps, width
def get_integer_input(prompt, min_val=None, max_val=None):
"""Get integer input within the range [min_val, max_val]."""
while True:
try:
value = int(input(prompt).strip())
if (min_val is not None and value < min_val) or (max_val is not None and value > max_val):
print(f"Please enter a value between {min_val} and {max_val}.")
continue
return value
except ValueError:
print("Invalid input. Please enter an integer.")
def main(files):
"""Main function."""
try:
if not (is_tool_available("ffmpeg") and is_tool_available("gifski")):
print("Install ffmpeg and gifski and try again.")
return
# Get common settings for all GIFs
quality, fps, width = get_common_inputs()
for file_or_folder in files:
if os.path.isdir(file_or_folder):
process_folder(file_or_folder, fps, width, quality)
elif is_png(file_or_folder):
# Collect PNGs and process them (existing code)
pass # You can insert your existing PNG processing code here
elif is_video(file_or_folder):
# Process video files (existing code)
pass # You can insert your existing video processing code here
except Exception as e:
print(f"Unexpected error: {e}")
if __name__ == "__main__":
try:
dropped_files = sys.argv[1:]
if not dropped_files:
print("Drag and drop PNG or video files or folders.")
else:
main(dropped_files)
except Exception as e:
print(f"Unexpected error: {e}")
input("Press Enter to continue...")
@ScribbleGhost
Copy link
Author

Example GIF from an 8K webm video. 90% quality and downscaled to 25fps at 720px.
Japan in 8K- 1 Hour Relaxing Aerial Film  qmN1Gf8rRc8  003108280-003112544 gifski-q90

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment