Moving og generation to lib component (Geoff Rich influenced).

This commit is contained in:
Bradley Shellnut 2023-12-08 17:04:31 -08:00
parent 0826667fb7
commit 17b5325ec4
5 changed files with 57 additions and 37 deletions

View file

@ -10,12 +10,12 @@
<div class="social-card" style={`width: ${width}px; height: ${height}px;`}>
<div class="social-card-header">
<div class="social-card-title">
<div class="social-card-image">
<img src={image || '/images/b_shell_nut_favicon.png'} alt="Bradley Shellnut" />
</div>
<h1>{header}</h1>
<div class="social-card-title">
<div class="social-card-image">
<img src={image || '/images/b_shell_nut_favicon.png'} alt="Bradley Shellnut" />
</div>
<h1>{header}</h1>
</div>
</div>
<div class="social-card-content">
<h2 class="page">{page}</h2>

40
src/lib/renderImage.ts Normal file
View file

@ -0,0 +1,40 @@
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
import { html as toReactNode } from 'satori-html';
// we use a Vite plugin to turn this import into the result of fs.readFileSync during build
import firaSansSemiBold from '$lib/fonts/FiraSans-SemiBold.ttf';
import { dev } from '$app/environment';
export async function componentToPng(component, props, height, width) {
const result = component.render(props);
const markup = toReactNode(`${result.html}<style lang="css">${result.css.code}</style>`);
const svg = await satori(markup, {
fonts: [
{
name: 'Fira Sans',
data: Buffer.from(firaSansSemiBold),
style: 'normal'
}
],
height: +height,
width: +width
});
const resvg = new Resvg(svg, {
fitTo: {
mode: 'width',
value: +width
}
});
const png = resvg.render();
return new Response(png.asPng(), {
headers: {
'content-type': 'image/png',
'cache-control': dev ? 'no-cache, no-store' : 'public, immutable, no-transform, max-age=86400'
}
});
}

View file

@ -1,7 +1,7 @@
import { BANDCAMP_USERNAME, USE_REDIS_CACHE } from '$env/static/private';
import scrapeIt from 'scrape-it';
import type { ScrapeResult } from 'scrape-it';
import { redis } from '../server/redis';
import { redis } from '$lib/server/redis';
import type { Album } from '../types/album';
export async function fetchBandcampAlbums() {

View file

@ -4,7 +4,12 @@ import type { PageServerLoad } from './$types';
import { fetchBandcampAlbums } from '$lib/util/fetchBandcampAlbums';
export const load: PageServerLoad = async ({ fetch, setHeaders, url }) => {
const baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
let baseUrl = 'https://bradleyshellnut.com';
if (url.origin.includes('prerender')) {
baseUrl = PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
} else {
baseUrl = new URL(url.origin).href || PUBLIC_SITE_URL || 'https://bradleyshellnut.com';
}
const currentPageUrl = new URL(url.pathname, url.origin).href;
const metaTags: MetaTagsProps = Object.freeze({

View file

@ -4,6 +4,8 @@ import { Resvg } from '@resvg/resvg-js';
import { html as toReactNode } from 'satori-html';
import FiraSansSemiBold from '$lib/fonts/FiraSans-SemiBold.ttf';
import SocialImageCard from '$lib/components/socialImageCard.svelte';
import { dev } from '$app/environment';
import { componentToPng } from '$root/lib/renderImage';
const height = 630;
const width = 1200;
@ -14,7 +16,8 @@ export const GET: RequestHandler = async ({ url }) => {
const header = url.searchParams.get('header') ?? undefined;
const page = url.searchParams.get('page') ?? undefined;
const content = url.searchParams.get('content') ?? '';
const result = SocialImageCard.render({
return componentToPng(SocialImageCard, {
header,
page,
content,
@ -22,35 +25,7 @@ export const GET: RequestHandler = async ({ url }) => {
width,
height,
url: new URL(url.origin).href
});
console.log('result', result);
const element = toReactNode(`${result.html}<style>${result.css.code}</style>`);
const svg = await satori(element, {
fonts: [
{
name: 'Fira Sans',
data: Buffer.from(FiraSansSemiBold),
style: 'normal'
}
],
height,
width
});
const resvg = new Resvg(svg, {
fitTo: {
mode: 'width',
value: width
}
});
const image = resvg.render();
return new Response(image.asPng(), {
headers: {
'content-type': 'image/png'
}
});
}, height, width);
} catch (e) {
console.error(e);
}