Skip to content

Commit 0501c08

Browse files
committed
feat: Add handleYoutube function
1 parent 9b3e083 commit 0501c08

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

config.default.ini.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,18 @@
132132
; Defines how often an error must occur before it is reported to the user
133133
report_limit = 1
134134

135+
[youtube]
136+
137+
; Whether to use an iframe to directly embed YouTube videos in feeds.
138+
; If false, a clickable video thumbnail is used instead. This avoids sending a referrer to YouTube or only getting the error 153 if suppressing the referrer browser-wide.
139+
iframe = true
140+
141+
; Use the youtube-nocookie.com domain instead of youtube.com for iframe embeds.
142+
; Only relevant if youtube.iframe is true.
143+
; See the following for a description:
144+
; https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode
145+
nocookie = false
146+
135147
; --- Cache specific configuration ---------------------------------------------
136148

137149
[FileCache]

docs/06_Helper_functions/index.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,20 @@ foreach ($urls as $url) {
355355
$lastmod = $url['lastmod'];
356356
}
357357
```
358+
359+
# handleYoutube(string $html): string
360+
361+
Use this function to throw a YouTube link, iframe tag or video ID and get a HTML snippet that returns a normalized iframe tag or clickable image thumbnail, depending on system configuration.
362+
363+
```php
364+
$result = handleYoutube('naYc5X6EL_Y');
365+
366+
$result = handleYoutube('https://www.youtube.com/watch?v=naYc5X6EL_Y');
367+
368+
$result = handleYoutube('https://www.youtube.com/embed/naYc5X6EL_Y');
369+
370+
$iframe = '<iframe width="560" height="315" src="https://www.youtube.com/embed/naYc5X6EL_Y?si=abcdefgh" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>';
371+
$result = handleYoutube($iframe);
372+
```
373+
374+
[Defined in lib/html.php](https://github.com/RSS-Bridge/rss-bridge/blob/master/lib/html.php)

lib/html.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,67 @@ function markdownToHtml($string, $config = [])
477477
}
478478
return $Parsedown->text($string);
479479
}
480+
481+
/**
482+
* Handle a YouTube video by either returning an iframe that embeds the video
483+
* or by returning a clickable image (an <img> in a <a> tag).
484+
* The system config can specify which to use, and whether to use youtube-nocookie.com over youtube.com.
485+
*
486+
* @param string $string A string containing a YouTube video URL or directly a video ID.
487+
* @return string A HTML snippet either with an iframe or a clickable thumbnail. An empty string if no YouTube video ID is found.
488+
*/
489+
function handleYoutube(string $string)
490+
{
491+
$useIframe = Configuration::getConfig('youtube', 'iframe');
492+
$useNocookie = Configuration::getConfig('youtube', 'nocookie');
493+
494+
// sourced from https://gist.github.com/afeld/1254889?permalink_comment_id=3580082#gistcomment-3580082
495+
$regex = '#(?:https?://|//)?(?:www\.|m\.|.+\.)?(?:youtu\.be/|youtube(?:-nocookie)\.com/(?:embed/|v/|shorts/|feeds/api/videos/|watch\?v=|watch\?.+&v=))([\w-]{11})#i';
496+
if (preg_match($regex, $string, $matches) === 1) {
497+
$videoID = $matches[1];
498+
} elseif (preg_match('#[\w-]{11}#i', $string, $matches2) === 1) {
499+
$videoID = $matches2[0];
500+
} else {
501+
return '';
502+
}
503+
504+
if ($useIframe) {
505+
if ($useNocookie) {
506+
$embedUri = 'https://www.youtube-nocookie.com/embed/' . $videoID;
507+
} else {
508+
$embedUri = 'https://www.youtube.com/embed/' . $videoID;
509+
}
510+
511+
return sprintf(<<<EOD
512+
<iframe width="560" height="315" src="%s" title="YouTube video player" frameborder="0"
513+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
514+
referrerpolicy="strict-origin" allowfullscreen></iframe>'
515+
EOD
516+
, $embedUri);
517+
} else {
518+
$videoUri = 'https://www.youtube.com/watch?v=' . $videoID;
519+
520+
$thumbnailJpegBaseUri = 'https://i.ytimg.com/vi/' . $videoID;
521+
$jpegSrcset = sprintf(
522+
'%1$s/mqdefault.jpg 320w, %1$s/0.jpg 480w, %1$s/hqdefault.jpg 481w, %1$s/sddefault.jpg 640w, %1$s/hq720.jpg 720w, %1$s/maxresdefault.jpg 721w',
523+
$thumbnailJpegBaseUri
524+
);
525+
526+
$thumbnailWebpBaseUri = 'https://i.ytimg.com/vi_webp/' . $videoID;
527+
$webpSrcset = sprintf(
528+
'%1$s/mqdefault.webp 320w, %1$s/0.webp 480w, %1$s/hqdefault.webp 481w, %1$s/sddefault.webp 640w, %1$s/hq720.webp 720w, %1$s/maxresdefault.webp 721w',
529+
$thumbnailWebpBaseUri
530+
);
531+
532+
$fallbackUri = $thumbnailJpegBaseUri . '/maxresdefault.jpg';
533+
534+
return sprintf(<<<EOD
535+
<a href="%s">
536+
<picture>
537+
<source srcset="%s" type="image/webp" referrerpolicy="no-referrer" />
538+
<img srcset="%s" src="%s" alt="Video thumbnail" title="YouTube video thumbnail" referrerpolicy="no-referrer" />
539+
</picture>
540+
</a>
541+
EOD, $videoUri, $webpSrcset, $jpegSrcset, $fallbackUri);
542+
}
543+
}

0 commit comments

Comments
 (0)