From 1164715d646147de431f12e51f2c5c0fc2dad23d Mon Sep 17 00:00:00 2001 From: gunshiz Date: Fri, 11 Sep 2026 17:28:38 +0700 Subject: [PATCH] feat : use cover image guide instead of shit image generater --- app/[character]/[page]/opengraph-image.tsx | 28 ---- app/[character]/[page]/page.tsx | 9 +- app/[character]/opengraph-image.tsx | 28 ---- app/[character]/page.tsx | 7 +- lib/og-image.tsx | 174 --------------------- 5 files changed, 14 insertions(+), 232 deletions(-) delete mode 100644 app/[character]/[page]/opengraph-image.tsx delete mode 100644 app/[character]/opengraph-image.tsx delete mode 100644 lib/og-image.tsx diff --git a/app/[character]/[page]/opengraph-image.tsx b/app/[character]/[page]/opengraph-image.tsx deleted file mode 100644 index c52b2b6..0000000 --- a/app/[character]/[page]/opengraph-image.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { connection } from "next/server"; -import { getCachedPublicGuide } from "@/lib/cache/public"; -import { - generateOgImage, - ogImageContentType, - ogImageSize, -} from "@/lib/og-image"; - -export const alt = "Buzz Guide character guide"; -export const size = ogImageSize; -export const contentType = ogImageContentType; - -export default async function Image({ - params, -}: { - params: Promise<{ character: string }>; -}) { - await connection(); - const guide = await getCachedPublicGuide((await params).character); - if (!guide) return new Response("Not found", { status: 404 }); - return generateOgImage({ - title: guide.name, - sub: guide.overview, - characterCard: true, - backgroundUrl: guide.character.nameCardImageUrl, - characterUrl: guide.character.splashImageUrl ?? guide.character.imageUrl, - }); -} diff --git a/app/[character]/[page]/page.tsx b/app/[character]/[page]/page.tsx index 6b3b129..0bd61b1 100644 --- a/app/[character]/[page]/page.tsx +++ b/app/[character]/[page]/page.tsx @@ -11,7 +11,14 @@ export const instant = false; export async function generateMetadata({ params }: { params: Params }): Promise { const { character } = await params; const guide = await getCachedPublicGuide(character); - return guide ? { title: `${guide.name}`, description: guide.overview } : { title: "ไม่พบไกด์" }; + return guide + ? { + title: `${guide.name}`, + description: guide.overview, + openGraph: { images: [guide.coverUrl] }, + twitter: { images: [guide.coverUrl] }, + } + : { title: "ไม่พบไกด์" }; } export default async function GuideSectionPage({ params }: { params: Params }) { diff --git a/app/[character]/opengraph-image.tsx b/app/[character]/opengraph-image.tsx deleted file mode 100644 index c52b2b6..0000000 --- a/app/[character]/opengraph-image.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { connection } from "next/server"; -import { getCachedPublicGuide } from "@/lib/cache/public"; -import { - generateOgImage, - ogImageContentType, - ogImageSize, -} from "@/lib/og-image"; - -export const alt = "Buzz Guide character guide"; -export const size = ogImageSize; -export const contentType = ogImageContentType; - -export default async function Image({ - params, -}: { - params: Promise<{ character: string }>; -}) { - await connection(); - const guide = await getCachedPublicGuide((await params).character); - if (!guide) return new Response("Not found", { status: 404 }); - return generateOgImage({ - title: guide.name, - sub: guide.overview, - characterCard: true, - backgroundUrl: guide.character.nameCardImageUrl, - characterUrl: guide.character.splashImageUrl ?? guide.character.imageUrl, - }); -} diff --git a/app/[character]/page.tsx b/app/[character]/page.tsx index 091eabe..61e0acb 100644 --- a/app/[character]/page.tsx +++ b/app/[character]/page.tsx @@ -15,7 +15,12 @@ export async function generateMetadata({ }): Promise { const guide = await getCachedPublicGuide((await params).character); return guide - ? { title: `${guide.name}`, description: guide.overview } + ? { + title: `${guide.name}`, + description: guide.overview, + openGraph: { images: [guide.coverUrl] }, + twitter: { images: [guide.coverUrl] }, + } : { title: "ไม่พบไกด์" }; } diff --git a/lib/og-image.tsx b/lib/og-image.tsx deleted file mode 100644 index b5660d7..0000000 --- a/lib/og-image.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/* 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; -let characterFontDataPromise: 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 getCharacterFontData(): Promise { - characterFontDataPromise ??= readFile(join(process.cwd(), "public/zh-cn.ttf")); - return characterFontDataPromise; -} - -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, - characterFont, - characterBackgroundSrc, - characterArtSrc, - ] = await Promise.all([ - characterCard ? null : getBannerData(), - getFontData(), - characterCard ? getCharacterFontData() : null, - 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 }, - ...(characterFont - ? [{ name: "Genshin", data: characterFont, style: "normal" as const, weight: 400 as const }] - : []), - ], - }, - ); -}