Spaces:
Running
Running
| 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 | |
| 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)}") |