import * as plugins from './plugins.js'; import * as pluginsTyped from './plugins.typed.js'; export interface IAssetVariation { format?: 'avif' | 'webp' | 'png'; width?: number; height?: number; invert?: boolean; } export interface ISmartJimpOptions { mode: 'sharp' | 'jimp'; } export class SmartJimp { public levelCache = new plugins.levelcache.LevelCache({ cacheId: 'mastercache', maxMemoryStorageInMB: 100, maxDiskStorageInMB: 5000, }); public options: ISmartJimpOptions; constructor(optionsArg: ISmartJimpOptions) { this.options = optionsArg; } /** * get a key that is unique for a wanted asset variation */ private getCacheKey( sourceTypeArg: 'streamfile' | 'smartfile', sourceIdArg: string, assetVariationArg?: IAssetVariation ) { return `${sourceTypeArg}_${sourceIdArg}_${ assetVariationArg ? `${assetVariationArg.width || 'auto'}x${assetVariationArg.height || 'auto'}` : 'original' }`; } sharpMod: typeof pluginsTyped.sharpType.default; public async getSharpMod(): Promise< typeof pluginsTyped.sharpType.default > { if (!this.sharpMod) { this.sharpMod = (await import('sharp')).default; } return this.sharpMod; } jimpMod: typeof pluginsTyped.jimpType; public async getJimpMod(): Promise { if (!this.jimpMod) { this.jimpMod = await import('jimp'); } return this.jimpMod.default; } public async computeAssetVariation(assetBufferArg: Buffer, assetVariationArg: IAssetVariation) { if (this.options.mode === 'sharp') { const sharp = await this.getSharpMod(); if (!assetVariationArg) { return assetBufferArg; } let sharpImage = sharp(assetBufferArg); sharpImage = sharpImage.resize(assetVariationArg.width, assetVariationArg.height); const resultResize = sharpImage.resize(assetVariationArg.width, assetVariationArg.height); if (assetVariationArg.invert) { // TODO: implement invert } switch (assetVariationArg.format) { case 'avif': sharpImage = resultResize.avif(); case 'webp': sharpImage = resultResize.webp(); case 'png': sharpImage = resultResize.png(); } return sharpImage.toBuffer(); } else if (this.options.mode === 'jimp') { const jimp = await this.getJimpMod(); let jimpImage = await jimp.read(assetBufferArg); if (assetVariationArg.width || assetVariationArg.height) { jimpImage = jimpImage.resize(assetVariationArg.width, assetVariationArg.height); } if (assetVariationArg.invert) { jimpImage = jimpImage.invert(); } switch (assetVariationArg.format) { case 'png': return await jimpImage.getBufferAsync(jimp.MIME_PNG); default: return await jimpImage.getBufferAsync(jimp.MIME_JPEG); } } } public async getFromSmartfile( smartfileArg: plugins.smartfile.SmartFile, wantedDimensionsArg?: IAssetVariation ) { const cacheKey = this.getCacheKey( 'smartfile', await smartfileArg.getHash(), wantedDimensionsArg ); const existingCacheEntry = await this.levelCache.retrieveCacheEntryByKey(cacheKey); if (existingCacheEntry) { return existingCacheEntry.contents; } else { const computedAssetBuffer = await this.computeAssetVariation( smartfileArg.contentBuffer, wantedDimensionsArg ); this.levelCache.storeCacheEntryByKey( cacheKey, new plugins.levelcache.CacheEntry({ contents: computedAssetBuffer, ttl: 600000, }) ); return computedAssetBuffer; } } public async createAvifImageFromBuffer(bufferArg: Buffer) { const sharp = await this.getSharpMod(); const sharpImage = sharp(bufferArg); const result = await sharpImage.avif().toBuffer(); return result; } }