diff options
author | Johannes 'josch' Schauer <josch@debian.org> | 2020-03-07 10:30:21 +0100 |
---|---|---|
committer | Johannes 'josch' Schauer <josch@debian.org> | 2020-03-07 10:30:21 +0100 |
commit | 432eb165b83d4483780a279b02929b05b3e09fa5 (patch) | |
tree | 53ff708fcd05370af1595fd720440cde85a46891 | |
parent | 779ac0902d2586e1ac31ad41881d8922ec40a7ea (diff) |
New upstream version 2020-02-26+dfsg1
37 files changed, 1082 insertions, 212 deletions
@@ -109,8 +109,8 @@ We are RSS-Bridge community, a group of developers continuing the project initia Use this script to generate the list automatically (using the GitHub API): https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 --> - * [16mhz](https://github.com/16mhz) +* [86423355844265459587182778](https://github.com/86423355844265459587182778) * [adamchainz](https://github.com/adamchainz) * [Ahiles3005](https://github.com/Ahiles3005) * [Albirew](https://github.com/Albirew) @@ -126,6 +126,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 * [azdkj532](https://github.com/azdkj532) * [b1nj](https://github.com/b1nj) * [benasse](https://github.com/benasse) +* [Binnette](https://github.com/Binnette) * [captn3m0](https://github.com/captn3m0) * [chemel](https://github.com/chemel) * [ckiw](https://github.com/ckiw) @@ -161,10 +162,12 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 * [IceWreck](https://github.com/IceWreck) * [j0k3r](https://github.com/j0k3r) * [JackNUMBER](https://github.com/JackNUMBER) +* [jdesgats](https://github.com/jdesgats) * [jdigilio](https://github.com/jdigilio) * [JeremyRand](https://github.com/JeremyRand) * [Jocker666z](https://github.com/Jocker666z) * [johnnygroovy](https://github.com/johnnygroovy) +* [johnpc](https://github.com/johnpc) * [killruana](https://github.com/killruana) * [klimplant](https://github.com/klimplant) * [kranack](https://github.com/kranack) @@ -187,7 +190,6 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 * [metaMMA](https://github.com/metaMMA) * [mitsukarenai](https://github.com/mitsukarenai) * [MonsieurPoutounours](https://github.com/MonsieurPoutounours) -* [mr-flibble](https://github.com/mr-flibble) * [mro](https://github.com/mro) * [mxmehl](https://github.com/mxmehl) * [nel50n](https://github.com/nel50n) @@ -221,6 +223,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 * [sysadminstory](https://github.com/sysadminstory) * [tameroski](https://github.com/tameroski) * [teromene](https://github.com/teromene) +* [tgkenney](https://github.com/tgkenney) * [thefranke](https://github.com/thefranke) * [ThePadawan](https://github.com/ThePadawan) * [TheRadialActive](https://github.com/TheRadialActive) @@ -232,7 +235,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8 * [xurxof](https://github.com/xurxof) * [yardenac](https://github.com/yardenac) * [ZeNairolf](https://github.com/ZeNairolf) - + Licenses === diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php index 89930cf..579630a 100644 --- a/actions/DisplayAction.php +++ b/actions/DisplayAction.php @@ -190,7 +190,7 @@ class DisplayAction extends ActionAbstract { $items[] = $item; } elseif(Configuration::getConfig('error', 'output') === 'http') { - header('Content-Type: text/html', true, get_return_code($e)); + header('Content-Type: text/html', true, $this->get_return_code($e)); die(buildTransformException($e, $bridge)); } } @@ -222,7 +222,7 @@ class DisplayAction extends ActionAbstract { $items[] = $item; } elseif(Configuration::getConfig('error', 'output') === 'http') { - header('Content-Type: text/html', true, get_return_code($e)); + header('Content-Type: text/html', true, $this->get_return_code($e)); die(buildTransformException($e, $bridge)); } } diff --git a/bridges/AllocineFRBridge.php b/bridges/AllocineFRBridge.php index 17da903..40ef9a9 100644 --- a/bridges/AllocineFRBridge.php +++ b/bridges/AllocineFRBridge.php @@ -8,14 +8,25 @@ class AllocineFRBridge extends BridgeAbstract { const DESCRIPTION = 'Bridge for allocine.fr'; const PARAMETERS = array( array( 'category' => array( - 'name' => 'category', + 'name' => 'Emission', 'type' => 'list', - 'exampleValue' => 'Faux Raccord', - 'title' => 'Select your category', + 'title' => 'Sélectionner l\'emission', 'values' => array( 'Faux Raccord' => 'faux-raccord', - 'Top 5' => 'top-5', - 'Tueurs en Séries' => 'tueurs-en-serie' + 'Fanzone' => 'fanzone', + 'Game In Ciné' => 'game-in-cine', + 'Pour la faire courte' => 'pour-la-faire-courte', + 'Home Cinéma' => 'home-cinema', + 'PILS - Par Ici Les Sorties' => 'pils-par-ici-les-sorties', + 'AlloCiné : l\'émission, sur LeStream' => 'allocine-lemission-sur-lestream', + 'Give Me Five' => 'give-me-five', + 'Aviez-vous remarqué ?' => 'aviez-vous-remarque', + 'Et paf, il est mort' => 'et-paf-il-est-mort', + 'The Big Fan Theory' => 'the-big-fan-theory', + 'Clichés' => 'cliches', + 'Complètement...' => 'completement', + '#Fun Facts' => 'fun-facts', + 'Origin Story' => 'origin-story', ) ) )); @@ -23,19 +34,30 @@ class AllocineFRBridge extends BridgeAbstract { public function getURI(){ if(!is_null($this->getInput('category'))) { - switch($this->getInput('category')) { - case 'faux-raccord': - $uri = static::URI . 'video/programme-12284/saison-32180/'; - break; - case 'top-5': - $uri = static::URI . 'video/programme-12299/saison-29561/'; - break; - case 'tueurs-en-serie': - $uri = static::URI . 'video/programme-12286/saison-22938/'; - break; - } + $categories = array( + 'faux-raccord' => 'video/programme-12284/saison-37054/', + 'fanzone' => 'video/programme-12298/saison-37059/', + 'game-in-cine' => 'video/programme-12288/saison-22971/', + 'pour-la-faire-courte' => 'video/programme-20960/saison-29678/', + 'home-cinema' => 'video/programme-12287/saison-34703/', + 'pils-par-ici-les-sorties' => 'video/programme-25789/saison-37253/', + 'allocine-lemission-sur-lestream' => 'video/programme-25123/saison-36067/', + 'give-me-five' => 'video/programme-21919/saison-34518/', + 'aviez-vous-remarque' => 'video/programme-19518/saison-37084/', + 'et-paf-il-est-mort' => 'video/programme-25113/saison-36657/', + 'the-big-fan-theory' => 'video/programme-20403/saison-37419/', + 'cliches' => 'video/programme-24834/saison-35591/', + 'completement' => 'video/programme-23859/saison-34102/', + 'fun-facts' => 'video/programme-23040/saison-32686/', + 'origin-story' => 'video/programme-25667/saison-37041/' + ); - return $uri; + $category = $this->getInput('category'); + if(array_key_exists($category, $categories)) { + return static::URI . $categories[$category]; + } else { + returnClientError('Emission inconnue'); + } } return parent::getURI(); @@ -63,23 +85,23 @@ class AllocineFRBridge extends BridgeAbstract { self::PARAMETERS[$this->queriedContext]['category']['values'] ); - foreach($html->find('.media-meta-list figure.media-meta-fig') as $element) { + foreach($html->find('div[class=col-left]', 0)->find('div[class*=video-card]') as $element) { $item = array(); - $title = $element->find('div.titlebar h3.title a', 0); - $content = trim($element->innertext); - $figCaption = strpos($content, $category); + $title = $element->find('a[class*=meta-title-link]', 0); + $content = trim($element->outertext); - if($figCaption !== false) { - $content = str_replace('src="/', 'src="' . static::URI, $content); - $content = str_replace('href="/', 'href="' . static::URI, $content); - $content = str_replace('src=\'/', 'src=\'' . static::URI, $content); - $content = str_replace('href=\'/', 'href=\'' . static::URI, $content); - $item['content'] = $content; - $item['title'] = trim($title->innertext); - $item['uri'] = static::URI . $title->href; - $this->items[] = $item; - } + // Replace image 'src' with the one in 'data-src' + $content = preg_replace('@src="data:image/gif;base64,[A-Za-z0-9+\/]*"@', '', $content); + $content = preg_replace('@data-src=@', 'src=', $content); + + // Remove date in the content to prevent content update while the video is getting older + $content = preg_replace('@<div class="meta-sub light">.*<span>[^<]*</span>[^<]*</div>@', '', $content); + + $item['content'] = $content; + $item['title'] = trim($title->innertext); + $item['uri'] = static::URI . substr($title->href, 1); + $this->items[] = $item; } } } diff --git a/bridges/AtmoNouvelleAquitaineBridge.php b/bridges/AtmoNouvelleAquitaineBridge.php index d395fa7..7766bd9 100644 --- a/bridges/AtmoNouvelleAquitaineBridge.php +++ b/bridges/AtmoNouvelleAquitaineBridge.php @@ -2,8 +2,8 @@ class AtmoNouvelleAquitaineBridge extends BridgeAbstract { const NAME = 'Atmo Nouvelle Aquitaine'; - const URI = 'https://www.atmo-nouvelleaquitaine.org/monair/commune/'; - const DESCRIPTION = 'Fetches the latest air polution of Bordeaux from Atmo Nouvelle Aquitaine'; + const URI = 'https://www.atmo-nouvelleaquitaine.org'; + const DESCRIPTION = 'Fetches the latest air polution of cities in Nouvelle Aquitaine from Atmo'; const MAINTAINER = 'floviolleau'; const PARAMETERS = array(array( 'cities' => array( @@ -27,7 +27,7 @@ class AtmoNouvelleAquitaineBridge extends BridgeAbstract { } public function collectData() { - $uri = self::URI . $this->getInput('cities'); + $uri = self::URI . '/monair/commune/' . $this->getInput('cities'); $html = getSimpleHTMLDOM($uri) or returnServerError('Could not request ' . $uri); diff --git a/bridges/AtmoOccitanieBridge.php b/bridges/AtmoOccitanieBridge.php new file mode 100644 index 0000000..24f6383 --- /dev/null +++ b/bridges/AtmoOccitanieBridge.php @@ -0,0 +1,58 @@ +<?php +class AtmoOccitanieBridge extends BridgeAbstract { + + const NAME = 'Atmo Occitanie'; + const URI = 'https://www.atmo-occitanie.org/'; + const DESCRIPTION = 'Fetches the latest air polution of cities in Occitanie from Atmo'; + const MAINTAINER = 'floviolleau'; + const PARAMETERS = array(array( + 'city' => array( + 'name' => 'Ville', + 'required' => true + ) + )); + const CACHE_TIMEOUT = 7200; + + public function collectData() { + $uri = self::URI . $this->getInput('city'); + + $html = getSimpleHTMLDOM($uri) + or returnServerError('Could not request ' . $uri); + + $generalMessage = $html->find('.landing-ville .city-banner .iqa-avertissement', 0)->innertext; + $recommendationsDom = $html->find('.landing-ville .recommandations', 0); + $recommendationsItemDom = $recommendationsDom->find('.recommandation-item .label'); + + $recommendationsMessage = ''; + + $i = 0; + $len = count($recommendationsItemDom); + foreach ($recommendationsItemDom as $key => $value) { + if ($i == 0) { + $recommendationsMessage .= trim($value->innertext) . '.'; + } else { + $recommendationsMessage .= ' ' . trim($value->innertext) . '.'; + } + $i++; + } + + $lastRecommendationsDom = $recommendationsDom->find('.col-md-6', -1); + $informationHeaderMessage = $lastRecommendationsDom->find('.heading', 0)->innertext; + $indice = $lastRecommendationsDom->find('.current-indice .indice div', 0)->innertext; + $informationDescriptionMessage = $lastRecommendationsDom->find('.current-indice .description p', 0)->innertext; + + $message = "$generalMessage L'indice est de $indice/10. $informationDescriptionMessage. $recommendationsMessage"; + $city = $this->getInput('city'); + + $item['uri'] = $uri; + $today = date('d/m/Y'); + $item['title'] = "Bulletin de l'air du $today pour la ville : $city."; + //$item['title'] .= ' Retrouvez plus d\'informations en allant sur atmo-occitanie.org #QualiteAir. ' . $message; + $item['title'] .= ' #QualiteAir. ' . $message; + $item['author'] = 'floviolleau'; + $item['content'] = $message; + $item['uid'] = hash('sha256', $item['title']); + + $this->items[] = $item; + } +} diff --git a/bridges/DaveRamseyBlogBridge.php b/bridges/DaveRamseyBlogBridge.php new file mode 100644 index 0000000..34c9044 --- /dev/null +++ b/bridges/DaveRamseyBlogBridge.php @@ -0,0 +1,24 @@ +<?php + +class DaveRamseyBlogBridge extends BridgeAbstract { + const MAINTAINER = 'johnpc'; + const NAME = 'Dave Ramsey Blog'; + const URI = 'https://www.daveramsey.com/blog'; + const CACHE_TIMEOUT = 7200; // 2h + const DESCRIPTION = 'Returns blog posts from daveramsey.com'; + + public function collectData() + { + $html = getSimpleHTMLDOM(self::URI) + or returnServerError('Could not request daveramsey.com.'); + + foreach ($html->find('.Post') as $element) { + $this->items[] = array( + 'uri' => 'https://www.daveramsey.com' . $element->find('header > a', 0)->href, + 'title' => $element->find('header > h2 > a', 0)->plaintext, + 'tags' => $element->find('.Post-topic', 0)->plaintext, + 'content' => $element->find('.Post-body', 0)->plaintext, + ); + } + } +} diff --git a/bridges/DevToBridge.php b/bridges/DevToBridge.php index 868ac97..c298d46 100644 --- a/bridges/DevToBridge.php +++ b/bridges/DevToBridge.php @@ -51,15 +51,10 @@ apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png' $html = defaultLinkTo($html, static::URI); - $articles = $html->find('div[class="single-article"]') + $articles = $html->find('div.single-article') or returnServerError('Could not find articles!'); foreach($articles as $article) { - - if($article->find('[class*="cta"]', 0)) { // Skip ads - continue; - } - $item = array(); $item['uri'] = $article->find('a[id*=article-link]', 0)->href; @@ -92,6 +87,14 @@ EOD; } + public function getName() { + if (!is_null($this->getInput('tag'))) { + return ucfirst($this->getInput('tag')) . ' - dev.to'; + } + + return parent::getName(); + } + private function getFullArticle($url) { $html = getSimpleHTMLDOMCached($url) or returnServerError('Unable to load article from "' . $url . '"!'); diff --git a/bridges/DonnonsBridge.php b/bridges/DonnonsBridge.php new file mode 100644 index 0000000..7741349 --- /dev/null +++ b/bridges/DonnonsBridge.php @@ -0,0 +1,123 @@ +<?php +/** + * Retourne les dons d'une recherche filtrée sur le site Donnons.org + * Example: https://donnons.org/Sport/Ile-de-France + */ +class DonnonsBridge extends BridgeAbstract { + + const MAINTAINER = 'Binnette'; + const NAME = 'Donnons.org'; + const URI = 'https://donnons.org'; + const CACHE_TIMEOUT = 1800; // 30min + const DESCRIPTION = 'Retourne les dons depuis le site Donnons.org.'; + + const PARAMETERS = array( + array( + 'q' => array( + 'name' => 'Url de recherche', + 'required' => true, + 'exampleValue' => '/Sport/Ile-de-France', + 'pattern' => '\/.*', + 'title' => 'Faites une recherche sur le site. Puis copiez ici la fin de l’url. Doit commencer par /', + ), + 'p' => array( + 'name' => 'Nombre de pages à scanner', + 'type' => 'number', + 'defaultValue' => 5, + 'title' => 'Indique le nombre de pages de donnons.org qui seront scannées' + ) + ) + ); + + public function collectData() { + $pages = $this->getInput('p'); + + for($i = 1; $i <= $pages; $i++) { + $this->collectDataByPage($i); + } + } + + private function collectDataByPage($page) { + $uri = $this->getPageURI($page); + + $html = getSimpleHTMLDOM($uri) + or returnServerError('No results for this query.'); + + $searchDiv = $html->find('div[id=search]', 0); + + if(!is_null($searchDiv)) { + $elements = $searchDiv->find('a.lst-annonce'); + foreach($elements as $element) { + $item = array(); + + // Lien vers le don + $item['uri'] = self::URI . $element->href; + // Id de l'objet + $item['uid'] = $element->getAttribute('data-id'); + + // Grab info from json + $jsonString = $element->find('script', 0)->innertext; + $json = json_decode($jsonString, true); + + $name = $json['name']; + $category = $json['category']; + $date = $json['availabilityStarts']; + $description = $json['description']; + $city = $json['availableAtOrFrom']['address']['addressLocality']; + $region = $json['availableAtOrFrom']['address']['addressRegion']; + + // Grab info from HTML + $imageSrc = $element->find('img.ima-center', 0)->getAttribute('data-src'); + $image = self::URI . $imageSrc; + $author = $element->find('div.avatar-holder', 0)->plaintext; + + $content = ' + <img style="margin-right:1em;" src="' . $image . '"> + <div> + <h1>' . $name . '</h1> + <p>' . $description . '</p> + <p>Lieu : <b>' . $city . '</b> - ' . $region . '</p> + <p>Par : ' . $author . '</p> + <p>Date : ' . $date . '</p> + </div> + '; + + // Titre du don + $item['title'] = '[' . $category . '] ' . $name; + $item['timestamp'] = $date; + $item['author'] = $author; + $item['content'] = $content; + $item['enclosures'] = array($image); + + $this->items[] = $item; + } + } + } + + private function getPageURI($page) { + $uri = $this->getURI(); + $haveQueryParams = strpos($uri, '?') !== false; + + if($haveQueryParams) { + return $uri . '&page=' . $page; + } else { + return $uri . '?page=' . $page; + } + } + + public function getURI() { + if(!is_null($this->getInput('q'))) { + return self::URI . $this->getInput('q'); + } + + return parent::getURI(); + } + + public function getName() { + if(!is_null($this->getInput('q'))) { + return 'Donnons.org - ' . $this->getInput('q'); + } + + return parent::getName(); + } +} diff --git a/bridges/ExtremeDownloadBridge.php b/bridges/ExtremeDownloadBridge.php index 1b4aa9a..bca3997 100644 --- a/bridges/ExtremeDownloadBridge.php +++ b/bridges/ExtremeDownloadBridge.php @@ -1,7 +1,7 @@ <?php class ExtremeDownloadBridge extends BridgeAbstract { const NAME = 'Extreme Download'; - const URI = 'https://wvw.extreme-down.xyz/'; + const URI = 'https://www.extreme-down.ninja/'; const DESCRIPTION = 'Suivi de série sur Extreme Download'; const MAINTAINER = 'sysadminstory'; const PARAMETERS = array( diff --git a/bridges/FacebookBridge.php b/bridges/FacebookBridge.php index 5ce67f9..13ccb27 100644 --- a/bridges/FacebookBridge.php +++ b/bridges/FacebookBridge.php @@ -729,6 +729,7 @@ EOD; } } } + #endregion (User) } diff --git a/bridges/FirstLookMediaTechBridge.php b/bridges/FirstLookMediaTechBridge.php new file mode 100644 index 0000000..114bf62 --- /dev/null +++ b/bridges/FirstLookMediaTechBridge.php @@ -0,0 +1,50 @@ +<?php +class FirstLookMediaTechBridge extends BridgeAbstract { + const NAME = 'First Look Media - Technology'; + const URI = 'https://tech.firstlook.media'; + const DESCRIPTION = 'First Look Media Technology page'; + const MAINTAINER = 'somini'; + const PARAMETERS = array( + array( + 'projects' => array( + 'type' => 'checkbox', + 'name' => 'Include Projects?', + ) + ) + ); + + public function collectData() { + $html = getSimpleHTMLDOM(self::URI) + or returnServerError('Could not load content'); + + if ($this->getInput('projects')) { + $top_projects = $html->find('.PromoList-ul', 0); + foreach($top_projects->find('li.PromoList-item') as $element) { + $item = array(); + + $item_uri = $element->find('a', 0); + $item['uri'] = $item_uri->href; + $item['title'] = strip_tags($item_uri->innertext); + $item['content'] = $element->find('div > div', 0); + + $this->items[] = $item; + } + } + + $top_articles = $html->find('.PromoList-ul', 1); + foreach($top_articles->find('li.PromoList-item') as $element) { + $item = array(); + + $item_left = $element->find('div > div', 0); + $item_date = $element->find('.PromoList-date', 0); + $item['timestamp'] = strtotime($item_date->innertext); + $item_date->outertext = ''; /* Remove */ + $item['author'] = $item_left->innertext; + $item_uri = $element->find('a', 0); + $item['uri'] = self::URI . $item_uri->href; + $item['title'] = strip_tags($item_uri); + + $this->items[] = $item; + } + } +} diff --git a/bridges/FolhaDeSaoPauloBridge.php b/bridges/FolhaDeSaoPauloBridge.php new file mode 100644 index 0000000..acd8d25 --- /dev/null +++ b/bridges/FolhaDeSaoPauloBridge.php @@ -0,0 +1,51 @@ +<?php +class FolhaDeSaoPauloBridge extends FeedExpander { + const MAINTAINER = 'somini'; + const NAME = 'Folha de São Paulo'; + const URI = 'https://www1.folha.uol.com.br'; + const DESCRIPTION = 'Returns the newest posts from Folha de São Paulo (full text)'; + const PARAMETERS = array( + array( + 'feed' => array( + 'name' => 'Feed sub-URL', + 'type' => 'text', + 'title' => 'Select the sub-feed (see https://www1.folha.uol.com.br/feed/)', + 'exampleValue' => 'emcimadahora/rss091.xml', + ) + ) + ); + + protected function parseItem($item){ + $item = parent::parseItem($item); + + $articleHTMLContent = getSimpleHTMLDOMCached($item['uri']); + if($articleHTMLContent) { + foreach ($articleHTMLContent->find('div.c-news__body .is-hidden') as $toRemove) { + $toRemove->innertext = ''; + } + $item_content = $articleHTMLContent->find('div.c-news__body', 0); + if ($item_content) { + $text = $item_content->innertext; + $text = strip_tags($text, '<p><b><a><blockquote><img><em>'); + $item['content'] = $text; + } + } else { + Debug::log('???: ' . $item['uri']); + } + + return $item; + } + + public function collectData(){ + $feed_input = $this->getInput('feed'); + if (substr($feed_input, 0, strlen(self::URI)) === self::URI) { + Debug::log('Input:: ' . $feed_input); + $feed_url = $feed_input; + } else { + /* TODO: prepend `/` if missing */ + $feed_url = self::URI . '/' . $this->getInput('feed'); + } + Debug::log('URL: ' . $feed_url); + $this->collectExpandableDatas($feed_url); + } +} diff --git a/bridges/GithubIssueBridge.php b/bridges/GithubIssueBridge.php index 2eddeb2..29a336b 100644 --- a/bridges/GithubIssueBridge.php +++ b/bridges/GithubIssueBridge.php @@ -82,17 +82,20 @@ class GithubIssueBridge extends BridgeAbstract { $uri = $this->buildGitHubIssueCommentUri($issueNbr, $comment->id); - $author = $comment->find('.author', 0)->plaintext; + $author = $comment->find('.author', 0); + if ($author) { + $author = $author->plaintext; + } else { + $author = ''; + } - $title .= ' / ' . trim($comment->plaintext); + $title .= ' / ' + . trim(str_replace( + array('octicon','-'), array(''), + $comment->find('.octicon', 0)->getAttribute('class') + )); - $content = $title; - if (null !== $comment->nextSibling()) { - $content = $comment->nextSibling()->innertext; - if ($comment->nextSibling()->nodeName() === 'span') { - $content = $comment->nextSibling()->nextSibling()->innertext; - } - } + $content = $comment->plaintext; $item = array(); $item['author'] = $author; @@ -135,32 +138,20 @@ class GithubIssueBridge extends BridgeAbstract { substr($issue->find('.gh-header-number', 0)->plaintext, 1) ); - $comments = $issue->find(' - [id^="issue-"] > .comment, - [id^="issuecomment-"] > .comment, - [id^="event-"], - [id^="ref-"] - '); - foreach($comments as $comment) { - - if (!$comment->hasChildNodes()) { - continue; - } + $comments = $issue->find( + '.comment, .TimelineItem-badge' + ); - if (!$comment->hasClass('discussion-item-header')) { + foreach($comments as $comment) { + if ($comment->hasClass('comment')) { + $comment = $comment->parent; $item = $this->extractIssueComment($issueNbr, $title, $comment); $items[] = $item; continue; - } - - while ($comment->hasClass('discussion-item-header')) { + } else { + $comment = $comment->parent; $item = $this->extractIssueEvent($issueNbr, $title, $comment); $items[] = $item; - $comment = $comment->nextSibling(); - if (null == $comment) { - break; - } - $classes = explode(' ', $comment->getAttribute('class')); } } diff --git a/bridges/GithubSearchBridge.php b/bridges/GithubSearchBridge.php index fd90934..9c1face 100644 --- a/bridges/GithubSearchBridge.php +++ b/bridges/GithubSearchBridge.php @@ -27,16 +27,16 @@ class GithubSearchBridge extends BridgeAbstract { foreach($html->find('li.repo-list-item') as $element) { $item = array(); - $uri = $element->find('h3 a', 0)->href; + $uri = $element->find('.f4 a', 0)->href; $uri = substr(self::URI, 0, -1) . $uri; $item['uri'] = $uri; - $title = $element->find('h3', 0)->plaintext; + $title = $element->find('.f4', 0)->plaintext; $item['title'] = $title; // Description - if (count($element->find('p.d-inline-block')) != 0) { - $content = $element->find('p.d-inline-block', 0)->innertext; + if (count($element->find('p.mb-1')) != 0) { + $content = $element->find('p.mb-1', 0)->innertext; } else{ $content = 'No description'; } diff --git a/bridges/GoComicsBridge.php b/bridges/GoComicsBridge.php index 3223d19..7512d84 100644 --- a/bridges/GoComicsBridge.php +++ b/bridges/GoComicsBridge.php @@ -28,7 +28,7 @@ class GoComicsBridge extends BridgeAbstract { $page = getSimpleHTMLDOM($link) or returnServerError('Could not request GoComics: ' . $link); - $imagelink = $page->find('.img-fluid', 1)->src; + $imagelink = $page->find('.comic.container', 0)->getAttribute('data-image'); $date = explode('/', $link); $item['id'] = $imagelink; diff --git a/bridges/IGNBridge.php b/bridges/IGNBridge.php index 6a254b3..ef5088f 100644 --- a/bridges/IGNBridge.php +++ b/bridges/IGNBridge.php @@ -19,6 +19,27 @@ class IGNBridge extends FeedExpander { // $articlePage gets the entire page's contents $articlePage = getSimpleHTMLDOM($newsItem->link); + // List of BS elements + $uselessElements = array( + '.wiki-page-tools', + '.feedback-container', + '.paging-container', + '.dropdown-wrapper', + '.mw-editsection', + '.jsx-4115608983', + '.jsx-4213937408', + '.commerce-container', + '.widget-container', + '.newsletter-signup-button' + ); + + // Remove useless elements + foreach($uselessElements as $uslElement) { + foreach($articlePage->find($uslElement) as $jsWidget) { + $jsWidget->remove(); + } + } + /* * NOTE: Though articles and wiki/howtos have seperate styles of pages, there is no mechanism * for handling them seperately as it just ignores the DOM querys which it does not find. @@ -33,19 +54,8 @@ class IGNBridge extends FeedExpander { } // For Wikis and HowTos - $uselessWikiElements = array( - '.wiki-page-tools', - '.feedback-container', - '.paging-container' - ); foreach($articlePage->find('.wiki-page') as $wikiContents) { - $copy = clone $wikiContents; - // Remove useless elements present in IGN wiki/howtos - foreach($uselessWikiElements as $uslElement) { - $toRemove = $wikiContents->find($uslElement, 0); - $copy = str_replace($toRemove, '', $copy); - } - $article = $article . $copy; + $article = $article . $wikiContents; } // Add content to feed diff --git a/bridges/InstagramBridge.php b/bridges/InstagramBridge.php index 679c4c0..0a6dbaa 100644 --- a/bridges/InstagramBridge.php +++ b/bridges/InstagramBridge.php @@ -123,6 +123,12 @@ class InstagramBridge extends BridgeAbstract { $item['title'] = substr($item['title'], 0, $titleLinePos) . '...'; } + if($directLink) { + $mediaURI = $media->display_url; + } else { + $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l'; + } + switch($media->__typename) { case 'GraphSidecar': $data = $this->getInstagramSidecarData($item['uri'], $item['title']); @@ -130,24 +136,20 @@ class InstagramBridge extends BridgeAbstract { $item['enclosures'] = $data[1]; break; case 'GraphImage': - if($directLink) { - $mediaURI = $media->display_url; - } else { - $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l'; - } $item['content'] = '<a href="' . htmlentities($item['uri']) . '" target="_blank">'; $item['content'] .= '<img src="' . htmlentities($mediaURI) . '" alt="' . $item['title'] . '" />'; $item['content'] .= '</a><br><br>' . nl2br(htmlentities($textContent)); $item['enclosures'] = array($mediaURI); break; case 'GraphVideo': - $data = $this->getInstagramVideoData($item['uri']); + $data = $this->getInstagramVideoData($item['uri'], $mediaURI); $item['content'] = $data[0]; if($directLink) { $item['enclosures'] = $data[1]; } else { - $item['enclosures'] = array(self::URI . 'p/' . $media->shortcode . '/media?size=l'); + $item['enclosures'] = array($mediaURI); } + $item['thumbnail'] = $mediaURI; break; default: break; } @@ -185,11 +187,14 @@ class InstagramBridge extends BridgeAbstract { } // returns Video post's contents and enclosures - protected function getInstagramVideoData($uri) { + protected function getInstagramVideoData($uri, $mediaURI) { $mediaInfo = $this->getSinglePostData($uri); $textContent = $this->getTextContent($mediaInfo); - $content = '<video controls><source src="' . $mediaInfo->video_url . '" type="video/mp4"></video><br>'; + $content = '<video controls>'; + $content .= '<source src="' . $mediaInfo->video_url . '" poster="' . $mediaURI . '" type="video/mp4">'; + $content .= '<img src="' . $mediaURI . '" alt="">'; + $content .= '</video><br>'; $content .= '<br>' . nl2br(htmlentities($textContent)); return array($content, array($mediaInfo->video_url)); diff --git a/bridges/JustETFBridge.php b/bridges/JustETFBridge.php index 8d5b3d5..746f1c9 100644 --- a/bridges/JustETFBridge.php +++ b/bridges/JustETFBridge.php @@ -347,5 +347,6 @@ class JustETFBridge extends BridgeAbstract { return $element->plaintext; } + #endregion } diff --git a/bridges/MozillaSecurityBridge.php b/bridges/MozillaSecurityBridge.php index 52672f5..1e7dc31 100644 --- a/bridges/MozillaSecurityBridge.php +++ b/bridges/MozillaSecurityBridge.php @@ -15,7 +15,7 @@ class MozillaSecurityBridge extends BridgeAbstract { $html = defaultLinkTo($html, self::WEBROOT); $item = array(); - $articles = $html->find('div[itemprop="articleBody"] h2'); + $articles = $html->find('div[id="main-content"] h2'); foreach ($articles as $element) { $item['title'] = $element->innertext; diff --git a/bridges/NewOnNetflixBridge.php b/bridges/NewOnNetflixBridge.php new file mode 100644 index 0000000..bb35e71 --- /dev/null +++ b/bridges/NewOnNetflixBridge.php @@ -0,0 +1,59 @@ +<?php + +class NewOnNetflixBridge extends BridgeAbstract { + const NAME = 'NewOnNetflix removals bridge'; + const URI = 'https://www.newonnetflix.info'; + const DESCRIPTION = 'Upcoming removals from Netflix (NewOnNetflix already provides additions as RSS)'; + const MAINTAINER = 'jdesgats'; + const PARAMETERS = array(array( + 'country' => array( + 'name' => 'Country', + 'type' => 'list', + 'values' => array( + 'Australia/New Zealand' => 'anz', + 'Canada' => 'can', + 'United Kingdom' => 'uk', + 'United States' => 'usa', + ), + 'defaultValue' => 'uk', + ) + )); + const CACHE_TIMEOUT = 3600 * 24; + + public function collectData() { + $baseURI = 'https://' . $this->getInput('country') . '.newonnetflix.info'; + $html = getSimpleHTMLDOMCached($baseURI . '/lastchance', self::CACHE_TIMEOUT) + or returnServerError('Could not request NewOnNetflix (U FAILED LOL).'); + + foreach($html->find('article.oldpost') as $element) { + $title = $element->find('a.infopop[title]', 0); + $img = $element->find('img[lazy_src]', 0); + $date = $element->find('span[title]', 0); + + // format sholud be 'dd/mm/yy - dd/mm/yy' + // (the added date might be "unknown") + $fromTo = array(); + if (preg_match('/^\s*(.*?)\s*-\s*(.*?)\s*$/', $date->title, $fromTo)) { + $from = $fromTo[1]; + $to = $fromTo[2]; + } else { + $from = 'unknown'; + $to = 'unknown'; + } + $summary = <<<EOD + <img src="{$img->lazy_src}" loading="lazy"> + <div>{$title->title}</div> + <div><strong>Added on:</strong>$from</div> + <div><strong>Removed on:</strong>$to</div> +EOD; + + $item = array(); + $item['uri'] = $baseURI . $title->href; + $item['title'] = $to . ' - ' . $title->plaintext; + $item['content'] = $summary; + // some movies are added and removed multiple times + $item['uid'] = $title->href . '-' . $to; + $this->items[] = $item; + } + } +} diff --git a/bridges/PcGamerBridge.php b/bridges/PcGamerBridge.php index e0e55ce..c4bcccf 100644 --- a/bridges/PcGamerBridge.php +++ b/bridges/PcGamerBridge.php @@ -2,22 +2,43 @@ class PcGamerBridge extends BridgeAbstract { const NAME = 'PC Gamer'; - const URI = 'https://www.pcgamer.com/'; + const URI = 'https://www.pcgamer.com/archive/'; const DESCRIPTION = 'PC Gamer Most Read Stories'; - const MAINTAINER = 'mdemoss'; + const CACHE_TIMEOUT = 3600; + const MAINTAINER = 'IceWreck, mdemoss'; public function collectData() { $html = getSimpleHTMLDOMCached($this->getURI(), 300); - $stories = $html->find('div#popularcontent li.most-popular-item'); + $stories = $html->find('ul.basic-list li.day-article'); + $i = 0; + // Find induvidual stories in the archive page foreach ($stories as $element) { + if($i == 15) break; $item['uri'] = $element->find('a', 0)->href; + // error_log(print_r($item['uri'], TRUE)); $articleHtml = getSimpleHTMLDOMCached($item['uri']); - $item['title'] = $element->find('h4 a', 0)->plaintext; + $item['title'] = $element->find('a', 0)->plaintext; $item['timestamp'] = strtotime($articleHtml->find('meta[name=pub_date]', 0)->content); - $item['content'] = $articleHtml->find('meta[name=description]', 0)->content; - $item['author'] = $articleHtml->find('a[itemprop=author]', 0)->plaintext; + $item['author'] = $articleHtml->find('span.by-author a', 0)->plaintext; + + // Get the article content + $articleContents = $articleHtml->find('#article-body', 0); + + /* + By default the img src has a link to an error image and then the actual image + is added in by JS. So we replace the error image with the actual full size image + whoose link is in one of the attributes of the img tag + */ + foreach($articleContents->find('img') as $img) { + $imgsrc = $img->getAttribute('data-original-mos'); + // error_log($imgsrc); + $img->src = $imgsrc; + } + + $item['content'] = $articleContents; $this->items[] = $item; + $i++; } } } diff --git a/bridges/PhoronixBridge.php b/bridges/PhoronixBridge.php new file mode 100644 index 0000000..c5ded27 --- /dev/null +++ b/bridges/PhoronixBridge.php @@ -0,0 +1,22 @@ +<?php +class PhoronixBridge extends FeedExpander { + + const MAINTAINER = 'IceWreck'; + const NAME = 'Phoronix Bridge'; + const URI = 'https://www.phoronix.com'; + const CACHE_TIMEOUT = 3600; + const DESCRIPTION = 'RSS feed for Linux news website Phoronix'; + + public function collectData(){ + $this->collectExpandableDatas('https://www.phoronix.com/rss.php', 15); + } + + protected function parseItem($newsItem){ + $item = parent::parseItem($newsItem); + // $articlePage gets the entire page's contents + $articlePage = getSimpleHTMLDOM($newsItem->link); + $article = $articlePage->find('.content', 0); + $item['content'] = $article; + return $item; + } +} diff --git a/bridges/PornhubBridge.php b/bridges/PornhubBridge.php new file mode 100644 index 0000000..b8da99a --- /dev/null +++ b/bridges/PornhubBridge.php @@ -0,0 +1,99 @@ +<?php + +class PornhubBridge extends BridgeAbstract { + + const MAINTAINER = 'Mitsukarenai'; + const NAME = 'Pornhub'; + const URI = 'https://www.pornhub.com/'; + const CACHE_TIMEOUT = 3600; // 1h + const DESCRIPTION = 'Returns videos from specified user,model,pornstar'; + + const PARAMETERS = array(array( + 'q' => array( + 'name' => 'User name', + 'required' => true, + ), + 'type' => array( + 'name' => 'User type', + 'type' => 'list', + 'values' => array( + 'user' => 'users', + 'model' => 'model', + 'pornstar' => 'pornstar', + ), + 'defaultValue' => 'users', + ), + 'sort' => array( + 'name' => 'Sort by', + 'type' => 'list', + 'values' => array( + 'Most recent' => '?', + 'Most views' => '?o=mv', + 'Top rated' => '?o=tr', + 'Longest' => '?o=lg', + ), + 'defaultValue' => '?', + ), + 'show_images' => array( + 'name' => 'Show thumbnails', + 'type' => 'checkbox', + ), + )); + + public function getName(){ + if(!is_null($this->getInput('type')) && !is_null($this->getInput('q'))) { + return 'PornHub ' . $this->getInput('type') . ':' . $this->getInput('q'); + } + + return parent::getName(); + } + + public function collectData() { + + $uri = 'https://www.pornhub.com/' . $this->getInput('type') . '/'; + switch($this->getInput('type')) { // select proper permalink format per user type... + case 'model': + $uri .= urlencode($this->getInput('q')) . '/videos' . $this->getInput('sort'); break; + case 'users': + $uri .= urlencode($this->getInput('q')) . '/videos/public' . $this->getInput('sort'); break; + case 'pornstar': + $uri .= urlencode($this->getInput('q')) . '/videos/upload' . $this->getInput('sort'); break; + } + + $show_images = $this->getInput('show_images'); + + $html = getSimpleHTMLDOM($uri) + or returnServerError('Could not request PornHub.'); + + foreach($html->find('div.videoUList ul.videos li.videoblock') as $element) { + + $item = array(); + + $item['author'] = $this->getInput('q'); + + // Title + $title = $element->find('a', 0)->getAttribute('title'); + if (is_null($title)) { + continue; + } + $item['title'] = $title; + + // Url + $url = $element->find('a', 0)->href; + $item['uri'] = 'https://www.pornhub.com' . $url; + + // Content + $image = $element->find('img', 0)->getAttribute('data-src'); + if($show_images === true) { + $item['content'] = '<a href="' . $item['uri'] . '"><img src="' . $image . '"></a>'; + } + + // date hack, guess upload YYYYMMDD from thumbnail URL (format: https://ci.phncdn.com/videos/201907/25/--- ) + $uploaded = explode('/', $image); + $uploaded = strtotime($uploaded[4] . $uploaded[5]); + $item['timestamp'] = $uploaded; + + $this->items[] = $item; + } + } +} diff --git a/bridges/RainbowSixSiegeBridge.php b/bridges/RainbowSixSiegeBridge.php index 724edc8..62ea482 100644 --- a/bridges/RainbowSixSiegeBridge.php +++ b/bridges/RainbowSixSiegeBridge.php @@ -2,19 +2,18 @@ class RainbowSixSiegeBridge extends BridgeAbstract { const MAINTAINER = 'corenting'; - const NAME = 'Rainbow Six Siege Blog'; - const URI = 'https://rainbow6.ubisoft.com/siege/en-us/news/'; + const NAME = 'Rainbow Six Siege News'; + const URI = 'https://www.ubisoft.com/en-us/game/rainbow-six/siege/news-updates'; const CACHE_TIMEOUT = 7200; // 2h - const DESCRIPTION = 'Latest articles from the Rainbow Six Siege blog'; + const DESCRIPTION = 'Latest news about Rainbow Six Siege'; public function getIcon() { - return 'https://ubistatic19-a.akamaihd.net/resource/en-us/game/rainbow6/siege-v3/r6s-favicon_316592.ico'; + return 'https://static-dm.akamaized.net/siege/prod/favicon-144x144.png'; } public function collectData(){ - $dlUrl = 'https://prod-tridionservice.ubisoft.com/live/v1/News/Latest?templateId=tcm%3A152-7677'; - $dlUrl .= '8-32&pageIndex=0&pageSize=10&language=en-US&detailPageId=tcm%3A150-194572-64'; - $dlUrl .= '&keywordList=233416%2C316144%2C233418%2C233417&siteId=undefined&useSeoFriendlyUrl=true'; + $dlUrl = 'https://www.ubisoft.com/api/updates/items?categoriesFilter=all'; + $dlUrl = $dlUrl . '&limit=6&mediaFilter=all&skip=0&startIndex=undefined&locale=en-us'; $jsonString = getContents($dlUrl) or returnServerError('Error while downloading the website content'); $json = json_decode($jsonString, true); @@ -22,17 +21,47 @@ class RainbowSixSiegeBridge extends BridgeAbstract { // Start at index 2 to remove highlighted articles for($i = 0; $i < count($json); $i++) { - $jsonItem = $json[$i]['Content']; - $article = str_get_html($jsonItem); + $jsonItem = $json[$i]; - $item = array(); + $uri = 'https://www.ubisoft.com/en-us/game/rainbow-six/siege'; + $uri = $uri . $jsonItem['button']['buttonUrl']; + + $thumbnail = '<img src="' . $jsonItem['thumbnail']['url'] . '" alt="Thumbnail">'; + $content = $thumbnail . '<br />' . $jsonItem['content']; + + // Markdown parsing from https://gist.github.com/jbroadway/2836900 + + // Line breaks + $content = preg_replace("/\r\n|\r|\n/", '<br/>', $content); + + // Links + $regex = '/\[([^\[]+)\]\(([^\)]+)\)/'; + $replacement = '<a href=\'\2\'>\1</a>'; + $content = preg_replace($regex, $replacement, $content); - $uri = $article->find('h3 a', 0)->href; - $uri = 'https://rainbow6.ubisoft.com' . $uri; + // Bold text + $regex = '/(\*\*|__)(.*?)\1/'; + $replacement = '<strong>\2</strong>'; + $content = preg_replace($regex, $replacement, $content); + + // Lists + $regex = '/\n\s*[\*|\-](.*)/'; + $content = preg_replace_callback($regex, function($regs) { + $item = $regs[1]; + return sprintf ('<ul><li>%s</li></ul>', trim ($item)); + }, $content); + + // Italic text + $regex = '/(\*\*|\*)(.*?)\1/'; + $replacement = '<i>\2</i>'; + $content = preg_replace($regex, $replacement, $content); + + $item = array(); $item['uri'] = $uri; - $item['title'] = $article->find('h3', 0)->plaintext; - $item['content'] = $article->find('img', 0)->outertext . '<br />' . $article->find('strong', 0)->plaintext; - $item['timestamp'] = strtotime($article->find('p.news_date', 0)->plaintext); + $item['id'] = $jsonItem['id']; + $item['title'] = $jsonItem['title']; + $item['content'] = $content; + $item['timestamp'] = strtotime($jsonItem['date']); $this->items[] = $item; } diff --git a/bridges/RedditBridge.php b/bridges/RedditBridge.php index d83c0a3..8de499f 100644 --- a/bridges/RedditBridge.php +++ b/bridges/RedditBridge.php @@ -27,7 +27,7 @@ class RedditBridge extends FeedExpander { public function collectData(){ - switch($this->queriedcontext) { + switch($this->queriedContext) { case 'single': $subreddits[] = $this->getInput('r'); break; case 'multi': $subreddits = explode(',', $this->getInput('rs')); break; } diff --git a/bridges/RevolutBridge.php b/bridges/RevolutBridge.php deleted file mode 100644 index 04ca377..0000000 --- a/bridges/RevolutBridge.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -class RevolutBridge extends BridgeAbstract { - - const NAME = 'Revolut Blog'; - const URI = 'https://blog.revolut.com/'; - const DESCRIPTION = 'Returns recent blog posts from Revolut.'; - const MAINTAINER = 'dominik-th'; - - public function getIcon() { - return self::URI . 'favicon.png'; - } - - public function collectData() { - $articleOverview = getSimpleHTMLDOM(self::URI . 'sitemap-posts.xml') - or returnServerError('Error while downloading the website content'); - - $articles = array_slice($articleOverview->find('url'), 0, 15); - - foreach($articles as $article) { - $item = array(); - - $item['uri'] = $article->find('loc', 0)->plaintext; - $item['timestamp'] = $article->find('lastmod', 0)->plaintext; - $item['enclosures'] = array( - $article->find('image:loc', 0)->plaintext - ); - - $fullArticle = getSimpleHTMLDOMCached($item['uri']) - or returnServerError('Error while downloading the full article'); - - $item['author'] = $fullArticle - ->find('h4[class="author-card-name"] a', 0) - ->plaintext; - $item['title'] = $fullArticle - ->find('h1[class="post-full-title"]', 0) - ->plaintext; - - $content = $fullArticle - ->find('section[class="post-full-content"]', 0); - - foreach($content->find('img') as $image) { - $image->src = $this->generateAbsoluteUrl($image->src); - } - - foreach($content->find('a') as $hyperlink) { - $hyperlink->href = $this->generateAbsoluteUrl($hyperlink->href); - } - - foreach($content->find('iframe') as $iframe) { - $iframe->outertext = $this->generateYoutubeReplacement($iframe); - } - - $item['content'] = $content->innertext; - $this->items[] = $item; - } - } - - private function generateAbsoluteUrl($path) { - if (filter_var($path, FILTER_VALIDATE_URL)) { - return $path; - } else { - return self::URI . $path; - } - } - - private function generateYoutubeReplacement($iframe) { - $embedUrl = $iframe->src; - if (parse_url($embedUrl, PHP_URL_HOST) === 'www.youtube.com') { - $urlParts = explode('/', parse_url($embedUrl, PHP_URL_PATH)); - $videoId = end($urlParts); - $thumbnailUrl = 'https://img.youtube.com/vi/' . $videoId . '/0.jpg'; - $videoUrl = 'https://www.youtube.com/watch?v=' . $videoId; - $videoReplacement = str_get_html('<a><img /></a>'); - $videoReplacement->find('a', 0)->href = $videoUrl; - $videoReplacement->find('img', 0)->src = $thumbnailUrl; - return $videoReplacement; - } - return $iframe->outertext; - } -} diff --git a/bridges/RoosterTeethBridge.php b/bridges/RoosterTeethBridge.php new file mode 100644 index 0000000..496c7de --- /dev/null +++ b/bridges/RoosterTeethBridge.php @@ -0,0 +1,107 @@ +<?php + +class RoosterTeethBridge extends BridgeAbstract { + + const MAINTAINER = 'tgkenney'; + const NAME = 'Rooster Teeth'; + const URI = 'https://roosterteeth.com'; + const DESCRIPTION = 'Gets the latest channel videos from the Rooster Teeth website'; + const API = 'https://svod-be.roosterteeth.com/'; + + const PARAMETERS = array( + 'Options' => array( + 'channel' => array( + 'type' => 'list', + 'name' => 'Channel', + 'title' => 'Select a channel to filter by', + 'values' => array( + 'All channels' => 'all', + 'Achievement Hunter' => 'achievement-hunter', + 'Cow Chop' => 'cow-chop', + 'Death Battle' => 'death-battle', + 'Funhaus' => 'funhaus', + 'Inside Gaming' => 'inside-gaming', + 'JT Music' => 'jt-music', + 'Kinda Funny' => 'kinda-funny', + 'Rooster Teeth' => 'rooster-teeth', + 'Sugar Pine 7' => 'sugar-pine-7' + ) + ), + 'sort' => array( + 'type' => 'list', + 'name' => 'Sort', + 'title' => 'Select a sort order', + 'values' => array( + 'Newest -> Oldest' => 'desc', + 'Oldest -> Newest' => 'asc' + ), + 'defaultValue' => 'desc' + ), + 'first' => array( + 'type' => 'list', + 'name' => 'RoosterTeeth First', + 'title' => 'Select whether to include "First" videos before they are public', + 'values' => array( + 'True' => true, + 'False' => false + ) + ), + 'limit' => array( + 'name' => 'Limit', + 'type' => 'number', + 'required' => false, + 'title' => 'Maximum number of items to return', + 'defaultValue' => 10 + ) + ) + ); + + public function collectData() { + if ($this->getInput('channel') !== 'all') { + $uri = self::API + . 'api/v1/episodes?per_page=' + . $this->getInput('limit') + . '&channel_id=' + . $this->getInput('channel') + . '&order=' . $this->getInput('sort') + . '&page=1'; + + $htmlJSON = getSimpleHTMLDOM($uri) + or returnServerError('Could not contact Rooster Teeth: ' . $uri); + } else { + $uri = self::API + . '/api/v1/episodes?per_page=' + . $this->getInput('limit') + . '&filter=all&order=' + . $this->getInput('sort') + . '&page=1'; + + $htmlJSON = getSimpleHTMLDOM($uri) + or returnServerError('Could not contact Rooster Teeth: ' . $uri); + } + + $htmlArray = json_decode($htmlJSON, true); + + foreach($htmlArray['data'] as $key => $value) { + $item = array(); + + if (!$this->getInput('first') && $value['attributes']['is_sponsors_only']) { + continue; + } + + $publicDate = date_create($value['attributes']['member_golive_at']); + $dateDiff = date_diff($publicDate, date_create(), false); + + if (!$this->getInput('first') && $dateDiff->invert == 1) { + continue; + } + + $item['uri'] = self::URI . $value['canonical_links']['self']; + $item['title'] = $value['attributes']['title']; + $item['timestamp'] = $value['attributes']['member_golive_at']; + $item['author'] = $value['attributes']['show_title']; + + $this->items[] = $item; + } + } +} diff --git a/bridges/ScribdBridge.php b/bridges/ScribdBridge.php new file mode 100644 index 0000000..3cb4199 --- /dev/null +++ b/bridges/ScribdBridge.php @@ -0,0 +1,83 @@ +<?php +class ScribdBridge extends BridgeAbstract { + const NAME = 'Scribd Bridge'; + const URI = 'https://www.scribd.com'; + const DESCRIPTION = 'Returns documents uploaded by a user.'; + const MAINTAINER = 'VerifiedJoseph'; + const PARAMETERS = array(array( + 'profile' => array( + 'name' => 'Profile URL', + 'type' => 'text', + 'required' => true, + 'title' => 'Profile URL. Example: https://www.scribd.com/user/489040929/number10leaks-com', + 'exampleValue' => 'https://www.scribd.com/user/' + ), + )); + + const CACHE_TIMEOUT = 3600; + + private $profileUrlRegex = '/scribd\.com\/(user\/[0-9]+\/[\w-]+)\/?/'; + private $feedName = ''; + + public function collectData() { + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request: ' . $this->getURI()); + + $header = $html->find('div.header', 0); + $this->feedName = $header->find('a', 0)->plaintext; + + foreach($html->find('div.content ul li') as $index => $li) { + $item = array(); + + $item['title'] = $li->find('div.under_title', 0)->plaintext; + $item['uri'] = $li->find('a', 0)->href; + $item['author'] = $li->find('span.uploader', 0)->plaintext; + //$item['timestamp'] = + $item['uid'] = $li->find('a', 0)->href; + + $pageHtml = getSimpleHTMLDOMCached($item['uri'], 3600) + or returnServerError('Could not request: ' . $item['uri']); + + $image = $pageHtml->find('meta[property="og:image"]', 0)->content; + $description = $pageHtml->find('meta[property="og:description"]', 0)->content; + + foreach ($pageHtml->find('ul.interest_pills li') as $pills) { + $item['categories'][] = $pills->plaintext; + } + + $item['content'] = <<<EOD +<p>{$description}<p><p><img src="{$image}"></p> +EOD; + + $item['enclosures'][] = $image; + + $this->items[] = $item; + + if (count($this->items) >= 15) { + break; + } + } + } + + public function getName() { + + if ($this->feedName) { + return $this->feedName . ' - Scribd'; + } + + return parent::getName(); + } + + public function getURI() { + + if (!is_null($this->getInput('profile'))) { + preg_match($this->profileUrlRegex, $this->getInput('profile'), $user) + or returnServerError('Could not extract user ID and name from given profile URL.'); + + return self::URI . '/' . $user[1] . '/uploads'; + } + + return parent::getURI(); + } +} diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php index 9607d33..99a2117 100644 --- a/bridges/SoundcloudBridge.php +++ b/bridges/SoundcloudBridge.php @@ -59,9 +59,13 @@ class SoundCloudBridge extends BridgeAbstract { return parent::getIcon(); } + public function getURI(){ + return 'https://soundcloud.com/' . $this->getInput('u'); + } + public function getName(){ if(!is_null($this->getInput('u'))) { - return self::NAME . ' - ' . $this->getInput('u'); + return $this->getInput('u') . ' - ' . self::NAME; } return parent::getName(); diff --git a/bridges/TinyLetterBridge.php b/bridges/TinyLetterBridge.php new file mode 100644 index 0000000..e9860b5 --- /dev/null +++ b/bridges/TinyLetterBridge.php @@ -0,0 +1,54 @@ +<?php +class TinyLetterBridge extends BridgeAbstract { + const NAME = 'Tiny Letter'; + const URI = 'https://tinyletter.com/'; + const DESCRIPTION = 'Tiny Letter is a mailing list service'; + const MAINTAINER = 'somini'; + const PARAMETERS = array( + array( + 'username' => array( + 'name' => 'User Name', + 'exampleValue' => 'forwards', + ) + ) + ); + + public function getName() { + $username = $this->getInput('username'); + if (!is_null($username)) { + return static::NAME . ' | ' . $username; + } + + return parent::getName(); + } + + public function getURI() { + $username = $this->getInput('username'); + if (!is_null($username)) { + return static::URI . urlencode($username); + } + + return parent::getURI(); + } + + public function collectData() { + $archives = self::getURI() . '/archive'; + $html = getSimpleHTMLDOMCached($archives) + or returnServerError('Could not load content'); + + foreach($html->find('.message-list li') as $element) { + $item = array(); + + $snippet = $element->find('p.message-snippet', 0); + $link = $element->find('.message-link', 0); + + $item['title'] = $link->plaintext; + $item['content'] = $snippet->innertext; + $item['uri'] = $link->href; + $item['timestamp'] = strtotime($element->find('.message-date', 0)->plaintext); + + $this->items[] = $item; + } + + } +} diff --git a/bridges/TorrentGalaxyBridge.php b/bridges/TorrentGalaxyBridge.php new file mode 100644 index 0000000..b71d2a5 --- /dev/null +++ b/bridges/TorrentGalaxyBridge.php @@ -0,0 +1,120 @@ +<?php + +class TorrentGalaxyBridge extends BridgeAbstract { + + const NAME = 'Torrent Galaxy Bridge'; + const URI = 'https://torrentgalaxy.to'; + const DESCRIPTION = 'Returns latest torrents'; + const MAINTAINER = 'GregThib'; + const CACHE_TIMEOUT = 14400; // 24h = 86400s + + const PARAMETERS = array( + array( + 'search' => array( + 'name' => 'search', + 'required' => true, + 'title' => 'Type your query' + ), + 'lang' => array( + 'name' => 'language', + 'type' => 'list', + 'exampleValue' => 'All languages', + 'title' => 'Select your language', + 'values' => array( + 'All languages' => '0', + 'English' => '1', + 'French' => '2', + 'German' => '3', + 'Italian' => '4', + 'Japanese' => '5', + 'Spanish' => '6', + 'Russian' => '7', + 'Hindi' => '8', + 'Other / Multiple' => '9', + 'Korean' => '10', + 'Danish' => '11', + 'Norwegian' => '12', + 'Dutch' => '13', + 'Manderin' => '14', + 'Portuguese' => '15', + 'Bengali' => '16', + 'Polish' => '17', + 'Turkish' => '18', + 'Telugu' => '19', + 'Urdu' => '20', + 'Arabic' => '21', + 'Swedish' => '22', + 'Romanian' => '23' + ) + ) + ) + ); + + public function collectData(){ + $url = self::URI + . '/torrents.php?search=' . urlencode($this->getInput('search')) + . '&lang=' . $this->getInput('lang') + . '&sort=id&order=desc'; + $html = getSimpleHTMLDOM($url) + or returnServerError("Error querying the server at $url"); + + foreach($html->find('div.tgxtablerow') as $result) { + $identity = $result->find('div.tgxtablecell', 3)->find('div a', 0); + $authorid = $result->find('div.tgxtablecell', 6)->find('a', 0); + $creadate = $result->find('div.tgxtablecell', 11)->plaintext; + $glxlinks = $result->find('div.tgxtablecell', 4); + + $item = array(); + $item['uri'] = self::URI . $identity->href; + $item['title'] = $identity->plaintext; + $item['timestamp'] = DateTime::createFromFormat('d/m/y H:i', $creadate)->format('U'); + $item['author'] = $authorid->plaintext; + $item['content'] = <<<HTML +<h1>{$identity->plaintext}</h1> +<h2>Links</h2> +<p><a href="{$glxlinks->find('a', 1)->href}" title="magnet link">magnet</a></p> +<p><a href="{$glxlinks->find('a', 0)->href}" title="torrent link">torrent</a></p> +<h2>Infos</h2> +<p>Size: {$result->find('div.tgxtablecell', 7)->plaintext}</p> +<p>Added by: <a href="{$authorid->href}" title="author profile">{$authorid->plaintext}</a></p> +<p>Upload time: {$creadate}</p> +HTML; + $item['enclosures'] = array($glxlinks->find('a', 0)->href); + $item['categories'] = array($result->find('div.tgxtablecell', 0)->plaintext); + if (preg_match('#/torrent/([^/]+)/#', self::URI . $identity->href, $torrentid)) { + $item['uid'] = $torrentid[1]; + } + $this->items[] = $item; + } + } + + public function getName(){ + if(!is_null($this->getInput('search'))) { + return $this->getInput('search') . ' : ' . self::NAME; + } + return parent::getName(); + } + + public function getURI(){ + if(!is_null($this->getInput('search'))) { + return self::URI + . '/torrents.php?search=' . urlencode($this->getInput('search')) + . '&lang=' . $this->getInput('lang'); + } + return parent::getURI(); + } + + public function getDescription(){ + if(!is_null($this->getInput('search'))) { + return 'Latest torrents for "' . $this->getInput('search') . '"'; + } + return parent::getDescription(); + } + + public function getIcon(){ + if(!is_null($this->getInput('search'))) { + return self::URI . '/common/favicon/favicon.ico'; + } + return parent::getIcon(); + } +} diff --git a/bridges/TwitterBridge.php b/bridges/TwitterBridge.php index 2f5565b..0d8b024 100644 --- a/bridges/TwitterBridge.php +++ b/bridges/TwitterBridge.php @@ -172,11 +172,15 @@ EOD $html = ''; $page = $this->getURI(); + $header = array( + 'User-Agent: Mozilla/5.0 (Windows NT 9.0; WOW64; Trident/7.0; rv:11.0) like Gecko' + ); + if(php_sapi_name() === 'cli' && empty(ini_get('curl.cainfo'))) { $cookies = $this->getCookies($page); - $html = getSimpleHTMLDOM($page, array("Cookie: $cookies")); + $html = getSimpleHTMLDOM($page, array_merge($header, array("Cookie: $cookies"))); } else { - $html = getSimpleHTMLDOM($page, array(), array(CURLOPT_COOKIEFILE => '')); + $html = getSimpleHTMLDOM($page, $header, array(CURLOPT_COOKIEFILE => '')); } if(!$html) { diff --git a/bridges/VkBridge.php b/bridges/VkBridge.php index 713b86f..ea81a2b 100644 --- a/bridges/VkBridge.php +++ b/bridges/VkBridge.php @@ -374,6 +374,8 @@ class VkBridge extends BridgeAbstract } elseif (strstr($strdate, 'yesterday ') !== false) { $time = time() - 60 * 60 * 24; $strdate = date('d-m-Y', $time) . ' ' . $strdate; + } elseif ($date['month'] && intval(date('m')) < $date['month']) { + $strdate = $strdate . ' ' . (date('Y') - 1); } else { $strdate = $strdate . ' ' . date('Y'); } diff --git a/bridges/ZoneTelechargementBridge.php b/bridges/ZoneTelechargementBridge.php index ab7b947..79723fc 100644 --- a/bridges/ZoneTelechargementBridge.php +++ b/bridges/ZoneTelechargementBridge.php @@ -8,7 +8,7 @@ class ZoneTelechargementBridge extends BridgeAbstract { */ const NAME = 'Zone Telechargement'; - const URI = 'https://www.zone-telechargement.net/'; + const URI = 'https://www.zone-annuaire.com/'; const DESCRIPTION = 'Suivi de série sur Zone Telechargement'; const MAINTAINER = 'sysadminstory'; const PARAMETERS = array( @@ -17,7 +17,7 @@ class ZoneTelechargementBridge extends BridgeAbstract { 'name' => 'URL de la série', 'type' => 'text', 'required' => true, - 'title' => 'URL d\'une série sans le https://wwv.zone-telechargement.net/', + 'title' => 'URL d\'une série sans le https://www.zone-annuaire.com/', 'exampleValue' => 'telecharger-series/31079-halt-and-catch-fire-saison-4-french-hd720p.html' ) ) diff --git a/formats/AtomFormat.php b/formats/AtomFormat.php index a1ecfcf..c1bde25 100644 --- a/formats/AtomFormat.php +++ b/formats/AtomFormat.php @@ -89,6 +89,10 @@ class AtomFormat extends FormatAbstract{ . PHP_EOL; } + $entryThumbnail = $item->thumbnail; + if (!empty($entryThumbnail)) + $entryThumbnail = '<media:thumbnail url="' . $this->xml_encode($entryThumbnail) . '"/>'; + $entryLinkAlternate = ''; if (!empty($entryUri)) { $entryLinkAlternate = '<link rel="alternate" type="text/html" href="' @@ -114,6 +118,7 @@ class AtomFormat extends FormatAbstract{ <content type="html">{$entryContent}</content> {$entryEnclosures} {$entryCategories} + {$entryThumbnail} </entry> EOD; @@ -18,7 +18,7 @@ if (isset($argv)) { } define('USER_AGENT', - 'Mozilla/5.0 (X11; Linux x86_64; rv:30.0) Gecko/20121202 Firefox/30.0(rss-bridge/' + 'Mozilla/5.0 (X11; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0(rss-bridge/' . Configuration::$VERSION . ';+' . REPOSITORY diff --git a/lib/Configuration.php b/lib/Configuration.php index 6d52423..76a34af 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -28,7 +28,7 @@ final class Configuration { * * @todo Replace this property by a constant. */ - public static $VERSION = '2019-12-01'; + public static $VERSION = 'dev.2020-02-26'; /** * Holds the configuration data. |