feat :idk
This commit is contained in:
+78
-7
@@ -4,12 +4,14 @@ 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<string> | undefined;
|
||||
let fontDataPromise: Promise<Buffer> | undefined;
|
||||
const imageDataPromises = new Map<string, Promise<string | null>>();
|
||||
|
||||
function shorten(value: string, length: number): string {
|
||||
const normalized = value.replace(/\s+/gu, " ").trim();
|
||||
@@ -31,14 +33,44 @@ function getFontData(): Promise<Buffer> {
|
||||
return fontDataPromise;
|
||||
}
|
||||
|
||||
function getImageData(url: string): Promise<string | null> {
|
||||
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] = await Promise.all([getBannerData(), getFontData()]);
|
||||
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(
|
||||
<div
|
||||
@@ -48,26 +80,65 @@ export async function generateOgImage({
|
||||
height: "560px",
|
||||
fontFamily: "Kanit",
|
||||
color: "#fff",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<img src={bannerSrc} alt="Banner" width={1222} height={560} />
|
||||
{backgroundSrc ? (
|
||||
<img src={backgroundSrc} alt="" width={1222} height={560} style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }} />
|
||||
) : (
|
||||
<div style={{ position: "absolute", inset: 0, background: "#111827" }} />
|
||||
)}
|
||||
{characterCard && characterArtSrc ? (
|
||||
<img
|
||||
src={characterArtSrc}
|
||||
alt=""
|
||||
width={480}
|
||||
height={360}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: "58%",
|
||||
height: "100%",
|
||||
objectFit: "cover",
|
||||
objectPosition: "center",
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{characterCard ? (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
background: "linear-gradient(90deg, rgba(3, 8, 18, 0.88) 0%, rgba(3, 8, 18, 0.62) 45%, rgba(3, 8, 18, 0.08) 100%)",
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<div
|
||||
style={{
|
||||
fontSize: 64,
|
||||
position: "absolute",
|
||||
top: "160px",
|
||||
top: characterCard ? "52px" : "160px",
|
||||
left: "60px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
right: "450px",
|
||||
right: characterCard ? "560px" : "450px",
|
||||
bottom: "100px",
|
||||
}}
|
||||
>
|
||||
<div>{shorten(title, 24)}</div>
|
||||
{characterCard ? (
|
||||
<div style={{ display: "flex", color: "#d8f4ee", fontSize: 28 }}>
|
||||
เกนชินไม่ใช่เกมมือถือไกด์
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ display: "flex", fontSize: 64, marginTop: characterCard ? "8px" : 0 }}>
|
||||
{shorten(title, 24)}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
fontSize: 24,
|
||||
marginTop: "-20px",
|
||||
marginTop: characterCard ? "8px" : "-20px",
|
||||
color: "#aaa",
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user