youtube-preview.js
87 lines
| 1 | const YOUTUBE_THUMBNAIL_BASE = 'https://i.ytimg.com/vi/'; |
| 2 | const YOUTUBE_VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/; |
| 3 | |
| 4 | const isYouTubeHost = (hostname) => { |
| 5 | const normalizedHostname = hostname.toLowerCase(); |
| 6 | |
| 7 | return ( |
| 8 | normalizedHostname === 'youtube.com' || |
| 9 | normalizedHostname.endsWith('.youtube.com') || |
| 10 | normalizedHostname === 'youtube-nocookie.com' || |
| 11 | normalizedHostname.endsWith('.youtube-nocookie.com') |
| 12 | ); |
| 13 | }; |
| 14 | |
| 15 | const getIframeSrc = (iframeCode) => { |
| 16 | if (!iframeCode || typeof iframeCode !== 'string') { |
| 17 | return null; |
| 18 | } |
| 19 | |
| 20 | if ( |
| 21 | typeof window !== 'undefined' && |
| 22 | typeof window.DOMParser !== 'undefined' |
| 23 | ) { |
| 24 | const parser = new window.DOMParser(); |
| 25 | const doc = parser.parseFromString(iframeCode, 'text/html'); |
| 26 | const iframe = doc.querySelector('iframe'); |
| 27 | |
| 28 | return iframe ? iframe.getAttribute('src') : null; |
| 29 | } |
| 30 | |
| 31 | const match = iframeCode.match(/<iframe\b[^>]*\bsrc=(["'])(.*?)\1/i); |
| 32 | |
| 33 | return match ? match[2] : null; |
| 34 | }; |
| 35 | |
| 36 | export const getYouTubeVideoIdFromSrc = (src) => { |
| 37 | if (!src || typeof src !== 'string') { |
| 38 | return null; |
| 39 | } |
| 40 | |
| 41 | let url; |
| 42 | try { |
| 43 | url = new URL(src); |
| 44 | } catch (e) { |
| 45 | return null; |
| 46 | } |
| 47 | |
| 48 | if (!isYouTubeHost(url.hostname)) { |
| 49 | return null; |
| 50 | } |
| 51 | |
| 52 | const match = url.pathname.match(/^\/embed\/([^/?#]+)/); |
| 53 | |
| 54 | if (!match) { |
| 55 | return null; |
| 56 | } |
| 57 | |
| 58 | let videoId; |
| 59 | try { |
| 60 | videoId = decodeURIComponent(match[1]); |
| 61 | } catch (e) { |
| 62 | return null; |
| 63 | } |
| 64 | |
| 65 | if (videoId === 'videoseries') { |
| 66 | return null; |
| 67 | } |
| 68 | |
| 69 | return YOUTUBE_VIDEO_ID_PATTERN.test(videoId) ? videoId : null; |
| 70 | }; |
| 71 | |
| 72 | export const getYouTubePreviewData = (iframeCode) => { |
| 73 | const src = getIframeSrc(iframeCode); |
| 74 | const videoId = getYouTubeVideoIdFromSrc(src); |
| 75 | |
| 76 | if (!videoId) { |
| 77 | return null; |
| 78 | } |
| 79 | |
| 80 | return { |
| 81 | videoId, |
| 82 | thumbnailUrl: `${YOUTUBE_THUMBNAIL_BASE}${encodeURIComponent( |
| 83 | videoId |
| 84 | )}/mqdefault.jpg`, |
| 85 | }; |
| 86 | }; |
| 87 |