mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-08-16 13:34:11 +02:00
Compare commits
152 Commits
2019-07-06
...
2020-02-26
Author | SHA1 | Date | |
---|---|---|---|
|
0705a2e7bb | ||
|
84616f53bf | ||
|
a981450ae0 | ||
|
d39741c296 | ||
|
3179c1e884 | ||
|
c9e5f6c9dd | ||
|
6b6974d115 | ||
|
96e58d4c94 | ||
|
f0363ba03b | ||
|
90147fc45c | ||
|
a3b4bd2d08 | ||
|
e102353ab8 | ||
|
a54eb88ee1 | ||
|
1584636e5b | ||
|
fe83d763a3 | ||
|
480694e819 | ||
|
8697e1e1a2 | ||
|
1ab7e493a8 | ||
|
e5303efba3 | ||
|
5bd07723ad | ||
|
00dbde2c24 | ||
|
a00e75b71c | ||
|
f040e4dc9c | ||
|
182e9e7b41 | ||
|
275662b8d4 | ||
|
f52eb43f8c | ||
|
2450f80823 | ||
|
45287e6853 | ||
|
830f57f607 | ||
|
6a90a9d33f | ||
|
46b9879c08 | ||
|
1343dbe97a | ||
|
2175a4d08b | ||
|
ad661c4c91 | ||
|
ba8c4623ed | ||
|
ba43c87952 | ||
|
595b87946d | ||
|
99d4e1a43d | ||
|
477de4e2df | ||
|
246470da18 | ||
|
df9f7eb778 | ||
|
375831f516 | ||
|
e518936be7 | ||
|
583dfb4958 | ||
|
2de45b163e | ||
|
48b0164676 | ||
|
4b3c3c58d2 | ||
|
60768b4885 | ||
|
02dd778124 | ||
|
5b63121e92 | ||
|
49019a843f | ||
|
d65714fa47 | ||
|
8161829ad5 | ||
|
7f35fc9f6b | ||
|
3bc8c9468a | ||
|
1df3598a74 | ||
|
5f64fe2516 | ||
|
50eee7e7b3 | ||
|
c0df9815c7 | ||
|
46d5895d1d | ||
|
7c16aaf303 | ||
|
cdc1d9c9ba | ||
|
6bc83310b9 | ||
|
c8d5c85c76 | ||
|
d1e4bd7285 | ||
|
1022b5fdf9 | ||
|
e8536ac1b2 | ||
|
a0afe36d56 | ||
|
0b80f9d61c | ||
|
424075981f | ||
|
c334df91ec | ||
|
f2346fb33e | ||
|
8a21fd1476 | ||
|
2ac44172ac | ||
|
4c78721f03 | ||
|
04be85996d | ||
|
59be6bded2 | ||
|
46873e14fe | ||
|
0f01cc97a4 | ||
|
a70e00a76d | ||
|
f0260c62c3 | ||
|
fc5a1526ca | ||
|
4c0e234479 | ||
|
0eab63d728 | ||
|
b0884e9158 | ||
|
b4581418d4 | ||
|
af1566f40d | ||
|
529e0d0cca | ||
|
a3532804ac | ||
|
8c19146d29 | ||
|
2a3d5865ad | ||
|
4d36c9dc30 | ||
|
a2e47a88c3 | ||
|
b09f50853f | ||
|
9b5bf565b3 | ||
|
5cc956367f | ||
|
548e28249b | ||
|
684c69b0cd | ||
|
3dae4e0801 | ||
|
4622d9be1e | ||
|
76183dcd44 | ||
|
50b234d893 | ||
|
af48f36fd2 | ||
|
7f6ca23e8f | ||
|
1daef22a3d | ||
|
c694810d9a | ||
|
f12f6a2dba | ||
|
b1be45df6c | ||
|
b4f393a5cc | ||
|
29126ebe29 | ||
|
50c971d545 | ||
|
7aba7992aa | ||
|
48ebed7b38 | ||
|
ccef6b95ad | ||
|
dd5da99a30 | ||
|
2ff27b92ff | ||
|
b47189921f | ||
|
f1d3e8c9c9 | ||
|
53fbd2a5a0 | ||
|
3254a4d7bc | ||
|
52d2d21da5 | ||
|
abb74f056c | ||
|
25548b6757 | ||
|
cfe433e9e2 | ||
|
0dfc4ea2c5 | ||
|
38960df180 | ||
|
b440a6fdc6 | ||
|
48d0385653 | ||
|
b68c0e0df8 | ||
|
f27b267614 | ||
|
8bff63d9c6 | ||
|
2b4a030158 | ||
|
6a99904e64 | ||
|
f3c687604f | ||
|
a86a94555d | ||
|
acc0787b00 | ||
|
c8992650a1 | ||
|
f9f511a849 | ||
|
990719d614 | ||
|
b6be18d585 | ||
|
cf525c964a | ||
|
52a4f0860c | ||
|
21b27a1042 | ||
|
2eee535171 | ||
|
da51fc065f | ||
|
e032705c9a | ||
|
be27bc9250 | ||
|
75edc1b2b7 | ||
|
c9ea53806d | ||
|
2bb9480555 | ||
|
eb942bc498 | ||
|
5a0ea423c4 |
15
Dockerfile
15
Dockerfile
@@ -3,9 +3,20 @@ FROM php:7-apache
|
||||
ENV APACHE_DOCUMENT_ROOT=/app
|
||||
|
||||
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
|
||||
&& apt-get --yes update && apt-get --yes install libxml2-dev \
|
||||
&& apt-get --yes update && apt-get --yes install libxml2-dev zlib1g-dev libmemcached-dev \
|
||||
&& docker-php-ext-install -j$(nproc) simplexml \
|
||||
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
|
||||
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
||||
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
|
||||
&& sed -ri -e 's/(MinProtocol\s*=\s*)TLSv1\.2/\1None/' /etc/ssl/openssl.cnf \
|
||||
&& sed -ri -e 's/(CipherString\s*=\s*DEFAULT)@SECLEVEL=2/\1/' /etc/ssl/openssl.cnf
|
||||
|
||||
RUN curl https://codeload.github.com/php-memcached-dev/php-memcached/tar.gz/v3.1.5 --output /tmp/php-memcached.tar.gz \
|
||||
&& mkdir -p /usr/src/php/ext \
|
||||
&& tar xzvf /tmp/php-memcached.tar.gz -C /usr/src/php/ext \
|
||||
&& mv /usr/src/php/ext/php-memcached-3.1.5 /usr/src/php/ext/memcached \
|
||||
&& cd /usr/src/php/ext/memcached \
|
||||
&& docker-php-ext-configure /usr/src/php/ext/memcached --disable-memcached-sasl \
|
||||
&& docker-php-ext-install /usr/src/php/ext/memcached \
|
||||
&& rm -rf /usr/src/php/ext/memcached
|
||||
|
||||
COPY --chown=www-data:www-data ./ /app/
|
30
README.md
30
README.md
@@ -2,7 +2,7 @@
|
||||
===
|
||||
[](UNLICENSE) [](https://github.com/rss-bridge/rss-bridge/releases/latest) [](https://tracker.debian.org/pkg/rss-bridge) [](https://www.gnu.org/software/guix/packages/R/) [](https://travis-ci.org/RSS-Bridge/rss-bridge) [](https://hub.docker.com/r/rssbridge/rss-bridge/)
|
||||
|
||||
RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites which don't have one. It can be used on webservers or as stand alone application in CLI mode.
|
||||
RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites that don't have one. It can be used on webservers or as a stand-alone application in CLI mode.
|
||||
|
||||
**Important**: RSS-Bridge is __not__ a feed reader or feed aggregator, but a tool to generate feeds that are consumed by feed readers and feed aggregators. Find a list of feed aggregators on [Wikipedia](https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators).
|
||||
|
||||
@@ -76,7 +76,7 @@ RSS-Bridge allows you to take full control over which bridges are displayed to t
|
||||
|
||||
Find more information on the [Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitelisting)
|
||||
|
||||
**Notice**: By default RSS-Bridge will only show a small subset of bridges. Make sure to read up on [whitelisting](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitelisting) to unlock the full potential of RSS-Bridge!
|
||||
**Notice**: By default, RSS-Bridge will only show a small subset of bridges. Make sure to read up on [whitelisting](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitelisting) to unlock the full potential of RSS-Bridge!
|
||||
|
||||
Deploy
|
||||
===
|
||||
@@ -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)
|
||||
@@ -118,6 +118,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
|
||||
* [alex73](https://github.com/alex73)
|
||||
* [alexAubin](https://github.com/alexAubin)
|
||||
* [AmauryCarrade](https://github.com/AmauryCarrade)
|
||||
* [AntoineTurmel](https://github.com/AntoineTurmel)
|
||||
* [ArthurHoaro](https://github.com/ArthurHoaro)
|
||||
* [Astalaseven](https://github.com/Astalaseven)
|
||||
* [Astyan-42](https://github.com/Astyan-42)
|
||||
@@ -125,38 +126,48 @@ 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)
|
||||
* [cnlpete](https://github.com/cnlpete)
|
||||
* [corenting](https://github.com/corenting)
|
||||
* [couraudt](https://github.com/couraudt)
|
||||
* [cyberjacob](https://github.com/cyberjacob)
|
||||
* [da2x](https://github.com/da2x)
|
||||
* [Daiyousei](https://github.com/Daiyousei)
|
||||
* [dawidsowa](https://github.com/dawidsowa)
|
||||
* [disk0x](https://github.com/disk0x)
|
||||
* [DJCrashdummy](https://github.com/DJCrashdummy)
|
||||
* [Djuuu](https://github.com/Djuuu)
|
||||
* [DnAp](https://github.com/DnAp)
|
||||
* [dominik-th](https://github.com/dominik-th)
|
||||
* [Draeli](https://github.com/Draeli)
|
||||
* [Dreckiger-Dan](https://github.com/Dreckiger-Dan)
|
||||
* [em92](https://github.com/em92)
|
||||
* [eMerzh](https://github.com/eMerzh)
|
||||
* [EtienneM](https://github.com/EtienneM)
|
||||
* [floviolleau](https://github.com/floviolleau)
|
||||
* [fluffy-critter](https://github.com/fluffy-critter)
|
||||
* [Frenzie](https://github.com/Frenzie)
|
||||
* [fulmeek](https://github.com/fulmeek)
|
||||
* [Ginko-Aloe](https://github.com/Ginko-Aloe)
|
||||
* [Glandos](https://github.com/Glandos)
|
||||
* [gloony](https://github.com/gloony)
|
||||
* [GregThib](https://github.com/GregThib)
|
||||
* [griffaurel](https://github.com/griffaurel)
|
||||
* [Grummfy](https://github.com/Grummfy)
|
||||
* [hunhejj](https://github.com/hunhejj)
|
||||
* [husim0](https://github.com/husim0)
|
||||
* [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)
|
||||
@@ -166,9 +177,11 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
|
||||
* [lagaisse](https://github.com/lagaisse)
|
||||
* [lalannev](https://github.com/lalannev)
|
||||
* [ldidry](https://github.com/ldidry)
|
||||
* [Leomaradan](https://github.com/Leomaradan)
|
||||
* [Limero](https://github.com/Limero)
|
||||
* [LogMANOriginal](https://github.com/LogMANOriginal)
|
||||
* [lorenzos](https://github.com/lorenzos)
|
||||
* [lukasklinger](https://github.com/lukasklinger)
|
||||
* [m0zes](https://github.com/m0zes)
|
||||
* [matthewseal](https://github.com/matthewseal)
|
||||
* [mcbyte-it](https://github.com/mcbyte-it)
|
||||
@@ -177,13 +190,14 @@ 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)
|
||||
* [niawag](https://github.com/niawag)
|
||||
* [Nono-m0le](https://github.com/Nono-m0le)
|
||||
* [ObsidianWitch](https://github.com/ObsidianWitch)
|
||||
* [OliverParoczai](https://github.com/OliverParoczai)
|
||||
* [oratosquilla-oratoria](https://github.com/oratosquilla-oratoria)
|
||||
* [ORelio](https://github.com/ORelio)
|
||||
* [PaulVayssiere](https://github.com/PaulVayssiere)
|
||||
* [pellaeon](https://github.com/pellaeon)
|
||||
@@ -199,15 +213,21 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
|
||||
* [rogerdc](https://github.com/rogerdc)
|
||||
* [Roliga](https://github.com/Roliga)
|
||||
* [sebsauvage](https://github.com/sebsauvage)
|
||||
* [shutosg](https://github.com/shutosg)
|
||||
* [somini](https://github.com/somini)
|
||||
* [squeek502](https://github.com/squeek502)
|
||||
* [stjohnjohnson](https://github.com/stjohnjohnson)
|
||||
* [Strubbl](https://github.com/Strubbl)
|
||||
* [sublimz](https://github.com/sublimz)
|
||||
* [sunchaserinfo](https://github.com/sunchaserinfo)
|
||||
* [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)
|
||||
* [TitiTestScalingo](https://github.com/TitiTestScalingo)
|
||||
* [triatic](https://github.com/triatic)
|
||||
* [VerifiedJoseph](https://github.com/VerifiedJoseph)
|
||||
* [WalterBarrett](https://github.com/WalterBarrett)
|
||||
@@ -215,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
|
||||
===
|
||||
|
||||
|
136
actions/ConnectivityAction.php
Normal file
136
actions/ConnectivityAction.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
|
||||
* Atom feeds for websites that don't have one.
|
||||
*
|
||||
* For the full license information, please view the UNLICENSE file distributed
|
||||
* with this source code.
|
||||
*
|
||||
* @package Core
|
||||
* @license http://unlicense.org/ UNLICENSE
|
||||
* @link https://github.com/rss-bridge/rss-bridge
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if the website for a given bridge is reachable.
|
||||
*
|
||||
* **Remarks**
|
||||
* - This action is only available in debug mode.
|
||||
* - Returns the bridge status as Json-formatted string.
|
||||
* - Returns an error if the bridge is not whitelisted.
|
||||
* - Returns a responsive web page that automatically checks all whitelisted
|
||||
* bridges (using JavaScript) if no bridge is specified.
|
||||
*/
|
||||
class ConnectivityAction extends ActionAbstract {
|
||||
public function execute() {
|
||||
|
||||
if(!Debug::isEnabled()) {
|
||||
returnError('This action is only available in debug mode!');
|
||||
}
|
||||
|
||||
if(!isset($this->userData['bridge'])) {
|
||||
$this->returnEntryPage();
|
||||
return;
|
||||
}
|
||||
|
||||
$bridgeName = $this->userData['bridge'];
|
||||
|
||||
$this->reportBridgeConnectivity($bridgeName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a report about the bridge connectivity status and sends it back
|
||||
* to the user.
|
||||
*
|
||||
* The report is generated as Json-formatted string in the format
|
||||
* {
|
||||
* "bridge": "<bridge-name>",
|
||||
* "successful": true/false
|
||||
* }
|
||||
*
|
||||
* @param string $bridgeName Name of the bridge to generate the report for
|
||||
* @return void
|
||||
*/
|
||||
private function reportBridgeConnectivity($bridgeName) {
|
||||
|
||||
$bridgeFac = new \BridgeFactory();
|
||||
$bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
|
||||
|
||||
if(!$bridgeFac->isWhitelisted($bridgeName)) {
|
||||
header('Content-Type: text/html');
|
||||
returnServerError('Bridge is not whitelisted!');
|
||||
}
|
||||
|
||||
header('Content-Type: text/json');
|
||||
|
||||
$retVal = array(
|
||||
'bridge' => $bridgeName,
|
||||
'successful' => false,
|
||||
'http_code' => 200,
|
||||
);
|
||||
|
||||
$bridge = $bridgeFac->create($bridgeName);
|
||||
|
||||
if($bridge === false) {
|
||||
echo json_encode($retVal);
|
||||
return;
|
||||
}
|
||||
|
||||
$curl_opts = array(
|
||||
CURLOPT_CONNECTTIMEOUT => 5
|
||||
);
|
||||
|
||||
try {
|
||||
$reply = getContents($bridge::URI, array(), $curl_opts, true);
|
||||
|
||||
if($reply) {
|
||||
$retVal['successful'] = true;
|
||||
if (isset($reply['header'])) {
|
||||
if (strpos($reply['header'], 'HTTP/1.1 301 Moved Permanently') !== false) {
|
||||
$retVal['http_code'] = 301;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception $e) {
|
||||
$retVal['successful'] = false;
|
||||
}
|
||||
|
||||
echo json_encode($retVal);
|
||||
|
||||
}
|
||||
|
||||
private function returnEntryPage() {
|
||||
echo <<<EOD
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="static/bootstrap.min.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
|
||||
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="static/connectivity.css">
|
||||
<script src="static/connectivity.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-content" class="container">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
<div id="status-message" class="sticky-top alert alert-primary alert-dismissible fade show" role="alert">
|
||||
<i id="status-icon" class="fas fa-sync"></i>
|
||||
<span>...</span>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close" onclick="stopConnectivityChecks()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="search" onkeyup="search()" placeholder="Search for bridge..">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOD;
|
||||
}
|
||||
}
|
@@ -12,6 +12,15 @@
|
||||
*/
|
||||
|
||||
class DisplayAction extends ActionAbstract {
|
||||
private function get_return_code($error) {
|
||||
$returnCode = $error->getCode();
|
||||
if ($returnCode === 301 || $returnCode === 302) {
|
||||
# Don't pass redirect codes to the exterior
|
||||
$returnCode = 508;
|
||||
}
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$bridge = array_key_exists('bridge', $this->userData) ? $this->userData['bridge'] : null;
|
||||
|
||||
@@ -146,63 +155,77 @@ class DisplayAction extends ActionAbstract {
|
||||
} catch(Error $e) {
|
||||
error_log($e);
|
||||
|
||||
$item = new \FeedItem();
|
||||
if(logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) {
|
||||
if(Configuration::getConfig('error', 'output') === 'feed') {
|
||||
$item = new \FeedItem();
|
||||
|
||||
// Create "new" error message every 24 hours
|
||||
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
||||
// Create "new" error message every 24 hours
|
||||
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
||||
|
||||
// Error 0 is a special case (i.e. "trying to get property of non-object")
|
||||
if($e->getCode() === 0) {
|
||||
$item->setTitle(
|
||||
'Bridge encountered an unexpected situation! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
} else {
|
||||
$item->setTitle(
|
||||
'Bridge returned error '
|
||||
. $e->getCode()
|
||||
. '! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
// Error 0 is a special case (i.e. "trying to get property of non-object")
|
||||
if($e->getCode() === 0) {
|
||||
$item->setTitle(
|
||||
'Bridge encountered an unexpected situation! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
} else {
|
||||
$item->setTitle(
|
||||
'Bridge returned error '
|
||||
. $e->getCode()
|
||||
. '! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
}
|
||||
|
||||
$item->setURI(
|
||||
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
||||
. '?'
|
||||
. http_build_query($this->userData)
|
||||
);
|
||||
|
||||
$item->setTimestamp(time());
|
||||
$item->setContent(buildBridgeException($e, $bridge));
|
||||
|
||||
$items[] = $item;
|
||||
} elseif(Configuration::getConfig('error', 'output') === 'http') {
|
||||
header('Content-Type: text/html', true, $this->get_return_code($e));
|
||||
die(buildTransformException($e, $bridge));
|
||||
}
|
||||
}
|
||||
|
||||
$item->setURI(
|
||||
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
||||
. '?'
|
||||
. http_build_query($this->userData)
|
||||
);
|
||||
|
||||
$item->setTimestamp(time());
|
||||
$item->setContent(buildBridgeException($e, $bridge));
|
||||
|
||||
$items[] = $item;
|
||||
} catch(Exception $e) {
|
||||
error_log($e);
|
||||
|
||||
$item = new \FeedItem();
|
||||
if(logBridgeError($bridge::NAME, $e->getCode()) >= Configuration::getConfig('error', 'report_limit')) {
|
||||
if(Configuration::getConfig('error', 'output') === 'feed') {
|
||||
$item = new \FeedItem();
|
||||
|
||||
// Create "new" error message every 24 hours
|
||||
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
||||
// Create "new" error message every 24 hours
|
||||
$this->userData['_error_time'] = urlencode((int)(time() / 86400));
|
||||
|
||||
$item->setURI(
|
||||
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
||||
. '?'
|
||||
. http_build_query($this->userData)
|
||||
);
|
||||
$item->setURI(
|
||||
(isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) : '')
|
||||
. '?'
|
||||
. http_build_query($this->userData)
|
||||
);
|
||||
|
||||
$item->setTitle(
|
||||
'Bridge returned error '
|
||||
. $e->getCode()
|
||||
. '! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
$item->setTimestamp(time());
|
||||
$item->setContent(buildBridgeException($e, $bridge));
|
||||
$item->setTitle(
|
||||
'Bridge returned error '
|
||||
. $e->getCode()
|
||||
. '! ('
|
||||
. $this->userData['_error_time']
|
||||
. ')'
|
||||
);
|
||||
$item->setTimestamp(time());
|
||||
$item->setContent(buildBridgeException($e, $bridge));
|
||||
|
||||
$items[] = $item;
|
||||
$items[] = $item;
|
||||
} elseif(Configuration::getConfig('error', 'output') === 'http') {
|
||||
header('Content-Type: text/html', true, $this->get_return_code($e));
|
||||
die(buildTransformException($e, $bridge));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store data in cache
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -134,11 +134,11 @@ EOT;
|
||||
// data-asin="B00WTHJ5SU" data-asin-price="14.99" data-asin-shipping="0"
|
||||
// data-asin-currency-code="USD" data-substitute-count="-1" ... />
|
||||
if ($asinData) {
|
||||
return [
|
||||
return array(
|
||||
'price' => $asinData->getAttribute('data-asin-price'),
|
||||
'currency' => $asinData->getAttribute('data-asin-currency-code'),
|
||||
'shipping' => $asinData->getAttribute('data-asin-shipping')
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -150,11 +150,11 @@ EOT;
|
||||
preg_match('/^\s*([A-Z]{3}|£|\$)\s?([\d.,]+)\s*$/', $priceDiv->plaintext, $matches);
|
||||
|
||||
if (count($matches) === 3) {
|
||||
return [
|
||||
return array(
|
||||
'price' => $matches[2],
|
||||
'currency' => $matches[1],
|
||||
'shipping' => '0'
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
149
bridges/AppleAppStoreBridge.php
Normal file
149
bridges/AppleAppStoreBridge.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
class AppleAppStoreBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'captn3m0';
|
||||
const NAME = 'Apple App Store';
|
||||
const URI = 'https://apps.apple.com/';
|
||||
const CACHE_TIMEOUT = 3600; // 1h
|
||||
const DESCRIPTION = 'Returns version updates for a specific application';
|
||||
|
||||
const PARAMETERS = array(array(
|
||||
'id' => array(
|
||||
'name' => 'Application ID',
|
||||
'required' => true,
|
||||
'exampleValue' => '310633997'
|
||||
),
|
||||
'p' => array(
|
||||
'name' => 'Platform',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'iPad' => 'ipad',
|
||||
'iPhone' => 'iphone',
|
||||
'Mac' => 'mac',
|
||||
|
||||
// The following 2 are present in responses
|
||||
// but not yet tested
|
||||
'Web' => 'web',
|
||||
'Apple TV' => 'appletv',
|
||||
),
|
||||
'defaultValue' => 'iphone',
|
||||
),
|
||||
'country' => array(
|
||||
'name' => 'Store Country',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'US' => 'US',
|
||||
'India' => 'IN',
|
||||
'Canada' => 'CA'
|
||||
),
|
||||
'defaultValue' => 'US',
|
||||
),
|
||||
));
|
||||
|
||||
const PLATFORM_MAPPING = array(
|
||||
'iphone' => 'ios',
|
||||
'ipad' => 'ios',
|
||||
);
|
||||
|
||||
private function makeHtmlUrl($id, $country){
|
||||
return 'https://apps.apple.com/' . $country . '/app/id' . $id;
|
||||
}
|
||||
|
||||
private function makeJsonUrl($id, $platform, $country){
|
||||
return "https://amp-api.apps.apple.com/v1/catalog/$country/apps/$id?platform=$platform&extend=versionHistory";
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
if (isset($this->name)) {
|
||||
return $this->name . ' - AppStore Updates';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of some platforms, the data is present in the initial response
|
||||
*/
|
||||
private function getDataFromShoebox($id, $platform, $country){
|
||||
$uri = $this->makeHtmlUrl($id, $country);
|
||||
$html = getSimpleHTMLDOMCached($uri, 3600);
|
||||
$script = $html->find('script[id="shoebox-ember-data-store"]', 0);
|
||||
|
||||
$json = json_decode($script->innertext, true);
|
||||
return $json['data'];
|
||||
}
|
||||
|
||||
private function getJWTToken($id, $platform, $country){
|
||||
$uri = $this->makeHtmlUrl($id, $country);
|
||||
|
||||
$html = getSimpleHTMLDOMCached($uri, 3600);
|
||||
|
||||
$meta = $html->find('meta[name="web-experience-app/config/environment"]', 0);
|
||||
|
||||
$json = urldecode($meta->content);
|
||||
|
||||
$json = json_decode($json);
|
||||
|
||||
return $json->MEDIA_API->token;
|
||||
}
|
||||
|
||||
private function getAppData($id, $platform, $country, $token){
|
||||
$uri = $this->makeJsonUrl($id, $platform, $country);
|
||||
|
||||
$headers = array(
|
||||
"Authorization: Bearer $token",
|
||||
);
|
||||
|
||||
$json = json_decode(getContents($uri, $headers), true);
|
||||
|
||||
return $json['data'][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the version history from the data received
|
||||
* @return array list of versions with details on each element
|
||||
*/
|
||||
private function getVersionHistory($data, $platform){
|
||||
switch($platform) {
|
||||
case 'mac':
|
||||
return $data['relationships']['platforms']['data'][0]['attributes']['versionHistory'];
|
||||
default:
|
||||
$os = self::PLATFORM_MAPPING[$platform];
|
||||
return $data['attributes']['platformAttributes'][$os]['versionHistory'];
|
||||
}
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$id = $this->getInput('id');
|
||||
$country = $this->getInput('country');
|
||||
$platform = $this->getInput('p');
|
||||
|
||||
switch ($platform) {
|
||||
case 'mac':
|
||||
$data = $this->getDataFromShoebox($id, $platform, $country);
|
||||
break;
|
||||
|
||||
default:
|
||||
$token = $this->getJWTToken($id, $platform, $country);
|
||||
$data = $this->getAppData($id, $platform, $country, $token);
|
||||
}
|
||||
|
||||
$versionHistory = $this->getVersionHistory($data, $platform);
|
||||
$name = $this->name = $data['attributes']['name'];
|
||||
$author = $data['attributes']['artistName'];
|
||||
|
||||
foreach ($versionHistory as $row) {
|
||||
$item = array();
|
||||
|
||||
$item['content'] = nl2br($row['releaseNotes']);
|
||||
$item['title'] = $name . ' - ' . $row['versionDisplay'];
|
||||
$item['timestamp'] = $row['releaseDate'];
|
||||
$item['author'] = $author;
|
||||
|
||||
$item['uri'] = $this->makeHtmlUrl($id, $country);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,19 +5,19 @@ class AppleMusicBridge extends BridgeAbstract {
|
||||
const URI = 'https://www.apple.com';
|
||||
const DESCRIPTION = 'Fetches the latest releases from an artist';
|
||||
const MAINTAINER = 'Limero';
|
||||
const PARAMETERS = [[
|
||||
'url' => [
|
||||
const PARAMETERS = array(array(
|
||||
'url' => array(
|
||||
'name' => 'Artist URL',
|
||||
'exampleValue' => 'https://itunes.apple.com/us/artist/dunderpatrullen/329796274',
|
||||
'required' => true,
|
||||
],
|
||||
'imgSize' => [
|
||||
),
|
||||
'imgSize' => array(
|
||||
'name' => 'Image size for thumbnails (in px)',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 512,
|
||||
'required' => true,
|
||||
]
|
||||
]];
|
||||
)
|
||||
));
|
||||
const CACHE_TIMEOUT = 21600; // 6 hours
|
||||
|
||||
public function collectData() {
|
||||
@@ -36,12 +36,12 @@ class AppleMusicBridge extends BridgeAbstract {
|
||||
// Loop through each object
|
||||
foreach ($json->included as $obj) {
|
||||
if ($obj->type === 'lockup/album') {
|
||||
$this->items[] = [
|
||||
$this->items[] = array(
|
||||
'title' => $obj->attributes->artistName . ' - ' . $obj->attributes->name,
|
||||
'uri' => $obj->attributes->url,
|
||||
'timestamp' => $obj->attributes->releaseDate,
|
||||
'enclosures' => $obj->relationships->artwork->data->id,
|
||||
];
|
||||
);
|
||||
} elseif ($obj->type === 'image') {
|
||||
$images[$obj->id] = $obj->attributes->url;
|
||||
}
|
||||
@@ -49,9 +49,9 @@ class AppleMusicBridge extends BridgeAbstract {
|
||||
|
||||
// Add the images to each item
|
||||
foreach ($this->items as &$item) {
|
||||
$item['enclosures'] = [
|
||||
$item['enclosures'] = array(
|
||||
str_replace('{w}x{h}bb.{f}', $imgSize . 'x0w.jpg', $images[$item['enclosures']]),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
// Sort the order to put the latest albums first
|
||||
|
4638
bridges/AtmoNouvelleAquitaineBridge.php
Normal file
4638
bridges/AtmoNouvelleAquitaineBridge.php
Normal file
File diff suppressed because it is too large
Load Diff
58
bridges/AtmoOccitanieBridge.php
Normal file
58
bridges/AtmoOccitanieBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
@@ -15,16 +15,6 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
'title' => 'URL d\'une recherche avec filtre de véhicules sans le http://www.autojm.fr/',
|
||||
'exampleValue' => 'achat-voitures-neuves-peugeot-nouvelle-308-5p'
|
||||
),
|
||||
'isDispo' => array(
|
||||
'name' => 'Disponibilité',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'-' => '',
|
||||
'En stock' => 1,
|
||||
'Sur commande' => 0
|
||||
),
|
||||
'title' => 'Critère de disponibilité'
|
||||
),
|
||||
'energy' => array(
|
||||
'name' => 'Carburant',
|
||||
'type' => 'list',
|
||||
@@ -92,7 +82,6 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
|
||||
// Build the form
|
||||
$post_data = array(
|
||||
'form[isDispo]' => $this->getInput('isDispo'),
|
||||
'form[energy]' => $this->getInput('energy'),
|
||||
'form[transmission]' => $this->getInput('transmission'),
|
||||
'form[priceMin]' => $this->getInput('priceMin'),
|
||||
@@ -121,7 +110,7 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
$html = str_get_html($data->content);
|
||||
|
||||
// Go through every finisha of the model
|
||||
$list = $html->find('h2');
|
||||
$list = $html->find('h3');
|
||||
foreach ($list as $finish) {
|
||||
$finish_name = $finish->plaintext;
|
||||
$motorizations = $finish->next_sibling()->find('li');
|
||||
|
@@ -1,73 +1,262 @@
|
||||
<?php
|
||||
class BandcampBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'sebsauvage';
|
||||
const NAME = 'Bandcamp Tag';
|
||||
const MAINTAINER = 'sebsauvage, Roliga';
|
||||
const NAME = 'Bandcamp Bridge';
|
||||
const URI = 'https://bandcamp.com/';
|
||||
const CACHE_TIMEOUT = 600; // 10min
|
||||
const DESCRIPTION = 'New bandcamp release by tag';
|
||||
const PARAMETERS = array( array(
|
||||
'tag' => array(
|
||||
'name' => 'tag',
|
||||
'type' => 'text',
|
||||
'required' => true
|
||||
const DESCRIPTION = 'New bandcamp releases by tag, band or album';
|
||||
const PARAMETERS = array(
|
||||
'By tag' => array(
|
||||
'tag' => array(
|
||||
'name' => 'tag',
|
||||
'type' => 'text',
|
||||
'required' => true
|
||||
)
|
||||
),
|
||||
'By band' => array(
|
||||
'band' => array(
|
||||
'name' => 'band',
|
||||
'type' => 'text',
|
||||
'title' => 'Band name as seen in the band page URL',
|
||||
'required' => true
|
||||
),
|
||||
'type' => array(
|
||||
'name' => 'Articles are',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Releases' => 'releases',
|
||||
'Releases, new one when track list changes' => 'changes',
|
||||
'Individual tracks' => 'tracks'
|
||||
),
|
||||
'defaultValue' => 'changes'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'limit',
|
||||
'type' => 'number',
|
||||
'title' => 'Number of releases to return',
|
||||
'defaultValue' => 5
|
||||
)
|
||||
),
|
||||
'By album' => array(
|
||||
'band' => array(
|
||||
'name' => 'band',
|
||||
'type' => 'text',
|
||||
'title' => 'Band name as seen in the album page URL',
|
||||
'required' => true
|
||||
),
|
||||
'album' => array(
|
||||
'name' => 'album',
|
||||
'type' => 'text',
|
||||
'title' => 'Album name as seen in the album page URL',
|
||||
'required' => true
|
||||
),
|
||||
'type' => array(
|
||||
'name' => 'Articles are',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Releases' => 'releases',
|
||||
'Releases, new one when track list changes' => 'changes',
|
||||
'Individual tracks' => 'tracks'
|
||||
),
|
||||
'defaultValue' => 'tracks'
|
||||
)
|
||||
)
|
||||
));
|
||||
);
|
||||
const IMGURI = 'https://f4.bcbits.com/';
|
||||
const IMGSIZE_300PX = 23;
|
||||
const IMGSIZE_700PX = 16;
|
||||
|
||||
private $feedName;
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://s4.bcbits.com/img/bc_favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$url = self::URI . 'api/hub/1/dig_deeper';
|
||||
$data = $this->buildRequestJson();
|
||||
$header = array(
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
);
|
||||
$opts = array(
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => $data
|
||||
);
|
||||
$content = getContents($url, $header, $opts)
|
||||
or returnServerError('Could not complete request to: ' . $url);
|
||||
|
||||
$json = json_decode($content);
|
||||
|
||||
if ($json->ok !== true) {
|
||||
returnServerError('Invalid response');
|
||||
}
|
||||
|
||||
foreach ($json->items as $entry) {
|
||||
$url = $entry->tralbum_url;
|
||||
$artist = $entry->artist;
|
||||
$title = $entry->title;
|
||||
// e.g. record label is the releaser, but not the artist
|
||||
$releaser = $entry->band_name !== $entry->artist ? $entry->band_name : null;
|
||||
|
||||
$full_title = $artist . ' - ' . $title;
|
||||
$full_artist = $artist;
|
||||
if (isset($releaser)) {
|
||||
$full_title .= ' (' . $releaser . ')';
|
||||
$full_artist .= ' (' . $releaser . ')';
|
||||
}
|
||||
$small_img = $this->getImageUrl($entry->art_id, self::IMGSIZE_300PX);
|
||||
$img = $this->getImageUrl($entry->art_id, self::IMGSIZE_700PX);
|
||||
|
||||
$item = array(
|
||||
'uri' => $url,
|
||||
'author' => $full_artist,
|
||||
'title' => $full_title
|
||||
switch($this->queriedContext) {
|
||||
case 'By tag':
|
||||
$url = self::URI . 'api/hub/1/dig_deeper';
|
||||
$data = $this->buildRequestJson();
|
||||
$header = array(
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data)
|
||||
);
|
||||
$item['content'] = "<img src='$small_img' /><br/>$full_title";
|
||||
$item['enclosures'] = array($img);
|
||||
$this->items[] = $item;
|
||||
$opts = array(
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => $data
|
||||
);
|
||||
$content = getContents($url, $header, $opts)
|
||||
or returnServerError('Could not complete request to: ' . $url);
|
||||
|
||||
$json = json_decode($content);
|
||||
|
||||
if ($json->ok !== true) {
|
||||
returnServerError('Invalid response');
|
||||
}
|
||||
|
||||
foreach ($json->items as $entry) {
|
||||
$url = $entry->tralbum_url;
|
||||
$artist = $entry->artist;
|
||||
$title = $entry->title;
|
||||
// e.g. record label is the releaser, but not the artist
|
||||
$releaser = $entry->band_name !== $entry->artist ? $entry->band_name : null;
|
||||
|
||||
$full_title = $artist . ' - ' . $title;
|
||||
$full_artist = $artist;
|
||||
if (isset($releaser)) {
|
||||
$full_title .= ' (' . $releaser . ')';
|
||||
$full_artist .= ' (' . $releaser . ')';
|
||||
}
|
||||
$small_img = $this->getImageUrl($entry->art_id, self::IMGSIZE_300PX);
|
||||
$img = $this->getImageUrl($entry->art_id, self::IMGSIZE_700PX);
|
||||
|
||||
$item = array(
|
||||
'uri' => $url,
|
||||
'author' => $full_artist,
|
||||
'title' => $full_title
|
||||
);
|
||||
$item['content'] = "<img src='$small_img' /><br/>$full_title";
|
||||
$item['enclosures'] = array($img);
|
||||
$this->items[] = $item;
|
||||
}
|
||||
break;
|
||||
case 'By band':
|
||||
case 'By album':
|
||||
$html = getSimpleHTMLDOMCached($this->getURI(), 86400);
|
||||
|
||||
$titleElement = $html->find('head meta[name=title]', 0)
|
||||
or returnServerError('Unable to find title on: ' . $this->getURI());
|
||||
$this->feedName = $titleElement->content;
|
||||
|
||||
$regex = '/band_id=(\d+)/';
|
||||
if(preg_match($regex, $html, $matches) == false)
|
||||
returnServerError('Unable to find band ID on: ' . $this->getURI());
|
||||
$band_id = $matches[1];
|
||||
|
||||
$tralbums = array();
|
||||
switch($this->queriedContext) {
|
||||
case 'By band':
|
||||
$query_data = array(
|
||||
'band_id' => $band_id
|
||||
);
|
||||
$band_data = $this->apiGet('mobile/22/band_details', $query_data);
|
||||
|
||||
$num_albums = min(count($band_data->discography), $this->getInput('limit'));
|
||||
for($i = 0; $i < $num_albums; $i++) {
|
||||
$album_basic_data = $band_data->discography[$i];
|
||||
|
||||
// 'a' or 't' for albums and individual tracks respectively
|
||||
$tralbum_type = substr($album_basic_data->item_type, 0, 1);
|
||||
|
||||
$query_data = array(
|
||||
'band_id' => $band_id,
|
||||
'tralbum_type' => $tralbum_type,
|
||||
'tralbum_id' => $album_basic_data->item_id
|
||||
);
|
||||
$tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data);
|
||||
}
|
||||
break;
|
||||
case 'By album':
|
||||
$regex = '/album=(\d+)/';
|
||||
if(preg_match($regex, $html, $matches) == false)
|
||||
returnServerError('Unable to find album ID on: ' . $this->getURI());
|
||||
$album_id = $matches[1];
|
||||
|
||||
$query_data = array(
|
||||
'band_id' => $band_id,
|
||||
'tralbum_type' => 'a',
|
||||
'tralbum_id' => $album_id
|
||||
);
|
||||
$tralbums[] = $this->apiGet('mobile/22/tralbum_details', $query_data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($tralbums as $tralbum_data) {
|
||||
if ($tralbum_data->type === 'a' && $this->getInput('type') === 'tracks') {
|
||||
foreach ($tralbum_data->tracks as $track) {
|
||||
$query_data = array(
|
||||
'band_id' => $band_id,
|
||||
'tralbum_type' => 't',
|
||||
'tralbum_id' => $track->track_id
|
||||
);
|
||||
$track_data = $this->apiGet('mobile/22/tralbum_details', $query_data);
|
||||
|
||||
$this->items[] = $this->buildTralbumItem($track_data);
|
||||
}
|
||||
} else {
|
||||
$this->items[] = $this->buildTralbumItem($tralbum_data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function buildTralbumItem($tralbum_data){
|
||||
$band_data = $tralbum_data->band;
|
||||
|
||||
// Format title like: ARTIST - ALBUM/TRACK (OPTIONAL RELEASER)
|
||||
// Format artist/author like: ARTIST (OPTIONAL RELEASER)
|
||||
//
|
||||
// If the album/track is released under a label/a band other than the artist
|
||||
// themselves, append that releaser name to the title and artist/author.
|
||||
//
|
||||
// This sadly doesn't always work right for individual tracks as the artist
|
||||
// of the track is always set to the releaser.
|
||||
$artist = $tralbum_data->tralbum_artist;
|
||||
$full_title = $artist . ' - ' . $tralbum_data->title;
|
||||
$full_artist = $artist;
|
||||
if (isset($tralbum_data->label)) {
|
||||
$full_title .= ' (' . $tralbum_data->label . ')';
|
||||
$full_artist .= ' (' . $tralbum_data->label . ')';
|
||||
} elseif ($band_data->name !== $artist) {
|
||||
$full_title .= ' (' . $band_data->name . ')';
|
||||
$full_artist .= ' (' . $band_data->name . ')';
|
||||
}
|
||||
|
||||
$small_img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_300PX);
|
||||
$img = $this->getImageUrl($tralbum_data->art_id, self::IMGSIZE_700PX);
|
||||
|
||||
$item = array(
|
||||
'uri' => $tralbum_data->bandcamp_url,
|
||||
'author' => $full_artist,
|
||||
'title' => $full_title,
|
||||
'enclosures' => array($img),
|
||||
'timestamp' => $tralbum_data->release_date
|
||||
);
|
||||
|
||||
$item['categories'] = array();
|
||||
foreach ($tralbum_data->tags as $tag) {
|
||||
$item['categories'][] = $tag->norm_name;
|
||||
}
|
||||
|
||||
// Give articles a unique UID depending on its track list
|
||||
// Releases should then show up as new articles when tracks are added
|
||||
if ($this->getInput('type') === 'changes') {
|
||||
$item['uid'] = "bandcamp/$band_data->band_id/$tralbum_data->id/";
|
||||
foreach ($tralbum_data->tracks as $track) {
|
||||
$item['uid'] .= $track->track_id;
|
||||
}
|
||||
}
|
||||
|
||||
$item['content'] = "<img src='$small_img' /><br/>$full_title<br/>";
|
||||
if ($tralbum_data->type === 'a') {
|
||||
$item['content'] .= '<ol>';
|
||||
foreach ($tralbum_data->tracks as $track) {
|
||||
$item['content'] .= "<li>$track->title</li>";
|
||||
}
|
||||
$item['content'] .= '</ol>';
|
||||
}
|
||||
if (!empty($tralbum_data->about)) {
|
||||
$item['content'] .= '<p>'
|
||||
. nl2br($tralbum_data->about)
|
||||
. '</p>';
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
private function buildRequestJson(){
|
||||
$requestJson = array(
|
||||
'tag' => $this->getInput('tag'),
|
||||
@@ -81,11 +270,94 @@ class BandcampBridge extends BridgeAbstract {
|
||||
return self::IMGURI . 'img/a' . $id . '_' . $size . '.jpg';
|
||||
}
|
||||
|
||||
private function apiGet($endpoint, $query_data) {
|
||||
$url = self::URI . 'api/' . $endpoint . '?' . http_build_query($query_data);
|
||||
$data = json_decode(getContents($url))
|
||||
or returnServerError('API request to "' . $url . '" failed.');
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
switch($this->queriedContext) {
|
||||
case 'By tag':
|
||||
if(!is_null($this->getInput('tag'))) {
|
||||
return self::URI
|
||||
. 'tag/'
|
||||
. urlencode($this->getInput('tag'))
|
||||
. '?sort_field=date';
|
||||
}
|
||||
break;
|
||||
case 'By band':
|
||||
if(!is_null($this->getInput('band'))) {
|
||||
return 'https://'
|
||||
. $this->getInput('band')
|
||||
. '.bandcamp.com/music';
|
||||
}
|
||||
break;
|
||||
case 'By album':
|
||||
if(!is_null($this->getInput('band')) && !is_null($this->getInput('album'))) {
|
||||
return 'https://'
|
||||
. $this->getInput('band')
|
||||
. '.bandcamp.com/album/'
|
||||
. $this->getInput('album');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('tag'))) {
|
||||
return $this->getInput('tag') . ' - Bandcamp Tag';
|
||||
switch($this->queriedContext) {
|
||||
case 'By tag':
|
||||
if(!is_null($this->getInput('tag'))) {
|
||||
return $this->getInput('tag') . ' - Bandcamp Tag';
|
||||
}
|
||||
break;
|
||||
case 'By band':
|
||||
if(isset($this->feedName)) {
|
||||
return $this->feedName . ' - Bandcamp Band';
|
||||
} elseif(!is_null($this->getInput('band'))) {
|
||||
return $this->getInput('band') . ' - Bandcamp Band';
|
||||
}
|
||||
break;
|
||||
case 'By album':
|
||||
if(isset($this->feedName)) {
|
||||
return $this->feedName . ' - Bandcamp Album';
|
||||
} elseif(!is_null($this->getInput('album'))) {
|
||||
return $this->getInput('album') . ' - Bandcamp Album';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function detectParameters($url) {
|
||||
$params = array();
|
||||
|
||||
// By tag
|
||||
$regex = '/^(https?:\/\/)?bandcamp\.com\/tag\/([^\/.&?\n]+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['tag'] = urldecode($matches[2]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// By band
|
||||
$regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['band'] = urldecode($matches[2]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// By album
|
||||
$regex = '/^(https?:\/\/)?([^\/.&?\n]+?)\.bandcamp\.com\/album\/([^\/.&?\n]+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['band'] = urldecode($matches[2]);
|
||||
$params['album'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -3,17 +3,11 @@ class BastaBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'qwertygc';
|
||||
const NAME = 'Bastamag Bridge';
|
||||
const URI = 'http://www.bastamag.net/';
|
||||
const URI = 'https://www.bastamag.net/';
|
||||
const CACHE_TIMEOUT = 7200; // 2h
|
||||
const DESCRIPTION = 'Returns the newest articles.';
|
||||
|
||||
public function collectData(){
|
||||
// Replaces all relative image URLs by absolute URLs.
|
||||
// Relative URLs always start with 'local/'!
|
||||
function replaceImageUrl($content){
|
||||
return preg_replace('/src=["\']{1}([^"\']+)/ims', 'src=\'' . self::URI . '$1\'', $content);
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend')
|
||||
or returnServerError('Could not request Bastamag.');
|
||||
|
||||
@@ -25,7 +19,13 @@ class BastaBridge extends BridgeAbstract {
|
||||
$item['title'] = $element->find('title', 0)->innertext;
|
||||
$item['uri'] = $element->find('guid', 0)->plaintext;
|
||||
$item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext);
|
||||
$item['content'] = replaceImageUrl(getSimpleHTMLDOM($item['uri'])->find('div.texte', 0)->innertext);
|
||||
// Replaces all relative image URLs by absolute URLs.
|
||||
// Relative URLs always start with 'local/'!
|
||||
$item['content'] = preg_replace(
|
||||
'/src=["\']{1}([^"\']+)/ims',
|
||||
'src=\'' . self::URI . '$1\'',
|
||||
getSimpleHTMLDOM($item['uri'])->find('div.texte', 0)->innertext
|
||||
);
|
||||
$this->items[] = $item;
|
||||
$limit++;
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ class BingSearchBridge extends BridgeAbstract
|
||||
or returnServerError('Could not request ' . self::NAME);
|
||||
$sizeKey = $this->getInput('image_size');
|
||||
|
||||
$items = [];
|
||||
$items = array();
|
||||
foreach ($html->find('a.iusc') as $element) {
|
||||
$data = json_decode(htmlspecialchars_decode($element->getAttribute('m')), true);
|
||||
|
||||
|
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
class BloombergBridge extends BridgeAbstract
|
||||
{
|
||||
const NAME = 'Bloomberg';
|
||||
const URI = 'https://www.bloomberg.com/';
|
||||
const DESCRIPTION = 'Trending stories from Bloomberg';
|
||||
const MAINTAINER = 'mdemoss';
|
||||
|
||||
const PARAMETERS = array(
|
||||
'Trending Stories' => array(),
|
||||
'From Search' => array(
|
||||
'q' => array(
|
||||
'name' => 'Keyword',
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function getName()
|
||||
{
|
||||
switch($this->queriedContext) {
|
||||
case 'Trending Stories':
|
||||
return self::NAME . ' Trending Stories';
|
||||
case 'From Search':
|
||||
if (!is_null($this->getInput('q'))) {
|
||||
return self::NAME . ' Search : ' . $this->getInput('q');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://assets.bwbx.io/s3/javelin/public/hub/images/favicon-black-63fe5249d3.png';
|
||||
}
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
switch($this->queriedContext) {
|
||||
case 'Trending Stories': // Get list of top new <article>s from the front page.
|
||||
$html = getSimpleHTMLDOMCached($this->getURI(), 300);
|
||||
$stories = $html->find('ul.top-news-v3__stories article.top-news-v3-story');
|
||||
break;
|
||||
case 'From Search': // Get list of <article> elements from search.
|
||||
$html = getSimpleHTMLDOMCached(
|
||||
$this->getURI() .
|
||||
'search?sort=time:desc&page=1&query=' .
|
||||
urlencode($this->getInput('q')), 300
|
||||
);
|
||||
$stories = $html->find('div.search-result-items article.search-result-story');
|
||||
break;
|
||||
}
|
||||
foreach ($stories as $element) {
|
||||
$item['uri'] = $element->find('h1 a', 0)->href;
|
||||
if (preg_match('#^https://#i', $item['uri']) !== 1) {
|
||||
$item['uri'] = $this->getURI() . $item['uri'];
|
||||
}
|
||||
$articleHtml = getSimpleHTMLDOMCached($item['uri']);
|
||||
if (!$articleHtml) {
|
||||
continue;
|
||||
}
|
||||
$item['title'] = $element->find('h1 a', 0)->plaintext;
|
||||
$item['timestamp'] = strtotime($articleHtml->find('meta[name=iso-8601-publish-date],meta[name=date]', 0)->content);
|
||||
$item['content'] = $articleHtml->find('meta[name=description]', 0)->content;
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
63
bridges/CNETFranceBridge.php
Normal file
63
bridges/CNETFranceBridge.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
class CNETFranceBridge extends FeedExpander
|
||||
{
|
||||
const MAINTAINER = 'leomaradan';
|
||||
const NAME = 'CNET France';
|
||||
const URI = 'https://www.cnetfrance.fr/';
|
||||
const CACHE_TIMEOUT = 3600; // 1h
|
||||
const DESCRIPTION = 'CNET France RSS with filters';
|
||||
const PARAMETERS = array(
|
||||
'filters' => array(
|
||||
'title' => array(
|
||||
'name' => 'Exclude by title',
|
||||
'required' => false,
|
||||
'title' => 'Title term, separated by semicolon (;)',
|
||||
'defaultValue' => 'bon plan;bons plans;au meilleur prix;des meilleures offres;Amazon Prime Day;RED by SFR ou B&You'
|
||||
),
|
||||
'url' => array(
|
||||
'name' => 'Exclude by url',
|
||||
'required' => false,
|
||||
'title' => 'URL term, separated by semicolon (;)',
|
||||
'defaultValue' => 'bon-plan;bons-plans'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
private $bannedTitle = array();
|
||||
private $bannedURL = array();
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$title = $this->getInput('title');
|
||||
$url = $this->getInput('url');
|
||||
|
||||
if ($title !== null) {
|
||||
$this->bannedTitle = explode(';', $title);
|
||||
}
|
||||
|
||||
if ($url !== null) {
|
||||
$this->bannedURL = explode(';', $url);
|
||||
}
|
||||
|
||||
$this->collectExpandableDatas('https://www.cnetfrance.fr/feeds/rss/news/');
|
||||
}
|
||||
|
||||
protected function parseItem($feedItem)
|
||||
{
|
||||
$item = parent::parseItem($feedItem);
|
||||
|
||||
foreach ($this->bannedTitle as $term) {
|
||||
if (preg_match('/' . $term . '/mi', $item['title']) === 1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->bannedURL as $term) {
|
||||
if (preg_match('/' . $term . '/mi', $item['uri']) === 1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ class CachetBridge extends BridgeAbstract {
|
||||
);
|
||||
const CACHE_TIMEOUT = 300;
|
||||
|
||||
private $componentCache = [];
|
||||
private $componentCache = array();
|
||||
|
||||
public function getURI() {
|
||||
return $this->getInput('host') === null ? 'https://cachethq.io/' : $this->getInput('host');
|
||||
@@ -114,13 +114,13 @@ class CachetBridge extends BridgeAbstract {
|
||||
$uidOrig = $permalink . $incident->created_at;
|
||||
$uid = hash('sha512', $uidOrig);
|
||||
$timestamp = strtotime($incident->created_at);
|
||||
$categories = [];
|
||||
$categories = array();
|
||||
$categories[] = $incident->human_status;
|
||||
if ($componentName !== '') {
|
||||
$categories[] = $componentName;
|
||||
}
|
||||
|
||||
$item = [];
|
||||
$item = array();
|
||||
$item['uri'] = $permalink;
|
||||
$item['title'] = $title;
|
||||
$item['timestamp'] = $timestamp;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
class CastorusBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'logmanoriginal';
|
||||
const NAME = 'Castorus Bridge';
|
||||
const URI = 'http://www.castorus.com';
|
||||
const URI = 'https://www.castorus.com';
|
||||
const CACHE_TIMEOUT = 600; // 10min
|
||||
const DESCRIPTION = 'Returns the latest changes';
|
||||
|
||||
|
@@ -3,7 +3,7 @@ class CollegeDeFranceBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'pit-fgfjiudghdf';
|
||||
const NAME = 'CollegeDeFrance';
|
||||
const URI = 'http://www.college-de-france.fr/';
|
||||
const URI = 'https://www.college-de-france.fr/';
|
||||
const CACHE_TIMEOUT = 10800; // 3h
|
||||
const DESCRIPTION = 'Returns the latest audio and video from CollegeDeFrance';
|
||||
|
||||
|
65
bridges/ComicsKingdomBridge.php
Normal file
65
bridges/ComicsKingdomBridge.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
class ComicsKingdomBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'stjohnjohnson';
|
||||
const NAME = 'Comics Kingdom Unofficial RSS';
|
||||
const URI = 'https://www.comicskingdom.com/';
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Comics Kingdom Unofficial RSS';
|
||||
const PARAMETERS = array( array(
|
||||
'comicname' => array(
|
||||
'name' => 'comicname',
|
||||
'type' => 'text',
|
||||
'required' => true
|
||||
)
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false)
|
||||
or returnServerError('Could not request Comics Kingdom: ' . $this->getURI());
|
||||
|
||||
// Get author from first page
|
||||
$author = $html->find('div.author p', 0)->plaintext
|
||||
or returnServerError('Comics Kingdom comic does not exist: ' . $this->getURI());;
|
||||
|
||||
// Get current date/link
|
||||
$link = $html->find('meta[property=og:url]', 0)->content;
|
||||
for($i = 0; $i < 5; $i++) {
|
||||
$item = array();
|
||||
|
||||
$page = getSimpleHTMLDOM($link)
|
||||
or returnServerError('Could not request Comics Kingdom: ' . $link);
|
||||
|
||||
$imagelink = $page->find('meta[property=og:image]', 0)->content;
|
||||
$prevSlug = $page->find('slider-arrow[:is-left-arrow=true]', 0);
|
||||
$link = $this->getURI() . '/' . $prevSlug->getAttribute('date-slug');
|
||||
|
||||
$date = explode('/', $link);
|
||||
|
||||
$item['id'] = $imagelink;
|
||||
$item['uri'] = $link;
|
||||
$item['author'] = $author;
|
||||
$item['title'] = 'Comics Kingdom ' . $this->getInput('comicname');
|
||||
$item['timestamp'] = DateTime::createFromFormat('Y-m-d', $date[count($date) - 1])->getTimestamp();
|
||||
$item['content'] = '<img src="' . $imagelink . '" />';
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('comicname'))) {
|
||||
return self::URI . urlencode($this->getInput('comicname'));
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('comicname'))) {
|
||||
return $this->getInput('comicname') . ' - Comics Kingdom';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
}
|
@@ -10,20 +10,20 @@ class ContainerLinuxReleasesBridge extends BridgeAbstract {
|
||||
const BETA = 'beta';
|
||||
const ALPHA = 'alpha';
|
||||
|
||||
const PARAMETERS = [
|
||||
[
|
||||
'channel' => [
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'channel' => array(
|
||||
'name' => 'Release Channel',
|
||||
'type' => 'list',
|
||||
'defaultValue' => self::STABLE,
|
||||
'values' => [
|
||||
'values' => array(
|
||||
'Stable' => self::STABLE,
|
||||
'Beta' => self::BETA,
|
||||
'Alpha' => self::ALPHA,
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
private function getReleaseFeed($jsonUrl) {
|
||||
$json = getContents($jsonUrl)
|
||||
@@ -39,7 +39,7 @@ class ContainerLinuxReleasesBridge extends BridgeAbstract {
|
||||
$data = $this->getReleaseFeed($this->getJsonUri());
|
||||
|
||||
foreach ($data as $releaseVersion => $release) {
|
||||
$item = [];
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = "https://coreos.com/releases/#$releaseVersion";
|
||||
$item['title'] = $releaseVersion;
|
||||
|
109
bridges/CuriousCatBridge.php
Normal file
109
bridges/CuriousCatBridge.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
class CuriousCatBridge extends BridgeAbstract {
|
||||
const NAME = 'Curious Cat Bridge';
|
||||
const URI = 'https://curiouscat.me';
|
||||
const DESCRIPTION = 'Returns list of newest questions and answers for a user profile';
|
||||
const MAINTAINER = 'VerifiedJoseph';
|
||||
const PARAMETERS = array(array(
|
||||
'username' => array(
|
||||
'name' => 'Username',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'koethekoethe',
|
||||
)
|
||||
));
|
||||
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
|
||||
public function collectData() {
|
||||
|
||||
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username'));
|
||||
|
||||
$apiJson = getContents($url)
|
||||
or returnServerError('Could not request: ' . $url);
|
||||
|
||||
$apiData = json_decode($apiJson, true);
|
||||
|
||||
foreach($apiData['posts'] as $post) {
|
||||
$item = array();
|
||||
|
||||
$item['author'] = 'Anonymous';
|
||||
|
||||
if ($post['senderData']['id'] !== false) {
|
||||
$item['author'] = $post['senderData']['username'];
|
||||
}
|
||||
|
||||
$item['uri'] = $this->getURI() . '/post/' . $post['id'];
|
||||
$item['title'] = $this->ellipsisTitle($post['comment']);
|
||||
|
||||
$item['content'] = $this->processContent($post);
|
||||
$item['timestamp'] = $post['timestamp'];
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
|
||||
if (!is_null($this->getInput('username'))) {
|
||||
return self::URI . '/' . $this->getInput('username');
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
|
||||
if (!is_null($this->getInput('username'))) {
|
||||
return $this->getInput('username') . ' - Curious Cat';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
private function processContent($post) {
|
||||
|
||||
$author = 'Anonymous';
|
||||
|
||||
if ($post['senderData']['id'] !== false) {
|
||||
$authorUrl = self::URI . '/' . $post['senderData']['username'];
|
||||
|
||||
$author = <<<EOD
|
||||
<a href="{$authorUrl}">{$post['senderData']['username']}</a>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$question = $this->formatUrls($post['comment']);
|
||||
$answer = $this->formatUrls($post['reply']);
|
||||
|
||||
$content = <<<EOD
|
||||
<p>{$author} asked:</p>
|
||||
<blockquote>{$question}</blockquote><br/>
|
||||
<p>{$post['addresseeData']['username']} answered:</p>
|
||||
<blockquote>{$answer}</blockquote>
|
||||
EOD;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function ellipsisTitle($text) {
|
||||
$length = 150;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ class DailymotionBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'mitsukarenai';
|
||||
const NAME = 'Dailymotion Bridge';
|
||||
const URI = 'https://www.dailymotion.com/';
|
||||
const CACHE_TIMEOUT = 10800; // 3h
|
||||
const CACHE_TIMEOUT = 3600; // 1h
|
||||
const DESCRIPTION = 'Returns the 5 newest videos by username/playlist or search';
|
||||
|
||||
const PARAMETERS = array (
|
||||
@@ -27,74 +27,99 @@ class DailymotionBridge extends BridgeAbstract {
|
||||
),
|
||||
'pa' => array(
|
||||
'name' => 'Page',
|
||||
'type' => 'number'
|
||||
'type' => 'number',
|
||||
'defaultValue' => 1,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
protected function getMetadata($id){
|
||||
$metadata = array();
|
||||
$html2 = getSimpleHTMLDOM(self::URI . 'video/' . $id);
|
||||
if(!$html2) {
|
||||
return $metadata;
|
||||
}
|
||||
private $feedName = '';
|
||||
|
||||
$metadata['title'] = $html2->find('meta[property=og:title]', 0)->getAttribute('content');
|
||||
$metadata['timestamp'] = strtotime(
|
||||
$html2->find('meta[property=video:release_date]', 0)->getAttribute('content')
|
||||
);
|
||||
$metadata['thumbnailUri'] = $html2->find('meta[property=og:image]', 0)->getAttribute('content');
|
||||
$metadata['uri'] = $html2->find('meta[property=og:url]', 0)->getAttribute('content');
|
||||
return $metadata;
|
||||
}
|
||||
private $apiUrl = 'https://api.dailymotion.com';
|
||||
private $apiFields = 'created_time,description,id,owner.screenname,tags,thumbnail_url,title,url';
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://static1-ssl.dmcdn.net/images/neon/favicons/android-icon-36x36.png.vf806ca4ed0deed812';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
$limit = 5;
|
||||
$count = 0;
|
||||
public function collectData() {
|
||||
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Could not request Dailymotion.');
|
||||
if ($this->queriedContext === 'By username' || $this->queriedContext === 'By playlist id') {
|
||||
|
||||
foreach($html->find('div.media a.preview_link') as $element) {
|
||||
if($count < $limit) {
|
||||
$apiJson = getContents($this->getApiUrl())
|
||||
or returnServerError('Could not request: ' . $this->getApiUrl());
|
||||
|
||||
$apiData = json_decode($apiJson, true);
|
||||
|
||||
$this->feedName = $this->getPlaylistTitle($this->getInput('p'));
|
||||
|
||||
foreach ($apiData['list'] as $apiItem) {
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $apiItem['url'];
|
||||
$item['uid'] = $apiItem['id'];
|
||||
$item['title'] = $apiItem['title'];
|
||||
$item['timestamp'] = $apiItem['created_time'];
|
||||
$item['author'] = $apiItem['owner.screenname'];
|
||||
$item['content'] = '<p><a href="' . $apiItem['url'] . '">
|
||||
<img src="' . $apiItem['thumbnail_url'] . '"></a></p><p>' . $apiItem['description'] . '</p>';
|
||||
$item['categories'] = $apiItem['tags'];
|
||||
$item['enclosures'][] = $apiItem['thumbnail_url'];
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->queriedContext === 'From search results') {
|
||||
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Could not request Dailymotion.');
|
||||
|
||||
foreach($html->find('div.media a.preview_link') as $element) {
|
||||
$item = array();
|
||||
|
||||
$item['id'] = str_replace('/video/', '', strtok($element->href, '_'));
|
||||
$metadata = $this->getMetadata($item['id']);
|
||||
|
||||
if(empty($metadata)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item['uri'] = $metadata['uri'];
|
||||
$item['title'] = $metadata['title'];
|
||||
$item['timestamp'] = $metadata['timestamp'];
|
||||
|
||||
$item['content'] = '<a href="'
|
||||
. $item['uri']
|
||||
. '"><img src="'
|
||||
. $metadata['thumbnailUri']
|
||||
. '" /></a><br><a href="'
|
||||
. $item['uri']
|
||||
. '">'
|
||||
. $item['title']
|
||||
. '</a>';
|
||||
. $item['uri']
|
||||
. '"><img src="'
|
||||
. $metadata['thumbnailUri']
|
||||
. '" /></a><br><a href="'
|
||||
. $item['uri']
|
||||
. '">'
|
||||
. $item['title']
|
||||
. '</a>';
|
||||
|
||||
$this->items[] = $item;
|
||||
$count++;
|
||||
|
||||
if (count($this->items) >= 5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
public function getName() {
|
||||
switch($this->queriedContext) {
|
||||
case 'By username':
|
||||
$specific = $this->getInput('u');
|
||||
break;
|
||||
case 'By playlist id':
|
||||
$specific = strtok($this->getInput('p'), '_');
|
||||
|
||||
if ($this->feedName) {
|
||||
$specific = $this->feedName;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'From search results':
|
||||
$specific = $this->getInput('s');
|
||||
@@ -102,26 +127,77 @@ class DailymotionBridge extends BridgeAbstract {
|
||||
default: return parent::getName();
|
||||
}
|
||||
|
||||
return $specific . ' : Dailymotion Bridge';
|
||||
return $specific . ' : Dailymotion';
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
$uri = self::URI;
|
||||
switch($this->queriedContext) {
|
||||
case 'By username':
|
||||
$uri .= 'user/' . urlencode($this->getInput('u')) . '/1';
|
||||
$uri .= 'user/' . urlencode($this->getInput('u'));
|
||||
break;
|
||||
case 'By playlist id':
|
||||
$uri .= 'playlist/' . urlencode(strtok($this->getInput('p'), '_'));
|
||||
break;
|
||||
case 'From search results':
|
||||
$uri .= 'search/' . urlencode($this->getInput('s'));
|
||||
if($this->getInput('pa')) {
|
||||
$uri .= '/' . $this->getInput('pa');
|
||||
|
||||
if(!is_null($this->getInput('pa'))) {
|
||||
$pa = $this->getInput('pa');
|
||||
|
||||
if ($this->getInput('pa') < 1) {
|
||||
$pa = 1;
|
||||
}
|
||||
|
||||
$uri .= '/' . $pa;
|
||||
}
|
||||
break;
|
||||
default: return parent::getURI();
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private function getMetadata($id) {
|
||||
$metadata = array();
|
||||
|
||||
$html = getSimpleHTMLDOM(self::URI . 'video/' . $id);
|
||||
|
||||
if(!$html) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
$metadata['title'] = $html->find('meta[property=og:title]', 0)->getAttribute('content');
|
||||
$metadata['timestamp'] = strtotime(
|
||||
$html->find('meta[property=video:release_date]', 0)->getAttribute('content')
|
||||
);
|
||||
$metadata['thumbnailUri'] = $html->find('meta[property=og:image]', 0)->getAttribute('content');
|
||||
$metadata['uri'] = $html->find('meta[property=og:url]', 0)->getAttribute('content');
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
private function getPlaylistTitle($id) {
|
||||
$title = '';
|
||||
|
||||
$url = self::URI . 'playlist/' . $id;
|
||||
|
||||
$html = getSimpleHTMLDOM($url)
|
||||
or returnServerError('Could not request: ' . $url);
|
||||
|
||||
$title = $html->find('meta[property=og:title]', 0)->getAttribute('content');
|
||||
return $title;
|
||||
}
|
||||
|
||||
private function getApiUrl() {
|
||||
|
||||
switch($this->queriedContext) {
|
||||
case 'By username':
|
||||
return $this->apiUrl . '/user/' . $this->getInput('u')
|
||||
. '/videos?fields=' . urlencode($this->apiFields) . '&availability=1&sort=recent&limit=5';
|
||||
break;
|
||||
case 'By playlist id':
|
||||
return $this->apiUrl . '/playlist/' . $this->getInput('p')
|
||||
. '/videos?fields=' . urlencode($this->apiFields) . '&limit=5';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ class DanbooruBridge extends BridgeAbstract {
|
||||
defaultLinkTo($element, $this->getURI());
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = $element->find('a', 0)->href;
|
||||
$item['uri'] = html_entity_decode($element->find('a', 0)->href);
|
||||
$item['postid'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE));
|
||||
$item['timestamp'] = time();
|
||||
$thumbnailUri = $element->find('img', 0)->src;
|
||||
|
79
bridges/DarkReadingBridge.php
Normal file
79
bridges/DarkReadingBridge.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
class DarkReadingBridge extends FeedExpander {
|
||||
const MAINTAINER = 'ORelio';
|
||||
const NAME = 'Dark Reading Bridge';
|
||||
const URI = 'https://www.darkreading.com/';
|
||||
const DESCRIPTION = 'Returns the newest articles from Dark Reading';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
'feed' => array(
|
||||
'name' => 'Feed',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'All Dark Reading Stories' => '000_AllArticles',
|
||||
'Attacks/Breaches' => '644_Attacks/Breaches',
|
||||
'Application Security' => '645_Application%20Security',
|
||||
'Database Security' => '646_Database%20Security',
|
||||
'Cloud' => '647_Cloud',
|
||||
'Endpoint' => '648_Endpoint',
|
||||
'Authentication' => '649_Authentication',
|
||||
'Privacy' => '650_Privacy',
|
||||
'Mobile' => '651_Mobile',
|
||||
'Perimeter' => '652_Perimeter',
|
||||
'Risk' => '653_Risk',
|
||||
'Compliance' => '654_Compliance',
|
||||
'Operations' => '655_Operations',
|
||||
'Careers and People' => '656_Careers%20and%20People',
|
||||
'Identity and Access Management' => '657_Identity%20and%20Access%20Management',
|
||||
'Analytics' => '658_Analytics',
|
||||
'Threat Intelligence' => '659_Threat%20Intelligence',
|
||||
'Security Monitoring' => '660_Security%20Monitoring',
|
||||
'Vulnerabilities / Threats' => '661_Vulnerabilities%20/%20Threats',
|
||||
'Advanced Threats' => '662_Advanced%20Threats',
|
||||
'Insider Threats' => '663_Insider%20Threats',
|
||||
'Vulnerability Management' => '664_Vulnerability%20Management',
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
$feed = $this->getInput('feed');
|
||||
$feed_splitted = explode('_', $feed);
|
||||
$feed_id = $feed_splitted[0];
|
||||
$feed_name = $feed_splitted[1];
|
||||
if(empty($feed) || !ctype_digit($feed_id) || !preg_match('/[A-Za-z%20\/]/', $feed_name)) {
|
||||
returnClientError('Invalid feed, please check the "feed" parameter.');
|
||||
}
|
||||
$feed_url = $this->getURI() . 'rss_simple.asp';
|
||||
if ($feed_id != '000') {
|
||||
$feed_url .= '?f_n=' . $feed_id . '&f_ln=' . $feed_name;
|
||||
}
|
||||
$this->collectExpandableDatas($feed_url);
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
$article = getSimpleHTMLDOMCached($item['uri'])
|
||||
or returnServerError('Could not request Dark Reading: ' . $item['uri']);
|
||||
$item['content'] = $this->extractArticleContent($article);
|
||||
$item['enclosures'] = array(); //remove author profile picture
|
||||
return $item;
|
||||
}
|
||||
|
||||
private function extractArticleContent($article){
|
||||
$content = $article->find('div#article-main', 0)->innertext;
|
||||
|
||||
foreach (array(
|
||||
'<div class="divsplitter',
|
||||
'<div style="float: left; margin-right: 2px;',
|
||||
'<div class="more-insights',
|
||||
'<div id="more-insights',
|
||||
) as $div_start) {
|
||||
$content = stripRecursiveHTMLSection($content, 'div', $div_start);
|
||||
}
|
||||
|
||||
$content = stripWithDelimiters($content, '<h1 ', '</h1>');
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
24
bridges/DaveRamseyBlogBridge.php
Normal file
24
bridges/DaveRamseyBlogBridge.php
Normal file
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
27
bridges/DavesTrailerPageBridge.php
Normal file
27
bridges/DavesTrailerPageBridge.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
class DavesTrailerPageBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'johnnygroovy';
|
||||
const NAME = 'Daves Trailer Page Bridge';
|
||||
const URI = 'https://www.davestrailerpage.co.uk/';
|
||||
const DESCRIPTION = 'Last trailers in HD thanks to Dave.';
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(static::URI)
|
||||
or returnClientError('No results for this query.');
|
||||
|
||||
foreach ($html->find('tr[!align]') as $tr) {
|
||||
$item = array();
|
||||
|
||||
// title
|
||||
$item['title'] = $tr->find('td', 0)->find('b', 0)->plaintext;
|
||||
|
||||
// content
|
||||
$item['content'] = $tr->find('ul', 1);
|
||||
|
||||
// uri
|
||||
$item['uri'] = $tr->find('a', 3)->getAttribute('href');
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1145,7 +1145,7 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
} else {
|
||||
foreach ($list as $deal) {
|
||||
$item = array();
|
||||
$item['uri'] = $deal->find('div[class=threadGrid-title]', 0)->find('a', 0)->href;
|
||||
$item['uri'] = $deal->find('div[class*=threadGrid-title]', 0)->find('a', 0)->href;
|
||||
$item['title'] = $deal->find('a[class*=' . $selectorLink . ']', 0
|
||||
)->plaintext;
|
||||
$item['author'] = $deal->find('span.thread-username', 0)->plaintext;
|
||||
|
@@ -116,6 +116,12 @@ class DesoutterBridge extends BridgeAbstract {
|
||||
'name' => 'Load full articles',
|
||||
'type' => 'checkbox',
|
||||
'title' => 'Enable to load the full article for each item'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 3,
|
||||
'title' => "Maximum number of items to return in the feed.\n0 = unlimited"
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -156,6 +162,8 @@ class DesoutterBridge extends BridgeAbstract {
|
||||
|
||||
$this->title = html_entity_decode($html->find('title', 0)->plaintext, ENT_QUOTES);
|
||||
|
||||
$limit = $this->getInput('limit') ?: 0;
|
||||
|
||||
foreach($html->find('article') as $article) {
|
||||
$item = array();
|
||||
|
||||
@@ -169,6 +177,8 @@ class DesoutterBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
if ($limit > 0 && count($this->items) >= $limit) break;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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 . '"!');
|
||||
|
60
bridges/DiarioDoAlentejoBridge.php
Normal file
60
bridges/DiarioDoAlentejoBridge.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
class DiarioDoAlentejoBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'somini';
|
||||
const NAME = 'Diário do Alentejo';
|
||||
const URI = 'https://www.diariodoalentejo.pt';
|
||||
const DESCRIPTION = 'Semanário Regionalista Independente';
|
||||
const CACHE_TIMEOUT = 28800; // 8h
|
||||
|
||||
/* This is used to hack around obtaining a timestamp. It's just a list of Month names in Portuguese ... */
|
||||
const PT_MONTH_NAMES = array(
|
||||
'janeiro',
|
||||
'fevereiro',
|
||||
'março',
|
||||
'abril',
|
||||
'maio',
|
||||
'junho',
|
||||
'julho',
|
||||
'agosto',
|
||||
'setembro',
|
||||
'outubro',
|
||||
'novembro',
|
||||
'dezembro');
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://www.diariodoalentejo.pt/images/favicon/apple-touch-icon.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
/* This is slow as molasses (>30s!), keep the cache timeout high to avoid killing the host */
|
||||
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx')
|
||||
or returnServerError('Could not load content');
|
||||
|
||||
foreach($html->find('.list_news .item') as $element) {
|
||||
$item = array();
|
||||
|
||||
$item_link = $element->find('.body h2.title a', 0);
|
||||
/* Another broken URL, see also `bridges/ComboiosDePortugalBridge.php` */
|
||||
$item['uri'] = self::URI . implode('/', array_map('urlencode', explode('/', $item_link->href)));
|
||||
$item['title'] = $item_link->innertext;
|
||||
|
||||
$item['timestamp'] = str_ireplace(
|
||||
array_map(function($name) { return ' ' . $name . ' '; }, self::PT_MONTH_NAMES),
|
||||
array_map(function($num) { return sprintf('-%02d-', $num); }, range(1, sizeof(self::PT_MONTH_NAMES))),
|
||||
$element->find('span.date', 0)->innertext);
|
||||
|
||||
/* Fix the Image URL */
|
||||
$item_image = $element->find('img.thumb', 0);
|
||||
$item_image->src = preg_replace('/.*&img=([^&]+).*/', '\1', $item_image->getAttribute('data-src'));
|
||||
|
||||
/* Content: */
|
||||
/* - Image */
|
||||
/* - Category */
|
||||
$content = $item_image .
|
||||
'<center>' . $element->find('a.category', 0) . '</center>';
|
||||
$item['content'] = defaultLinkTo($content, self::URI);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
123
bridges/DonnonsBridge.php
Normal file
123
bridges/DonnonsBridge.php
Normal file
@@ -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();
|
||||
}
|
||||
}
|
6195
bridges/DownDetectorBridge.php
Normal file
6195
bridges/DownDetectorBridge.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
|
||||
$json = $this->loadEmbeddedJsonData($html);
|
||||
|
||||
foreach($html->find('li[id^="screenshot-"]') as $shot) {
|
||||
$item = [];
|
||||
$item = array();
|
||||
|
||||
$additional_data = $this->findJsonForShot($shot, $json);
|
||||
if ($additional_data === null) {
|
||||
@@ -38,14 +38,14 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
|
||||
|
||||
$preview_path = $shot->find('picture source', 0)->attr['srcset'];
|
||||
$item['content'] .= $this->getImageTag($preview_path, $item['title']);
|
||||
$item['enclosures'] = [$this->getFullSizeImagePath($preview_path)];
|
||||
$item['enclosures'] = array($this->getFullSizeImagePath($preview_path));
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function loadEmbeddedJsonData($html){
|
||||
$json = [];
|
||||
$json = array();
|
||||
$scripts = $html->find('script');
|
||||
|
||||
foreach($scripts as $script) {
|
||||
|
@@ -40,7 +40,7 @@ class EconomistBridge extends BridgeAbstract {
|
||||
if ($nextprev)
|
||||
$nextprev->outertext = '';
|
||||
|
||||
$section = [ $article->find('h3[itemprop="articleSection"]', 0)->plaintext ];
|
||||
$section = array( $article->find('h3[itemprop="articleSection"]', 0)->plaintext );
|
||||
|
||||
$item = array();
|
||||
$item['title'] = $header->find('span', 0)->innertext . ': '
|
||||
|
@@ -95,7 +95,7 @@ class ElloBridge extends BridgeAbstract {
|
||||
|
||||
private function getEnclosures($post, $postData) {
|
||||
|
||||
$assets = [];
|
||||
$assets = array();
|
||||
foreach($post->links->assets as $asset) {
|
||||
foreach($postData->linked->assets as $assetLink) {
|
||||
if($asset == $assetLink->id) {
|
||||
@@ -124,7 +124,7 @@ class ElloBridge extends BridgeAbstract {
|
||||
$cacheFac->setWorkingDir(PATH_LIB_CACHES);
|
||||
$cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
|
||||
$cache->setScope(get_called_class());
|
||||
$cache->setKey(['key']);
|
||||
$cache->setKey(array('key'));
|
||||
$key = $cache->loadData();
|
||||
|
||||
if($key == null) {
|
||||
|
@@ -3,7 +3,7 @@ class ElsevierBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'Pierre Mazière';
|
||||
const NAME = 'Elsevier journals recent articles';
|
||||
const URI = 'http://www.journals.elsevier.com/';
|
||||
const URI = 'https://www.journals.elsevier.com/';
|
||||
const CACHE_TIMEOUT = 43200; //12h
|
||||
const DESCRIPTION = 'Returns the recent articles published in Elsevier journals';
|
||||
|
||||
|
26
bridges/EngadgetBridge.php
Normal file
26
bridges/EngadgetBridge.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
class EngadgetBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'IceWreck';
|
||||
const NAME = 'Engadget Bridge';
|
||||
const URI = 'https://www.engadget.com/';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
const DESCRIPTION = 'Article content for Engadget.';
|
||||
|
||||
public function collectData(){
|
||||
$this->collectExpandableDatas(static::URI . 'rss.xml', 15);
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
// $articlePage gets the entire page's contents
|
||||
$articlePage = getSimpleHTMLDOM($newsItem->link);
|
||||
// figure contain's the main article image
|
||||
$article = $articlePage->find('figure', 0);
|
||||
// .article-text has the actual article
|
||||
foreach($articlePage->find('.article-text') as $element)
|
||||
$article = $article . $element;
|
||||
$item['content'] = $article;
|
||||
return $item;
|
||||
}
|
||||
}
|
70
bridges/EsquerdaNetBridge.php
Normal file
70
bridges/EsquerdaNetBridge.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
class EsquerdaNetBridge extends FeedExpander {
|
||||
const MAINTAINER = 'somini';
|
||||
const NAME = 'Esquerda.net';
|
||||
const URI = 'https://www.esquerda.net';
|
||||
const DESCRIPTION = 'Esquerda.net';
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'feed' => array(
|
||||
'name' => 'Feed',
|
||||
'type' => 'list',
|
||||
'defaultValue' => 'Geral',
|
||||
'values' => array(
|
||||
'Geral' => 'geral',
|
||||
'Dossier' => 'artigos-dossier',
|
||||
'Vídeo' => 'video',
|
||||
'Opinião' => 'opinioes',
|
||||
'Rádio' => 'radio',
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function getURI() {
|
||||
$type = $this->getInput('feed');
|
||||
return self::URI . '/rss/' . $type;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://www.esquerda.net/sites/default/files/favicon_0.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
parent::collectExpandableDatas($this->getURI());
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
# Fix Publish date
|
||||
$badDate = $newsItem->pubDate;
|
||||
preg_match('|(?P<day>\d\d)/(?P<month>\d\d)/(?P<year>\d\d\d\d) - (?P<hour>\d\d):(?P<minute>\d\d)|', $badDate, $d);
|
||||
$newsItem->pubDate = sprintf('%s-%s-%sT%s:%s', $d['year'], $d['month'], $d['day'], $d['hour'], $d['minute']);
|
||||
$item = parent::parseItem($newsItem);
|
||||
# Include all the content
|
||||
$uri = $item['uri'];
|
||||
$html = getSimpleHTMLDOMCached($uri)
|
||||
or returnServerError('Could not load content for ' . $uri);
|
||||
$content = $html->find('div#content div.content', 0);
|
||||
## Fix author
|
||||
$authorHTML = $html->find('.field-name-field-op-author a', 0);
|
||||
if ($authorHTML) {
|
||||
$item['author'] = $authorHTML->innertext;
|
||||
$authorHTML->remove();
|
||||
}
|
||||
## Remove crap
|
||||
$content->find('.field-name-addtoany', 0)->remove();
|
||||
## Fix links
|
||||
$content = defaultLinkTo($content, self::URI);
|
||||
## Fix Images
|
||||
foreach($content->find('img') as $img) {
|
||||
$altSrc = $img->getAttribute('data-src');
|
||||
if ($altSrc) {
|
||||
$img->setAttribute('src', $altSrc);
|
||||
}
|
||||
$img->width = null;
|
||||
$img->height = null;
|
||||
}
|
||||
$item['content'] = $content;
|
||||
return $item;
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class ExtremeDownloadBridge extends BridgeAbstract {
|
||||
const NAME = 'Extreme Download';
|
||||
const URI = 'https://ww1.extreme-d0wn.com/';
|
||||
const URI = 'https://www.extreme-down.ninja/';
|
||||
const DESCRIPTION = 'Suivi de série sur Extreme Download';
|
||||
const MAINTAINER = 'sysadminstory';
|
||||
const PARAMETERS = array(
|
||||
|
@@ -2,7 +2,7 @@
|
||||
class FB2Bridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'teromene';
|
||||
const NAME = 'Facebook Alternate';
|
||||
const NAME = 'Facebook Bridge | Touch Site';
|
||||
const URI = 'https://www.facebook.com/';
|
||||
const CACHE_TIMEOUT = 1000;
|
||||
const DESCRIPTION = 'Input a page title or a profile log. For a profile log,
|
||||
@@ -12,7 +12,12 @@ class FB2Bridge extends BridgeAbstract {
|
||||
'u' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true
|
||||
)
|
||||
),
|
||||
'abbrev_name' => array(
|
||||
'name' => 'Abbreviate author name in title',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => true,
|
||||
),
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
@@ -102,12 +107,12 @@ EOD
|
||||
else
|
||||
$timestamp = 0;
|
||||
|
||||
$item['uri'] = html_entity_decode('http://touch.facebook.com'
|
||||
$item['uri'] = html_entity_decode('https://touch.facebook.com'
|
||||
. $content->find("div[class='_52jc _5qc4 _78cz _24u0 _36xo']", 0)->find('a', 0)->getAttribute('href'), ENT_QUOTES);
|
||||
|
||||
//Decode images
|
||||
$imagecleaned = preg_replace_callback('/<i [^>]* style="[^"]*url\(\'(.*?)\'\).*?><\/i>/m', function ($matches) {
|
||||
return "<img src='" . str_replace(['\\3a ', '\\3d ', '\\26 '], [':', '=', '&'], $matches[1]) . "' />";
|
||||
return "<img src='" . str_replace(array('\\3a ', '\\3d ', '\\26 '), array(':', '=', '&'), $matches[1]) . "' />";
|
||||
}, $content);
|
||||
$content = str_get_html($imagecleaned);
|
||||
|
||||
@@ -159,7 +164,11 @@ EOD
|
||||
$content = preg_replace('/<img src=\'.*?safe_image\.php.*?\' \/>/m', '', $content);
|
||||
|
||||
//Remove the double section tags
|
||||
$content = str_replace(['<section><section>', '</section></section>'], ['<section>', '</section>'], $content);
|
||||
$content = str_replace(
|
||||
array('<section><section>', '</section></section>'),
|
||||
array('<section>', '</section>'),
|
||||
$content
|
||||
);
|
||||
|
||||
//Move the section tag link upper, if it is down
|
||||
$content = str_get_html($content);
|
||||
@@ -182,8 +191,10 @@ EOD
|
||||
$item['content'] = html_entity_decode($content, ENT_QUOTES);
|
||||
|
||||
$title = $author;
|
||||
if (strlen($title) > 24)
|
||||
$title = substr($title, 0, strpos(wordwrap($title, 24), "\n")) . '...';
|
||||
if ($this->getInput('abbrev_name') === true) {
|
||||
if (strlen($title) > 24)
|
||||
$title = substr($title, 0, strpos(wordwrap($title, 24), "\n")) . '...';
|
||||
}
|
||||
$title = $title . ' | ' . strip_tags($content);
|
||||
if (strlen($title) > 64)
|
||||
$title = substr($title, 0, strpos(wordwrap($title, 64), "\n")) . '...';
|
||||
@@ -281,10 +292,20 @@ EOD
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return (isset($this->name) ? $this->name . ' - ' : '') . 'Facebook Bridge';
|
||||
$username = $this->getInput('u');
|
||||
if (isset($username)) {
|
||||
return $this->getInput('u') . ' | Facebook';
|
||||
} else {
|
||||
return self::NAME;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
return 'http://facebook.com';
|
||||
$username = $this->getInput('u');
|
||||
if (isset($username)) {
|
||||
return 'https://facebook.com/' . $this->getInput('u') . '/posts';
|
||||
} else {
|
||||
return self::URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
36
bridges/FabriceBellardBridge.php
Normal file
36
bridges/FabriceBellardBridge.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
class FabriceBellardBridge extends BridgeAbstract {
|
||||
const NAME = 'Fabrice Bellard';
|
||||
const URI = 'https://bellard.org/';
|
||||
const DESCRIPTION = "Fabrice Bellard's Home Page";
|
||||
const MAINTAINER = 'somini';
|
||||
|
||||
public function collectData() {
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
or returnServerError('Could not load content');
|
||||
|
||||
foreach ($html->find('p') as $obj) {
|
||||
$item = array();
|
||||
|
||||
$html = defaultLinkTo($html, $this->getURI());
|
||||
|
||||
$links = $obj->find('a');
|
||||
if (count($links) > 0) {
|
||||
$link_uri = $links[0]->href;
|
||||
} else {
|
||||
$link_uri = $this->getURI();
|
||||
}
|
||||
|
||||
/* try to make sure the link is valid */
|
||||
if ($link_uri[-1] !== '/' && strpos($link_uri, '/') === false) {
|
||||
$link_uri = $link_uri . '/';
|
||||
}
|
||||
|
||||
$item['title'] = strip_tags($obj->innertext);
|
||||
$item['uri'] = $link_uri;
|
||||
$item['content'] = $obj->innertext;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
class FacebookBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'teromene, logmanoriginal';
|
||||
const NAME = 'Facebook Bridge';
|
||||
const NAME = 'Facebook Bridge | Main Site';
|
||||
const URI = 'https://www.facebook.com/';
|
||||
const CACHE_TIMEOUT = 300; // 5min
|
||||
const DESCRIPTION = 'Input a page title or a profile log. For a profile log,
|
||||
@@ -66,14 +66,13 @@ class FacebookBridge extends BridgeAbstract {
|
||||
|
||||
case 'User':
|
||||
if(!empty($this->authorName)) {
|
||||
return isset($this->extraInfos['name']) ? $this->extraInfos['name'] : $this->authorName
|
||||
. ' - ' . static::NAME;
|
||||
return isset($this->extraInfos['name']) ? $this->extraInfos['name'] : $this->authorName;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Group':
|
||||
if(!empty($this->groupName)) {
|
||||
return $this->groupName . ' - ' . static::NAME;
|
||||
return $this->groupName;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -82,6 +81,34 @@ class FacebookBridge extends BridgeAbstract {
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function detectParameters($url){
|
||||
$params = array();
|
||||
|
||||
// By profile
|
||||
$regex = '/^(https?:\/\/)?(www\.)?facebook\.com\/profile\.php\?id\=([^\/?&\n]+)?(.*)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['u'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// By group
|
||||
$regex = '/^(https?:\/\/)?(www\.)?facebook\.com\/groups\/([^\/?\n]+)?(.*)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['g'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// By username
|
||||
$regex = '/^(https?:\/\/)?(www\.)?facebook\.com\/([^\/?\n]+)/';
|
||||
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['u'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$uri = self::URI;
|
||||
|
||||
@@ -142,7 +169,11 @@ class FacebookBridge extends BridgeAbstract {
|
||||
|
||||
private function collectGroupData() {
|
||||
|
||||
$header = array('Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE') . "\r\n");
|
||||
if(getEnv('HTTP_ACCEPT_LANGUAGE')) {
|
||||
$header = array('Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE'));
|
||||
} else {
|
||||
$header = array();
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOM($this->getURI(), $header)
|
||||
or returnServerError('Failed loading facebook page: ' . $this->getURI());
|
||||
@@ -505,7 +536,11 @@ EOD;
|
||||
// Retrieve page contents
|
||||
if(is_null($html)) {
|
||||
|
||||
$header = array('Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE'));
|
||||
if(getEnv('HTTP_ACCEPT_LANGUAGE')) {
|
||||
$header = array('Accept-Language: ' . getEnv('HTTP_ACCEPT_LANGUAGE'));
|
||||
} else {
|
||||
$header = array();
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOM($this->getURI(), $header)
|
||||
or returnServerError('No results for this query.');
|
||||
@@ -580,6 +615,8 @@ EOD;
|
||||
'._5mly', // Remove embedded videos (the preview image remains)
|
||||
'._2ezg', // Remove "Views ..."
|
||||
'.hidden_elem', // Remove hidden elements (they are hidden anyway)
|
||||
'.timestampContent', // Remove relative timestamp
|
||||
'._6spk', // Remove redundant separator
|
||||
);
|
||||
|
||||
foreach($content_filters as $filter) {
|
||||
@@ -664,8 +701,15 @@ EOD;
|
||||
|
||||
$uri = $post->find('abbr')[0]->parent()->getAttribute('href');
|
||||
|
||||
if (false !== strpos($uri, '?')) {
|
||||
$uri = substr($uri, 0, strpos($uri, '?'));
|
||||
// Extract fbid and patch link
|
||||
if (strpos($uri, '?') !== false) {
|
||||
$query = substr($uri, strpos($uri, '?') + 1);
|
||||
parse_str($query, $query_params);
|
||||
if (isset($query_params['story_fbid'])) {
|
||||
$uri = self::URI . $query_params['story_fbid'];
|
||||
} else {
|
||||
$uri = substr($uri, 0, strpos($uri, '?'));
|
||||
}
|
||||
}
|
||||
|
||||
//Build and add final item
|
||||
@@ -685,6 +729,7 @@ EOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion (User)
|
||||
|
||||
}
|
||||
|
50
bridges/FirstLookMediaTechBridge.php
Normal file
50
bridges/FirstLookMediaTechBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
51
bridges/FolhaDeSaoPauloBridge.php
Normal file
51
bridges/FolhaDeSaoPauloBridge.php
Normal file
@@ -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);
|
||||
}
|
||||
}
|
27
bridges/FreeCodeCampBridge.php
Normal file
27
bridges/FreeCodeCampBridge.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
class FreeCodeCampBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'IceWreck';
|
||||
const NAME = 'FreeCodecamp Bridge';
|
||||
const URI = 'https://www.freecodecamp.org';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
const DESCRIPTION = 'RSS feed for FreeCodeCamp';
|
||||
// Freecodecamp removed their old full content rss feed and replaced it with one liner content.
|
||||
|
||||
public function collectData(){
|
||||
$this->collectExpandableDatas('https://www.freecodecamp.org/news/rss/', 15);
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
// $articlePage gets the entire page's contents
|
||||
$articlePage = getSimpleHTMLDOM($newsItem->link);
|
||||
// figure contain's the main article image
|
||||
$article = $articlePage->find('figure', 0);
|
||||
// the actual article
|
||||
foreach($articlePage->find('.post-full-content') as $element)
|
||||
$article = $article . $element;
|
||||
$item['content'] = $article;
|
||||
return $item;
|
||||
}
|
||||
}
|
918
bridges/FurAffinityBridge.php
Normal file
918
bridges/FurAffinityBridge.php
Normal file
@@ -0,0 +1,918 @@
|
||||
<?php
|
||||
class FurAffinityBridge extends BridgeAbstract {
|
||||
const NAME = 'FurAffinity Bridge';
|
||||
const URI = 'https://www.furaffinity.net';
|
||||
const CACHE_TIMEOUT = 300; // 5min
|
||||
const DESCRIPTION = 'Returns posts from various sections of FurAffinity';
|
||||
const MAINTAINER = 'Roliga';
|
||||
const PARAMETERS = array(
|
||||
'Search' => array(
|
||||
'q' => array(
|
||||
'name' => 'Query',
|
||||
'required' => true
|
||||
),
|
||||
'rating-general' => array(
|
||||
'name' => 'General',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'rating-mature' => array(
|
||||
'name' => 'Mature',
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'rating-adult' => array(
|
||||
'name' => 'Adult',
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'range' => array(
|
||||
'name' => 'Time range',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'A Day' => 'day',
|
||||
'3 Days' => '3days',
|
||||
'A Week' => 'week',
|
||||
'A Month' => 'month',
|
||||
'All time' => 'all'
|
||||
),
|
||||
'defaultValue' => 'all'
|
||||
),
|
||||
'type-art' => array(
|
||||
'name' => 'Art',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'type-flash' => array(
|
||||
'name' => 'Flash',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'type-photo' => array(
|
||||
'name' => 'Photography',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'type-music' => array(
|
||||
'name' => 'Music',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'type-story' => array(
|
||||
'name' => 'Story',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'type-poetry' => array(
|
||||
'name' => 'Poetry',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'mode' => array(
|
||||
'name' => 'Match mode',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'All of the words' => 'all',
|
||||
'Any of the words' => 'any',
|
||||
'Extended' => 'extended'
|
||||
),
|
||||
'defaultValue' => 'extended'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
),
|
||||
'Browse' => array(
|
||||
'cat' => array(
|
||||
'name' => 'Category',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Visual Art' => array(
|
||||
'All' => 1,
|
||||
'Artwork (Digital)' => 2,
|
||||
'Artwork (Traditional)' => 3,
|
||||
'Cellshading' => 4,
|
||||
'Crafting' => 5,
|
||||
'Designs' => 6,
|
||||
'Flash' => 7,
|
||||
'Fursuiting' => 8,
|
||||
'Icons' => 9,
|
||||
'Mosaics' => 10,
|
||||
'Photography' => 11,
|
||||
'Sculpting' => 12
|
||||
),
|
||||
'Readable Art' => array(
|
||||
'Story' => 13,
|
||||
'Poetry' => 14,
|
||||
'Prose' => 15
|
||||
),
|
||||
'Audio Art' => array(
|
||||
'Music' => 16,
|
||||
'Podcasts' => 17
|
||||
),
|
||||
'Downloadable' => array(
|
||||
'Skins' => 18,
|
||||
'Handhelds' => 19,
|
||||
'Resources' => 20
|
||||
),
|
||||
'Other Stuff' => array(
|
||||
'Adoptables' => 21,
|
||||
'Auctions' => 22,
|
||||
'Contests' => 23,
|
||||
'Current Events' => 24,
|
||||
'Desktops' => 25,
|
||||
'Stockart' => 26,
|
||||
'Screenshots' => 27,
|
||||
'Scraps' => 28,
|
||||
'Wallpaper' => 29,
|
||||
'YCH / Sale' => 30,
|
||||
'Other' => 31
|
||||
)
|
||||
),
|
||||
'defaultValue' => 1
|
||||
),
|
||||
'atype' => array(
|
||||
'name' => 'Type',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'General Things' => array(
|
||||
'All' => 1,
|
||||
'Abstract' => 2,
|
||||
'Animal related (non-anthro)' => 3,
|
||||
'Anime' => 4,
|
||||
'Comics' => 5,
|
||||
'Doodle' => 6,
|
||||
'Fanart' => 7,
|
||||
'Fantasy' => 8,
|
||||
'Human' => 9,
|
||||
'Portraits' => 10,
|
||||
'Scenery' => 11,
|
||||
'Still Life' => 12,
|
||||
'Tutorials' => 13,
|
||||
'Miscellaneous' => 14
|
||||
),
|
||||
'Fetish / Furry specialty' => array(
|
||||
'Baby fur' => 101,
|
||||
'Bondage' => 102,
|
||||
'Digimon' => 103,
|
||||
'Fat Furs' => 104,
|
||||
'Fetish Other' => 105,
|
||||
'Fursuit' => 106,
|
||||
'Gore / Macabre Art' => 119,
|
||||
'Hyper' => 107,
|
||||
'Inflation' => 108,
|
||||
'Macro / Micro' => 109,
|
||||
'Muscle' => 110,
|
||||
'My Little Pony / Brony' => 111,
|
||||
'Paw' => 112,
|
||||
'Pokemon' => 113,
|
||||
'Pregnancy' => 114,
|
||||
'Sonic' => 115,
|
||||
'Transformation' => 116,
|
||||
'Vore' => 117,
|
||||
'Water Sports' => 118,
|
||||
'General Furry Art' => 100
|
||||
),
|
||||
'Music' => array(
|
||||
'Techno' => 201,
|
||||
'Trance' => 202,
|
||||
'House' => 203,
|
||||
'90s' => 204,
|
||||
'80s' => 205,
|
||||
'70s' => 206,
|
||||
'60s' => 207,
|
||||
'Pre-60s' => 208,
|
||||
'Classical' => 209,
|
||||
'Game Music' => 210,
|
||||
'Rock' => 211,
|
||||
'Pop' => 212,
|
||||
'Rap' => 213,
|
||||
'Industrial' => 214,
|
||||
'Other Music' => 200
|
||||
)
|
||||
),
|
||||
'defaultValue' => 1
|
||||
),
|
||||
'species' => array(
|
||||
'name' => 'Species',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Unspecified / Any' => 1,
|
||||
'Amphibian' => array(
|
||||
'Frog' => 1001,
|
||||
'Newt' => 1002,
|
||||
'Salamander' => 1003,
|
||||
'Amphibian (Other)' => 1000
|
||||
),
|
||||
'Aquatic' => array(
|
||||
'Cephalopod' => 2001,
|
||||
'Dolphin' => 2002,
|
||||
'Fish' => 2005,
|
||||
'Porpoise' => 2004,
|
||||
'Seal' => 6068,
|
||||
'Shark' => 2006,
|
||||
'Whale' => 2003,
|
||||
'Aquatic (Other)' => 2000
|
||||
),
|
||||
'Avian' => array(
|
||||
'Corvid' => 3001,
|
||||
'Crow' => 3002,
|
||||
'Duck' => 3003,
|
||||
'Eagle' => 3004,
|
||||
'Falcon' => 3005,
|
||||
'Goose' => 3006,
|
||||
'Gryphon' => 3007,
|
||||
'Hawk' => 3008,
|
||||
'Owl' => 3009,
|
||||
'Phoenix' => 3010,
|
||||
'Swan' => 3011,
|
||||
'Avian (Other)' => 3000
|
||||
),
|
||||
'Bears & Ursines' => array(
|
||||
'Bear' => 6002
|
||||
),
|
||||
'Camelids' => array(
|
||||
'Camel' => 6074,
|
||||
'Llama' => 6036
|
||||
),
|
||||
'Canines & Lupines' => array(
|
||||
'Coyote' => 6008,
|
||||
'Doberman' => 6009,
|
||||
'Dog' => 6010,
|
||||
'Dingo' => 6011,
|
||||
'German Shepherd' => 6012,
|
||||
'Jackal' => 6013,
|
||||
'Husky' => 6014,
|
||||
'Wolf' => 6016,
|
||||
'Canine (Other)' => 6017
|
||||
),
|
||||
'Cervines' => array(
|
||||
'Cervine (Other)' => 6018
|
||||
),
|
||||
'Cows & Bovines' => array(
|
||||
'Antelope' => 6004,
|
||||
'Cows' => 6003,
|
||||
'Gazelle' => 6005,
|
||||
'Goat' => 6006,
|
||||
'Bovines (General)' => 6007
|
||||
),
|
||||
'Dragons' => array(
|
||||
'Eastern Dragon' => 4001,
|
||||
'Hydra' => 4002,
|
||||
'Serpent' => 4003,
|
||||
'Western Dragon' => 4004,
|
||||
'Wyvern' => 4005,
|
||||
'Dragon (Other)' => 4000
|
||||
),
|
||||
'Equestrians' => array(
|
||||
'Donkey' => 6019,
|
||||
'Horse' => 6034,
|
||||
'Pony' => 6073,
|
||||
'Zebra' => 6071
|
||||
),
|
||||
'Exotic & Mythicals' => array(
|
||||
'Argonian' => 5002,
|
||||
'Chakat' => 5003,
|
||||
'Chocobo' => 5004,
|
||||
'Citra' => 5005,
|
||||
'Crux' => 5006,
|
||||
'Daemon' => 5007,
|
||||
'Digimon' => 5008,
|
||||
'Dracat' => 5009,
|
||||
'Draenei' => 5010,
|
||||
'Elf' => 5011,
|
||||
'Gargoyle' => 5012,
|
||||
'Iksar' => 5013,
|
||||
'Kaiju/Monster' => 5015,
|
||||
'Langurhali' => 5014,
|
||||
'Moogle' => 5017,
|
||||
'Naga' => 5016,
|
||||
'Orc' => 5018,
|
||||
'Pokemon' => 5019,
|
||||
'Satyr' => 5020,
|
||||
'Sergal' => 5021,
|
||||
'Tanuki' => 5022,
|
||||
'Unicorn' => 5023,
|
||||
'Xenomorph' => 5024,
|
||||
'Alien (Other)' => 5001,
|
||||
'Exotic (Other)' => 5000
|
||||
),
|
||||
'Felines' => array(
|
||||
'Domestic Cat' => 6020,
|
||||
'Cheetah' => 6021,
|
||||
'Cougar' => 6022,
|
||||
'Jaguar' => 6023,
|
||||
'Leopard' => 6024,
|
||||
'Lion' => 6025,
|
||||
'Lynx' => 6026,
|
||||
'Ocelot' => 6027,
|
||||
'Panther' => 6028,
|
||||
'Tiger' => 6029,
|
||||
'Feline (Other)' => 6030
|
||||
),
|
||||
'Insects' => array(
|
||||
'Arachnid' => 8000,
|
||||
'Mantid' => 8004,
|
||||
'Scorpion' => 8005,
|
||||
'Insect (Other)' => 8003
|
||||
),
|
||||
'Mammals (Other)' => array(
|
||||
'Bat' => 6001,
|
||||
'Giraffe' => 6031,
|
||||
'Hedgehog' => 6032,
|
||||
'Hippopotamus' => 6033,
|
||||
'Hyena' => 6035,
|
||||
'Panda' => 6052,
|
||||
'Pig/Swine' => 6053,
|
||||
'Rabbit/Hare' => 6059,
|
||||
'Raccoon' => 6060,
|
||||
'Red Panda' => 6062,
|
||||
'Meerkat' => 6043,
|
||||
'Mongoose' => 6044,
|
||||
'Rhinoceros' => 6063,
|
||||
'Mammals (Other)' => 6000
|
||||
),
|
||||
'Marsupials' => array(
|
||||
'Opossum' => 6037,
|
||||
'Kangaroo' => 6038,
|
||||
'Koala' => 6039,
|
||||
'Quoll' => 6040,
|
||||
'Wallaby' => 6041,
|
||||
'Marsupial (Other)' => 6042
|
||||
),
|
||||
'Mustelids' => array(
|
||||
'Badger' => 6045,
|
||||
'Ferret' => 6046,
|
||||
'Mink' => 6048,
|
||||
'Otter' => 6047,
|
||||
'Skunk' => 6069,
|
||||
'Weasel' => 6049,
|
||||
'Mustelid (Other)' => 6051
|
||||
),
|
||||
'Primates' => array(
|
||||
'Gorilla' => 6054,
|
||||
'Human' => 6055,
|
||||
'Lemur' => 6056,
|
||||
'Monkey' => 6057,
|
||||
'Primate (Other)' => 6058
|
||||
),
|
||||
'Reptillian' => array(
|
||||
'Alligator & Crocodile' => 7001,
|
||||
'Gecko' => 7003,
|
||||
'Iguana' => 7004,
|
||||
'Lizard' => 7005,
|
||||
'Snakes & Serpents' => 7006,
|
||||
'Turtle' => 7007,
|
||||
'Reptilian (Other)' => 7000
|
||||
),
|
||||
'Rodents' => array(
|
||||
'Beaver' => 6064,
|
||||
'Mouse' => 6065,
|
||||
'Rat' => 6061,
|
||||
'Squirrel' => 6070,
|
||||
'Rodent (Other)' => 6067
|
||||
),
|
||||
'Vulpines' => array(
|
||||
'Fennec' => 6072,
|
||||
'Fox' => 6075,
|
||||
'Vulpine (Other)' => 6015
|
||||
),
|
||||
'Other' => array(
|
||||
'Dinosaur' => 8001,
|
||||
'Wolverine' => 6050
|
||||
)
|
||||
),
|
||||
'defaultValue' => 1
|
||||
),
|
||||
'gender' => array(
|
||||
'name' => 'Gender',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Any' => 0,
|
||||
'Male' => 2,
|
||||
'Female' => 3,
|
||||
'Herm' => 4,
|
||||
'Transgender' => 5,
|
||||
'Multiple characters' => 6,
|
||||
'Other / Not Specified' => 7
|
||||
),
|
||||
'defaultValue' => 0
|
||||
),
|
||||
'rating_general' => array(
|
||||
'name' => 'General',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'rating_mature' => array(
|
||||
'name' => 'Mature',
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'rating_adult' => array(
|
||||
'name' => 'Adult',
|
||||
'type' => 'checkbox',
|
||||
),
|
||||
'limit-browse' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'required' => true,
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
|
||||
),
|
||||
'Journals' => array(
|
||||
'username-journals' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true,
|
||||
'title' => 'Lowercase username as seen in URLs'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => -1,
|
||||
'title' => 'Limit number of journals to return. -1 for unlimited.'
|
||||
)
|
||||
|
||||
),
|
||||
'Single Journal' => array(
|
||||
'journal-id' => array(
|
||||
'name' => 'Journal ID',
|
||||
'required' => true,
|
||||
'type' => 'number',
|
||||
'title' => 'Number seen in journal URL'
|
||||
)
|
||||
),
|
||||
'Gallery' => array(
|
||||
'username-gallery' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true,
|
||||
'title' => 'Lowercase username as seen in URLs'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
),
|
||||
'Scraps' => array(
|
||||
'username-scraps' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true,
|
||||
'title' => 'Lowercase username as seen in URLs'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
),
|
||||
'Favorites' => array(
|
||||
'username-favorites' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true,
|
||||
'title' => 'Lowercase username as seen in URLs'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
),
|
||||
'Gallery Folder' => array(
|
||||
'username-folder' => array(
|
||||
'name' => 'Username',
|
||||
'required' => true,
|
||||
'title' => 'Lowercase username as seen in URLs'
|
||||
),
|
||||
'folder-id' => array(
|
||||
'name' => 'Folder ID',
|
||||
'required' => true,
|
||||
'type' => 'number',
|
||||
'title' => 'Number seen in folder URL'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 10,
|
||||
'title' => 'Limit number of submissions to return. -1 for unlimited.'
|
||||
),
|
||||
'full' => array(
|
||||
'name' => 'Full view',
|
||||
'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
),
|
||||
'cache' => array(
|
||||
'name' => 'Cache submission pages',
|
||||
'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
|
||||
'type' => 'checkbox',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* This was aquired by creating a new user on FA then
|
||||
* extracting the cookie from the browsers dev console.
|
||||
*/
|
||||
const FA_AUTH_COOKIE = 'b=4ce65691-b50f-4742-a990-bf28d6de16ee; a=ca6e4566-9d81-4263-9444-653b142e35f8';
|
||||
|
||||
public function detectParameters($url) {
|
||||
$params = array();
|
||||
|
||||
// Single journal
|
||||
$regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journal\/(\d+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['journal-id'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// Journals
|
||||
$regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journals\/([^\/&?\n]+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['username-journals'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
// Gallery folder
|
||||
$regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/gallery\/([^\/&?\n]+)\/folder\/(\d+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['username-folder'] = urldecode($matches[3]);
|
||||
$params['folder-id'] = urldecode($matches[4]);
|
||||
$params['full'] = 'on';
|
||||
return $params;
|
||||
}
|
||||
|
||||
// Gallery (must be after gallery folder)
|
||||
$regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/(gallery|scraps|favorites)\/([^\/&?\n]+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['username-' . $matches[3]] = urldecode($matches[4]);
|
||||
$params['full'] = 'on';
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
switch($this->queriedContext) {
|
||||
case 'Search':
|
||||
return 'Search For '
|
||||
. $this->getInput('q');
|
||||
case 'Browse':
|
||||
return 'Browse';
|
||||
case 'Journals':
|
||||
return $this->getInput('username-journals');
|
||||
case 'Single Journal':
|
||||
return 'Journal '
|
||||
. $this->getInput('journal-id');
|
||||
case 'Gallery':
|
||||
return $this->getInput('username-gallery');
|
||||
case 'Scraps':
|
||||
return $this->getInput('username-scraps');
|
||||
case 'Favorites':
|
||||
return $this->getInput('username-favorites');
|
||||
case 'Gallery Folder':
|
||||
return $this->getInput('username-folder')
|
||||
. '\'s Folder '
|
||||
. $this->getInput('folder-id');
|
||||
default: return parent::getName();
|
||||
}
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
switch($this->queriedContext) {
|
||||
case 'Search':
|
||||
return 'FurAffinity Search For '
|
||||
. $this->getInput('q');
|
||||
case 'Browse':
|
||||
return 'FurAffinity Browse';
|
||||
case 'Journals':
|
||||
return 'FurAffinity Journals By '
|
||||
. $this->getInput('username-journals');
|
||||
case 'Single Journal':
|
||||
return 'FurAffinity Journal '
|
||||
. $this->getInput('journal-id');
|
||||
case 'Gallery':
|
||||
return 'FurAffinity Gallery By '
|
||||
. $this->getInput('username-gallery');
|
||||
case 'Scraps':
|
||||
return 'FurAffinity Scraps By '
|
||||
. $this->getInput('username-scraps');
|
||||
case 'Favorites':
|
||||
return 'FurAffinity Favorites By '
|
||||
. $this->getInput('username-favorites');
|
||||
case 'Gallery Folder':
|
||||
return 'FurAffinity Gallery Folder '
|
||||
. $this->getInput('folder-id')
|
||||
. ' By '
|
||||
. $this->getInput('username-folder');
|
||||
default: return parent::getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
switch($this->queriedContext) {
|
||||
case 'Search':
|
||||
return SELF::URI
|
||||
. '/search';
|
||||
case 'Browse':
|
||||
return SELF::URI
|
||||
. '/browse';
|
||||
case 'Journals':
|
||||
return SELF::URI
|
||||
. '/journals/'
|
||||
. $this->getInput('username-journals');
|
||||
case 'Single Journal':
|
||||
return SELF::URI
|
||||
. '/journal/'
|
||||
. $this->getInput('journal-id');
|
||||
case 'Gallery':
|
||||
return SELF::URI
|
||||
. '/gallery/'
|
||||
. $this->getInput('username-gallery');
|
||||
case 'Scraps':
|
||||
return SELF::URI
|
||||
. '/scraps/'
|
||||
. $this->getInput('username-scraps');
|
||||
case 'Favorites':
|
||||
return SELF::URI
|
||||
. '/favorites/'
|
||||
. $this->getInput('username-favorites');
|
||||
case 'Gallery Folder':
|
||||
return SELF::URI
|
||||
. '/gallery/'
|
||||
. $this->getInput('username-folder')
|
||||
. '/folder/'
|
||||
. $this->getInput('folder-id');
|
||||
default: return parent::getURI();
|
||||
}
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
switch($this->queriedContext) {
|
||||
case 'Search':
|
||||
$data = array(
|
||||
'q' => $this->getInput('q'),
|
||||
'perpage' => 72,
|
||||
'rating-general' => ($this->getInput('rating-general') === true ? 'on' : 0),
|
||||
'rating-mature' => ($this->getInput('rating-mature') === true ? 'on' : 0),
|
||||
'rating-adult' => ($this->getInput('rating-adult') === true ? 'on' : 0),
|
||||
'range' => $this->getInput('range'),
|
||||
'type-art' => ($this->getInput('type-art') === true ? 'on' : 0),
|
||||
'type-flash' => ($this->getInput('type-flash') === true ? 'on' : 0),
|
||||
'type-photo' => ($this->getInput('type-photo') === true ? 'on' : 0),
|
||||
'type-music' => ($this->getInput('type-music') === true ? 'on' : 0),
|
||||
'type-story' => ($this->getInput('type-story') === true ? 'on' : 0),
|
||||
'type-poetry' => ($this->getInput('type-poetry') === true ? 'on' : 0),
|
||||
'mode' => $this->getInput('mode')
|
||||
);
|
||||
$html = $this->postFASimpleHTMLDOM($data);
|
||||
$limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
|
||||
$this->itemsFromSubmissionList($html, $limit);
|
||||
break;
|
||||
case 'Browse':
|
||||
$data = array(
|
||||
'cat' => $this->getInput('cat'),
|
||||
'atype' => $this->getInput('atype'),
|
||||
'species' => $this->getInput('species'),
|
||||
'gender' => $this->getInput('gender'),
|
||||
'perpage' => 72,
|
||||
'rating_general' => ($this->getInput('rating_general') === true ? 'on' : 0),
|
||||
'rating_mature' => ($this->getInput('rating_mature') === true ? 'on' : 0),
|
||||
'rating_adult' => ($this->getInput('rating_adult') === true ? 'on' : 0)
|
||||
);
|
||||
$html = $this->postFASimpleHTMLDOM($data);
|
||||
$limit = (is_int($this->getInput('limit-browse')) ? $this->getInput('limit-browse') : 10);
|
||||
$this->itemsFromSubmissionList($html, $limit);
|
||||
break;
|
||||
case 'Journals':
|
||||
$html = $this->getFASimpleHTMLDOM($this->getURI());
|
||||
$limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : -1);
|
||||
$this->itemsFromJournalList($html, $limit);
|
||||
break;
|
||||
case 'Single Journal':
|
||||
$html = $this->getFASimpleHTMLDOM($this->getURI());
|
||||
$this->itemsFromJournal($html);
|
||||
break;
|
||||
case 'Gallery':
|
||||
case 'Scraps':
|
||||
case 'Favorites':
|
||||
case 'Gallery Folder':
|
||||
$html = $this->getFASimpleHTMLDOM($this->getURI());
|
||||
$limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
|
||||
$this->itemsFromSubmissionList($html, $limit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function postFASimpleHTMLDOM($data) {
|
||||
$opts = array(
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => http_build_query($data)
|
||||
);
|
||||
$header = array(
|
||||
'Host: ' . parse_url(self::URI, PHP_URL_HOST),
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'Cookie: ' . self::FA_AUTH_COOKIE
|
||||
);
|
||||
|
||||
$html = getSimpleHTMLDOM($this->getURI(), $header, $opts);
|
||||
$html = defaultLinkTo($html, $this->getURI());
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function getFASimpleHTMLDOM($url, $cache = false) {
|
||||
$header = array(
|
||||
'Cookie: ' . self::FA_AUTH_COOKIE
|
||||
);
|
||||
|
||||
if($cache) {
|
||||
$html = getSimpleHTMLDOMCached($url, 86400, $header); // 24 hours
|
||||
} else {
|
||||
$html = getSimpleHTMLDOM($url, $header);
|
||||
}
|
||||
|
||||
$html = defaultLinkTo($html, $url);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function itemsFromJournalList($html, $limit) {
|
||||
foreach($html->find('table[id^=jid:]') as $journal) {
|
||||
# allows limit = -1 to mean 'unlimited'
|
||||
if($limit-- === 0) break;
|
||||
|
||||
$item = array();
|
||||
|
||||
$this->setReferrerPolicy($journal);
|
||||
|
||||
$item['uri'] = $journal->find('a', 0)->href;
|
||||
$item['title'] = html_entity_decode($journal->find('a', 0)->plaintext);
|
||||
$item['author'] = $this->getInput('username-journals');
|
||||
$item['timestamp'] = strtotime(
|
||||
$journal->find('span.popup_date', 0)->plaintext);
|
||||
$item['content'] = $journal
|
||||
->find('.alt1 table div.no_overflow', 0)
|
||||
->innertext;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function itemsFromJournal($html) {
|
||||
$this->setReferrerPolicy($html);
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $this->getURI();
|
||||
|
||||
$title = $html->find('.journal-title-box .no_overflow', 0)->plaintext;
|
||||
$title = html_entity_decode($title);
|
||||
$title = trim($title, " \t\n\r\0\x0B" . chr(0xC2) . chr(0xA0));
|
||||
$item['title'] = $title;
|
||||
|
||||
$item['author'] = $html->find('.journal-title-box a', 0)->plaintext;
|
||||
$item['timestamp'] = strtotime(
|
||||
$html->find('.journal-title-box span.popup_date', 0)->plaintext);
|
||||
$item['content'] = $html->find('.journal-body', 0)->innertext;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
||||
private function itemsFromSubmissionList($html, $limit) {
|
||||
$cache = ($this->getInput('cache') === true);
|
||||
|
||||
foreach($html->find('section.gallery figure') as $figure) {
|
||||
# allows limit = -1 to mean 'unlimited'
|
||||
if($limit-- === 0) break;
|
||||
|
||||
$item = array();
|
||||
|
||||
$submissionURL = $figure->find('b u a', 0)->href;
|
||||
$imgURL = 'https:' . $figure->find('b u a img', 0)->src;
|
||||
|
||||
$item['uri'] = $submissionURL;
|
||||
$item['title'] = html_entity_decode(
|
||||
$figure->find('figcaption p a[href*=/view/]', 0)->title);
|
||||
$item['author'] = $figure->find('figcaption p a[href*=/user/]', 0)->title;
|
||||
|
||||
if($this->getInput('full') === true) {
|
||||
$submissionHTML = $this->getFASimpleHTMLDOM($submissionURL, $cache);
|
||||
|
||||
$stats = $submissionHTML->find('.stats-container', 0);
|
||||
$item['timestamp'] = strtotime($stats->find('.popup_date', 0)->title);
|
||||
$item['enclosures'] = array(
|
||||
$submissionHTML->find('.actions a[href^=https://d.facdn]', 0)->href
|
||||
);
|
||||
foreach($stats->find('#keywords a') as $keyword) {
|
||||
$item['categories'][] = $keyword->plaintext;
|
||||
}
|
||||
|
||||
$previewSrc = $submissionHTML->find('#submissionImg', 0)
|
||||
->{'data-preview-src'};
|
||||
if($previewSrc) {
|
||||
$imgURL = 'https:' . $previewSrc;
|
||||
}
|
||||
|
||||
$description = $submissionHTML
|
||||
->find('.maintable .maintable tr td.alt1', -1);
|
||||
$this->setReferrerPolicy($description);
|
||||
$description = $description->innertext;
|
||||
|
||||
$item['content'] = <<<EOD
|
||||
<a href="$submissionURL">
|
||||
<img src="{$imgURL}" referrerpolicy="no-referrer" />
|
||||
</a>
|
||||
<p>
|
||||
{$description}
|
||||
</p>
|
||||
EOD;
|
||||
} else {
|
||||
$item['content'] = <<<EOD
|
||||
<a href="$submissionURL">
|
||||
<img src="$imgURL" referrerpolicy="no-referrer" />
|
||||
</a>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
private function setReferrerPolicy(&$html) {
|
||||
foreach($html->find('img') as $img) {
|
||||
/*
|
||||
* Note: Without the no-referrer policy their CDN sometimes denies requests.
|
||||
* We can't control this for enclosures sadly.
|
||||
* At least tt-rss adds the referrerpolicy on its own.
|
||||
* Alternatively we could not use https for images, but that's not ideal.
|
||||
*/
|
||||
$img->referrerpolicy = 'no-referrer';
|
||||
}
|
||||
}
|
||||
}
|
110
bridges/FurAffinityUserBridge.php
Normal file
110
bridges/FurAffinityUserBridge.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
class FurAffinityUserBridge extends BridgeAbstract {
|
||||
const NAME = 'FurAffinity User Gallery';
|
||||
const URI = 'https://www.furaffinity.net';
|
||||
const MAINTAINER = 'CyberJacob';
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'searchUsername' => array(
|
||||
'name' => 'Search Username',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'title' => 'Username to fetch the gallery for'
|
||||
),
|
||||
'loginUsername' => array(
|
||||
'name' => 'Login Username',
|
||||
'type' => 'text',
|
||||
'required' => true
|
||||
),
|
||||
'loginPassword' => array(
|
||||
'name' => 'Login Password',
|
||||
'type' => 'text',
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function collectData() {
|
||||
$cookies = self::login();
|
||||
$url = self::URI . '/gallery/' . $this->getInput('searchUsername');
|
||||
|
||||
$html = getSimpleHTMLDOM($url, $cookies)
|
||||
or returnServerError('Could not load the user\'s galary page.');
|
||||
|
||||
$submissions = $html->find('section[id=gallery-gallery]', 0)->find('figure');
|
||||
foreach($submissions as $submission) {
|
||||
$item = array();
|
||||
$item['title'] = $submission->find('figcaption', 0)->find('a', 0)->plaintext;
|
||||
|
||||
$thumbnail = $submission->find('a', 0);
|
||||
$thumbnail->href = self::URI . $thumbnail->href;
|
||||
|
||||
$item['content'] = $submission->find('a', 0);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return self::NAME . ' for ' . $this->getInput('searchUsername');
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return self::URI . '/user/' . $this->getInput('searchUsername');
|
||||
}
|
||||
|
||||
private function login() {
|
||||
$ch = curl_init(self::URI . '/login/');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, ini_get('user_agent'));
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||
|
||||
$fields = implode('&', array(
|
||||
'action=login',
|
||||
'retard_protection=1',
|
||||
'name=' . urlencode($this->getInput('loginUsername')),
|
||||
'pass=' . urlencode($this->getInput('loginPassword')),
|
||||
'login=Login to Faraffinity'
|
||||
));
|
||||
|
||||
curl_setopt($ch, CURLOPT_POST, 5);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
|
||||
|
||||
if(defined('PROXY_URL') && !defined('NOPROXY')) {
|
||||
curl_setopt($ch, CURLOPT_PROXY, PROXY_URL);
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
|
||||
|
||||
$data = curl_exec($ch);
|
||||
|
||||
$errorCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
$curlError = curl_error($ch);
|
||||
$curlErrno = curl_errno($ch);
|
||||
$curlInfo = curl_getinfo($ch);
|
||||
|
||||
if($data === false)
|
||||
fDebug::log("Cant't download {$url} cUrl error: {$curlError} ({$curlErrno})");
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if($errorCode != 200) {
|
||||
returnServerError(error_get_last());
|
||||
} else {
|
||||
preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $data, $matches);
|
||||
$cookies = array();
|
||||
|
||||
foreach($matches[1] as $item) {
|
||||
parse_str($item, $cookie);
|
||||
$cookies = array_merge($cookies, $cookie);
|
||||
}
|
||||
|
||||
return $cookies;
|
||||
}
|
||||
}
|
||||
}
|
@@ -61,7 +61,7 @@ class GQMagazineBridge extends BridgeAbstract
|
||||
|
||||
private function findTitleOf($link) {
|
||||
foreach (self::POSSIBLE_TITLES as $tag) {
|
||||
$title = $link->find($tag, 0);
|
||||
$title = $link->parent()->find($tag, 0);
|
||||
if($title !== null) {
|
||||
if($title->plaintext !== null) {
|
||||
return $title->plaintext;
|
||||
@@ -77,11 +77,13 @@ class GQMagazineBridge extends BridgeAbstract
|
||||
// Since GQ don't want simple class scrapping, let's do it the hard way and ... discover content !
|
||||
$main = $html->find('main', 0);
|
||||
foreach ($main->find('a') as $link) {
|
||||
if(strpos($link, $this->getInput('page')))
|
||||
continue;
|
||||
$uri = $link->href;
|
||||
$date = $link->find('time', 0);
|
||||
$date = $link->parent()->find('time', 0);
|
||||
|
||||
$item = array();
|
||||
$author = $link->find('span[itemprop=name]', 0);
|
||||
$author = $link->parent()->find('span[itemprop=name]', 0);
|
||||
if($author !== null) {
|
||||
$item['author'] = $author->plaintext;
|
||||
$item['title'] = $this->findTitleOf($link);
|
||||
@@ -115,7 +117,7 @@ class GQMagazineBridge extends BridgeAbstract
|
||||
*/
|
||||
private function loadFullArticle($uri){
|
||||
$html = getSimpleHTMLDOMCached($uri);
|
||||
return $html->find('section[data-test-id=ArticleBodyContent]', 0);
|
||||
return $html->find('section[data-test-id=MainContentWrapper]', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -5,7 +5,7 @@ class GiphyBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'kraoc';
|
||||
const NAME = 'Giphy Bridge';
|
||||
const URI = 'http://giphy.com/';
|
||||
const URI = 'https://giphy.com/';
|
||||
const CACHE_TIMEOUT = 300; //5min
|
||||
const DESCRIPTION = 'Bridge for giphy.com';
|
||||
|
||||
|
@@ -82,18 +82,21 @@ class GithubIssueBridge extends BridgeAbstract {
|
||||
|
||||
$uri = $this->buildGitHubIssueCommentUri($issueNbr, $comment->id);
|
||||
|
||||
$author = $comment->find('.author', 0)->plaintext;
|
||||
|
||||
$title .= ' / ' . trim($comment->plaintext);
|
||||
|
||||
$content = $title;
|
||||
if (null !== $comment->nextSibling()) {
|
||||
$content = $comment->nextSibling()->innertext;
|
||||
if ($comment->nextSibling()->nodeName() === 'span') {
|
||||
$content = $comment->nextSibling()->nextSibling()->innertext;
|
||||
}
|
||||
$author = $comment->find('.author', 0);
|
||||
if ($author) {
|
||||
$author = $author->plaintext;
|
||||
} else {
|
||||
$author = '';
|
||||
}
|
||||
|
||||
$title .= ' / '
|
||||
. trim(str_replace(
|
||||
array('octicon','-'), array(''),
|
||||
$comment->find('.octicon', 0)->getAttribute('class')
|
||||
));
|
||||
|
||||
$content = $comment->plaintext;
|
||||
|
||||
$item = array();
|
||||
$item['author'] = $author;
|
||||
$item['uri'] = $uri;
|
||||
@@ -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-"]
|
||||
');
|
||||
$comments = $issue->find(
|
||||
'.comment, .TimelineItem-badge'
|
||||
);
|
||||
|
||||
foreach($comments as $comment) {
|
||||
|
||||
if (!$comment->hasChildNodes()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$comment->hasClass('discussion-item-header')) {
|
||||
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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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';
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -25,13 +25,10 @@ class GoogleSearchBridge extends BridgeAbstract {
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
|
||||
$html = getSimpleHTMLDOM(self::URI
|
||||
. 'search?q='
|
||||
. urlencode($this->getInput('q'))
|
||||
. '&num=100&complete=0&tbs=qdr:y,sbd:1')
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('No results for this query.');
|
||||
|
||||
$emIsRes = $html->find('div[id=ires]', 0);
|
||||
$emIsRes = $html->find('div[id=res]', 0);
|
||||
|
||||
if(!is_null($emIsRes)) {
|
||||
foreach($emIsRes->find('div[class=g]') as $element) {
|
||||
@@ -54,6 +51,17 @@ class GoogleSearchBridge extends BridgeAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
if (!is_null($this->getInput('q'))) {
|
||||
return self::URI
|
||||
. 'search?q='
|
||||
. urlencode($this->getInput('q'))
|
||||
. '&num=100&complete=0&tbs=qdr:y,sbd:1';
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('q'))) {
|
||||
return $this->getInput('q') . ' - Google search';
|
||||
|
@@ -2,7 +2,7 @@
|
||||
class HDWallpapersBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'nel50n';
|
||||
const NAME = 'HD Wallpapers Bridge';
|
||||
const URI = 'http://www.hdwallpapers.in/';
|
||||
const URI = 'https://www.hdwallpapers.in/';
|
||||
const CACHE_TIMEOUT = 43200; //12h
|
||||
const DESCRIPTION = 'Returns the latests wallpapers from HDWallpapers';
|
||||
|
||||
@@ -72,7 +72,7 @@ class HDWallpapersBridge extends BridgeAbstract {
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('c')) && !is_null($this->getInput('r'))) {
|
||||
return 'HDWallpapers - '
|
||||
. str_replace(['__', '_'], [' & ', ' '], $this->getInput('c'))
|
||||
. str_replace(array('__', '_'), array(' & ', ' '), $this->getInput('c'))
|
||||
. ' ['
|
||||
. $this->getInput('r')
|
||||
. ']';
|
||||
|
@@ -13,6 +13,11 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
|
||||
'Date added to HIBP' => 'dateAdded',
|
||||
),
|
||||
'defaultValue' => 'dateAdded',
|
||||
),
|
||||
'item_limit' => array(
|
||||
'name' => 'Limit number of returned items',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 20,
|
||||
)
|
||||
));
|
||||
|
||||
@@ -109,6 +114,12 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
|
||||
*/
|
||||
private function createItems() {
|
||||
|
||||
$limit = $this->getInput('item_limit');
|
||||
|
||||
if ($limit < 1) {
|
||||
$limit = 20;
|
||||
}
|
||||
|
||||
foreach ($this->breaches as $breach) {
|
||||
$item = array();
|
||||
|
||||
@@ -118,6 +129,10 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
|
||||
$item['content'] = $breach['content'];
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
if (count($this->items) >= $limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ class HentaiHavenBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'albirew';
|
||||
const NAME = 'Hentai Haven';
|
||||
const URI = 'http://hentaihaven.org/';
|
||||
const URI = 'https://hentaihaven.org/';
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Returns releases from Hentai Haven';
|
||||
|
||||
|
65
bridges/IGNBridge.php
Normal file
65
bridges/IGNBridge.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
class IGNBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'IceWreck';
|
||||
const NAME = 'IGN Bridge';
|
||||
const URI = 'https://www.ign.com/';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
const DESCRIPTION = 'RSS Feed For IGN';
|
||||
|
||||
public function collectData(){
|
||||
$this->collectExpandableDatas('http://feeds.ign.com/ign/all', 15);
|
||||
}
|
||||
|
||||
// IGNs feed is both hidden and incomplete. This bridge tries to fix this.
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
|
||||
// $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.
|
||||
* (and their scraping)
|
||||
*/
|
||||
|
||||
// For Articles
|
||||
$article = $articlePage->find('section.article-page', 0);
|
||||
// add in verdicts in articles, reviews etc
|
||||
foreach($articlePage->find('div.article-section') as $element) {
|
||||
$article = $article . $element;
|
||||
}
|
||||
|
||||
// For Wikis and HowTos
|
||||
foreach($articlePage->find('.wiki-page') as $wikiContents) {
|
||||
$article = $article . $wikiContents;
|
||||
}
|
||||
|
||||
// Add content to feed
|
||||
$item['content'] = $article;
|
||||
return $item;
|
||||
}
|
||||
}
|
@@ -32,28 +32,61 @@ class InstagramBridge extends BridgeAbstract {
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'All' => 'all',
|
||||
'Story' => 'story',
|
||||
'Video' => 'video',
|
||||
'Picture' => 'picture',
|
||||
'Multiple' => 'multiple',
|
||||
),
|
||||
'defaultValue' => 'all'
|
||||
),
|
||||
'direct_links' => array(
|
||||
'name' => 'Use direct media links',
|
||||
'type' => 'checkbox',
|
||||
)
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
public function collectData(){
|
||||
const USER_QUERY_HASH = '58b6785bea111c67129decbe6a448951';
|
||||
const TAG_QUERY_HASH = '174a5243287c5f3a7de741089750ab3b';
|
||||
const SHORTCODE_QUERY_HASH = '865589822932d1b43dfe312121dd353a';
|
||||
|
||||
if(is_null($this->getInput('u')) && $this->getInput('media_type') == 'story') {
|
||||
returnClientError('Stories are not supported for hashtags nor locations!');
|
||||
protected function getInstagramUserId($username) {
|
||||
|
||||
if(is_numeric($username)) return $username;
|
||||
|
||||
$cacheFac = new CacheFactory();
|
||||
$cacheFac->setWorkingDir(PATH_LIB_CACHES);
|
||||
$cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
|
||||
$cache->setScope(get_called_class());
|
||||
$cache->setKey(array($username));
|
||||
$key = $cache->loadData();
|
||||
|
||||
if($key == null) {
|
||||
$data = getContents(self::URI . 'web/search/topsearch/?query=' . $username);
|
||||
|
||||
foreach(json_decode($data)->users as $user) {
|
||||
if($user->user->username === $username) {
|
||||
$key = $user->user->pk;
|
||||
}
|
||||
}
|
||||
if($key == null) {
|
||||
returnServerError('Unable to find username in search result.');
|
||||
}
|
||||
$cache->saveData($key);
|
||||
}
|
||||
return $key;
|
||||
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$directLink = !is_null($this->getInput('direct_links')) && $this->getInput('direct_links');
|
||||
|
||||
$data = $this->getInstagramJSON($this->getURI());
|
||||
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
$userMedia = $data->entry_data->ProfilePage[0]->graphql->user->edge_owner_to_timeline_media->edges;
|
||||
$userMedia = $data->data->user->edge_owner_to_timeline_media->edges;
|
||||
} elseif(!is_null($this->getInput('h'))) {
|
||||
$userMedia = $data->entry_data->TagPage[0]->graphql->hashtag->edge_hashtag_to_media->edges;
|
||||
$userMedia = $data->data->hashtag->edge_hashtag_to_media->edges;
|
||||
} elseif(!is_null($this->getInput('l'))) {
|
||||
$userMedia = $data->entry_data->LocationsPage[0]->graphql->location->edge_location_to_media->edges;
|
||||
}
|
||||
@@ -61,22 +94,18 @@ class InstagramBridge extends BridgeAbstract {
|
||||
foreach($userMedia as $media) {
|
||||
$media = $media->node;
|
||||
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
switch($this->getInput('media_type')) {
|
||||
case 'all': break;
|
||||
case 'video':
|
||||
if($media->__typename != 'GraphVideo') continue 2;
|
||||
break;
|
||||
case 'picture':
|
||||
if($media->__typename != 'GraphImage') continue 2;
|
||||
break;
|
||||
case 'story':
|
||||
if($media->__typename != 'GraphSidecar') continue 2;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
if($this->getInput('media_type') == 'video' && !$media->is_video) continue;
|
||||
switch($this->getInput('media_type')) {
|
||||
case 'all': break;
|
||||
case 'video':
|
||||
if($media->__typename != 'GraphVideo' || !$media->is_video) continue 2;
|
||||
break;
|
||||
case 'picture':
|
||||
if($media->__typename != 'GraphImage') continue 2;
|
||||
break;
|
||||
case 'multiple':
|
||||
if($media->__typename != 'GraphSidecar') continue 2;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
$item = array();
|
||||
@@ -86,72 +115,146 @@ class InstagramBridge extends BridgeAbstract {
|
||||
$item['author'] = $media->owner->username;
|
||||
}
|
||||
|
||||
if (isset($media->edge_media_to_caption->edges[0]->node->text)) {
|
||||
$textContent = $media->edge_media_to_caption->edges[0]->node->text;
|
||||
} else {
|
||||
$textContent = '(no text)';
|
||||
}
|
||||
$textContent = $this->getTextContent($media);
|
||||
|
||||
$item['title'] = ($media->is_video ? '▶ ' : '') . trim($textContent);
|
||||
$item['title'] = ($media->is_video ? '▶ ' : '') . $textContent;
|
||||
$titleLinePos = strpos(wordwrap($item['title'], 120), "\n");
|
||||
if ($titleLinePos != false) {
|
||||
$item['title'] = substr($item['title'], 0, $titleLinePos) . '...';
|
||||
}
|
||||
|
||||
if(!is_null($this->getInput('u')) && $media->__typename == 'GraphSidecar') {
|
||||
$data = $this->getInstagramStory($item['uri']);
|
||||
$item['content'] = $data[0];
|
||||
$item['enclosures'] = $data[1];
|
||||
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);
|
||||
}
|
||||
|
||||
switch($media->__typename) {
|
||||
case 'GraphSidecar':
|
||||
$data = $this->getInstagramSidecarData($item['uri'], $item['title']);
|
||||
$item['content'] = $data[0];
|
||||
$item['enclosures'] = $data[1];
|
||||
break;
|
||||
case 'GraphImage':
|
||||
$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'], $mediaURI);
|
||||
$item['content'] = $data[0];
|
||||
if($directLink) {
|
||||
$item['enclosures'] = $data[1];
|
||||
} else {
|
||||
$item['enclosures'] = array($mediaURI);
|
||||
}
|
||||
$item['thumbnail'] = $mediaURI;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
$item['timestamp'] = $media->taken_at_timestamp;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getInstagramStory($uri) {
|
||||
// returns Sidecar(a post which has multiple media)'s contents and enclosures
|
||||
protected function getInstagramSidecarData($uri, $postTitle) {
|
||||
$mediaInfo = $this->getSinglePostData($uri);
|
||||
|
||||
$data = $this->getInstagramJSON($uri);
|
||||
$mediaInfo = $data->entry_data->PostPage[0]->graphql->shortcode_media;
|
||||
$textContent = $this->getTextContent($mediaInfo);
|
||||
|
||||
//Process the first element, that isn't in the node graph
|
||||
if (count($mediaInfo->edge_media_to_caption->edges) > 0) {
|
||||
$caption = $mediaInfo->edge_media_to_caption->edges[0]->node->text;
|
||||
} else {
|
||||
$caption = '';
|
||||
}
|
||||
|
||||
$enclosures = [$mediaInfo->display_url];
|
||||
$content = '<img src="' . htmlentities($mediaInfo->display_url) . '" alt="' . $caption . '" />';
|
||||
|
||||
foreach($mediaInfo->edge_sidecar_to_children->edges as $media) {
|
||||
$display_url = $media->node->display_url;
|
||||
if(!in_array($display_url, $enclosures)) { // add only if not added yet
|
||||
$content .= '<img src="' . htmlentities($display_url) . '" alt="' . $caption . '" />';
|
||||
$enclosures[] = $display_url;
|
||||
$enclosures = array();
|
||||
$content = '';
|
||||
foreach($mediaInfo->edge_sidecar_to_children->edges as $singleMedia) {
|
||||
$singleMedia = $singleMedia->node;
|
||||
if($singleMedia->is_video) {
|
||||
if(in_array($singleMedia->video_url, $enclosures)) continue; // check if not added yet
|
||||
$content .= '<video controls><source src="' . $singleMedia->video_url . '" type="video/mp4"></video><br>';
|
||||
array_push($enclosures, $singleMedia->video_url);
|
||||
} else {
|
||||
if(in_array($singleMedia->display_url, $enclosures)) continue; // check if not added yet
|
||||
$content .= '<a href="' . $singleMedia->display_url . '" target="_blank">';
|
||||
$content .= '<img src="' . $singleMedia->display_url . '" alt="' . $postTitle . '" />';
|
||||
$content .= '</a><br>';
|
||||
array_push($enclosures, $singleMedia->display_url);
|
||||
}
|
||||
}
|
||||
$content .= '<br>' . nl2br(htmlentities($textContent));
|
||||
|
||||
return [$content, $enclosures];
|
||||
return array($content, $enclosures);
|
||||
}
|
||||
|
||||
// returns Video post's contents and enclosures
|
||||
protected function getInstagramVideoData($uri, $mediaURI) {
|
||||
$mediaInfo = $this->getSinglePostData($uri);
|
||||
|
||||
$textContent = $this->getTextContent($mediaInfo);
|
||||
$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));
|
||||
}
|
||||
|
||||
protected function getTextContent($media) {
|
||||
$textContent = '(no text)';
|
||||
//Process the first element, that isn't in the node graph
|
||||
if (count($media->edge_media_to_caption->edges) > 0) {
|
||||
$textContent = trim($media->edge_media_to_caption->edges[0]->node->text);
|
||||
}
|
||||
return $textContent;
|
||||
}
|
||||
|
||||
protected function getSinglePostData($uri) {
|
||||
$shortcode = explode('/', $uri)[4];
|
||||
$data = getContents(self::URI .
|
||||
'graphql/query/?query_hash=' .
|
||||
self::SHORTCODE_QUERY_HASH .
|
||||
'&variables={"shortcode"%3A"' .
|
||||
$shortcode .
|
||||
'"}');
|
||||
|
||||
return json_decode($data)->data->shortcode_media;
|
||||
}
|
||||
|
||||
protected function getInstagramJSON($uri) {
|
||||
|
||||
$html = getContents($uri)
|
||||
or returnServerError('Could not request Instagram.');
|
||||
$scriptRegex = '/window\._sharedData = (.*);<\/script>/';
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
|
||||
preg_match($scriptRegex, $html, $matches, PREG_OFFSET_CAPTURE, 0);
|
||||
$userId = $this->getInstagramUserId($this->getInput('u'));
|
||||
|
||||
return json_decode($matches[1][0]);
|
||||
$data = getContents(self::URI .
|
||||
'graphql/query/?query_hash=' .
|
||||
self::USER_QUERY_HASH .
|
||||
'&variables={"id"%3A"' .
|
||||
$userId .
|
||||
'"%2C"first"%3A10}');
|
||||
return json_decode($data);
|
||||
|
||||
} elseif(!is_null($this->getInput('h'))) {
|
||||
$data = getContents(self::URI .
|
||||
'graphql/query/?query_hash=' .
|
||||
self::TAG_QUERY_HASH .
|
||||
'&variables={"tag_name"%3A"' .
|
||||
$this->getInput('h') .
|
||||
'"%2C"first"%3A10}');
|
||||
return json_decode($data);
|
||||
|
||||
} else {
|
||||
|
||||
$html = getContents($uri)
|
||||
or returnServerError('Could not request Instagram.');
|
||||
$scriptRegex = '/window\._sharedData = (.*);<\/script>/';
|
||||
|
||||
preg_match($scriptRegex, $html, $matches, PREG_OFFSET_CAPTURE, 0);
|
||||
|
||||
return json_decode($matches[1][0]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -133,7 +133,7 @@ class InternetArchiveBridge extends BridgeAbstract {
|
||||
|
||||
$item['title'] = trim($result->find('div.ttl', 0)->innertext);
|
||||
$item['timestamp'] = strtotime($result->find('div.hidden-tiles.pubdate.C.C3', 0)->children(0)->plaintext);
|
||||
$item['uri'] = self::URI . $result->find('div.item-ttl.C.C2 > a', 0)->href;
|
||||
$item['uri'] = $result->find('div.item-ttl.C.C2 > a', 0)->href;
|
||||
|
||||
if ($result->find('div.by.C.C4', 0)->children(2)) {
|
||||
$item['author'] = $result->find('div.by.C.C4', 0)->children(2)->plaintext;
|
||||
|
@@ -19,28 +19,6 @@ class JapanExpoBridge extends BridgeAbstract {
|
||||
|
||||
public function collectData(){
|
||||
|
||||
function frenchPubDateToTimestamp($date_to_parse) {
|
||||
return strtotime(
|
||||
strtr(
|
||||
strtolower(str_replace('Publié le ', '', $date_to_parse)),
|
||||
array(
|
||||
'janvier' => 'jan',
|
||||
'février' => 'feb',
|
||||
'mars' => 'march',
|
||||
'avril' => 'apr',
|
||||
'mai' => 'may',
|
||||
'juin' => 'jun',
|
||||
'juillet' => 'jul',
|
||||
'août' => 'aug',
|
||||
'septembre' => 'sep',
|
||||
'octobre' => 'oct',
|
||||
'novembre' => 'nov',
|
||||
'décembre' => 'dec'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$convert_article_images = function($matches){
|
||||
if(is_array($matches) && count($matches) > 1) {
|
||||
return '<img src="' . $matches[1] . '" />';
|
||||
@@ -82,7 +60,7 @@ class JapanExpoBridge extends BridgeAbstract {
|
||||
$content = $headings . $article;
|
||||
} else {
|
||||
$date_text = $element->find('span.date', 0)->plaintext;
|
||||
$timestamp = frenchPubDateToTimestamp($date_text);
|
||||
$timestamp = $this->frenchPubDateToTimestamp($date_text);
|
||||
$title = trim($element->find('span._title', 0)->plaintext);
|
||||
$content = '<img src="'
|
||||
. $thumbnail
|
||||
@@ -103,4 +81,26 @@ class JapanExpoBridge extends BridgeAbstract {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
private function frenchPubDateToTimestamp($date_to_parse) {
|
||||
return strtotime(
|
||||
strtr(
|
||||
strtolower(str_replace('Publié le ', '', $date_to_parse)),
|
||||
array(
|
||||
'janvier' => 'jan',
|
||||
'février' => 'feb',
|
||||
'mars' => 'march',
|
||||
'avril' => 'apr',
|
||||
'mai' => 'may',
|
||||
'juin' => 'jun',
|
||||
'juillet' => 'jul',
|
||||
'août' => 'aug',
|
||||
'septembre' => 'sep',
|
||||
'octobre' => 'oct',
|
||||
'novembre' => 'nov',
|
||||
'décembre' => 'dec'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -347,5 +347,6 @@ class JustETFBridge extends BridgeAbstract {
|
||||
|
||||
return $element->plaintext;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ class KonachanBridge extends MoebooruBridge {
|
||||
|
||||
const MAINTAINER = 'mitsukarenai';
|
||||
const NAME = 'Konachan';
|
||||
const URI = 'http://konachan.com/';
|
||||
const URI = 'https://konachan.com/';
|
||||
const DESCRIPTION = 'Returns images from given page';
|
||||
|
||||
}
|
||||
|
@@ -34,6 +34,12 @@ class KununuBridge extends BridgeAbstract {
|
||||
'name' => 'Include benefits',
|
||||
'type' => 'checkbox',
|
||||
'title' => 'Activate to include benefits in the feed'
|
||||
),
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'defaultValue' => 3,
|
||||
'title' => "Maximum number of items to return in the feed.\n0 = unlimited"
|
||||
)
|
||||
),
|
||||
array(
|
||||
@@ -108,6 +114,8 @@ class KununuBridge extends BridgeAbstract {
|
||||
$articles = $section->find('article')
|
||||
or returnServerError('Unable to find articles!');
|
||||
|
||||
$limit = $this->getInput('limit') ?: 0;
|
||||
|
||||
// Go through all articles
|
||||
foreach($articles as $article) {
|
||||
|
||||
@@ -141,6 +149,8 @@ class KununuBridge extends BridgeAbstract {
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
if ($limit > 0 && count($this->items) >= $limit) break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
477
bridges/LaCentraleBridge.php
Normal file
477
bridges/LaCentraleBridge.php
Normal file
@@ -0,0 +1,477 @@
|
||||
<?php
|
||||
class LaCentraleBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'jacknumber';
|
||||
const NAME = 'La Centrale';
|
||||
const URI = 'https://www.lacentrale.fr/';
|
||||
const DESCRIPTION = 'Returns most recent vehicules ads from LaCentrale';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
'type' => array(
|
||||
'name' => 'Type de véhicule',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Voiture' => 'car',
|
||||
'Camion/Pickup' => 'truck',
|
||||
'Moto' => 'moto',
|
||||
'Scooter' => 'scooter',
|
||||
'Quad' => 'quad',
|
||||
'Caravane/Camping-car' => 'mobileHome'
|
||||
)
|
||||
),
|
||||
'brand' => array(
|
||||
'name' => 'Marque',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'ABARTH' => 'ABARTH',
|
||||
'AC' => 'AC',
|
||||
'AIXAM' => 'AIXAM',
|
||||
'ALFA ROMEO' => 'ALFA ROMEO',
|
||||
'ALKE' => 'ALKE',
|
||||
'ALPINA' => 'ALPINA',
|
||||
'ALPINE' => 'ALPINE',
|
||||
'AMC' => 'AMC',
|
||||
'ANAIG' => 'ANAIG',
|
||||
'APRILIA' => 'APRILIA',
|
||||
'ARIEL' => 'ARIEL',
|
||||
'ASTON MARTIN' => 'ASTON MARTIN',
|
||||
'AUDI' => 'AUDI',
|
||||
'AUSTIN HEALEY' => 'AUSTIN HEALEY',
|
||||
'AUSTIN' => 'AUSTIN',
|
||||
'AUTOBIANCHI' => 'AUTOBIANCHI',
|
||||
'AVINTON' => 'AVINTON',
|
||||
'BELLIER' => 'BELLIER',
|
||||
'BENELLI' => 'BENELLI',
|
||||
'BENTLEY' => 'BENTLEY',
|
||||
'BETA' => 'BETA',
|
||||
'BMW' => 'BMW',
|
||||
'BOLLORE' => 'BOLLORE',
|
||||
'BRIXTON' => 'BRIXTON',
|
||||
'BUELL' => 'BUELL',
|
||||
'BUGATTI' => 'BUGATTI',
|
||||
'BUICK' => 'BUICK',
|
||||
'BULLIT' => 'BULLIT',
|
||||
'CADILLAC' => 'CADILLAC',
|
||||
'CASALINI' => 'CASALINI',
|
||||
'CATERHAM' => 'CATERHAM',
|
||||
'CHATENET' => 'CHATENET',
|
||||
'CHEVROLET' => 'CHEVROLET',
|
||||
'CHRYSLER' => 'CHRYSLER',
|
||||
'CHUNLAN' => 'CHUNLAN',
|
||||
'CITROEN' => 'CITROEN',
|
||||
'COURB' => 'COURB',
|
||||
'CR&S' => 'CR&S',
|
||||
'CUPRA' => 'CUPRA',
|
||||
'CYCLONE' => 'CYCLONE',
|
||||
'DACIA' => 'DACIA',
|
||||
'DAELIM' => 'DAELIM',
|
||||
'DAEWOO' => 'DAEWOO',
|
||||
'DAF' => 'DAF',
|
||||
'DAIHATSU' => 'DAIHATSU',
|
||||
'DANGEL' => 'DANGEL',
|
||||
'DATSUN' => 'DATSUN',
|
||||
'DE SOTO' => 'DE SOTO',
|
||||
'DE TOMASO' => 'DE TOMASO',
|
||||
'DERBI' => 'DERBI',
|
||||
'DEVINCI' => 'DEVINCI',
|
||||
'DODGE' => 'DODGE',
|
||||
'DONKERVOORT' => 'DONKERVOORT',
|
||||
'DS' => 'DS',
|
||||
'DUCATI' => 'DUCATI',
|
||||
'DUCATY' => 'DUCATY',
|
||||
'DUE' => 'DUE',
|
||||
'ENFIELD' => 'ENFIELD',
|
||||
'EXCALIBUR' => 'EXCALIBUR',
|
||||
'FACEL VEGA' => 'FACEL VEGA',
|
||||
'FANTIC MOTOR' => 'FANTIC MOTOR',
|
||||
'FERRARI' => 'FERRARI',
|
||||
'FIAT' => 'FIAT',
|
||||
'FISKER' => 'FISKER',
|
||||
'FORD' => 'FORD',
|
||||
'FUSO' => 'FUSO',
|
||||
'GAS GAS' => 'GAS GAS',
|
||||
'GILERA' => 'GILERA',
|
||||
'GMC' => 'GMC',
|
||||
'GOWINN' => 'GOWINN',
|
||||
'GRANDIN' => 'GRANDIN',
|
||||
'HARLEY DAVIDSON' => 'HARLEY DAVIDSON',
|
||||
'HOMMELL' => 'HOMMELL',
|
||||
'HONDA' => 'HONDA',
|
||||
'HUMMER' => 'HUMMER',
|
||||
'HUSABERG' => 'HUSABERG',
|
||||
'HUSQVARNA' => 'HUSQVARNA',
|
||||
'HYOSUNG' => 'HYOSUNG',
|
||||
'HYUNDAI' => 'HYUNDAI',
|
||||
'INDIAN' => 'INDIAN',
|
||||
'INFINITI' => 'INFINITI',
|
||||
'INNOCENTI' => 'INNOCENTI',
|
||||
'ISUZU' => 'ISUZU',
|
||||
'IVECO' => 'IVECO',
|
||||
'JAGUAR' => 'JAGUAR',
|
||||
'JDM SIMPA' => 'JDM SIMPA',
|
||||
'JEEP' => 'JEEP',
|
||||
'JENSEN' => 'JENSEN',
|
||||
'JIAYUAN' => 'JIAYUAN',
|
||||
'KAWASAKI' => 'KAWASAKI',
|
||||
'KEEWAY' => 'KEEWAY',
|
||||
'KIA' => 'KIA',
|
||||
'KSR' => 'KSR',
|
||||
'KTM' => 'KTM',
|
||||
'KYMCO' => 'KYMCO',
|
||||
'LADA' => 'LADA',
|
||||
'LAMBORGHINI' => 'LAMBORGHINI',
|
||||
'LANCIA' => 'LANCIA',
|
||||
'LAND ROVER' => 'LAND ROVER',
|
||||
'LEXUS' => 'LEXUS',
|
||||
'LIGIER' => 'LIGIER',
|
||||
'LINCOLN' => 'LINCOLN',
|
||||
'LONDON TAXI COMPANY' => 'LONDON TAXI COMPANY',
|
||||
'LOTUS' => 'LOTUS',
|
||||
'MAGPOWER' => 'MAGPOWER',
|
||||
'MAN' => 'MAN',
|
||||
'MASAI' => 'MASAI',
|
||||
'MASERATI' => 'MASERATI',
|
||||
'MASH' => 'MASH',
|
||||
'MATRA' => 'MATRA',
|
||||
'MAYBACH' => 'MAYBACH',
|
||||
'MAZDA' => 'MAZDA',
|
||||
'MCLAREN' => 'MCLAREN',
|
||||
'MEGA' => 'MEGA',
|
||||
'MERCEDES' => 'MERCEDES',
|
||||
'MERCEDES-AMG' => 'MERCEDES-AMG',
|
||||
'MERCURY' => 'MERCURY',
|
||||
'MEYERS MANX' => 'MEYERS MANX',
|
||||
'MG' => 'MG',
|
||||
'MIA ELECTRIC' => 'MIA ELECTRIC',
|
||||
'MICROCAR' => 'MICROCAR',
|
||||
'MINAUTO' => 'MINAUTO',
|
||||
'MINI' => 'MINI',
|
||||
'MITSUBISHI' => 'MITSUBISHI',
|
||||
'MORGAN' => 'MORGAN',
|
||||
'MORRIS' => 'MORRIS',
|
||||
'MOTO GUZZI' => 'MOTO GUZZI',
|
||||
'MOTO MORINI' => 'MOTO MORINI',
|
||||
'MOTOBECANE' => 'MOTOBECANE',
|
||||
'MPM MOTORS' => 'MPM MOTORS',
|
||||
'MV AGUSTA' => 'MV AGUSTA',
|
||||
'NISSAN' => 'NISSAN',
|
||||
'NORTON' => 'NORTON',
|
||||
'NSU' => 'NSU',
|
||||
'OLDSMOBILE' => 'OLDSMOBILE',
|
||||
'OPEL' => 'OPEL',
|
||||
'ORCAL' => 'ORCAL',
|
||||
'OSSA' => 'OSSA',
|
||||
'PACKARD' => 'PACKARD',
|
||||
'PANTHER' => 'PANTHER',
|
||||
'PEUGEOT' => 'PEUGEOT',
|
||||
'PGO' => 'PGO',
|
||||
'PIAGGIO' => 'PIAGGIO',
|
||||
'PLYMOUTH' => 'PLYMOUTH',
|
||||
'POLARIS' => 'POLARIS',
|
||||
'PONTIAC' => 'PONTIAC',
|
||||
'PORSCHE' => 'PORSCHE',
|
||||
'REALM' => 'REALM',
|
||||
'REGAL RAPTOR' => 'REGAL RAPTOR',
|
||||
'RENAULT' => 'RENAULT',
|
||||
'RIEJU' => 'RIEJU',
|
||||
'ROLLS ROYCE' => 'ROLLS ROYCE',
|
||||
'ROVER' => 'ROVER',
|
||||
'ROYAL ENFIELD' => 'ROYAL ENFIELD',
|
||||
'SAAB' => 'SAAB',
|
||||
'SANTANA' => 'SANTANA',
|
||||
'SCANIA' => 'SCANIA',
|
||||
'SEAT' => 'SEAT',
|
||||
'SECMA' => 'SECMA',
|
||||
'SHELBY' => 'SHELBY',
|
||||
'SHERCO' => 'SHERCO',
|
||||
'SIMCA' => 'SIMCA',
|
||||
'SKODA' => 'SKODA',
|
||||
'SMART' => 'SMART',
|
||||
'SPYKER' => 'SPYKER',
|
||||
'SSANGYONG' => 'SSANGYONG',
|
||||
'STUDEBAKER' => 'STUDEBAKER',
|
||||
'SUBARU' => 'SUBARU',
|
||||
'SUNBEAM' => 'SUNBEAM',
|
||||
'SUZUKI' => 'SUZUKI',
|
||||
'SWM' => 'SWM',
|
||||
'SYM' => 'SYM',
|
||||
'TALBOT SIMCA' => 'TALBOT SIMCA',
|
||||
'TALBOT' => 'TALBOT',
|
||||
'TEILHOL' => 'TEILHOL',
|
||||
'TESLA' => 'TESLA',
|
||||
'TM' => 'TM',
|
||||
'TNT MOTOR' => 'TNT MOTOR',
|
||||
'TOYOTA' => 'TOYOTA',
|
||||
'TRIUMPH' => 'TRIUMPH',
|
||||
'TVR' => 'TVR',
|
||||
'VAUXHALL' => 'VAUXHALL',
|
||||
'VESPA' => 'VESPA',
|
||||
'VICTORY' => 'VICTORY',
|
||||
'VOLKSWAGEN' => 'VOLKSWAGEN',
|
||||
'VOLVO' => 'VOLVO',
|
||||
'VOXAN' => 'VOXAN',
|
||||
'WIESMANN' => 'WIESMANN',
|
||||
'YAMAHA' => 'YAMAHA',
|
||||
'YCF' => 'YCF',
|
||||
'ZERO' => 'ZERO',
|
||||
'ZONGSHEN' => 'ZONGSHEN'
|
||||
)
|
||||
),
|
||||
'model' => array(
|
||||
'name' => 'Modèle',
|
||||
'type' => 'text',
|
||||
'title' => 'Get the exact name on LaCentrale'
|
||||
),
|
||||
'versions' => array(
|
||||
'name' => 'Version(s)',
|
||||
'type' => 'text',
|
||||
'title' => 'Get the exact name(s) on LaCentrale. Separate by comma'
|
||||
),
|
||||
'category' => array(
|
||||
'name' => 'Catégorie',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'Voiture' => array(
|
||||
'4x4, SUV & Crossover' => '47',
|
||||
'Citadine' => '40',
|
||||
'Berline' => '41_42',
|
||||
'Break' => '43',
|
||||
'Cabriolet' => '46',
|
||||
'Coupé' => '45',
|
||||
'Monospace' => '44',
|
||||
'Bus et minibus' => '82',
|
||||
'Fourgonnette' => '85',
|
||||
'Fourgon (< 3,5 tonnes)' => '81',
|
||||
'Pick-up' => '50',
|
||||
'Voiture société, commerciale' => '80',
|
||||
'Sans permis' => '48',
|
||||
'Camion (> 3,5 tonnes)' => '83',
|
||||
),
|
||||
'Camion/Pickup' => array(
|
||||
'Camion (> 3,5 tonnes)' => '83',
|
||||
'Fourgon (< 3,5 tonnes)' => '81',
|
||||
'Bus et minibus' => '82',
|
||||
'Fourgonnette' => '85',
|
||||
'Pick-up' => '50',
|
||||
'Voiture société, commerciale' => '80'
|
||||
),
|
||||
'Moto' => array(
|
||||
'Custom' => '60',
|
||||
'Offroad' => '61',
|
||||
'Roadster' => '62',
|
||||
'GT' => '63',
|
||||
'Mini moto' => '64',
|
||||
'Mobylette' => '65',
|
||||
'Supermotard' => '66',
|
||||
'Trail' => '67',
|
||||
'Side-car' => '69',
|
||||
'Sportive' => '68'
|
||||
),
|
||||
'Caravane/Camping-car' => array(
|
||||
'Caravane' => '423',
|
||||
'Profilé' => '506',
|
||||
'Fourgon aménagé' => '507',
|
||||
'Intégral' => '508',
|
||||
'Capucine' => '510'
|
||||
)
|
||||
)
|
||||
),
|
||||
'pricemin' => array(
|
||||
'name' => 'Prix min',
|
||||
'type' => 'number'
|
||||
),
|
||||
'pricemax' => array(
|
||||
'name' => 'Prix max',
|
||||
'type' => 'number'
|
||||
),
|
||||
'location' => array(
|
||||
'name' => 'CP ou département',
|
||||
'type' => 'number',
|
||||
'title' => 'Only one'
|
||||
),
|
||||
'distance' => array(
|
||||
'name' => 'Rayon de recherche',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'10 km' => '1',
|
||||
'20 km' => '2',
|
||||
'50 km' => '3',
|
||||
'100 km' => '4',
|
||||
'200 km' => '5'
|
||||
)
|
||||
),
|
||||
'region' => array(
|
||||
'name' => 'Région',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'Auvergne-Rhône-Alpes' => 'FR-ARA',
|
||||
'Bourgogne-Franche-Comté' => 'FR-BFC',
|
||||
'Bretagne' => 'FR-BRE',
|
||||
'Centre-Val de Loire' => 'FR-CVL',
|
||||
'Corse' => 'FR-COR',
|
||||
'Grand Est' => 'FR-GES',
|
||||
'Hauts-de-France' => 'FR-HDF',
|
||||
'Île-de-France' => 'FR-IDF',
|
||||
'Normandie' => 'FR-NOR',
|
||||
'Nouvelle-Aquitaine' => 'FR-PAC',
|
||||
'Occitanie' => 'FR-PDL',
|
||||
'Pays de la Loire' => 'FR-OCC',
|
||||
'Provence-Alpes-Côte d\'Azur' => 'FR-NAQ'
|
||||
)
|
||||
),
|
||||
'mileagemin' => array(
|
||||
'name' => 'Kilométrage min',
|
||||
'type' => 'number'
|
||||
),
|
||||
'mileagemax' => array(
|
||||
'name' => 'Kilométrage max',
|
||||
'type' => 'number'
|
||||
),
|
||||
'yearmin' => array(
|
||||
'name' => 'Année min',
|
||||
'type' => 'number'
|
||||
),
|
||||
'yearmax' => array(
|
||||
'name' => 'Année max',
|
||||
'type' => 'number'
|
||||
),
|
||||
'cubiccapacitymin' => array(
|
||||
'name' => 'Cylindrée min',
|
||||
'type' => 'number'
|
||||
),
|
||||
'cubiccapacitymax' => array(
|
||||
'name' => 'Cylindrée max',
|
||||
'type' => 'number'
|
||||
),
|
||||
'fuel' => array(
|
||||
'name' => 'Énergie',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'Diesel' => 'dies',
|
||||
'Essence' => 'ess',
|
||||
'Électrique' => 'elec',
|
||||
'Hybride' => 'hyb',
|
||||
'GPL' => 'gpl',
|
||||
'Bioéthanol' => 'eth',
|
||||
'Autre' => 'alt'
|
||||
)
|
||||
),
|
||||
'gearbox' => array(
|
||||
'name' => 'Boite de vitesse',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'Boite automatique' => 'AUTO',
|
||||
'Boite mécanique' => 'MANUAL'
|
||||
)
|
||||
),
|
||||
'doors' => array(
|
||||
'name' => 'Nombre de portes',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'2 portes' => '2',
|
||||
'3 portes' => '3',
|
||||
'4 portes' => '4',
|
||||
'5 portes' => '5',
|
||||
'6 portes ou plus' => '6'
|
||||
)
|
||||
),
|
||||
'firsthand' => array(
|
||||
'name' => 'Première main',
|
||||
'type' => 'checkbox'
|
||||
),
|
||||
'seller' => array(
|
||||
'name' => 'Vendeur',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'' => '',
|
||||
'Particulier' => 'PART',
|
||||
'Professionel' => 'PRO'
|
||||
)
|
||||
),
|
||||
'sort' => array(
|
||||
'name' => 'Tri',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Prix (croissant)' => 'priceAsc',
|
||||
'Prix (décroissant)' => 'priceDesc',
|
||||
'Marque (croissant)' => 'makeAsc',
|
||||
'Marque (décroissant)' => 'makeDesc',
|
||||
'Kilométrage (croissant)' => 'mileageAsc',
|
||||
'Kilométrage (décroissant)' => 'mileageDesc',
|
||||
'Année (croissant)' => 'yearAsc',
|
||||
'Année (décroissant)' => 'yearDesc',
|
||||
'Département (croissant)' => 'visitPlaceAsc',
|
||||
'Département (décroissant)' => 'visitPlaceDesc'
|
||||
)
|
||||
),
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
// check data
|
||||
if(!empty($this->getInput('distance'))
|
||||
&& is_null($this->getInput('location'))) {
|
||||
returnClientError('You need a place ("CP ou département") to search arround.');
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'vertical' => $this->getInput('type'),
|
||||
'makesModelsCommercialNames' => $this->getInput('brand') . ':' . $this->getInput('model'),
|
||||
'versions' => $this->getInput('versions'),
|
||||
'categories' => $this->getInput('category'),
|
||||
'priceMin' => $this->getInput('pricemin'),
|
||||
'priceMax' => $this->getInput('pricemax'),
|
||||
'dptCp' => $this->getInput('location'),
|
||||
'distance' => $this->getInput('distance'),
|
||||
'regions' => $this->getInput('region'),
|
||||
'mileageMin' => $this->getInput('mileagemin'),
|
||||
'mileageMax' => $this->getInput('mileagemax'),
|
||||
'yearMin' => $this->getInput('yearmin'),
|
||||
'yearMax' => $this->getInput('yearmax'),
|
||||
'cubicMin' => $this->getInput('cubiccapacitymin'),
|
||||
'cubicMax' => $this->getInput('cubiccapacitymax'),
|
||||
'energies' => $this->getInput('fuel'),
|
||||
'firstHand' => $this->getInput('firsthand') ? 'true' : 'false',
|
||||
'gearbox' => $this->getInput('gearbox'),
|
||||
'doors' => $this->getInput('doors'),
|
||||
'sortBy' => $this->getInput('sort')
|
||||
);
|
||||
$url = self::URI . 'listing?' . http_build_query($params);
|
||||
$html = getSimpleHTMLDOM($url)
|
||||
or returnServerError('Could not request LaCentrale.');
|
||||
|
||||
foreach($html->find('.linkAd') as $element) {
|
||||
|
||||
$item = array();
|
||||
$item['uri'] = trim(self::URI, '/') . $element->href;
|
||||
$item['title'] = $element->find('.brandModel', 0)->plaintext;
|
||||
$item['sellerType'] = $element->find('.typeSeller', 0)->plaintext;
|
||||
$item['author'] = $item['sellerType'];
|
||||
$item['version'] = $element->find('.version', 0)->plaintext;
|
||||
$item['price'] = $element->find('.fieldPrice', 0)->plaintext;
|
||||
$item['year'] = $element->find('.fieldYear', 0)->plaintext;
|
||||
$item['mileage'] = $element->find('.fieldMileage', 0)->plaintext;
|
||||
$item['departement'] = str_replace(',', '', $element->find('.dptCont', 0)->plaintext);
|
||||
$item['thumbnail'] = $element->find('.imgContent img', 0)->src;
|
||||
$item['enclosures'] = array($item['thumbnail']);
|
||||
|
||||
$item['content'] = '
|
||||
<img src="' . $item['thumbnail'] . '">
|
||||
<br>Variation : ' . $item['version']
|
||||
. '<br>Prix : ' . $item['price']
|
||||
. '<br>Année : ' . $item['year']
|
||||
. '<br>Kilométrage : ' . $item['mileage']
|
||||
. '<br>Département : ' . $item['departement']
|
||||
. '<br>Type de vendeur : ' . $item['sellerType'];
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -356,6 +356,7 @@ class LeBonCoinBridge extends BridgeAbstract {
|
||||
$data = $this->buildRequestJson();
|
||||
|
||||
$header = array(
|
||||
'User-Agent: LBC;Android;Null;Null;Null;Null;Null;Null;Null;Null',
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($data),
|
||||
'api_key: ' . self::$LBC_API_KEY
|
||||
@@ -430,11 +431,11 @@ class LeBonCoinBridge extends BridgeAbstract {
|
||||
);
|
||||
|
||||
if($this->getInput('region') != '') {
|
||||
$requestJson->filters->location['regions'] = [$this->getInput('region')];
|
||||
$requestJson->filters->location['regions'] = array($this->getInput('region'));
|
||||
}
|
||||
|
||||
if($this->getInput('department') != '') {
|
||||
$requestJson->filters->location['departments'] = [$this->getInput('department')];
|
||||
$requestJson->filters->location['departments'] = array($this->getInput('department'));
|
||||
}
|
||||
|
||||
if($this->getInput('cities') != '') {
|
||||
@@ -466,7 +467,7 @@ class LeBonCoinBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
if($this->getInput('estate') != '') {
|
||||
$requestJson->filters->enums['real_estate_type'] = [$this->getInput('estate')];
|
||||
$requestJson->filters->enums['real_estate_type'] = array($this->getInput('estate'));
|
||||
}
|
||||
|
||||
if($this->getInput('roomsmin') != ''
|
||||
@@ -525,7 +526,7 @@ class LeBonCoinBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
if($this->getInput('fuel') != '') {
|
||||
$requestJson->filters->enums['fuel'] = [$this->getInput('fuel')];
|
||||
$requestJson->filters->enums['fuel'] = array($this->getInput('fuel'));
|
||||
}
|
||||
|
||||
$requestJson->limit = 30;
|
||||
|
22
bridges/ListverseBridge.php
Normal file
22
bridges/ListverseBridge.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
class ListverseBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'IceWreck';
|
||||
const NAME = 'Listverse Bridge';
|
||||
const URI = 'https://listverse.com/';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
const DESCRIPTION = 'RSS feed for Listverse';
|
||||
|
||||
public function collectData(){
|
||||
$this->collectExpandableDatas('https://listverse.com/feed/', 15);
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
// $articlePage gets the entire page's contents
|
||||
$articlePage = getSimpleHTMLDOM($newsItem->link);
|
||||
$article = $articlePage->find('#articlecontentonly', 0);
|
||||
$item['content'] = $article;
|
||||
return $item;
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ class MangareaderBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'logmanoriginal';
|
||||
const NAME = 'Mangareader Bridge';
|
||||
const URI = 'http://www.mangareader.net';
|
||||
const URI = 'https://www.mangareader.net';
|
||||
const CACHE_TIMEOUT = 10800; // 3h
|
||||
const DESCRIPTION = 'Returns the latest updates, popular mangas or manga updates (new chapters)';
|
||||
|
||||
|
@@ -30,29 +30,34 @@ class MediapartBridge extends FeedExpander {
|
||||
protected function parseItem($newsItem) {
|
||||
$item = parent::parseItem($newsItem);
|
||||
|
||||
// Enable single page mode?
|
||||
if ($this->getInput('single_page_mode') === true) {
|
||||
$item['uri'] .= '?onglet=full';
|
||||
}
|
||||
// Mediapart provide multiple type of contents.
|
||||
// We only process items relative to the newspaper
|
||||
// See issue #1292 - https://github.com/RSS-Bridge/rss-bridge/issues/1292
|
||||
if (strpos($item['uri'], self::URI . 'journal/') === 0) {
|
||||
// Enable single page mode?
|
||||
if ($this->getInput('single_page_mode') === true) {
|
||||
$item['uri'] .= '?onglet=full';
|
||||
}
|
||||
|
||||
// If a session cookie is defined, get the full article
|
||||
$mpsessid = $this->getInput('mpsessid');
|
||||
if (!empty($mpsessid)) {
|
||||
// Set the session cookie
|
||||
$opt = array();
|
||||
$opt[CURLOPT_COOKIE] = 'MPSESSID=' . $mpsessid;
|
||||
// If a session cookie is defined, get the full article
|
||||
$mpsessid = $this->getInput('mpsessid');
|
||||
if (!empty($mpsessid)) {
|
||||
// Set the session cookie
|
||||
$opt = array();
|
||||
$opt[CURLOPT_COOKIE] = 'MPSESSID=' . $mpsessid;
|
||||
|
||||
// Get the page
|
||||
$articlePage = getSimpleHTMLDOM(
|
||||
$newsItem->link . '?onglet=full',
|
||||
array(),
|
||||
$opt);
|
||||
// Get the page
|
||||
$articlePage = getSimpleHTMLDOM(
|
||||
$newsItem->link . '?onglet=full',
|
||||
array(),
|
||||
$opt);
|
||||
|
||||
// Extract the article content
|
||||
$content = $articlePage->find('div.content-article', 0)->innertext;
|
||||
$content = sanitize($content);
|
||||
$content = defaultLinkTo($content, static::URI);
|
||||
$item['content'] .= $content;
|
||||
// Extract the article content
|
||||
$content = $articlePage->find('div.content-article', 0)->innertext;
|
||||
$content = sanitize($content);
|
||||
$content = defaultLinkTo($content, static::URI);
|
||||
$item['content'] .= $content;
|
||||
}
|
||||
}
|
||||
|
||||
return $item;
|
||||
|
@@ -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;
|
||||
|
@@ -15,11 +15,11 @@ class N26Bridge extends BridgeAbstract
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$html = getSimpleHTMLDOM(self::URI . '/en-fr/blog-archive')
|
||||
$html = getSimpleHTMLDOM(self::URI . '/en-eu/blog-archive')
|
||||
or returnServerError('Error while downloading the website content');
|
||||
|
||||
foreach($html->find('div.ga') as $article) {
|
||||
$item = [];
|
||||
foreach($html->find('div[class="ag ah ai aj bs bt dx ea fo gx ie if ih ii ij ik s"]') as $article) {
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = self::URI . $article->find('h2 a', 0)->href;
|
||||
$item['title'] = $article->find('h2 a', 0)->plaintext;
|
||||
@@ -27,9 +27,9 @@ class N26Bridge extends BridgeAbstract
|
||||
$fullArticle = getSimpleHTMLDOM($item['uri'])
|
||||
or returnServerError('Error while downloading the full article');
|
||||
|
||||
$dateElement = $fullArticle->find('span[class="fk fl de ch fm by"]', 0);
|
||||
$dateElement = $fullArticle->find('time', 0);
|
||||
$item['timestamp'] = strtotime($dateElement->plaintext);
|
||||
$item['content'] = $fullArticle->find('main article', 0)->innertext;
|
||||
$item['content'] = $fullArticle->find('div[class="af ag ah ai an"]', 1)->innertext;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
60
bridges/NFLRUSBridge.php
Normal file
60
bridges/NFLRUSBridge.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
class NFLRUSBridge extends BridgeAbstract {
|
||||
|
||||
const NAME = 'NFLRUS';
|
||||
const URI = 'http://nflrus.ru/';
|
||||
const DESCRIPTION = 'Returns the recent articles published on nflrus.ru';
|
||||
const MAINTAINER = 'Maxim Shpak';
|
||||
|
||||
private function getEnglishMonth($month) {
|
||||
$months = array(
|
||||
'Января' => 'January',
|
||||
'Февраля' => 'February',
|
||||
'Марта' => 'March',
|
||||
'Апреля' => 'April',
|
||||
'Мая' => 'May',
|
||||
'Июня' => 'June',
|
||||
'Июля' => 'July',
|
||||
'Августа' => 'August',
|
||||
'Сентября' => 'September',
|
||||
'Октября' => 'October',
|
||||
'Ноября' => 'November',
|
||||
'Декабря' => 'December',
|
||||
);
|
||||
|
||||
if (isset($months[$month])) {
|
||||
return $months[$month];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function extractArticleTimestamp($article) {
|
||||
$time = $article->find('time', 0);
|
||||
if($time) {
|
||||
$timestring = trim($time->plaintext);
|
||||
$parts = explode(' ', $timestring);
|
||||
$month = $this->getEnglishMonth($parts[1]);
|
||||
if ($month) {
|
||||
$timestring = $parts[0] . ' ' . $month . ' ' . $parts[2];
|
||||
return strtotime($timestring);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
or returnServerError('Unable to get any articles from NFLRUS');
|
||||
$html = defaultLinkTo($html, self::URI);
|
||||
|
||||
foreach($html->find('article') as $article) {
|
||||
$item = array();
|
||||
$item['uri'] = $article->find('.b-article__title a', 0)->href;
|
||||
$item['title'] = $article->find('.b-article__title a', 0)->plaintext;
|
||||
$item['author'] = $article->find('.link-author', 0)->plaintext;
|
||||
$item['timestamp'] = $this->extractArticleTimestamp($article);
|
||||
$item['content'] = $article->find('div', 0)->innertext;
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
26
bridges/NYTBridge.php
Normal file
26
bridges/NYTBridge.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
class NYTBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'IceWreck';
|
||||
const NAME = 'New York Times Bridge';
|
||||
const URI = 'https://www.nytimes.com/';
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
const DESCRIPTION = 'RSS feed for the New York Times';
|
||||
|
||||
public function collectData(){
|
||||
$this->collectExpandableDatas('https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml', 15);
|
||||
}
|
||||
|
||||
protected function parseItem($newsItem){
|
||||
$item = parent::parseItem($newsItem);
|
||||
// $articlePage gets the entire page's contents
|
||||
$articlePage = getSimpleHTMLDOM($newsItem->link);
|
||||
// figure contain's the main article image
|
||||
$article = $articlePage->find('figure', 0);
|
||||
// p > css-exrw3m has the actual article
|
||||
foreach($articlePage->find('p.css-exrw3m') as $element)
|
||||
$article = $article . $element;
|
||||
$item['content'] = $article;
|
||||
return $item;
|
||||
}
|
||||
}
|
59
bridges/NewOnNetflixBridge.php
Normal file
59
bridges/NewOnNetflixBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ class NiceMatinBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'pit-fgfjiudghdf';
|
||||
const NAME = 'NiceMatin';
|
||||
const URI = 'http://www.nicematin.com/';
|
||||
const URI = 'https://www.nicematin.com/';
|
||||
const DESCRIPTION = 'Returns the 10 newest posts from NiceMatin (full text)';
|
||||
|
||||
public function collectData(){
|
||||
|
@@ -17,6 +17,15 @@ class NineGagBridge extends BridgeAbstract {
|
||||
'Fresh' => 'fresh',
|
||||
),
|
||||
),
|
||||
'video' => array(
|
||||
'name' => 'Filter Video',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'NotFiltred' => 'none',
|
||||
'VideoFiltred' => 'without',
|
||||
'VideoOnly' => 'only',
|
||||
),
|
||||
),
|
||||
'p' => array(
|
||||
'name' => 'Pages',
|
||||
'type' => 'number',
|
||||
@@ -121,13 +130,32 @@ class NineGagBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$item['uri'] = $post['url'];
|
||||
$item['title'] = $post['title'];
|
||||
$item['content'] = self::getContent($post);
|
||||
$item['categories'] = self::getCategories($post);
|
||||
$item['timestamp'] = self::getTimestamp($post);
|
||||
$AvoidElement = false;
|
||||
switch ($this->getInput('video')) {
|
||||
case 'without':
|
||||
if ($post['type'] === 'Animated') {
|
||||
$AvoidElement = true;
|
||||
}
|
||||
break;
|
||||
case 'only':
|
||||
echo $post['type'];
|
||||
if ($post['type'] !== 'Animated') {
|
||||
$AvoidElement = true;
|
||||
}
|
||||
break;
|
||||
case 'none': default:
|
||||
break;
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
if (!$AvoidElement) {
|
||||
$item['uri'] = $post['url'];
|
||||
$item['title'] = $post['title'];
|
||||
$item['content'] = self::getContent($post);
|
||||
$item['categories'] = self::getCategories($post);
|
||||
$item['timestamp'] = self::getTimestamp($post);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@ class NovelUpdatesBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'albirew';
|
||||
const NAME = 'Novel Updates';
|
||||
const URI = 'http://www.novelupdates.com/';
|
||||
const URI = 'https://www.novelupdates.com/';
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Returns releases from Novel Updates';
|
||||
const PARAMETERS = array( array(
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
class WhydBridge extends BridgeAbstract {
|
||||
class OpenwhydBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'kranack';
|
||||
const NAME = 'Whyd Bridge';
|
||||
const URI = 'http://www.whyd.com/';
|
||||
const NAME = 'Openwhyd Bridge';
|
||||
const URI = 'https://openwhyd.org';
|
||||
const CACHE_TIMEOUT = 600; // 10min
|
||||
const DESCRIPTION = 'Returns 10 newest music from user profile';
|
||||
|
||||
@@ -17,8 +17,7 @@ class WhydBridge extends BridgeAbstract {
|
||||
private $userName = '';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'assets/favicons/
|
||||
32-6b62a9f14d5e1a9213090d8f00f286bba3a6022381a76390d1d0926493b12593.png?v=6';
|
||||
return self::URI . '/images/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
@@ -26,11 +25,11 @@ class WhydBridge extends BridgeAbstract {
|
||||
if(strlen(preg_replace('/[^0-9a-f]/', '', $this->getInput('u'))) == 24) {
|
||||
// is input the userid ?
|
||||
$html = getSimpleHTMLDOM(
|
||||
self::URI . 'u/' . preg_replace('/[^0-9a-f]/', '', $this->getInput('u'))
|
||||
self::URI . '/u/' . preg_replace('/[^0-9a-f]/', '', $this->getInput('u'))
|
||||
) or returnServerError('No results for this query.');
|
||||
} else { // input may be the username
|
||||
$html = getSimpleHTMLDOM(
|
||||
self::URI . 'search?q=' . urlencode($this->getInput('u'))
|
||||
self::URI . '/search?q=' . urlencode($this->getInput('u'))
|
||||
) or returnServerError('No results for this query.');
|
||||
|
||||
for($j = 0; $j < 5; $j++) {
|
||||
@@ -57,6 +56,6 @@ class WhydBridge extends BridgeAbstract {
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return (!empty($this->userName) ? $this->userName . ' - ' : '') . 'Whyd Bridge';
|
||||
return (!empty($this->userName) ? $this->userName . ' - ' : '') . 'Openwhyd Bridge';
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ class ParuVenduImmoBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'polo2ro';
|
||||
const NAME = 'Paru Vendu Immobilier';
|
||||
const URI = 'http://www.paruvendu.fr';
|
||||
const URI = 'https://www.paruvendu.fr';
|
||||
const CACHE_TIMEOUT = 10800; // 3h
|
||||
const DESCRIPTION = 'Returns the ads from the first page of search result.';
|
||||
|
||||
|
203
bridges/PatreonBridge.php
Normal file
203
bridges/PatreonBridge.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
class PatreonBridge extends BridgeAbstract {
|
||||
const NAME = 'Patreon Bridge';
|
||||
const URI = 'https://www.patreon.com/';
|
||||
const CACHE_TIMEOUT = 300; // 5min
|
||||
const DESCRIPTION = 'Returns posts by creators on Patreon';
|
||||
const MAINTAINER = 'Roliga';
|
||||
const PARAMETERS = array( array(
|
||||
'creator' => array(
|
||||
'name' => 'Creator',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'title' => 'Creator name as seen in their page URL'
|
||||
)
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOMCached($this->getURI(), 86400)
|
||||
or returnServerError('Failed to load creator page at ' . $this->getURI());
|
||||
$regex = '#/api/campaigns/([0-9]+)#';
|
||||
if(preg_match($regex, $html->save(), $matches) > 0) {
|
||||
$campaign_id = $matches[1];
|
||||
} else {
|
||||
returnServerError('Could not find campaign ID');
|
||||
}
|
||||
|
||||
$query = array(
|
||||
'include' => implode(',', array(
|
||||
'user',
|
||||
'attachments',
|
||||
'user_defined_tags',
|
||||
//'campaign',
|
||||
//'poll.choices',
|
||||
//'poll.current_user_responses.user',
|
||||
//'poll.current_user_responses.choice',
|
||||
//'poll.current_user_responses.poll',
|
||||
//'access_rules.tier.null',
|
||||
//'images.null',
|
||||
//'audio.null'
|
||||
)),
|
||||
'fields' => array(
|
||||
'post' => implode(',', array(
|
||||
//'change_visibility_at',
|
||||
//'comment_count',
|
||||
'content',
|
||||
//'current_user_can_delete',
|
||||
//'current_user_can_view',
|
||||
//'current_user_has_liked',
|
||||
//'embed',
|
||||
'image',
|
||||
//'is_paid',
|
||||
//'like_count',
|
||||
//'min_cents_pledged_to_view',
|
||||
//'patreon_url',
|
||||
//'patron_count',
|
||||
//'pledge_url',
|
||||
//'post_file',
|
||||
//'post_metadata',
|
||||
//'post_type',
|
||||
'published_at',
|
||||
'teaser_text',
|
||||
//'thumbnail_url',
|
||||
'title',
|
||||
//'upgrade_url',
|
||||
'url',
|
||||
//'was_posted_by_campaign_owner'
|
||||
)),
|
||||
'user' => implode(',', array(
|
||||
//'image_url',
|
||||
'full_name',
|
||||
//'url'
|
||||
))
|
||||
),
|
||||
'filter' => array(
|
||||
'contains_exclusive_posts' => true,
|
||||
'is_draft' => false,
|
||||
'campaign_id' => $campaign_id
|
||||
),
|
||||
'sort' => '-published_at'
|
||||
);
|
||||
$posts = $this->apiGet('posts', $query);
|
||||
|
||||
foreach($posts->data as $post) {
|
||||
$item = array(
|
||||
'uri' => $post->attributes->url,
|
||||
'title' => $post->attributes->title,
|
||||
'timestamp' => $post->attributes->published_at,
|
||||
'content' => '',
|
||||
'uid' => 'patreon.com/' . $post->id
|
||||
);
|
||||
|
||||
$user = $this->findInclude($posts,
|
||||
'user',
|
||||
$post->relationships->user->data->id);
|
||||
$item['author'] = $user->full_name;
|
||||
|
||||
if(isset($post->attributes->image))
|
||||
$item['content'] .= '<p><a href="'
|
||||
. $post->attributes->url
|
||||
. '"><img src="'
|
||||
. $post->attributes->image->thumb_url
|
||||
. '" /></a></p>';
|
||||
|
||||
if(isset($post->attributes->content)) {
|
||||
$item['content'] .= $post->attributes->content;
|
||||
} elseif (isset($post->attributes->teaser_text)) {
|
||||
$item['content'] .= '<p>'
|
||||
. $post->attributes->teaser_text
|
||||
. '</p>';
|
||||
}
|
||||
|
||||
if(isset($post->relationships->user_defined_tags)) {
|
||||
$item['categories'] = array();
|
||||
foreach($post->relationships->user_defined_tags->data as $tag) {
|
||||
$attrs = $this->findInclude($posts, 'post_tag', $tag->id);
|
||||
$item['categories'][] = $attrs->value;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($post->relationships->attachments)) {
|
||||
$item['enclosures'] = array();
|
||||
foreach($post->relationships->attachments->data as $attachment) {
|
||||
$attrs = $this->findInclude($posts, 'attachment', $attachment->id);
|
||||
$item['enclosures'][] = $attrs->url;
|
||||
}
|
||||
}
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Searches the "included" array in an API response and returns attributes
|
||||
* for the first match.
|
||||
*/
|
||||
private function findInclude($data, $type, $id) {
|
||||
foreach($data->included as $include)
|
||||
if($include->type === $type && $include->id === $id)
|
||||
return $include->attributes;
|
||||
}
|
||||
|
||||
private function apiGet($endpoint, $query_data = array()) {
|
||||
$query_data['json-api-version'] = 1.0;
|
||||
$query_data['json-api-use-default-includes'] = 0;
|
||||
|
||||
$url = 'https://www.patreon.com/api/'
|
||||
. $endpoint
|
||||
. '?'
|
||||
. http_build_query($query_data);
|
||||
|
||||
/*
|
||||
* Accept-Language header and the CURL cipher list are for bypassing the
|
||||
* Cloudflare anti-bot protection on the Patreon API. If this ever breaks,
|
||||
* here are some other project that also deal with this:
|
||||
* https://github.com/mikf/gallery-dl/issues/342
|
||||
* https://github.com/daemionfox/patreon-feed/issues/7
|
||||
* https://www.patreondevelopers.com/t/api-returning-cloudflare-challenge/2025
|
||||
* https://github.com/splitbrain/patreon-rss/issues/4
|
||||
*/
|
||||
$header = array(
|
||||
'Accept-Language: en-US',
|
||||
'Content-Type: application/json'
|
||||
);
|
||||
$opts = array(
|
||||
CURLOPT_SSL_CIPHER_LIST => implode(':', array(
|
||||
'DEFAULT',
|
||||
'!DHE-RSA-CHACHA20-POLY1305'
|
||||
))
|
||||
);
|
||||
|
||||
$data = json_decode(getContents($url, $header, $opts))
|
||||
or returnServerError('API request to "' . $url . '" failed.');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('creator')))
|
||||
return $this->getInput('creator') . ' posts';
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('creator')))
|
||||
return self::URI . $this->getInput('creator');
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function detectParameters($url){
|
||||
$params = array();
|
||||
|
||||
// Matches e.g. https://www.patreon.com/SomeCreator
|
||||
$regex = '/^(https?:\/\/)?(www\.)?patreon\.com\/([^\/&?\n]+)/';
|
||||
if(preg_match($regex, $url, $matches) > 0) {
|
||||
$params['creator'] = urldecode($matches[3]);
|
||||
return $params;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
bridges/PhoronixBridge.php
Normal file
22
bridges/PhoronixBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ class PickyWallpapersBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'nel50n';
|
||||
const NAME = 'PickyWallpapers Bridge';
|
||||
const URI = 'http://www.pickywallpapers.com/';
|
||||
const URI = 'https://www.pickywallpapers.com/';
|
||||
const CACHE_TIMEOUT = 43200; // 12h
|
||||
const DESCRIPTION = 'Returns the latests wallpapers from PickyWallpapers';
|
||||
|
||||
|
@@ -32,6 +32,13 @@ class PikabuBridge extends BridgeAbstract {
|
||||
'required' => true
|
||||
),
|
||||
'filter' => self::PARAMETERS_FILTER
|
||||
),
|
||||
'По пользователю' => array(
|
||||
'user' => array(
|
||||
'name' => 'Пользователь',
|
||||
'exampleValue' => 'admin',
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -40,6 +47,8 @@ class PikabuBridge extends BridgeAbstract {
|
||||
public function getURI() {
|
||||
if ($this->getInput('tag')) {
|
||||
return self::URI . '/tag/' . rawurlencode($this->getInput('tag')) . '/' . rawurlencode($this->getInput('filter'));
|
||||
} else if ($this->getInput('user')) {
|
||||
return self::URI . '/@' . rawurlencode($this->getInput('user'));
|
||||
} else if ($this->getInput('community')) {
|
||||
$uri = self::URI . '/community/' . rawurlencode($this->getInput('community'));
|
||||
if ($this->getInput('filter') != 'hot') {
|
||||
@@ -101,6 +110,10 @@ class PikabuBridge extends BridgeAbstract {
|
||||
}
|
||||
}
|
||||
$img->outertext = '<img src="' . $src . '">';
|
||||
|
||||
// it is assumed, that img's parents are links to post itself
|
||||
// we don't need them
|
||||
$img->parent()->outertext = $img->outertext;
|
||||
}
|
||||
|
||||
$categories = array();
|
||||
@@ -116,7 +129,10 @@ class PikabuBridge extends BridgeAbstract {
|
||||
$item['categories'] = $categories;
|
||||
$item['author'] = $post->find('.user__nick', 0)->innertext;
|
||||
$item['title'] = $title->plaintext;
|
||||
$item['content'] = strip_tags(backgroundToImg($post->find('.story__content-inner', 0)->innertext), '<br><p><img>');
|
||||
$item['content'] = strip_tags(
|
||||
backgroundToImg($post->find('.story__content-inner', 0)->innertext),
|
||||
'<br><p><img><a>
|
||||
');
|
||||
$item['uri'] = $title->href;
|
||||
$item['timestamp'] = strtotime($time->getAttribute('datetime'));
|
||||
$this->items[] = $item;
|
||||
|
@@ -30,7 +30,7 @@ class PinterestBridge extends FeedExpander {
|
||||
|
||||
private function fixLowRes() {
|
||||
|
||||
$newitems = [];
|
||||
$newitems = array();
|
||||
$pattern = '/https\:\/\/i\.pinimg\.com\/[a-zA-Z0-9]*x\//';
|
||||
foreach($this->items as $item) {
|
||||
|
||||
|
88
bridges/PirateCommunityBridge.php
Normal file
88
bridges/PirateCommunityBridge.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
class PirateCommunityBridge extends BridgeAbstract {
|
||||
const NAME = 'Pirate-Community Bridge';
|
||||
const URI = 'https://raymanpc.com/';
|
||||
const CACHE_TIMEOUT = 300; // 5min
|
||||
const DESCRIPTION = 'Returns replies to topics';
|
||||
const MAINTAINER = 'Roliga';
|
||||
const PARAMETERS = array( array(
|
||||
't' => array(
|
||||
'name' => 'Topic ID',
|
||||
'type' => 'number',
|
||||
'title' => 'Topic ID from topic URL. If the URL contains t=12 the ID is 12.',
|
||||
'required' => true
|
||||
)));
|
||||
|
||||
private $feedName = '';
|
||||
|
||||
public function detectParameters($url){
|
||||
$parsed_url = parse_url($url);
|
||||
|
||||
if($parsed_url['host'] !== 'raymanpc.com')
|
||||
return null;
|
||||
|
||||
parse_str($parsed_url['query'], $parsed_query);
|
||||
|
||||
if($parsed_url['path'] === '/forum/viewtopic.php'
|
||||
&& array_key_exists('t', $parsed_query)) {
|
||||
return array('t' => $parsed_query['t']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
if(!empty($this->feedName))
|
||||
return $this->feedName;
|
||||
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('t'))) {
|
||||
return self::URI
|
||||
. 'forum/viewtopic.php?t='
|
||||
. $this->getInput('t')
|
||||
. '&sd=d'; // sort posts decending by ate so first page has latest posts
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Could not retrieve topic page at ' . $this->getURI());
|
||||
|
||||
$this->feedName = $html->find('head title', 0)->plaintext;
|
||||
|
||||
foreach($html->find('.post') as $reply) {
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $this->getURI()
|
||||
. $reply->find('h3 a', 0)->getAttribute('href');
|
||||
|
||||
$item['title'] = $reply->find('h3 a', 0)->plaintext;
|
||||
|
||||
$author_html = $reply->find('.author', 0);
|
||||
// author_html contains the timestamp as text directly inside it,
|
||||
// so delete all other child elements
|
||||
foreach($author_html->children as $child)
|
||||
$child->outertext = '';
|
||||
// Timestamps are always in UTC+1
|
||||
$item['timestamp'] = trim($author_html->innertext) . ' +01:00';
|
||||
|
||||
$item['author'] = $reply
|
||||
->find('.username, .username-coloured', 0)
|
||||
->plaintext;
|
||||
|
||||
$item['content'] = defaultLinkTo($reply->find('.content', 0)->innertext,
|
||||
$this->getURI());
|
||||
|
||||
$item['enclosures'] = array();
|
||||
foreach($reply->find('.attachbox img.postimage') as $img)
|
||||
$item['enclosures'][] = urljoin($this->getURI(), $img->src);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
67
bridges/PlantUMLReleasesBridge.php
Normal file
67
bridges/PlantUMLReleasesBridge.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PlantUML releases bridge showing latest releases content
|
||||
* @author nicolas-delsaux
|
||||
*
|
||||
*/
|
||||
class PlantUMLReleasesBridge extends BridgeAbstract
|
||||
{
|
||||
const MAINTAINER = 'Riduidel';
|
||||
|
||||
const NAME = 'PlantUML Releases';
|
||||
|
||||
const AUTHOR = 'PlantUML team';
|
||||
|
||||
// URI is no more valid, since we can address the whole gq galaxy
|
||||
const URI = 'http://plantuml.com/fr/changes';
|
||||
|
||||
const CACHE_TIMEOUT = 7200; // 2h
|
||||
const DESCRIPTION = 'PlantUML releases bridge, showing for each release the changelog';
|
||||
|
||||
const DEFAULT_DOMAIN = 'plantuml.com';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
));
|
||||
|
||||
const REPLACED_ATTRIBUTES = array(
|
||||
'href' => 'href',
|
||||
'src' => 'src',
|
||||
'data-original' => 'src'
|
||||
);
|
||||
|
||||
private function getDomain() {
|
||||
$domain = $this->getInput('domain');
|
||||
if (empty($domain))
|
||||
$domain = self::DEFAULT_DOMAIN;
|
||||
if (strpos($domain, '://') === false)
|
||||
$domain = 'https://' . $domain;
|
||||
return $domain;
|
||||
}
|
||||
|
||||
public function getURI()
|
||||
{
|
||||
return self::URI;
|
||||
}
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$html = getSimpleHTMLDOM($this->getURI()) or returnServerError('Could not request ' . $this->getURI());
|
||||
|
||||
// Since GQ don't want simple class scrapping, let's do it the hard way and ... discover content !
|
||||
$main = $html->find('div[id=root]', 0);
|
||||
foreach ($main->find('h2') as $release) {
|
||||
$item = array();
|
||||
$item['author'] = self::AUTHOR;
|
||||
$release_text = $release->innertext;
|
||||
if (preg_match('/(.+) \((.*)\)/', $release_text, $matches)) {
|
||||
$item['title'] = $matches[1];
|
||||
// And now, build the date from the date text
|
||||
$item['timestamp'] = strtotime($matches[2]);
|
||||
}
|
||||
$item['uri'] = $this->getURI();
|
||||
$item['content'] = $release->next_sibling ();
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
99
bridges/PornhubBridge.php
Normal file
99
bridges/PornhubBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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];
|
||||
|
||||
$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);
|
||||
|
||||
// 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();
|
||||
|
||||
$uri = $article->find('h3 a', 0)->href;
|
||||
$uri = 'https://rainbow6.ubisoft.com' . $uri;
|
||||
$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;
|
||||
}
|
||||
|
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
class ReadComicsBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'niawag';
|
||||
const NAME = 'Read Comics';
|
||||
const URI = 'http://www.readcomics.tv/';
|
||||
const DESCRIPTION = 'Enter the comics as they appear in the website uri,
|
||||
separated by semicolons, ex: good-comic-1;good-comic-2; ...';
|
||||
|
||||
const PARAMETERS = array( array(
|
||||
'q' => array(
|
||||
'name' => 'keywords, separated by semicolons',
|
||||
'exampleValue' => 'first list;second list;...',
|
||||
'required' => true
|
||||
),
|
||||
));
|
||||
|
||||
public function collectData(){
|
||||
|
||||
function parseDateTimestamp($element){
|
||||
$guessedDate = $element->find('span', 0)->plaintext;
|
||||
$guessedDate = strptime($guessedDate, '%m/%d/%Y');
|
||||
$timestamp = mktime(0, 0, 0, $guessedDate['tm_mon'] + 1, $guessedDate['tm_mday'], date('Y'));
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
$keywordsList = explode(';', $this->getInput('q'));
|
||||
foreach($keywordsList as $keywords) {
|
||||
$html = $this->getSimpleHTMLDOM(self::URI . 'comic/' . rawurlencode($keywords))
|
||||
or $this->returnServerError('Could not request readcomics.tv.');
|
||||
|
||||
foreach($html->find('li') as $element) {
|
||||
$item = array();
|
||||
$item['uri'] = $element->find('a.ch-name', 0)->href;
|
||||
$item['id'] = $item['uri'];
|
||||
$item['timestamp'] = parseDateTimestamp($element);
|
||||
$item['title'] = $element->find('a.ch-name', 0)->plaintext;
|
||||
if(isset($item['title']))
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
bridges/RedditBridge.php
Normal file
40
bridges/RedditBridge.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
class RedditBridge extends FeedExpander {
|
||||
|
||||
const MAINTAINER = 'leomaradan';
|
||||
const NAME = 'Reddit Bridge';
|
||||
const URI = 'https://www.reddit.com/';
|
||||
const DESCRIPTION = 'Reddit RSS Feed fixer';
|
||||
|
||||
const PARAMETERS = array(
|
||||
'single' => array(
|
||||
'r' => array(
|
||||
'name' => 'SubReddit',
|
||||
'required' => true,
|
||||
'exampleValue' => 'selfhosted',
|
||||
'title' => 'SubReddit name'
|
||||
)
|
||||
),
|
||||
'multi' => array(
|
||||
'rs' => array(
|
||||
'name' => 'SubReddits',
|
||||
'required' => true,
|
||||
'exampleValue' => 'selfhosted, php',
|
||||
'title' => 'SubReddit names, separated by commas'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function collectData(){
|
||||
|
||||
switch($this->queriedContext) {
|
||||
case 'single': $subreddits[] = $this->getInput('r'); break;
|
||||
case 'multi': $subreddits = explode(',', $this->getInput('rs')); break;
|
||||
}
|
||||
|
||||
foreach ($subreddits as $subreddit) {
|
||||
$name = trim($subreddit);
|
||||
$this->collectExpandableDatas("https://www.reddit.com/r/$name/.rss");
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,22 +9,6 @@ class Releases3DSBridge extends BridgeAbstract {
|
||||
|
||||
public function collectData(){
|
||||
|
||||
function typeToString($type){
|
||||
switch($type) {
|
||||
case 1: return '3DS Game';
|
||||
case 4: return 'eShop';
|
||||
default: return '??? (' . $type . ')';
|
||||
}
|
||||
}
|
||||
|
||||
function cardToString($card){
|
||||
switch($card) {
|
||||
case 1: return 'Regular (CARD1)';
|
||||
case 2: return 'NAND (CARD2)';
|
||||
default: return '??? (' . $card . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$dataUrl = self::URI . 'xml.php';
|
||||
$xml = getContents($dataUrl)
|
||||
or returnServerError('Could not request 3dsdb: ' . $dataUrl);
|
||||
@@ -95,8 +79,8 @@ class Releases3DSBridge extends BridgeAbstract {
|
||||
. '<br /><b>Release Name: </b>' . $releasename
|
||||
. '<br /><b>Trimmed size: </b>' . intval(intval($trimmedsize) / 1048576)
|
||||
. 'MB<br /><b>Firmware: </b>' . $firmware
|
||||
. '<br /><b>Type: </b>' . typeToString($type)
|
||||
. '<br /><b>Card: </b>' . cardToString($card)
|
||||
. '<br /><b>Type: </b>' . $this->typeToString($type)
|
||||
. '<br /><b>Card: </b>' . $this->cardToString($card)
|
||||
. '<br />';
|
||||
|
||||
//Build search links section to facilitate release search using search engines
|
||||
@@ -124,4 +108,20 @@ class Releases3DSBridge extends BridgeAbstract {
|
||||
$limit++;
|
||||
}
|
||||
}
|
||||
|
||||
private function typeToString($type){
|
||||
switch($type) {
|
||||
case 1: return '3DS Game';
|
||||
case 4: return 'eShop';
|
||||
default: return '??? (' . $type . ')';
|
||||
}
|
||||
}
|
||||
|
||||
private function cardToString($card){
|
||||
switch($card) {
|
||||
case 1: return 'Regular (CARD1)';
|
||||
case 2: return 'NAND (CARD2)';
|
||||
default: return '??? (' . $card . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ class ReporterreBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'nyutag';
|
||||
const NAME = 'Reporterre Bridge';
|
||||
const URI = 'http://www.reporterre.net/';
|
||||
const URI = 'https://www.reporterre.net/';
|
||||
const DESCRIPTION = 'Returns the newest articles.';
|
||||
|
||||
private function extractContent($url){
|
||||
|
@@ -25,7 +25,7 @@ class RoadAndTrackBridge extends BridgeAbstract {
|
||||
|
||||
private function fixImages($content) {
|
||||
|
||||
$enclosures = [];
|
||||
$enclosures = array();
|
||||
foreach($content->find('img') as $image) {
|
||||
$image->src = explode('?', $image->getAttribute('data-src'))[0];
|
||||
$enclosures[] = $image->src;
|
||||
|
107
bridges/RoosterTeethBridge.php
Normal file
107
bridges/RoosterTeethBridge.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ class Rule34Bridge extends GelbooruBridge {
|
||||
|
||||
const MAINTAINER = 'mitsukarenai';
|
||||
const NAME = 'Rule34';
|
||||
const URI = 'http://rule34.xxx/';
|
||||
const URI = 'https://rule34.xxx/';
|
||||
const DESCRIPTION = 'Returns images from given page';
|
||||
|
||||
const PIDBYPAGE = 50;
|
||||
|
@@ -5,6 +5,23 @@ class Rule34pahealBridge extends Shimmie2Bridge {
|
||||
|
||||
const MAINTAINER = 'mitsukarenai';
|
||||
const NAME = 'Rule34paheal';
|
||||
const URI = 'http://rule34.paheal.net/';
|
||||
const URI = 'https://rule34.paheal.net/';
|
||||
const DESCRIPTION = 'Returns images from given page';
|
||||
|
||||
protected function getItemFromElement($element){
|
||||
$item = array();
|
||||
$item['uri'] = $this->getURI() . $element->href;
|
||||
$item['id'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE));
|
||||
$item['timestamp'] = time();
|
||||
$thumbnailUri = $element->find('img', 0)->src;
|
||||
$item['tags'] = $element->getAttribute('data-tags');
|
||||
$item['title'] = $this->getName() . ' | ' . $item['id'];
|
||||
$item['content'] = '<a href="'
|
||||
. $item['uri']
|
||||
. '"><img src="'
|
||||
. $thumbnailUri
|
||||
. '" /></a><br>Tags: '
|
||||
. $item['tags'];
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ class SafebooruBridge extends GelbooruBridge {
|
||||
|
||||
const MAINTAINER = 'mitsukarenai';
|
||||
const NAME = 'Safebooru';
|
||||
const URI = 'http://safebooru.org/';
|
||||
const URI = 'https://safebooru.org/';
|
||||
const DESCRIPTION = 'Returns images from given page';
|
||||
|
||||
const PIDBYPAGE = 40;
|
||||
|
@@ -3,7 +3,7 @@ class ScmbBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'Astalaseven';
|
||||
const NAME = 'Se Coucher Moins Bête Bridge';
|
||||
const URI = 'http://secouchermoinsbete.fr';
|
||||
const URI = 'https://secouchermoinsbete.fr';
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Returns the newest anecdotes.';
|
||||
|
||||
|
@@ -3,7 +3,7 @@ class ScoopItBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'Pitchoule';
|
||||
const NAME = 'ScoopIt';
|
||||
const URI = 'http://www.scoop.it/';
|
||||
const URI = 'https://www.scoop.it/';
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Returns most recent results from ScoopIt.';
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user