@@ -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+
1351export 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