mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-08-15 13:04:01 +02:00
Compare commits
22 Commits
2018-03-10
...
2018-04-06
Author | SHA1 | Date | |
---|---|---|---|
|
178177e787 | ||
|
1cb83ccea3 | ||
|
c899399569 | ||
|
0f93370e92 | ||
|
45c3dcb636 | ||
|
ecfc220b10 | ||
|
4b3efed7ec | ||
|
bc28c5da8e | ||
|
5bd9c1611d | ||
|
6caca4946b | ||
|
ee78e7613f | ||
|
2df2623430 | ||
|
de5f850cdb | ||
|
ac6847045c | ||
|
df6da837dc | ||
|
41b7984a4e | ||
|
38c7e0272e | ||
|
29c690dbcd | ||
|
8ba817478b | ||
|
cacbe90102 | ||
|
cb91cd5d2f | ||
|
52dfa3fe76 |
@@ -64,13 +64,11 @@ class Arte7Bridge extends BridgeAbstract {
|
||||
. $lang
|
||||
. ($category != null ? '&category.code=' . $category : '');
|
||||
|
||||
$context = array(
|
||||
'http' => array(
|
||||
'header' => 'Authorization: Bearer '. self::API_TOKEN
|
||||
)
|
||||
$header = array(
|
||||
'Authorization: Bearer ' . self::API_TOKEN
|
||||
);
|
||||
|
||||
$input = getContents($url, false, stream_context_create($context)) or die('Could not request ARTE.');
|
||||
$input = getContents($url, $header) or die('Could not request ARTE.');
|
||||
$input_json = json_decode($input, true);
|
||||
|
||||
foreach($input_json['videos'] as $element) {
|
||||
|
@@ -160,9 +160,6 @@ class DealabsBridge extends BridgeAbstract {
|
||||
'cept-tt',
|
||||
'thread-link',
|
||||
'linkPlain',
|
||||
'space--r-1',
|
||||
'size--all-s',
|
||||
'size--fromW3-m',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -208,7 +205,7 @@ class DealabsBridge extends BridgeAbstract {
|
||||
foreach($list as $deal) {
|
||||
$item = array();
|
||||
$item['uri'] = $deal->find('div[class=threadGrid-title]', 0)->find('a', 0)->href;
|
||||
$item['title'] = $deal->find('a[class='. $selectorLink .']', 0
|
||||
$item['title'] = $deal->find('a[class*='. $selectorLink .']', 0
|
||||
)->plaintext;
|
||||
$item['author'] = $deal->find('span.thread-username', 0)->plaintext;
|
||||
$item['content'] = '<table><tr><td><a href="'
|
||||
@@ -217,9 +214,9 @@ class DealabsBridge extends BridgeAbstract {
|
||||
. '"><img src="'
|
||||
. $this->getImage($deal)
|
||||
. '"/></td><td><h2><a href="'
|
||||
. $deal->find('a[class='. $selectorLink .']', 0)->href
|
||||
. $deal->find('a[class*='. $selectorLink .']', 0)->href
|
||||
. '">'
|
||||
. $deal->find('a[class='. $selectorLink .']', 0)->innertext
|
||||
. $deal->find('a[class*='. $selectorLink .']', 0)->innertext
|
||||
. '</a></h2>'
|
||||
. $this->getPrix($deal)
|
||||
. $this->getReduction($deal)
|
||||
|
@@ -96,17 +96,15 @@ class FacebookBridge extends BridgeAbstract {
|
||||
$captcha_action = $_SESSION['captcha_action'];
|
||||
$captcha_fields = $_SESSION['captcha_fields'];
|
||||
$captcha_fields['captcha_response'] = preg_replace("/[^a-zA-Z0-9]+/", "", $_POST['captcha_response']);
|
||||
$http_options = array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'user_agent' => ini_get('user_agent'),
|
||||
'header' => array("Content-type:
|
||||
application/x-www-form-urlencoded\r\nReferer: $captcha_action\r\nCookie: noscript=1\r\n"),
|
||||
'content' => http_build_query($captcha_fields)
|
||||
),
|
||||
|
||||
$header = array("Content-type:
|
||||
application/x-www-form-urlencoded\r\nReferer: $captcha_action\r\nCookie: noscript=1\r\n");
|
||||
$opts = array(
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => http_build_query($captcha_fields)
|
||||
);
|
||||
$context = stream_context_create($http_options);
|
||||
$html = getContents($captcha_action, false, $context);
|
||||
|
||||
$html = getContents($captcha_action, $header, $opts);
|
||||
|
||||
if($html === false) {
|
||||
returnServerError('Failed to submit captcha response back to Facebook');
|
||||
@@ -120,23 +118,18 @@ class FacebookBridge extends BridgeAbstract {
|
||||
|
||||
//Retrieve page contents
|
||||
if(is_null($html)) {
|
||||
$http_options = array(
|
||||
'http' => array(
|
||||
'method' => 'GET',
|
||||
'user_agent' => ini_get('user_agent'),
|
||||
'header' => 'Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE') . "\r\n"
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($http_options);
|
||||
$header = array('Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE') . "\r\n");
|
||||
|
||||
// First character cannot be a forward slash
|
||||
if(strpos($this->getInput('u'), "/") === 0) {
|
||||
returnClientError('Remove leading slash "/" from the username!');
|
||||
}
|
||||
|
||||
if(!strpos($this->getInput('u'), "/")) {
|
||||
$html = getSimpleHTMLDOM(self::URI . urlencode($this->getInput('u')) . '?_fb_noscript=1',
|
||||
false,
|
||||
$context)
|
||||
$html = getSimpleHTMLDOM(self::URI . urlencode($this->getInput('u')) . '?_fb_noscript=1', $header)
|
||||
or returnServerError('No results for this query.');
|
||||
} else {
|
||||
$html = getSimpleHTMLDOM(self::URI . 'pages/' . $this->getInput('u') . '?_fb_noscript=1',
|
||||
false,
|
||||
$context)
|
||||
$html = getSimpleHTMLDOM(self::URI . 'pages/' . $this->getInput('u') . '?_fb_noscript=1', $header)
|
||||
or returnServerError('No results for this query.');
|
||||
}
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ class IPBBridge extends FeedExpander {
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'required' => false,
|
||||
'title' => 'Specify how many pages should be fetched (-1: all)',
|
||||
'defaultValue' => 1
|
||||
'title' => 'Specifies the number of items to return on each request (-1: all)',
|
||||
'defaultValue' => 10
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -161,15 +161,18 @@ class IPBBridge extends FeedExpander {
|
||||
|
||||
$next = null; // Holds the URI of the next page
|
||||
|
||||
do {
|
||||
// Skip loading HTML on first iteration
|
||||
if(!is_null($next)) {
|
||||
$html = getSimpleHTMLDOMCached($next);
|
||||
while(true) {
|
||||
$next = $this->$callback($html, is_null($next));
|
||||
|
||||
if(is_null($next) || ($limit > 0 && count($this->items) >= $limit)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$next = $this->$callback($html, is_null($next));
|
||||
$limit--;
|
||||
} while(!is_null($next) && $limit <> 0);
|
||||
$html = getSimpleHTMLDOMCached($next);
|
||||
}
|
||||
|
||||
// We might have more items than specified, remove excess
|
||||
$this->items = array_slice($this->items, 0, $limit);
|
||||
}
|
||||
|
||||
private function collectTopicArticle($html, $firstrun = true){
|
||||
|
@@ -6,23 +6,42 @@ class InstagramBridge extends BridgeAbstract {
|
||||
const URI = 'https://instagram.com/';
|
||||
const DESCRIPTION = 'Returns the newest images';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
'u' => array(
|
||||
'name' => 'username',
|
||||
'required' => true
|
||||
),
|
||||
'media_type' => array(
|
||||
'name' => 'Media type',
|
||||
'type' => 'list',
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'Both' => 'all',
|
||||
'Video' => 'video',
|
||||
'Picture' => 'picture'
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'u' => array(
|
||||
'name' => 'username',
|
||||
'required' => true
|
||||
),
|
||||
'defaultValue' => 'all'
|
||||
'media_type' => array(
|
||||
'name' => 'Media type',
|
||||
'type' => 'list',
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'Both' => 'all',
|
||||
'Video' => 'video',
|
||||
'Picture' => 'picture'
|
||||
),
|
||||
'defaultValue' => 'all'
|
||||
)
|
||||
),
|
||||
array(
|
||||
'h' => array(
|
||||
'name' => 'hashtag',
|
||||
'required' => true
|
||||
),
|
||||
'media_type' => array(
|
||||
'name' => 'Media type',
|
||||
'type' => 'list',
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'Both' => 'all',
|
||||
'Video' => 'video',
|
||||
'Picture' => 'picture'
|
||||
),
|
||||
'defaultValue' => 'all'
|
||||
)
|
||||
)
|
||||
));
|
||||
);
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
@@ -47,9 +66,14 @@ class InstagramBridge extends BridgeAbstract {
|
||||
$json = trim(substr($innertext, $pos + 18), ' =;');
|
||||
$data = json_decode($json);
|
||||
|
||||
$userMedia = $data->entry_data->ProfilePage[0]->user->media->nodes;
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
$userMedia = $data->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges;
|
||||
} else {
|
||||
$userMedia = $data->entry_data->TagPage[0]->graphql->hashtag->edge_hashtag_to_media->edges;
|
||||
}
|
||||
|
||||
foreach($userMedia as $media) {
|
||||
$media = $media->node;
|
||||
// Check media type
|
||||
switch($this->getInput('media_type')) {
|
||||
case 'all': break;
|
||||
@@ -63,14 +87,14 @@ class InstagramBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = self::URI . 'p/' . $media->code . '/';
|
||||
$item['content'] = '<img src="' . htmlentities($media->display_src) . '" />';
|
||||
if (isset($media->caption)) {
|
||||
$item['title'] = $media->caption;
|
||||
$item['uri'] = self::URI . 'p/' . $media->shortcode . '/';
|
||||
$item['content'] = '<img src="' . htmlentities($media->display_url) . '" />';
|
||||
if (isset($media->edge_media_to_caption->edges[0]->node->text)) {
|
||||
$item['title'] = $media->edge_media_to_caption->edges[0]->node->text;
|
||||
} else {
|
||||
$item['title'] = basename($media->display_src);
|
||||
$item['title'] = basename($media->display_url);
|
||||
}
|
||||
$item['timestamp'] = $media->date;
|
||||
$item['timestamp'] = $media->taken_at_timestamp;
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
@@ -86,6 +110,8 @@ class InstagramBridge extends BridgeAbstract {
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
return self::URI . urlencode($this->getInput('u'));
|
||||
} elseif(!is_null($this->getInput('h'))) {
|
||||
return self::URI . 'explore/tags/' . urlencode($this->getInput('h'));
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
|
@@ -45,9 +45,7 @@ class KernelBugTrackerBridge extends BridgeAbstract {
|
||||
// We use the print preview page for simplicity
|
||||
$html = getSimpleHTMLDOMCached($this->getURI() . '&format=multiple',
|
||||
86400,
|
||||
false,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
|
30
bridges/RadioMelodieBridge.php
Normal file
30
bridges/RadioMelodieBridge.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
class RadioMelodieBridge extends BridgeAbstract {
|
||||
const NAME = 'Radio Melodie Actu';
|
||||
const URI = 'https://www.radiomelodie.com/';
|
||||
const DESCRIPTION = 'Retourne les actualités publiées par Radio Melodie';
|
||||
const MAINTAINER = 'sysadminstory';
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI . 'actu')
|
||||
or returnServerError('Could not request Radio Melodie.');
|
||||
$list = $html->find('div[class=actuitem]');
|
||||
foreach($list as $element) {
|
||||
$item = array();
|
||||
|
||||
// Get picture URL
|
||||
$pictureHTML = $element->find('div[class=picture]');
|
||||
preg_match(
|
||||
'/background-image:url\((.*)\);/',
|
||||
$pictureHTML[0]->getAttribute('style'),
|
||||
$pictures);
|
||||
$pictureURL = $pictures[1];
|
||||
|
||||
$item['enclosures'] = array($pictureURL);
|
||||
$item['uri'] = self::URI . $element->parent()->href;
|
||||
$item['title'] = $element->find('h3', 0)->plaintext;
|
||||
$item['content'] = $element->find('p', 0)->plaintext . '<br/><img src="'.$pictureURL.'"/>';
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ class SteamBridge extends BridgeAbstract {
|
||||
const NAME = 'Steam Bridge';
|
||||
const URI = 'https://store.steampowered.com/';
|
||||
const CACHE_TIMEOUT = 3600; // 1h
|
||||
const DESCRIPTION = 'Returns games list';
|
||||
const DESCRIPTION = 'Returns apps list';
|
||||
const MAINTAINER = 'jacknumber';
|
||||
const PARAMETERS = array(
|
||||
'Wishlist' => array(
|
||||
@@ -47,16 +47,6 @@ class SteamBridge extends BridgeAbstract {
|
||||
'AED' => 'ae',
|
||||
),
|
||||
),
|
||||
'sort' => array(
|
||||
'name' => 'Sort by',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Rank' => 'rank',
|
||||
'Date Added' => 'added',
|
||||
'Name' => 'name',
|
||||
'Price' => 'price',
|
||||
)
|
||||
),
|
||||
'only_discount' => array(
|
||||
'name' => 'Only discount',
|
||||
'type' => 'checkbox',
|
||||
@@ -68,49 +58,44 @@ class SteamBridge extends BridgeAbstract {
|
||||
|
||||
$username = $this->getInput('username');
|
||||
$params = array(
|
||||
'cc' => $this->getInput('currency'),
|
||||
'sort' => $this->getInput('sort')
|
||||
'cc' => $this->getInput('currency')
|
||||
);
|
||||
|
||||
$url = self::URI . 'wishlist/id/' . $username . '/?' . http_build_query($params);
|
||||
$url = self::URI . 'wishlist/id/' . $username . '?' . http_build_query($params);
|
||||
|
||||
$jsonDataRegex = '/var g_rg(?:WishlistData|AppInfo) = ([^;]*)/';
|
||||
$content = getContents($url)
|
||||
$targetVariable = 'g_rgAppInfo';
|
||||
$sort = array();
|
||||
|
||||
$html = '';
|
||||
$html = getSimpleHTMLDOM($url)
|
||||
or returnServerError("Could not request Steam Wishlist. Tried:\n - $url");
|
||||
|
||||
preg_match_all($jsonDataRegex, $content, $matches, PREG_SET_ORDER, 0);
|
||||
|
||||
$appList = json_decode($matches[0][1], true);
|
||||
$fullAppList = json_decode($matches[1][1], true);
|
||||
//var_dump($matches[1][1]);
|
||||
//var_dump($fullAppList);
|
||||
$sortedElementList = array_fill(0, count($appList), 0);
|
||||
foreach($appList as $app) {
|
||||
|
||||
$sortedElementList[$app["priority"] - 1] = $app["appid"];
|
||||
$jsContent = $html->find('.responsive_page_template_content script', 0)->innertext;
|
||||
|
||||
if(preg_match('/var ' . $targetVariable . ' = (.*?);/s', $jsContent, $matches)) {
|
||||
$appsData = json_decode($matches[1]);
|
||||
} else {
|
||||
returnServerError("Could not parse JS variable ($targetVariable) in page content.");
|
||||
}
|
||||
|
||||
foreach($sortedElementList as $appId) {
|
||||
foreach($appsData as $id => $element) {
|
||||
|
||||
$app = $fullAppList[$appId];
|
||||
$gameTitle = $app["name"];
|
||||
$gameUri = "http://store.steampowered.com/app/" . $appId . "/";
|
||||
$gameImg = $app["capsule"];
|
||||
$appType = $element->type;
|
||||
$appIsBuyable = 0;
|
||||
$appHasDiscount = 0;
|
||||
$appIsFree = 0;
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = $gameUri;
|
||||
$item['title'] = $gameTitle;
|
||||
if($element->subs) {
|
||||
$appIsBuyable = 1;
|
||||
|
||||
if(count($app["subs"]) > 0) {
|
||||
if($app["subs"][0]["discount_pct"] != 0) {
|
||||
if($element->subs[0]->discount_pct) {
|
||||
|
||||
$item['promoValue'] = $app["subs"][0]["discount_pct"];
|
||||
$item['oldPrice'] = $app["subs"][0]["price"] / 100 / ((100 - $gamePromoValue / 100));
|
||||
$item['newPrice'] = $app["subs"][0]["price"] / 100;
|
||||
$item['price'] = $item['newPrice'];
|
||||
|
||||
$item['hasPromo'] = true;
|
||||
$appHasDiscount = 1;
|
||||
$discountBlock = str_get_html($element->subs[0]->discount_block);
|
||||
$appDiscountValue = $discountBlock->find('.discount_pct', 0)->plaintext;
|
||||
$appOldPrice = $discountBlock->find('.discount_original_price', 0)->plaintext;
|
||||
$appNewPrice = $discountBlock->find('.discount_final_price', 0)->plaintext;
|
||||
$appPrice = $appNewPrice;
|
||||
|
||||
} else {
|
||||
|
||||
@@ -118,15 +103,55 @@ class SteamBridge extends BridgeAbstract {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item['price'] = $app["subs"][0]["price"] / 100;
|
||||
$item['hasPromo'] = false;
|
||||
$appPrice = $element->subs[0]->price / 100;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if($this->getInput('only_discount')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isset($element->free) && $element->free = 1) {
|
||||
$appIsFree = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = "http://store.steampowered.com/app/$id/";
|
||||
$item['title'] = $element->name;
|
||||
$item['type'] = $appType;
|
||||
$item['cover'] = str_replace('_292x136', '', $element->capsule);
|
||||
$item['timestamp'] = $element->added;
|
||||
$item['isBuyable'] = $appIsBuyable;
|
||||
$item['hasDiscount'] = $appHasDiscount;
|
||||
$item['isFree'] = $appIsFree;
|
||||
$item['priority'] = $element->priority;
|
||||
|
||||
if($appIsBuyable) {
|
||||
$item['price'] = floatval(str_replace(',', '.', $appPrice));
|
||||
}
|
||||
|
||||
if($appHasDiscount) {
|
||||
|
||||
$item['discount']['value'] = $appDiscountValue;
|
||||
$item['discount']['oldPrice'] = floatval(str_replace(',', '.', $appOldPrice));
|
||||
$item['discount']['newPrice'] = floatval(str_replace(',', '.', $appNewPrice));
|
||||
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
$item['enclosures'] = array();
|
||||
$item['enclosures'][] = str_replace('_292x136', '', $element->capsule);
|
||||
|
||||
foreach($element->screenshots as $screenshot) {
|
||||
$item['enclosures'][] = substr($element->capsule, 0, -31) . $screenshot;
|
||||
}
|
||||
|
||||
$sort[$id] = $element->priority;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
||||
array_multisort($sort, SORT_ASC, $this->items);
|
||||
}
|
||||
}
|
||||
|
@@ -109,19 +109,9 @@ class VkBridge extends BridgeAbstract
|
||||
{
|
||||
ini_set('user-agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0');
|
||||
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'user_agent' => ini_get('user_agent'),
|
||||
'accept_encoding' => 'gzip',
|
||||
'header' => "Accept-language: en\r\n
|
||||
Cookie: remixlang=3\r\n"
|
||||
)
|
||||
);
|
||||
$header = array('Accept-language: en', 'Cookie: remixlang=3');
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
return getContents($this->getURI(), false, $context);
|
||||
return getContents($this->getURI(), $header);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -110,8 +110,8 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
$this->feedName = $this->ytBridgeFixTitle($xml->find('feed > title', 0)->plaintext); // feedName will be used by getName()
|
||||
}
|
||||
|
||||
private function ytBridgeParseHtmlListing($html, $element_selector, $title_selector){
|
||||
$limit = 10;
|
||||
private function ytBridgeParseHtmlListing($html, $element_selector, $title_selector, $add_parsed_items = true) {
|
||||
$limit = $add_parsed_items ? 10 : INF;
|
||||
$count = 0;
|
||||
foreach($html->find($element_selector) as $element) {
|
||||
if($count < $limit) {
|
||||
@@ -122,12 +122,15 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
$vid = substr($vid, 0, strpos($vid, '&') ?: strlen($vid));
|
||||
$title = $this->ytBridgeFixTitle($element->find($title_selector, 0)->plaintext);
|
||||
if($title != '[Private Video]' && strpos($vid, 'googleads') === false) {
|
||||
$this->ytBridgeQueryVideoInfo($vid, $author, $desc, $time);
|
||||
$this->ytBridgeAddItem($vid, $title, $author, $desc, $time);
|
||||
if ($add_parsed_items) {
|
||||
$this->ytBridgeQueryVideoInfo($vid, $author, $desc, $time);
|
||||
$this->ytBridgeAddItem($vid, $title, $author, $desc, $time);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function ytBridgeFixTitle($title) {
|
||||
@@ -137,10 +140,8 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
|
||||
private function ytGetSimpleHTMLDOM($url){
|
||||
return getSimpleHTMLDOM($url,
|
||||
$use_include_path = false,
|
||||
$context = null,
|
||||
$offset = 0,
|
||||
$maxLen = null,
|
||||
$header = array(),
|
||||
$opts = array(),
|
||||
$lowercase = true,
|
||||
$forceTagsClosed = true,
|
||||
$target_charset = DEFAULT_TARGET_CHARSET,
|
||||
@@ -176,11 +177,20 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
}
|
||||
} elseif($this->getInput('p')) { /* playlist mode */
|
||||
$this->request = $this->getInput('p');
|
||||
$url_feed = self::URI . 'feeds/videos.xml?playlist_id=' . urlencode($this->request);
|
||||
$url_listing = self::URI . 'playlist?list=' . urlencode($this->request);
|
||||
$html = $this->ytGetSimpleHTMLDOM($url_listing)
|
||||
or returnServerError("Could not request YouTube. Tried:\n - $url_listing");
|
||||
$this->ytBridgeParseHtmlListing($html, 'tr.pl-video', '.pl-video-title a');
|
||||
$item_count = $this->ytBridgeParseHtmlListing($html, 'tr.pl-video', '.pl-video-title a', false);
|
||||
if ($item_count <= 15 && ($xml = $this->ytGetSimpleHTMLDOM($url_feed))) {
|
||||
$this->ytBridgeParseXmlFeed($xml);
|
||||
} else {
|
||||
$this->ytBridgeParseHtmlListing($html, 'tr.pl-video', '.pl-video-title a');
|
||||
}
|
||||
$this->feedName = 'Playlist: ' . str_replace(' - YouTube', '', $html->find('title', 0)->plaintext); // feedName will be used by getName()
|
||||
usort($this->items, function ($item1, $item2) {
|
||||
return $item2['timestamp'] - $item1['timestamp'];
|
||||
});
|
||||
} elseif($this->getInput('s')) { /* search mode */
|
||||
$this->request = $this->getInput('s');
|
||||
$page = 1;
|
||||
|
34
index.php
34
index.php
@@ -19,6 +19,10 @@ define('PROXY_BYBRIDGE', false);
|
||||
// Comment this line or keep PROXY_NAME empty to display PROXY_URL instead
|
||||
define('PROXY_NAME', 'Hidden Proxy Name');
|
||||
|
||||
// Allows the operator to specify custom cache timeouts via '&_cache_timeout=3600'
|
||||
// true: enabled, false: disabled (default)
|
||||
define('CUSTOM_CACHE_TIMEOUT', false);
|
||||
|
||||
date_default_timezone_set('UTC');
|
||||
error_reporting(0);
|
||||
|
||||
@@ -73,6 +77,12 @@ if(!extension_loaded('libxml'))
|
||||
if(!extension_loaded('mbstring'))
|
||||
die('"mbstring" extension not loaded. Please check "php.ini"');
|
||||
|
||||
if(!extension_loaded('simplexml'))
|
||||
die('"simplexml" extension not loaded. Please check "php.ini"');
|
||||
|
||||
if(!extension_loaded('curl'))
|
||||
die('"curl" extension not loaded. Please check "php.ini"');
|
||||
|
||||
// configuration checks
|
||||
if(ini_get('allow_url_fopen') !== "1")
|
||||
die('"allow_url_fopen" is not set to "1". Please check "php.ini');
|
||||
@@ -168,6 +178,16 @@ try {
|
||||
define('NOPROXY', true);
|
||||
}
|
||||
|
||||
// Custom cache timeout
|
||||
$cache_timeout = -1;
|
||||
if(array_key_exists('_cache_timeout', $params)) {
|
||||
if(!CUSTOM_CACHE_TIMEOUT) {
|
||||
throw new \HttpException('This server doesn\'t support "_cache_timeout"!');
|
||||
}
|
||||
|
||||
$cache_timeout = filter_var($params['_cache_timeout'], FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
// Initialize cache
|
||||
$cache = Cache::create('FileCache');
|
||||
$cache->setPath(CACHE_DIR);
|
||||
@@ -178,11 +198,17 @@ try {
|
||||
unset($params['bridge']);
|
||||
unset($params['format']);
|
||||
unset($params['_noproxy']);
|
||||
unset($params['_cache_timeout']);
|
||||
|
||||
// Load cache & data
|
||||
try {
|
||||
$bridge->setCache($cache);
|
||||
$bridge->setCacheTimeout($cache_timeout);
|
||||
$bridge->setDatas($params);
|
||||
} catch(Error $e) {
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildBridgeException($e, $bridge));
|
||||
} catch(Exception $e) {
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
@@ -195,10 +221,14 @@ try {
|
||||
$format->setItems($bridge->getItems());
|
||||
$format->setExtraInfos($bridge->getExtraInfos());
|
||||
$format->display();
|
||||
} catch(Exception $e) {
|
||||
} catch(Error $e) {
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildTransformException($e, $bridge));
|
||||
} catch(Exception $e) {
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildBridgeException($e, $bridge));
|
||||
}
|
||||
|
||||
die;
|
||||
@@ -273,7 +303,7 @@ EOD;
|
||||
echo $inactiveBridges;
|
||||
?>
|
||||
<section class="footer">
|
||||
<a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge 2017-08-19 ~ Public Domain</a><br />
|
||||
<a href="https://github.com/RSS-Bridge/rss-bridge">RSS-Bridge 2018-04-06 ~ Public Domain</a><br />
|
||||
<?= $activeFoundBridgeCount; ?>/<?= count($bridgeList) ?> active bridges. <br />
|
||||
<?php
|
||||
if($activeFoundBridgeCount !== count($bridgeList)) {
|
||||
|
@@ -14,6 +14,7 @@ abstract class BridgeAbstract implements BridgeInterface {
|
||||
protected $items = array();
|
||||
protected $inputs = array();
|
||||
protected $queriedContext = '';
|
||||
protected $cacheTimeout;
|
||||
|
||||
/**
|
||||
* Return cachable datas (extrainfos and items) stored in the bridge
|
||||
@@ -171,7 +172,7 @@ abstract class BridgeAbstract implements BridgeInterface {
|
||||
if(!is_null($this->cache)) {
|
||||
$time = $this->cache->getTime();
|
||||
if($time !== false
|
||||
&& (time() - static::CACHE_TIMEOUT < $time)
|
||||
&& (time() - $this->getCacheTimeout() < $time)
|
||||
&& (!defined('DEBUG') || DEBUG !== true)) {
|
||||
$cached = $this->cache->loadData();
|
||||
if(isset($cached['items']) && isset($cached['extraInfos'])) {
|
||||
@@ -268,4 +269,17 @@ abstract class BridgeAbstract implements BridgeInterface {
|
||||
public function setCache(\CacheInterface $cache){
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function setCacheTimeout($timeout){
|
||||
if(is_numeric($timeout) && ($timeout < 1 || $timeout > 86400)) {
|
||||
$this->cacheTimeout = static::CACHE_TIMEOUT;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cacheTimeout = $timeout;
|
||||
}
|
||||
|
||||
public function getCacheTimeout(){
|
||||
return isset($this->cacheTimeout) ? $this->cacheTimeout : static::CACHE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
@@ -68,4 +68,20 @@ interface BridgeInterface {
|
||||
* @param object CacheInterface The cache instance
|
||||
*/
|
||||
public function setCache(\CacheInterface $cache);
|
||||
|
||||
/**
|
||||
* Sets the timeout for clearing the cache files. The timeout must be
|
||||
* specified between 1..86400 seconds (max. 24 hours). The default timeout
|
||||
* (specified by the bridge maintainer) applies for invalid values.
|
||||
*
|
||||
* @param int $timeout The cache timeout in seconds
|
||||
*/
|
||||
public function setCacheTimeout($timeout);
|
||||
|
||||
/**
|
||||
* Returns the cache timeout
|
||||
*
|
||||
* @return int Cache timeout
|
||||
*/
|
||||
public function getCacheTimeout();
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ function buildGitHubIssueQuery($title, $body, $labels = null, $maintainer = null
|
||||
* provided parameter are invalid
|
||||
*/
|
||||
function buildBridgeException($e, $bridge){
|
||||
if(!($e instanceof \Exception) || !($bridge instanceof \BridgeInterface)) {
|
||||
if(( !($e instanceof \Exception) && !($e instanceof \Error)) || !($bridge instanceof \BridgeInterface)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ unable to receive or process the remote website's content!";
|
||||
* provided parameter are invalid
|
||||
*/
|
||||
function buildTransformException($e, $bridge){
|
||||
if(!($e instanceof \Exception) || !($bridge instanceof \BridgeInterface)) {
|
||||
if(( !($e instanceof \Exception) && !($e instanceof \Error)) || !($bridge instanceof \BridgeInterface)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -1,77 +1,45 @@
|
||||
<?php
|
||||
function getContents($url,
|
||||
$use_include_path = false,
|
||||
$context = null,
|
||||
$offset = 0,
|
||||
$maxlen = null){
|
||||
$contextOptions = array(
|
||||
'http' => array(
|
||||
'user_agent' => ini_get('user_agent'),
|
||||
'accept_encoding' => 'gzip'
|
||||
)
|
||||
);
|
||||
function getContents($url, $header = array(), $opts = array()){
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
if(defined('PROXY_URL') && !defined('NOPROXY')) {
|
||||
$contextOptions['http']['proxy'] = PROXY_URL;
|
||||
$contextOptions['http']['request_fulluri'] = true;
|
||||
if(is_array($header) && count($header) !== 0)
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
|
||||
if(is_null($context)) {
|
||||
$context = stream_context_create($contextOptions);
|
||||
} else {
|
||||
$prevContext = $context;
|
||||
if(!stream_context_set_option($context, $contextOptions)) {
|
||||
$context = $prevContext;
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, ini_get('user_agent'));
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||
|
||||
if(is_array($opts)) {
|
||||
foreach($opts as $key => $value) {
|
||||
curl_setopt($ch, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_null($maxlen)) {
|
||||
$content = file_get_contents($url, $use_include_path, $context, $offset);
|
||||
} else {
|
||||
$content = file_get_contents($url, $use_include_path, $context, $offset, $maxlen);
|
||||
if(defined('PROXY_URL') && !defined('NOPROXY')) {
|
||||
curl_setopt($ch, CURLOPT_PROXY, PROXY_URL);
|
||||
}
|
||||
|
||||
$content = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if($content === false)
|
||||
debugMessage('Cant\'t download ' . $url);
|
||||
|
||||
// handle compressed data
|
||||
foreach($http_response_header as $header) {
|
||||
if(stristr($header, 'content-encoding')) {
|
||||
switch(true) {
|
||||
case stristr($header, 'gzip'):
|
||||
$content = gzinflate(substr($content, 10, -8));
|
||||
break;
|
||||
case stristr($header, 'compress'):
|
||||
//TODO
|
||||
case stristr($header, 'deflate'):
|
||||
//TODO
|
||||
case stristr($header, 'brotli'):
|
||||
//TODO
|
||||
returnServerError($header . '=> Not implemented yet');
|
||||
break;
|
||||
case stristr($header, 'identity'):
|
||||
break;
|
||||
default:
|
||||
returnServerError($header . '=> Unknown compression');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
function getSimpleHTMLDOM($url,
|
||||
$use_include_path = false,
|
||||
$context = null,
|
||||
$offset = 0,
|
||||
$maxLen = null,
|
||||
$header = array(),
|
||||
$opts = array(),
|
||||
$lowercase = true,
|
||||
$forceTagsClosed = true,
|
||||
$target_charset = DEFAULT_TARGET_CHARSET,
|
||||
$stripRN = true,
|
||||
$defaultBRText = DEFAULT_BR_TEXT,
|
||||
$defaultSpanText = DEFAULT_SPAN_TEXT){
|
||||
$content = getContents($url, $use_include_path, $context, $offset, $maxLen);
|
||||
$content = getContents($url, $header, $opts);
|
||||
return str_get_html($content,
|
||||
$lowercase,
|
||||
$forceTagsClosed,
|
||||
@@ -89,10 +57,8 @@ $defaultSpanText = DEFAULT_SPAN_TEXT){
|
||||
*/
|
||||
function getSimpleHTMLDOMCached($url,
|
||||
$duration = 86400,
|
||||
$use_include_path = false,
|
||||
$context = null,
|
||||
$offset = 0,
|
||||
$maxLen = null,
|
||||
$header = array(),
|
||||
$opts = array(),
|
||||
$lowercase = true,
|
||||
$forceTagsClosed = true,
|
||||
$target_charset = DEFAULT_TARGET_CHARSET,
|
||||
@@ -116,7 +82,7 @@ $defaultSpanText = DEFAULT_SPAN_TEXT){
|
||||
&& (!defined('DEBUG') || DEBUG !== true)) { // Contents within duration
|
||||
$content = $cache->loadData();
|
||||
} else { // Content not within duration
|
||||
$content = getContents($url, $use_include_path, $context, $offset, $maxLen);
|
||||
$content = getContents($url, $header, $opts);
|
||||
if($content !== false) {
|
||||
$cache->saveData($content);
|
||||
}
|
||||
|
35
lib/html.php
35
lib/html.php
@@ -75,8 +75,24 @@ CARD;
|
||||
. ((defined('PROXY_NAME') && PROXY_NAME) ? PROXY_NAME : PROXY_URL)
|
||||
. ')</label><br />'
|
||||
. PHP_EOL;
|
||||
}
|
||||
} if(CUSTOM_CACHE_TIMEOUT) {
|
||||
$idArg = 'arg-'
|
||||
. urlencode($bridgeName)
|
||||
. '-'
|
||||
. urlencode('_cache_timeout');
|
||||
|
||||
$card .= '<label for="'
|
||||
. $idArg
|
||||
. '">Cache timeout in seconds : </label>'
|
||||
. PHP_EOL;
|
||||
|
||||
$card .= '<input id="'
|
||||
. $idArg
|
||||
. '" type="number" value="'
|
||||
. $bridge->getCacheTimeout()
|
||||
. '" name="_cache_timeout" /><br />'
|
||||
. PHP_EOL;
|
||||
}
|
||||
$card .= $getHelperButtonsFormat($formats);
|
||||
} else {
|
||||
$card .= '<span style="font-weight: bold;">Inactive</span>';
|
||||
@@ -251,6 +267,23 @@ CARD;
|
||||
. ((defined('PROXY_NAME') && PROXY_NAME) ? PROXY_NAME : PROXY_URL)
|
||||
. ')</label><br />'
|
||||
. PHP_EOL;
|
||||
} if(CUSTOM_CACHE_TIMEOUT) {
|
||||
$idArg = 'arg-'
|
||||
. urlencode($bridgeName)
|
||||
. '-'
|
||||
. urlencode('_cache_timeout');
|
||||
|
||||
$card .= '<label for="'
|
||||
. $idArg
|
||||
. '">Cache timeout in seconds : </label>'
|
||||
. PHP_EOL;
|
||||
|
||||
$card .= '<input id="'
|
||||
. $idArg
|
||||
. '" type="number" value="'
|
||||
. $bridge->getCacheTimeout()
|
||||
. '" name="_cache_timeout" /><br />'
|
||||
. PHP_EOL;
|
||||
}
|
||||
$card .= $getHelperButtonsFormat($formats);
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user