redirecter for ao3 that adds opengraph metadata
1import { ImageResponse } from "next/og" 2import General from "@/icons/general.js" 3import Teen from "@/icons/teen.js" 4import Mature from "@/icons/mature.js" 5import Explicit from "@/icons/explicit.js" 6import NotRated from "@/icons/notrated.js" 7import Gen from "@/icons/gen.js" 8import Yaoi from "@/icons/yaoi.js" 9import Yuri from "@/icons/yuri.js" 10import Het from "@/icons/het.js" 11import OtherShip from "@/icons/other.js" 12import MultiShip from "@/icons/multi.js" 13import NoWarnings from "@/icons/nowarnings.js" 14import Warnings from "@/icons/warnings.js" 15import ChoseNotToWarn from "@/icons/chosenottowarn.js" 16 17export default async function OGImage ({ theme, baseFont, titleFont, image, addr, opts }) { 18 return new ImageResponse( 19 ( 20 <div 21 style={{ 22 display: "flex", 23 flexDirection: "column", 24 color: theme.color, 25 backgroundColor: theme.background, 26 fontFamily: baseFont, 27 fontSize: 24, 28 padding: 20, 29 width: "100%", 30 height: "100%", 31 }} 32 > 33 <div 34 style={{ 35 display: "flex", 36 flexDirection: "column", 37 marginBottom: 20 38 }} 39 > 40 <div 41 style={{ 42 textTransform: "uppercase", 43 display: "flex", 44 justifyContent: "center", 45 gap: 10, 46 color: theme.accent, 47 alignItems: "center" 48 }} 49 > 50 <div 51 style={{ 52 display: "flex" 53 }} 54 > 55 {image.topLine} 56 </div> 57 58 {image.props.get('rating') === 'true' && image.rating === 'E' && (<Explicit fg={theme.background} bg={theme.accent} width={28} height={28} />)} 59 {image.props.get('rating') === 'true' && image.rating === 'M' && (<Mature fg={theme.background} bg={theme.accent} width={28} height={28} />)} 60 {image.props.get('rating') === 'true' && image.rating === 'T' && (<Teen fg={theme.background} bg={theme.accent} width={28} height={28} />)} 61 {image.props.get('rating') === 'true' && image.rating === 'G' && (<General fg={theme.background} bg={theme.accent} width={28} height={28} />)} 62 {image.props.get('rating') === 'true' && image.rating === 'NR' && (<NotRated fg={theme.background} bg={theme.accent} width={28} height={28} />)} 63 64 {image.props.get('warnings') === 'true' && image.warning === 'NW' && (<NoWarnings fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 65 {image.props.get('warnings') === 'true' && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 66 {image.props.get('warnings') === 'true' && image.warning === 'W' && (<Warnings fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 67 68 {image.props.get('category') === 'true' && image.category === 'F' && (<Yuri fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 69 {image.props.get('category') === 'true' && image.category === 'M' && (<Yaoi fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 70 {image.props.get('category') === 'true' && image.category === 'FM' && (<Het fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 71 {image.props.get('category') === 'true' && image.category === 'G' && (<Gen fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 72 {image.props.get('category') === 'true' && image.category === 'MX' && (<MultiShip fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 73 {image.props.get('category') === 'true' && image.category === 'O' && (<OtherShip fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 74 </div> 75 <div 76 style={{ 77 fontSize: 54, 78 justifyContent: "center", 79 fontFamily: titleFont, 80 fontWeight: "bold", 81 color: theme.color 82 }} 83 > 84 {image.titleLine} 85 </div> 86 <div 87 style={{ 88 fontSize: 42, 89 display: "flex", 90 justifyContent: "center", 91 fontFamily: titleFont, 92 color: theme.color 93 }} 94 > 95 {`by ${image.authorLine}`} 96 </div> 97 <div 98 style={{ 99 fontStyle: "italic", 100 fontSize: 36, 101 fontFamily: titleFont, 102 display: "flex", 103 justifyContent: "center", 104 color: theme.color 105 }} 106 > 107 {image.chapterLine} 108 </div> 109 </div> 110 <div 111 style={{ 112 backgroundColor: theme.descBackground, 113 padding: 20, 114 display: "flex", 115 flexDirection: "column", 116 flexGrow: 1, 117 color: theme.descColor, 118 alignItems: "flex-end" 119 }} 120 > 121 {image.props.get("charTags") === 'true' && (<div 122 style={{ 123 display: "flex", 124 flexWrap: "wrap", 125 gap: 5, 126 fontSize: 18, 127 width: "100%", 128 marginBottom: 5 129 }} 130 > 131 {image.charTags.map(c => ( 132 <span 133 style={{ 134 backgroundColor: theme.accent2, 135 color: theme.descBackground, 136 padding: "3px 5px", 137 borderRadius: 5 138 }} 139 > 140 {c} 141 </span> 142 ))} 143 </div>)} 144 {image.props.get("relTags") === 'true' && (<div 145 style={{ 146 display: "flex", 147 flexWrap: "wrap", 148 gap: 5, 149 fontSize: 18, 150 width: "100%", 151 marginBottom: 5 152 }} 153 > 154 {image.relTags.map(r => ( 155 <span 156 style={{ 157 backgroundColor: theme.accent3, 158 color: theme.descBackground, 159 padding: "3px 5px", 160 borderRadius: 5 161 }} 162 > 163 {r} 164 </span> 165 ))} 166 </div>)} 167 {image.props.get("freeTags") === 'true' && (<div 168 style={{ 169 display: "flex", 170 flexWrap: "wrap", 171 gap: 5, 172 fontSize: 18, 173 width: "100%", 174 marginBottom: 5 175 }} 176 > 177 {image.freeTags.map(f => ( 178 <span 179 style={{ 180 backgroundColor: theme.accent4, 181 color: theme.descBackground, 182 padding: "3px 5px", 183 borderRadius: 5 184 }} 185 > 186 {f} 187 </span> 188 ))} 189 </div>)} 190 {image.props.get("summary") === 'true' && (<div 191 style={{ 192 display: "flex", 193 flexDirection: "column", 194 flexGrow: 1, 195 width: '100%' 196 }} 197 > 198 {image.summary.map(l => ( 199 <div 200 style={{ 201 width: "100%", 202 marginBottom: 10 203 }} 204 > 205 {l} 206 </div> 207 ))} 208 </div>)} 209 <div 210 style={{ 211 textAlign: "right", 212 fontSize: 18, 213 display: "flex", 214 justifyContent: "flex-end", 215 alignItems: "center", 216 color: theme.accent2 217 }} 218 > 219 {image.props.get("wordcount") === 'true' && `${image.words} words • `}{(image.props.get("chapters") === 'true' && image.chapterCount !== null) && `${image.chapterCount} chapters • `}{image.props.get("postedAt") === 'true' && `posted on ${image.postedAt}`}{image.props.get("updatedAt") === 'true' && `updated on ${image.updatedAt}`}{process.env.ARCHIVE}/{addr} 220 </div> 221 </div> 222 </div> 223 ), 224 opts 225 ) 226}