Skip to content

Commit

Permalink
Merge pull request #582 from onnimonni/add-support-for-high-quality-a…
Browse files Browse the repository at this point in the history
…nd-responsive-youtube-images
  • Loading branch information
transitive-bullshit authored Nov 18, 2024
2 parents 154c816 + 8f1ad74 commit f875ba8
Showing 1 changed file with 64 additions and 11 deletions.
75 changes: 64 additions & 11 deletions packages/react-notion-x/src/components/lite-youtube-embed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,44 @@ const qs = (params: Record<string, string>) => {
.join('&')
}

type ImageType = 'jpg' | 'webp';
// Define a type for video resolutions
type VideoResolution = 120 | 320 | 480 | 640 | 1280;

const resolutions: VideoResolution[] = [120, 320, 480, 640, 1280];

const resolutionMap: Record<VideoResolution, string> = {
120: 'default',
320: 'mqdefault',
480: 'hqdefault',
640: 'sddefault',
1280: 'maxresdefault'
// 2k, 4k, 8k images don't seem to be available
// Source: https://longzero.com/articles/youtube-thumbnail-sizes-url/
};

// Function to get the poster URL based on the resolution type
function getPosterUrl(id: string, resolution: VideoResolution = 480, type: ImageType = 'jpg'): string {
// Return the appropriate URL based on the image type
if (type === 'webp') {
return `https://i.ytimg.com/vi_webp/${id}/${resolutionMap[resolution]}.webp`;
}
// Default to jpg
return `https://i.ytimg.com/vi/${id}/${resolutionMap[resolution]}.jpg`;
}

function generateSrcSet(id: string, type: ImageType = 'jpg'): string {
return resolutions
.map((resolution) => `${getPosterUrl(id, resolution, type)} ${resolution}w`)
.join(', ');
}

function generateSizes(): string {
return resolutions
.map((resolution) => `(max-width: ${resolution}px) ${resolution}px`)
.join(', ');
}

export function LiteYouTubeEmbed({
id,
defaultPlay = false,
Expand Down Expand Up @@ -38,10 +76,7 @@ export function LiteYouTubeEmbed({
() => qs({ autoplay: '1', mute: muteParam, ...params }),
[muteParam, params]
)
// const mobileResolution = 'hqdefault'
// const desktopResolution = 'maxresdefault'
const resolution = 'hqdefault'
const posterUrl = `https://i.ytimg.com/vi/${id}/${resolution}.jpg`

const ytUrl = 'https://www.youtube-nocookie.com'
const iframeSrc = `${ytUrl}/embed/${id}?${queryString}`

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

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

{isPreconnected && (
<>
Expand Down Expand Up @@ -96,12 +135,26 @@ export function LiteYouTubeEmbed({
)}
style={style}
>
<img
src={posterUrl}
className='notion-yt-thumbnail'
loading={lazyImage ? 'lazy' : undefined}
alt={alt}
/>
<picture>
{/*
Browsers which don't support srcSet will most likely not support webp too
These browsers will then just get the default 480 size jpg
*/}
{resolutions.map((resolution) => (
<source
key={resolution}
srcSet={`${getPosterUrl(id, resolution, 'webp')} ${resolution}w`}
media={`(max-width: ${resolution}px)`}
type='image/webp'
/>
))}
<img
src={getPosterUrl(id)}
className='notion-yt-thumbnail'
loading={lazyImage ? 'lazy' : undefined}
alt={alt}
/>
</picture>

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

Expand Down

0 comments on commit f875ba8

Please sign in to comment.