mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-03-14 20:21:14 +03:00
[YouTubeCommunityTabBridge] Add Bridge (#1594)
This commit is contained in:
parent
33da1476c9
commit
7f4a0fae0c
1 changed files with 267 additions and 0 deletions
267
bridges/YouTubeCommunityTabBridge.php
Normal file
267
bridges/YouTubeCommunityTabBridge.php
Normal file
|
@ -0,0 +1,267 @@
|
|||
<?php
|
||||
class YouTubeCommunityTabBridge extends BridgeAbstract {
|
||||
const NAME = 'YouTube Community Tab Bridge';
|
||||
const URI = 'https://www.youtube.com';
|
||||
const DESCRIPTION = 'Returns posts from a channel\'s community tab';
|
||||
const MAINTAINER = 'VerifiedJoseph';
|
||||
const PARAMETERS = array(
|
||||
'By channel ID' => array(
|
||||
'channel' => array(
|
||||
'name' => 'Channel ID',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'UCULkRHBdLC5ZcEQBaL0oYHQ'
|
||||
)
|
||||
),
|
||||
'By username' => array(
|
||||
'username' => array(
|
||||
'name' => 'Username',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'YouTubeUK'
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
const CACHE_TIMEOUT = 3600; // 1 hour
|
||||
|
||||
private $feedUrl = '';
|
||||
private $feedName = '';
|
||||
private $itemTitle = '';
|
||||
|
||||
private $urlRegex = '/youtube\.com\/(channel|user|c)\/([\w]+)\/community/';
|
||||
private $jsonRegex = '/var ytInitialData = (.*);<\/script><link rel="canonical"/';
|
||||
|
||||
public function detectParameters($url) {
|
||||
$params = array();
|
||||
|
||||
if(preg_match($this->urlRegex, $url, $matches)) {
|
||||
if ($matches[1] === 'channel') {
|
||||
$params['context'] = 'By channel ID';
|
||||
$params['channel'] = $matches[2];
|
||||
}
|
||||
|
||||
if ($matches[1] === 'user') {
|
||||
$params['context'] = 'By username';
|
||||
$params['username'] = $matches[2];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
|
||||
if (is_null($this->getInput('username')) === false) {
|
||||
try {
|
||||
$this->feedUrl = $this->buildCommunityUri($this->getInput('username'), 'c');
|
||||
$html = getSimpleHTMLDOM($this->feedUrl);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->feedUrl = $this->buildCommunityUri($this->getInput('username'), 'user');
|
||||
$html = getSimpleHTMLDOM($this->feedUrl);
|
||||
}
|
||||
} else {
|
||||
$this->feedUrl = $this->buildCommunityUri($this->getInput('channel'), 'channel');
|
||||
$html = getSimpleHTMLDOM($this->feedUrl);
|
||||
}
|
||||
|
||||
$json = $this->extractJson($html->find('body', 0)->innertext);
|
||||
|
||||
$this->feedName = $json->header->c4TabbedHeaderRenderer->title;
|
||||
|
||||
if ($this->hasCommunityTab($json) === false) {
|
||||
returnServerError('Channel does not have a community tab');
|
||||
}
|
||||
|
||||
foreach ($this->getCommunityPosts($json) as $post) {
|
||||
$this->itemTitle = '';
|
||||
|
||||
if (!isset($post->backstagePostThreadRenderer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$details = $post->backstagePostThreadRenderer->post->backstagePostRenderer;
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = self::URI . '/post/' . $details->postId;
|
||||
$item['author'] = $details->authorText->runs[0]->text;
|
||||
$item['content'] = '';
|
||||
|
||||
if (isset($details->contentText)) {
|
||||
$text = $this->getText($details->contentText->runs);
|
||||
|
||||
$this->itemTitle = $this->ellipsisTitle($text);
|
||||
$item['content'] = $text;
|
||||
}
|
||||
|
||||
$item['content'] .= $this->getAttachments($details);
|
||||
$item['title'] = $this->itemTitle;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
|
||||
if (!empty($this->feedUri)) {
|
||||
return $this->feedUri;
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
|
||||
if (!empty($this->feedName)) {
|
||||
return $this->feedName . ' - YouTube Community Tab';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Community URI
|
||||
*/
|
||||
private function buildCommunityUri($value, $type) {
|
||||
return self::URI . '/' . $type . '/' . $value . '/community';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JSON from page
|
||||
*/
|
||||
private function extractJson($html) {
|
||||
|
||||
if (!preg_match($this->jsonRegex, $html, $parts)) {
|
||||
returnServerError('Failed to extract data from page');
|
||||
}
|
||||
|
||||
$data = json_decode($parts[1]);
|
||||
|
||||
if ($data === false) {
|
||||
returnServerError('Failed to decode extracted data');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if channel has a community tab
|
||||
*/
|
||||
private function hasCommunityTab($json) {
|
||||
|
||||
foreach ($json->contents->twoColumnBrowseResultsRenderer->tabs as $tab) {
|
||||
if (isset($tab->tabRenderer) && $tab->tabRenderer->title === 'Community') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get community tab posts
|
||||
*/
|
||||
private function getCommunityPosts($json) {
|
||||
|
||||
foreach ($json->contents->twoColumnBrowseResultsRenderer->tabs as $tab) {
|
||||
if ($tab->tabRenderer->title === 'Community') {
|
||||
return $tab->tabRenderer->content->sectionListRenderer->contents[0]->itemSectionRenderer->contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text content for a post
|
||||
*/
|
||||
private function getText($runs) {
|
||||
$text = '';
|
||||
|
||||
foreach ($runs as $part) {
|
||||
$text .= $this->formatUrls($part->text);
|
||||
}
|
||||
|
||||
return nl2br($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachments for posts
|
||||
*/
|
||||
private function getAttachments($details) {
|
||||
$content = '';
|
||||
|
||||
if (isset($details->backstageAttachment)) {
|
||||
$attachments = $details->backstageAttachment;
|
||||
|
||||
// Video
|
||||
if (isset($attachments->videoRenderer) && isset($attachments->videoRenderer->videoId)) {
|
||||
if (empty($this->itemTitle)) {
|
||||
$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;
|
||||
}
|
||||
|
||||
// Image
|
||||
if (isset($attachments->backstageImageRenderer)) {
|
||||
if (empty($this->itemTitle)) {
|
||||
$this->itemTitle = $this->feedName . ' posted an image';
|
||||
}
|
||||
|
||||
$lastThumb = end($attachments->backstageImageRenderer->image->thumbnails);
|
||||
|
||||
$content = <<<EOD
|
||||
<p><img src="{$lastThumb->url}"></p>
|
||||
EOD;
|
||||
}
|
||||
|
||||
// Poll
|
||||
if (isset($attachments->pollRenderer)) {
|
||||
if (empty($this->itemTitle)) {
|
||||
$this->itemTitle = $this->feedName . ' posted a poll';
|
||||
}
|
||||
|
||||
$pollChoices = '';
|
||||
|
||||
foreach ($attachments->pollRenderer->choices as $choice) {
|
||||
$pollChoices .= <<<EOD
|
||||
<li>{$choice->text->runs[0]->text}</li>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$content = <<<EOD
|
||||
<hr><p>Poll ({$attachments->pollRenderer->totalVotes->simpleText})<br><ul>{$pollChoices}</ul><p>
|
||||
EOD;
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/*
|
||||
Ellipsis text for title
|
||||
*/
|
||||
private function ellipsisTitle($text) {
|
||||
$length = 100;
|
||||
|
||||
if (strlen($text) > $length) {
|
||||
$text = explode('<br>', wordwrap($text, $length, '<br>'));
|
||||
return $text[0] . '...';
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function formatUrls($content) {
|
||||
return preg_replace(
|
||||
'/(http[s]{0,1}\:\/\/[a-zA-Z0-9.\/\?\&=\-_]{4,})/ims',
|
||||
'<a target="_blank" href="$1" target="_blank">$1</a> ',
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue