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