Skip to content

Commit fd7cf2a

Browse files
committed
feat(social): add movie opengraph image
1 parent d533d62 commit fd7cf2a

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/* eslint-disable jsx-a11y/alt-text -- The entire response is an image */
2+
/* eslint-disable @next/next/no-img-element -- The entire response is an image */
3+
import { loadGoogleFont } from '@/core/utils/social';
4+
import { movieService } from '@/services/movie.service';
5+
import { ImageResponse } from 'next/og';
6+
7+
export const runtime = 'edge';
8+
9+
export const alt = 'Kanojo';
10+
11+
export const contentType = 'image/png';
12+
13+
/**
14+
* Generates an OpenGraph image for a movie.
15+
* @param properties - The properties passed to the image.
16+
* @param properties.params - The parameters passed to the image.
17+
* @param properties.params.id - The ID of the movie to generate the image for.
18+
* @returns The generated image response.
19+
* @async
20+
*/
21+
export default async function Image({ params }: { params: { id: string } }) {
22+
const movie = await movieService.getMovie(Number(params.id));
23+
24+
// If there are roles, get the first 3 ones into an array.
25+
const roles = movie.roles?.slice(0, 3).map((role) => role);
26+
// If there are more than 3 roles, get a count of the remaining ones.
27+
const remainingRoles =
28+
movie.roles && movie.roles?.length > 3 ? movie.roles.length - 3 : 0;
29+
30+
return new ImageResponse(
31+
(
32+
<div tw="flex flex-row w-full h-full items-center justify-center bg-pink-200">
33+
<div tw="flex flex-col items-center justify-center mr-6">
34+
{movie.front_cover_url?.poster ? (
35+
<img
36+
alt={movie.display_name}
37+
src={movie?.front_cover_url?.poster}
38+
style={{ height: '600px', objectFit: 'cover', width: '400px' }}
39+
tw="rounded-lg border-4 border-pink-300"
40+
/>
41+
) : (
42+
<div
43+
style={{ height: '600px', objectFit: 'cover', width: '400px' }}
44+
tw="flex items-center justify-center rounded-lg border-4 border-pink-300 bg-pink-100"
45+
>
46+
<p tw="text-6xl text-center font-black text-pink-300">No Image</p>
47+
</div>
48+
)}
49+
</div>
50+
<div
51+
style={{ height: '600px', maxWidth: '740px' }}
52+
tw="flex flex-col h-full justify-between"
53+
>
54+
<div tw="flex flex-col items-start justify-start">
55+
<div tw="flex flex-row items-center justify-between mb-1">
56+
<div tw="flex items-center justify-between bg-pink-800 pt-0 pb-0 pr-2 pl-2 rounded-lg mr-2">
57+
<p tw="text-lg font-bold text-white mt-0 mb-0">
58+
{movie.dvd_id}
59+
</p>
60+
</div>
61+
{movie.format && (
62+
<div tw="flex items-center justify-between bg-pink-800 pt-0 pb-0 pr-2 pl-2 rounded-lg">
63+
<p tw="text-lg font-bold text-white mt-0 mb-0">
64+
{movie.format}
65+
</p>
66+
</div>
67+
)}
68+
</div>
69+
<h1 tw="text-4xl font-black mb-0 mt-0 line-clamp-2 ellipsis text-pink-900">
70+
{movie.display_name}
71+
</h1>
72+
{movie.alternative_name && (
73+
<p tw="text-2xl font-bold mb-2 mt-0 line-clamp-1 ellipsis text-pink-700">
74+
{movie.alternative_name}
75+
</p>
76+
)}
77+
</div>
78+
<div tw="flex flex-row">
79+
{roles?.map((role) => (
80+
// Make small cards for each role.
81+
<div
82+
key={role.id}
83+
tw="flex flex-col items-center justify-center mr-4"
84+
>
85+
<img
86+
alt={role.person?.display_name}
87+
src={role.person?.profile_url?.poster ?? ''}
88+
style={{
89+
height: '200px',
90+
objectFit: 'cover',
91+
width: '150px',
92+
}}
93+
tw="rounded-lg border-2 border-pink-300"
94+
/>
95+
<p tw="text-xl font-bold text-center mt-2 mb-0 line-clamp-1 ellipsis text-pink-900">
96+
{role.person?.display_name}
97+
</p>
98+
<p tw="text-lg text-center -mt-1 mb-0 line-clamp-1 ellipsis text-pink-700">
99+
{role.age} years old
100+
</p>
101+
</div>
102+
))}
103+
{remainingRoles > 0 && (
104+
// Make a card for the remaining roles count.
105+
<div tw="flex flex-col items-center justify-start mr-4">
106+
<div
107+
style={{ height: '200px', width: '150px' }}
108+
tw="flex flex-col h-full justify-center items-center rounded-lg border-2 border-pink-300 bg-pink-100"
109+
>
110+
<p tw="text-6xl font-black text-center text-pink-300 mt-2">{`+${remainingRoles}`}</p>
111+
</div>
112+
</div>
113+
)}
114+
</div>
115+
</div>
116+
</div>
117+
),
118+
{
119+
fonts: [
120+
{
121+
data: await loadGoogleFont('Noto+Sans+JP', '700'),
122+
name: 'Noto Sans JP',
123+
style: 'normal',
124+
},
125+
{
126+
data: await loadGoogleFont('Noto+Sans+JP', '800'),
127+
name: 'Noto Sans JP',
128+
style: 'normal',
129+
},
130+
{
131+
data: await loadGoogleFont('Noto+Sans+JP', '900'),
132+
name: 'Noto Sans JP',
133+
style: 'normal',
134+
},
135+
],
136+
height: 630,
137+
width: 1200,
138+
},
139+
);
140+
}
141+
/* eslint-enable jsx-a11y/alt-text -- The entire response is an image */
142+
/* eslint-enable @next/next/no-img-element -- The entire response is an image */

0 commit comments

Comments
 (0)