Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions bridges/AssociatedPressNewsBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,7 @@ private function processMediaPlaceholders($html, $id)
}

if ($media['type'] === 'YouTube') {
$div->outertext = <<<EOD
<iframe src="https://www.youtube.com/embed/{$media['externalId']}" width="560" height="315">
</iframe>
EOD;
$div->outertext = handleYoutube($media['externalId']);
}
}
}
Expand Down Expand Up @@ -249,10 +246,7 @@ private function processVideo($storyContent)
$video = $storyContent['media'][0];

if ($video['type'] === 'YouTube') {
$url = 'https://www.youtube.com/embed/' . $video['externalId'];
$html = <<<EOD
<iframe width="560" height="315" src="{$url}" frameborder="0" allowfullscreen></iframe>
EOD;
$html = handleYoutube($video['externalId']);
} else {
$html = <<<EOD
<video controls poster="https://storage.googleapis.com/afs-prod/media/{$video['id']}/800.jpeg" preload="none">
Expand Down
4 changes: 1 addition & 3 deletions bridges/Drive2ruBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,7 @@ private function adjustContent($content)
$node->outertext = '';
}
foreach ($content->find('iframe') as $node) {
preg_match('/embed\/(.*?)\?/', $node->src, $match);
$node->outertext = '<a href="https://www.youtube.com/watch?v=' . $match[1] .
'">https://www.youtube.com/watch?v=' . $match[1] . '</a>';
$node->outertext = handleYoutube($node->src);
}
return $content;
}
Expand Down
3 changes: 1 addition & 2 deletions bridges/EuronewsBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,7 @@ private function getItemContent($url)
$player_div = $html->find('.js-player-pfp', 0);
if ($player_div) {
$video_id = $player_div->getAttribute('data-video-id');
$video_url = 'https://www.youtube.com/watch?v=' . $video_id;
$content .= '<a href="' . $video_url . '">' . $video_url . '</a>';
$content .= handleYoutube($video_id);
}
}

Expand Down
15 changes: 5 additions & 10 deletions bridges/FallGuysBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,23 +94,18 @@ public function collectData()
if (count($mediaOptions) == count($mainContentOptions)) {
for ($i = 0; $i < count($mediaOptions); $i++) {
if (property_exists($mediaOptions[$i], 'youtubeVideo')) {
$videoUrl = 'https://youtu.be/' . $mediaOptions[$i]->youtubeVideo->contentId;
$image = $mainContentOptions[$i]->image->src ?? '';
$videoID = $mediaOptions[$i]->youtubeVideo->contentId;
$videoHtml = handleYoutube($videoID);

$content .= '<p>';
$content .= '<p>' . $videoHtml;

$image = $mainContentOptions[$i]->image->src ?? '';
if ($image != $headerImage) {
$contentImages[] = $image;

$content .= <<<HTML
<a href="{$videoUrl}"><img src="{$image}"></a><br>
HTML;
$content .= '<img src="{$image}"><br>';
}

$content .= <<<HTML
<i>(Video: <a href="{$videoUrl}">{$videoUrl}</a>)</i>
HTML;

$content .= '</p>';
}
}
Expand Down
3 changes: 1 addition & 2 deletions bridges/GenshinImpactBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ public function collectData()
if (preg_match($exp_youtube, $article_html, $matches)) {
// Replace the YouTube embed with a YouTube link
$yt_embed = $article_html->find('div[class="ttr-video-frame"]', 0);
$yt_link = sprintf('<a href="https://www.youtube.com/watch?v=%1$s">https://www.youtube.com/watch?v=%1$s</a>', $matches[1]);
$article_html = str_replace($yt_embed, $yt_link, $article_html);
$yt_embed->outertext = handleYoutube($yt_embed);
}
$item = [];
$item['title'] = $json_item['sTitle'];
Expand Down
6 changes: 1 addition & 5 deletions bridges/GolemBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,7 @@ private function extractContent($page, $prevcontent)
if (array_key_exists($i, $embedSrcs)) {
$src = $embedSrcs[$i];
if (preg_match('/youtube(-nocookie)?\.com/', $src, $match)) {
$placeholders[$i]->innertext = <<<EOT
<iframe width="560" height="315" src="$src" 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>';
EOT;
$placeholders[$i]->innertext = handleYoutube($src);
}
}
}
Expand Down
19 changes: 7 additions & 12 deletions bridges/HeiseBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public function collectData()

protected function parseItem(array $item)
{
$item['uri'] = 'https://www.heise.de/news/ChatGPT-Atlas-ist-ein-Quatsch-Browser-10964861.html';
$sessioncookie = $this->getInput('sessioncookie');

// strip rss parameter
Expand Down Expand Up @@ -207,19 +208,13 @@ private function addArticleToItem($item, $article)
//fix for embbedded youtube-videos
$oldlink = '';
foreach ($article->find('div.video__yt-container') as &$ytvideo) {
if (preg_match('/www.youtube.*?\"/', $ytvideo->innertext, $link) && $link[0] != $oldlink) {
//save link to prevent duplicates
$oldlink = $link[0];
$ytiframe = <<<EOT
<iframe width="560" height="315" src="https://$link[0] 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>
EOT;
//check if video is in header or article for correct possitioning
if (strpos($header->innertext, $link[0])) {
$item['content'] .= $ytiframe;
$ytResult = handleYoutube($ytvideo->innertext);
if ($ytResult) {
//check if video is in header or article for correct positioning
if (strpos($header->innertext, $ytvideo)) {
$item['content'] .= $ytResult;
} else {
$ytvideo->innertext .= $ytiframe;
$ytvideo->innertext .= $ytResult;
$reloadneeded = 1;
}
}
Expand Down
3 changes: 1 addition & 2 deletions bridges/RedditBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ private function collectDataInternal(): void
}
} elseif (isset($data->media) && $data->media->type == 'youtube.com') {
// Youtube link
$item['content'] = $this->createFigureLink($data->url, $data->media->oembed->thumbnail_url, 'YouTube');
//$item['content'] = htmlspecialchars_decode($data->media->oembed->html);
$item['content'] = handleYoutube($data->url);
} elseif (explode('.', $data->domain)[0] == 'self') {
// Crossposted text post
// TODO (optionally?) Fetch content of the original post.
Expand Down
12 changes: 1 addition & 11 deletions bridges/ReutersBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,17 +535,7 @@ private function handleArticleContent($contents)
EOD;
break;
case 'youtube':
$url = "https://www.youtube.com/embed/$cid";
$embed .= <<<EOD
<‌iframe
width="560"
height="315"
src="{$url}"
frameborder="0"
allowfullscreen
>
</iframe>
EOD;
$embed .= handleYoutube($cid);
break;
}
$description .= $embed;
Expand Down
15 changes: 9 additions & 6 deletions bridges/SteamCommunityBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,10 @@ private function collectMedia()
$mediaURI = $media->getAttribute('src');
$downloadURI = $mediaURI;

$content = '';

if ($category == 'videos') {
preg_match('/.*\/embed\/(.*)\?/', $mediaURI, $result);
$youtubeID = $result[1];
$mediaURI = 'https://img.youtube.com/vi/' . $youtubeID . '/hqdefault.jpg';
$downloadURI = 'https://www.youtube.com/watch?v=' . $youtubeID;
$content = handleYoutube($mediaURI);
}

$desc = '';
Expand All @@ -133,8 +132,12 @@ private function collectMedia()
$downloadURI = $htmlCard->find('a.downloadImage', 0)->href;
}

$item['content'] = '<p><a href="' . $downloadURI . '"><img src="' . $mediaURI . '"/></a></p>';
$item['content'] .= '<p>' . $desc . '</p>';
if (empty($content)) {
$content = '<p><a href="' . $downloadURI . '"><img src="' . $mediaURI . '"/></a></p>';
$content .= '<p>' . $desc . '</p>';
}

$item['content'] = $content;

$this->items[] = $item;

Expand Down
2 changes: 1 addition & 1 deletion bridges/TheDriveBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected function parseItem($feedItem)
$videoID = $youtubeVideoDiv->getAttribute('data-video-id');

//place <a> around the <div>
$youtubeVideoDiv->outertext = '<a href="https://www.youtube.com/watch?v=' . $videoID . '">' . $youtubeVideoDiv->outertext . '</a>';
$youtubeVideoDiv->outertext = handleYoutube($videoID);
}

$item['content'] = $html;
Expand Down
6 changes: 1 addition & 5 deletions bridges/WebfailBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,7 @@ private function extractArticle($html)
} elseif (!is_null($article->find('div.wf-video', 0))) { // Video type
$videoId = $this->getVideoId($article->find('div.wf-play', 0)->onclick);
$item['uri'] = 'https://www.youtube.com/watch?v=' . $videoId;
$item['content'] = '<a href="'
. $item['uri']
. '"><img src="http://img.youtube.com/vi/'
. $videoId
. '/0.jpg"></a>';
$item['content'] = handleYoutube($videoId);
} elseif (!is_null($article->find('video[id*=gif-]', 0))) { // Gif type
$item['uri'] = $this->getURI() . $article->find('a', 2)->href;
$item['content'] = '<video controls src="'
Expand Down
11 changes: 4 additions & 7 deletions bridges/YorushikaBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,10 @@ public function collectData()
$art_html = getSimpleHTMLDOMCached($url)->find('.text.inview', 0);
$art_html = defaultLinkTo($art_html, $this->getURI());

// Check if article contains a embed YouTube video
$exp_youtube = '/https:\/\/[w\.]+youtube\.com\/embed\/([\w]+)/m';
if (preg_match($exp_youtube, $art_html, $matches)) {
// Replace the YouTube embed with a YouTube link
$yt_embed = $art_html->find('iframe[src*="youtube.com"]', 0);
$yt_link = sprintf('<a href="https://www.youtube.com/watch?v=%1$s">https://www.youtube.com/watch?v=%1$s</a>', $matches[1]);
$art_html = str_replace($yt_embed, $yt_link, $art_html);
// Rewrite the YouTube embed with a YouTube link
$yt_embed = $art_html->find('iframe[src*="youtube.com"]', 0);
if ($yt_embed) {
$yt_embed->outertext = handleYoutube($yt_embed->outertext);
}


Expand Down
5 changes: 1 addition & 4 deletions bridges/YouTubeCommunityTabBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,7 @@ private function getAttachments($details)
$this->itemTitle = $this->feedName . ' posted a video';
}

$content = <<<EOD
<iframe width="100%" height="410" src="https://www.youtube.com/embed/{$attachments->videoRenderer->videoId}"
frameborder="0" allow="encrypted-media;" allowfullscreen></iframe>
EOD;
$content = handleYoutube($attachments->videoRenderer->videoId);
} elseif (isset($attachments->backstageImageRenderer)) {
// Image
if (empty($this->itemTitle)) {
Expand Down
3 changes: 1 addition & 2 deletions bridges/YouTubeFeedExpanderBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ protected function parseItem(array $item)
}
$embed = $embedURI . 'embed/' . $id;
if ($this->getInput('embed')) {
$iframe_fmt = '<iframe width="448" height="350" src="%s" title="%s" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>'; //phpcs:ignore
$iframe = sprintf($iframe_fmt, $embed, $item['title']) . '<br>';
$iframe = handleYoutube($id) . '<br>';
$item['content'] = $iframe . $item['content'];
}
if ($this->getInput('embedurl')) {
Expand Down
12 changes: 12 additions & 0 deletions config.default.ini.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@
; Defines how often an error must occur before it is reported to the user
report_limit = 1

[youtube]

; Whether to use an iframe to directly embed YouTube videos in feeds.
; 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.
iframe = true

; Use the youtube-nocookie.com domain instead of youtube.com for iframe embeds.
; Only relevant if youtube.iframe is true.
; See the following for a description:
; https://support.google.com/youtube/answer/171780?hl=en#zippy=%2Cturn-on-privacy-enhanced-mode
nocookie = false

; --- Cache specific configuration ---------------------------------------------

[FileCache]
Expand Down
17 changes: 17 additions & 0 deletions docs/06_Helper_functions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,20 @@ foreach ($urls as $url) {
$lastmod = $url['lastmod'];
}
```

# handleYoutube(string $html): string

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.

```php
$result = handleYoutube('naYc5X6EL_Y');

$result = handleYoutube('https://www.youtube.com/watch?v=naYc5X6EL_Y');

$result = handleYoutube('https://www.youtube.com/embed/naYc5X6EL_Y');

$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>';
$result = handleYoutube($iframe);
```

[Defined in lib/html.php](https://github.com/RSS-Bridge/rss-bridge/blob/master/lib/html.php)
64 changes: 64 additions & 0 deletions lib/html.php
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,67 @@ function markdownToHtml($string, $config = [])
}
return $Parsedown->text($string);
}

/**
* Handle a YouTube video by either returning an iframe that embeds the video
* or by returning a clickable image (an <img> in a <a> tag).
* The system config can specify which to use, and whether to use youtube-nocookie.com over youtube.com.
*
* @param string $string A string containing a YouTube video URL or directly a video ID.
* @return string A HTML snippet either with an iframe or a clickable thumbnail. An empty string if no YouTube video ID is found.
*/
function handleYoutube(string $string)
{
$useIframe = Configuration::getConfig('youtube', 'iframe');
$useNocookie = Configuration::getConfig('youtube', 'nocookie');

// sourced from https://gist.github.com/afeld/1254889?permalink_comment_id=3580082#gistcomment-3580082
$regex = '#(?:https?://|//)?(?:www\.|m\.|.+\.)?(?:youtu\.be/|youtube(?:-nocookie)\.com/(?:embed/|v/|shorts/|feeds/api/videos/|watch\?v=|watch\?.+&v=))([\w-]{11})#i';
if (preg_match($regex, $string, $matches) === 1) {
$videoID = $matches[1];
} elseif (preg_match('#[\w-]{11}#i', $string, $matches2) === 1) {
$videoID = $matches2[0];
} else {
return '';
}

if ($useIframe) {
if ($useNocookie) {
$embedUri = 'https://www.youtube-nocookie.com/embed/' . $videoID;
} else {
$embedUri = 'https://www.youtube.com/embed/' . $videoID;
}

return sprintf(<<<EOD
<iframe width="560" height="315" src="%s" title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin" allowfullscreen></iframe>'
EOD
, $embedUri);
} else {
$videoUri = 'https://www.youtube.com/watch?v=' . $videoID;

$thumbnailJpegBaseUri = 'https://i.ytimg.com/vi/' . $videoID;
$jpegSrcset = sprintf(
'%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',
$thumbnailJpegBaseUri
);

$thumbnailWebpBaseUri = 'https://i.ytimg.com/vi_webp/' . $videoID;
$webpSrcset = sprintf(
'%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',
$thumbnailWebpBaseUri
);

$fallbackUri = $thumbnailJpegBaseUri . '/maxresdefault.jpg';

return sprintf(<<<EOD
<a href="%s">
<picture>
<source srcset="%s" type="image/webp" referrerpolicy="no-referrer" />
<img srcset="%s" src="%s" alt="Video thumbnail" title="YouTube video thumbnail" referrerpolicy="no-referrer" />
</picture>
</a>
EOD, $videoUri, $webpSrcset, $jpegSrcset, $fallbackUri);
}
}