""" Servicio de generación de imágenes para posts. """ import os from typing import Dict, Optional from pathlib import Path from html2image import Html2Image from PIL import Image from jinja2 import Environment, FileSystemLoader from app.core.config import settings class ImageGenerator: """Generador de imágenes usando plantillas HTML.""" def __init__(self): self.templates_dir = Path("templates") self.output_dir = Path("uploads/generated") self.output_dir.mkdir(parents=True, exist_ok=True) self.jinja_env = Environment( loader=FileSystemLoader(self.templates_dir) ) self._hti = None @property def hti(self): """Lazy initialization de Html2Image.""" if self._hti is None: try: self._hti = Html2Image( output_path=str(self.output_dir), custom_flags=['--no-sandbox', '--disable-gpu'] ) except FileNotFoundError: raise RuntimeError( "Chrome/Chromium no encontrado. Instala Chrome o ejecuta en Docker." ) return self._hti def _render_template(self, template_name: str, variables: Dict) -> str: """Renderizar una plantilla HTML con variables.""" template = self.jinja_env.get_template(template_name) return template.render(**variables) async def generate_tip_card( self, title: str, content: str, category: str, output_name: str ) -> str: """Generar imagen de tip tech.""" variables = { "title": title, "content": content, "category": category, "logo_url": f"{settings.BUSINESS_WEBSITE}/logo.png", "website": settings.BUSINESS_WEBSITE, "business_name": settings.BUSINESS_NAME } html_content = self._render_template("tip_card.html", variables) # Generar imagen output_file = f"{output_name}.png" self.hti.screenshot( html_str=html_content, save_as=output_file, size=(1080, 1080) ) return str(self.output_dir / output_file) async def generate_product_card( self, name: str, price: float, image_url: str, highlights: list, output_name: str ) -> str: """Generar imagen de producto.""" variables = { "name": name, "price": f"${price:,.2f} MXN", "image_url": image_url, "highlights": highlights[:3], # Máximo 3 highlights "logo_url": f"{settings.BUSINESS_WEBSITE}/logo.png", "website": settings.BUSINESS_WEBSITE, "business_name": settings.BUSINESS_NAME } html_content = self._render_template("product_card.html", variables) output_file = f"{output_name}.png" self.hti.screenshot( html_str=html_content, save_as=output_file, size=(1080, 1080) ) return str(self.output_dir / output_file) async def generate_service_card( self, name: str, tagline: str, benefits: list, icon: str, output_name: str ) -> str: """Generar imagen de servicio.""" variables = { "name": name, "tagline": tagline, "benefits": benefits[:4], # Máximo 4 beneficios "icon": icon, "logo_url": f"{settings.BUSINESS_WEBSITE}/logo.png", "website": settings.BUSINESS_WEBSITE, "business_name": settings.BUSINESS_NAME } html_content = self._render_template("service_card.html", variables) output_file = f"{output_name}.png" self.hti.screenshot( html_str=html_content, save_as=output_file, size=(1080, 1080) ) return str(self.output_dir / output_file) async def resize_for_platform( self, image_path: str, platform: str ) -> str: """Redimensionar imagen para una plataforma específica.""" sizes = { "x": (1200, 675), # 16:9 "threads": (1080, 1080), # 1:1 "instagram": (1080, 1080), # 1:1 "facebook": (1200, 630) # ~1.9:1 } target_size = sizes.get(platform, (1080, 1080)) img = Image.open(image_path) # Crear nueva imagen con el tamaño objetivo new_img = Image.new('RGB', target_size, (26, 26, 46)) # Color de fondo de la marca # Calcular posición para centrar img_ratio = img.width / img.height target_ratio = target_size[0] / target_size[1] if img_ratio > target_ratio: # Imagen más ancha new_width = target_size[0] new_height = int(new_width / img_ratio) else: # Imagen más alta new_height = target_size[1] new_width = int(new_height * img_ratio) img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS) # Centrar en la nueva imagen x = (target_size[0] - new_width) // 2 y = (target_size[1] - new_height) // 2 new_img.paste(img_resized, (x, y)) # Guardar output_path = image_path.replace('.png', f'_{platform}.png') new_img.save(output_path, 'PNG', quality=95) return output_path # Instancia global image_generator = ImageGenerator()