import path from "path"; import sharp from 'sharp'; import { FileMetadata, ImageMetadata, ResourceProcessor } from "../types"; import { Resource, ResourceStatus, db } from "@nice/common"; import { getUploadFilePath } from "@server/utils/file"; import { Logger } from "@nestjs/common"; export class ImageProcessor implements ResourceProcessor { private logger = new Logger(ImageProcessor.name) constructor() { } async process(resource: Resource): Promise { const { fileId } = resource; const filepath = getUploadFilePath(fileId); const originMeta = resource.metadata as unknown as FileMetadata; if (!originMeta.mimeType?.startsWith('image/')) { this.logger.log(`Skipping non-image resource: ${resource.id}`); return resource; } try { const image = sharp(filepath); const metadata = await image.metadata(); if (!metadata) { throw new Error(`Failed to get metadata for image: ${fileId}`); } // Create WebP compressed version const compressedPath = path.join( path.dirname(filepath), `${path.basename(filepath, path.extname(filepath))}_compressed.webp` ); await image .webp({ quality: 80, lossless: false, effort: 5 // Range 0-6, higher means slower but better compression }) .toFile(compressedPath); const imageMeta: ImageMetadata = { width: metadata.width || 0, height: metadata.height || 0, compressedUrl: path.basename(compressedPath), orientation: metadata.orientation, space: metadata.space, hasAlpha: metadata.hasAlpha, } console.log(imageMeta) const updatedResource = await db.resource.update({ where: { id: resource.id }, data: { metadata: { ...originMeta, ...imageMeta } } }); return updatedResource; } catch (error: any) { throw new Error(`Failed to process image: ${error.message}`); } } }