redirecter for ao3 that adds opengraph metadata
1import { $ } from "bun"
2import DOM from "fauxdom"
3import themes from '../themes.js'
4import baseFonts from '../baseFonts.js'
5
6async function Image ({ data, addr, opts = {} }) {
7 const filename = addr.replaceAll("/", "-")
8 const cachedFile = Bun.file(`imagecache/${filename}.webp`)
9 const cacheTime = Date.now() - (24*60*60*1000)
10 if (await cachedFile.exists() && (await cachedFile.mtime) > cacheTime) {
11 return Response(await cachedFile.arrayBuffer(), {
12 headers: {
13 'Content-Type': 'image/webp'
14 }
15 })
16 }
17 const authorsFormatted = (data.authors
18 ? data.authors.map((a) => {
19 if (a.anonymous) return "Anonymous"
20 if (a.pseud !== a.username) return `${a.pseud} (${a.username})`
21 return a.username
22 })
23 : [])
24 const authorString = authorsFormatted.length > 1
25 ? authorsFormatted.slice(0, -1).join(", ") + " & " +
26 authorsFormatted.slice(-1)[0]
27 : authorsFormatted[0]
28 const summaryDOM = new DOM(data.summary, {decodeEntities: true});
29 const summaryFormatted = summaryDOM.innerHTML.replace("<br />", "\n").replace(
30 /(<([^>]+)>)/ig,
31 "",
32 )
33 const titleString = `<b>${data.title}</b> by ${authorString}`
34 const chapterString = data.chapterInfo ? (data.chapterInfo.name
35 ? data.chapterInfo.name
36 : "Chapter " + data.chapterInfo.index) : ''
37 const chapterCountString = data.chapters
38 ? ' | <b>Chapters:</b> '+data.chapters.published+' / '+(
39 data.chapters.total
40 ? data.chapters.total
41 : '?'
42 )
43 : ''
44 const fandomString = (data.fandoms.length > 1 ? (data.fandoms.length <= 5 ? data.fandoms.slice(0, -1).join(", ")+" & "+data.fandoms.slice(-1) : data.fandoms.join(", ")+" (+"+(data.fandoms.length - 4)+")") : data.fandoms[0]).toUpperCase()
45 const theme = opts.theme ? opts.theme : 'ao3'
46 const baseFont = opts.baseFont ? opts.baseFont : 'Verdana'
47 const titleFont = opts.titleFont ? opts.titleFont : 'Georgia'
48 await $`magick -size 1520x300 -background none -font ${titleFont} -pointsize 64 -fill ${theme.color} pango:"<span size='16pt'>${fandomString}</span>\n${titleString}${chapterString !== '' ? "\n<span size='36pt'><i>"+chapterString+"</i></span></span>" : ''}" tmp/${filename}-title.png`
49 await $`magick -size 1520x480 xc:${theme.descBackground} tmp/${filename}-box.png`
50 await $`magick -size 1440x20 -background none -gravity East -font ${baseFont} -pointsize 18 -fill ${theme.descColor} caption:"https://archiveofourown.org/${addr}" tmp/${filename}-addr.png`
51 await $`magick -size 1440x400 -background none -font ${baseFont} -pointsize 22 -fill ${theme.descColor} pango:"<b>Wordcount:</b> ${data.words}${chapterCountString} | <b>Rating:</b> ${data.rating}\n\n${summaryFormatted}" tmp/${filename}-desc.png`
52 await $`magick -size 1600x900 xc:${theme.background} -draw "image over 40,40, 0,0 tmp/${filename}-title.png" -draw "image over 40,380, 0,0 tmp/${filename}-box.png" -draw "image over 80,420 0,0 tmp/${filename}-desc.png" -draw "image over 100,820 0,0 tmp/${filename}-addr.png" imagecache/${filename}.webp`
53 await $`rm tmp/${filename}-*.png`
54 const file = Bun.file(`imagecache/${filename}.webp`)
55 return Response(await file.arrayBuffer(), {
56 headers: {
57 'Content-Type': 'image/webp'
58 }
59 })
60}
61
62export default Image