UNPKG

mcp-image-server

Version:

Servidor MCP para geração de imagens e ícones, integrado ao Vibecoding.

96 lines (95 loc) 3.72 kB
import OpenAI from 'openai'; import sharp from 'sharp'; import fs from 'fs/promises'; import path from 'path'; export class ImageGenerationService { openai; outputDir; constructor() { this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY || '' }); this.outputDir = process.env.OUTPUT_DIR || './generated-assets'; this.ensureOutputDir(); } async ensureOutputDir() { try { await fs.mkdir(this.outputDir, { recursive: true }); } catch (error) { console.error('Erro ao criar diretório de saída:', error); } } async generateImage(options) { const { prompt, width = 512, height = 512, format = 'png', quality = 80, style, background } = options; const enhancedPrompt = this.enhancePrompt(prompt, style, background); try { const response = await this.openai.images.generate({ model: 'dall-e-3', prompt: enhancedPrompt, size: this.getValidSize(width, height), quality: 'standard', response_format: 'url' }); const imageUrl = response?.data && Array.isArray(response.data) && response.data[0]?.url ? response.data[0].url : undefined; if (!imageUrl) throw new Error('Nenhuma URL de imagem retornada pela OpenAI'); const imageBuffer = await this.downloadImage(imageUrl); const processedBuffer = await this.processImage(imageBuffer, { width, height, format, quality, background }); const filename = `image_${Date.now()}.${format}`; const filePath = path.join(this.outputDir, filename); await fs.writeFile(filePath, processedBuffer); return { filePath: path.resolve(filePath), filename, format, width, height }; } catch (error) { throw new Error(`Falha na geração da imagem: ${error.message}`); } } enhancePrompt(prompt, style, background) { let enhanced = prompt; if (style) enhanced += `, ${style} style`; if (background && background !== 'transparent') enhanced += `, ${background} background`; else if (background === 'transparent') enhanced += ', transparent background, isolated subject'; enhanced += ', high quality, clean, professional'; return enhanced; } getValidSize(width, height) { const aspectRatio = width / height; if (aspectRatio > 1.5) return '1792x1024'; if (aspectRatio < 0.7) return '1024x1792'; return '1024x1024'; } async downloadImage(url) { const response = await fetch(url); return Buffer.from(await response.arrayBuffer()); } async processImage(buffer, options) { let image = sharp(buffer); if (options.width !== 1024 || options.height !== 1024) { image = image.resize(options.width, options.height, { fit: 'contain' }); } if (options.background && options.background !== 'transparent') { image = image.flatten({ background: options.background }); } switch (options.format) { case 'jpeg': return await image.jpeg({ quality: options.quality }).toBuffer(); case 'webp': return await image.webp({ quality: options.quality }).toBuffer(); case 'png': default: return await image.png({ quality: options.quality }).toBuffer(); } } }