/ scripts / responsive_image_generator.py
responsive_image_generator.py
 1  # /// script
 2  # requires-python: ">=3.11"
 3  # dependencies = [
 4  #   "pillow",
 5  #   "tyro",
 6  #   "thumbhash-python",
 7  # ]
 8  # ///
 9  
10  
11  from PIL import Image, ImageOps
12  from pathlib import Path
13  import base64
14  from io import BytesIO
15  import tyro
16  from thumbhash import image_to_thumbhash, thumbhash_to_image
17  
18  
19  def generate_responsive_images(input_image_path: Path):
20      # Define the sizes and suffixes
21      sizes = [(320, "-small"), (640, "-medium"), (1024, "-large"), (1920, "-xlarge")]
22  
23      # Create opt directory if it doesn't exist
24      opt_dir = input_image_path.parent / "opt"
25      opt_dir.mkdir(exist_ok=True)
26  
27      # Extract the base filename
28      base_name = input_image_path.stem
29      ext = input_image_path.suffix
30  
31      width = 200
32      new_height = 200
33  
34      # Process each size
35      for width, suffix in sizes:
36          # Open the original image
37          with Image.open(input_image_path) as img:
38              img = ImageOps.exif_transpose(img) or img
39              # Calculate the new height maintaining the aspect ratio
40              aspect_ratio = img.height / img.width
41              new_height = int(width * aspect_ratio)
42  
43              # Resize the image
44              resized_img = img.resize((width, new_height), Image.Resampling.LANCZOS)
45  
46              # Save the resized image in opt directory (JPEG)
47              resized_image_path = opt_dir / f"{base_name}{suffix}{ext}"
48              resized_img.save(resized_image_path)
49              print(f"Saved resized image: {resized_image_path}")
50  
51              # Save WebP version
52              webp_path = opt_dir / f"{base_name}{suffix}.webp"
53              resized_img.save(webp_path, format="WEBP", quality=85, method=6)
54              print(f"Saved WebP image: {webp_path}")
55  
56      # thumbhash
57      thumbhash = image_to_thumbhash(str(input_image_path))
58      thumbhash_image = thumbhash_to_image(thumbhash)
59      thumbhash_path = opt_dir / f"{base_name}-thumbhash.png"
60      thumbhash_image.save(thumbhash_path)
61  
62      # Convert the thumbhash image to Base64
63      buffered = BytesIO()
64      thumbhash_image.save(buffered, format="PNG")
65  
66      # Generate the Jekyll template insertion code
67      template_code = f"""
68  {{% include responsive_image.html base_image_name="{base_name}" alt="Your Alt Text Here" 
69      width="{width}" height="{new_height}" %}}
70  """
71      print("\nJekyll template insertion code:\n")
72      print(template_code)
73  
74  
75  def main(in_path: str):
76      input_path = Path(in_path)
77      if input_path.is_file():
78          generate_responsive_images(input_path)
79      elif input_path.is_dir():
80          for file in input_path.iterdir():
81              if file.is_file() and file.suffix.lower() in [".jpg", ".jpeg", ".png"]:
82                  generate_responsive_images(file)
83  
84  
85  if __name__ == "__main__":
86      tyro.cli(main)