import sharp from "sharp"; import { getMinioClient } from "./minio"; import { lookup } from "mime-types"; export async function processImage( bucket: string, key: string ): Promise { const minio = getMinioClient(); // Get metadata const stat = await minio.statObject(bucket, key); const meta = stat as unknown as { metaData: Record }; const mime = meta.metaData["content-type"] || lookup(key) || ""; // Skip if not an image if (!mime.startsWith("image/")) { console.log(`⏭️ Skipping non-image file: ${key}`); return false; } // Skip if already processed if ( meta.metaData["x-amz-meta-processed"] === "true" || meta.metaData["processed"] === "true" ) { console.log(`♻️ Already processed: ${key}`); return false; } // Read original image const stream = await minio.getObject(bucket, key); const chunks: Buffer[] = []; for await (const chunk of stream) chunks.push(chunk); const buffer = Buffer.concat(chunks); const fileName = key.split("/").pop(); const filePath = key.substring(0, key.lastIndexOf("/")); const processedMeta = { "x-amz-meta-processed": "true", }; // Helper function to write to MinIO async function writeImage(path: string, buffer: Buffer, mimeType: string) { await minio.putObject(bucket, path, buffer, buffer.length, { "Content-Type": mimeType, ...processedMeta, }); } try { // 🖼️ Create thumbnail const thumb = await sharp(buffer).resize(200).toBuffer(); await writeImage(`${filePath}/thumbs/${fileName}`, thumb, mime); // 📸 Optimized JPEG const optimized = await sharp(buffer).jpeg({ quality: 80 }).toBuffer(); await writeImage( `${filePath}/optimized/${fileName}`, optimized, "image/jpeg" ); // 🌐 WebP variant const webpName = fileName?.replace(/\.[^/.]+$/, ".webp"); const webp = await sharp(buffer).webp({ quality: 80 }).toBuffer(); await writeImage(`${filePath}/webp/${webpName}`, webp, "image/webp"); // (Optional: AVIF format - super modern) // const avifName = fileName?.replace(/\.[^/.]+$/, ".avif"); // const avif = await sharp(buffer).avif({ quality: 50 }).toBuffer(); // await writeImage(`${filePath}/avif/${avifName}`, avif, "image/avif"); // 🔁 Re-upload original with metadata to mark as processed await writeImage(key, buffer, mime); console.log(`✅ Processed image: ${key}`); return true; } catch (err) { console.error(`❌ Error processing image (${key}):`, err); return false; } }