2025-01-21 20:05:42 +08:00
|
|
|
import path from 'path';
|
2025-01-03 09:24:46 +08:00
|
|
|
import sharp from 'sharp';
|
2025-01-21 20:05:42 +08:00
|
|
|
import { FileMetadata, ImageMetadata, ResourceProcessor } from '../types';
|
|
|
|
import { Resource, ResourceStatus, db } from '@nice/common';
|
|
|
|
import { getUploadFilePath } from '@server/utils/file';
|
|
|
|
import { BaseProcessor } from './BaseProcessor';
|
2025-01-06 18:30:16 +08:00
|
|
|
|
2025-01-08 00:52:11 +08:00
|
|
|
export class ImageProcessor extends BaseProcessor {
|
2025-01-21 20:05:42 +08:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
}
|
2025-01-03 09:24:46 +08:00
|
|
|
|
|
|
|
async process(resource: Resource): Promise<Resource> {
|
2025-01-08 00:52:11 +08:00
|
|
|
const { url } = resource;
|
|
|
|
const filepath = getUploadFilePath(url);
|
2025-02-06 16:32:52 +08:00
|
|
|
const originMeta = resource.meta as unknown as FileMetadata;
|
2025-01-03 09:24:46 +08:00
|
|
|
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) {
|
2025-01-08 00:52:11 +08:00
|
|
|
throw new Error(`Failed to get metadata for image: ${url}`);
|
2025-01-03 09:24:46 +08:00
|
|
|
}
|
|
|
|
// Create WebP compressed version
|
2025-01-21 20:05:42 +08:00
|
|
|
const compressedDir = this.createOutputDir(filepath, 'compressed');
|
|
|
|
const compressedPath = path.join(
|
|
|
|
compressedDir,
|
|
|
|
`${path.basename(filepath, path.extname(filepath))}.webp`,
|
|
|
|
);
|
2025-01-03 09:24:46 +08:00
|
|
|
await image
|
|
|
|
.webp({
|
|
|
|
quality: 80,
|
|
|
|
lossless: false,
|
2025-01-21 20:05:42 +08:00
|
|
|
effort: 5, // Range 0-6, higher means slower but better compression
|
2025-01-03 09:24:46 +08:00
|
|
|
})
|
|
|
|
.toFile(compressedPath);
|
|
|
|
const imageMeta: ImageMetadata = {
|
|
|
|
width: metadata.width || 0,
|
|
|
|
height: metadata.height || 0,
|
|
|
|
orientation: metadata.orientation,
|
|
|
|
space: metadata.space,
|
|
|
|
hasAlpha: metadata.hasAlpha,
|
2025-01-21 20:05:42 +08:00
|
|
|
};
|
2025-01-03 09:24:46 +08:00
|
|
|
const updatedResource = await db.resource.update({
|
|
|
|
where: { id: resource.id },
|
|
|
|
data: {
|
2025-02-06 16:32:52 +08:00
|
|
|
meta: {
|
2025-01-03 09:24:46 +08:00
|
|
|
...originMeta,
|
2025-01-21 20:05:42 +08:00
|
|
|
...imageMeta,
|
|
|
|
},
|
|
|
|
},
|
2025-01-03 09:24:46 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
return updatedResource;
|
|
|
|
} catch (error: any) {
|
|
|
|
throw new Error(`Failed to process image: ${error.message}`);
|
|
|
|
}
|
|
|
|
}
|
2025-01-21 20:05:42 +08:00
|
|
|
}
|