116 lines
3.7 KiB
Python
116 lines
3.7 KiB
Python
# 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)
|