/* eslint-disable @next/next/no-img-element */ import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { ImageResponse } from "next/og"; import sharp from "sharp"; export const ogImageSize = { width: 1222, height: 560 } as const; export const ogImageContentType = "image/png"; let bannerDataPromise: Promise | undefined; let fontDataPromise: Promise | undefined; const imageDataPromises = new Map>(); function shorten(value: string, length: number): string { const normalized = value.replace(/\s+/gu, " ").trim(); return normalized.length > length ? `${normalized.slice(0, length - 1).trimEnd()}…` : normalized; } function getBannerData(): Promise { bannerDataPromise ??= readFile( join(process.cwd(), "public/web_banner.png"), "base64", ).then((data) => `data:image/png;base64,${data}`); return bannerDataPromise; } function getFontData(): Promise { fontDataPromise ??= readFile(join(process.cwd(), "public/Kanit-SemiBold.ttf")); return fontDataPromise; } function getImageData(url: string): Promise { const cached = imageDataPromises.get(url); if (cached) return cached; const promise = fetch(url) .then(async (response) => { if (!response.ok) return null; const png = await sharp(Buffer.from(await response.arrayBuffer())) .png() .toBuffer(); return `data:image/png;base64,${png.toString("base64")}`; }) .catch(() => null); imageDataPromises.set(url, promise); return promise; } export async function generateOgImage({ title, sub, characterCard = false, backgroundUrl, characterUrl, }: { title: string; sub: string; characterCard?: boolean; backgroundUrl?: string | null; characterUrl?: string | null; }) { const [bannerSrc, kanit, characterBackgroundSrc, characterArtSrc] = await Promise.all([ characterCard ? null : getBannerData(), getFontData(), characterCard && backgroundUrl ? getImageData(backgroundUrl) : null, characterCard && characterUrl ? getImageData(characterUrl) : null, ]); const backgroundSrc = characterCard ? characterBackgroundSrc : bannerSrc; return new ImageResponse(
{backgroundSrc ? ( ) : (
)} {characterCard && characterArtSrc ? ( ) : null} {characterCard ? (
) : null}
{characterCard ? (
เกนชินไม่ใช่เกมมือถือไกด์
) : null}
{shorten(title, 24)}
{shorten(sub, 92)}
, { ...ogImageSize, fonts: [{ name: "Kanit", data: kanit, style: "normal", weight: 600 }], }, ); }