from __future__ import annotations from imagepipeline.core.context import ModuleContext from imagepipeline.core.params import Param from imagepipeline.modules.base import SubprocessModule from imagepipeline.modules.registry import register from imagepipeline.utils.subprocess import run_command def build_resize_arguments(*, max_edge: int) -> list[str]: if max_edge <= 0: raise ValueError("max_edge must be positive") return ["-auto-orient", "-resize", f"{max_edge}x{max_edge}>"] @register class ImageMagickResizeModule(SubprocessModule): name = "imagemagick_resize" description = ( "Resize images so the longer side is at most max_edge pixels " "(aspect ratio preserved; never upscales)" ) command_candidates = ("magick", "convert") @classmethod def parameters(cls) -> dict[str, Param]: return { "max_edge": Param( "int", default=2000, help="Maximum length of the longer side in pixels", ), } def run(self, ctx: ModuleContext) -> None: command = self.resolve_command() max_edge = ctx.params["max_edge"] resize_args = build_resize_arguments(max_edge=max_edge) ctx.output_dir.mkdir(parents=True, exist_ok=True) total = len(ctx.input_paths) for index, src in enumerate(ctx.input_paths, start=1): self.log_image(ctx, index, total, src) dst = ctx.output_dir / src.name run_command([command, str(src), *resize_args, str(dst)])