96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import sharp from "sharp";
|
|
import { getMinioClient } from "./minio";
|
|
import { lookup } from "mime-types";
|
|
|
|
export async function processImage(
|
|
bucket: string,
|
|
key: string
|
|
): Promise<boolean> {
|
|
const minio = getMinioClient();
|
|
|
|
// Fetch metadata
|
|
const stat = await minio.statObject(bucket, key);
|
|
const meta = stat as unknown as { metaData: Record<string, string> };
|
|
const mime = meta.metaData["content-type"] || lookup(key) || "";
|
|
|
|
if (!mime.startsWith("image/")) {
|
|
console.log(`⏭️ Skipping non-image file: ${key}`);
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
meta.metaData["x-amz-meta-processed"] === "true" ||
|
|
meta.metaData["processed"] === "true"
|
|
) {
|
|
console.log(`♻️ Already processed: ${key}`);
|
|
return false;
|
|
}
|
|
|
|
const originalStream = await minio.getObject(bucket, key);
|
|
|
|
const fileName = key.split("/").pop()!;
|
|
const filePath = key.substring(0, key.lastIndexOf("/"));
|
|
|
|
const processedMeta = {
|
|
"x-amz-meta-processed": "true",
|
|
};
|
|
|
|
// Helper to upload from a stream
|
|
async function uploadFromStream(
|
|
targetPath: string,
|
|
mimeType: string,
|
|
transformStream: NodeJS.ReadableStream
|
|
) {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of transformStream) {
|
|
chunks.push(chunk as Buffer);
|
|
}
|
|
const finalBuffer = Buffer.concat(chunks);
|
|
|
|
await minio.putObject(bucket, targetPath, finalBuffer, finalBuffer.length, {
|
|
"Content-Type": mimeType,
|
|
...processedMeta,
|
|
});
|
|
}
|
|
|
|
try {
|
|
// 🖼️ Thumbnail (resize to 200px width)
|
|
await uploadFromStream(
|
|
`${filePath}/thumbs/${fileName}`,
|
|
mime,
|
|
originalStream.pipe(sharp().resize(200))
|
|
);
|
|
|
|
// Re-fetch original again for each variant (streams are one-time-use)
|
|
const optimizedStream = await minio.getObject(bucket, key);
|
|
|
|
// 📸 Optimized JPEG
|
|
await uploadFromStream(
|
|
`${filePath}/optimized/${fileName}`,
|
|
"image/jpeg",
|
|
optimizedStream.pipe(sharp().jpeg({ quality: 80 }))
|
|
);
|
|
|
|
const webpStream = await minio.getObject(bucket, key);
|
|
|
|
// 🌐 WebP version
|
|
const webpName = fileName.replace(/\.[^/.]+$/, ".webp");
|
|
await uploadFromStream(
|
|
`${filePath}/webp/${webpName}`,
|
|
"image/webp",
|
|
webpStream.pipe(sharp().webp({ quality: 80 }))
|
|
);
|
|
|
|
const finalOriginalStream = await minio.getObject(bucket, key);
|
|
|
|
// 🔁 Re-upload the original with updated metadata to mark it processed
|
|
await uploadFromStream(key, mime, finalOriginalStream);
|
|
|
|
console.log(`✅ Processed image: ${key}`);
|
|
return true;
|
|
} catch (err) {
|
|
console.error(`❌ Error processing image (${key}):`, err);
|
|
return false;
|
|
}
|
|
}
|