"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.coverRoutes = void 0; const express_1 = require("express"); const multer_1 = __importDefault(require("multer")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const sharp_1 = __importDefault(require("sharp")); const axios_1 = __importDefault(require("axios")); exports.coverRoutes = (0, express_1.Router)(); // Configure multer for file uploads const storage = multer_1.default.diskStorage({ destination: (req, file, cb) => { const uploadDir = path_1.default.join(process.cwd(), 'uploads', 'covers'); if (!fs_1.default.existsSync(uploadDir)) { fs_1.default.mkdirSync(uploadDir, { recursive: true }); } cb(null, uploadDir); }, filename: (req, file, cb) => { const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`; cb(null, `cover-${uniqueSuffix}${path_1.default.extname(file.originalname)}`); } }); const upload = (0, multer_1.default)({ storage, limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit fileFilter: (req, file, cb) => { const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('Invalid file type. Only JPEG, PNG, and WEBP are allowed.')); } } }); // Upload a cover image exports.coverRoutes.post('/upload', upload.single('image'), (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } res.json({ id: path_1.default.basename(req.file.filename, path_1.default.extname(req.file.filename)), filename: req.file.filename, path: req.file.path, url: `/uploads/covers/${req.file.filename}`, size: req.file.size }); } catch (error) { console.error('Upload error:', error); res.status(500).json({ error: 'Failed to upload image', details: error instanceof Error ? error.message : String(error) }); } }); // Get all covers exports.coverRoutes.get('/', (req, res) => { try { const coversDir = path_1.default.join(process.cwd(), 'uploads', 'covers'); if (!fs_1.default.existsSync(coversDir)) { return res.json({ covers: [] }); } const files = fs_1.default.readdirSync(coversDir) .filter(file => /\.(jpg|jpeg|png|webp)$/i.test(file)) .map(file => ({ id: path_1.default.basename(file, path_1.default.extname(file)), filename: file, url: `/uploads/covers/${file}`, createdAt: fs_1.default.statSync(path_1.default.join(coversDir, file)).mtime })) .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); res.json({ covers: files }); } catch (error) { console.error('List covers error:', error); res.status(500).json({ error: 'Failed to list covers', details: error instanceof Error ? error.message : String(error) }); } }); // Delete a cover exports.coverRoutes.delete('/:id', (req, res) => { try { const coverPath = path_1.default.join(process.cwd(), 'uploads', 'covers', `${req.params.id}*`); const coversDir = path_1.default.join(process.cwd(), 'uploads', 'covers'); if (!fs_1.default.existsSync(coversDir)) { return res.status(404).json({ error: 'Cover not found' }); } const files = fs_1.default.readdirSync(coversDir) .filter(f => f.startsWith(`${req.params.id}`)); if (files.length === 0) { return res.status(404).json({ error: 'Cover not found' }); } files.forEach(file => { fs_1.default.unlinkSync(path_1.default.join(coversDir, file)); }); res.json({ message: 'Cover deleted successfully' }); } catch (error) { console.error('Delete error:', error); res.status(500).json({ error: 'Failed to delete cover', details: error instanceof Error ? error.message : String(error) }); } }); // Process cover image (resize, filter, etc.) exports.coverRoutes.post('/process', upload.single('image'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } const { width, height, filter, brightness, contrast } = req.body; let pipeline = (0, sharp_1.default)(req.file.path); // Resize if dimensions provided if (width && height) { pipeline = pipeline.resize(parseInt(width), parseInt(height), { fit: 'cover' }); } // Apply filters if (filter === 'grayscale') { pipeline = pipeline.grayscale(); } else if (filter === 'sepia') { pipeline = pipeline.modulate({ saturation: 0, brightness: 1.1 }); } // Adjust brightness and saturation (sharp doesn't have contrast) if (brightness) { pipeline = pipeline.modulate({ brightness: parseFloat(brightness) }); } if (contrast) { pipeline = pipeline.modulate({ saturation: parseFloat(contrast) }); } const processedBuffer = await pipeline.toBuffer(); const outputPath = path_1.default.join(process.cwd(), 'uploads', 'covers', `processed-${req.file.filename}`); await pipeline.toFile(outputPath); res.json({ id: `processed-${path_1.default.basename(req.file.filename, path_1.default.extname(req.file.filename))}`, filename: `processed-${req.file.filename}`, path: outputPath, url: `/uploads/covers/processed-${req.file.filename}` }); } catch (error) { console.error('Process error:', error); res.status(500).json({ error: 'Failed to process image', details: error instanceof Error ? error.message : String(error) }); } }); // Generate cover image using AI (Pollinations.ai - Free, no API key required) exports.coverRoutes.post('/generate', async (req, res) => { try { const { prompt, genre, language } = req.body; const targetLang = language && language.startsWith('es') ? 'es' : 'en'; if (!prompt) { return res.status(400).json({ error: 'prompt is required' }); } let enhancedPrompt = ''; if (targetLang === 'es') { enhancedPrompt = genre ? `Portada de libro para una novela de ${genre}: ${prompt}, diseño profesional de portada de libro, alta calidad, ilustración detallada, 8k, obra maestra` : `Portada de libro: ${prompt}, diseño profesional de portada de libro, alta calidad, ilustración detallada, 8k, obra maestra`; } else { enhancedPrompt = genre ? `Book cover for a ${genre} novel: ${prompt}, professional book cover design, high quality, detailed illustration, 8k, masterpiece` : `Book cover: ${prompt}, professional book cover design, high quality, detailed illustration, 8k, masterpiece`; } // Use Pollinations.ai free text-to-image API (no API key required) const imageUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(enhancedPrompt)}?width=1024&height=1024&nologo=true&seed=${Date.now()}`; // Fetch the generated image const imageResponse = await axios_1.default.get(imageUrl, { responseType: 'arraybuffer', timeout: 30000 }); const buffer = Buffer.from(imageResponse.data); const uploadsDir = path_1.default.join(process.cwd(), 'uploads', 'covers'); if (!fs_1.default.existsSync(uploadsDir)) { fs_1.default.mkdirSync(uploadsDir, { recursive: true }); } const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`; const filename = `generated-${uniqueSuffix}.png`; const filePath = path_1.default.join(uploadsDir, filename); fs_1.default.writeFileSync(filePath, buffer); res.json({ id: `generated-${uniqueSuffix}`, filename, path: filePath, url: `/uploads/covers/${filename}`, generated: true }); } catch (error) { console.error('Image generation error:', error); res.status(500).json({ error: 'Failed to generate image', details: error instanceof Error ? error.message : String(error) }); } }); //# sourceMappingURL=covers.js.map