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}