# processors/image_normalizer.py import os from core.constants import IMAGE_EXTENSIONS from core.result import StepResult def preview_normalize_images(work_dir: str, target_ext: str) -> list[tuple[str, str]]: """Devuelve lista de (nombre_actual, nombre_final) sin convertir nada.""" target_ext = target_ext.lower() if not target_ext.startswith("."): target_ext = "." + target_ext result = [] for root, _, files in os.walk(work_dir): for f in files: ext = os.path.splitext(f)[1].lower() normalized_ext = ".jpg" if ext == ".jpeg" else ext if ext not in IMAGE_EXTENSIONS: continue if normalized_ext == target_ext: continue stem = os.path.splitext(f)[0] result.append((f, stem + target_ext)) return result def normalize_images(work_dir: str, target_ext: str = ".jpg") -> StepResult: """ Convierte todas las imágenes al formato indicado por target_ext. Requiere Pillow. Si no está instalado, devuelve un StepResult con error. """ try: from PIL import Image except ImportError: return StepResult( step="normalize_images", changed=False, errors=["Pillow no instalado. Ejecuta: pip install Pillow"], ) target_ext = target_ext.lower() if not target_ext.startswith("."): target_ext = "." + target_ext # Mapa de extensión a formato PIL FORMAT_MAP = { ".jpg": "JPEG", ".jpeg": "JPEG", ".png": "PNG", ".webp": "WEBP", } pil_format = FORMAT_MAP.get(target_ext) if pil_format is None: return StepResult( step="normalize_images", changed=False, errors=[f"Formato de imagen no soportado: {target_ext}"], ) changed = False details = [] for root, _, files in os.walk(work_dir): for f in files: ext = os.path.splitext(f)[1].lower() normalized_ext = ".jpg" if ext == ".jpeg" else ext if ext not in IMAGE_EXTENSIONS: continue if normalized_ext == target_ext: continue src = os.path.join(root, f) stem = os.path.splitext(f)[0] dst = os.path.join(root, stem + target_ext) with Image.open(src) as img: # Convertir modos incompatibles con JPEG if pil_format == "JPEG" and img.mode in ("RGBA", "P", "LA"): img = img.convert("RGB") img.save(dst, format=pil_format) os.remove(src) details.append(f"{f} → {stem + target_ext}") changed = True return StepResult(step="normalize_images", changed=changed, details=details) def _detected_formats(work_dir: str) -> set[str]: """Devuelve el conjunto de extensiones de imagen presentes en work_dir (normalizando .jpeg → .jpg).""" formats = set() for fname in os.listdir(work_dir): ext = os.path.splitext(fname)[1].lower() if ext == ".jpeg": ext = ".jpg" if ext in IMAGE_EXTENSIONS: formats.add(ext) return formats def preview_uniformize_images(work_dir: str, target_ext: str) -> list[tuple[str, str]]: """Como preview_normalize_images pero devuelve [] si las imágenes ya son uniformes.""" if len(_detected_formats(work_dir)) <= 1: return [] return preview_normalize_images(work_dir, target_ext) def uniformize_images(work_dir: str, target_ext: str = ".jpg") -> StepResult: """Como normalize_images pero solo actúa si hay formatos mixtos.""" if len(_detected_formats(work_dir)) <= 1: return StepResult(step="normalize_images", changed=False) return normalize_images(work_dir, target_ext)