feat: generate og:image when there isn't one

This commit is contained in:
grassblock 2025-05-14 22:21:59 +08:00
parent 65032ab2fc
commit 9e7318fb63
2 changed files with 73 additions and 4 deletions

View file

@ -3,6 +3,10 @@ import Layout from '../../layouts/Layout.astro';
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import Comments from "../../components/Comments.astro"; import Comments from "../../components/Comments.astro";
import {getImage} from "astro:assets"; import {getImage} from "astro:assets";
import { unified } from "unified";
import { select } from "unist-util-select";
import remarkMdx from "remark-mdx";
import remarkParse from "remark-parse";
export async function getStaticPaths() { export async function getStaticPaths() {
const blogEntries = await getCollection('posts'); const blogEntries = await getCollection('posts');
@ -14,16 +18,19 @@ export async function getStaticPaths() {
const { entry } = Astro.props; const { entry } = Astro.props;
const { Content } = await entry.render(); const { Content } = await entry.render();
const slug = Astro.params.slug; const slug = Astro.params.slug;
// get featured image and use it as og:image
// use the custom cover image if it exists, otherwise use the featured image file in the same directory
const featuredImages = import.meta.glob(`/src/content/posts/*/featured.*`,{import:'default',eager:true}); const featuredImages = import.meta.glob(`/src/content/posts/*/featured.*`,{import:'default',eager:true});
const customFeaturedImage = entry.data.cover?.src
const matchedImage = Object.keys(featuredImages).find(path => path.includes(slug)); const matchedImage = Object.keys(featuredImages).find(path => path.includes(slug));
let matchedImage_src; let matchedImage_src;
if (matchedImage) { if (matchedImage && !customFeaturedImage) {
matchedImage_src = await getImage({src: featuredImages[matchedImage], format: 'webp'}) || null; matchedImage_src = await getImage({src: featuredImages[matchedImage], format: 'webp'}) || null;
} }
const cover = entry.data.cover || matchedImage_src?.src || `/post/${slug}/og.webp`;
//|| `/posts/${slug}/featured.webp` || `/posts/${slug}/featured.png` || `/posts/${slug}/featured.jpg` const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/featured.png` || '';
--- ---
<Layout <Layout

View file

@ -0,0 +1,62 @@
import { getCollection } from 'astro:content';
import sharp from 'sharp';
import {getImage} from "astro:assets";
export async function getStaticPaths() {
const blogEntries = await getCollection('posts');
return blogEntries.map(post => ({
params: { slug: post.slug }, props: { post },
}));
}
// get the post has a external featured.* image files
async function getExternalImage(post) {
const featuredImages = import.meta.glob(`/src/content/posts/*/featured.*`, {import: 'default', eager: true});
const matchedImage = Object.keys(featuredImages).find(path => path.includes(post.slug));
let matchedImage_src;
if (matchedImage) {
matchedImage_src = await getImage({src: featuredImages[matchedImage], format: 'webp'}) || null;
}
return matchedImage_src;
}
// This function dynamically generates og:images for posts that don't have a featured image
export async function GET({ props }) {
const {post} = props;
try {
// Check if a custom cover image already exists
if (post.data.cover && await getExternalImage(post)) {
// Redirect to the existing image
return new Response(null);
}
// Generate an image with post title and description
const width = 1280;
const height = 720;
// Create a simple image with text
const svg = `
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
<rect width="${width}" height="${height}" fill="#2e3440"/>
<text x="50%" y="40%" font-family="JetBrains Mono, monospace" font-size="50" text-anchor="middle" fill="#eceff4">${post.data.title}</text>
<text x="50%" y="60%" font-family="JetBrains Mono, monospace" font-size="30" text-anchor="middle" fill="#81a1c1">${post.data.description}</text>
</svg>
`;
// Convert SVG to WebP
const buffer = await sharp(Buffer.from(svg))
.toFormat('webp')
.toBuffer();
// Return the image
return new Response(buffer, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=31536000'
}
});
} catch (error) {
console.error('Error generating image:', error);
return new Response('Error generating image', { status: 500 });
}
}