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.rating && image.rating === 'E' && (<Explicit fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
59 {image.props.rating && image.rating === 'M' && (<Mature fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
60 {image.props.rating && image.rating === 'T' && (<Teen fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
61 {image.props.rating && image.rating === 'G' && (<General fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
62 {image.props.rating && image.rating === 'NR' && (<NotRated fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)}
63
64 {image.props.warnings && image.warning === 'NW' && (<NoWarnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
65 {image.props.warnings && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
66 {image.props.warnings && image.warning === 'W' && (<Warnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)}
67
68 {image.props.category && image.category === 'F' && (<Yuri fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
69 {image.props.category && image.category === 'M' && (<Yaoi fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
70 {image.props.category && image.category === 'FM' && (<Het fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
71 {image.props.category && image.category === 'G' && (<Gen fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
72 {image.props.category && image.category === 'MX' && (<MultiShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
73 {image.props.category && image.category === 'O' && (<OtherShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)}
74 </div>
75 <div
76 style={{
77 fontSize: 54,
78 justifyContent: "center",
79 textAlign: "center",
80 fontFamily: titleFont,
81 fontWeight: "bold",
82 color: theme.color,
83 textTransform: (image.props.uppercaseTitle ? 'uppercase' : 'none')
84 }}
85 >
86 {image.titleLine}
87 </div>
88 <div
89 style={{
90 fontSize: 42,
91 display: "flex",
92 justifyContent: "center",
93 fontFamily: titleFont,
94 color: theme.color
95 }}
96 >
97 {`by ${image.authorLine}`}
98 </div>
99 {image.chapterLine !== '' && (<div
100 style={{
101 fontStyle: "italic",
102 fontSize: 36,
103 fontFamily: titleFont,
104 display: "flex",
105 justifyContent: "center",
106 color: theme.color,
107 textTransform: (image.props.uppercaseChapterName ? 'uppercase' : 'none')
108 }}
109 >
110 {image.chapterLine}
111 </div>)}
112 </div>
113 <div
114 style={{
115 backgroundColor: theme.descBackground,
116 padding: 20,
117 display: "flex",
118 flexDirection: "column",
119 flexGrow: 1,
120 color: theme.descColor,
121 alignItems: "flex-end"
122 }}
123 >
124 {image.props.charTags && (<div
125 style={{
126 display: "flex",
127 flexWrap: "wrap",
128 gap: 5,
129 fontSize: 18,
130 width: "100%",
131 marginBottom: 5
132 }}
133 >
134 {image.charTags.map(c => (
135 <span
136 style={{
137 backgroundColor: theme.accent2,
138 color: theme.accent2Color,
139 padding: "3px 5px",
140 borderRadius: 5
141 }}
142 >
143 {c}
144 </span>
145 ))}
146 </div>)}
147 {image.props.relTags && (<div
148 style={{
149 display: "flex",
150 flexWrap: "wrap",
151 gap: 5,
152 fontSize: 18,
153 width: "100%",
154 marginBottom: 5
155 }}
156 >
157 {image.relTags.map(r => (
158 <span
159 style={{
160 backgroundColor: theme.accent3,
161 color: theme.accent3Color,
162 padding: "3px 5px",
163 borderRadius: 5
164 }}
165 >
166 {r}
167 </span>
168 ))}
169 </div>)}
170 {image.props.freeTags && (<div
171 style={{
172 display: "flex",
173 flexWrap: "wrap",
174 gap: 5,
175 fontSize: 18,
176 width: "100%",
177 marginBottom: 5
178 }}
179 >
180 {image.freeTags.map(f => (
181 <span
182 style={{
183 backgroundColor: theme.accent4,
184 color: theme.accent4Color,
185 padding: "3px 5px",
186 borderRadius: 5
187 }}
188 >
189 {f}
190 </span>
191 ))}
192 </div>)}
193 {image.props.summary && (<div
194 style={{
195 display: "flex",
196 flexDirection: "column",
197 flexGrow: 1,
198 width: '100%'
199 }}
200 >
201 {image.summary.map(l => (
202 <div
203 style={{
204 width: "100%",
205 marginBottom: 10
206 }}
207 >
208 {l}
209 </div>
210 ))}
211 </div>)}
212 <div
213 style={{
214 textAlign: "right",
215 fontSize: 18,
216 display: "flex",
217 justifyContent: "flex-end",
218 alignItems: "center",
219 color: theme.accent2
220 }}
221 >
222 {image.props.wordcount && `${image.words} words • `}{(image.props.chapters && image.chapterCount !== null) && `${image.chapterCount} chapters • `}{image.props.postedAt && `posted on ${image.postedAt} • `}{image.props.updatedAt && `updated on ${image.updatedAt} • `}{process.env.ARCHIVE}/{addr}
223 </div>
224 </div>
225 </div>
226 ),
227 opts
228 )
229}