Skip to content

Commit f875ba8

Browse files
Merge pull request #582 from onnimonni/add-support-for-high-quality-and-responsive-youtube-images
2 parents 154c816 + 8f1ad74 commit f875ba8

File tree

1 file changed

+64
-11
lines changed

1 file changed

+64
-11
lines changed

packages/react-notion-x/src/components/lite-youtube-embed.tsx

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,44 @@ const qs = (params: Record<string, string>) => {
1010
.join('&')
1111
}
1212

13+
type ImageType = 'jpg' | 'webp';
14+
// Define a type for video resolutions
15+
type VideoResolution = 120 | 320 | 480 | 640 | 1280;
16+
17+
const resolutions: VideoResolution[] = [120, 320, 480, 640, 1280];
18+
19+
const resolutionMap: Record<VideoResolution, string> = {
20+
120: 'default',
21+
320: 'mqdefault',
22+
480: 'hqdefault',
23+
640: 'sddefault',
24+
1280: 'maxresdefault'
25+
// 2k, 4k, 8k images don't seem to be available
26+
// Source: https://longzero.com/articles/youtube-thumbnail-sizes-url/
27+
};
28+
29+
// Function to get the poster URL based on the resolution type
30+
function getPosterUrl(id: string, resolution: VideoResolution = 480, type: ImageType = 'jpg'): string {
31+
// Return the appropriate URL based on the image type
32+
if (type === 'webp') {
33+
return `https://i.ytimg.com/vi_webp/${id}/${resolutionMap[resolution]}.webp`;
34+
}
35+
// Default to jpg
36+
return `https://i.ytimg.com/vi/${id}/${resolutionMap[resolution]}.jpg`;
37+
}
38+
39+
function generateSrcSet(id: string, type: ImageType = 'jpg'): string {
40+
return resolutions
41+
.map((resolution) => `${getPosterUrl(id, resolution, type)} ${resolution}w`)
42+
.join(', ');
43+
}
44+
45+
function generateSizes(): string {
46+
return resolutions
47+
.map((resolution) => `(max-width: ${resolution}px) ${resolution}px`)
48+
.join(', ');
49+
}
50+
1351
export function LiteYouTubeEmbed({
1452
id,
1553
defaultPlay = false,
@@ -38,10 +76,7 @@ export function LiteYouTubeEmbed({
3876
() => qs({ autoplay: '1', mute: muteParam, ...params }),
3977
[muteParam, params]
4078
)
41-
// const mobileResolution = 'hqdefault'
42-
// const desktopResolution = 'maxresdefault'
43-
const resolution = 'hqdefault'
44-
const posterUrl = `https://i.ytimg.com/vi/${id}/${resolution}.jpg`
79+
4580
const ytUrl = 'https://www.youtube-nocookie.com'
4681
const iframeSrc = `${ytUrl}/embed/${id}?${queryString}`
4782

@@ -65,7 +100,11 @@ export function LiteYouTubeEmbed({
65100

66101
return (
67102
<>
68-
<link rel='preload' href={posterUrl} as='image' />
103+
{/*
104+
'it seems pretty unlikely for a browser to support preloading but not WebP images'
105+
Source: https://blog.laurenashpole.com/post/658079409151016960
106+
*/}
107+
<link rel='preload' as='image' href={getPosterUrl(id)} imageSrcSet={generateSrcSet(id, 'webp')} imageSizes={generateSizes()} />
69108

70109
{isPreconnected && (
71110
<>
@@ -96,12 +135,26 @@ export function LiteYouTubeEmbed({
96135
)}
97136
style={style}
98137
>
99-
<img
100-
src={posterUrl}
101-
className='notion-yt-thumbnail'
102-
loading={lazyImage ? 'lazy' : undefined}
103-
alt={alt}
104-
/>
138+
<picture>
139+
{/*
140+
Browsers which don't support srcSet will most likely not support webp too
141+
These browsers will then just get the default 480 size jpg
142+
*/}
143+
{resolutions.map((resolution) => (
144+
<source
145+
key={resolution}
146+
srcSet={`${getPosterUrl(id, resolution, 'webp')} ${resolution}w`}
147+
media={`(max-width: ${resolution}px)`}
148+
type='image/webp'
149+
/>
150+
))}
151+
<img
152+
src={getPosterUrl(id)}
153+
className='notion-yt-thumbnail'
154+
loading={lazyImage ? 'lazy' : undefined}
155+
alt={alt}
156+
/>
157+
</picture>
105158

106159
<div className='notion-yt-playbtn' />
107160

0 commit comments

Comments
 (0)