Skip to content

Instantly share code, notes, and snippets.

@ph33nx
Last active July 1, 2025 00:53
Show Gist options
  • Save ph33nx/861d9ff35721d56205c834c7f8308e0b to your computer and use it in GitHub Desktop.
Save ph33nx/861d9ff35721d56205c834c7f8308e0b to your computer and use it in GitHub Desktop.
Batch Convert Images to Web-Optimized JPG or WEBP using Python with EXIF Preservation 🖼️✨ Fast, batch image conversion while preserving EXIF metadata using Python Script
#!/usr/bin/env python3
"""
Author: ph33nx
URL: https://gist.github.com/ph33nx/861d9ff35721d56205c834c7f8308e0b
Description:
This script converts images from various formats (e.g., PNG, JPG, WebP) to either JPG or WebP for web optimization.
It preserves EXIF metadata, allows for optional parameter customization, and handles errors gracefully.
Key Features:
- Converts images to web-optimized JPG or WebP while preserving EXIF metadata.
- Accepts multiple image formats as input.
- Provides optional parameters for output format, metadata preservation, and file cleanup.
- Displays help and examples when no parameters are passed or `-h` is used.
Requirements:
- Python 3.x
- Pillow library for image processing (will auto-install if missing).
Usage:
python image_converter.py <folder_path> [--format=jpg|webp] [--preserve-exif=true|false] [--delete-original=true|false] [--quality=<int>] [--verbose]
Options:
<folder_path> Path to the folder containing images for conversion.
--format=jpg|webp Output format (default: jpg).
--preserve-exif=true|false Preserve EXIF metadata (default: true).
--delete-original=true|false Delete original files after conversion (default: false).
--quality=<int> Output quality for JPG/WebP (default: 85).
--verbose Enable detailed logging.
-h, --help Display this help information.
Example:
python image_converter.py /path/to/images --format=webp --preserve-exif=true --delete-original=true --quality=90 --verbose
"""
import os
import sys
import subprocess
def ensure_pillow_installed():
try:
import PIL
except ImportError:
print("Pillow is not installed. Installing now...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow"])
print("Pillow installed successfully.")
def is_image_file(filename):
# Check for common image extensions
image_extensions = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tiff", ".gif"}
return any(filename.lower().endswith(ext) for ext in image_extensions)
def convert_image(
file_path,
output_format="jpg",
preserve_exif=True,
delete_original=False,
quality=85,
verbose=False,
):
try:
output_ext = output_format.lower()
valid_formats = {"jpg": "JPEG", "webp": "WEBP"}
if output_ext not in valid_formats:
raise ValueError("Unsupported output format. Use 'jpg' or 'webp'.")
output_path = f"{os.path.splitext(file_path)[0]}.{output_ext}"
with Image.open(file_path) as img:
exif_data = (
img.info.get("exif") if preserve_exif and "exif" in img.info else None
)
if exif_data is None and preserve_exif:
if verbose:
print(
f"Warning: No EXIF data found for {file_path}. EXIF preservation skipped."
)
img = img.convert("RGB")
# Save with or without EXIF
if exif_data:
img.save(
output_path,
valid_formats[output_ext],
exif=exif_data,
quality=quality,
)
else:
img.save(output_path, valid_formats[output_ext], quality=quality)
if verbose:
print(f"Converted {file_path} to {output_path}.")
if delete_original:
os.remove(file_path)
if verbose:
print(f"Deleted original file: {file_path}")
except Exception as e:
print(f"Failed to convert {file_path}: {e}")
def process_folder(
folder_path,
output_format="jpg",
preserve_exif=True,
delete_original=False,
quality=85,
verbose=False,
):
if not os.path.isdir(folder_path):
print(f"Error: {folder_path} is not a valid directory.")
return
for root, _, files in os.walk(folder_path):
for file in files:
# Skip hidden files (e.g., files starting with .)
if file.startswith("."):
if verbose:
print(f"Skipping hidden file: {file}")
continue
# Skip non-image files
if not is_image_file(file):
if verbose:
print(f"Skipping non-image file: {file}")
continue
file_path = os.path.join(root, file)
convert_image(
file_path,
output_format,
preserve_exif,
delete_original,
quality,
verbose,
)
if __name__ == "__main__":
ensure_pillow_installed()
from PIL import Image
if "-h" in sys.argv or "--help" in sys.argv or len(sys.argv) < 2:
print(__doc__)
sys.exit(0)
folder_path = sys.argv[1]
output_format = "jpg"
preserve_exif = True
delete_original = False
quality = 85
verbose = False
for arg in sys.argv[2:]:
if arg.startswith("--format="):
output_format = arg.split("=")[1].lower()
elif arg.startswith("--preserve-exif="):
preserve_exif = arg.split("=")[1].lower() == "true"
elif arg.startswith("--delete-original="):
delete_original = arg.split("=")[1].lower() == "true"
elif arg.startswith("--quality="):
try:
quality = int(arg.split("=")[1])
except ValueError:
print("Error: Quality must be an integer.")
sys.exit(1)
elif arg == "--verbose":
verbose = True
try:
process_folder(
folder_path, output_format, preserve_exif, delete_original, quality, verbose
)
except Exception as e:
print(f"An error occurred: {e}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment