UNPKG

rss2microblog

Version:

A Node.js application that automatically crossposts RSS feed items to multiple social media platforms

97 lines (79 loc) 2.83 kB
// src/utils/imageProcessor.js const sharp = require('sharp'); class ImageProcessor { static MAX_FILE_SIZE = 800 * 1024; // 800KB in bytes static TARGET_QUALITY = 90; // Maintain good quality static async optimizeImage(buffer) { // First, get image metadata const metadata = await sharp(buffer).metadata(); // Estimate output size based on dimensions and format const estimatedSize = this.estimateFileSize(metadata); // Calculate target dimensions if needed const { width, height } = this.calculateOptimalDimensions( metadata.width, metadata.height, estimatedSize ); // Process image let processedImage = sharp(buffer) .jpeg({ quality: this.TARGET_QUALITY }) // Convert to JPEG .withMetadata(); // Preserve metadata // Resize if necessary if (width < metadata.width) { processedImage = processedImage.resize(width, height, { fit: 'inside', withoutEnlargement: true }); } // Get the processed buffer const outputBuffer = await processedImage.toBuffer(); // If still too large, try progressive reduction if (outputBuffer.length > this.MAX_FILE_SIZE) { return this.progressiveReduction(buffer); } return outputBuffer; } static estimateFileSize(metadata) { // Rough estimation based on dimensions and color depth const bitsPerPixel = 24; // For JPEG const compressionRatio = 0.7; // Typical JPEG compression return (metadata.width * metadata.height * bitsPerPixel / 8) * compressionRatio; } static calculateOptimalDimensions(width, height, estimatedSize) { if (estimatedSize <= this.MAX_FILE_SIZE) { return { width, height }; } const ratio = Math.sqrt(this.MAX_FILE_SIZE / estimatedSize); return { width: Math.floor(width * ratio), height: Math.floor(height * ratio) }; } static async progressiveReduction(buffer) { const sizes = [2048, 1600, 1200, 1024, 800]; for (const maxDimension of sizes) { try { console.log(`Attempting dimension: ${maxDimension}`); const processed = await sharp(buffer) .resize(maxDimension, maxDimension, { fit: 'inside', withoutEnlargement: true }) .jpeg({ quality: this.TARGET_QUALITY }) .toBuffer(); if (processed.length <= this.MAX_FILE_SIZE) { return processed; } } catch (error) { console.error(`Failed at dimension ${maxDimension}:`, error); } } // Final fallback with more aggressive compression console.log('Using final fallback compression'); return sharp(buffer) .resize(800, 800, { fit: 'inside' }) .jpeg({ quality: 80 }) .toBuffer(); } } module.exports = ImageProcessor;