switch to puppeteer
This commit is contained in:
parent
92b6846bbb
commit
6e3bad10f8
6 changed files with 611 additions and 667 deletions
143
assets/DMSans/style.css
Normal file
143
assets/DMSans/style.css
Normal file
|
@ -0,0 +1,143 @@
|
|||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-SemiBoldItalic.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Thin.ttf') format('truetype');
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-ThinItalic.ttf') format('truetype');
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Regular.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-LightItalic.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-MediumItalic.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-ExtraLightItalic.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-BlackItalic.ttf') format('truetype');
|
||||
font-weight: 900;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Italic.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-ExtraBold.ttf') format('truetype');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-ExtraBoldItalic.ttf') format('truetype');
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Black.ttf') format('truetype');
|
||||
font-weight: 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-Bold.ttf') format('truetype');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-BoldItalic.ttf') format('truetype');
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DM Sans';
|
||||
src: url('DMSans-ExtraLight.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
|
@ -1,3 +1,8 @@
|
|||
@import url('/assets/DMSans/style.css');
|
||||
body {
|
||||
font-family: 'DM Sans', sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -76,6 +81,7 @@ p {
|
|||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
}
|
||||
.is-debug .description {
|
||||
display: block;
|
||||
|
|
|
@ -1,35 +1,72 @@
|
|||
import html2png, { renderHTML as inline } from '@besties/html2png'
|
||||
import puppeteer from 'puppeteer'
|
||||
import fs from 'node:fs'
|
||||
|
||||
const style = fs.readFileSync('./assets/style.css', 'utf8')
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
defaultViewport: {
|
||||
width: 1200,
|
||||
height: 600
|
||||
},
|
||||
headless: 'new',
|
||||
// slowMo: 250,
|
||||
args: [
|
||||
// TODO: fix this
|
||||
'--disable-gpu',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-setuid-sandbox',
|
||||
'--no-sandbox'
|
||||
]
|
||||
})
|
||||
|
||||
async function wait() {
|
||||
const selectors = [...document.querySelectorAll('img')]
|
||||
await Promise.all([
|
||||
document.fonts.ready,
|
||||
...selectors.map(img => {
|
||||
// Image has already finished loading, let’s see if it worked
|
||||
if (img.complete) {
|
||||
// Image loaded and has presence
|
||||
if (img.naturalHeight !== 0) return
|
||||
// Image failed, so it has no height
|
||||
throw new Error('Image failed to load')
|
||||
}
|
||||
// Image hasn’t loaded yet, added an event listener to know when it does
|
||||
return new Promise((resolve, reject) => {
|
||||
img.addEventListener('load', resolve)
|
||||
img.addEventListener('error', reject)
|
||||
})
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
export async function renderPage(html) {
|
||||
const page = await browser.newPage()
|
||||
try {
|
||||
await page.setContent(html, {
|
||||
waitUntil: 'domcontentloaded'
|
||||
})
|
||||
await page.evaluate(wait)
|
||||
const buffer = await page.screenshot({
|
||||
type: 'png',
|
||||
encoding: 'binary'
|
||||
})
|
||||
await page.close()
|
||||
return buffer
|
||||
} catch {
|
||||
await page.close()
|
||||
throw new Error('Failed to render page')
|
||||
}
|
||||
}
|
||||
|
||||
export default async function (html, options = {}) {
|
||||
return html2png(`<style>${style}</style>${html}`, options)
|
||||
if (options.format == 'html') {
|
||||
return await renderHtml(html)
|
||||
}
|
||||
return renderPage(`<style>${style}</style>${html}`)
|
||||
}
|
||||
|
||||
export async function renderHtml(html) {
|
||||
const styleNow = await fs.promises.readFile('./assets/style.css', 'utf8')
|
||||
return (
|
||||
`<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
:root {
|
||||
height: 100%;
|
||||
font-family: 'DM Sans', Arial, Helvetica, sans-serif;
|
||||
}
|
||||
* {
|
||||
font-family: inherit;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: black;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<title>OpenGraph Debug Preview</title>
|
||||
</head>` + (await inline(`<style>${styleNow}</style>${html}`))
|
||||
)
|
||||
return `<style>${styleNow}</style>${html}`
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
"author": "besties",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@besties/html2png": "^1.1.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"eta": "^3.1.0",
|
||||
"express": "^4.18.2",
|
||||
"puppeteer": "^21.6.1",
|
||||
"undici": "^5.24.0"
|
||||
},
|
||||
"type": "module",
|
||||
|
|
982
pnpm-lock.yaml
generated
982
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
54
server.js
54
server.js
|
@ -1,13 +1,10 @@
|
|||
import 'dotenv/config'
|
||||
import express from 'express'
|
||||
import { Eta } from 'eta'
|
||||
import render, { renderHtml } from './lib/render.js'
|
||||
import render from './lib/render.js'
|
||||
import languageColors from './lib/languagecolors.js'
|
||||
import { fetch } from 'undici'
|
||||
import hash from './lib/hash.js'
|
||||
import { DMSans, ComicSans } from './lib/fonts.js'
|
||||
// satori is using the native fetch api, causing a warning, so make node-fetch the default
|
||||
globalThis.fetch = fetch
|
||||
|
||||
const forgejoBaseUrl = process.env.FORGEJO_BASE_URL
|
||||
if (!forgejoBaseUrl) throw new Error('FORGEJO_BASE_URL unspecified')
|
||||
|
@ -21,7 +18,7 @@ const eta = new Eta({
|
|||
|
||||
const app = express()
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
app.get('/', function (_, res) {
|
||||
res.redirect('https://git.gay/gitgay/og.git')
|
||||
})
|
||||
|
||||
|
@ -50,19 +47,19 @@ async function getLanguages(owner, repo) {
|
|||
return {}
|
||||
}
|
||||
|
||||
const comicSansNames = new Set(['trixie'])
|
||||
const allowedFormats = new Set(['png', 'svg', 'json'])
|
||||
app.use('/assets', express.static('assets'))
|
||||
|
||||
const defaultFormat = debug ? 'html' : 'png'
|
||||
|
||||
app.use('/', function (req, res, next) {
|
||||
if (req.query.format && !allowedFormats.has(req.query.format)) {
|
||||
res.status(400)
|
||||
res.end()
|
||||
return
|
||||
app.locals.options = req.query.debug
|
||||
? {
|
||||
format: 'html'
|
||||
}
|
||||
app.locals.options = {
|
||||
fonts: comicSansNames.has(req.params.owner) ? ComicSans : DMSans,
|
||||
format: req.query.format ?? 'png'
|
||||
: {
|
||||
format: defaultFormat
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
next()
|
||||
})
|
||||
|
||||
|
@ -87,15 +84,14 @@ app.get('/:user', async function (req, res) {
|
|||
await reposResp.json()
|
||||
])
|
||||
user.repo_count = repos.length
|
||||
user.description = user.description
|
||||
?.split('---\r\n')
|
||||
.slice(0, -1)
|
||||
.join('---\r\n')
|
||||
const html = await eta.renderAsync('user', {
|
||||
user,
|
||||
debug
|
||||
})
|
||||
if (debug) {
|
||||
res.send(await renderHtml(html))
|
||||
return
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
res.set('Content-Disposition', 'inline')
|
||||
res.send(await render(html, app.locals.options))
|
||||
})
|
||||
|
@ -128,11 +124,6 @@ app.get('/:owner/:repo', async function (req, res) {
|
|||
languageColors,
|
||||
debug
|
||||
})
|
||||
if (debug) {
|
||||
res.send(await renderHtml(html))
|
||||
return
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
res.set('Content-Disposition', 'inline')
|
||||
res.send(await render(html, app.locals.options))
|
||||
})
|
||||
|
@ -174,11 +165,6 @@ app.get('/:owner/:repo/commit/:hash', async function (req, res) {
|
|||
languageColors,
|
||||
debug
|
||||
})
|
||||
if (debug) {
|
||||
res.send(await renderHtml(html))
|
||||
return
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
res.set('Content-Disposition', 'inline')
|
||||
res.send(await render(html, app.locals.options))
|
||||
})
|
||||
|
@ -219,11 +205,6 @@ app.get('/:owner/:repo/issues/:num', async function (req, res) {
|
|||
languageColors,
|
||||
debug
|
||||
})
|
||||
if (debug) {
|
||||
res.send(await renderHtml(html))
|
||||
return
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
res.set('Content-Disposition', 'inline')
|
||||
res.send(await render(html, app.locals.options))
|
||||
})
|
||||
|
@ -270,11 +251,6 @@ app.get('/:owner/:repo/pulls/:num', async function (req, res) {
|
|||
languageColors,
|
||||
debug
|
||||
})
|
||||
if (debug) {
|
||||
res.send(await renderHtml(html))
|
||||
return
|
||||
}
|
||||
res.type(app.locals.options.format)
|
||||
res.set('Content-Disposition', 'inline')
|
||||
res.send(await render(html, app.locals.options))
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue