memorium/lib/cache/image.ts

86 lines
1.9 KiB
TypeScript

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<boolean>((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<string>(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,
}),
);
}