|
1 | 1 | import concurrent.futures |
| 2 | +from concurrent.futures import Future |
2 | 3 | from pathlib import Path |
3 | 4 |
|
4 | 5 | from wand.image import Image |
@@ -43,38 +44,53 @@ def convert_image_to_avif(image_path: Path, target_path: Path | None = None): |
43 | 44 |
|
44 | 45 |
|
45 | 46 | def clone_and_resize_image( |
46 | | - image: Image, target_width: int, resized_image_destination: Path |
| 47 | + image_path: Path, target_width: int, resized_image_destination: Path |
47 | 48 | ): |
48 | 49 | if resized_image_destination.exists(): |
49 | 50 | return |
50 | | - image.transform(resize=str(target_width)) |
51 | | - image.save(filename=resized_image_destination) |
| 51 | + with Image(filename=str(image_path)) as image: |
| 52 | + image.transform(resize=str(target_width)) |
| 53 | + image.save(filename=resized_image_destination) |
52 | 54 |
|
53 | 55 |
|
54 | 56 | async def generate_images_for_srcset(image_path: Path): |
55 | 57 | BUILD_DIR = get_build_directory() |
56 | 58 | CONTENT_DIR = get_content_directory() |
57 | | - filepaths_to_convert_to_avif = [] |
| 59 | + image_futures: list[Future] = [] |
58 | 60 |
|
59 | 61 | build_path = BUILD_DIR / image_path.resolve().relative_to(CONTENT_DIR) |
60 | 62 | with concurrent.futures.ThreadPoolExecutor() as executor: |
61 | | - with Image(filename=str(image_path)) as img: |
62 | | - width = img.width |
63 | | - |
64 | | - for target_width in get_widths_for_image_width(width): |
65 | | - new_filepath = add_image_width_to_path(image_path, target_width) |
66 | | - relative_filepath = new_filepath.resolve().relative_to(CONTENT_DIR) |
67 | | - build_filepath = BUILD_DIR / relative_filepath |
68 | | - # We convert the resized images to AVIF, so do this synchronously |
69 | | - clone_and_resize_image(img, target_width, build_filepath) |
70 | | - filepaths_to_convert_to_avif.append(build_filepath) |
71 | | - |
72 | 63 | # Convert original image |
73 | | - executor.submit( |
| 64 | + full_sized_avif_future = executor.submit( |
74 | 65 | convert_image_to_avif, image_path=image_path, target_path=build_path |
75 | 66 | ) |
76 | | - # Generate AVIF files for resized images |
77 | | - executor.map(convert_image_to_avif, filepaths_to_convert_to_avif) |
| 67 | + image_futures.append(full_sized_avif_future) |
| 68 | + |
| 69 | + # Store resized image futures so AVIF versions can be created once they're done |
| 70 | + resize_future_to_filepath: dict[Future, Path] = {} |
| 71 | + |
| 72 | + width = Image(filename=str(image_path)).width |
| 73 | + |
| 74 | + for target_width in get_widths_for_image_width(width): |
| 75 | + new_filepath = add_image_width_to_path(image_path, target_width) |
| 76 | + new_filepath_in_build = BUILD_DIR / new_filepath.relative_to(CONTENT_DIR) |
| 77 | + |
| 78 | + resized_original_image_type_future = executor.submit( |
| 79 | + clone_and_resize_image, image_path, target_width, new_filepath_in_build |
| 80 | + ) |
| 81 | + resize_future_to_filepath[ |
| 82 | + resized_original_image_type_future |
| 83 | + ] = new_filepath_in_build |
| 84 | + |
| 85 | + # Create AVIF versions of resized images as they're ready |
| 86 | + for future in concurrent.futures.as_completed(resize_future_to_filepath): |
| 87 | + resized_image_filepath = resize_future_to_filepath[future] |
| 88 | + resized_avif_future = executor.submit( |
| 89 | + convert_image_to_avif, resized_image_filepath |
| 90 | + ) |
| 91 | + image_futures.append(resized_avif_future) |
| 92 | + |
| 93 | + concurrent.futures.wait(image_futures + list(resize_future_to_filepath.keys())) |
78 | 94 |
|
79 | 95 |
|
80 | 96 | def get_widths_for_image_width(image_width: int) -> list[int]: |
|
0 commit comments