Created
August 8, 2026 10:04
-
-
Save weshford/a7b71db3350732b9bce387af4ae390d6 to your computer and use it in GitHub Desktop.
small python script to convert png jpgs etc to avif (in parallel hopefully). for me, this reduced filesizes from ~20GB to 7,9GB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import os | |
| from pathlib import Path | |
| from multiprocessing import Pool, cpu_count | |
| from PIL import Image | |
| import pillow_avif # noqa: F401 | |
| from tqdm import tqdm | |
| SOURCE = Path("/home/sonic/Desktop/alte") | |
| DEST = Path("/home/sonic/Desktop/hallo") | |
| WORKERS = 12 # your CPU cores | |
| def find_images(): | |
| files = [] | |
| for ext in ("*.jpg", "*.jpeg", "*.png", "*.JPG", "*.JPEG", "*.PNG"): | |
| files.extend(SOURCE.rglob(ext)) | |
| return files | |
| def convert_file(path): | |
| try: | |
| relative = path.relative_to(SOURCE) | |
| output = DEST / relative.with_suffix(".avif") | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| # Skip already converted files | |
| if output.exists(): | |
| return ("skipped", str(path)) | |
| with Image.open(path) as img: | |
| img = img.convert("RGB") | |
| img.save( | |
| output, | |
| "AVIF", | |
| quality=85, | |
| speed=6 | |
| ) | |
| return ("success", str(path)) | |
| except Exception as e: | |
| return ("failed", f"{path}: {e}") | |
| def main(): | |
| DEST.mkdir(parents=True, exist_ok=True) | |
| images = find_images() | |
| if not images: | |
| print("No images found.") | |
| return | |
| total = len(images) | |
| print(f"Found {total} images") | |
| print(f"Using {WORKERS} CPU workers") | |
| print() | |
| success = 0 | |
| failed = 0 | |
| skipped = 0 | |
| with Pool(WORKERS) as pool: | |
| with tqdm( | |
| total=total, | |
| desc="Converting", | |
| unit="img", | |
| dynamic_ncols=True | |
| ) as bar: | |
| for result, info in pool.imap_unordered(convert_file, images): | |
| if result == "success": | |
| success += 1 | |
| elif result == "failed": | |
| failed += 1 | |
| print("\nFAILED:", info) | |
| elif result == "skipped": | |
| skipped += 1 | |
| bar.update(1) | |
| bar.set_postfix( | |
| ok=success, | |
| fail=failed, | |
| skip=skipped | |
| ) | |
| print("\n========== Finished ==========") | |
| print(f"Total: {total}") | |
| print(f"Successful: {success}") | |
| print(f"Failed: {failed}") | |
| print(f"Skipped: {skipped}") | |
| print(f"Remaining: {total - success - failed - skipped}") | |
| print("==============================") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment