PluginProbe ʕ •ᴥ•ʔ
VK Blocks / trunk
VK Blocks vtrunk
1.122.0 1.121.0 1.120.0 1.119.2 1.119.1 1.119.0 1.118.7 1.82.0.0 1.82.0.1 1.83.0.0 1.83.0.1 1.84.0.0 1.84.0.1 1.85.0.2 1.85.0.3 1.85.1.0 1.85.1.1 1.86.0.0 1.86.0.1 1.86.1.0 1.87.0.0 1.87.0.1 1.88.0.1 1.88.0.2 1.89.0.0 1.9.2 1.90.0.0 1.90.0.1 1.90.1.1 1.91.0.0 1.91.1.1 1.92.0.0 1.92.0.1 1.92.1.0 1.92.1.1 1.93.0.0 trunk 1.93.0.1 0.1.1 1.93.1.0 0.10.3 1.93.1.1 0.11.0 1.94.0.0 0.12.6 1.94.0.1 0.15.1 1.94.1.0 0.16.2 1.94.2.1 0.17.6 1.94.2.2 0.2.2 1.95.0.3 0.22.4 1.95.0.4 0.26.7 1.96.2.0 0.3.2 1.96.2.1 0.31.0 1.97.0.1 0.35.5 1.97.0.2 0.37.5 1.99.0.0 0.38.8 1.99.0.1 0.39.4 0.4.7 0.41.0 0.42.0 0.44.13 0.45.2 0.46.1 0.47.0 0.48.0 0.49.8 0.5.2 0.50.3 0.51.0 0.52.2 0.53.2 0.54.0 0.55.0 0.56.2 0.56.3 0.57.6 0.58.10 0.59.0 0.6.0 0.60.1 0.61.2 0.7.1 0.8.2 1.0.16 1.10.0 1.100.0.0 1.100.0.1 1.102.0.0 1.102.0.1 1.103.0.0 1.104.0.0 1.104.0.1 1.105.1.0 1.105.1.1 1.106.0.0 1.106.0.1 1.107.0.0 1.107.0.1 1.107.0.2 1.108.0.0 1.108.0.1 1.109.0.0 1.109.0.1 1.11.4 1.110.0.0 1.110.0.1 1.111.0.0 1.111.0.1 1.111.0.2 1.112.0.0 1.112.0.1 1.113.0.0 1.113.0.1 1.114.0.0 1.114.0.1 1.114.2.0 1.114.2.1 1.115.0.0 1.115.0.1 1.115.1.0 1.115.1.1 1.115.2.0 1.115.2.1 1.116.0.0 1.116.0.1 1.116.1.0 1.116.1.1 1.116.2.0 1.117.0.0 1.117.0.1 1.117.1.0 1.118.2 1.118.5 1.12.0 1.13.2 1.14.1 1.15.1 1.16.11 1.17.0 1.18.6 1.19.0 1.19.1 1.2.3 1.20.7 1.21.0 1.22.4 1.23.0 1.24.5 1.25.1 1.26.2 1.27.7.2 1.28.0.1 1.29.2.0 1.3.9 1.30.0.1 1.31.0.1 1.32.0.2 1.33.2.1 1.36.1.5 1.37.0.0 1.39.2.1 1.4.6 1.40.0.1 1.40.1.0 1.40.1.1 1.41.0.0 1.41.0.1 1.41.2.2 1.41.2.3 1.43.0.0 1.43.0.1 1.43.0.2 1.44.0.0 1.44.0.1 1.45.0.0 1.45.0.1 1.46.0.0 1.46.0.1 1.47.0.0 1.47.0.1 1.47.1.0 1.48.0.0 1.48.0.1 1.48.0.2 1.48.1.0 1.48.1.1 1.5.0 1.50.0.0 1.50.0.1 1.51.0.0 1.51.0.1 1.52.0.0 1.52.0.1 1.53.0.0 1.53.0.1 1.54.0.0 1.54.0.1 1.55.0.0 1.55.0.1 1.56.0.0 1.56.0.1 1.57.0.0 1.57.0.2 1.57.0.3 1.57.0.4 1.57.0.5 1.57.1.0 1.57.1.1 1.57.1.2 1.58.0.0 1.58.0.1 1.59.0.0 1.59.0.1 1.6.0 1.60.0.0 1.60.0.1 1.63.0.0 1.63.0.1 1.64.0.0 1.64.0.1 1.64.1.0 1.64.1.2 1.67.0.0 1.67.0.1 1.68.0.0 1.68.0.1 1.69.0.0 1.69.0.1 1.69.1.1 1.69.1.2 1.7.1 1.70.0.0 1.70.0.1 1.71.0.0 1.71.0.1 1.72.0.0 1.72.1.0 1.72.1.1 1.73.0.0 1.73.0.1 1.74.0.0 1.74.0.1 1.75.0.0 1.75.1.0 1.75.1.1 1.76.0.0 1.76.0.1 1.76.1.0 1.76.1.1 1.76.2.0 1.76.2.1 1.77.0.0 1.77.0.1 1.78.0.0 1.78.0.1 1.79.0.0 1.79.0.1 1.79.0.2 1.79.0.3 1.79.1.0 1.79.1.1 1.8.2 1.80.1.0 1.80.1.1 1.80.1.2 1.81.0.1 1.81.0.2
vk-blocks / src / blocks / visual-embed / utils / youtube-preview.js
vk-blocks / src / blocks / visual-embed / utils Last commit date
get-css-length.js 1 week ago match-url-pattern.js 1 month ago youtube-preview.js 1 week ago
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