""" Art Providers - Museu, Galeria e Geração de Imagens """ import requests import logging from typing import Dict, Optional, List logger = logging.getLogger("ArtProviders") class ArtProviders: """Gerencia múltiplos provedores de arte e criatividade""" API_TIMEOUT = 5.0 @staticmethod def search_metropolitan_museum(query: str, max_results: int = 5) -> Optional[Dict]: """ Busca no acervo do Museu Metropolitano (470k+ obras) Sem autenticação necessária! """ try: logger.info(f"🖼️ Buscando no Met Museum por '{query}'") # Busca IDs de objetos search_url = "https://collectionapi.metmuseum.org/public/collection/v1/search" search_params = { "q": query, "hasImages": "true", "isPublicDomain": "true" } response = requests.get(search_url, params=search_params, timeout=ArtProviders.API_TIMEOUT) if response.status_code != 200: logger.warning(f"❌ Met Museum search retornou {response.status_code}") return None data = response.json() object_ids = data.get("objectIDs", [])[:max_results] if not object_ids: logger.warning(f"❌ Nenhuma obra encontrada para '{query}'") return None # Busca detalhes das obras obras = [] for obj_id in object_ids[:max_results]: try: obj_url = f"https://collectionapi.metmuseum.org/public/collection/v1/objects/{obj_id}" obj_response = requests.get(obj_url, timeout=ArtProviders.API_TIMEOUT) if obj_response.status_code == 200: obj_data = obj_response.json() obras.append({ "titulo": obj_data.get("title", "Desconhecido"), "artista": obj_data.get("artistDisplayName", "Artista desconhecido"), "ano": obj_data.get("objectDate", "Data desconhecida"), "tecnica": obj_data.get("medium", "Técnica desconhecida"), "url_imagem": obj_data.get("primaryImage"), "url_met": obj_data.get("objectURL"), "descricao": obj_data.get("creditLine", ""), "departamento": obj_data.get("department", "") }) except Exception as e: logger.debug(f"Erro ao buscar objeto {obj_id}: {e}") continue return { "sucesso": True, "tipo": "museum_search", "query": query, "obras": obras, "total": len(obras), "fonte": "met_museum" } except requests.Timeout: logger.warning("⏱️ Met Museum timeout") return None except Exception as e: logger.error(f"💥 Erro em Met Museum: {e}") return None @staticmethod def generate_with_pollinations(prompt: str, style: str = None) -> Optional[Dict]: """ Gera imagem com Pollinations AI (fallback para Flux) Vantagem: Sem autenticação, resposta direta (binary) Desvantagem: Menos controle, qualidade variável """ try: logger.info(f"🤖 Gerando imagem com Pollinations AI: {prompt}") # Pollinations retorna imagem diretamente como URL # Devemos usar endpoint especial para json api_url = "https://api.pollinations.ai/v1/images/generations" payload = { "prompt": prompt, "model": "flux", "width": 1024, "height": 1024 } response = requests.post( api_url, json=payload, timeout=ArtProviders.API_TIMEOUT ) if response.status_code != 200: logger.warning(f"❌ Pollinations retornou {response.status_code}") return None data = response.json() # Pollinations retorna URL da imagem gerada if "data" in data and len(data["data"]) > 0: return { "sucesso": True, "tipo": "image_generation", "prompt": prompt, "url_imagem": data["data"][0].get("url"), "modelo": "flux", "fonte": "pollinations_ai" } return None except requests.Timeout: logger.warning("⏱️ Pollinations timeout") return None except Exception as e: logger.error(f"💥 Erro em Pollinations: {e}") return None @staticmethod def generate_simple_ascii_art(theme: str) -> Dict: """ Fallback: ASCII art simples Quando tudo falha, pelo menos retorna algo criativo """ ascii_arts = { "cat": """ /\\_/\\ ( o.o ) > ^ < /| |\\ | | """, "heart": """ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️❤️ """, "rocket": """ /\\ / \\ / \\ /______\\ | | /| |\\ / | | \\ / |______| \\ / /\\ \\ / / \\ \\ (_/ \\_) """, "tree": """ * /|\\ | /*\\ / \\ | /**\\ / \\ | /***\\ / \\ || /||\\ /_||_\\ """ } art = ascii_arts.get(theme, ascii_arts["cat"]) return { "sucesso": True, "tipo": "ascii_art", "tema": theme, "arte": art, "fonte": "fallback_ascii" }