from fastapi import APIRouter, Query, HTTPException from fastapi.responses import StreamingResponse from PIL import Image, ImageDraw from io import BytesIO import requests router = APIRouter() def download_image_from_url(url: str) -> Image.Image: headers = { "User-Agent": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/115.0.0.0 Safari/537.36" ) } try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() return Image.open(BytesIO(response.content)).convert("RGBA") except Exception as e: raise HTTPException(status_code=400, detail=f"Erro ao baixar imagem: {url} ({str(e)})") def resize_and_crop_to_fill(img: Image.Image, target_width: int, target_height: int) -> Image.Image: """ Redimensiona e corta a imagem para preencher exatamente o espaço alvo (sempre centralizado). Args: img: Imagem PIL target_width: Largura alvo target_height: Altura alvo """ img_ratio = img.width / img.height target_ratio = target_width / target_height if img_ratio > target_ratio: # Imagem é mais larga proporcionalmente - redimensionar baseado na altura scale_height = target_height scale_width = int(scale_height * img_ratio) else: # Imagem é mais alta proporcionalmente - redimensionar baseado na largura scale_width = target_width scale_height = int(scale_width / img_ratio) img_resized = img.resize((scale_width, scale_height), Image.LANCZOS) # Centralizar o crop left = (scale_width - target_width) // 2 top = (scale_height - target_height) // 2 right = left + target_width bottom = top + target_height return img_resized.crop((left, top, right, bottom)) def add_rounded_corners(img: Image.Image, radius: int) -> Image.Image: """ Adiciona cantos arredondados à imagem. Args: img: Imagem PIL radius: Raio do arredondamento em pixels Returns: Imagem com cantos arredondados """ # Criar máscara para os cantos arredondados mask = Image.new("L", img.size, 0) draw = ImageDraw.Draw(mask) # Desenhar retângulo com cantos arredondados draw.rounded_rectangle( [(0, 0), (img.width, img.height)], radius=radius, fill=255 ) # Aplicar máscara à imagem output = Image.new("RGBA", img.size, (0, 0, 0, 0)) output.paste(img, (0, 0)) output.putalpha(mask) return output def create_cover_image(image_url: str) -> BytesIO: """ Cria uma capa para reels do Instagram. - Fundo preto - Largura: 1080, Altura: 1920 - Imagem renderizada em Largura: 1080, Altura: 1440, X: 0, Y: 240 - Cantos arredondados com raio de 145px """ # Dimensões do canvas canvas_width = 1080 canvas_height = 1920 # Criar canvas com fundo preto canvas = Image.new("RGBA", (canvas_width, canvas_height), color=(0, 0, 0, 255)) # Baixar e processar imagem img = download_image_from_url(image_url) # Redimensionar e cortar imagem para as dimensões especificadas # Largura: 1080, Altura: 1440 img_width = 1080 img_height = 1440 filled_img = resize_and_crop_to_fill(img, img_width, img_height) # Aplicar arredondamento de 145px nos cantos rounded_img = add_rounded_corners(filled_img, 145) # Colar imagem na posição X: 0, Y: 240 canvas.paste(rounded_img, (0, 240), rounded_img) # Converter para bytes buffer = BytesIO() canvas.convert("RGB").save(buffer, format="PNG") buffer.seek(0) return buffer @router.get("/cover/reel") def get_reel_cover( image_url: str = Query(..., description="URL da imagem a ser renderizada") ): """ Endpoint para gerar capa para reels do Instagram. Args: image_url: URL da imagem a ser renderizada (obrigatório) Returns: Imagem PNG com fundo preto e imagem renderizada nas posições especificadas """ try: buffer = create_cover_image(image_url) return StreamingResponse(buffer, media_type="image/png") except Exception as e: raise HTTPException(status_code=500, detail=f"Erro ao gerar imagem: {str(e)}")