import { hash } from "@lib/hash.ts"; import * as cache from "@lib/cache/cache.ts"; import { ImageMagick } from "https://deno.land/x/imagemagick_deno@0.0.25/mod.ts"; type ImageCacheOptions = { url: string; width: number; height: number; mediaType?: string; }; const CACHE_KEY = "images"; function getCacheKey({ url: _url, width, height }: ImageCacheOptions) { const url = new URL(_url); return `${CACHE_KEY}/${url.hostname}/${url.pathname}/${width}/${height}` .replace( "//", "/", ); } function verifyImage( imageBuffer: Uint8Array, ) { return new Promise((resolve) => { try { ImageMagick.read(imageBuffer, (image) => { resolve(image.height !== 0 && image.width !== 0); }); } catch (_err) { resolve(false); } }); } export async function getImage({ url, width, height }: ImageCacheOptions) { const cacheKey = getCacheKey({ url, width, height }); const pointerCacheRaw = await cache.get(cacheKey); if (!pointerCacheRaw) return; const pointerCache = typeof pointerCacheRaw === "string" ? JSON.parse(pointerCacheRaw) : pointerCacheRaw; const imageContent = await cache.get(pointerCache.id, true); if (!imageContent) return; return { ...pointerCache, buffer: imageContent, }; } export async function setImage( buffer: Uint8Array, { url, width, height, mediaType }: ImageCacheOptions, ) { const clone = new Uint8Array(buffer); const imageCorrect = await verifyImage(clone); if (!imageCorrect) { console.log("[cache/image] failed to store image", { url }); return; } const cacheKey = getCacheKey({ url, width, height }); const pointerId = await hash(cacheKey); await cache.set(pointerId, clone); cache.expire(pointerId, 60 * 60 * 24); cache.expire(cacheKey, 60 * 60 * 24); await cache.set( cacheKey, JSON.stringify({ id: pointerId, url, width, height, mediaType, }), ); }