diff --git a/.gitattributes b/.gitattributes
index 13ebe2ca..36544021 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -22,18 +22,24 @@
*.RTF diff=astextplain
# Ignore files in git archive (i.e. GitHub release builds)
+
## Docker
Dockerfile export-ignore
.dockerignore export-ignore
+
## Travis
.travis.yml export-ignore
+
## GitHub
.github/ export-ignore
+
## Git
.gitattributes export-ignore
.gitignore export-ignore
+
## Scalingo
scalingo.json export-ignore
+
## RSS-Bridge
phpunit.xml export-ignore
phpcs.xml export-ignore
@@ -42,8 +48,22 @@ tests/ export-ignore
cache/.gitkeep export-ignore
bridges/DemoBridge.php export-ignore
bridges/FeedExpanderExampleBridge.php export-ignore
+
## Composer
-composer.json export-ignore
-composer.lock export-ignore
+#
+# Keep the following lines commented out. Heroku does
+# not function if the composer files are ignored during
+# export. For more information see
+# https://github.com/rss-bridge/rss-bridge/issues/1165
+#
+# composer.json export-ignore
+# composer.lock export-ignore
+
## Heroku
-app.json export-ignore
+#
+# Keep the following line commented out. Heroku does
+# not function if app.json is ignored during export.
+# For more information see
+# https://github.com/rss-bridge/rss-bridge/issues/1165
+#
+# app.json export-ignore
diff --git a/Dockerfile b/Dockerfile
index 7d0611be..fa9979d6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,6 +6,8 @@ RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& apt-get --yes update && apt-get --yes install libxml2-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
COPY --chown=www-data:www-data ./ /app/
\ No newline at end of file
diff --git a/README.md b/README.md
index 95086c03..a9db8eaf 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-rss-bridge
+![RSS-Bridge](static/logo_600px.png)
===
-[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE) [![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg)](https://github.com/rss-bridge/rss-bridge/releases/latest) [![Debian Release](https://img.shields.io/badge/dynamic/json.svg?label=debian%20release&url=https%3A%2F%2Fsources.debian.org%2Fapi%2Fsrc%2Frss-bridge%2F&query=%24.versions%5B0%5D.version&colorB=blue)](https://tracker.debian.org/pkg/rss-bridge) [![Guix Release](https://img.shields.io/badge/guix%20release-unknown-light--gray.svg)](https://www.gnu.org/software/guix/packages/R/) [![Build Status](https://travis-ci.org/RSS-Bridge/rss-bridge.svg?branch=master)](https://travis-ci.org/RSS-Bridge/rss-bridge) [![Docker Build Status](https://img.shields.io/docker/build/rssbridge/rss-bridge.svg)](https://hub.docker.com/r/rssbridge/rss-bridge/)
+[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE) [![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg?logo=github)](https://github.com/rss-bridge/rss-bridge/releases/latest) [![Debian Release](https://img.shields.io/badge/dynamic/json.svg?logo=debian&label=debian%20release&url=https%3A%2F%2Fsources.debian.org%2Fapi%2Fsrc%2Frss-bridge%2F&query=%24.versions%5B0%5D.version&colorB=blue)](https://tracker.debian.org/pkg/rss-bridge) [![Guix Release](https://img.shields.io/badge/guix%20release-unknown-blue.svg)](https://www.gnu.org/software/guix/packages/R/) [![Build Status](https://travis-ci.org/RSS-Bridge/rss-bridge.svg?branch=master)](https://travis-ci.org/RSS-Bridge/rss-bridge) [![Docker Build Status](https://img.shields.io/docker/build/rssbridge/rss-bridge.svg?logo=docker)](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).
@@ -15,7 +15,6 @@ Supported sites/pages (examples)
* `DuckDuckGo`: Most recent results from [DuckDuckGo.com](https://duckduckgo.com/)
* `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/)
* `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr
-* `GooglePlus` : Most recent posts of user timeline
* `GoogleSearch` : Most recent results from Google Search
* `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances)
* `Instagram`: Most recent photos from an Instagram user
@@ -77,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
===
@@ -111,110 +110,128 @@ Use this script to generate the list automatically (using the GitHub API):
https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
-->
- * [16mhz](https://github.com/16mhz)
- * [adamchainz](https://github.com/adamchainz)
- * [Ahiles3005](https://github.com/Ahiles3005)
- * [Albirew](https://github.com/Albirew)
- * [aledeg](https://github.com/aledeg)
- * [alex73](https://github.com/alex73)
- * [alexAubin](https://github.com/alexAubin)
- * [AmauryCarrade](https://github.com/AmauryCarrade)
- * [ArthurHoaro](https://github.com/ArthurHoaro)
- * [Astalaseven](https://github.com/Astalaseven)
- * [Astyan-42](https://github.com/Astyan-42)
- * [az5he6ch](https://github.com/az5he6ch)
- * [azdkj532](https://github.com/azdkj532)
- * [b1nj](https://github.com/b1nj)
- * [benasse](https://github.com/benasse)
- * [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)
- * [da2x](https://github.com/da2x)
- * [Daiyousei](https://github.com/Daiyousei)
- * [disk0x](https://github.com/disk0x)
- * [DJCrashdummy](https://github.com/DJCrashdummy)
- * [Djuuu](https://github.com/Djuuu)
- * [DnAp](https://github.com/DnAp)
- * [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)
- * [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)
- * [GregThib](https://github.com/GregThib)
- * [griffaurel](https://github.com/griffaurel)
- * [Grummfy](https://github.com/Grummfy)
- * [hunhejj](https://github.com/hunhejj)
- * [j0k3r](https://github.com/j0k3r)
- * [JackNUMBER](https://github.com/JackNUMBER)
- * [jdigilio](https://github.com/jdigilio)
- * [JeremyRand](https://github.com/JeremyRand)
- * [Jocker666z](https://github.com/Jocker666z)
- * [killruana](https://github.com/killruana)
- * [klimplant](https://github.com/klimplant)
- * [kranack](https://github.com/kranack)
- * [kraoc](https://github.com/kraoc)
- * [l1n](https://github.com/l1n)
- * [laBecasse](https://github.com/laBecasse)
- * [lagaisse](https://github.com/lagaisse)
- * [lalannev](https://github.com/lalannev)
- * [ldidry](https://github.com/ldidry)
- * [Limero](https://github.com/Limero)
- * [LogMANOriginal](https://github.com/LogMANOriginal)
- * [lorenzos](https://github.com/lorenzos)
- * [m0zes](https://github.com/m0zes)
- * [matthewseal](https://github.com/matthewseal)
- * [mcbyte-it](https://github.com/mcbyte-it)
- * [mdemoss](https://github.com/mdemoss)
- * [melangue](https://github.com/melangue)
- * [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)
- * [ORelio](https://github.com/ORelio)
- * [PaulVayssiere](https://github.com/PaulVayssiere)
- * [pellaeon](https://github.com/pellaeon)
- * [Piranhaplant](https://github.com/Piranhaplant)
- * [pit-fgfjiudghdf](https://github.com/pit-fgfjiudghdf)
- * [pitchoule](https://github.com/pitchoule)
- * [pmaziere](https://github.com/pmaziere)
- * [Pofilo](https://github.com/Pofilo)
- * [prysme01](https://github.com/prysme01)
- * [quentinus95](https://github.com/quentinus95)
- * [regisenguehard](https://github.com/regisenguehard)
- * [Riduidel](https://github.com/Riduidel)
- * [rogerdc](https://github.com/rogerdc)
- * [Roliga](https://github.com/Roliga)
- * [sebsauvage](https://github.com/sebsauvage)
- * [somini](https://github.com/somini)
- * [squeek502](https://github.com/squeek502)
- * [Strubbl](https://github.com/Strubbl)
- * [sublimz](https://github.com/sublimz)
- * [sysadminstory](https://github.com/sysadminstory)
- * [tameroski](https://github.com/tameroski)
- * [teromene](https://github.com/teromene)
- * [thefranke](https://github.com/thefranke)
- * [TheRadialActive](https://github.com/TheRadialActive)
- * [triatic](https://github.com/triatic)
- * [VerifiedJoseph](https://github.com/VerifiedJoseph)
- * [WalterBarrett](https://github.com/WalterBarrett)
- * [wtuuju](https://github.com/wtuuju)
- * [xurxof](https://github.com/xurxof)
- * [yardenac](https://github.com/yardenac)
- * [ZeNairolf](https://github.com/ZeNairolf)
+* [16mhz](https://github.com/16mhz)
+* [adamchainz](https://github.com/adamchainz)
+* [Ahiles3005](https://github.com/Ahiles3005)
+* [Albirew](https://github.com/Albirew)
+* [aledeg](https://github.com/aledeg)
+* [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)
+* [az5he6ch](https://github.com/az5he6ch)
+* [azdkj532](https://github.com/azdkj532)
+* [b1nj](https://github.com/b1nj)
+* [benasse](https://github.com/benasse)
+* [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)
+* [jdigilio](https://github.com/jdigilio)
+* [JeremyRand](https://github.com/JeremyRand)
+* [Jocker666z](https://github.com/Jocker666z)
+* [johnnygroovy](https://github.com/johnnygroovy)
+* [killruana](https://github.com/killruana)
+* [klimplant](https://github.com/klimplant)
+* [kranack](https://github.com/kranack)
+* [kraoc](https://github.com/kraoc)
+* [l1n](https://github.com/l1n)
+* [laBecasse](https://github.com/laBecasse)
+* [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)
+* [mdemoss](https://github.com/mdemoss)
+* [melangue](https://github.com/melangue)
+* [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)
+* [Piranhaplant](https://github.com/Piranhaplant)
+* [pit-fgfjiudghdf](https://github.com/pit-fgfjiudghdf)
+* [pitchoule](https://github.com/pitchoule)
+* [pmaziere](https://github.com/pmaziere)
+* [Pofilo](https://github.com/Pofilo)
+* [prysme01](https://github.com/prysme01)
+* [quentinus95](https://github.com/quentinus95)
+* [regisenguehard](https://github.com/regisenguehard)
+* [Riduidel](https://github.com/Riduidel)
+* [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)
+* [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)
+* [wtuuju](https://github.com/wtuuju)
+* [xurxof](https://github.com/xurxof)
+* [yardenac](https://github.com/yardenac)
+* [ZeNairolf](https://github.com/ZeNairolf)
Licenses
===
diff --git a/actions/ConnectivityAction.php b/actions/ConnectivityAction.php
new file mode 100644
index 00000000..69272dda
--- /dev/null
+++ b/actions/ConnectivityAction.php
@@ -0,0 +1,136 @@
+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": "",
+ * "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;
+ }
+}
diff --git a/actions/DetectAction.php b/actions/DetectAction.php
index 2ad79a27..86605de4 100644
--- a/actions/DetectAction.php
+++ b/actions/DetectAction.php
@@ -19,13 +19,16 @@ class DetectAction extends ActionAbstract {
$format = $this->userData['format']
or returnClientError('You must specify a format!');
- foreach(Bridge::getBridgeNames() as $bridgeName) {
+ $bridgeFac = new \BridgeFactory();
+ $bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
- if(!Bridge::isWhitelisted($bridgeName)) {
+ foreach($bridgeFac->getBridgeNames() as $bridgeName) {
+
+ if(!$bridgeFac->isWhitelisted($bridgeName)) {
continue;
}
- $bridge = Bridge::create($bridgeName);
+ $bridge = $bridgeFac->create($bridgeName);
if($bridge === false) {
continue;
diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php
index a1b106f5..579630a1 100644
--- a/actions/DisplayAction.php
+++ b/actions/DisplayAction.php
@@ -12,26 +12,32 @@
*/
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;
$format = $this->userData['format']
or returnClientError('You must specify a format!');
- // DEPRECATED: 'nameFormat' scheme is replaced by 'name' in format parameter values
- // this is to keep compatibility until futher complete removal
- if(($pos = strpos($format, 'Format')) === (strlen($format) - strlen('Format'))) {
- $format = substr($format, 0, $pos);
- }
+ $bridgeFac = new \BridgeFactory();
+ $bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
// whitelist control
- if(!Bridge::isWhitelisted($bridge)) {
+ if(!$bridgeFac->isWhitelisted($bridge)) {
throw new \Exception('This bridge is not whitelisted', 401);
die;
}
// Data retrieval
- $bridge = Bridge::create($bridge);
+ $bridge = $bridgeFac->create($bridge);
$noproxy = array_key_exists('_noproxy', $this->userData)
&& filter_var($this->userData['_noproxy'], FILTER_VALIDATE_BOOLEAN);
@@ -85,7 +91,9 @@ class DisplayAction extends ActionAbstract {
);
// Initialize cache
- $cache = Cache::create(Configuration::getConfig('cache', 'type'));
+ $cacheFac = new CacheFactory();
+ $cacheFac->setWorkingDir(PATH_LIB_CACHES);
+ $cache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
$cache->setScope('');
$cache->purgeCache(86400); // 24 hours
$cache->setKey($cache_params);
@@ -147,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
@@ -216,7 +238,9 @@ class DisplayAction extends ActionAbstract {
// Data transformation
try {
- $format = Format::create($format);
+ $formatFac = new FormatFactory();
+ $formatFac->setWorkingDir(PATH_LIB_FORMATS);
+ $format = $formatFac->create($format);
$format->setItems($items);
$format->setExtraInfos($infos);
$format->setLastModified($cache->getTime());
diff --git a/actions/ListAction.php b/actions/ListAction.php
index 03e06119..92aef0e0 100644
--- a/actions/ListAction.php
+++ b/actions/ListAction.php
@@ -17,9 +17,12 @@ class ListAction extends ActionAbstract {
$list->bridges = array();
$list->total = 0;
- foreach(Bridge::getBridgeNames() as $bridgeName) {
+ $bridgeFac = new \BridgeFactory();
+ $bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
- $bridge = Bridge::create($bridgeName);
+ foreach($bridgeFac->getBridgeNames() as $bridgeName) {
+
+ $bridge = $bridgeFac->create($bridgeName);
if($bridge === false) { // Broken bridge, show as inactive
@@ -31,7 +34,7 @@ class ListAction extends ActionAbstract {
}
- $status = Bridge::isWhitelisted($bridgeName) ? 'active' : 'inactive';
+ $status = $bridgeFac->isWhitelisted($bridgeName) ? 'active' : 'inactive';
$list->bridges[$bridgeName] = array(
'status' => $status,
diff --git a/bridges/AmazonPriceTrackerBridge.php b/bridges/AmazonPriceTrackerBridge.php
index 6fa11c91..950178a7 100644
--- a/bridges/AmazonPriceTrackerBridge.php
+++ b/bridges/AmazonPriceTrackerBridge.php
@@ -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;
diff --git a/bridges/AppleAppStoreBridge.php b/bridges/AppleAppStoreBridge.php
new file mode 100644
index 00000000..c1403fe0
--- /dev/null
+++ b/bridges/AppleAppStoreBridge.php
@@ -0,0 +1,149 @@
+ 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;
+ }
+ }
+}
diff --git a/bridges/AppleMusicBridge.php b/bridges/AppleMusicBridge.php
index 5a4f40a4..30119777 100644
--- a/bridges/AppleMusicBridge.php
+++ b/bridges/AppleMusicBridge.php
@@ -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
diff --git a/bridges/AtmoNouvelleAquitaineBridge.php b/bridges/AtmoNouvelleAquitaineBridge.php
new file mode 100644
index 00000000..d395fa78
--- /dev/null
+++ b/bridges/AtmoNouvelleAquitaineBridge.php
@@ -0,0 +1,4638 @@
+ array(
+ 'name' => 'Choisir une ville',
+ 'type' => 'list',
+ 'values' => self::CITIES
+ )
+ ));
+ const CACHE_TIMEOUT = 7200;
+
+ private $dom;
+
+ private function getClosest($search, $arr) {
+ $closest = null;
+ foreach ($arr as $key => $value) {
+ if ($closest === null || abs((int)$search - $closest) > abs((int)$key - (int)$search)) {
+ $closest = (int)$key;
+ }
+ }
+ return $arr[$closest];
+ }
+
+ public function collectData() {
+ $uri = self::URI . $this->getInput('cities');
+
+ $html = getSimpleHTMLDOM($uri)
+ or returnServerError('Could not request ' . $uri);
+
+ $this->dom = $html->find('#block-system-main .city-prevision-map', 0);
+
+ $message = $this->getIndexMessage() . ' ' . $this->getQualityMessage();
+ $message .= ' ' . $this->getTomorrowTrendIndexMessage() . ' ' . $this->getTomorrowTrendQualityMessage();
+
+ $item['uri'] = $uri;
+ $today = date('d/m/Y');
+ $item['title'] = "Bulletin de l'air du $today pour la région Nouvelle Aquitaine.";
+ $item['title'] .= ' Retrouvez plus d\'informations en allant sur atmo-nouvelleaquitaine.org #QualiteAir.';
+ $item['author'] = 'floviolleau';
+ $item['content'] = $message;
+ $item['uid'] = hash('sha256', $item['title']);
+
+ $this->items[] = $item;
+ }
+
+ private function getIndex() {
+ $index = $this->dom->find('.indice', 0)->innertext;
+
+ if ($index == 'XX') {
+ return -1;
+ }
+
+ return $index;
+ }
+
+ private function getMaxIndexText() {
+ // will return '/100'
+ return $this->dom->find('.pourcent', 0)->innertext;
+ }
+
+ private function getQualityText($index, $indexes) {
+ if ($index == -1) {
+ if (array_key_exists('no-available', $indexes)) {
+ return $indexes['no-available'];
+ }
+
+ return 'Aucune donnée';
+ }
+
+ return $this->getClosest($index, $indexes);
+ }
+
+ private function getLegendIndexes() {
+ $rawIndexes = $this->dom->find('.prevision-legend .prevision-legend-label');
+ $indexes = array();
+ for ($i = 0; $i < count($rawIndexes); $i++) {
+ if ($rawIndexes[$i]->hasAttribute('data-color')) {
+ $indexes[$rawIndexes[$i]->getAttribute('data-color')] = $rawIndexes[$i]->innertext;
+ }
+ }
+
+ return $indexes;
+ }
+
+ private function getTomorrowTrendIndex() {
+ $tomorrowTrendDomNode = $this->dom
+ ->find('.day-controls.raster-controls .list-raster-controls .raster-control', 2);
+ $tomorrowTrendIndexNode = null;
+
+ if ($tomorrowTrendDomNode) {
+ $tomorrowTrendIndexNode = $tomorrowTrendDomNode->find('.raster-control-link', 0);
+ }
+
+ if ($tomorrowTrendIndexNode && $tomorrowTrendIndexNode->hasAttribute('data-index')) {
+ $tomorrowTrendIndex = $tomorrowTrendIndexNode->getAttribute('data-index');
+ } else {
+ return -1;
+ }
+
+ return $tomorrowTrendIndex;
+ }
+
+ private function getTomorrowTrendQualityText($trendIndex, $indexes) {
+ if ($trendIndex == -1) {
+ if (array_key_exists('no-available', $indexes)) {
+ return $indexes['no-available'];
+ }
+
+ return 'Aucune donnée';
+ }
+
+ return $this->getClosest($trendIndex, $indexes);
+ }
+
+ private function getIndexMessage() {
+ $index = $this->getIndex();
+ $maxIndexText = $this->getMaxIndexText();
+
+ if ($index == -1) {
+ return 'Aucune donnée pour l\'indice.';
+ }
+
+ return "L'indice d'aujourd'hui est $index$maxIndexText.";
+ }
+
+ private function getQualityMessage() {
+ $index = $index = $this->getIndex();
+ $indexes = $this->getLegendIndexes();
+ $quality = $this->getQualityText($index, $indexes);
+
+ if ($index == -1) {
+ return 'Aucune donnée pour la qualité de l\'air.';
+ }
+
+ return "La qualité de l'air est $quality.";
+ }
+
+ private function getTomorrowTrendIndexMessage() {
+ $trendIndex = $this->getTomorrowTrendIndex();
+ $maxIndexText = $this->getMaxIndexText();
+
+ if ($trendIndex == -1) {
+ return 'Aucune donnée pour l\'indice prévu demain.';
+ }
+
+ return "L'indice prévu pour demain est $trendIndex$maxIndexText.";
+ }
+
+ private function getTomorrowTrendQualityMessage() {
+ $trendIndex = $this->getTomorrowTrendIndex();
+ $indexes = $this->getLegendIndexes();
+ $trendQuality = $this->getTomorrowTrendQualityText($trendIndex, $indexes);
+
+ if ($trendIndex == -1) {
+ return 'Aucune donnée pour la qualité de l\'air de demain.';
+ }
+ return "La qualite de l'air pour demain sera $trendQuality.";
+ }
+
+ const CITIES = array(
+ 'Aast (64460)' => '64001',
+ 'Abère (64160)' => '64002',
+ 'Abidos (64150)' => '64003',
+ 'Abitain (64390)' => '64004',
+ 'Abjat-sur-Bandiat (24300)' => '24001',
+ 'Abos (64360)' => '64005',
+ 'Abzac (16500)' => '16001',
+ 'Abzac (33230)' => '33001',
+ 'Accous (64490)' => '64006',
+ 'Adilly (79200)' => '79002',
+ 'Adriers (86430)' => '86001',
+ 'Affieux (19260)' => '19001',
+ 'Agen (47000)' => '47001',
+ 'Agmé (47350)' => '47002',
+ 'Agnac (47800)' => '47003',
+ 'Agnos (64400)' => '64007',
+ 'Agonac (24460)' => '24002',
+ 'Agris (16110)' => '16003',
+ 'Agudelle (17500)' => '17002',
+ 'Ahaxe-Alciette-Bascassan (64220)' => '64008',
+ 'Ahetze (64210)' => '64009',
+ 'Ahun (23150)' => '23001',
+ 'Aïcirits-Camou-Suhast (64120)' => '64010',
+ 'Aiffres (79230)' => '79003',
+ 'Aignes-et-Puypéroux (16190)' => '16004',
+ 'Aigonnay (79370)' => '79004',
+ 'Aigre (16140)' => '16005',
+ 'Aigrefeuille-d\'Aunis (17290)' => '17003',
+ 'Aiguillon (47190)' => '47004',
+ 'Aillas (33124)' => '33002',
+ 'Aincille (64220)' => '64011',
+ 'Ainharp (64130)' => '64012',
+ 'Ainhice-Mongelos (64220)' => '64013',
+ 'Ainhoa (64250)' => '64014',
+ 'Aire-sur-l\'Adour (40800)' => '40001',
+ 'Airvault (79600)' => '79005',
+ 'Aix (19200)' => '19002',
+ 'Aixe-sur-Vienne (87700)' => '87001',
+ 'Ajain (23380)' => '23002',
+ 'Ajat (24210)' => '24004',
+ 'Albignac (19190)' => '19003',
+ 'Albussac (19380)' => '19004',
+ 'Alçay-Alçabéhéty-Sunharette (64470)' => '64015',
+ 'Aldudes (64430)' => '64016',
+ 'Allas-Bocage (17150)' => '17005',
+ 'Allas-Champagne (17500)' => '17006',
+ 'Allas-les-Mines (24220)' => '24006',
+ 'Allassac (19240)' => '19005',
+ 'Allemans (24600)' => '24007',
+ 'Allemans-du-Dropt (47800)' => '47005',
+ 'Alles-sur-Dordogne (24480)' => '24005',
+ 'Alleyrat (19200)' => '19006',
+ 'Alleyrat (23200)' => '23003',
+ 'Allez-et-Cazeneuve (47110)' => '47006',
+ 'Allonne (79130)' => '79007',
+ 'Allons (47420)' => '47007',
+ 'Alloue (16490)' => '16007',
+ 'Alos-Sibas-Abense (64470)' => '64017',
+ 'Altillac (19120)' => '19007',
+ 'Amailloux (79350)' => '79008',
+ 'Ambarès-et-Lagrave (33440)' => '33003',
+ 'Ambazac (87240)' => '87002',
+ 'Ambérac (16140)' => '16008',
+ 'Ambernac (16490)' => '16009',
+ 'Amberre (86110)' => '86002',
+ 'Ambès (33810)' => '33004',
+ 'Ambleville (16300)' => '16010',
+ 'Ambrugeat (19250)' => '19008',
+ 'Ambrus (47160)' => '47008',
+ 'Amendeuix-Oneix (64120)' => '64018',
+ 'Amorots-Succos (64120)' => '64019',
+ 'Amou (40330)' => '40002',
+ 'Amuré (79210)' => '79009',
+ 'Anais (16560)' => '16011',
+ 'Anais (17540)' => '17007',
+ 'Ance (64570)' => '64020',
+ 'Anché (86700)' => '86003',
+ 'Andernos-les-Bains (33510)' => '33005',
+ 'Andilly (17230)' => '17008',
+ 'Andiran (47170)' => '47009',
+ 'Andoins (64420)' => '64021',
+ 'Andrein (64390)' => '64022',
+ 'Angaïs (64510)' => '64023',
+ 'Angeac-Champagne (16130)' => '16012',
+ 'Angeac-Charente (16120)' => '16013',
+ 'Angeduc (16300)' => '16014',
+ 'Anglade (33390)' => '33006',
+ 'Angles-sur-l\'Anglin (86260)' => '86004',
+ 'Anglet (64600)' => '64024',
+ 'Angliers (17540)' => '17009',
+ 'Angliers (86330)' => '86005',
+ 'Angoisse (24270)' => '24008',
+ 'Angoulême (16000)' => '16015',
+ 'Angoulins (17690)' => '17010',
+ 'Angoumé (40990)' => '40003',
+ 'Angous (64190)' => '64025',
+ 'Angresse (40150)' => '40004',
+ 'Anhaux (64220)' => '64026',
+ 'Anlhiac (24160)' => '24009',
+ 'Annepont (17350)' => '17011',
+ 'Annesse-et-Beaulieu (24430)' => '24010',
+ 'Annezay (17380)' => '17012',
+ 'Anos (64160)' => '64027',
+ 'Anoye (64350)' => '64028',
+ 'Ansac-sur-Vienne (16500)' => '16016',
+ 'Antagnac (47700)' => '47010',
+ 'Antezant-la-Chapelle (17400)' => '17013',
+ 'Anthé (47370)' => '47011',
+ 'Antigny (86310)' => '86006',
+ 'Antonne-et-Trigonant (24420)' => '24011',
+ 'Antran (86100)' => '86007',
+ 'Anville (16170)' => '16017',
+ 'Anzême (23000)' => '23004',
+ 'Anzex (47700)' => '47012',
+ 'Aramits (64570)' => '64029',
+ 'Arancou (64270)' => '64031',
+ 'Araujuzon (64190)' => '64032',
+ 'Araux (64190)' => '64033',
+ 'Arbanats (33640)' => '33007',
+ 'Arbérats-Sillègue (64120)' => '64034',
+ 'Arbis (33760)' => '33008',
+ 'Arbonne (64210)' => '64035',
+ 'Arboucave (40320)' => '40005',
+ 'Arbouet-Sussaute (64120)' => '64036',
+ 'Arbus (64230)' => '64037',
+ 'Arcachon (33120)' => '33009',
+ 'Arçais (79210)' => '79010',
+ 'Arcangues (64200)' => '64038',
+ 'Arçay (86200)' => '86008',
+ 'Arces (17120)' => '17015',
+ 'Archiac (17520)' => '17016',
+ 'Archignac (24590)' => '24012',
+ 'Archigny (86210)' => '86009',
+ 'Archingeay (17380)' => '17017',
+ 'Arcins (33460)' => '33010',
+ 'Ardilleux (79110)' => '79011',
+ 'Ardillières (17290)' => '17018',
+ 'Ardin (79160)' => '79012',
+ 'Aren (64400)' => '64039',
+ 'Arengosse (40110)' => '40006',
+ 'Arès (33740)' => '33011',
+ 'Aressy (64320)' => '64041',
+ 'Arette (64570)' => '64040',
+ 'Arfeuille-Châtain (23700)' => '23005',
+ 'Argagnon (64300)' => '64042',
+ 'Argelos (40700)' => '40007',
+ 'Argelos (64450)' => '64043',
+ 'Argelouse (40430)' => '40008',
+ 'Argentat (19400)' => '19010',
+ 'Argenton (47250)' => '47013',
+ 'Argenton-l\'Église (79290)' => '79014',
+ 'Argentonnay (79150)' => '79013',
+ 'Arget (64410)' => '64044',
+ 'Arhansus (64120)' => '64045',
+ 'Arjuzanx (40110)' => '40009',
+ 'Armendarits (64640)' => '64046',
+ 'Armillac (47800)' => '47014',
+ 'Arnac-la-Poste (87160)' => '87003',
+ 'Arnac-Pompadour (19230)' => '19011',
+ 'Arnéguy (64220)' => '64047',
+ 'Arnos (64370)' => '64048',
+ 'Aroue-Ithorots-Olhaïby (64120)' => '64049',
+ 'Arrast-Larrebieu (64130)' => '64050',
+ 'Arraute-Charritte (64120)' => '64051',
+ 'Arrènes (23210)' => '23006',
+ 'Arricau-Bordes (64350)' => '64052',
+ 'Arrien (64420)' => '64053',
+ 'Arros-de-Nay (64800)' => '64054',
+ 'Arrosès (64350)' => '64056',
+ 'Ars (16130)' => '16018',
+ 'Ars (23480)' => '23007',
+ 'Ars-en-Ré (17590)' => '17019',
+ 'Arsac (33460)' => '33012',
+ 'Arsague (40330)' => '40011',
+ 'Artassenx (40090)' => '40012',
+ 'Arthenac (17520)' => '17020',
+ 'Arthez-d\'Armagnac (40190)' => '40013',
+ 'Arthez-d\'Asson (64800)' => '64058',
+ 'Arthez-de-Béarn (64370)' => '64057',
+ 'Artigueloutan (64420)' => '64059',
+ 'Artiguelouve (64230)' => '64060',
+ 'Artigues-près-Bordeaux (33370)' => '33013',
+ 'Artix (64170)' => '64061',
+ 'Arudy (64260)' => '64062',
+ 'Arue (40120)' => '40014',
+ 'Arvert (17530)' => '17021',
+ 'Arveyres (33500)' => '33015',
+ 'Arx (40310)' => '40015',
+ 'Arzacq-Arraziguet (64410)' => '64063',
+ 'Asasp-Arros (64660)' => '64064',
+ 'Ascain (64310)' => '64065',
+ 'Ascarat (64220)' => '64066',
+ 'Aslonnes (86340)' => '86010',
+ 'Asnières-en-Poitou (79170)' => '79015',
+ 'Asnières-la-Giraud (17400)' => '17022',
+ 'Asnières-sur-Blour (86430)' => '86011',
+ 'Asnières-sur-Nouère (16290)' => '16019',
+ 'Asnois (86250)' => '86012',
+ 'Asques (33240)' => '33016',
+ 'Assais-les-Jumeaux (79600)' => '79016',
+ 'Assat (64510)' => '64067',
+ 'Asson (64800)' => '64068',
+ 'Astaffort (47220)' => '47015',
+ 'Astaillac (19120)' => '19012',
+ 'Aste-Béon (64260)' => '64069',
+ 'Astis (64450)' => '64070',
+ 'Athos-Aspis (64390)' => '64071',
+ 'Aubagnan (40700)' => '40016',
+ 'Aubas (24290)' => '24014',
+ 'Aubazines (19190)' => '19013',
+ 'Aubertin (64290)' => '64072',
+ 'Aubeterre-sur-Dronne (16390)' => '16020',
+ 'Aubiac (33430)' => '33017',
+ 'Aubiac (47310)' => '47016',
+ 'Aubigné (79110)' => '79018',
+ 'Aubigny (79390)' => '79019',
+ 'Aubin (64230)' => '64073',
+ 'Aubous (64330)' => '64074',
+ 'Aubusson (23200)' => '23008',
+ 'Audaux (64190)' => '64075',
+ 'Audenge (33980)' => '33019',
+ 'Audignon (40500)' => '40017',
+ 'Audon (40400)' => '40018',
+ 'Audrix (24260)' => '24015',
+ 'Auga (64450)' => '64077',
+ 'Auge (23170)' => '23009',
+ 'Augé (79400)' => '79020',
+ 'Auge-Saint-Médard (16170)' => '16339',
+ 'Augères (23210)' => '23010',
+ 'Augignac (24300)' => '24016',
+ 'Augne (87120)' => '87004',
+ 'Aujac (17770)' => '17023',
+ 'Aulnay (17470)' => '17024',
+ 'Aulnay (86330)' => '86013',
+ 'Aulon (23210)' => '23011',
+ 'Aumagne (17770)' => '17025',
+ 'Aunac (16460)' => '16023',
+ 'Auradou (47140)' => '47017',
+ 'Aureil (87220)' => '87005',
+ 'Aureilhan (40200)' => '40019',
+ 'Auriac (19220)' => '19014',
+ 'Auriac (64450)' => '64078',
+ 'Auriac-du-Périgord (24290)' => '24018',
+ 'Auriac-sur-Dropt (47120)' => '47018',
+ 'Auriat (23400)' => '23012',
+ 'Aurice (40500)' => '40020',
+ 'Auriolles (33790)' => '33020',
+ 'Aurions-Idernes (64350)' => '64079',
+ 'Auros (33124)' => '33021',
+ 'Aussac-Vadalle (16560)' => '16024',
+ 'Aussevielle (64230)' => '64080',
+ 'Aussurucq (64130)' => '64081',
+ 'Auterrive (64270)' => '64082',
+ 'Autevielle-Saint-Martin-Bideren (64390)' => '64083',
+ 'Authon-Ébéon (17770)' => '17026',
+ 'Auzances (23700)' => '23013',
+ 'Availles-en-Châtellerault (86530)' => '86014',
+ 'Availles-Limouzine (86460)' => '86015',
+ 'Availles-Thouarsais (79600)' => '79022',
+ 'Avanton (86170)' => '86016',
+ 'Avensan (33480)' => '33022',
+ 'Avon (79800)' => '79023',
+ 'Avy (17800)' => '17027',
+ 'Aydie (64330)' => '64084',
+ 'Aydius (64490)' => '64085',
+ 'Ayen (19310)' => '19015',
+ 'Ayguemorte-les-Graves (33640)' => '33023',
+ 'Ayherre (64240)' => '64086',
+ 'Ayron (86190)' => '86017',
+ 'Aytré (17440)' => '17028',
+ 'Azat-Châtenet (23210)' => '23014',
+ 'Azat-le-Ris (87360)' => '87006',
+ 'Azay-le-Brûlé (79400)' => '79024',
+ 'Azay-sur-Thouet (79130)' => '79025',
+ 'Azerables (23160)' => '23015',
+ 'Azerat (24210)' => '24019',
+ 'Azur (40140)' => '40021',
+ 'Badefols-d\'Ans (24390)' => '24021',
+ 'Badefols-sur-Dordogne (24150)' => '24022',
+ 'Bagas (33190)' => '33024',
+ 'Bagnizeau (17160)' => '17029',
+ 'Bahus-Soubiran (40320)' => '40022',
+ 'Baigneaux (33760)' => '33025',
+ 'Baignes-Sainte-Radegonde (16360)' => '16025',
+ 'Baigts (40380)' => '40023',
+ 'Baigts-de-Béarn (64300)' => '64087',
+ 'Bajamont (47480)' => '47019',
+ 'Balansun (64300)' => '64088',
+ 'Balanzac (17600)' => '17030',
+ 'Baleix (64460)' => '64089',
+ 'Baleyssagues (47120)' => '47020',
+ 'Baliracq-Maumusson (64330)' => '64090',
+ 'Baliros (64510)' => '64091',
+ 'Balizac (33730)' => '33026',
+ 'Ballans (17160)' => '17031',
+ 'Balledent (87290)' => '87007',
+ 'Ballon (17290)' => '17032',
+ 'Balzac (16430)' => '16026',
+ 'Banca (64430)' => '64092',
+ 'Baneuil (24150)' => '24023',
+ 'Banize (23120)' => '23016',
+ 'Banos (40500)' => '40024',
+ 'Bar (19800)' => '19016',
+ 'Barbaste (47230)' => '47021',
+ 'Barbezières (16140)' => '16027',
+ 'Barbezieux-Saint-Hilaire (16300)' => '16028',
+ 'Barcus (64130)' => '64093',
+ 'Bardenac (16210)' => '16029',
+ 'Bardos (64520)' => '64094',
+ 'Bardou (24560)' => '24024',
+ 'Barie (33190)' => '33027',
+ 'Barinque (64160)' => '64095',
+ 'Baron (33750)' => '33028',
+ 'Barraute-Camu (64390)' => '64096',
+ 'Barret (16300)' => '16030',
+ 'Barro (16700)' => '16031',
+ 'Bars (24210)' => '24025',
+ 'Barsac (33720)' => '33030',
+ 'Barzan (17120)' => '17034',
+ 'Barzun (64530)' => '64097',
+ 'Bas-Mauco (40500)' => '40026',
+ 'Bascons (40090)' => '40025',
+ 'Bassac (16120)' => '16032',
+ 'Bassanne (33190)' => '33031',
+ 'Bassens (33530)' => '33032',
+ 'Bassercles (40700)' => '40027',
+ 'Basses (86200)' => '86018',
+ 'Bassignac-le-Bas (19430)' => '19017',
+ 'Bassignac-le-Haut (19220)' => '19018',
+ 'Bassillac (24330)' => '24026',
+ 'Bassillon-Vauzé (64350)' => '64098',
+ 'Bassussarry (64200)' => '64100',
+ 'Bastanès (64190)' => '64099',
+ 'Bastennes (40360)' => '40028',
+ 'Basville (23260)' => '23017',
+ 'Bats (40320)' => '40029',
+ 'Baudignan (40310)' => '40030',
+ 'Baudreix (64800)' => '64101',
+ 'Baurech (33880)' => '33033',
+ 'Bayac (24150)' => '24027',
+ 'Bayas (33230)' => '33034',
+ 'Bayers (16460)' => '16033',
+ 'Bayon-sur-Gironde (33710)' => '33035',
+ 'Bayonne (64100)' => '64102',
+ 'Bazac (16210)' => '16034',
+ 'Bazas (33430)' => '33036',
+ 'Bazauges (17490)' => '17035',
+ 'Bazelat (23160)' => '23018',
+ 'Bazens (47130)' => '47022',
+ 'Beaugas (47290)' => '47023',
+ 'Beaugeay (17620)' => '17036',
+ 'Beaulieu-sous-Parthenay (79420)' => '79029',
+ 'Beaulieu-sur-Dordogne (19120)' => '19019',
+ 'Beaulieu-sur-Sonnette (16450)' => '16035',
+ 'Beaumont (19390)' => '19020',
+ 'Beaumont (86490)' => '86019',
+ 'Beaumont-du-Lac (87120)' => '87009',
+ 'Beaumontois en Périgord (24440)' => '24028',
+ 'Beaupouyet (24400)' => '24029',
+ 'Beaupuy (47200)' => '47024',
+ 'Beauregard-de-Terrasson (24120)' => '24030',
+ 'Beauregard-et-Bassac (24140)' => '24031',
+ 'Beauronne (24400)' => '24032',
+ 'Beaussac (24340)' => '24033',
+ 'Beaussais-Vitré (79370)' => '79030',
+ 'Beautiran (33640)' => '33037',
+ 'Beauvais-sur-Matha (17490)' => '17037',
+ 'Beauville (47470)' => '47025',
+ 'Beauvoir-sur-Niort (79360)' => '79031',
+ 'Beauziac (47700)' => '47026',
+ 'Béceleuf (79160)' => '79032',
+ 'Bécheresse (16250)' => '16036',
+ 'Bédeille (64460)' => '64103',
+ 'Bedenac (17210)' => '17038',
+ 'Bedous (64490)' => '64104',
+ 'Bégaar (40400)' => '40031',
+ 'Bégadan (33340)' => '33038',
+ 'Bègles (33130)' => '33039',
+ 'Béguey (33410)' => '33040',
+ 'Béguios (64120)' => '64105',
+ 'Béhasque-Lapiste (64120)' => '64106',
+ 'Béhorléguy (64220)' => '64107',
+ 'Beissat (23260)' => '23019',
+ 'Beleymas (24140)' => '24034',
+ 'Belhade (40410)' => '40032',
+ 'Belin-Béliet (33830)' => '33042',
+ 'Bélis (40120)' => '40033',
+ 'Bellac (87300)' => '87011',
+ 'Bellebat (33760)' => '33043',
+ 'Bellechassagne (19290)' => '19021',
+ 'Bellefond (33760)' => '33044',
+ 'Bellefonds (86210)' => '86020',
+ 'Bellegarde-en-Marche (23190)' => '23020',
+ 'Belleville (79360)' => '79033',
+ 'Bellocq (64270)' => '64108',
+ 'Bellon (16210)' => '16037',
+ 'Belluire (17800)' => '17039',
+ 'Bélus (40300)' => '40034',
+ 'Belvès-de-Castillon (33350)' => '33045',
+ 'Benassay (86470)' => '86021',
+ 'Benayes (19510)' => '19022',
+ 'Bénéjacq (64800)' => '64109',
+ 'Bénesse-lès-Dax (40180)' => '40035',
+ 'Bénesse-Maremne (40230)' => '40036',
+ 'Benest (16350)' => '16038',
+ 'Bénévent-l\'Abbaye (23210)' => '23021',
+ 'Benon (17170)' => '17041',
+ 'Benquet (40280)' => '40037',
+ 'Bentayou-Sérée (64460)' => '64111',
+ 'Béost (64440)' => '64110',
+ 'Berbiguières (24220)' => '24036',
+ 'Bercloux (17770)' => '17042',
+ 'Bérenx (64300)' => '64112',
+ 'Bergerac (24100)' => '24037',
+ 'Bergouey (40250)' => '40038',
+ 'Bergouey-Viellenave (64270)' => '64113',
+ 'Bernac (16700)' => '16039',
+ 'Bernadets (64160)' => '64114',
+ 'Bernay-Saint-Martin (17330)' => '17043',
+ 'Berneuil (16480)' => '16040',
+ 'Berneuil (17460)' => '17044',
+ 'Berneuil (87300)' => '87012',
+ 'Bernos-Beaulac (33430)' => '33046',
+ 'Berrie (86120)' => '86022',
+ 'Berrogain-Laruns (64130)' => '64115',
+ 'Bersac-sur-Rivalier (87370)' => '87013',
+ 'Berson (33390)' => '33047',
+ 'Berthegon (86420)' => '86023',
+ 'Berthez (33124)' => '33048',
+ 'Bertric-Burée (24320)' => '24038',
+ 'Béruges (86190)' => '86024',
+ 'Bescat (64260)' => '64116',
+ 'Bésingrand (64150)' => '64117',
+ 'Bessac (16250)' => '16041',
+ 'Bessé (16140)' => '16042',
+ 'Besse (24550)' => '24039',
+ 'Bessines (79000)' => '79034',
+ 'Bessines-sur-Gartempe (87250)' => '87014',
+ 'Betbezer-d\'Armagnac (40240)' => '40039',
+ 'Bétête (23270)' => '23022',
+ 'Béthines (86310)' => '86025',
+ 'Bétracq (64350)' => '64118',
+ 'Beurlay (17250)' => '17045',
+ 'Beuste (64800)' => '64119',
+ 'Beuxes (86120)' => '86026',
+ 'Beychac-et-Caillau (33750)' => '33049',
+ 'Beylongue (40370)' => '40040',
+ 'Beynac (87700)' => '87015',
+ 'Beynac-et-Cazenac (24220)' => '24040',
+ 'Beynat (19190)' => '19023',
+ 'Beyrie-en-Béarn (64230)' => '64121',
+ 'Beyrie-sur-Joyeuse (64120)' => '64120',
+ 'Beyries (40700)' => '40041',
+ 'Beyssac (19230)' => '19024',
+ 'Beyssenac (19230)' => '19025',
+ 'Bézenac (24220)' => '24041',
+ 'Biard (86580)' => '86027',
+ 'Biarritz (64200)' => '64122',
+ 'Biarrotte (40390)' => '40042',
+ 'Bias (40170)' => '40043',
+ 'Bias (47300)' => '47027',
+ 'Biaudos (40390)' => '40044',
+ 'Bidache (64520)' => '64123',
+ 'Bidarray (64780)' => '64124',
+ 'Bidart (64210)' => '64125',
+ 'Bidos (64400)' => '64126',
+ 'Bielle (64260)' => '64127',
+ 'Bieujac (33210)' => '33050',
+ 'Biganos (33380)' => '33051',
+ 'Bignay (17400)' => '17046',
+ 'Bignoux (86800)' => '86028',
+ 'Bilhac (19120)' => '19026',
+ 'Bilhères (64260)' => '64128',
+ 'Billère (64140)' => '64129',
+ 'Bioussac (16700)' => '16044',
+ 'Birac (16120)' => '16045',
+ 'Birac (33430)' => '33053',
+ 'Birac-sur-Trec (47200)' => '47028',
+ 'Biras (24310)' => '24042',
+ 'Biriatou (64700)' => '64130',
+ 'Biron (17800)' => '17047',
+ 'Biron (24540)' => '24043',
+ 'Biron (64300)' => '64131',
+ 'Biscarrosse (40600)' => '40046',
+ 'Bizanos (64320)' => '64132',
+ 'Blaignac (33190)' => '33054',
+ 'Blaignan (33340)' => '33055',
+ 'Blanquefort (33290)' => '33056',
+ 'Blanquefort-sur-Briolance (47500)' => '47029',
+ 'Blanzac (87300)' => '87017',
+ 'Blanzac-lès-Matha (17160)' => '17048',
+ 'Blanzac-Porcheresse (16250)' => '16046',
+ 'Blanzaguet-Saint-Cybard (16320)' => '16047',
+ 'Blanzay (86400)' => '86029',
+ 'Blanzay-sur-Boutonne (17470)' => '17049',
+ 'Blasimon (33540)' => '33057',
+ 'Blaslay (86170)' => '86030',
+ 'Blaudeix (23140)' => '23023',
+ 'Blaye (33390)' => '33058',
+ 'Blaymont (47470)' => '47030',
+ 'Blésignac (33670)' => '33059',
+ 'Blessac (23200)' => '23024',
+ 'Blis-et-Born (24330)' => '24044',
+ 'Blond (87300)' => '87018',
+ 'Boé (47550)' => '47031',
+ 'Boeil-Bezing (64510)' => '64133',
+ 'Bois (17240)' => '17050',
+ 'Boisbreteau (16480)' => '16048',
+ 'Boismé (79300)' => '79038',
+ 'Boisné-La Tude (16320)' => '16082',
+ 'Boisredon (17150)' => '17052',
+ 'Boisse (24560)' => '24045',
+ 'Boisserolles (79360)' => '79039',
+ 'Boisseuil (87220)' => '87019',
+ 'Boisseuilh (24390)' => '24046',
+ 'Bommes (33210)' => '33060',
+ 'Bon-Encontre (47240)' => '47032',
+ 'Bonloc (64240)' => '64134',
+ 'Bonnac-la-Côte (87270)' => '87020',
+ 'Bonnat (23220)' => '23025',
+ 'Bonnefond (19170)' => '19027',
+ 'Bonnegarde (40330)' => '40047',
+ 'Bonnes (16390)' => '16049',
+ 'Bonnes (86300)' => '86031',
+ 'Bonnetan (33370)' => '33061',
+ 'Bonneuil (16120)' => '16050',
+ 'Bonneuil-Matours (86210)' => '86032',
+ 'Bonneville (16170)' => '16051',
+ 'Bonneville-et-Saint-Avit-de-Fumadières (24230)' => '24048',
+ 'Bonnut (64300)' => '64135',
+ 'Bonzac (33910)' => '33062',
+ 'Boos (40370)' => '40048',
+ 'Borce (64490)' => '64136',
+ 'Bord-Saint-Georges (23230)' => '23026',
+ 'Bordeaux (33000)' => '33063',
+ 'Bordères (64800)' => '64137',
+ 'Bordères-et-Lamensans (40270)' => '40049',
+ 'Bordes (64510)' => '64138',
+ 'Bords (17430)' => '17053',
+ 'Boresse-et-Martron (17270)' => '17054',
+ 'Borrèze (24590)' => '24050',
+ 'Bors (Canton de Baignes-Sainte-Radegonde) (16360)' => '16053',
+ 'Bors (Canton de Montmoreau-Saint-Cybard) (16190)' => '16052',
+ 'Bort-les-Orgues (19110)' => '19028',
+ 'Boscamnant (17360)' => '17055',
+ 'Bosdarros (64290)' => '64139',
+ 'Bosmie-l\'Aiguille (87110)' => '87021',
+ 'Bosmoreau-les-Mines (23400)' => '23027',
+ 'Bosroger (23200)' => '23028',
+ 'Bosset (24130)' => '24051',
+ 'Bossugan (33350)' => '33064',
+ 'Bostens (40090)' => '40050',
+ 'Boucau (64340)' => '64140',
+ 'Boudy-de-Beauregard (47290)' => '47033',
+ 'Boueilh-Boueilho-Lasque (64330)' => '64141',
+ 'Bouëx (16410)' => '16055',
+ 'Bougarber (64230)' => '64142',
+ 'Bouglon (47250)' => '47034',
+ 'Bougneau (17800)' => '17056',
+ 'Bougon (79800)' => '79042',
+ 'Bougue (40090)' => '40051',
+ 'Bouhet (17540)' => '17057',
+ 'Bouillac (24480)' => '24052',
+ 'Bouillé-Loretz (79290)' => '79043',
+ 'Bouillé-Saint-Paul (79290)' => '79044',
+ 'Bouillon (64410)' => '64143',
+ 'Bouin (79110)' => '79045',
+ 'Boulazac Isle Manoire (24750)' => '24053',
+ 'Bouliac (33270)' => '33065',
+ 'Boumourt (64370)' => '64144',
+ 'Bouniagues (24560)' => '24054',
+ 'Bourcefranc-le-Chapus (17560)' => '17058',
+ 'Bourdalat (40190)' => '40052',
+ 'Bourdeilles (24310)' => '24055',
+ 'Bourdelles (33190)' => '33066',
+ 'Bourdettes (64800)' => '64145',
+ 'Bouresse (86410)' => '86034',
+ 'Bourg (33710)' => '33067',
+ 'Bourg-Archambault (86390)' => '86035',
+ 'Bourg-Charente (16200)' => '16056',
+ 'Bourg-des-Maisons (24320)' => '24057',
+ 'Bourg-du-Bost (24600)' => '24058',
+ 'Bourganeuf (23400)' => '23030',
+ 'Bourgnac (24400)' => '24059',
+ 'Bourgneuf (17220)' => '17059',
+ 'Bourgougnague (47410)' => '47035',
+ 'Bourideys (33113)' => '33068',
+ 'Bourlens (47370)' => '47036',
+ 'Bournand (86120)' => '86036',
+ 'Bournel (47210)' => '47037',
+ 'Bourniquel (24150)' => '24060',
+ 'Bournos (64450)' => '64146',
+ 'Bourran (47320)' => '47038',
+ 'Bourriot-Bergonce (40120)' => '40053',
+ 'Bourrou (24110)' => '24061',
+ 'Boussac (23600)' => '23031',
+ 'Boussac-Bourg (23600)' => '23032',
+ 'Boussais (79600)' => '79047',
+ 'Boussès (47420)' => '47039',
+ 'Bouteilles-Saint-Sébastien (24320)' => '24062',
+ 'Boutenac-Touvent (17120)' => '17060',
+ 'Bouteville (16120)' => '16057',
+ 'Boutiers-Saint-Trojan (16100)' => '16058',
+ 'Bouzic (24250)' => '24063',
+ 'Brach (33480)' => '33070',
+ 'Bran (17210)' => '17061',
+ 'Branceilles (19500)' => '19029',
+ 'Branne (33420)' => '33071',
+ 'Brannens (33124)' => '33072',
+ 'Brantôme en Périgord (24310)' => '24064',
+ 'Brassempouy (40330)' => '40054',
+ 'Braud-et-Saint-Louis (33820)' => '33073',
+ 'Brax (47310)' => '47040',
+ 'Bresdon (17490)' => '17062',
+ 'Bressuire (79300)' => '79049',
+ 'Bretagne-de-Marsan (40280)' => '40055',
+ 'Bretignolles (79140)' => '79050',
+ 'Brettes (16240)' => '16059',
+ 'Breuil-la-Réorte (17700)' => '17063',
+ 'Breuil-Magné (17870)' => '17065',
+ 'Breuilaufa (87300)' => '87022',
+ 'Breuilh (24380)' => '24065',
+ 'Breuillet (17920)' => '17064',
+ 'Bréville (16370)' => '16060',
+ 'Brie (16590)' => '16061',
+ 'Brie (79100)' => '79054',
+ 'Brie-sous-Archiac (17520)' => '17066',
+ 'Brie-sous-Barbezieux (16300)' => '16062',
+ 'Brie-sous-Chalais (16210)' => '16063',
+ 'Brie-sous-Matha (17160)' => '17067',
+ 'Brie-sous-Mortagne (17120)' => '17068',
+ 'Brieuil-sur-Chizé (79170)' => '79055',
+ 'Brignac-la-Plaine (19310)' => '19030',
+ 'Brigueil-le-Chantre (86290)' => '86037',
+ 'Brigueuil (16420)' => '16064',
+ 'Brillac (16500)' => '16065',
+ 'Brion (86160)' => '86038',
+ 'Brion-près-Thouet (79290)' => '79056',
+ 'Brioux-sur-Boutonne (79170)' => '79057',
+ 'Briscous (64240)' => '64147',
+ 'Brive-la-Gaillarde (19100)' => '19031',
+ 'Brives-sur-Charente (17800)' => '17069',
+ 'Brivezac (19120)' => '19032',
+ 'Brizambourg (17770)' => '17070',
+ 'Brocas (40420)' => '40056',
+ 'Brossac (16480)' => '16066',
+ 'Brouchaud (24210)' => '24066',
+ 'Brouqueyran (33124)' => '33074',
+ 'Brousse (23700)' => '23034',
+ 'Bruch (47130)' => '47041',
+ 'Bruges (33520)' => '33075',
+ 'Bruges-Capbis-Mifaget (64800)' => '64148',
+ 'Brugnac (47260)' => '47042',
+ 'Brûlain (79230)' => '79058',
+ 'Brux (86510)' => '86039',
+ 'Buanes (40320)' => '40057',
+ 'Budelière (23170)' => '23035',
+ 'Budos (33720)' => '33076',
+ 'Bugeat (19170)' => '19033',
+ 'Bugnein (64190)' => '64149',
+ 'Bujaleuf (87460)' => '87024',
+ 'Bunus (64120)' => '64150',
+ 'Bunzac (16110)' => '16067',
+ 'Burgaronne (64390)' => '64151',
+ 'Burgnac (87800)' => '87025',
+ 'Burie (17770)' => '17072',
+ 'Buros (64160)' => '64152',
+ 'Burosse-Mendousse (64330)' => '64153',
+ 'Bussac (24350)' => '24069',
+ 'Bussac-Forêt (17210)' => '17074',
+ 'Bussac-sur-Charente (17100)' => '17073',
+ 'Busserolles (24360)' => '24070',
+ 'Bussière-Badil (24360)' => '24071',
+ 'Bussière-Dunoise (23320)' => '23036',
+ 'Bussière-Galant (87230)' => '87027',
+ 'Bussière-Nouvelle (23700)' => '23037',
+ 'Bussière-Poitevine (87320)' => '87028',
+ 'Bussière-Saint-Georges (23600)' => '23038',
+ 'Bussunarits-Sarrasquette (64220)' => '64154',
+ 'Bustince-Iriberry (64220)' => '64155',
+ 'Buxerolles (86180)' => '86041',
+ 'Buxeuil (37160)' => '86042',
+ 'Buzet-sur-Baïse (47160)' => '47043',
+ 'Buziet (64680)' => '64156',
+ 'Buzy (64260)' => '64157',
+ 'Cabanac-et-Villagrains (33650)' => '33077',
+ 'Cabara (33420)' => '33078',
+ 'Cabariot (17430)' => '17075',
+ 'Cabidos (64410)' => '64158',
+ 'Cachen (40120)' => '40058',
+ 'Cadarsac (33750)' => '33079',
+ 'Cadaujac (33140)' => '33080',
+ 'Cadillac (33410)' => '33081',
+ 'Cadillac-en-Fronsadais (33240)' => '33082',
+ 'Cadillon (64330)' => '64159',
+ 'Cagnotte (40300)' => '40059',
+ 'Cahuzac (47330)' => '47044',
+ 'Calès (24150)' => '24073',
+ 'Calignac (47600)' => '47045',
+ 'Callen (40430)' => '40060',
+ 'Calonges (47430)' => '47046',
+ 'Calviac-en-Périgord (24370)' => '24074',
+ 'Camarsac (33750)' => '33083',
+ 'Cambes (33880)' => '33084',
+ 'Cambes (47350)' => '47047',
+ 'Camblanes-et-Meynac (33360)' => '33085',
+ 'Cambo-les-Bains (64250)' => '64160',
+ 'Came (64520)' => '64161',
+ 'Camiac-et-Saint-Denis (33420)' => '33086',
+ 'Camiran (33190)' => '33087',
+ 'Camou-Cihigue (64470)' => '64162',
+ 'Campagnac-lès-Quercy (24550)' => '24075',
+ 'Campagne (24260)' => '24076',
+ 'Campagne (40090)' => '40061',
+ 'Campet-et-Lamolère (40090)' => '40062',
+ 'Camps-Saint-Mathurin-Léobazel (19430)' => '19034',
+ 'Camps-sur-l\'Isle (33660)' => '33088',
+ 'Campsegret (24140)' => '24077',
+ 'Campugnan (33390)' => '33089',
+ 'Cancon (47290)' => '47048',
+ 'Candresse (40180)' => '40063',
+ 'Canéjan (33610)' => '33090',
+ 'Canenx-et-Réaut (40090)' => '40064',
+ 'Cantenac (33460)' => '33091',
+ 'Cantillac (24530)' => '24079',
+ 'Cantois (33760)' => '33092',
+ 'Capbreton (40130)' => '40065',
+ 'Capdrot (24540)' => '24080',
+ 'Capian (33550)' => '33093',
+ 'Caplong (33220)' => '33094',
+ 'Captieux (33840)' => '33095',
+ 'Carbon-Blanc (33560)' => '33096',
+ 'Carcans (33121)' => '33097',
+ 'Carcarès-Sainte-Croix (40400)' => '40066',
+ 'Carcen-Ponson (40400)' => '40067',
+ 'Cardan (33410)' => '33098',
+ 'Cardesse (64360)' => '64165',
+ 'Carignan-de-Bordeaux (33360)' => '33099',
+ 'Carlux (24370)' => '24081',
+ 'Caro (64220)' => '64166',
+ 'Carrère (64160)' => '64167',
+ 'Carresse-Cassaber (64270)' => '64168',
+ 'Cars (33390)' => '33100',
+ 'Carsac-Aillac (24200)' => '24082',
+ 'Carsac-de-Gurson (24610)' => '24083',
+ 'Cartelègue (33390)' => '33101',
+ 'Carves (24170)' => '24084',
+ 'Cassen (40380)' => '40068',
+ 'Casseneuil (47440)' => '47049',
+ 'Casseuil (33190)' => '33102',
+ 'Cassignas (47340)' => '47050',
+ 'Castagnède (64270)' => '64170',
+ 'Castaignos-Souslens (40700)' => '40069',
+ 'Castandet (40270)' => '40070',
+ 'Casteide-Cami (64170)' => '64171',
+ 'Casteide-Candau (64370)' => '64172',
+ 'Casteide-Doat (64460)' => '64173',
+ 'Castel-Sarrazin (40330)' => '40074',
+ 'Castelculier (47240)' => '47051',
+ 'Casteljaloux (47700)' => '47052',
+ 'Castella (47340)' => '47053',
+ 'Castelmoron-d\'Albret (33540)' => '33103',
+ 'Castelmoron-sur-Lot (47260)' => '47054',
+ 'Castelnau-Chalosse (40360)' => '40071',
+ 'Castelnau-de-Médoc (33480)' => '33104',
+ 'Castelnau-sur-Gupie (47180)' => '47056',
+ 'Castelnau-Tursan (40320)' => '40072',
+ 'Castelnaud-de-Gratecambe (47290)' => '47055',
+ 'Castelnaud-la-Chapelle (24250)' => '24086',
+ 'Castelner (40700)' => '40073',
+ 'Castels (24220)' => '24087',
+ 'Castelviel (33540)' => '33105',
+ 'Castéra-Loubix (64460)' => '64174',
+ 'Castet (64260)' => '64175',
+ 'Castetbon (64190)' => '64176',
+ 'Castétis (64300)' => '64177',
+ 'Castetnau-Camblong (64190)' => '64178',
+ 'Castetner (64300)' => '64179',
+ 'Castetpugon (64330)' => '64180',
+ 'Castets (40260)' => '40075',
+ 'Castets-en-Dorthe (33210)' => '33106',
+ 'Castillon (Canton d\'Arthez-de-Béarn) (64370)' => '64181',
+ 'Castillon (Canton de Lembeye) (64350)' => '64182',
+ 'Castillon-de-Castets (33210)' => '33107',
+ 'Castillon-la-Bataille (33350)' => '33108',
+ 'Castillonnès (47330)' => '47057',
+ 'Castres-Gironde (33640)' => '33109',
+ 'Caubeyres (47160)' => '47058',
+ 'Caubios-Loos (64230)' => '64183',
+ 'Caubon-Saint-Sauveur (47120)' => '47059',
+ 'Caudecoste (47220)' => '47060',
+ 'Caudrot (33490)' => '33111',
+ 'Caumont (33540)' => '33112',
+ 'Caumont-sur-Garonne (47430)' => '47061',
+ 'Cauna (40500)' => '40076',
+ 'Caunay (79190)' => '79060',
+ 'Cauneille (40300)' => '40077',
+ 'Caupenne (40250)' => '40078',
+ 'Cause-de-Clérans (24150)' => '24088',
+ 'Cauvignac (33690)' => '33113',
+ 'Cauzac (47470)' => '47062',
+ 'Cavarc (47330)' => '47063',
+ 'Cavignac (33620)' => '33114',
+ 'Cazalis (33113)' => '33115',
+ 'Cazalis (40700)' => '40079',
+ 'Cazats (33430)' => '33116',
+ 'Cazaugitat (33790)' => '33117',
+ 'Cazères-sur-l\'Adour (40270)' => '40080',
+ 'Cazideroque (47370)' => '47064',
+ 'Cazoulès (24370)' => '24089',
+ 'Ceaux-en-Couhé (86700)' => '86043',
+ 'Ceaux-en-Loudun (86200)' => '86044',
+ 'Celle-Lévescault (86600)' => '86045',
+ 'Cellefrouin (16260)' => '16068',
+ 'Celles (17520)' => '17076',
+ 'Celles (24600)' => '24090',
+ 'Celles-sur-Belle (79370)' => '79061',
+ 'Cellettes (16230)' => '16069',
+ 'Cénac (33360)' => '33118',
+ 'Cénac-et-Saint-Julien (24250)' => '24091',
+ 'Cendrieux (24380)' => '24092',
+ 'Cenon (33150)' => '33119',
+ 'Cenon-sur-Vienne (86530)' => '86046',
+ 'Cercles (24320)' => '24093',
+ 'Cercoux (17270)' => '17077',
+ 'Cère (40090)' => '40081',
+ 'Cerizay (79140)' => '79062',
+ 'Cernay (86140)' => '86047',
+ 'Cérons (33720)' => '33120',
+ 'Cersay (79290)' => '79063',
+ 'Cescau (64170)' => '64184',
+ 'Cessac (33760)' => '33121',
+ 'Cestas (33610)' => '33122',
+ 'Cette-Eygun (64490)' => '64185',
+ 'Ceyroux (23210)' => '23042',
+ 'Cézac (33620)' => '33123',
+ 'Chabanais (16150)' => '16070',
+ 'Chabournay (86380)' => '86048',
+ 'Chabrac (16150)' => '16071',
+ 'Chabrignac (19350)' => '19035',
+ 'Chadenac (17800)' => '17078',
+ 'Chadurie (16250)' => '16072',
+ 'Chail (79500)' => '79064',
+ 'Chaillac-sur-Vienne (87200)' => '87030',
+ 'Chaillevette (17890)' => '17079',
+ 'Chalagnac (24380)' => '24094',
+ 'Chalais (16210)' => '16073',
+ 'Chalais (24800)' => '24095',
+ 'Chalais (86200)' => '86049',
+ 'Chalandray (86190)' => '86050',
+ 'Challignac (16300)' => '16074',
+ 'Châlus (87230)' => '87032',
+ 'Chamadelle (33230)' => '33124',
+ 'Chamberaud (23480)' => '23043',
+ 'Chamberet (19370)' => '19036',
+ 'Chambon (17290)' => '17080',
+ 'Chambon-Sainte-Croix (23220)' => '23044',
+ 'Chambon-sur-Voueize (23170)' => '23045',
+ 'Chambonchard (23110)' => '23046',
+ 'Chamborand (23240)' => '23047',
+ 'Chamboret (87140)' => '87033',
+ 'Chamboulive (19450)' => '19037',
+ 'Chameyrat (19330)' => '19038',
+ 'Chamouillac (17130)' => '17081',
+ 'Champagnac (17500)' => '17082',
+ 'Champagnac-de-Belair (24530)' => '24096',
+ 'Champagnac-la-Noaille (19320)' => '19039',
+ 'Champagnac-la-Prune (19320)' => '19040',
+ 'Champagnac-la-Rivière (87150)' => '87034',
+ 'Champagnat (23190)' => '23048',
+ 'Champagne (17620)' => '17083',
+ 'Champagne-et-Fontaine (24320)' => '24097',
+ 'Champagné-le-Sec (86510)' => '86051',
+ 'Champagne-Mouton (16350)' => '16076',
+ 'Champagné-Saint-Hilaire (86160)' => '86052',
+ 'Champagne-Vigny (16250)' => '16075',
+ 'Champagnolles (17240)' => '17084',
+ 'Champcevinel (24750)' => '24098',
+ 'Champdeniers-Saint-Denis (79220)' => '79066',
+ 'Champdolent (17430)' => '17085',
+ 'Champeaux-et-la-Chapelle-Pommier (24340)' => '24099',
+ 'Champigny-le-Sec (86170)' => '86053',
+ 'Champmillon (16290)' => '16077',
+ 'Champnétery (87400)' => '87035',
+ 'Champniers (16430)' => '16078',
+ 'Champniers (86400)' => '86054',
+ 'Champniers-et-Reilhac (24360)' => '24100',
+ 'Champs-Romain (24470)' => '24101',
+ 'Champsac (87230)' => '87036',
+ 'Champsanglard (23220)' => '23049',
+ 'Chanac-les-Mines (19150)' => '19041',
+ 'Chancelade (24650)' => '24102',
+ 'Chaniers (17610)' => '17086',
+ 'Chantecorps (79340)' => '79068',
+ 'Chanteix (19330)' => '19042',
+ 'Chanteloup (79320)' => '79069',
+ 'Chantemerle-sur-la-Soie (17380)' => '17087',
+ 'Chantérac (24190)' => '24104',
+ 'Chantillac (16360)' => '16079',
+ 'Chapdeuil (24320)' => '24105',
+ 'Chapelle-Spinasse (19300)' => '19046',
+ 'Chapelle-Viviers (86300)' => '86059',
+ 'Chaptelat (87270)' => '87038',
+ 'Chard (23700)' => '23053',
+ 'Charmé (16140)' => '16083',
+ 'Charrais (86170)' => '86060',
+ 'Charras (16380)' => '16084',
+ 'Charre (64190)' => '64186',
+ 'Charritte-de-Bas (64130)' => '64187',
+ 'Charron (17230)' => '17091',
+ 'Charron (23700)' => '23054',
+ 'Charroux (86250)' => '86061',
+ 'Chartrier-Ferrière (19600)' => '19047',
+ 'Chartuzac (17130)' => '17092',
+ 'Chassaignes (24600)' => '24114',
+ 'Chasseneuil-du-Poitou (86360)' => '86062',
+ 'Chasseneuil-sur-Bonnieure (16260)' => '16085',
+ 'Chassenon (16150)' => '16086',
+ 'Chassiecq (16350)' => '16087',
+ 'Chassors (16200)' => '16088',
+ 'Chasteaux (19600)' => '19049',
+ 'Chatain (86250)' => '86063',
+ 'Château-Chervix (87380)' => '87039',
+ 'Château-Garnier (86350)' => '86064',
+ 'Château-l\'Évêque (24460)' => '24115',
+ 'Château-Larcher (86370)' => '86065',
+ 'Châteaubernard (16100)' => '16089',
+ 'Châteauneuf-la-Forêt (87130)' => '87040',
+ 'Châteauneuf-sur-Charente (16120)' => '16090',
+ 'Châteauponsac (87290)' => '87041',
+ 'Châtelaillon-Plage (17340)' => '17094',
+ 'Châtelard (23700)' => '23055',
+ 'Châtellerault (86100)' => '86066',
+ 'Châtelus-le-Marcheix (23430)' => '23056',
+ 'Châtelus-Malvaleix (23270)' => '23057',
+ 'Chatenet (17210)' => '17095',
+ 'Châtignac (16480)' => '16091',
+ 'Châtillon (86700)' => '86067',
+ 'Châtillon-sur-Thouet (79200)' => '79080',
+ 'Châtres (24120)' => '24116',
+ 'Chauffour-sur-Vell (19500)' => '19050',
+ 'Chaumeil (19390)' => '19051',
+ 'Chaunac (17130)' => '17096',
+ 'Chaunay (86510)' => '86068',
+ 'Chauray (79180)' => '79081',
+ 'Chauvigny (86300)' => '86070',
+ 'Chavagnac (24120)' => '24117',
+ 'Chavanac (19290)' => '19052',
+ 'Chavanat (23250)' => '23060',
+ 'Chaveroche (19200)' => '19053',
+ 'Chazelles (16380)' => '16093',
+ 'Chef-Boutonne (79110)' => '79083',
+ 'Cheissoux (87460)' => '87043',
+ 'Chenac-Saint-Seurin-d\'Uzet (17120)' => '17098',
+ 'Chenailler-Mascheix (19120)' => '19054',
+ 'Chenay (79120)' => '79084',
+ 'Cheneché (86380)' => '86071',
+ 'Chénérailles (23130)' => '23061',
+ 'Chenevelles (86450)' => '86072',
+ 'Chéniers (23220)' => '23062',
+ 'Chenommet (16460)' => '16094',
+ 'Chenon (16460)' => '16095',
+ 'Chepniers (17210)' => '17099',
+ 'Chérac (17610)' => '17100',
+ 'Chéraute (64130)' => '64188',
+ 'Cherbonnières (17470)' => '17101',
+ 'Chérigné (79170)' => '79085',
+ 'Chermignac (17460)' => '17102',
+ 'Chéronnac (87600)' => '87044',
+ 'Cherval (24320)' => '24119',
+ 'Cherveix-Cubas (24390)' => '24120',
+ 'Cherves (86170)' => '86073',
+ 'Cherves-Châtelars (16310)' => '16096',
+ 'Cherves-Richemont (16370)' => '16097',
+ 'Chervettes (17380)' => '17103',
+ 'Cherveux (79410)' => '79086',
+ 'Chevanceaux (17210)' => '17104',
+ 'Chey (79120)' => '79087',
+ 'Chiché (79350)' => '79088',
+ 'Chillac (16480)' => '16099',
+ 'Chirac (16150)' => '16100',
+ 'Chirac-Bellevue (19160)' => '19055',
+ 'Chiré-en-Montreuil (86190)' => '86074',
+ 'Chives (17510)' => '17105',
+ 'Chizé (79170)' => '79090',
+ 'Chouppes (86110)' => '86075',
+ 'Chourgnac (24640)' => '24121',
+ 'Ciboure (64500)' => '64189',
+ 'Cierzac (17520)' => '17106',
+ 'Cieux (87520)' => '87045',
+ 'Ciré-d\'Aunis (17290)' => '17107',
+ 'Cirières (79140)' => '79091',
+ 'Cissac-Médoc (33250)' => '33125',
+ 'Cissé (86170)' => '86076',
+ 'Civaux (86320)' => '86077',
+ 'Civrac-de-Blaye (33920)' => '33126',
+ 'Civrac-en-Médoc (33340)' => '33128',
+ 'Civrac-sur-Dordogne (33350)' => '33127',
+ 'Civray (86400)' => '86078',
+ 'Cladech (24170)' => '24122',
+ 'Clairac (47320)' => '47065',
+ 'Clairavaux (23500)' => '23063',
+ 'Claix (16440)' => '16101',
+ 'Clam (17500)' => '17108',
+ 'Claracq (64330)' => '64190',
+ 'Classun (40320)' => '40082',
+ 'Clavé (79420)' => '79092',
+ 'Clavette (17220)' => '17109',
+ 'Clèdes (40320)' => '40083',
+ 'Clérac (17270)' => '17110',
+ 'Clergoux (19320)' => '19056',
+ 'Clermont (40180)' => '40084',
+ 'Clermont-d\'Excideuil (24160)' => '24124',
+ 'Clermont-de-Beauregard (24140)' => '24123',
+ 'Clermont-Dessous (47130)' => '47066',
+ 'Clermont-Soubiran (47270)' => '47067',
+ 'Clessé (79350)' => '79094',
+ 'Cleyrac (33540)' => '33129',
+ 'Clion (17240)' => '17111',
+ 'Cloué (86600)' => '86080',
+ 'Clugnat (23270)' => '23064',
+ 'Clussais-la-Pommeraie (79190)' => '79095',
+ 'Coarraze (64800)' => '64191',
+ 'Cocumont (47250)' => '47068',
+ 'Cognac (16100)' => '16102',
+ 'Cognac-la-Forêt (87310)' => '87046',
+ 'Coimères (33210)' => '33130',
+ 'Coirac (33540)' => '33131',
+ 'Coivert (17330)' => '17114',
+ 'Colayrac-Saint-Cirq (47450)' => '47069',
+ 'Collonges-la-Rouge (19500)' => '19057',
+ 'Colombier (24560)' => '24126',
+ 'Colombiers (17460)' => '17115',
+ 'Colombiers (86490)' => '86081',
+ 'Colondannes (23800)' => '23065',
+ 'Coly (24120)' => '24127',
+ 'Comberanche-et-Épeluche (24600)' => '24128',
+ 'Combiers (16320)' => '16103',
+ 'Combrand (79140)' => '79096',
+ 'Combressol (19250)' => '19058',
+ 'Commensacq (40210)' => '40085',
+ 'Compreignac (87140)' => '87047',
+ 'Comps (33710)' => '33132',
+ 'Concèze (19350)' => '19059',
+ 'Conchez-de-Béarn (64330)' => '64192',
+ 'Condac (16700)' => '16104',
+ 'Condat-sur-Ganaveix (19140)' => '19060',
+ 'Condat-sur-Trincou (24530)' => '24129',
+ 'Condat-sur-Vézère (24570)' => '24130',
+ 'Condat-sur-Vienne (87920)' => '87048',
+ 'Condéon (16360)' => '16105',
+ 'Condezaygues (47500)' => '47070',
+ 'Confolens (16500)' => '16106',
+ 'Confolent-Port-Dieu (19200)' => '19167',
+ 'Conne-de-Labarde (24560)' => '24132',
+ 'Connezac (24300)' => '24131',
+ 'Consac (17150)' => '17116',
+ 'Contré (17470)' => '17117',
+ 'Corbère-Abères (64350)' => '64193',
+ 'Corgnac-sur-l\'Isle (24800)' => '24134',
+ 'Corignac (17130)' => '17118',
+ 'Corme-Écluse (17600)' => '17119',
+ 'Corme-Royal (17600)' => '17120',
+ 'Cornil (19150)' => '19061',
+ 'Cornille (24750)' => '24135',
+ 'Corrèze (19800)' => '19062',
+ 'Coslédaà-Lube-Boast (64160)' => '64194',
+ 'Cosnac (19360)' => '19063',
+ 'Coubeyrac (33890)' => '33133',
+ 'Coubjours (24390)' => '24136',
+ 'Coublucq (64410)' => '64195',
+ 'Coudures (40500)' => '40086',
+ 'Couffy-sur-Sarsonne (19340)' => '19064',
+ 'Couhé (86700)' => '86082',
+ 'Coulaures (24420)' => '24137',
+ 'Coulgens (16560)' => '16107',
+ 'Coulombiers (86600)' => '86083',
+ 'Coulon (79510)' => '79100',
+ 'Coulonges (16330)' => '16108',
+ 'Coulonges (17800)' => '17122',
+ 'Coulonges (86290)' => '86084',
+ 'Coulonges-sur-l\'Autize (79160)' => '79101',
+ 'Coulonges-Thouarsais (79330)' => '79102',
+ 'Coulounieix-Chamiers (24660)' => '24138',
+ 'Coulx (47260)' => '47071',
+ 'Couquèques (33340)' => '33134',
+ 'Courant (17330)' => '17124',
+ 'Courbiac (47370)' => '47072',
+ 'Courbillac (16200)' => '16109',
+ 'Courcelles (17400)' => '17125',
+ 'Courcerac (17160)' => '17126',
+ 'Courcôme (16240)' => '16110',
+ 'Courçon (17170)' => '17127',
+ 'Courcoury (17100)' => '17128',
+ 'Courgeac (16190)' => '16111',
+ 'Courlac (16210)' => '16112',
+ 'Courlay (79440)' => '79103',
+ 'Courpiac (33760)' => '33135',
+ 'Courpignac (17130)' => '17129',
+ 'Cours (47360)' => '47073',
+ 'Cours (79220)' => '79104',
+ 'Cours-de-Monségur (33580)' => '33136',
+ 'Cours-de-Pile (24520)' => '24140',
+ 'Cours-les-Bains (33690)' => '33137',
+ 'Coursac (24430)' => '24139',
+ 'Courteix (19340)' => '19065',
+ 'Coussac-Bonneval (87500)' => '87049',
+ 'Coussay (86110)' => '86085',
+ 'Coussay-les-Bois (86270)' => '86086',
+ 'Couthures-sur-Garonne (47180)' => '47074',
+ 'Coutières (79340)' => '79105',
+ 'Coutras (33230)' => '33138',
+ 'Couture (16460)' => '16114',
+ 'Couture-d\'Argenson (79110)' => '79106',
+ 'Coutures (24320)' => '24141',
+ 'Coutures (33580)' => '33139',
+ 'Coux (17130)' => '17130',
+ 'Coux et Bigaroque-Mouzens (24220)' => '24142',
+ 'Couze-et-Saint-Front (24150)' => '24143',
+ 'Couzeix (87270)' => '87050',
+ 'Cozes (17120)' => '17131',
+ 'Cramchaban (17170)' => '17132',
+ 'Craon (86110)' => '86087',
+ 'Cravans (17260)' => '17133',
+ 'Crazannes (17350)' => '17134',
+ 'Créon (33670)' => '33140',
+ 'Créon-d\'Armagnac (40240)' => '40087',
+ 'Cressac-Saint-Genis (16250)' => '16115',
+ 'Cressat (23140)' => '23068',
+ 'Cressé (17160)' => '17135',
+ 'Creyssac (24350)' => '24144',
+ 'Creysse (24100)' => '24145',
+ 'Creyssensac-et-Pissot (24380)' => '24146',
+ 'Crézières (79110)' => '79107',
+ 'Criteuil-la-Magdeleine (16300)' => '16116',
+ 'Crocq (23260)' => '23069',
+ 'Croignon (33750)' => '33141',
+ 'Croix-Chapeau (17220)' => '17136',
+ 'Cromac (87160)' => '87053',
+ 'Crouseilles (64350)' => '64196',
+ 'Croutelle (86240)' => '86088',
+ 'Crozant (23160)' => '23070',
+ 'Croze (23500)' => '23071',
+ 'Cubjac (24640)' => '24147',
+ 'Cublac (19520)' => '19066',
+ 'Cubnezais (33620)' => '33142',
+ 'Cubzac-les-Ponts (33240)' => '33143',
+ 'Cudos (33430)' => '33144',
+ 'Cuhon (86110)' => '86089',
+ 'Cunèges (24240)' => '24148',
+ 'Cuq (47220)' => '47076',
+ 'Cuqueron (64360)' => '64197',
+ 'Curac (16210)' => '16117',
+ 'Curçay-sur-Dive (86120)' => '86090',
+ 'Curemonte (19500)' => '19067',
+ 'Cursan (33670)' => '33145',
+ 'Curzay-sur-Vonne (86600)' => '86091',
+ 'Cussac (87150)' => '87054',
+ 'Cussac-Fort-Médoc (33460)' => '33146',
+ 'Cuzorn (47500)' => '47077',
+ 'Daglan (24250)' => '24150',
+ 'Daignac (33420)' => '33147',
+ 'Damazan (47160)' => '47078',
+ 'Dampierre-sur-Boutonne (17470)' => '17138',
+ 'Dampniat (19360)' => '19068',
+ 'Dangé-Saint-Romain (86220)' => '86092',
+ 'Darazac (19220)' => '19069',
+ 'Dardenac (33420)' => '33148',
+ 'Darnac (87320)' => '87055',
+ 'Darnets (19300)' => '19070',
+ 'Daubèze (33540)' => '33149',
+ 'Dausse (47140)' => '47079',
+ 'Davignac (19250)' => '19071',
+ 'Dax (40100)' => '40088',
+ 'Denguin (64230)' => '64198',
+ 'Dercé (86420)' => '86093',
+ 'Deviat (16190)' => '16118',
+ 'Dévillac (47210)' => '47080',
+ 'Dienné (86410)' => '86094',
+ 'Dieulivol (33580)' => '33150',
+ 'Dignac (16410)' => '16119',
+ 'Dinsac (87210)' => '87056',
+ 'Dirac (16410)' => '16120',
+ 'Dissay (86130)' => '86095',
+ 'Diusse (64330)' => '64199',
+ 'Doazit (40700)' => '40089',
+ 'Doazon (64370)' => '64200',
+ 'Doeuil-sur-le-Mignon (17330)' => '17139',
+ 'Dognen (64190)' => '64201',
+ 'Doissat (24170)' => '24151',
+ 'Dolmayrac (47110)' => '47081',
+ 'Dolus-d\'Oléron (17550)' => '17140',
+ 'Domeyrot (23140)' => '23072',
+ 'Domezain-Berraute (64120)' => '64202',
+ 'Domme (24250)' => '24152',
+ 'Dompierre-les-Églises (87190)' => '87057',
+ 'Dompierre-sur-Charente (17610)' => '17141',
+ 'Dompierre-sur-Mer (17139)' => '17142',
+ 'Domps (87120)' => '87058',
+ 'Dondas (47470)' => '47082',
+ 'Donnezac (33860)' => '33151',
+ 'Dontreix (23700)' => '23073',
+ 'Donzac (33410)' => '33152',
+ 'Donzacq (40360)' => '40090',
+ 'Donzenac (19270)' => '19072',
+ 'Douchapt (24350)' => '24154',
+ 'Doudrac (47210)' => '47083',
+ 'Doulezon (33350)' => '33153',
+ 'Doumy (64450)' => '64203',
+ 'Dournazac (87230)' => '87060',
+ 'Doussay (86140)' => '86096',
+ 'Douville (24140)' => '24155',
+ 'Doux (79390)' => '79108',
+ 'Douzains (47330)' => '47084',
+ 'Douzat (16290)' => '16121',
+ 'Douzillac (24190)' => '24157',
+ 'Droux (87190)' => '87061',
+ 'Duhort-Bachen (40800)' => '40091',
+ 'Dumes (40500)' => '40092',
+ 'Dun-le-Palestel (23800)' => '23075',
+ 'Durance (47420)' => '47085',
+ 'Duras (47120)' => '47086',
+ 'Dussac (24270)' => '24158',
+ 'Eaux-Bonnes (64440)' => '64204',
+ 'Ébréon (16140)' => '16122',
+ 'Échallat (16170)' => '16123',
+ 'Échebrune (17800)' => '17145',
+ 'Échillais (17620)' => '17146',
+ 'Échiré (79410)' => '79109',
+ 'Échourgnac (24410)' => '24159',
+ 'Écoyeux (17770)' => '17147',
+ 'Écuras (16220)' => '16124',
+ 'Écurat (17810)' => '17148',
+ 'Édon (16320)' => '16125',
+ 'Égletons (19300)' => '19073',
+ 'Église-Neuve-d\'Issac (24400)' => '24161',
+ 'Église-Neuve-de-Vergt (24380)' => '24160',
+ 'Empuré (16240)' => '16127',
+ 'Engayrac (47470)' => '47087',
+ 'Ensigné (79170)' => '79111',
+ 'Épannes (79270)' => '79112',
+ 'Épargnes (17120)' => '17152',
+ 'Épenède (16490)' => '16128',
+ 'Éraville (16120)' => '16129',
+ 'Escalans (40310)' => '40093',
+ 'Escassefort (47350)' => '47088',
+ 'Escaudes (33840)' => '33155',
+ 'Escaunets (65500)' => '65160',
+ 'Esclottes (47120)' => '47089',
+ 'Escoire (24420)' => '24162',
+ 'Escos (64270)' => '64205',
+ 'Escot (64490)' => '64206',
+ 'Escou (64870)' => '64207',
+ 'Escoubès (64160)' => '64208',
+ 'Escource (40210)' => '40094',
+ 'Escoussans (33760)' => '33156',
+ 'Escout (64870)' => '64209',
+ 'Escurès (64350)' => '64210',
+ 'Eslourenties-Daban (64420)' => '64211',
+ 'Esnandes (17137)' => '17153',
+ 'Espagnac (19150)' => '19075',
+ 'Espartignac (19140)' => '19076',
+ 'Espéchède (64160)' => '64212',
+ 'Espelette (64250)' => '64213',
+ 'Espès-Undurein (64130)' => '64214',
+ 'Espiens (47600)' => '47090',
+ 'Espiet (33420)' => '33157',
+ 'Espiute (64390)' => '64215',
+ 'Espoey (64420)' => '64216',
+ 'Esquiule (64400)' => '64217',
+ 'Esse (16500)' => '16131',
+ 'Essouvert (17400)' => '17277',
+ 'Estérençuby (64220)' => '64218',
+ 'Estialescq (64290)' => '64219',
+ 'Estibeaux (40290)' => '40095',
+ 'Estigarde (40240)' => '40096',
+ 'Estillac (47310)' => '47091',
+ 'Estivals (19600)' => '19077',
+ 'Estivaux (19410)' => '19078',
+ 'Estos (64400)' => '64220',
+ 'Étagnac (16150)' => '16132',
+ 'Étaules (17750)' => '17155',
+ 'Étauliers (33820)' => '33159',
+ 'Etcharry (64120)' => '64221',
+ 'Etchebar (64470)' => '64222',
+ 'Étouars (24360)' => '24163',
+ 'Étriac (16250)' => '16133',
+ 'Etsaut (64490)' => '64223',
+ 'Eugénie-les-Bains (40320)' => '40097',
+ 'Évaux-les-Bains (23110)' => '23076',
+ 'Excideuil (24160)' => '24164',
+ 'Exideuil (16150)' => '16134',
+ 'Exireuil (79400)' => '79114',
+ 'Exoudun (79800)' => '79115',
+ 'Expiremont (17130)' => '17156',
+ 'Eybouleuf (87400)' => '87062',
+ 'Eyburie (19140)' => '19079',
+ 'Eygurande (19340)' => '19080',
+ 'Eygurande-et-Gardedeuil (24700)' => '24165',
+ 'Eyjeaux (87220)' => '87063',
+ 'Eyliac (24330)' => '24166',
+ 'Eymet (24500)' => '24167',
+ 'Eymouthiers (16220)' => '16135',
+ 'Eymoutiers (87120)' => '87064',
+ 'Eynesse (33220)' => '33160',
+ 'Eyrans (33390)' => '33161',
+ 'Eyrein (19800)' => '19081',
+ 'Eyres-Moncube (40500)' => '40098',
+ 'Eysines (33320)' => '33162',
+ 'Eysus (64400)' => '64224',
+ 'Eyvirat (24460)' => '24170',
+ 'Eyzerac (24800)' => '24171',
+ 'Faleyras (33760)' => '33163',
+ 'Fals (47220)' => '47092',
+ 'Fanlac (24290)' => '24174',
+ 'Fargues (33210)' => '33164',
+ 'Fargues (40500)' => '40099',
+ 'Fargues-Saint-Hilaire (33370)' => '33165',
+ 'Fargues-sur-Ourbise (47700)' => '47093',
+ 'Fauguerolles (47400)' => '47094',
+ 'Fauillet (47400)' => '47095',
+ 'Faurilles (24560)' => '24176',
+ 'Faux (24560)' => '24177',
+ 'Faux-la-Montagne (23340)' => '23077',
+ 'Faux-Mazuras (23400)' => '23078',
+ 'Favars (19330)' => '19082',
+ 'Faye-l\'Abbesse (79350)' => '79116',
+ 'Faye-sur-Ardin (79160)' => '79117',
+ 'Féas (64570)' => '64225',
+ 'Felletin (23500)' => '23079',
+ 'Fénery (79450)' => '79118',
+ 'Féniers (23100)' => '23080',
+ 'Fenioux (17350)' => '17157',
+ 'Fenioux (79160)' => '79119',
+ 'Ferrensac (47330)' => '47096',
+ 'Ferrières (17170)' => '17158',
+ 'Festalemps (24410)' => '24178',
+ 'Feugarolles (47230)' => '47097',
+ 'Feuillade (16380)' => '16137',
+ 'Feyt (19340)' => '19083',
+ 'Feytiat (87220)' => '87065',
+ 'Fichous-Riumayou (64410)' => '64226',
+ 'Fieux (47600)' => '47098',
+ 'Firbeix (24450)' => '24180',
+ 'Flaugeac (24240)' => '24181',
+ 'Flaujagues (33350)' => '33168',
+ 'Flavignac (87230)' => '87066',
+ 'Flayat (23260)' => '23081',
+ 'Fléac (16730)' => '16138',
+ 'Fléac-sur-Seugne (17800)' => '17159',
+ 'Fleix (86300)' => '86098',
+ 'Fleurac (16200)' => '16139',
+ 'Fleurac (24580)' => '24183',
+ 'Fleurat (23320)' => '23082',
+ 'Fleuré (86340)' => '86099',
+ 'Floirac (17120)' => '17160',
+ 'Floirac (33270)' => '33167',
+ 'Florimont-Gaumier (24250)' => '24184',
+ 'Floudès (33190)' => '33169',
+ 'Folles (87250)' => '87067',
+ 'Fomperron (79340)' => '79121',
+ 'Fongrave (47260)' => '47099',
+ 'Fonroque (24500)' => '24186',
+ 'Fontaine-Chalendray (17510)' => '17162',
+ 'Fontaine-le-Comte (86240)' => '86100',
+ 'Fontaines-d\'Ozillac (17500)' => '17163',
+ 'Fontanières (23110)' => '23083',
+ 'Fontclaireau (16230)' => '16140',
+ 'Fontcouverte (17100)' => '17164',
+ 'Fontenet (17400)' => '17165',
+ 'Fontenille (16230)' => '16141',
+ 'Fontenille-Saint-Martin-d\'Entraigues (79110)' => '79122',
+ 'Fontet (33190)' => '33170',
+ 'Forges (17290)' => '17166',
+ 'Forgès (19380)' => '19084',
+ 'Fors (79230)' => '79125',
+ 'Fossemagne (24210)' => '24188',
+ 'Fossès-et-Baleyssac (33190)' => '33171',
+ 'Fougueyrolles (33220)' => '24189',
+ 'Foulayronnes (47510)' => '47100',
+ 'Fouleix (24380)' => '24190',
+ 'Fouquebrune (16410)' => '16143',
+ 'Fouqueure (16140)' => '16144',
+ 'Fouras (17450)' => '17168',
+ 'Fourques-sur-Garonne (47200)' => '47101',
+ 'Fours (33390)' => '33172',
+ 'Foussignac (16200)' => '16145',
+ 'Fraisse (24130)' => '24191',
+ 'Francescas (47600)' => '47102',
+ 'François (79260)' => '79128',
+ 'Francs (33570)' => '33173',
+ 'Fransèches (23480)' => '23086',
+ 'Fréchou (47600)' => '47103',
+ 'Frégimont (47360)' => '47104',
+ 'Frespech (47140)' => '47105',
+ 'Fresselines (23450)' => '23087',
+ 'Fressines (79370)' => '79129',
+ 'Fromental (87250)' => '87068',
+ 'Fronsac (33126)' => '33174',
+ 'Frontenac (33760)' => '33175',
+ 'Frontenay-Rohan-Rohan (79270)' => '79130',
+ 'Frozes (86190)' => '86102',
+ 'Fumel (47500)' => '47106',
+ 'Gaas (40350)' => '40101',
+ 'Gabarnac (33410)' => '33176',
+ 'Gabarret (40310)' => '40102',
+ 'Gabaston (64160)' => '64227',
+ 'Gabat (64120)' => '64228',
+ 'Gabillou (24210)' => '24192',
+ 'Gageac-et-Rouillac (24240)' => '24193',
+ 'Gaillan-en-Médoc (33340)' => '33177',
+ 'Gaillères (40090)' => '40103',
+ 'Gajac (33430)' => '33178',
+ 'Gajoubert (87330)' => '87069',
+ 'Galapian (47190)' => '47107',
+ 'Galgon (33133)' => '33179',
+ 'Gamarde-les-Bains (40380)' => '40104',
+ 'Gamarthe (64220)' => '64229',
+ 'Gan (64290)' => '64230',
+ 'Gans (33430)' => '33180',
+ 'Garat (16410)' => '16146',
+ 'Gardegan-et-Tourtirac (33350)' => '33181',
+ 'Gardères (65320)' => '65185',
+ 'Gardes-le-Pontaroux (16320)' => '16147',
+ 'Gardonne (24680)' => '24194',
+ 'Garein (40420)' => '40105',
+ 'Garindein (64130)' => '64231',
+ 'Garlède-Mondebat (64450)' => '64232',
+ 'Garlin (64330)' => '64233',
+ 'Garos (64410)' => '64234',
+ 'Garrey (40180)' => '40106',
+ 'Garris (64120)' => '64235',
+ 'Garrosse (40110)' => '40107',
+ 'Gartempe (23320)' => '23088',
+ 'Gastes (40160)' => '40108',
+ 'Gaugeac (24540)' => '24195',
+ 'Gaujac (47200)' => '47108',
+ 'Gaujacq (40330)' => '40109',
+ 'Gauriac (33710)' => '33182',
+ 'Gauriaguet (33240)' => '33183',
+ 'Gavaudun (47150)' => '47109',
+ 'Gayon (64350)' => '64236',
+ 'Geaune (40320)' => '40110',
+ 'Geay (17250)' => '17171',
+ 'Geay (79330)' => '79131',
+ 'Gelos (64110)' => '64237',
+ 'Geloux (40090)' => '40111',
+ 'Gémozac (17260)' => '17172',
+ 'Genac-Bignac (16170)' => '16148',
+ 'Gençay (86160)' => '86103',
+ 'Générac (33920)' => '33184',
+ 'Génis (24160)' => '24196',
+ 'Génissac (33420)' => '33185',
+ 'Genneton (79150)' => '79132',
+ 'Genouillac (16270)' => '16149',
+ 'Genouillac (23350)' => '23089',
+ 'Genouillé (17430)' => '17174',
+ 'Genouillé (86250)' => '86104',
+ 'Gensac (33890)' => '33186',
+ 'Gensac-la-Pallue (16130)' => '16150',
+ 'Genté (16130)' => '16151',
+ 'Gentioux-Pigerolles (23340)' => '23090',
+ 'Ger (64530)' => '64238',
+ 'Gerderest (64160)' => '64239',
+ 'Gère-Bélesten (64260)' => '64240',
+ 'Germignac (17520)' => '17175',
+ 'Germond-Rouvre (79220)' => '79133',
+ 'Géronce (64400)' => '64241',
+ 'Gestas (64190)' => '64242',
+ 'Géus-d\'Arzacq (64370)' => '64243',
+ 'Geüs-d\'Oloron (64400)' => '64244',
+ 'Gibourne (17160)' => '17176',
+ 'Gibret (40380)' => '40112',
+ 'Gimel-les-Cascades (19800)' => '19085',
+ 'Gimeux (16130)' => '16152',
+ 'Ginestet (24130)' => '24197',
+ 'Gioux (23500)' => '23091',
+ 'Gironde-sur-Dropt (33190)' => '33187',
+ 'Giscos (33840)' => '33188',
+ 'Givrezac (17260)' => '17178',
+ 'Gizay (86340)' => '86105',
+ 'Glandon (87500)' => '87071',
+ 'Glanges (87380)' => '87072',
+ 'Glénay (79330)' => '79134',
+ 'Glénic (23380)' => '23092',
+ 'Glénouze (86200)' => '86106',
+ 'Goès (64400)' => '64245',
+ 'Gomer (64420)' => '64246',
+ 'Gond-Pontouvre (16160)' => '16154',
+ 'Gondeville (16200)' => '16153',
+ 'Gontaud-de-Nogaret (47400)' => '47110',
+ 'Goos (40180)' => '40113',
+ 'Gornac (33540)' => '33189',
+ 'Gorre (87310)' => '87073',
+ 'Gotein-Libarrenx (64130)' => '64247',
+ 'Goualade (33840)' => '33190',
+ 'Gouex (86320)' => '86107',
+ 'Goulles (19430)' => '19086',
+ 'Gourbera (40990)' => '40114',
+ 'Gourdon-Murat (19170)' => '19087',
+ 'Gourgé (79200)' => '79135',
+ 'Gournay-Loizé (79110)' => '79136',
+ 'Gours (33660)' => '33191',
+ 'Gourville (16170)' => '16156',
+ 'Gourvillette (17490)' => '17180',
+ 'Gousse (40465)' => '40115',
+ 'Gout-Rossignol (24320)' => '24199',
+ 'Gouts (40400)' => '40116',
+ 'Gouzon (23230)' => '23093',
+ 'Gradignan (33170)' => '33192',
+ 'Grand-Brassac (24350)' => '24200',
+ 'Grandjean (17350)' => '17181',
+ 'Grandsaigne (19300)' => '19088',
+ 'Granges-d\'Ans (24390)' => '24202',
+ 'Granges-sur-Lot (47260)' => '47111',
+ 'Granzay-Gript (79360)' => '79137',
+ 'Grassac (16380)' => '16158',
+ 'Grateloup-Saint-Gayrand (47400)' => '47112',
+ 'Graves-Saint-Amant (16120)' => '16297',
+ 'Grayan-et-l\'Hôpital (33590)' => '33193',
+ 'Grayssas (47270)' => '47113',
+ 'Grenade-sur-l\'Adour (40270)' => '40117',
+ 'Grézac (17120)' => '17183',
+ 'Grèzes (24120)' => '24204',
+ 'Grézet-Cavagnan (47250)' => '47114',
+ 'Grézillac (33420)' => '33194',
+ 'Grignols (24110)' => '24205',
+ 'Grignols (33690)' => '33195',
+ 'Grives (24170)' => '24206',
+ 'Groléjac (24250)' => '24207',
+ 'Gros-Chastang (19320)' => '19089',
+ 'Grun-Bordas (24380)' => '24208',
+ 'Guéret (23000)' => '23096',
+ 'Guérin (47250)' => '47115',
+ 'Guesnes (86420)' => '86109',
+ 'Guéthary (64210)' => '64249',
+ 'Guiche (64520)' => '64250',
+ 'Guillac (33420)' => '33196',
+ 'Guillos (33720)' => '33197',
+ 'Guimps (16300)' => '16160',
+ 'Guinarthe-Parenties (64390)' => '64251',
+ 'Guitinières (17500)' => '17187',
+ 'Guîtres (33230)' => '33198',
+ 'Guizengeard (16480)' => '16161',
+ 'Gujan-Mestras (33470)' => '33199',
+ 'Gumond (19320)' => '19090',
+ 'Gurat (16320)' => '16162',
+ 'Gurmençon (64400)' => '64252',
+ 'Gurs (64190)' => '64253',
+ 'Habas (40290)' => '40118',
+ 'Hagetaubin (64370)' => '64254',
+ 'Hagetmau (40700)' => '40119',
+ 'Haimps (17160)' => '17188',
+ 'Haims (86310)' => '86110',
+ 'Halsou (64480)' => '64255',
+ 'Hanc (79110)' => '79140',
+ 'Hasparren (64240)' => '64256',
+ 'Hastingues (40300)' => '40120',
+ 'Hauriet (40250)' => '40121',
+ 'Haut-de-Bosdarros (64800)' => '64257',
+ 'Haut-Mauco (40280)' => '40122',
+ 'Hautefage (19400)' => '19091',
+ 'Hautefage-la-Tour (47340)' => '47117',
+ 'Hautefaye (24300)' => '24209',
+ 'Hautefort (24390)' => '24210',
+ 'Hautesvignes (47400)' => '47118',
+ 'Haux (33550)' => '33201',
+ 'Haux (64470)' => '64258',
+ 'Hélette (64640)' => '64259',
+ 'Hendaye (64700)' => '64260',
+ 'Herm (40990)' => '40123',
+ 'Herré (40310)' => '40124',
+ 'Herrère (64680)' => '64261',
+ 'Heugas (40180)' => '40125',
+ 'Hiers-Brouage (17320)' => '17189',
+ 'Hiersac (16290)' => '16163',
+ 'Hiesse (16490)' => '16164',
+ 'Higuères-Souye (64160)' => '64262',
+ 'Hinx (40180)' => '40126',
+ 'Hontanx (40190)' => '40127',
+ 'Horsarrieu (40700)' => '40128',
+ 'Hosta (64120)' => '64265',
+ 'Hostens (33125)' => '33202',
+ 'Houeillès (47420)' => '47119',
+ 'Houlette (16200)' => '16165',
+ 'Hours (64420)' => '64266',
+ 'Hourtin (33990)' => '33203',
+ 'Hure (33190)' => '33204',
+ 'Ibarrolle (64120)' => '64267',
+ 'Idaux-Mendy (64130)' => '64268',
+ 'Idron (64320)' => '64269',
+ 'Igon (64800)' => '64270',
+ 'Iholdy (64640)' => '64271',
+ 'Île-d\'Aix (17123)' => '17004',
+ 'Ilharre (64120)' => '64272',
+ 'Illats (33720)' => '33205',
+ 'Ingrandes (86220)' => '86111',
+ 'Irais (79600)' => '79141',
+ 'Irissarry (64780)' => '64273',
+ 'Irouléguy (64220)' => '64274',
+ 'Isle (87170)' => '87075',
+ 'Isle-Saint-Georges (33640)' => '33206',
+ 'Ispoure (64220)' => '64275',
+ 'Issac (24400)' => '24211',
+ 'Issigeac (24560)' => '24212',
+ 'Issor (64570)' => '64276',
+ 'Issoudun-Létrieix (23130)' => '23097',
+ 'Isturits (64240)' => '64277',
+ 'Iteuil (86240)' => '86113',
+ 'Itxassou (64250)' => '64279',
+ 'Izeste (64260)' => '64280',
+ 'Izon (33450)' => '33207',
+ 'Jabreilles-les-Bordes (87370)' => '87076',
+ 'Jalesches (23270)' => '23098',
+ 'Janailhac (87800)' => '87077',
+ 'Janaillat (23250)' => '23099',
+ 'Jardres (86800)' => '86114',
+ 'Jarnac (16200)' => '16167',
+ 'Jarnac-Champagne (17520)' => '17192',
+ 'Jarnages (23140)' => '23100',
+ 'Jasses (64190)' => '64281',
+ 'Jatxou (64480)' => '64282',
+ 'Jau-Dignac-et-Loirac (33590)' => '33208',
+ 'Jauldes (16560)' => '16168',
+ 'Jaunay-Clan (86130)' => '86115',
+ 'Jaure (24140)' => '24213',
+ 'Javerdat (87520)' => '87078',
+ 'Javerlhac-et-la-Chapelle-Saint-Robert (24300)' => '24214',
+ 'Javrezac (16100)' => '16169',
+ 'Jaxu (64220)' => '64283',
+ 'Jayac (24590)' => '24215',
+ 'Jazeneuil (86600)' => '86116',
+ 'Jazennes (17260)' => '17196',
+ 'Jonzac (17500)' => '17197',
+ 'Josse (40230)' => '40129',
+ 'Jouac (87890)' => '87080',
+ 'Jouhet (86500)' => '86117',
+ 'Jouillat (23220)' => '23101',
+ 'Jourgnac (87800)' => '87081',
+ 'Journet (86290)' => '86118',
+ 'Journiac (24260)' => '24217',
+ 'Joussé (86350)' => '86119',
+ 'Jugazan (33420)' => '33209',
+ 'Jugeals-Nazareth (19500)' => '19093',
+ 'Juicq (17770)' => '17198',
+ 'Juignac (16190)' => '16170',
+ 'Juillac (19350)' => '19094',
+ 'Juillac (33890)' => '33210',
+ 'Juillac-le-Coq (16130)' => '16171',
+ 'Juillé (16230)' => '16173',
+ 'Juillé (79170)' => '79142',
+ 'Julienne (16200)' => '16174',
+ 'Jumilhac-le-Grand (24630)' => '24218',
+ 'Jurançon (64110)' => '64284',
+ 'Juscorps (79230)' => '79144',
+ 'Jusix (47180)' => '47120',
+ 'Jussas (17130)' => '17199',
+ 'Juxue (64120)' => '64285',
+ 'L\'Absie (79240)' => '79001',
+ 'L\'Église-aux-Bois (19170)' => '19074',
+ 'L\'Éguille (17600)' => '17151',
+ 'L\'Hôpital-d\'Orion (64270)' => '64263',
+ 'L\'Hôpital-Saint-Blaise (64130)' => '64264',
+ 'L\'Houmeau (17137)' => '17190',
+ 'L\'Isle-d\'Espagnac (16340)' => '16166',
+ 'L\'Isle-Jourdain (86150)' => '86112',
+ 'La Bachellerie (24210)' => '24020',
+ 'La Barde (17360)' => '17033',
+ 'La Bastide-Clairence (64240)' => '64289',
+ 'La Bataille (79110)' => '79027',
+ 'La Bazeuge (87210)' => '87008',
+ 'La Boissière-d\'Ans (24640)' => '24047',
+ 'La Boissière-en-Gâtine (79310)' => '79040',
+ 'La Brède (33650)' => '33213',
+ 'La Brée-les-Bains (17840)' => '17486',
+ 'La Brionne (23000)' => '23033',
+ 'La Brousse (17160)' => '17071',
+ 'La Bussière (86310)' => '86040',
+ 'La Cassagne (24120)' => '24085',
+ 'La Celle-Dunoise (23800)' => '23039',
+ 'La Celle-sous-Gouzon (23230)' => '23040',
+ 'La Cellette (23350)' => '23041',
+ 'La Chapelle (16140)' => '16081',
+ 'La Chapelle-Aubareil (24290)' => '24106',
+ 'La Chapelle-aux-Brocs (19360)' => '19043',
+ 'La Chapelle-aux-Saints (19120)' => '19044',
+ 'La Chapelle-Baloue (23160)' => '23050',
+ 'La Chapelle-Bâton (79220)' => '79070',
+ 'La Chapelle-Bâton (86250)' => '86055',
+ 'La Chapelle-Bertrand (79200)' => '79071',
+ 'La Chapelle-des-Pots (17100)' => '17089',
+ 'La Chapelle-Faucher (24530)' => '24107',
+ 'La Chapelle-Gonaguet (24350)' => '24108',
+ 'La Chapelle-Grésignac (24320)' => '24109',
+ 'La Chapelle-Montabourlet (24320)' => '24110',
+ 'La Chapelle-Montbrandeix (87440)' => '87037',
+ 'La Chapelle-Montmoreau (24300)' => '24111',
+ 'La Chapelle-Montreuil (86470)' => '86056',
+ 'La Chapelle-Moulière (86210)' => '86058',
+ 'La Chapelle-Pouilloux (79190)' => '79074',
+ 'La Chapelle-Saint-Étienne (79240)' => '79075',
+ 'La Chapelle-Saint-Géraud (19430)' => '19045',
+ 'La Chapelle-Saint-Jean (24390)' => '24113',
+ 'La Chapelle-Saint-Laurent (79430)' => '79076',
+ 'La Chapelle-Saint-Martial (23250)' => '23051',
+ 'La Chapelle-Taillefert (23000)' => '23052',
+ 'La Chapelle-Thireuil (79160)' => '79077',
+ 'La Chaussade (23200)' => '23059',
+ 'La Chaussée (86330)' => '86069',
+ 'La Chèvrerie (16240)' => '16098',
+ 'La Clisse (17600)' => '17112',
+ 'La Clotte (17360)' => '17113',
+ 'La Coquille (24450)' => '24133',
+ 'La Couarde (79800)' => '79098',
+ 'La Couarde-sur-Mer (17670)' => '17121',
+ 'La Couronne (16400)' => '16113',
+ 'La Courtine (23100)' => '23067',
+ 'La Crèche (79260)' => '79048',
+ 'La Croisille-sur-Briance (87130)' => '87051',
+ 'La Croix-Blanche (47340)' => '47075',
+ 'La Croix-Comtesse (17330)' => '17137',
+ 'La Croix-sur-Gartempe (87210)' => '87052',
+ 'La Dornac (24120)' => '24153',
+ 'La Douze (24330)' => '24156',
+ 'La Faye (16700)' => '16136',
+ 'La Ferrière-Airoux (86160)' => '86097',
+ 'La Ferrière-en-Parthenay (79390)' => '79120',
+ 'La Feuillade (24120)' => '24179',
+ 'La Flotte (17630)' => '17161',
+ 'La Force (24130)' => '24222',
+ 'La Forêt-de-Tessé (16240)' => '16142',
+ 'La Forêt-du-Temple (23360)' => '23084',
+ 'La Forêt-sur-Sèvre (79380)' => '79123',
+ 'La Foye-Monjault (79360)' => '79127',
+ 'La Frédière (17770)' => '17169',
+ 'La Genétouze (17360)' => '17173',
+ 'La Geneytouse (87400)' => '87070',
+ 'La Gonterie-Boulouneix (24310)' => '24198',
+ 'La Grève-sur-Mignon (17170)' => '17182',
+ 'La Grimaudière (86330)' => '86108',
+ 'La Gripperie-Saint-Symphorien (17620)' => '17184',
+ 'La Jard (17460)' => '17191',
+ 'La Jarne (17220)' => '17193',
+ 'La Jarrie (17220)' => '17194',
+ 'La Jarrie-Audouin (17330)' => '17195',
+ 'La Jemaye (24410)' => '24216',
+ 'La Jonchère-Saint-Maurice (87340)' => '87079',
+ 'La Laigne (17170)' => '17201',
+ 'La Lande-de-Fronsac (33240)' => '33219',
+ 'La Magdeleine (16240)' => '16197',
+ 'La Mazière-aux-Bons-Hommes (23260)' => '23129',
+ 'La Meyze (87800)' => '87096',
+ 'La Mothe-Saint-Héray (79800)' => '79184',
+ 'La Nouaille (23500)' => '23144',
+ 'La Péruse (16270)' => '16259',
+ 'La Petite-Boissière (79700)' => '79207',
+ 'La Peyratte (79200)' => '79208',
+ 'La Porcherie (87380)' => '87120',
+ 'La Pouge (23250)' => '23157',
+ 'La Puye (86260)' => '86202',
+ 'La Réole (33190)' => '33352',
+ 'La Réunion (47700)' => '47222',
+ 'La Rivière (33126)' => '33356',
+ 'La Roche-Canillac (19320)' => '19174',
+ 'La Roche-Chalais (24490)' => '24354',
+ 'La Roche-l\'Abeille (87800)' => '87127',
+ 'La Roche-Posay (86270)' => '86207',
+ 'La Roche-Rigault (86200)' => '86079',
+ 'La Rochebeaucourt-et-Argentine (24340)' => '24353',
+ 'La Rochefoucauld (16110)' => '16281',
+ 'La Rochelle (17000)' => '17300',
+ 'La Rochénard (79270)' => '79229',
+ 'La Rochette (16110)' => '16282',
+ 'La Ronde (17170)' => '17303',
+ 'La Roque-Gageac (24250)' => '24355',
+ 'La Roquille (33220)' => '33360',
+ 'La Saunière (23000)' => '23169',
+ 'La Sauve (33670)' => '33505',
+ 'La Sauvetat-de-Savères (47270)' => '47289',
+ 'La Sauvetat-du-Dropt (47800)' => '47290',
+ 'La Sauvetat-sur-Lède (47150)' => '47291',
+ 'La Serre-Bussière-Vieille (23190)' => '23172',
+ 'La Souterraine (23300)' => '23176',
+ 'La Tâche (16260)' => '16377',
+ 'La Teste-de-Buch (33260)' => '33529',
+ 'La Tour-Blanche (24320)' => '24554',
+ 'La Tremblade (17390)' => '17452',
+ 'La Trimouille (86290)' => '86273',
+ 'La Vallée (17250)' => '17455',
+ 'La Vergne (17400)' => '17465',
+ 'La Villedieu (17470)' => '17471',
+ 'La Villedieu (23340)' => '23264',
+ 'La Villedieu-du-Clain (86340)' => '86290',
+ 'La Villeneuve (23260)' => '23265',
+ 'La Villetelle (23260)' => '23266',
+ 'Laà-Mondrans (64300)' => '64286',
+ 'Laàs (64390)' => '64287',
+ 'Labarde (33460)' => '33211',
+ 'Labastide-Castel-Amouroux (47250)' => '47121',
+ 'Labastide-Cézéracq (64170)' => '64288',
+ 'Labastide-Chalosse (40700)' => '40130',
+ 'Labastide-d\'Armagnac (40240)' => '40131',
+ 'Labastide-Monréjeau (64170)' => '64290',
+ 'Labastide-Villefranche (64270)' => '64291',
+ 'Labatmale (64530)' => '64292',
+ 'Labatut (40300)' => '40132',
+ 'Labatut (64460)' => '64293',
+ 'Labenne (40530)' => '40133',
+ 'Labescau (33690)' => '33212',
+ 'Labets-Biscay (64120)' => '64294',
+ 'Labeyrie (64300)' => '64295',
+ 'Labouheyre (40210)' => '40134',
+ 'Labretonie (47350)' => '47122',
+ 'Labrit (40420)' => '40135',
+ 'Lacadée (64300)' => '64296',
+ 'Lacajunte (40320)' => '40136',
+ 'Lacanau (33680)' => '33214',
+ 'Lacapelle-Biron (47150)' => '47123',
+ 'Lacarre (64220)' => '64297',
+ 'Lacarry-Arhan-Charritte-de-Haut (64470)' => '64298',
+ 'Lacaussade (47150)' => '47124',
+ 'Lacelle (19170)' => '19095',
+ 'Lacépède (47360)' => '47125',
+ 'Lachaise (16300)' => '16176',
+ 'Lachapelle (47350)' => '47126',
+ 'Lacommande (64360)' => '64299',
+ 'Lacq (64170)' => '64300',
+ 'Lacquy (40120)' => '40137',
+ 'Lacrabe (40700)' => '40138',
+ 'Lacropte (24380)' => '24220',
+ 'Ladapeyre (23270)' => '23102',
+ 'Ladaux (33760)' => '33215',
+ 'Ladignac-le-Long (87500)' => '87082',
+ 'Ladignac-sur-Rondelles (19150)' => '19096',
+ 'Ladiville (16120)' => '16177',
+ 'Lados (33124)' => '33216',
+ 'Lafage-sur-Sombre (19320)' => '19097',
+ 'Lafat (23800)' => '23103',
+ 'Lafitte-sur-Lot (47320)' => '47127',
+ 'Lafox (47240)' => '47128',
+ 'Lagarde-Enval (19150)' => '19098',
+ 'Lagarde-sur-le-Né (16300)' => '16178',
+ 'Lagarrigue (47190)' => '47129',
+ 'Lageon (79200)' => '79145',
+ 'Lagleygeolle (19500)' => '19099',
+ 'Laglorieuse (40090)' => '40139',
+ 'Lagor (64150)' => '64301',
+ 'Lagorce (33230)' => '33218',
+ 'Lagord (17140)' => '17200',
+ 'Lagos (64800)' => '64302',
+ 'Lagrange (40240)' => '40140',
+ 'Lagraulière (19700)' => '19100',
+ 'Lagruère (47400)' => '47130',
+ 'Laguenne (19150)' => '19101',
+ 'Laguinge-Restoue (64470)' => '64303',
+ 'Lagupie (47180)' => '47131',
+ 'Lahonce (64990)' => '64304',
+ 'Lahontan (64270)' => '64305',
+ 'Lahosse (40250)' => '40141',
+ 'Lahourcade (64150)' => '64306',
+ 'Lalande-de-Pomerol (33500)' => '33222',
+ 'Lalandusse (47330)' => '47132',
+ 'Lalinde (24150)' => '24223',
+ 'Lalongue (64350)' => '64307',
+ 'Lalonquette (64450)' => '64308',
+ 'Laluque (40465)' => '40142',
+ 'Lamarque (33460)' => '33220',
+ 'Lamayou (64460)' => '64309',
+ 'Lamazière-Basse (19160)' => '19102',
+ 'Lamazière-Haute (19340)' => '19103',
+ 'Lamongerie (19510)' => '19104',
+ 'Lamontjoie (47310)' => '47133',
+ 'Lamonzie-Montastruc (24520)' => '24224',
+ 'Lamonzie-Saint-Martin (24680)' => '24225',
+ 'Lamothe (40250)' => '40143',
+ 'Lamothe-Landerron (33190)' => '33221',
+ 'Lamothe-Montravel (24230)' => '24226',
+ 'Landerrouat (33790)' => '33223',
+ 'Landerrouet-sur-Ségur (33540)' => '33224',
+ 'Landes (17380)' => '17202',
+ 'Landiras (33720)' => '33225',
+ 'Landrais (17290)' => '17203',
+ 'Langoiran (33550)' => '33226',
+ 'Langon (33210)' => '33227',
+ 'Lanne-en-Barétous (64570)' => '64310',
+ 'Lannecaube (64350)' => '64311',
+ 'Lanneplaà (64300)' => '64312',
+ 'Lannes (47170)' => '47134',
+ 'Lanouaille (24270)' => '24227',
+ 'Lanquais (24150)' => '24228',
+ 'Lansac (33710)' => '33228',
+ 'Lantabat (64640)' => '64313',
+ 'Lanteuil (19190)' => '19105',
+ 'Lanton (33138)' => '33229',
+ 'Laparade (47260)' => '47135',
+ 'Laperche (47800)' => '47136',
+ 'Lapleau (19550)' => '19106',
+ 'Laplume (47310)' => '47137',
+ 'Lapouyade (33620)' => '33230',
+ 'Laprade (16390)' => '16180',
+ 'Larbey (40250)' => '40144',
+ 'Larceveau-Arros-Cibits (64120)' => '64314',
+ 'Larche (19600)' => '19107',
+ 'Largeasse (79240)' => '79147',
+ 'Laroche-près-Feyt (19340)' => '19108',
+ 'Laroin (64110)' => '64315',
+ 'Laroque (33410)' => '33231',
+ 'Laroque-Timbaut (47340)' => '47138',
+ 'Larrau (64560)' => '64316',
+ 'Larressore (64480)' => '64317',
+ 'Larreule (64410)' => '64318',
+ 'Larribar-Sorhapuru (64120)' => '64319',
+ 'Larrivière-Saint-Savin (40270)' => '40145',
+ 'Lartigue (33840)' => '33232',
+ 'Laruns (64440)' => '64320',
+ 'Laruscade (33620)' => '33233',
+ 'Larzac (24170)' => '24230',
+ 'Lascaux (19130)' => '19109',
+ 'Lasclaveries (64450)' => '64321',
+ 'Lasse (64220)' => '64322',
+ 'Lasserre (47600)' => '47139',
+ 'Lasserre (64350)' => '64323',
+ 'Lasseube (64290)' => '64324',
+ 'Lasseubetat (64290)' => '64325',
+ 'Lathus-Saint-Rémy (86390)' => '86120',
+ 'Latillé (86190)' => '86121',
+ 'Latresne (33360)' => '33234',
+ 'Latrille (40800)' => '40146',
+ 'Latronche (19160)' => '19110',
+ 'Laugnac (47360)' => '47140',
+ 'Laurède (40250)' => '40147',
+ 'Lauret (40320)' => '40148',
+ 'Laurière (87370)' => '87083',
+ 'Laussou (47150)' => '47141',
+ 'Lauthiers (86300)' => '86122',
+ 'Lauzun (47410)' => '47142',
+ 'Laval-sur-Luzège (19550)' => '19111',
+ 'Lavalade (24540)' => '24231',
+ 'Lavardac (47230)' => '47143',
+ 'Lavaufranche (23600)' => '23104',
+ 'Lavaur (24550)' => '24232',
+ 'Lavausseau (86470)' => '86123',
+ 'Lavaveix-les-Mines (23150)' => '23105',
+ 'Lavazan (33690)' => '33235',
+ 'Lavergne (47800)' => '47144',
+ 'Laveyssière (24130)' => '24233',
+ 'Lavignac (87230)' => '87084',
+ 'Lavoux (86800)' => '86124',
+ 'Lay-Lamidou (64190)' => '64326',
+ 'Layrac (47390)' => '47145',
+ 'Le Barp (33114)' => '33029',
+ 'Le Beugnon (79130)' => '79035',
+ 'Le Bois-Plage-en-Ré (17580)' => '17051',
+ 'Le Bouchage (16350)' => '16054',
+ 'Le Bourdeix (24300)' => '24056',
+ 'Le Bourdet (79210)' => '79046',
+ 'Le Bourg-d\'Hem (23220)' => '23029',
+ 'Le Bouscat (33110)' => '33069',
+ 'Le Breuil-Bernard (79320)' => '79051',
+ 'Le Bugue (24260)' => '24067',
+ 'Le Buis (87140)' => '87023',
+ 'Le Buisson-de-Cadouin (24480)' => '24068',
+ 'Le Busseau (79240)' => '79059',
+ 'Le Chalard (87500)' => '87031',
+ 'Le Change (24640)' => '24103',
+ 'Le Chastang (19190)' => '19048',
+ 'Le Château-d\'Oléron (17480)' => '17093',
+ 'Le Châtenet-en-Dognon (87400)' => '87042',
+ 'Le Chauchet (23130)' => '23058',
+ 'Le Chay (17600)' => '17097',
+ 'Le Chillou (79600)' => '79089',
+ 'Le Compas (23700)' => '23066',
+ 'Le Donzeil (23480)' => '23074',
+ 'Le Dorat (87210)' => '87059',
+ 'Le Douhet (17100)' => '17143',
+ 'Le Fieu (33230)' => '33166',
+ 'Le Fleix (24130)' => '24182',
+ 'Le Fouilloux (17270)' => '17167',
+ 'Le Frêche (40190)' => '40100',
+ 'Le Gicq (17160)' => '17177',
+ 'Le Grand-Bourg (23240)' => '23095',
+ 'Le Grand-Madieu (16450)' => '16157',
+ 'Le Grand-Village-Plage (17370)' => '17485',
+ 'Le Gua (17600)' => '17185',
+ 'Le Gué-d\'Alleré (17540)' => '17186',
+ 'Le Haillan (33185)' => '33200',
+ 'Le Jardin (19300)' => '19092',
+ 'Le Lardin-Saint-Lazare (24570)' => '24229',
+ 'Le Leuy (40250)' => '40153',
+ 'Le Lindois (16310)' => '16188',
+ 'Le Lonzac (19470)' => '19118',
+ 'Le Mas-d\'Agenais (47430)' => '47159',
+ 'Le Mas-d\'Artige (23100)' => '23125',
+ 'Le Monteil-au-Vicomte (23460)' => '23134',
+ 'Le Mung (17350)' => '17252',
+ 'Le Nizan (33430)' => '33305',
+ 'Le Palais-sur-Vienne (87410)' => '87113',
+ 'Le Passage (47520)' => '47201',
+ 'Le Pescher (19190)' => '19163',
+ 'Le Pian-Médoc (33290)' => '33322',
+ 'Le Pian-sur-Garonne (33490)' => '33323',
+ 'Le Pin (17210)' => '17276',
+ 'Le Pin (79140)' => '79210',
+ 'Le Pizou (24700)' => '24329',
+ 'Le Porge (33680)' => '33333',
+ 'Le Pout (33670)' => '33335',
+ 'Le Puy (33580)' => '33345',
+ 'Le Retail (79130)' => '79226',
+ 'Le Rochereau (86170)' => '86208',
+ 'Le Sen (40420)' => '40297',
+ 'Le Seure (17770)' => '17426',
+ 'Le Taillan-Médoc (33320)' => '33519',
+ 'Le Tallud (79200)' => '79322',
+ 'Le Tâtre (16360)' => '16380',
+ 'Le Teich (33470)' => '33527',
+ 'Le Temple (33680)' => '33528',
+ 'Le Temple-sur-Lot (47110)' => '47306',
+ 'Le Thou (17290)' => '17447',
+ 'Le Tourne (33550)' => '33534',
+ 'Le Tuzan (33125)' => '33536',
+ 'Le Vanneau-Irleau (79270)' => '79337',
+ 'Le Verdon-sur-Mer (33123)' => '33544',
+ 'Le Vert (79170)' => '79346',
+ 'Le Vieux-Cérier (16350)' => '16403',
+ 'Le Vigeant (86150)' => '86289',
+ 'Le Vigen (87110)' => '87205',
+ 'Le Vignau (40270)' => '40329',
+ 'Lecumberry (64220)' => '64327',
+ 'Lédat (47300)' => '47146',
+ 'Ledeuix (64400)' => '64328',
+ 'Lée (64320)' => '64329',
+ 'Lées-Athas (64490)' => '64330',
+ 'Lège-Cap-Ferret (33950)' => '33236',
+ 'Léguillac-de-Cercles (24340)' => '24235',
+ 'Léguillac-de-l\'Auche (24110)' => '24236',
+ 'Leigné-les-Bois (86450)' => '86125',
+ 'Leigné-sur-Usseau (86230)' => '86127',
+ 'Leignes-sur-Fontaine (86300)' => '86126',
+ 'Lembeye (64350)' => '64331',
+ 'Lembras (24100)' => '24237',
+ 'Lème (64450)' => '64332',
+ 'Lempzours (24800)' => '24238',
+ 'Lencloître (86140)' => '86128',
+ 'Lencouacq (40120)' => '40149',
+ 'Léogeats (33210)' => '33237',
+ 'Léognan (33850)' => '33238',
+ 'Léon (40550)' => '40150',
+ 'Léoville (17500)' => '17204',
+ 'Lépaud (23170)' => '23106',
+ 'Lépinas (23150)' => '23107',
+ 'Léren (64270)' => '64334',
+ 'Lerm-et-Musset (33840)' => '33239',
+ 'Les Adjots (16700)' => '16002',
+ 'Les Alleuds (79190)' => '79006',
+ 'Les Angles-sur-Corrèze (19000)' => '19009',
+ 'Les Artigues-de-Lussac (33570)' => '33014',
+ 'Les Billanges (87340)' => '87016',
+ 'Les Billaux (33500)' => '33052',
+ 'Les Cars (87230)' => '87029',
+ 'Les Éduts (17510)' => '17149',
+ 'Les Églises-d\'Argenteuil (17400)' => '17150',
+ 'Les Églisottes-et-Chalaures (33230)' => '33154',
+ 'Les Essards (16210)' => '16130',
+ 'Les Essards (17250)' => '17154',
+ 'Les Esseintes (33190)' => '33158',
+ 'Les Eyzies-de-Tayac-Sireuil (24620)' => '24172',
+ 'Les Farges (24290)' => '24175',
+ 'Les Forges (79340)' => '79124',
+ 'Les Fosses (79360)' => '79126',
+ 'Les Gonds (17100)' => '17179',
+ 'Les Gours (16140)' => '16155',
+ 'Les Grands-Chézeaux (87160)' => '87074',
+ 'Les Graulges (24340)' => '24203',
+ 'Les Groseillers (79220)' => '79139',
+ 'Les Lèches (24400)' => '24234',
+ 'Les Lèves-et-Thoumeyragues (33220)' => '33242',
+ 'Les Mars (23700)' => '23123',
+ 'Les Mathes (17570)' => '17225',
+ 'Les Métairies (16200)' => '16220',
+ 'Les Nouillers (17380)' => '17266',
+ 'Les Ormes (86220)' => '86183',
+ 'Les Peintures (33230)' => '33315',
+ 'Les Pins (16260)' => '16261',
+ 'Les Portes-en-Ré (17880)' => '17286',
+ 'Les Salles-de-Castillon (33350)' => '33499',
+ 'Les Salles-Lavauguyon (87440)' => '87189',
+ 'Les Touches-de-Périgny (17160)' => '17451',
+ 'Les Trois-Moutiers (86120)' => '86274',
+ 'Lescar (64230)' => '64335',
+ 'Lescun (64490)' => '64336',
+ 'Lesgor (40400)' => '40151',
+ 'Lésignac-Durand (16310)' => '16183',
+ 'Lésigny (86270)' => '86129',
+ 'Lesparre-Médoc (33340)' => '33240',
+ 'Lesperon (40260)' => '40152',
+ 'Lespielle (64350)' => '64337',
+ 'Lespourcy (64160)' => '64338',
+ 'Lessac (16500)' => '16181',
+ 'Lestards (19170)' => '19112',
+ 'Lestelle-Bétharram (64800)' => '64339',
+ 'Lesterps (16420)' => '16182',
+ 'Lestiac-sur-Garonne (33550)' => '33241',
+ 'Leugny (86220)' => '86130',
+ 'Lévignac-de-Guyenne (47120)' => '47147',
+ 'Lévignacq (40170)' => '40154',
+ 'Leyrat (23600)' => '23108',
+ 'Leyritz-Moncassin (47700)' => '47148',
+ 'Lezay (79120)' => '79148',
+ 'Lhommaizé (86410)' => '86131',
+ 'Lhoumois (79390)' => '79149',
+ 'Libourne (33500)' => '33243',
+ 'Lichans-Sunhar (64470)' => '64340',
+ 'Lichères (16460)' => '16184',
+ 'Lichos (64130)' => '64341',
+ 'Licq-Athérey (64560)' => '64342',
+ 'Liginiac (19160)' => '19113',
+ 'Liglet (86290)' => '86132',
+ 'Lignan-de-Bazas (33430)' => '33244',
+ 'Lignan-de-Bordeaux (33360)' => '33245',
+ 'Lignareix (19200)' => '19114',
+ 'Ligné (16140)' => '16185',
+ 'Ligneyrac (19500)' => '19115',
+ 'Lignières-Sonneville (16130)' => '16186',
+ 'Ligueux (33220)' => '33246',
+ 'Ligugé (86240)' => '86133',
+ 'Limalonges (79190)' => '79150',
+ 'Limendous (64420)' => '64343',
+ 'Limeuil (24510)' => '24240',
+ 'Limeyrat (24210)' => '24241',
+ 'Limoges (87000)' => '87085',
+ 'Linard (23220)' => '23109',
+ 'Linards (87130)' => '87086',
+ 'Linars (16730)' => '16187',
+ 'Linazay (86400)' => '86134',
+ 'Liniers (86800)' => '86135',
+ 'Linxe (40260)' => '40155',
+ 'Liorac-sur-Louyre (24520)' => '24242',
+ 'Liourdres (19120)' => '19116',
+ 'Lioux-les-Monges (23700)' => '23110',
+ 'Liposthey (40410)' => '40156',
+ 'Lisle (24350)' => '24243',
+ 'Lissac-sur-Couze (19600)' => '19117',
+ 'Listrac-de-Durèze (33790)' => '33247',
+ 'Listrac-Médoc (33480)' => '33248',
+ 'Lit-et-Mixe (40170)' => '40157',
+ 'Livron (64530)' => '64344',
+ 'Lizant (86400)' => '86136',
+ 'Lizières (23240)' => '23111',
+ 'Lohitzun-Oyhercq (64120)' => '64345',
+ 'Loire-les-Marais (17870)' => '17205',
+ 'Loiré-sur-Nie (17470)' => '17206',
+ 'Loix (17111)' => '17207',
+ 'Lolme (24540)' => '24244',
+ 'Lombia (64160)' => '64346',
+ 'Lonçon (64410)' => '64347',
+ 'Londigny (16700)' => '16189',
+ 'Longèves (17230)' => '17208',
+ 'Longré (16240)' => '16190',
+ 'Longueville (47200)' => '47150',
+ 'Lonnes (16230)' => '16191',
+ 'Lons (64140)' => '64348',
+ 'Lonzac (17520)' => '17209',
+ 'Lorignac (17240)' => '17210',
+ 'Lorigné (79190)' => '79152',
+ 'Lormont (33310)' => '33249',
+ 'Losse (40240)' => '40158',
+ 'Lostanges (19500)' => '19119',
+ 'Loubejac (24550)' => '24245',
+ 'Loubens (33190)' => '33250',
+ 'Loubès-Bernac (47120)' => '47151',
+ 'Loubieng (64300)' => '64349',
+ 'Loubigné (79110)' => '79153',
+ 'Loubillé (79110)' => '79154',
+ 'Louchats (33125)' => '33251',
+ 'Loudun (86200)' => '86137',
+ 'Louer (40380)' => '40159',
+ 'Lougratte (47290)' => '47152',
+ 'Louhossoa (64250)' => '64350',
+ 'Louignac (19310)' => '19120',
+ 'Louin (79600)' => '79156',
+ 'Loulay (17330)' => '17211',
+ 'Loupes (33370)' => '33252',
+ 'Loupiac (33410)' => '33253',
+ 'Loupiac-de-la-Réole (33190)' => '33254',
+ 'Lourdios-Ichère (64570)' => '64351',
+ 'Lourdoueix-Saint-Pierre (23360)' => '23112',
+ 'Lourenties (64420)' => '64352',
+ 'Lourquen (40250)' => '40160',
+ 'Louvie-Juzon (64260)' => '64353',
+ 'Louvie-Soubiron (64440)' => '64354',
+ 'Louvigny (64410)' => '64355',
+ 'Louzac-Saint-André (16100)' => '16193',
+ 'Louzignac (17160)' => '17212',
+ 'Louzy (79100)' => '79157',
+ 'Lozay (17330)' => '17213',
+ 'Lubbon (40240)' => '40161',
+ 'Lubersac (19210)' => '19121',
+ 'Luc-Armau (64350)' => '64356',
+ 'Lucarré (64350)' => '64357',
+ 'Lucbardez-et-Bargues (40090)' => '40162',
+ 'Lucgarier (64420)' => '64358',
+ 'Luchapt (86430)' => '86138',
+ 'Luchat (17600)' => '17214',
+ 'Luché-sur-Brioux (79170)' => '79158',
+ 'Luché-Thouarsais (79330)' => '79159',
+ 'Lucmau (33840)' => '33255',
+ 'Lucq-de-Béarn (64360)' => '64359',
+ 'Ludon-Médoc (33290)' => '33256',
+ 'Lüe (40210)' => '40163',
+ 'Lugaignac (33420)' => '33257',
+ 'Lugasson (33760)' => '33258',
+ 'Luglon (40630)' => '40165',
+ 'Lugon-et-l\'Île-du-Carnay (33240)' => '33259',
+ 'Lugos (33830)' => '33260',
+ 'Lunas (24130)' => '24246',
+ 'Lupersat (23190)' => '23113',
+ 'Lupsault (16140)' => '16194',
+ 'Luquet (65320)' => '65292',
+ 'Lurbe-Saint-Christau (64660)' => '64360',
+ 'Lusignac (24320)' => '24247',
+ 'Lusignan (86600)' => '86139',
+ 'Lusignan-Petit (47360)' => '47154',
+ 'Lussac (16450)' => '16195',
+ 'Lussac (17500)' => '17215',
+ 'Lussac (33570)' => '33261',
+ 'Lussac-les-Châteaux (86320)' => '86140',
+ 'Lussac-les-Églises (87360)' => '87087',
+ 'Lussagnet (40270)' => '40166',
+ 'Lussagnet-Lusson (64160)' => '64361',
+ 'Lussant (17430)' => '17216',
+ 'Lussas-et-Nontronneau (24300)' => '24248',
+ 'Lussat (23170)' => '23114',
+ 'Lusseray (79170)' => '79160',
+ 'Luxé (16230)' => '16196',
+ 'Luxe-Sumberraute (64120)' => '64362',
+ 'Luxey (40430)' => '40167',
+ 'Luzay (79100)' => '79161',
+ 'Lys (64260)' => '64363',
+ 'Macau (33460)' => '33262',
+ 'Macaye (64240)' => '64364',
+ 'Macqueville (17490)' => '17217',
+ 'Madaillan (47360)' => '47155',
+ 'Madirac (33670)' => '33263',
+ 'Madranges (19470)' => '19122',
+ 'Magescq (40140)' => '40168',
+ 'Magnac-Bourg (87380)' => '87088',
+ 'Magnac-Laval (87190)' => '87089',
+ 'Magnac-Lavalette-Villars (16320)' => '16198',
+ 'Magnac-sur-Touvre (16600)' => '16199',
+ 'Magnat-l\'Étrange (23260)' => '23115',
+ 'Magné (79460)' => '79162',
+ 'Magné (86160)' => '86141',
+ 'Mailhac-sur-Benaize (87160)' => '87090',
+ 'Maillas (40120)' => '40169',
+ 'Maillé (86190)' => '86142',
+ 'Maillères (40120)' => '40170',
+ 'Maine-de-Boixe (16230)' => '16200',
+ 'Mainsat (23700)' => '23116',
+ 'Mainxe (16200)' => '16202',
+ 'Mainzac (16380)' => '16203',
+ 'Mairé (86270)' => '86143',
+ 'Mairé-Levescault (79190)' => '79163',
+ 'Maison-Feyne (23800)' => '23117',
+ 'Maisonnais-sur-Tardoire (87440)' => '87091',
+ 'Maisonnay (79500)' => '79164',
+ 'Maisonneuve (86170)' => '86144',
+ 'Maisonnisses (23150)' => '23118',
+ 'Maisontiers (79600)' => '79165',
+ 'Malaussanne (64410)' => '64365',
+ 'Malaville (16120)' => '16204',
+ 'Malemort (19360)' => '19123',
+ 'Malleret (23260)' => '23119',
+ 'Malleret-Boussac (23600)' => '23120',
+ 'Malval (23220)' => '23121',
+ 'Manaurie (24620)' => '24249',
+ 'Mano (40410)' => '40171',
+ 'Manot (16500)' => '16205',
+ 'Mansac (19520)' => '19124',
+ 'Mansat-la-Courrière (23400)' => '23122',
+ 'Mansle (16230)' => '16206',
+ 'Mant (40700)' => '40172',
+ 'Manzac-sur-Vern (24110)' => '24251',
+ 'Marans (17230)' => '17218',
+ 'Maransin (33230)' => '33264',
+ 'Marc-la-Tour (19150)' => '19127',
+ 'Marçay (86370)' => '86145',
+ 'Marcellus (47200)' => '47156',
+ 'Marcenais (33620)' => '33266',
+ 'Marcheprime (33380)' => '33555',
+ 'Marcillac (33860)' => '33267',
+ 'Marcillac-la-Croisille (19320)' => '19125',
+ 'Marcillac-la-Croze (19500)' => '19126',
+ 'Marcillac-Lanville (16140)' => '16207',
+ 'Marcillac-Saint-Quentin (24200)' => '24252',
+ 'Marennes (17320)' => '17219',
+ 'Mareuil (16170)' => '16208',
+ 'Mareuil (24340)' => '24253',
+ 'Margaux (33460)' => '33268',
+ 'Margerides (19200)' => '19128',
+ 'Margueron (33220)' => '33269',
+ 'Marignac (17800)' => '17220',
+ 'Marigny (79360)' => '79166',
+ 'Marigny-Brizay (86380)' => '86146',
+ 'Marigny-Chemereau (86370)' => '86147',
+ 'Marillac-le-Franc (16110)' => '16209',
+ 'Marimbault (33430)' => '33270',
+ 'Marions (33690)' => '33271',
+ 'Marmande (47200)' => '47157',
+ 'Marmont-Pachas (47220)' => '47158',
+ 'Marnac (24220)' => '24254',
+ 'Marnay (86160)' => '86148',
+ 'Marnes (79600)' => '79167',
+ 'Marpaps (40330)' => '40173',
+ 'Marquay (24620)' => '24255',
+ 'Marsac (16570)' => '16210',
+ 'Marsac (23210)' => '23124',
+ 'Marsac-sur-l\'Isle (24430)' => '24256',
+ 'Marsais (17700)' => '17221',
+ 'Marsalès (24540)' => '24257',
+ 'Marsaneix (24750)' => '24258',
+ 'Marsas (33620)' => '33272',
+ 'Marsilly (17137)' => '17222',
+ 'Martaizé (86330)' => '86149',
+ 'Marthon (16380)' => '16211',
+ 'Martignas-sur-Jalle (33127)' => '33273',
+ 'Martillac (33650)' => '33274',
+ 'Martres (33760)' => '33275',
+ 'Marval (87440)' => '87092',
+ 'Masbaraud-Mérignat (23400)' => '23126',
+ 'Mascaraàs-Haron (64330)' => '64366',
+ 'Maslacq (64300)' => '64367',
+ 'Masléon (87130)' => '87093',
+ 'Masparraute (64120)' => '64368',
+ 'Maspie-Lalonquère-Juillacq (64350)' => '64369',
+ 'Masquières (47370)' => '47160',
+ 'Massac (17490)' => '17223',
+ 'Massais (79150)' => '79168',
+ 'Masseilles (33690)' => '33276',
+ 'Massels (47140)' => '47161',
+ 'Masseret (19510)' => '19129',
+ 'Massignac (16310)' => '16212',
+ 'Massognes (86170)' => '86150',
+ 'Massoulès (47140)' => '47162',
+ 'Massugas (33790)' => '33277',
+ 'Matha (17160)' => '17224',
+ 'Maucor (64160)' => '64370',
+ 'Maulay (86200)' => '86151',
+ 'Mauléon (79700)' => '79079',
+ 'Mauléon-Licharre (64130)' => '64371',
+ 'Mauprévoir (86460)' => '86152',
+ 'Maure (64460)' => '64372',
+ 'Maurens (24140)' => '24259',
+ 'Mauriac (33540)' => '33278',
+ 'Mauries (40320)' => '40174',
+ 'Maurrin (40270)' => '40175',
+ 'Maussac (19250)' => '19130',
+ 'Mautes (23190)' => '23127',
+ 'Mauvezin-d\'Armagnac (40240)' => '40176',
+ 'Mauvezin-sur-Gupie (47200)' => '47163',
+ 'Mauzac-et-Grand-Castang (24150)' => '24260',
+ 'Mauzé-sur-le-Mignon (79210)' => '79170',
+ 'Mauzé-Thouarsais (79100)' => '79171',
+ 'Mauzens-et-Miremont (24260)' => '24261',
+ 'Mayac (24420)' => '24262',
+ 'Maylis (40250)' => '40177',
+ 'Mazeirat (23150)' => '23128',
+ 'Mazeray (17400)' => '17226',
+ 'Mazères (33210)' => '33279',
+ 'Mazères-Lezons (64110)' => '64373',
+ 'Mazerolles (16310)' => '16213',
+ 'Mazerolles (17800)' => '17227',
+ 'Mazerolles (40090)' => '40178',
+ 'Mazerolles (64230)' => '64374',
+ 'Mazerolles (86320)' => '86153',
+ 'Mazeuil (86110)' => '86154',
+ 'Mazeyrolles (24550)' => '24263',
+ 'Mazières (16270)' => '16214',
+ 'Mazières-en-Gâtine (79310)' => '79172',
+ 'Mazières-Naresse (47210)' => '47164',
+ 'Mazières-sur-Béronne (79500)' => '79173',
+ 'Mazion (33390)' => '33280',
+ 'Méasnes (23360)' => '23130',
+ 'Médillac (16210)' => '16215',
+ 'Médis (17600)' => '17228',
+ 'Mées (40990)' => '40179',
+ 'Méharin (64120)' => '64375',
+ 'Meilhac (87800)' => '87094',
+ 'Meilhan (40400)' => '40180',
+ 'Meilhan-sur-Garonne (47180)' => '47165',
+ 'Meilhards (19510)' => '19131',
+ 'Meillon (64510)' => '64376',
+ 'Melle (79500)' => '79174',
+ 'Melleran (79190)' => '79175',
+ 'Mendionde (64240)' => '64377',
+ 'Menditte (64130)' => '64378',
+ 'Mendive (64220)' => '64379',
+ 'Ménesplet (24700)' => '24264',
+ 'Ménigoute (79340)' => '79176',
+ 'Ménoire (19190)' => '19132',
+ 'Mensignac (24350)' => '24266',
+ 'Méracq (64410)' => '64380',
+ 'Mercoeur (19430)' => '19133',
+ 'Mérignac (16200)' => '16216',
+ 'Mérignac (17210)' => '17229',
+ 'Mérignac (33700)' => '33281',
+ 'Mérignas (33350)' => '33282',
+ 'Mérinchal (23420)' => '23131',
+ 'Méritein (64190)' => '64381',
+ 'Merlines (19340)' => '19134',
+ 'Merpins (16100)' => '16217',
+ 'Meschers-sur-Gironde (17132)' => '17230',
+ 'Mescoules (24240)' => '24267',
+ 'Mesnac (16370)' => '16218',
+ 'Mesplède (64370)' => '64382',
+ 'Messac (17130)' => '17231',
+ 'Messanges (40660)' => '40181',
+ 'Messé (79120)' => '79177',
+ 'Messemé (86200)' => '86156',
+ 'Mesterrieux (33540)' => '33283',
+ 'Mestes (19200)' => '19135',
+ 'Meursac (17120)' => '17232',
+ 'Meux (17500)' => '17233',
+ 'Meuzac (87380)' => '87095',
+ 'Meymac (19250)' => '19136',
+ 'Meyrals (24220)' => '24268',
+ 'Meyrignac-l\'Église (19800)' => '19137',
+ 'Meyssac (19500)' => '19138',
+ 'Mézin (47170)' => '47167',
+ 'Mézos (40170)' => '40182',
+ 'Mialet (24450)' => '24269',
+ 'Mialos (64410)' => '64383',
+ 'Mignaloux-Beauvoir (86550)' => '86157',
+ 'Migné-Auxances (86440)' => '86158',
+ 'Migré (17330)' => '17234',
+ 'Migron (17770)' => '17235',
+ 'Milhac-d\'Auberoche (24330)' => '24270',
+ 'Milhac-de-Nontron (24470)' => '24271',
+ 'Millac (86150)' => '86159',
+ 'Millevaches (19290)' => '19139',
+ 'Mimbaste (40350)' => '40183',
+ 'Mimizan (40200)' => '40184',
+ 'Minzac (24610)' => '24272',
+ 'Mios (33380)' => '33284',
+ 'Miossens-Lanusse (64450)' => '64385',
+ 'Mirambeau (17150)' => '17236',
+ 'Miramont-de-Guyenne (47800)' => '47168',
+ 'Miramont-Sensacq (40320)' => '40185',
+ 'Mirebeau (86110)' => '86160',
+ 'Mirepeix (64800)' => '64386',
+ 'Missé (79100)' => '79178',
+ 'Misson (40290)' => '40186',
+ 'Moëze (17780)' => '17237',
+ 'Moirax (47310)' => '47169',
+ 'Moissannes (87400)' => '87099',
+ 'Molières (24480)' => '24273',
+ 'Moliets-et-Maa (40660)' => '40187',
+ 'Momas (64230)' => '64387',
+ 'Mombrier (33710)' => '33285',
+ 'Momuy (40700)' => '40188',
+ 'Momy (64350)' => '64388',
+ 'Monassut-Audiracq (64160)' => '64389',
+ 'Monbahus (47290)' => '47170',
+ 'Monbalen (47340)' => '47171',
+ 'Monbazillac (24240)' => '24274',
+ 'Moncaup (64350)' => '64390',
+ 'Moncaut (47310)' => '47172',
+ 'Moncayolle-Larrory-Mendibieu (64130)' => '64391',
+ 'Monceaux-sur-Dordogne (19400)' => '19140',
+ 'Moncla (64330)' => '64392',
+ 'Monclar (47380)' => '47173',
+ 'Moncontour (86330)' => '86161',
+ 'Moncoutant (79320)' => '79179',
+ 'Moncrabeau (47600)' => '47174',
+ 'Mondion (86230)' => '86162',
+ 'Monein (64360)' => '64393',
+ 'Monestier (24240)' => '24276',
+ 'Monestier-Merlines (19340)' => '19141',
+ 'Monestier-Port-Dieu (19110)' => '19142',
+ 'Monfaucon (24130)' => '24277',
+ 'Monflanquin (47150)' => '47175',
+ 'Mongaillard (47230)' => '47176',
+ 'Mongauzy (33190)' => '33287',
+ 'Monget (40700)' => '40189',
+ 'Monheurt (47160)' => '47177',
+ 'Monmadalès (24560)' => '24278',
+ 'Monmarvès (24560)' => '24279',
+ 'Monpazier (24540)' => '24280',
+ 'Monpezat (64350)' => '64394',
+ 'Monplaisant (24170)' => '24293',
+ 'Monprimblanc (33410)' => '33288',
+ 'Mons (16140)' => '16221',
+ 'Mons (17160)' => '17239',
+ 'Monsac (24440)' => '24281',
+ 'Monsaguel (24560)' => '24282',
+ 'Monsec (24340)' => '24283',
+ 'Monségur (33580)' => '33289',
+ 'Monségur (40700)' => '40190',
+ 'Monségur (47150)' => '47178',
+ 'Monségur (64460)' => '64395',
+ 'Monsempron-Libos (47500)' => '47179',
+ 'Mont (64300)' => '64396',
+ 'Mont-de-Marsan (40000)' => '40192',
+ 'Mont-Disse (64330)' => '64401',
+ 'Montagnac-d\'Auberoche (24210)' => '24284',
+ 'Montagnac-la-Crempse (24140)' => '24285',
+ 'Montagnac-sur-Auvignon (47600)' => '47180',
+ 'Montagnac-sur-Lède (47150)' => '47181',
+ 'Montagne (33570)' => '33290',
+ 'Montagoudin (33190)' => '33291',
+ 'Montagrier (24350)' => '24286',
+ 'Montagut (64410)' => '64397',
+ 'Montaignac-Saint-Hippolyte (19300)' => '19143',
+ 'Montaigut-le-Blanc (23320)' => '23132',
+ 'Montalembert (79190)' => '79180',
+ 'Montamisé (86360)' => '86163',
+ 'Montaner (64460)' => '64398',
+ 'Montardon (64121)' => '64399',
+ 'Montastruc (47380)' => '47182',
+ 'Montauriol (47330)' => '47183',
+ 'Montaut (24560)' => '24287',
+ 'Montaut (40500)' => '40191',
+ 'Montaut (47210)' => '47184',
+ 'Montaut (64800)' => '64400',
+ 'Montayral (47500)' => '47185',
+ 'Montazeau (24230)' => '24288',
+ 'Montboucher (23400)' => '23133',
+ 'Montboyer (16620)' => '16222',
+ 'Montbron (16220)' => '16223',
+ 'Montcaret (24230)' => '24289',
+ 'Montégut (40190)' => '40193',
+ 'Montemboeuf (16310)' => '16225',
+ 'Montendre (17130)' => '17240',
+ 'Montesquieu (47130)' => '47186',
+ 'Monteton (47120)' => '47187',
+ 'Montferrand-du-Périgord (24440)' => '24290',
+ 'Montfort (64190)' => '64403',
+ 'Montfort-en-Chalosse (40380)' => '40194',
+ 'Montgaillard (40500)' => '40195',
+ 'Montgibaud (19210)' => '19144',
+ 'Montguyon (17270)' => '17241',
+ 'Monthoiron (86210)' => '86164',
+ 'Montignac (24290)' => '24291',
+ 'Montignac (33760)' => '33292',
+ 'Montignac-Charente (16330)' => '16226',
+ 'Montignac-de-Lauzun (47800)' => '47188',
+ 'Montignac-le-Coq (16390)' => '16227',
+ 'Montignac-Toupinerie (47350)' => '47189',
+ 'Montigné (16170)' => '16228',
+ 'Montils (17800)' => '17242',
+ 'Montjean (16240)' => '16229',
+ 'Montlieu-la-Garde (17210)' => '17243',
+ 'Montmérac (16300)' => '16224',
+ 'Montmoreau-Saint-Cybard (16190)' => '16230',
+ 'Montmorillon (86500)' => '86165',
+ 'Montory (64470)' => '64404',
+ 'Montpellier-de-Médillan (17260)' => '17244',
+ 'Montpeyroux (24610)' => '24292',
+ 'Montpezat (47360)' => '47190',
+ 'Montpon-Ménestérol (24700)' => '24294',
+ 'Montpouillan (47200)' => '47191',
+ 'Montravers (79140)' => '79183',
+ 'Montrem (24110)' => '24295',
+ 'Montreuil-Bonnin (86470)' => '86166',
+ 'Montrol-Sénard (87330)' => '87100',
+ 'Montrollet (16420)' => '16231',
+ 'Montroy (17220)' => '17245',
+ 'Monts-sur-Guesnes (86420)' => '86167',
+ 'Montsoué (40500)' => '40196',
+ 'Montussan (33450)' => '33293',
+ 'Monviel (47290)' => '47192',
+ 'Moragne (17430)' => '17246',
+ 'Morcenx (40110)' => '40197',
+ 'Morganx (40700)' => '40198',
+ 'Morizès (33190)' => '33294',
+ 'Morlaàs (64160)' => '64405',
+ 'Morlanne (64370)' => '64406',
+ 'Mornac (16600)' => '16232',
+ 'Mornac-sur-Seudre (17113)' => '17247',
+ 'Mortagne-sur-Gironde (17120)' => '17248',
+ 'Mortemart (87330)' => '87101',
+ 'Mortiers (17500)' => '17249',
+ 'Morton (86120)' => '86169',
+ 'Mortroux (23220)' => '23136',
+ 'Mosnac (16120)' => '16233',
+ 'Mosnac (17240)' => '17250',
+ 'Mougon (79370)' => '79185',
+ 'Mouguerre (64990)' => '64407',
+ 'Mouhous (64330)' => '64408',
+ 'Mouillac (33240)' => '33295',
+ 'Mouleydier (24520)' => '24296',
+ 'Moulidars (16290)' => '16234',
+ 'Mouliets-et-Villemartin (33350)' => '33296',
+ 'Moulin-Neuf (24700)' => '24297',
+ 'Moulinet (47290)' => '47193',
+ 'Moulis-en-Médoc (33480)' => '33297',
+ 'Moulismes (86500)' => '86170',
+ 'Moulon (33420)' => '33298',
+ 'Moumour (64400)' => '64409',
+ 'Mourens (33410)' => '33299',
+ 'Mourenx (64150)' => '64410',
+ 'Mourioux-Vieilleville (23210)' => '23137',
+ 'Mouscardès (40290)' => '40199',
+ 'Moussac (86150)' => '86171',
+ 'Moustey (40410)' => '40200',
+ 'Moustier (47800)' => '47194',
+ 'Moustier-Ventadour (19300)' => '19145',
+ 'Mouterre-Silly (86200)' => '86173',
+ 'Mouterre-sur-Blourde (86430)' => '86172',
+ 'Mouthiers-sur-Boëme (16440)' => '16236',
+ 'Moutier-d\'Ahun (23150)' => '23138',
+ 'Moutier-Malcard (23220)' => '23139',
+ 'Moutier-Rozeille (23200)' => '23140',
+ 'Moutiers-sous-Chantemerle (79320)' => '79188',
+ 'Mouton (16460)' => '16237',
+ 'Moutonneau (16460)' => '16238',
+ 'Mouzon (16310)' => '16239',
+ 'Mugron (40250)' => '40201',
+ 'Muron (17430)' => '17253',
+ 'Musculdy (64130)' => '64411',
+ 'Mussidan (24400)' => '24299',
+ 'Nabas (64190)' => '64412',
+ 'Nabinaud (16390)' => '16240',
+ 'Nabirat (24250)' => '24300',
+ 'Nachamps (17380)' => '17254',
+ 'Nadaillac (24590)' => '24301',
+ 'Nailhac (24390)' => '24302',
+ 'Naillat (23800)' => '23141',
+ 'Naintré (86530)' => '86174',
+ 'Nalliers (86310)' => '86175',
+ 'Nanclars (16230)' => '16241',
+ 'Nancras (17600)' => '17255',
+ 'Nanteuil (79400)' => '79189',
+ 'Nanteuil-Auriac-de-Bourzac (24320)' => '24303',
+ 'Nanteuil-en-Vallée (16700)' => '16242',
+ 'Nantheuil (24800)' => '24304',
+ 'Nanthiat (24800)' => '24305',
+ 'Nantiat (87140)' => '87103',
+ 'Nantillé (17770)' => '17256',
+ 'Narcastet (64510)' => '64413',
+ 'Narp (64190)' => '64414',
+ 'Narrosse (40180)' => '40202',
+ 'Nassiet (40330)' => '40203',
+ 'Nastringues (24230)' => '24306',
+ 'Naujac-sur-Mer (33990)' => '33300',
+ 'Naujan-et-Postiac (33420)' => '33301',
+ 'Naussannes (24440)' => '24307',
+ 'Navailles-Angos (64450)' => '64415',
+ 'Navarrenx (64190)' => '64416',
+ 'Naves (19460)' => '19146',
+ 'Nay (64800)' => '64417',
+ 'Néac (33500)' => '33302',
+ 'Nedde (87120)' => '87104',
+ 'Négrondes (24460)' => '24308',
+ 'Néoux (23200)' => '23142',
+ 'Nérac (47600)' => '47195',
+ 'Nerbis (40250)' => '40204',
+ 'Nercillac (16200)' => '16243',
+ 'Néré (17510)' => '17257',
+ 'Nérigean (33750)' => '33303',
+ 'Nérignac (86150)' => '86176',
+ 'Nersac (16440)' => '16244',
+ 'Nespouls (19600)' => '19147',
+ 'Neuffons (33580)' => '33304',
+ 'Neuillac (17520)' => '17258',
+ 'Neulles (17500)' => '17259',
+ 'Neuvic (19160)' => '19148',
+ 'Neuvic (24190)' => '24309',
+ 'Neuvic-Entier (87130)' => '87105',
+ 'Neuvicq (17270)' => '17260',
+ 'Neuvicq-le-Château (17490)' => '17261',
+ 'Neuville (19380)' => '19149',
+ 'Neuville-de-Poitou (86170)' => '86177',
+ 'Neuvy-Bouin (79130)' => '79190',
+ 'Nexon (87800)' => '87106',
+ 'Nicole (47190)' => '47196',
+ 'Nieuil (16270)' => '16245',
+ 'Nieuil-l\'Espoir (86340)' => '86178',
+ 'Nieul (87510)' => '87107',
+ 'Nieul-le-Virouil (17150)' => '17263',
+ 'Nieul-lès-Saintes (17810)' => '17262',
+ 'Nieul-sur-Mer (17137)' => '17264',
+ 'Nieulle-sur-Seudre (17600)' => '17265',
+ 'Niort (79000)' => '79191',
+ 'Noailhac (19500)' => '19150',
+ 'Noaillac (33190)' => '33306',
+ 'Noaillan (33730)' => '33307',
+ 'Noailles (19600)' => '19151',
+ 'Noguères (64150)' => '64418',
+ 'Nomdieu (47600)' => '47197',
+ 'Nonac (16190)' => '16246',
+ 'Nonards (19120)' => '19152',
+ 'Nonaville (16120)' => '16247',
+ 'Nontron (24300)' => '24311',
+ 'Noth (23300)' => '23143',
+ 'Notre-Dame-de-Sanilhac (24660)' => '24312',
+ 'Nouaillé-Maupertuis (86340)' => '86180',
+ 'Nouhant (23170)' => '23145',
+ 'Nouic (87330)' => '87108',
+ 'Nousse (40380)' => '40205',
+ 'Nousty (64420)' => '64419',
+ 'Nouzerines (23600)' => '23146',
+ 'Nouzerolles (23360)' => '23147',
+ 'Nouziers (23350)' => '23148',
+ 'Nuaillé-d\'Aunis (17540)' => '17267',
+ 'Nuaillé-sur-Boutonne (17470)' => '17268',
+ 'Nueil-les-Aubiers (79250)' => '79195',
+ 'Nueil-sous-Faye (86200)' => '86181',
+ 'Objat (19130)' => '19153',
+ 'Oeyregave (40300)' => '40206',
+ 'Oeyreluy (40180)' => '40207',
+ 'Ogenne-Camptort (64190)' => '64420',
+ 'Ogeu-les-Bains (64680)' => '64421',
+ 'Oiron (79100)' => '79196',
+ 'Oloron-Sainte-Marie (64400)' => '64422',
+ 'Omet (33410)' => '33308',
+ 'Onard (40380)' => '40208',
+ 'Ondres (40440)' => '40209',
+ 'Onesse-Laharie (40110)' => '40210',
+ 'Oraàs (64390)' => '64423',
+ 'Oradour (16140)' => '16248',
+ 'Oradour-Fanais (16500)' => '16249',
+ 'Oradour-Saint-Genest (87210)' => '87109',
+ 'Oradour-sur-Glane (87520)' => '87110',
+ 'Oradour-sur-Vayres (87150)' => '87111',
+ 'Orches (86230)' => '86182',
+ 'Ordiarp (64130)' => '64424',
+ 'Ordonnac (33340)' => '33309',
+ 'Orègue (64120)' => '64425',
+ 'Orgedeuil (16220)' => '16250',
+ 'Orgnac-sur-Vézère (19410)' => '19154',
+ 'Origne (33113)' => '33310',
+ 'Orignolles (17210)' => '17269',
+ 'Orin (64400)' => '64426',
+ 'Oriolles (16480)' => '16251',
+ 'Orion (64390)' => '64427',
+ 'Orist (40300)' => '40211',
+ 'Orival (16210)' => '16252',
+ 'Orliac (24170)' => '24313',
+ 'Orliac-de-Bar (19390)' => '19155',
+ 'Orliaguet (24370)' => '24314',
+ 'Oroux (79390)' => '79197',
+ 'Orriule (64390)' => '64428',
+ 'Orsanco (64120)' => '64429',
+ 'Orthevielle (40300)' => '40212',
+ 'Orthez (64300)' => '64430',
+ 'Orx (40230)' => '40213',
+ 'Os-Marsillon (64150)' => '64431',
+ 'Ossages (40290)' => '40214',
+ 'Ossas-Suhare (64470)' => '64432',
+ 'Osse-en-Aspe (64490)' => '64433',
+ 'Ossenx (64190)' => '64434',
+ 'Osserain-Rivareyte (64390)' => '64435',
+ 'Ossès (64780)' => '64436',
+ 'Ostabat-Asme (64120)' => '64437',
+ 'Ouillon (64160)' => '64438',
+ 'Ousse (64320)' => '64439',
+ 'Ousse-Suzan (40110)' => '40215',
+ 'Ouzilly (86380)' => '86184',
+ 'Oyré (86220)' => '86186',
+ 'Ozenx-Montestrucq (64300)' => '64440',
+ 'Ozillac (17500)' => '17270',
+ 'Ozourt (40380)' => '40216',
+ 'Pageas (87230)' => '87112',
+ 'Pagolle (64120)' => '64441',
+ 'Paillé (17470)' => '17271',
+ 'Paillet (33550)' => '33311',
+ 'Pailloles (47440)' => '47198',
+ 'Paizay-le-Chapt (79170)' => '79198',
+ 'Paizay-le-Sec (86300)' => '86187',
+ 'Paizay-le-Tort (79500)' => '79199',
+ 'Paizay-Naudouin-Embourie (16240)' => '16253',
+ 'Palazinges (19190)' => '19156',
+ 'Palisse (19160)' => '19157',
+ 'Palluaud (16390)' => '16254',
+ 'Pamplie (79220)' => '79200',
+ 'Pamproux (79800)' => '79201',
+ 'Panazol (87350)' => '87114',
+ 'Pandrignes (19150)' => '19158',
+ 'Parbayse (64360)' => '64442',
+ 'Parcoul-Chenaud (24410)' => '24316',
+ 'Pardaillan (47120)' => '47199',
+ 'Pardies (64150)' => '64443',
+ 'Pardies-Piétat (64800)' => '64444',
+ 'Parempuyre (33290)' => '33312',
+ 'Parentis-en-Born (40160)' => '40217',
+ 'Parleboscq (40310)' => '40218',
+ 'Parranquet (47210)' => '47200',
+ 'Parsac-Rimondeix (23140)' => '23149',
+ 'Parthenay (79200)' => '79202',
+ 'Parzac (16450)' => '16255',
+ 'Pas-de-Jeu (79100)' => '79203',
+ 'Passirac (16480)' => '16256',
+ 'Pau (64000)' => '64445',
+ 'Pauillac (33250)' => '33314',
+ 'Paulhiac (47150)' => '47202',
+ 'Paulin (24590)' => '24317',
+ 'Paunat (24510)' => '24318',
+ 'Paussac-et-Saint-Vivien (24310)' => '24319',
+ 'Payré (86700)' => '86188',
+ 'Payros-Cazautets (40320)' => '40219',
+ 'Payroux (86350)' => '86189',
+ 'Pays de Belvès (24170)' => '24035',
+ 'Payzac (24270)' => '24320',
+ 'Pazayac (24120)' => '24321',
+ 'Pécorade (40320)' => '40220',
+ 'Pellegrue (33790)' => '33316',
+ 'Penne-d\'Agenais (47140)' => '47203',
+ 'Pensol (87440)' => '87115',
+ 'Péré (17700)' => '17272',
+ 'Péret-Bel-Air (19300)' => '19159',
+ 'Pérignac (16250)' => '16258',
+ 'Pérignac (17800)' => '17273',
+ 'Périgné (79170)' => '79204',
+ 'Périgny (17180)' => '17274',
+ 'Périgueux (24000)' => '24322',
+ 'Périssac (33240)' => '33317',
+ 'Pérols-sur-Vézère (19170)' => '19160',
+ 'Perpezac-le-Blanc (19310)' => '19161',
+ 'Perpezac-le-Noir (19410)' => '19162',
+ 'Perquie (40190)' => '40221',
+ 'Pers (79190)' => '79205',
+ 'Persac (86320)' => '86190',
+ 'Pessac (33600)' => '33318',
+ 'Pessac-sur-Dordogne (33890)' => '33319',
+ 'Pessines (17810)' => '17275',
+ 'Petit-Bersac (24600)' => '24323',
+ 'Petit-Palais-et-Cornemps (33570)' => '33320',
+ 'Peujard (33240)' => '33321',
+ 'Pey (40300)' => '40222',
+ 'Peyrabout (23000)' => '23150',
+ 'Peyrat-de-Bellac (87300)' => '87116',
+ 'Peyrat-la-Nonière (23130)' => '23151',
+ 'Peyrat-le-Château (87470)' => '87117',
+ 'Peyre (40700)' => '40223',
+ 'Peyrehorade (40300)' => '40224',
+ 'Peyrelevade (19290)' => '19164',
+ 'Peyrelongue-Abos (64350)' => '64446',
+ 'Peyrière (47350)' => '47204',
+ 'Peyrignac (24210)' => '24324',
+ 'Peyrilhac (87510)' => '87118',
+ 'Peyrillac-et-Millac (24370)' => '24325',
+ 'Peyrissac (19260)' => '19165',
+ 'Peyzac-le-Moustier (24620)' => '24326',
+ 'Pezuls (24510)' => '24327',
+ 'Philondenx (40320)' => '40225',
+ 'Piégut-Pluviers (24360)' => '24328',
+ 'Pierre-Buffière (87260)' => '87119',
+ 'Pierrefitte (19450)' => '19166',
+ 'Pierrefitte (23130)' => '23152',
+ 'Pierrefitte (79330)' => '79209',
+ 'Piets-Plasence-Moustrou (64410)' => '64447',
+ 'Pillac (16390)' => '16260',
+ 'Pimbo (40320)' => '40226',
+ 'Pindères (47700)' => '47205',
+ 'Pindray (86500)' => '86191',
+ 'Pinel-Hauterive (47380)' => '47206',
+ 'Pineuilh (33220)' => '33324',
+ 'Pionnat (23140)' => '23154',
+ 'Pioussay (79110)' => '79211',
+ 'Pisany (17600)' => '17278',
+ 'Pissos (40410)' => '40227',
+ 'Plaisance (24560)' => '24168',
+ 'Plaisance (86500)' => '86192',
+ 'Plassac (17240)' => '17279',
+ 'Plassac (33390)' => '33325',
+ 'Plassac-Rouffiac (16250)' => '16263',
+ 'Plassay (17250)' => '17280',
+ 'Plazac (24580)' => '24330',
+ 'Pleine-Selve (33820)' => '33326',
+ 'Pleumartin (86450)' => '86193',
+ 'Pleuville (16490)' => '16264',
+ 'Pliboux (79190)' => '79212',
+ 'Podensac (33720)' => '33327',
+ 'Poey-d\'Oloron (64400)' => '64449',
+ 'Poey-de-Lescar (64230)' => '64448',
+ 'Poitiers (86000)' => '86194',
+ 'Polignac (17210)' => '17281',
+ 'Pomarez (40360)' => '40228',
+ 'Pomerol (33500)' => '33328',
+ 'Pommiers-Moulons (17130)' => '17282',
+ 'Pompaire (79200)' => '79213',
+ 'Pompéjac (33730)' => '33329',
+ 'Pompiey (47230)' => '47207',
+ 'Pompignac (33370)' => '33330',
+ 'Pompogne (47420)' => '47208',
+ 'Pomport (24240)' => '24331',
+ 'Pomps (64370)' => '64450',
+ 'Pondaurat (33190)' => '33331',
+ 'Pons (17800)' => '17283',
+ 'Ponson-Debat-Pouts (64460)' => '64451',
+ 'Ponson-Dessus (64460)' => '64452',
+ 'Pont-du-Casse (47480)' => '47209',
+ 'Pont-l\'Abbé-d\'Arnoult (17250)' => '17284',
+ 'Pontacq (64530)' => '64453',
+ 'Pontarion (23250)' => '23155',
+ 'Pontcharraud (23260)' => '23156',
+ 'Pontenx-les-Forges (40200)' => '40229',
+ 'Ponteyraud (24410)' => '24333',
+ 'Pontiacq-Viellepinte (64460)' => '64454',
+ 'Pontonx-sur-l\'Adour (40465)' => '40230',
+ 'Pontours (24150)' => '24334',
+ 'Porchères (33660)' => '33332',
+ 'Port-d\'Envaux (17350)' => '17285',
+ 'Port-de-Lanne (40300)' => '40231',
+ 'Port-de-Piles (86220)' => '86195',
+ 'Port-des-Barques (17730)' => '17484',
+ 'Port-Sainte-Foy-et-Ponchapt (33220)' => '24335',
+ 'Port-Sainte-Marie (47130)' => '47210',
+ 'Portet (64330)' => '64455',
+ 'Portets (33640)' => '33334',
+ 'Pouançay (86120)' => '86196',
+ 'Pouant (86200)' => '86197',
+ 'Poudenas (47170)' => '47211',
+ 'Poudenx (40700)' => '40232',
+ 'Pouffonds (79500)' => '79214',
+ 'Pougne-Hérisson (79130)' => '79215',
+ 'Pouillac (17210)' => '17287',
+ 'Pouillé (86800)' => '86198',
+ 'Pouillon (40350)' => '40233',
+ 'Pouliacq (64410)' => '64456',
+ 'Poullignac (16190)' => '16267',
+ 'Poursac (16700)' => '16268',
+ 'Poursay-Garnaud (17400)' => '17288',
+ 'Poursiugues-Boucoue (64410)' => '64457',
+ 'Poussanges (23500)' => '23158',
+ 'Poussignac (47700)' => '47212',
+ 'Pouydesseaux (40120)' => '40234',
+ 'Poyanne (40380)' => '40235',
+ 'Poyartin (40380)' => '40236',
+ 'Pradines (19170)' => '19168',
+ 'Prahecq (79230)' => '79216',
+ 'Prailles (79370)' => '79217',
+ 'Pranzac (16110)' => '16269',
+ 'Prats-de-Carlux (24370)' => '24336',
+ 'Prats-du-Périgord (24550)' => '24337',
+ 'Prayssas (47360)' => '47213',
+ 'Préchac (33730)' => '33336',
+ 'Préchacq-Josbaig (64190)' => '64458',
+ 'Préchacq-les-Bains (40465)' => '40237',
+ 'Préchacq-Navarrenx (64190)' => '64459',
+ 'Précilhon (64400)' => '64460',
+ 'Préguillac (17460)' => '17289',
+ 'Preignac (33210)' => '33337',
+ 'Pressac (86460)' => '86200',
+ 'Pressignac (16150)' => '16270',
+ 'Pressignac-Vicq (24150)' => '24338',
+ 'Pressigny (79390)' => '79218',
+ 'Preyssac-d\'Excideuil (24160)' => '24339',
+ 'Priaires (79210)' => '79219',
+ 'Prignac (17160)' => '17290',
+ 'Prignac-en-Médoc (33340)' => '33338',
+ 'Prignac-et-Marcamps (33710)' => '33339',
+ 'Prigonrieux (24130)' => '24340',
+ 'Prin-Deyrançon (79210)' => '79220',
+ 'Prinçay (86420)' => '86201',
+ 'Prissé-la-Charrière (79360)' => '79078',
+ 'Proissans (24200)' => '24341',
+ 'Puch-d\'Agenais (47160)' => '47214',
+ 'Pugnac (33710)' => '33341',
+ 'Pugny (79320)' => '79222',
+ 'Puihardy (79160)' => '79223',
+ 'Puilboreau (17138)' => '17291',
+ 'Puisseguin (33570)' => '33342',
+ 'Pujo-le-Plan (40190)' => '40238',
+ 'Pujols (33350)' => '33344',
+ 'Pujols (47300)' => '47215',
+ 'Pujols-sur-Ciron (33210)' => '33343',
+ 'Puy-d\'Arnac (19120)' => '19169',
+ 'Puy-du-Lac (17380)' => '17292',
+ 'Puy-Malsignat (23130)' => '23159',
+ 'Puybarban (33190)' => '33346',
+ 'Puymiclan (47350)' => '47216',
+ 'Puymirol (47270)' => '47217',
+ 'Puymoyen (16400)' => '16271',
+ 'Puynormand (33660)' => '33347',
+ 'Puyol-Cazalet (40320)' => '40239',
+ 'Puyoô (64270)' => '64461',
+ 'Puyravault (17700)' => '17293',
+ 'Puyréaux (16230)' => '16272',
+ 'Puyrenier (24340)' => '24344',
+ 'Puyrolland (17380)' => '17294',
+ 'Puysserampion (47800)' => '47218',
+ 'Queaux (86150)' => '86203',
+ 'Queyrac (33340)' => '33348',
+ 'Queyssac (24140)' => '24345',
+ 'Queyssac-les-Vignes (19120)' => '19170',
+ 'Quinçay (86190)' => '86204',
+ 'Quinsac (24530)' => '24346',
+ 'Quinsac (33360)' => '33349',
+ 'Raix (16240)' => '16273',
+ 'Ramous (64270)' => '64462',
+ 'Rampieux (24440)' => '24347',
+ 'Rancogne (16110)' => '16274',
+ 'Rancon (87290)' => '87121',
+ 'Ranton (86200)' => '86205',
+ 'Ranville-Breuillaud (16140)' => '16275',
+ 'Raslay (86120)' => '86206',
+ 'Rauzan (33420)' => '33350',
+ 'Rayet (47210)' => '47219',
+ 'Razac-d\'Eymet (24500)' => '24348',
+ 'Razac-de-Saussignac (24240)' => '24349',
+ 'Razac-sur-l\'Isle (24430)' => '24350',
+ 'Razès (87640)' => '87122',
+ 'Razimet (47160)' => '47220',
+ 'Réaup-Lisse (47170)' => '47221',
+ 'Réaux sur Trèfle (17500)' => '17295',
+ 'Rébénacq (64260)' => '64463',
+ 'Reffannes (79420)' => '79225',
+ 'Reignac (16360)' => '16276',
+ 'Reignac (33860)' => '33351',
+ 'Rempnat (87120)' => '87123',
+ 'Renung (40270)' => '40240',
+ 'Réparsac (16200)' => '16277',
+ 'Rétaud (17460)' => '17296',
+ 'Reterre (23110)' => '23160',
+ 'Retjons (40120)' => '40164',
+ 'Reygade (19430)' => '19171',
+ 'Ribagnac (24240)' => '24351',
+ 'Ribarrouy (64330)' => '64464',
+ 'Ribérac (24600)' => '24352',
+ 'Rilhac-Lastours (87800)' => '87124',
+ 'Rilhac-Rancon (87570)' => '87125',
+ 'Rilhac-Treignac (19260)' => '19172',
+ 'Rilhac-Xaintrie (19220)' => '19173',
+ 'Rimbez-et-Baudiets (40310)' => '40242',
+ 'Rimons (33580)' => '33353',
+ 'Riocaud (33220)' => '33354',
+ 'Rion-des-Landes (40370)' => '40243',
+ 'Rions (33410)' => '33355',
+ 'Rioux (17460)' => '17298',
+ 'Rioux-Martin (16210)' => '16279',
+ 'Riupeyrous (64160)' => '64465',
+ 'Rivedoux-Plage (17940)' => '17297',
+ 'Rivehaute (64190)' => '64466',
+ 'Rives (47210)' => '47223',
+ 'Rivière-Saas-et-Gourby (40180)' => '40244',
+ 'Rivières (16110)' => '16280',
+ 'Roaillan (33210)' => '33357',
+ 'Roche-le-Peyroux (19160)' => '19175',
+ 'Rochechouart (87600)' => '87126',
+ 'Rochefort (17300)' => '17299',
+ 'Roches (23270)' => '23162',
+ 'Roches-Prémarie-Andillé (86340)' => '86209',
+ 'Roiffé (86120)' => '86210',
+ 'Rom (79120)' => '79230',
+ 'Romagne (33760)' => '33358',
+ 'Romagne (86700)' => '86211',
+ 'Romans (79260)' => '79231',
+ 'Romazières (17510)' => '17301',
+ 'Romegoux (17250)' => '17302',
+ 'Romestaing (47250)' => '47224',
+ 'Ronsenac (16320)' => '16283',
+ 'Rontignon (64110)' => '64467',
+ 'Roquebrune (33580)' => '33359',
+ 'Roquefort (40120)' => '40245',
+ 'Roquefort (47310)' => '47225',
+ 'Roquiague (64130)' => '64468',
+ 'Rosiers-d\'Égletons (19300)' => '19176',
+ 'Rosiers-de-Juillac (19350)' => '19177',
+ 'Rouffiac (16210)' => '16284',
+ 'Rouffiac (17800)' => '17304',
+ 'Rouffignac (17130)' => '17305',
+ 'Rouffignac-de-Sigoulès (24240)' => '24357',
+ 'Rouffignac-Saint-Cernin-de-Reilhac (24580)' => '24356',
+ 'Rougnac (16320)' => '16285',
+ 'Rougnat (23700)' => '23164',
+ 'Rouillac (16170)' => '16286',
+ 'Rouillé (86480)' => '86213',
+ 'Roullet-Saint-Estèphe (16440)' => '16287',
+ 'Roumagne (47800)' => '47226',
+ 'Roumazières-Loubert (16270)' => '16192',
+ 'Roussac (87140)' => '87128',
+ 'Roussines (16310)' => '16289',
+ 'Rouzède (16220)' => '16290',
+ 'Royan (17200)' => '17306',
+ 'Royère-de-Vassivière (23460)' => '23165',
+ 'Royères (87400)' => '87129',
+ 'Roziers-Saint-Georges (87130)' => '87130',
+ 'Ruch (33350)' => '33361',
+ 'Rudeau-Ladosse (24340)' => '24221',
+ 'Ruelle-sur-Touvre (16600)' => '16291',
+ 'Ruffec (16700)' => '16292',
+ 'Ruffiac (47700)' => '47227',
+ 'Sablonceaux (17600)' => '17307',
+ 'Sablons (33910)' => '33362',
+ 'Sabres (40630)' => '40246',
+ 'Sadillac (24500)' => '24359',
+ 'Sadirac (33670)' => '33363',
+ 'Sadroc (19270)' => '19178',
+ 'Sagelat (24170)' => '24360',
+ 'Sagnat (23800)' => '23166',
+ 'Saillac (19500)' => '19179',
+ 'Saillans (33141)' => '33364',
+ 'Saillat-sur-Vienne (87720)' => '87131',
+ 'Saint Aulaye-Puymangou (24410)' => '24376',
+ 'Saint Maurice Étusson (79150)' => '79280',
+ 'Saint-Abit (64800)' => '64469',
+ 'Saint-Adjutory (16310)' => '16293',
+ 'Saint-Agnant (17620)' => '17308',
+ 'Saint-Agnant-de-Versillat (23300)' => '23177',
+ 'Saint-Agnant-près-Crocq (23260)' => '23178',
+ 'Saint-Agne (24520)' => '24361',
+ 'Saint-Agnet (40800)' => '40247',
+ 'Saint-Aignan (33126)' => '33365',
+ 'Saint-Aigulin (17360)' => '17309',
+ 'Saint-Alpinien (23200)' => '23179',
+ 'Saint-Amand (23200)' => '23180',
+ 'Saint-Amand-de-Coly (24290)' => '24364',
+ 'Saint-Amand-de-Vergt (24380)' => '24365',
+ 'Saint-Amand-Jartoudeix (23400)' => '23181',
+ 'Saint-Amand-le-Petit (87120)' => '87132',
+ 'Saint-Amand-Magnazeix (87290)' => '87133',
+ 'Saint-Amand-sur-Sèvre (79700)' => '79235',
+ 'Saint-Amant-de-Boixe (16330)' => '16295',
+ 'Saint-Amant-de-Bonnieure (16230)' => '16296',
+ 'Saint-Amant-de-Montmoreau (16190)' => '16294',
+ 'Saint-Amant-de-Nouère (16170)' => '16298',
+ 'Saint-André-d\'Allas (24200)' => '24366',
+ 'Saint-André-de-Cubzac (33240)' => '33366',
+ 'Saint-André-de-Double (24190)' => '24367',
+ 'Saint-André-de-Lidon (17260)' => '17310',
+ 'Saint-André-de-Seignanx (40390)' => '40248',
+ 'Saint-André-du-Bois (33490)' => '33367',
+ 'Saint-André-et-Appelles (33220)' => '33369',
+ 'Saint-André-sur-Sèvre (79380)' => '79236',
+ 'Saint-Androny (33390)' => '33370',
+ 'Saint-Angeau (16230)' => '16300',
+ 'Saint-Angel (19200)' => '19180',
+ 'Saint-Antoine-Cumond (24410)' => '24368',
+ 'Saint-Antoine-d\'Auberoche (24330)' => '24369',
+ 'Saint-Antoine-de-Breuilh (24230)' => '24370',
+ 'Saint-Antoine-de-Ficalba (47340)' => '47228',
+ 'Saint-Antoine-du-Queyret (33790)' => '33372',
+ 'Saint-Antoine-sur-l\'Isle (33660)' => '33373',
+ 'Saint-Aquilin (24110)' => '24371',
+ 'Saint-Armou (64160)' => '64470',
+ 'Saint-Astier (24110)' => '24372',
+ 'Saint-Astier (47120)' => '47229',
+ 'Saint-Aubin (40250)' => '40249',
+ 'Saint-Aubin (47150)' => '47230',
+ 'Saint-Aubin-de-Blaye (33820)' => '33374',
+ 'Saint-Aubin-de-Branne (33420)' => '33375',
+ 'Saint-Aubin-de-Cadelech (24500)' => '24373',
+ 'Saint-Aubin-de-Lanquais (24560)' => '24374',
+ 'Saint-Aubin-de-Médoc (33160)' => '33376',
+ 'Saint-Aubin-de-Nabirat (24250)' => '24375',
+ 'Saint-Aubin-du-Plain (79300)' => '79238',
+ 'Saint-Aubin-le-Cloud (79450)' => '79239',
+ 'Saint-Augustin (17570)' => '17311',
+ 'Saint-Augustin (19390)' => '19181',
+ 'Saint-Aulaire (19130)' => '19182',
+ 'Saint-Aulais-la-Chapelle (16300)' => '16301',
+ 'Saint-Auvent (87310)' => '87135',
+ 'Saint-Avit (16210)' => '16302',
+ 'Saint-Avit (40090)' => '40250',
+ 'Saint-Avit (47350)' => '47231',
+ 'Saint-Avit-de-Soulège (33220)' => '33377',
+ 'Saint-Avit-de-Tardes (23200)' => '23182',
+ 'Saint-Avit-de-Vialard (24260)' => '24377',
+ 'Saint-Avit-le-Pauvre (23480)' => '23183',
+ 'Saint-Avit-Rivière (24540)' => '24378',
+ 'Saint-Avit-Saint-Nazaire (33220)' => '33378',
+ 'Saint-Avit-Sénieur (24440)' => '24379',
+ 'Saint-Barbant (87330)' => '87136',
+ 'Saint-Bard (23260)' => '23184',
+ 'Saint-Barthélemy (40390)' => '40251',
+ 'Saint-Barthélemy-d\'Agenais (47350)' => '47232',
+ 'Saint-Barthélemy-de-Bellegarde (24700)' => '24380',
+ 'Saint-Barthélemy-de-Bussière (24360)' => '24381',
+ 'Saint-Bazile (87150)' => '87137',
+ 'Saint-Bazile-de-la-Roche (19320)' => '19183',
+ 'Saint-Bazile-de-Meyssac (19500)' => '19184',
+ 'Saint-Benoît (86280)' => '86214',
+ 'Saint-Boès (64300)' => '64471',
+ 'Saint-Bonnet (16300)' => '16303',
+ 'Saint-Bonnet-Avalouze (19150)' => '19185',
+ 'Saint-Bonnet-Briance (87260)' => '87138',
+ 'Saint-Bonnet-de-Bellac (87300)' => '87139',
+ 'Saint-Bonnet-Elvert (19380)' => '19186',
+ 'Saint-Bonnet-l\'Enfantier (19410)' => '19188',
+ 'Saint-Bonnet-la-Rivière (19130)' => '19187',
+ 'Saint-Bonnet-les-Tours-de-Merle (19430)' => '19189',
+ 'Saint-Bonnet-près-Bort (19200)' => '19190',
+ 'Saint-Bonnet-sur-Gironde (17150)' => '17312',
+ 'Saint-Brice (16100)' => '16304',
+ 'Saint-Brice (33540)' => '33379',
+ 'Saint-Brice-sur-Vienne (87200)' => '87140',
+ 'Saint-Bris-des-Bois (17770)' => '17313',
+ 'Saint-Caprais-de-Blaye (33820)' => '33380',
+ 'Saint-Caprais-de-Bordeaux (33880)' => '33381',
+ 'Saint-Caprais-de-Lerm (47270)' => '47234',
+ 'Saint-Capraise-d\'Eymet (24500)' => '24383',
+ 'Saint-Capraise-de-Lalinde (24150)' => '24382',
+ 'Saint-Cassien (24540)' => '24384',
+ 'Saint-Castin (64160)' => '64472',
+ 'Saint-Cernin-de-l\'Herm (24550)' => '24386',
+ 'Saint-Cernin-de-Labarde (24560)' => '24385',
+ 'Saint-Cernin-de-Larche (19600)' => '19191',
+ 'Saint-Césaire (17770)' => '17314',
+ 'Saint-Chabrais (23130)' => '23185',
+ 'Saint-Chamant (19380)' => '19192',
+ 'Saint-Chamassy (24260)' => '24388',
+ 'Saint-Christoly-de-Blaye (33920)' => '33382',
+ 'Saint-Christoly-Médoc (33340)' => '33383',
+ 'Saint-Christophe (16420)' => '16306',
+ 'Saint-Christophe (17220)' => '17315',
+ 'Saint-Christophe (23000)' => '23186',
+ 'Saint-Christophe (86230)' => '86217',
+ 'Saint-Christophe-de-Double (33230)' => '33385',
+ 'Saint-Christophe-des-Bardes (33330)' => '33384',
+ 'Saint-Christophe-sur-Roc (79220)' => '79241',
+ 'Saint-Cibard (33570)' => '33386',
+ 'Saint-Ciers-Champagne (17520)' => '17316',
+ 'Saint-Ciers-d\'Abzac (33910)' => '33387',
+ 'Saint-Ciers-de-Canesse (33710)' => '33388',
+ 'Saint-Ciers-du-Taillon (17240)' => '17317',
+ 'Saint-Ciers-sur-Bonnieure (16230)' => '16307',
+ 'Saint-Ciers-sur-Gironde (33820)' => '33389',
+ 'Saint-Cirgues-la-Loutre (19220)' => '19193',
+ 'Saint-Cirq (24260)' => '24389',
+ 'Saint-Clair (86330)' => '86218',
+ 'Saint-Claud (16450)' => '16308',
+ 'Saint-Clément (19700)' => '19194',
+ 'Saint-Clément-des-Baleines (17590)' => '17318',
+ 'Saint-Colomb-de-Lauzun (47410)' => '47235',
+ 'Saint-Côme (33430)' => '33391',
+ 'Saint-Coutant (16350)' => '16310',
+ 'Saint-Coutant (79120)' => '79243',
+ 'Saint-Coutant-le-Grand (17430)' => '17320',
+ 'Saint-Crépin (17380)' => '17321',
+ 'Saint-Crépin-d\'Auberoche (24330)' => '24390',
+ 'Saint-Crépin-de-Richemont (24310)' => '24391',
+ 'Saint-Crépin-et-Carlucet (24590)' => '24392',
+ 'Saint-Cricq-Chalosse (40700)' => '40253',
+ 'Saint-Cricq-du-Gave (40300)' => '40254',
+ 'Saint-Cricq-Villeneuve (40190)' => '40255',
+ 'Saint-Cybardeaux (16170)' => '16312',
+ 'Saint-Cybranet (24250)' => '24395',
+ 'Saint-Cyprien (19130)' => '19195',
+ 'Saint-Cyprien (24220)' => '24396',
+ 'Saint-Cyr (86130)' => '86219',
+ 'Saint-Cyr (87310)' => '87141',
+ 'Saint-Cyr-du-Doret (17170)' => '17322',
+ 'Saint-Cyr-la-Lande (79100)' => '79244',
+ 'Saint-Cyr-la-Roche (19130)' => '19196',
+ 'Saint-Cyr-les-Champagnes (24270)' => '24397',
+ 'Saint-Denis-d\'Oléron (17650)' => '17323',
+ 'Saint-Denis-de-Pile (33910)' => '33393',
+ 'Saint-Denis-des-Murs (87400)' => '87142',
+ 'Saint-Dizant-du-Bois (17150)' => '17324',
+ 'Saint-Dizant-du-Gua (17240)' => '17325',
+ 'Saint-Dizier-la-Tour (23130)' => '23187',
+ 'Saint-Dizier-les-Domaines (23270)' => '23188',
+ 'Saint-Dizier-Leyrenne (23400)' => '23189',
+ 'Saint-Domet (23190)' => '23190',
+ 'Saint-Dos (64270)' => '64474',
+ 'Saint-Éloi (23000)' => '23191',
+ 'Saint-Éloy-les-Tuileries (19210)' => '19198',
+ 'Saint-Émilion (33330)' => '33394',
+ 'Saint-Esteben (64640)' => '64476',
+ 'Saint-Estèphe (24360)' => '24398',
+ 'Saint-Estèphe (33180)' => '33395',
+ 'Saint-Étienne-aux-Clos (19200)' => '19199',
+ 'Saint-Étienne-d\'Orthe (40300)' => '40256',
+ 'Saint-Étienne-de-Baïgorry (64430)' => '64477',
+ 'Saint-Étienne-de-Fougères (47380)' => '47239',
+ 'Saint-Étienne-de-Fursac (23290)' => '23192',
+ 'Saint-Étienne-de-Lisse (33330)' => '33396',
+ 'Saint-Étienne-de-Puycorbier (24400)' => '24399',
+ 'Saint-Étienne-de-Villeréal (47210)' => '47240',
+ 'Saint-Étienne-la-Cigogne (79360)' => '79247',
+ 'Saint-Étienne-la-Geneste (19160)' => '19200',
+ 'Saint-Eugène (17520)' => '17326',
+ 'Saint-Eutrope (16190)' => '16314',
+ 'Saint-Eutrope-de-Born (47210)' => '47241',
+ 'Saint-Exupéry (33190)' => '33398',
+ 'Saint-Exupéry-les-Roches (19200)' => '19201',
+ 'Saint-Faust (64110)' => '64478',
+ 'Saint-Félix (16480)' => '16315',
+ 'Saint-Félix (17330)' => '17327',
+ 'Saint-Félix-de-Bourdeilles (24340)' => '24403',
+ 'Saint-Félix-de-Foncaude (33540)' => '33399',
+ 'Saint-Félix-de-Reillac-et-Mortemart (24260)' => '24404',
+ 'Saint-Félix-de-Villadeix (24510)' => '24405',
+ 'Saint-Ferme (33580)' => '33400',
+ 'Saint-Fiel (23000)' => '23195',
+ 'Saint-Fort-sur-Gironde (17240)' => '17328',
+ 'Saint-Fort-sur-le-Né (16130)' => '16316',
+ 'Saint-Fraigne (16140)' => '16317',
+ 'Saint-Fréjoux (19200)' => '19204',
+ 'Saint-Frion (23500)' => '23196',
+ 'Saint-Front (16460)' => '16318',
+ 'Saint-Front-d\'Alemps (24460)' => '24408',
+ 'Saint-Front-de-Pradoux (24400)' => '24409',
+ 'Saint-Front-la-Rivière (24300)' => '24410',
+ 'Saint-Front-sur-Lémance (47500)' => '47242',
+ 'Saint-Front-sur-Nizonne (24300)' => '24411',
+ 'Saint-Froult (17780)' => '17329',
+ 'Saint-Gaudent (86400)' => '86220',
+ 'Saint-Gein (40190)' => '40259',
+ 'Saint-Gelais (79410)' => '79249',
+ 'Saint-Génard (79500)' => '79251',
+ 'Saint-Gence (87510)' => '87143',
+ 'Saint-Généroux (79600)' => '79252',
+ 'Saint-Genès-de-Blaye (33390)' => '33405',
+ 'Saint-Genès-de-Castillon (33350)' => '33406',
+ 'Saint-Genès-de-Fronsac (33240)' => '33407',
+ 'Saint-Genès-de-Lombaud (33670)' => '33408',
+ 'Saint-Genest-d\'Ambière (86140)' => '86221',
+ 'Saint-Genest-sur-Roselle (87260)' => '87144',
+ 'Saint-Geniès (24590)' => '24412',
+ 'Saint-Geniez-ô-Merle (19220)' => '19205',
+ 'Saint-Genis-d\'Hiersac (16570)' => '16320',
+ 'Saint-Genis-de-Saintonge (17240)' => '17331',
+ 'Saint-Genis-du-Bois (33760)' => '33409',
+ 'Saint-Georges (16700)' => '16321',
+ 'Saint-Georges (47370)' => '47328',
+ 'Saint-Georges-Antignac (17240)' => '17332',
+ 'Saint-Georges-Blancaneix (24130)' => '24413',
+ 'Saint-Georges-d\'Oléron (17190)' => '17337',
+ 'Saint-Georges-de-Didonne (17110)' => '17333',
+ 'Saint-Georges-de-Longuepierre (17470)' => '17334',
+ 'Saint-Georges-de-Montclard (24140)' => '24414',
+ 'Saint-Georges-de-Noisné (79400)' => '79253',
+ 'Saint-Georges-de-Rex (79210)' => '79254',
+ 'Saint-Georges-des-Agoûts (17150)' => '17335',
+ 'Saint-Georges-des-Coteaux (17810)' => '17336',
+ 'Saint-Georges-du-Bois (17700)' => '17338',
+ 'Saint-Georges-la-Pouge (23250)' => '23197',
+ 'Saint-Georges-lès-Baillargeaux (86130)' => '86222',
+ 'Saint-Georges-les-Landes (87160)' => '87145',
+ 'Saint-Georges-Nigremont (23500)' => '23198',
+ 'Saint-Geours-d\'Auribat (40380)' => '40260',
+ 'Saint-Geours-de-Maremne (40230)' => '40261',
+ 'Saint-Géraud (47120)' => '47245',
+ 'Saint-Géraud-de-Corps (24700)' => '24415',
+ 'Saint-Germain (86310)' => '86223',
+ 'Saint-Germain-Beaupré (23160)' => '23199',
+ 'Saint-Germain-d\'Esteuil (33340)' => '33412',
+ 'Saint-Germain-de-Belvès (24170)' => '24416',
+ 'Saint-Germain-de-Grave (33490)' => '33411',
+ 'Saint-Germain-de-la-Rivière (33240)' => '33414',
+ 'Saint-Germain-de-Longue-Chaume (79200)' => '79255',
+ 'Saint-Germain-de-Lusignan (17500)' => '17339',
+ 'Saint-Germain-de-Marencennes (17700)' => '17340',
+ 'Saint-Germain-de-Montbron (16380)' => '16323',
+ 'Saint-Germain-de-Vibrac (17500)' => '17341',
+ 'Saint-Germain-des-Prés (24160)' => '24417',
+ 'Saint-Germain-du-Puch (33750)' => '33413',
+ 'Saint-Germain-du-Salembre (24190)' => '24418',
+ 'Saint-Germain-du-Seudre (17240)' => '17342',
+ 'Saint-Germain-et-Mons (24520)' => '24419',
+ 'Saint-Germain-Lavolps (19290)' => '19206',
+ 'Saint-Germain-les-Belles (87380)' => '87146',
+ 'Saint-Germain-les-Vergnes (19330)' => '19207',
+ 'Saint-Germier (79340)' => '79256',
+ 'Saint-Gervais (33240)' => '33415',
+ 'Saint-Gervais-les-Trois-Clochers (86230)' => '86224',
+ 'Saint-Géry (24400)' => '24420',
+ 'Saint-Geyrac (24330)' => '24421',
+ 'Saint-Gilles-les-Forêts (87130)' => '87147',
+ 'Saint-Girons-d\'Aiguevives (33920)' => '33416',
+ 'Saint-Girons-en-Béarn (64300)' => '64479',
+ 'Saint-Gladie-Arrive-Munein (64390)' => '64480',
+ 'Saint-Goin (64400)' => '64481',
+ 'Saint-Gor (40120)' => '40262',
+ 'Saint-Gourson (16700)' => '16325',
+ 'Saint-Goussaud (23430)' => '23200',
+ 'Saint-Grégoire-d\'Ardennes (17240)' => '17343',
+ 'Saint-Groux (16230)' => '16326',
+ 'Saint-Hilaire-Bonneval (87260)' => '87148',
+ 'Saint-Hilaire-d\'Estissac (24140)' => '24422',
+ 'Saint-Hilaire-de-la-Noaille (33190)' => '33418',
+ 'Saint-Hilaire-de-Lusignan (47450)' => '47246',
+ 'Saint-Hilaire-de-Villefranche (17770)' => '17344',
+ 'Saint-Hilaire-du-Bois (17500)' => '17345',
+ 'Saint-Hilaire-du-Bois (33540)' => '33419',
+ 'Saint-Hilaire-Foissac (19550)' => '19208',
+ 'Saint-Hilaire-la-Palud (79210)' => '79257',
+ 'Saint-Hilaire-la-Plaine (23150)' => '23201',
+ 'Saint-Hilaire-la-Treille (87190)' => '87149',
+ 'Saint-Hilaire-le-Château (23250)' => '23202',
+ 'Saint-Hilaire-les-Courbes (19170)' => '19209',
+ 'Saint-Hilaire-les-Places (87800)' => '87150',
+ 'Saint-Hilaire-Luc (19160)' => '19210',
+ 'Saint-Hilaire-Peyroux (19560)' => '19211',
+ 'Saint-Hilaire-Taurieux (19400)' => '19212',
+ 'Saint-Hippolyte (17430)' => '17346',
+ 'Saint-Hippolyte (33330)' => '33420',
+ 'Saint-Jacques-de-Thouars (79100)' => '79258',
+ 'Saint-Jal (19700)' => '19213',
+ 'Saint-Jammes (64160)' => '64482',
+ 'Saint-Jean-d\'Angély (17400)' => '17347',
+ 'Saint-Jean-d\'Angle (17620)' => '17348',
+ 'Saint-Jean-d\'Ataux (24190)' => '24424',
+ 'Saint-Jean-d\'Estissac (24140)' => '24426',
+ 'Saint-Jean-d\'Eyraud (24140)' => '24427',
+ 'Saint-Jean-d\'Illac (33127)' => '33422',
+ 'Saint-Jean-de-Blaignac (33420)' => '33421',
+ 'Saint-Jean-de-Côle (24800)' => '24425',
+ 'Saint-Jean-de-Duras (47120)' => '47247',
+ 'Saint-Jean-de-Lier (40380)' => '40263',
+ 'Saint-Jean-de-Liversay (17170)' => '17349',
+ 'Saint-Jean-de-Luz (64500)' => '64483',
+ 'Saint-Jean-de-Marsacq (40230)' => '40264',
+ 'Saint-Jean-de-Sauves (86330)' => '86225',
+ 'Saint-Jean-de-Thouars (79100)' => '79259',
+ 'Saint-Jean-de-Thurac (47270)' => '47248',
+ 'Saint-Jean-le-Vieux (64220)' => '64484',
+ 'Saint-Jean-Ligoure (87260)' => '87151',
+ 'Saint-Jean-Pied-de-Port (64220)' => '64485',
+ 'Saint-Jean-Poudge (64330)' => '64486',
+ 'Saint-Jory-de-Chalais (24800)' => '24428',
+ 'Saint-Jory-las-Bloux (24160)' => '24429',
+ 'Saint-Jouin-de-Marnes (79600)' => '79260',
+ 'Saint-Jouin-de-Milly (79380)' => '79261',
+ 'Saint-Jouvent (87510)' => '87152',
+ 'Saint-Julien-aux-Bois (19220)' => '19214',
+ 'Saint-Julien-Beychevelle (33250)' => '33423',
+ 'Saint-Julien-d\'Armagnac (40240)' => '40265',
+ 'Saint-Julien-d\'Eymet (24500)' => '24433',
+ 'Saint-Julien-de-Crempse (24140)' => '24431',
+ 'Saint-Julien-de-l\'Escap (17400)' => '17350',
+ 'Saint-Julien-de-Lampon (24370)' => '24432',
+ 'Saint-Julien-en-Born (40170)' => '40266',
+ 'Saint-Julien-l\'Ars (86800)' => '86226',
+ 'Saint-Julien-la-Genête (23110)' => '23203',
+ 'Saint-Julien-le-Châtel (23130)' => '23204',
+ 'Saint-Julien-le-Pèlerin (19430)' => '19215',
+ 'Saint-Julien-le-Petit (87460)' => '87153',
+ 'Saint-Julien-le-Vendômois (19210)' => '19216',
+ 'Saint-Julien-Maumont (19500)' => '19217',
+ 'Saint-Julien-près-Bort (19110)' => '19218',
+ 'Saint-Junien (87200)' => '87154',
+ 'Saint-Junien-la-Bregère (23400)' => '23205',
+ 'Saint-Junien-les-Combes (87300)' => '87155',
+ 'Saint-Just (24320)' => '24434',
+ 'Saint-Just-Ibarre (64120)' => '64487',
+ 'Saint-Just-le-Martel (87590)' => '87156',
+ 'Saint-Just-Luzac (17320)' => '17351',
+ 'Saint-Justin (40240)' => '40267',
+ 'Saint-Laon (86200)' => '86227',
+ 'Saint-Laurent (23000)' => '23206',
+ 'Saint-Laurent (47130)' => '47249',
+ 'Saint-Laurent-Bretagne (64160)' => '64488',
+ 'Saint-Laurent-d\'Arce (33240)' => '33425',
+ 'Saint-Laurent-de-Belzagot (16190)' => '16328',
+ 'Saint-Laurent-de-Céris (16450)' => '16329',
+ 'Saint-Laurent-de-Cognac (16100)' => '16330',
+ 'Saint-Laurent-de-Gosse (40390)' => '40268',
+ 'Saint-Laurent-de-Jourdes (86410)' => '86228',
+ 'Saint-Laurent-de-la-Barrière (17380)' => '17352',
+ 'Saint-Laurent-de-la-Prée (17450)' => '17353',
+ 'Saint-Laurent-des-Combes (16480)' => '16331',
+ 'Saint-Laurent-des-Combes (33330)' => '33426',
+ 'Saint-Laurent-des-Hommes (24400)' => '24436',
+ 'Saint-Laurent-des-Vignes (24100)' => '24437',
+ 'Saint-Laurent-du-Bois (33540)' => '33427',
+ 'Saint-Laurent-du-Plan (33190)' => '33428',
+ 'Saint-Laurent-la-Vallée (24170)' => '24438',
+ 'Saint-Laurent-les-Églises (87240)' => '87157',
+ 'Saint-Laurent-Médoc (33112)' => '33424',
+ 'Saint-Laurent-sur-Gorre (87310)' => '87158',
+ 'Saint-Laurs (79160)' => '79263',
+ 'Saint-Léger (16250)' => '16332',
+ 'Saint-Léger (17800)' => '17354',
+ 'Saint-Léger (47160)' => '47250',
+ 'Saint-Léger-Bridereix (23300)' => '23207',
+ 'Saint-Léger-de-Balson (33113)' => '33429',
+ 'Saint-Léger-de-la-Martinière (79500)' => '79264',
+ 'Saint-Léger-de-Montbrillais (86120)' => '86229',
+ 'Saint-Léger-de-Montbrun (79100)' => '79265',
+ 'Saint-Léger-la-Montagne (87340)' => '87159',
+ 'Saint-Léger-le-Guérétois (23000)' => '23208',
+ 'Saint-Léger-Magnazeix (87190)' => '87160',
+ 'Saint-Léomer (86290)' => '86230',
+ 'Saint-Léon (33670)' => '33431',
+ 'Saint-Léon (47160)' => '47251',
+ 'Saint-Léon-d\'Issigeac (24560)' => '24441',
+ 'Saint-Léon-sur-l\'Isle (24110)' => '24442',
+ 'Saint-Léon-sur-Vézère (24290)' => '24443',
+ 'Saint-Léonard-de-Noblat (87400)' => '87161',
+ 'Saint-Lin (79420)' => '79267',
+ 'Saint-Lon-les-Mines (40300)' => '40269',
+ 'Saint-Loubert (33210)' => '33432',
+ 'Saint-Loubès (33450)' => '33433',
+ 'Saint-Loubouer (40320)' => '40270',
+ 'Saint-Louis-de-Montferrand (33440)' => '33434',
+ 'Saint-Louis-en-l\'Isle (24400)' => '24444',
+ 'Saint-Loup (17380)' => '17356',
+ 'Saint-Loup (23130)' => '23209',
+ 'Saint-Loup-Lamairé (79600)' => '79268',
+ 'Saint-Macaire (33490)' => '33435',
+ 'Saint-Macoux (86400)' => '86231',
+ 'Saint-Magne (33125)' => '33436',
+ 'Saint-Magne-de-Castillon (33350)' => '33437',
+ 'Saint-Maigrin (17520)' => '17357',
+ 'Saint-Maime-de-Péreyrol (24380)' => '24459',
+ 'Saint-Maixant (23200)' => '23210',
+ 'Saint-Maixant (33490)' => '33438',
+ 'Saint-Maixent-de-Beugné (79160)' => '79269',
+ 'Saint-Maixent-l\'École (79400)' => '79270',
+ 'Saint-Mandé-sur-Brédoire (17470)' => '17358',
+ 'Saint-Marc-à-Frongier (23200)' => '23211',
+ 'Saint-Marc-à-Loubaud (23460)' => '23212',
+ 'Saint-Marc-la-Lande (79310)' => '79271',
+ 'Saint-Marcel-du-Périgord (24510)' => '24445',
+ 'Saint-Marcory (24540)' => '24446',
+ 'Saint-Mard (17700)' => '17359',
+ 'Saint-Marien (23600)' => '23213',
+ 'Saint-Mariens (33620)' => '33439',
+ 'Saint-Martial (16190)' => '16334',
+ 'Saint-Martial (17330)' => '17361',
+ 'Saint-Martial (33490)' => '33440',
+ 'Saint-Martial-d\'Albarède (24160)' => '24448',
+ 'Saint-Martial-d\'Artenset (24700)' => '24449',
+ 'Saint-Martial-de-Gimel (19150)' => '19220',
+ 'Saint-Martial-de-Mirambeau (17150)' => '17362',
+ 'Saint-Martial-de-Nabirat (24250)' => '24450',
+ 'Saint-Martial-de-Valette (24300)' => '24451',
+ 'Saint-Martial-de-Vitaterne (17500)' => '17363',
+ 'Saint-Martial-Entraygues (19400)' => '19221',
+ 'Saint-Martial-le-Mont (23150)' => '23214',
+ 'Saint-Martial-le-Vieux (23100)' => '23215',
+ 'Saint-Martial-sur-Isop (87330)' => '87163',
+ 'Saint-Martial-sur-Né (17520)' => '17364',
+ 'Saint-Martial-Viveyrol (24320)' => '24452',
+ 'Saint-Martin-Château (23460)' => '23216',
+ 'Saint-Martin-Curton (47700)' => '47254',
+ 'Saint-Martin-d\'Arberoue (64640)' => '64489',
+ 'Saint-Martin-d\'Arrossa (64780)' => '64490',
+ 'Saint-Martin-d\'Ary (17270)' => '17365',
+ 'Saint-Martin-d\'Oney (40090)' => '40274',
+ 'Saint-Martin-de-Beauville (47270)' => '47255',
+ 'Saint-Martin-de-Bernegoue (79230)' => '79273',
+ 'Saint-Martin-de-Coux (17360)' => '17366',
+ 'Saint-Martin-de-Fressengeas (24800)' => '24453',
+ 'Saint-Martin-de-Gurson (24610)' => '24454',
+ 'Saint-Martin-de-Hinx (40390)' => '40272',
+ 'Saint-Martin-de-Juillers (17400)' => '17367',
+ 'Saint-Martin-de-Jussac (87200)' => '87164',
+ 'Saint-Martin-de-Laye (33910)' => '33442',
+ 'Saint-Martin-de-Lerm (33540)' => '33443',
+ 'Saint-Martin-de-Mâcon (79100)' => '79274',
+ 'Saint-Martin-de-Ré (17410)' => '17369',
+ 'Saint-Martin-de-Ribérac (24600)' => '24455',
+ 'Saint-Martin-de-Saint-Maixent (79400)' => '79276',
+ 'Saint-Martin-de-Sanzay (79290)' => '79277',
+ 'Saint-Martin-de-Seignanx (40390)' => '40273',
+ 'Saint-Martin-de-Sescas (33490)' => '33444',
+ 'Saint-Martin-de-Villeréal (47210)' => '47256',
+ 'Saint-Martin-des-Combes (24140)' => '24456',
+ 'Saint-Martin-du-Bois (33910)' => '33445',
+ 'Saint-Martin-du-Clocher (16700)' => '16335',
+ 'Saint-Martin-du-Fouilloux (79420)' => '79278',
+ 'Saint-Martin-du-Puy (33540)' => '33446',
+ 'Saint-Martin-l\'Ars (86350)' => '86234',
+ 'Saint-Martin-l\'Astier (24400)' => '24457',
+ 'Saint-Martin-la-Méanne (19320)' => '19222',
+ 'Saint-Martin-Lacaussade (33390)' => '33441',
+ 'Saint-Martin-le-Mault (87360)' => '87165',
+ 'Saint-Martin-le-Pin (24300)' => '24458',
+ 'Saint-Martin-le-Vieux (87700)' => '87166',
+ 'Saint-Martin-lès-Melle (79500)' => '79279',
+ 'Saint-Martin-Petit (47180)' => '47257',
+ 'Saint-Martin-Sainte-Catherine (23430)' => '23217',
+ 'Saint-Martin-Sepert (19210)' => '19223',
+ 'Saint-Martin-Terressus (87400)' => '87167',
+ 'Saint-Mary (16260)' => '16336',
+ 'Saint-Mathieu (87440)' => '87168',
+ 'Saint-Maurice-de-Lestapel (47290)' => '47259',
+ 'Saint-Maurice-des-Lions (16500)' => '16337',
+ 'Saint-Maurice-la-Clouère (86160)' => '86235',
+ 'Saint-Maurice-la-Souterraine (23300)' => '23219',
+ 'Saint-Maurice-les-Brousses (87800)' => '87169',
+ 'Saint-Maurice-près-Crocq (23260)' => '23218',
+ 'Saint-Maurice-sur-Adour (40270)' => '40275',
+ 'Saint-Maurin (47270)' => '47260',
+ 'Saint-Maxire (79410)' => '79281',
+ 'Saint-Méard (87130)' => '87170',
+ 'Saint-Méard-de-Drône (24600)' => '24460',
+ 'Saint-Méard-de-Gurçon (24610)' => '24461',
+ 'Saint-Médard (16300)' => '16338',
+ 'Saint-Médard (17500)' => '17372',
+ 'Saint-Médard (64370)' => '64491',
+ 'Saint-Médard (79370)' => '79282',
+ 'Saint-Médard-d\'Aunis (17220)' => '17373',
+ 'Saint-Médard-d\'Excideuil (24160)' => '24463',
+ 'Saint-Médard-d\'Eyrans (33650)' => '33448',
+ 'Saint-Médard-de-Guizières (33230)' => '33447',
+ 'Saint-Médard-de-Mussidan (24400)' => '24462',
+ 'Saint-Médard-en-Jalles (33160)' => '33449',
+ 'Saint-Médard-la-Rochette (23200)' => '23220',
+ 'Saint-Même-les-Carrières (16720)' => '16340',
+ 'Saint-Merd-de-Lapleau (19320)' => '19225',
+ 'Saint-Merd-la-Breuille (23100)' => '23221',
+ 'Saint-Merd-les-Oussines (19170)' => '19226',
+ 'Saint-Mesmin (24270)' => '24464',
+ 'Saint-Mexant (19330)' => '19227',
+ 'Saint-Michel (16470)' => '16341',
+ 'Saint-Michel (64220)' => '64492',
+ 'Saint-Michel-de-Castelnau (33840)' => '33450',
+ 'Saint-Michel-de-Double (24400)' => '24465',
+ 'Saint-Michel-de-Fronsac (33126)' => '33451',
+ 'Saint-Michel-de-Lapujade (33190)' => '33453',
+ 'Saint-Michel-de-Montaigne (24230)' => '24466',
+ 'Saint-Michel-de-Rieufret (33720)' => '33452',
+ 'Saint-Michel-de-Veisse (23480)' => '23222',
+ 'Saint-Michel-de-Villadeix (24380)' => '24468',
+ 'Saint-Michel-Escalus (40550)' => '40276',
+ 'Saint-Moreil (23400)' => '23223',
+ 'Saint-Morillon (33650)' => '33454',
+ 'Saint-Nazaire-sur-Charente (17780)' => '17375',
+ 'Saint-Nexans (24520)' => '24472',
+ 'Saint-Nicolas-de-la-Balerme (47220)' => '47262',
+ 'Saint-Oradoux-de-Chirouze (23100)' => '23224',
+ 'Saint-Oradoux-près-Crocq (23260)' => '23225',
+ 'Saint-Ouen-d\'Aunis (17230)' => '17376',
+ 'Saint-Ouen-la-Thène (17490)' => '17377',
+ 'Saint-Ouen-sur-Gartempe (87300)' => '87172',
+ 'Saint-Palais (33820)' => '33456',
+ 'Saint-Palais (64120)' => '64493',
+ 'Saint-Palais-de-Négrignac (17210)' => '17378',
+ 'Saint-Palais-de-Phiolin (17800)' => '17379',
+ 'Saint-Palais-du-Né (16300)' => '16342',
+ 'Saint-Palais-sur-Mer (17420)' => '17380',
+ 'Saint-Pancrace (24530)' => '24474',
+ 'Saint-Pandelon (40180)' => '40277',
+ 'Saint-Pantaléon-de-Lapleau (19160)' => '19228',
+ 'Saint-Pantaléon-de-Larche (19600)' => '19229',
+ 'Saint-Pantaly-d\'Ans (24640)' => '24475',
+ 'Saint-Pantaly-d\'Excideuil (24160)' => '24476',
+ 'Saint-Pardon-de-Conques (33210)' => '33457',
+ 'Saint-Pardoult (17400)' => '17381',
+ 'Saint-Pardoux (79310)' => '79285',
+ 'Saint-Pardoux (87250)' => '87173',
+ 'Saint-Pardoux-Corbier (19210)' => '19230',
+ 'Saint-Pardoux-d\'Arnet (23260)' => '23226',
+ 'Saint-Pardoux-de-Drône (24600)' => '24477',
+ 'Saint-Pardoux-du-Breuil (47200)' => '47263',
+ 'Saint-Pardoux-et-Vielvic (24170)' => '24478',
+ 'Saint-Pardoux-Isaac (47800)' => '47264',
+ 'Saint-Pardoux-l\'Ortigier (19270)' => '19234',
+ 'Saint-Pardoux-la-Croisille (19320)' => '19231',
+ 'Saint-Pardoux-la-Rivière (24470)' => '24479',
+ 'Saint-Pardoux-le-Neuf (19200)' => '19232',
+ 'Saint-Pardoux-le-Neuf (23200)' => '23228',
+ 'Saint-Pardoux-le-Vieux (19200)' => '19233',
+ 'Saint-Pardoux-les-Cards (23150)' => '23229',
+ 'Saint-Pardoux-Morterolles (23400)' => '23227',
+ 'Saint-Pastour (47290)' => '47265',
+ 'Saint-Paul (19150)' => '19235',
+ 'Saint-Paul (33390)' => '33458',
+ 'Saint-Paul (87260)' => '87174',
+ 'Saint-Paul-de-Serre (24380)' => '24480',
+ 'Saint-Paul-en-Born (40200)' => '40278',
+ 'Saint-Paul-en-Gâtine (79240)' => '79286',
+ 'Saint-Paul-la-Roche (24800)' => '24481',
+ 'Saint-Paul-lès-Dax (40990)' => '40279',
+ 'Saint-Paul-Lizonne (24320)' => '24482',
+ 'Saint-Pé-de-Léren (64270)' => '64494',
+ 'Saint-Pé-Saint-Simon (47170)' => '47266',
+ 'Saint-Pée-sur-Nivelle (64310)' => '64495',
+ 'Saint-Perdon (40090)' => '40280',
+ 'Saint-Perdoux (24560)' => '24483',
+ 'Saint-Pey-d\'Armens (33330)' => '33459',
+ 'Saint-Pey-de-Castets (33350)' => '33460',
+ 'Saint-Philippe-d\'Aiguille (33350)' => '33461',
+ 'Saint-Philippe-du-Seignal (33220)' => '33462',
+ 'Saint-Pierre-Bellevue (23460)' => '23232',
+ 'Saint-Pierre-Chérignat (23430)' => '23230',
+ 'Saint-Pierre-d\'Amilly (17700)' => '17382',
+ 'Saint-Pierre-d\'Aurillac (33490)' => '33463',
+ 'Saint-Pierre-d\'Exideuil (86400)' => '86237',
+ 'Saint-Pierre-d\'Eyraud (24130)' => '24487',
+ 'Saint-Pierre-d\'Irube (64990)' => '64496',
+ 'Saint-Pierre-d\'Oléron (17310)' => '17385',
+ 'Saint-Pierre-de-Bat (33760)' => '33464',
+ 'Saint-Pierre-de-Buzet (47160)' => '47267',
+ 'Saint-Pierre-de-Chignac (24330)' => '24484',
+ 'Saint-Pierre-de-Clairac (47270)' => '47269',
+ 'Saint-Pierre-de-Côle (24800)' => '24485',
+ 'Saint-Pierre-de-Frugie (24450)' => '24486',
+ 'Saint-Pierre-de-Fursac (23290)' => '23231',
+ 'Saint-Pierre-de-Juillers (17400)' => '17383',
+ 'Saint-Pierre-de-l\'Isle (17330)' => '17384',
+ 'Saint-Pierre-de-Maillé (86260)' => '86236',
+ 'Saint-Pierre-de-Mons (33210)' => '33465',
+ 'Saint-Pierre-des-Échaubrognes (79700)' => '79289',
+ 'Saint-Pierre-du-Mont (40280)' => '40281',
+ 'Saint-Pierre-du-Palais (17270)' => '17386',
+ 'Saint-Pierre-le-Bost (23600)' => '23233',
+ 'Saint-Pierre-sur-Dropt (47120)' => '47271',
+ 'Saint-Pompain (79160)' => '79290',
+ 'Saint-Pompont (24170)' => '24488',
+ 'Saint-Porchaire (17250)' => '17387',
+ 'Saint-Preuil (16130)' => '16343',
+ 'Saint-Priest (23110)' => '23234',
+ 'Saint-Priest-de-Gimel (19800)' => '19236',
+ 'Saint-Priest-la-Feuille (23300)' => '23235',
+ 'Saint-Priest-la-Plaine (23240)' => '23236',
+ 'Saint-Priest-les-Fougères (24450)' => '24489',
+ 'Saint-Priest-Ligoure (87800)' => '87176',
+ 'Saint-Priest-Palus (23400)' => '23237',
+ 'Saint-Priest-sous-Aixe (87700)' => '87177',
+ 'Saint-Priest-Taurion (87480)' => '87178',
+ 'Saint-Privat (19220)' => '19237',
+ 'Saint-Privat-des-Prés (24410)' => '24490',
+ 'Saint-Projet-Saint-Constant (16110)' => '16344',
+ 'Saint-Quantin-de-Rançanne (17800)' => '17388',
+ 'Saint-Quentin-de-Baron (33750)' => '33466',
+ 'Saint-Quentin-de-Caplong (33220)' => '33467',
+ 'Saint-Quentin-de-Chalais (16210)' => '16346',
+ 'Saint-Quentin-du-Dropt (47330)' => '47272',
+ 'Saint-Quentin-la-Chabanne (23500)' => '23238',
+ 'Saint-Quentin-sur-Charente (16150)' => '16345',
+ 'Saint-Rabier (24210)' => '24491',
+ 'Saint-Raphaël (24160)' => '24493',
+ 'Saint-Rémy (19290)' => '19238',
+ 'Saint-Rémy (24700)' => '24494',
+ 'Saint-Rémy (79410)' => '79293',
+ 'Saint-Rémy-sur-Creuse (86220)' => '86241',
+ 'Saint-Robert (19310)' => '19239',
+ 'Saint-Robert (47340)' => '47273',
+ 'Saint-Rogatien (17220)' => '17391',
+ 'Saint-Romain (16210)' => '16347',
+ 'Saint-Romain (86250)' => '86242',
+ 'Saint-Romain-de-Benet (17600)' => '17393',
+ 'Saint-Romain-de-Monpazier (24540)' => '24495',
+ 'Saint-Romain-et-Saint-Clément (24800)' => '24496',
+ 'Saint-Romain-la-Virvée (33240)' => '33470',
+ 'Saint-Romain-le-Noble (47270)' => '47274',
+ 'Saint-Romain-sur-Gironde (17240)' => '17392',
+ 'Saint-Romans-des-Champs (79230)' => '79294',
+ 'Saint-Romans-lès-Melle (79500)' => '79295',
+ 'Saint-Salvadour (19700)' => '19240',
+ 'Saint-Salvy (47360)' => '47275',
+ 'Saint-Sardos (47360)' => '47276',
+ 'Saint-Saturnin (16290)' => '16348',
+ 'Saint-Saturnin-du-Bois (17700)' => '17394',
+ 'Saint-Saud-Lacoussière (24470)' => '24498',
+ 'Saint-Sauvant (17610)' => '17395',
+ 'Saint-Sauvant (86600)' => '86244',
+ 'Saint-Sauveur (24520)' => '24499',
+ 'Saint-Sauveur (33250)' => '33471',
+ 'Saint-Sauveur-d\'Aunis (17540)' => '17396',
+ 'Saint-Sauveur-de-Meilhan (47180)' => '47277',
+ 'Saint-Sauveur-de-Puynormand (33660)' => '33472',
+ 'Saint-Sauveur-Lalande (24700)' => '24500',
+ 'Saint-Savin (33920)' => '33473',
+ 'Saint-Savin (86310)' => '86246',
+ 'Saint-Savinien (17350)' => '17397',
+ 'Saint-Saviol (86400)' => '86247',
+ 'Saint-Sébastien (23160)' => '23239',
+ 'Saint-Secondin (86350)' => '86248',
+ 'Saint-Selve (33650)' => '33474',
+ 'Saint-Sernin (47120)' => '47278',
+ 'Saint-Setiers (19290)' => '19241',
+ 'Saint-Seurin-de-Bourg (33710)' => '33475',
+ 'Saint-Seurin-de-Cadourne (33180)' => '33476',
+ 'Saint-Seurin-de-Cursac (33390)' => '33477',
+ 'Saint-Seurin-de-Palenne (17800)' => '17398',
+ 'Saint-Seurin-de-Prats (24230)' => '24501',
+ 'Saint-Seurin-sur-l\'Isle (33660)' => '33478',
+ 'Saint-Sève (33190)' => '33479',
+ 'Saint-Sever (40500)' => '40282',
+ 'Saint-Sever-de-Saintonge (17800)' => '17400',
+ 'Saint-Séverin (16390)' => '16350',
+ 'Saint-Séverin-d\'Estissac (24190)' => '24502',
+ 'Saint-Séverin-sur-Boutonne (17330)' => '17401',
+ 'Saint-Sigismond-de-Clermont (17240)' => '17402',
+ 'Saint-Silvain-Bas-le-Roc (23600)' => '23240',
+ 'Saint-Silvain-Bellegarde (23190)' => '23241',
+ 'Saint-Silvain-Montaigut (23320)' => '23242',
+ 'Saint-Silvain-sous-Toulx (23140)' => '23243',
+ 'Saint-Simeux (16120)' => '16351',
+ 'Saint-Simon (16120)' => '16352',
+ 'Saint-Simon-de-Bordes (17500)' => '17403',
+ 'Saint-Simon-de-Pellouaille (17260)' => '17404',
+ 'Saint-Sixte (47220)' => '47279',
+ 'Saint-Solve (19130)' => '19242',
+ 'Saint-Sorlin-de-Conac (17150)' => '17405',
+ 'Saint-Sornin (16220)' => '16353',
+ 'Saint-Sornin (17600)' => '17406',
+ 'Saint-Sornin-la-Marche (87210)' => '87179',
+ 'Saint-Sornin-Lavolps (19230)' => '19243',
+ 'Saint-Sornin-Leulac (87290)' => '87180',
+ 'Saint-Sulpice-d\'Arnoult (17250)' => '17408',
+ 'Saint-Sulpice-d\'Excideuil (24800)' => '24505',
+ 'Saint-Sulpice-de-Cognac (16370)' => '16355',
+ 'Saint-Sulpice-de-Faleyrens (33330)' => '33480',
+ 'Saint-Sulpice-de-Guilleragues (33580)' => '33481',
+ 'Saint-Sulpice-de-Mareuil (24340)' => '24503',
+ 'Saint-Sulpice-de-Pommiers (33540)' => '33482',
+ 'Saint-Sulpice-de-Roumagnac (24600)' => '24504',
+ 'Saint-Sulpice-de-Royan (17200)' => '17409',
+ 'Saint-Sulpice-de-Ruffec (16460)' => '16356',
+ 'Saint-Sulpice-et-Cameyrac (33450)' => '33483',
+ 'Saint-Sulpice-Laurière (87370)' => '87181',
+ 'Saint-Sulpice-le-Dunois (23800)' => '23244',
+ 'Saint-Sulpice-le-Guérétois (23000)' => '23245',
+ 'Saint-Sulpice-les-Bois (19250)' => '19244',
+ 'Saint-Sulpice-les-Champs (23480)' => '23246',
+ 'Saint-Sulpice-les-Feuilles (87160)' => '87182',
+ 'Saint-Sylvain (19380)' => '19245',
+ 'Saint-Sylvestre (87240)' => '87183',
+ 'Saint-Sylvestre-sur-Lot (47140)' => '47280',
+ 'Saint-Symphorien (33113)' => '33484',
+ 'Saint-Symphorien (79270)' => '79298',
+ 'Saint-Symphorien-sur-Couze (87140)' => '87184',
+ 'Saint-Thomas-de-Conac (17150)' => '17410',
+ 'Saint-Trojan (33710)' => '33486',
+ 'Saint-Trojan-les-Bains (17370)' => '17411',
+ 'Saint-Urcisse (47270)' => '47281',
+ 'Saint-Vaize (17100)' => '17412',
+ 'Saint-Vallier (16480)' => '16357',
+ 'Saint-Varent (79330)' => '79299',
+ 'Saint-Vaury (23320)' => '23247',
+ 'Saint-Viance (19240)' => '19246',
+ 'Saint-Victor (24350)' => '24508',
+ 'Saint-Victor-en-Marche (23000)' => '23248',
+ 'Saint-Victour (19200)' => '19247',
+ 'Saint-Victurnien (87420)' => '87185',
+ 'Saint-Vincent (64800)' => '64498',
+ 'Saint-Vincent-de-Connezac (24190)' => '24509',
+ 'Saint-Vincent-de-Cosse (24220)' => '24510',
+ 'Saint-Vincent-de-Lamontjoie (47310)' => '47282',
+ 'Saint-Vincent-de-Paul (33440)' => '33487',
+ 'Saint-Vincent-de-Paul (40990)' => '40283',
+ 'Saint-Vincent-de-Pertignas (33420)' => '33488',
+ 'Saint-Vincent-de-Tyrosse (40230)' => '40284',
+ 'Saint-Vincent-Jalmoutiers (24410)' => '24511',
+ 'Saint-Vincent-la-Châtre (79500)' => '79301',
+ 'Saint-Vincent-le-Paluel (24200)' => '24512',
+ 'Saint-Vincent-sur-l\'Isle (24420)' => '24513',
+ 'Saint-Vite (47500)' => '47283',
+ 'Saint-Vitte-sur-Briance (87380)' => '87186',
+ 'Saint-Vivien (17220)' => '17413',
+ 'Saint-Vivien (24230)' => '24514',
+ 'Saint-Vivien-de-Blaye (33920)' => '33489',
+ 'Saint-Vivien-de-Médoc (33590)' => '33490',
+ 'Saint-Vivien-de-Monségur (33580)' => '33491',
+ 'Saint-Xandre (17138)' => '17414',
+ 'Saint-Yaguen (40400)' => '40285',
+ 'Saint-Ybard (19140)' => '19248',
+ 'Saint-Yrieix-la-Montagne (23460)' => '23249',
+ 'Saint-Yrieix-la-Perche (87500)' => '87187',
+ 'Saint-Yrieix-le-Déjalat (19300)' => '19249',
+ 'Saint-Yrieix-les-Bois (23150)' => '23250',
+ 'Saint-Yrieix-sous-Aixe (87700)' => '87188',
+ 'Saint-Yrieix-sur-Charente (16710)' => '16358',
+ 'Saint-Yzan-de-Soudiac (33920)' => '33492',
+ 'Saint-Yzans-de-Médoc (33340)' => '33493',
+ 'Sainte-Alvère-Saint-Laurent Les Bâtons (24510)' => '24362',
+ 'Sainte-Anne-Saint-Priest (87120)' => '87134',
+ 'Sainte-Bazeille (47180)' => '47233',
+ 'Sainte-Blandine (79370)' => '79240',
+ 'Sainte-Colombe (16230)' => '16309',
+ 'Sainte-Colombe (17210)' => '17319',
+ 'Sainte-Colombe (33350)' => '33390',
+ 'Sainte-Colombe (40700)' => '40252',
+ 'Sainte-Colombe-de-Duras (47120)' => '47236',
+ 'Sainte-Colombe-de-Villeneuve (47300)' => '47237',
+ 'Sainte-Colombe-en-Bruilhois (47310)' => '47238',
+ 'Sainte-Colome (64260)' => '64473',
+ 'Sainte-Croix (24440)' => '24393',
+ 'Sainte-Croix-de-Mareuil (24340)' => '24394',
+ 'Sainte-Croix-du-Mont (33410)' => '33392',
+ 'Sainte-Eanne (79800)' => '79246',
+ 'Sainte-Engrâce (64560)' => '64475',
+ 'Sainte-Eulalie (33560)' => '33397',
+ 'Sainte-Eulalie-d\'Ans (24640)' => '24401',
+ 'Sainte-Eulalie-d\'Eymet (24500)' => '24402',
+ 'Sainte-Eulalie-en-Born (40200)' => '40257',
+ 'Sainte-Féréole (19270)' => '19202',
+ 'Sainte-Feyre (23000)' => '23193',
+ 'Sainte-Feyre-la-Montagne (23500)' => '23194',
+ 'Sainte-Florence (33350)' => '33401',
+ 'Sainte-Fortunade (19490)' => '19203',
+ 'Sainte-Foy (40190)' => '40258',
+ 'Sainte-Foy-de-Belvès (24170)' => '24406',
+ 'Sainte-Foy-de-Longas (24510)' => '24407',
+ 'Sainte-Foy-la-Grande (33220)' => '33402',
+ 'Sainte-Foy-la-Longue (33490)' => '33403',
+ 'Sainte-Gemme (17250)' => '17330',
+ 'Sainte-Gemme (33580)' => '33404',
+ 'Sainte-Gemme (79330)' => '79250',
+ 'Sainte-Gemme-Martaillac (47250)' => '47244',
+ 'Sainte-Hélène (33480)' => '33417',
+ 'Sainte-Innocence (24500)' => '24423',
+ 'Sainte-Lheurine (17520)' => '17355',
+ 'Sainte-Livrade-sur-Lot (47110)' => '47252',
+ 'Sainte-Marie-de-Chignac (24330)' => '24447',
+ 'Sainte-Marie-de-Gosse (40390)' => '40271',
+ 'Sainte-Marie-de-Ré (17740)' => '17360',
+ 'Sainte-Marie-de-Vaux (87420)' => '87162',
+ 'Sainte-Marie-Lapanouze (19160)' => '19219',
+ 'Sainte-Marthe (47430)' => '47253',
+ 'Sainte-Maure-de-Peyriac (47170)' => '47258',
+ 'Sainte-Même (17770)' => '17374',
+ 'Sainte-Mondane (24370)' => '24470',
+ 'Sainte-Nathalène (24200)' => '24471',
+ 'Sainte-Néomaye (79260)' => '79283',
+ 'Sainte-Orse (24210)' => '24473',
+ 'Sainte-Ouenne (79220)' => '79284',
+ 'Sainte-Radegonde (17250)' => '17389',
+ 'Sainte-Radegonde (24560)' => '24492',
+ 'Sainte-Radegonde (33350)' => '33468',
+ 'Sainte-Radegonde (79100)' => '79292',
+ 'Sainte-Radégonde (86300)' => '86239',
+ 'Sainte-Ramée (17240)' => '17390',
+ 'Sainte-Sévère (16200)' => '16349',
+ 'Sainte-Soline (79120)' => '79297',
+ 'Sainte-Souline (16480)' => '16354',
+ 'Sainte-Soulle (17220)' => '17407',
+ 'Sainte-Terre (33350)' => '33485',
+ 'Sainte-Trie (24160)' => '24507',
+ 'Sainte-Verge (79100)' => '79300',
+ 'Saintes (17100)' => '17415',
+ 'Saires (86420)' => '86249',
+ 'Saivres (79400)' => '79302',
+ 'Saix (86120)' => '86250',
+ 'Salagnac (24160)' => '24515',
+ 'Salaunes (33160)' => '33494',
+ 'Saleignes (17510)' => '17416',
+ 'Salies-de-Béarn (64270)' => '64499',
+ 'Salignac-de-Mirambeau (17130)' => '17417',
+ 'Salignac-Eyvigues (24590)' => '24516',
+ 'Salignac-sur-Charente (17800)' => '17418',
+ 'Salleboeuf (33370)' => '33496',
+ 'Salles (33770)' => '33498',
+ 'Salles (47150)' => '47284',
+ 'Salles (79800)' => '79303',
+ 'Salles-d\'Angles (16130)' => '16359',
+ 'Salles-de-Barbezieux (16300)' => '16360',
+ 'Salles-de-Belvès (24170)' => '24517',
+ 'Salles-de-Villefagnan (16700)' => '16361',
+ 'Salles-Lavalette (16190)' => '16362',
+ 'Salles-Mongiscard (64300)' => '64500',
+ 'Salles-sur-Mer (17220)' => '17420',
+ 'Sallespisse (64300)' => '64501',
+ 'Salon (24380)' => '24518',
+ 'Salon-la-Tour (19510)' => '19250',
+ 'Samadet (40320)' => '40286',
+ 'Samazan (47250)' => '47285',
+ 'Sames (64520)' => '64502',
+ 'Sammarçolles (86200)' => '86252',
+ 'Samonac (33710)' => '33500',
+ 'Samsons-Lion (64350)' => '64503',
+ 'Sanguinet (40460)' => '40287',
+ 'Sannat (23110)' => '23167',
+ 'Sansais (79270)' => '79304',
+ 'Sanxay (86600)' => '86253',
+ 'Sarbazan (40120)' => '40288',
+ 'Sardent (23250)' => '23168',
+ 'Sare (64310)' => '64504',
+ 'Sarlande (24270)' => '24519',
+ 'Sarlat-la-Canéda (24200)' => '24520',
+ 'Sarliac-sur-l\'Isle (24420)' => '24521',
+ 'Sarpourenx (64300)' => '64505',
+ 'Sarran (19800)' => '19251',
+ 'Sarrance (64490)' => '64506',
+ 'Sarrazac (24800)' => '24522',
+ 'Sarraziet (40500)' => '40289',
+ 'Sarron (40800)' => '40290',
+ 'Sarroux (19110)' => '19252',
+ 'Saubion (40230)' => '40291',
+ 'Saubole (64420)' => '64507',
+ 'Saubrigues (40230)' => '40292',
+ 'Saubusse (40180)' => '40293',
+ 'Saucats (33650)' => '33501',
+ 'Saucède (64400)' => '64508',
+ 'Saugnac-et-Cambran (40180)' => '40294',
+ 'Saugnacq-et-Muret (40410)' => '40295',
+ 'Saugon (33920)' => '33502',
+ 'Sauguis-Saint-Étienne (64470)' => '64509',
+ 'Saujon (17600)' => '17421',
+ 'Saulgé (86500)' => '86254',
+ 'Saulgond (16420)' => '16363',
+ 'Sault-de-Navailles (64300)' => '64510',
+ 'Sauméjan (47420)' => '47286',
+ 'Saumont (47600)' => '47287',
+ 'Saumos (33680)' => '33503',
+ 'Saurais (79200)' => '79306',
+ 'Saussignac (24240)' => '24523',
+ 'Sauternes (33210)' => '33504',
+ 'Sauvagnac (16310)' => '16364',
+ 'Sauvagnas (47340)' => '47288',
+ 'Sauvagnon (64230)' => '64511',
+ 'Sauvelade (64150)' => '64512',
+ 'Sauveterre-de-Béarn (64390)' => '64513',
+ 'Sauveterre-de-Guyenne (33540)' => '33506',
+ 'Sauveterre-la-Lémance (47500)' => '47292',
+ 'Sauveterre-Saint-Denis (47220)' => '47293',
+ 'Sauviac (33430)' => '33507',
+ 'Sauviat-sur-Vige (87400)' => '87190',
+ 'Sauvignac (16480)' => '16365',
+ 'Sauzé-Vaussais (79190)' => '79307',
+ 'Savennes (23000)' => '23170',
+ 'Savignac (33124)' => '33508',
+ 'Savignac-de-Duras (47120)' => '47294',
+ 'Savignac-de-l\'Isle (33910)' => '33509',
+ 'Savignac-de-Miremont (24260)' => '24524',
+ 'Savignac-de-Nontron (24300)' => '24525',
+ 'Savignac-Lédrier (24270)' => '24526',
+ 'Savignac-les-Églises (24420)' => '24527',
+ 'Savignac-sur-Leyze (47150)' => '47295',
+ 'Savigné (86400)' => '86255',
+ 'Savigny-Lévescault (86800)' => '86256',
+ 'Savigny-sous-Faye (86140)' => '86257',
+ 'Sceau-Saint-Angel (24300)' => '24528',
+ 'Sciecq (79000)' => '79308',
+ 'Scillé (79240)' => '79309',
+ 'Scorbé-Clairvaux (86140)' => '86258',
+ 'Séby (64410)' => '64514',
+ 'Secondigné-sur-Belle (79170)' => '79310',
+ 'Secondigny (79130)' => '79311',
+ 'Sedze-Maubecq (64160)' => '64515',
+ 'Sedzère (64160)' => '64516',
+ 'Ségalas (47410)' => '47296',
+ 'Segonzac (16130)' => '16366',
+ 'Segonzac (19310)' => '19253',
+ 'Segonzac (24600)' => '24529',
+ 'Ségur-le-Château (19230)' => '19254',
+ 'Seigné (17510)' => '17422',
+ 'Seignosse (40510)' => '40296',
+ 'Seilhac (19700)' => '19255',
+ 'Séligné (79170)' => '79312',
+ 'Sembas (47360)' => '47297',
+ 'Séméacq-Blachon (64350)' => '64517',
+ 'Semens (33490)' => '33510',
+ 'Semillac (17150)' => '17423',
+ 'Semoussac (17150)' => '17424',
+ 'Semussac (17120)' => '17425',
+ 'Sencenac-Puy-de-Fourches (24310)' => '24530',
+ 'Sendets (33690)' => '33511',
+ 'Sendets (64320)' => '64518',
+ 'Sénestis (47430)' => '47298',
+ 'Senillé-Saint-Sauveur (86100)' => '86245',
+ 'Sepvret (79120)' => '79313',
+ 'Sérandon (19160)' => '19256',
+ 'Séreilhac (87620)' => '87191',
+ 'Sergeac (24290)' => '24531',
+ 'Sérignac-Péboudou (47410)' => '47299',
+ 'Sérignac-sur-Garonne (47310)' => '47300',
+ 'Sérigny (86230)' => '86260',
+ 'Sérilhac (19190)' => '19257',
+ 'Sermur (23700)' => '23171',
+ 'Séron (65320)' => '65422',
+ 'Serres-Castet (64121)' => '64519',
+ 'Serres-et-Montguyard (24500)' => '24532',
+ 'Serres-Gaston (40700)' => '40298',
+ 'Serres-Morlaàs (64160)' => '64520',
+ 'Serres-Sainte-Marie (64170)' => '64521',
+ 'Serreslous-et-Arribans (40700)' => '40299',
+ 'Sers (16410)' => '16368',
+ 'Servanches (24410)' => '24533',
+ 'Servières-le-Château (19220)' => '19258',
+ 'Sévignacq (64160)' => '64523',
+ 'Sévignacq-Meyracq (64260)' => '64522',
+ 'Sèvres-Anxaumont (86800)' => '86261',
+ 'Sexcles (19430)' => '19259',
+ 'Seyches (47350)' => '47301',
+ 'Seyresse (40180)' => '40300',
+ 'Siecq (17490)' => '17427',
+ 'Siest (40180)' => '40301',
+ 'Sigalens (33690)' => '33512',
+ 'Sigogne (16200)' => '16369',
+ 'Sigoulès (24240)' => '24534',
+ 'Sillars (86320)' => '86262',
+ 'Sillas (33690)' => '33513',
+ 'Simacourbe (64350)' => '64524',
+ 'Simeyrols (24370)' => '24535',
+ 'Sindères (40110)' => '40302',
+ 'Singleyrac (24500)' => '24536',
+ 'Sioniac (19120)' => '19260',
+ 'Siorac-de-Ribérac (24600)' => '24537',
+ 'Siorac-en-Périgord (24170)' => '24538',
+ 'Sireuil (16440)' => '16370',
+ 'Siros (64230)' => '64525',
+ 'Smarves (86240)' => '86263',
+ 'Solférino (40210)' => '40303',
+ 'Solignac (87110)' => '87192',
+ 'Sommières-du-Clain (86160)' => '86264',
+ 'Sompt (79110)' => '79314',
+ 'Sonnac (17160)' => '17428',
+ 'Soorts-Hossegor (40150)' => '40304',
+ 'Sorbets (40320)' => '40305',
+ 'Sorde-l\'Abbaye (40300)' => '40306',
+ 'Sore (40430)' => '40307',
+ 'Sorges et Ligueux en Périgord (24420)' => '24540',
+ 'Sornac (19290)' => '19261',
+ 'Sort-en-Chalosse (40180)' => '40308',
+ 'Sos (47170)' => '47302',
+ 'Sossais (86230)' => '86265',
+ 'Soubise (17780)' => '17429',
+ 'Soubran (17150)' => '17430',
+ 'Soubrebost (23250)' => '23173',
+ 'Soudaine-Lavinadière (19370)' => '19262',
+ 'Soudan (79800)' => '79316',
+ 'Soudat (24360)' => '24541',
+ 'Soudeilles (19300)' => '19263',
+ 'Souffrignac (16380)' => '16372',
+ 'Soulac-sur-Mer (33780)' => '33514',
+ 'Soulaures (24540)' => '24542',
+ 'Soulignac (33760)' => '33515',
+ 'Soulignonne (17250)' => '17431',
+ 'Soumans (23600)' => '23174',
+ 'Soumensac (47120)' => '47303',
+ 'Souméras (17130)' => '17432',
+ 'Soumoulou (64420)' => '64526',
+ 'Souprosse (40250)' => '40309',
+ 'Souraïde (64250)' => '64527',
+ 'Soursac (19550)' => '19264',
+ 'Sourzac (24400)' => '24543',
+ 'Sous-Parsat (23150)' => '23175',
+ 'Sousmoulins (17130)' => '17433',
+ 'Soussac (33790)' => '33516',
+ 'Soussans (33460)' => '33517',
+ 'Soustons (40140)' => '40310',
+ 'Soutiers (79310)' => '79318',
+ 'Souvigné (16240)' => '16373',
+ 'Souvigné (79800)' => '79319',
+ 'Soyaux (16800)' => '16374',
+ 'Suaux (16260)' => '16375',
+ 'Suhescun (64780)' => '64528',
+ 'Surdoux (87130)' => '87193',
+ 'Surgères (17700)' => '17434',
+ 'Surin (79220)' => '79320',
+ 'Surin (86250)' => '86266',
+ 'Suris (16270)' => '16376',
+ 'Sus (64190)' => '64529',
+ 'Susmiou (64190)' => '64530',
+ 'Sussac (87130)' => '87194',
+ 'Tabaille-Usquain (64190)' => '64531',
+ 'Tabanac (33550)' => '33518',
+ 'Tadousse-Ussau (64330)' => '64532',
+ 'Taillant (17350)' => '17435',
+ 'Taillebourg (17350)' => '17436',
+ 'Taillebourg (47200)' => '47304',
+ 'Taillecavat (33580)' => '33520',
+ 'Taizé (79100)' => '79321',
+ 'Taizé-Aizie (16700)' => '16378',
+ 'Talais (33590)' => '33521',
+ 'Talence (33400)' => '33522',
+ 'Taller (40260)' => '40311',
+ 'Talmont-sur-Gironde (17120)' => '17437',
+ 'Tamniès (24620)' => '24544',
+ 'Tanzac (17260)' => '17438',
+ 'Taponnat-Fleurignac (16110)' => '16379',
+ 'Tardes (23170)' => '23251',
+ 'Tardets-Sorholus (64470)' => '64533',
+ 'Targon (33760)' => '33523',
+ 'Tarnac (19170)' => '19265',
+ 'Tarnès (33240)' => '33524',
+ 'Tarnos (40220)' => '40312',
+ 'Taron-Sadirac-Viellenave (64330)' => '64534',
+ 'Tarsacq (64360)' => '64535',
+ 'Tartas (40400)' => '40313',
+ 'Taugon (17170)' => '17439',
+ 'Tauriac (33710)' => '33525',
+ 'Tayac (33570)' => '33526',
+ 'Tayrac (47270)' => '47305',
+ 'Teillots (24390)' => '24545',
+ 'Temple-Laguyon (24390)' => '24546',
+ 'Tercé (86800)' => '86268',
+ 'Tercillat (23350)' => '23252',
+ 'Tercis-les-Bains (40180)' => '40314',
+ 'Ternant (17400)' => '17440',
+ 'Ternay (86120)' => '86269',
+ 'Terrasson-Lavilledieu (24120)' => '24547',
+ 'Tersannes (87360)' => '87195',
+ 'Tesson (17460)' => '17441',
+ 'Tessonnière (79600)' => '79325',
+ 'Téthieu (40990)' => '40315',
+ 'Teuillac (33710)' => '33530',
+ 'Teyjat (24300)' => '24548',
+ 'Thaims (17120)' => '17442',
+ 'Thairé (17290)' => '17443',
+ 'Thalamy (19200)' => '19266',
+ 'Thauron (23250)' => '23253',
+ 'Theil-Rabier (16240)' => '16381',
+ 'Thénac (17460)' => '17444',
+ 'Thénac (24240)' => '24549',
+ 'Thénezay (79390)' => '79326',
+ 'Thenon (24210)' => '24550',
+ 'Thézac (17600)' => '17445',
+ 'Thézac (47370)' => '47307',
+ 'Thèze (64450)' => '64536',
+ 'Thiat (87320)' => '87196',
+ 'Thiviers (24800)' => '24551',
+ 'Thollet (86290)' => '86270',
+ 'Thonac (24290)' => '24552',
+ 'Thorigné (79370)' => '79327',
+ 'Thorigny-sur-le-Mignon (79360)' => '79328',
+ 'Thors (17160)' => '17446',
+ 'Thouars (79100)' => '79329',
+ 'Thouars-sur-Garonne (47230)' => '47308',
+ 'Thouron (87140)' => '87197',
+ 'Thurageau (86110)' => '86271',
+ 'Thuré (86540)' => '86272',
+ 'Tilh (40360)' => '40316',
+ 'Tillou (79110)' => '79330',
+ 'Tizac-de-Curton (33420)' => '33531',
+ 'Tizac-de-Lapouyade (33620)' => '33532',
+ 'Tocane-Saint-Apre (24350)' => '24553',
+ 'Tombeboeuf (47380)' => '47309',
+ 'Tonnay-Boutonne (17380)' => '17448',
+ 'Tonnay-Charente (17430)' => '17449',
+ 'Tonneins (47400)' => '47310',
+ 'Torsac (16410)' => '16382',
+ 'Torxé (17380)' => '17450',
+ 'Tosse (40230)' => '40317',
+ 'Toulenne (33210)' => '33533',
+ 'Toulouzette (40250)' => '40318',
+ 'Toulx-Sainte-Croix (23600)' => '23254',
+ 'Tourliac (47210)' => '47311',
+ 'Tournon-d\'Agenais (47370)' => '47312',
+ 'Tourriers (16560)' => '16383',
+ 'Tourtenay (79100)' => '79331',
+ 'Tourtoirac (24390)' => '24555',
+ 'Tourtrès (47380)' => '47313',
+ 'Touvérac (16360)' => '16384',
+ 'Touvre (16600)' => '16385',
+ 'Touzac (16120)' => '16386',
+ 'Toy-Viam (19170)' => '19268',
+ 'Trayes (79240)' => '79332',
+ 'Treignac (19260)' => '19269',
+ 'Trélissac (24750)' => '24557',
+ 'Trémolat (24510)' => '24558',
+ 'Trémons (47140)' => '47314',
+ 'Trensacq (40630)' => '40319',
+ 'Trentels (47140)' => '47315',
+ 'Tresses (33370)' => '33535',
+ 'Triac-Lautrait (16200)' => '16387',
+ 'Trizay (17250)' => '17453',
+ 'Troche (19230)' => '19270',
+ 'Trois-Fonds (23230)' => '23255',
+ 'Trois-Palis (16730)' => '16388',
+ 'Trois-Villes (64470)' => '64537',
+ 'Tudeils (19120)' => '19271',
+ 'Tugéras-Saint-Maurice (17130)' => '17454',
+ 'Tulle (19000)' => '19272',
+ 'Turenne (19500)' => '19273',
+ 'Turgon (16350)' => '16389',
+ 'Tursac (24620)' => '24559',
+ 'Tusson (16140)' => '16390',
+ 'Tuzie (16700)' => '16391',
+ 'Uchacq-et-Parentis (40090)' => '40320',
+ 'Uhart-Cize (64220)' => '64538',
+ 'Uhart-Mixe (64120)' => '64539',
+ 'Urcuit (64990)' => '64540',
+ 'Urdès (64370)' => '64541',
+ 'Urdos (64490)' => '64542',
+ 'Urepel (64430)' => '64543',
+ 'Urgons (40320)' => '40321',
+ 'Urost (64160)' => '64544',
+ 'Urrugne (64122)' => '64545',
+ 'Urt (64240)' => '64546',
+ 'Urval (24480)' => '24560',
+ 'Ussac (19270)' => '19274',
+ 'Usseau (79210)' => '79334',
+ 'Usseau (86230)' => '86275',
+ 'Ussel (19200)' => '19275',
+ 'Usson-du-Poitou (86350)' => '86276',
+ 'Ustaritz (64480)' => '64547',
+ 'Uza (40170)' => '40322',
+ 'Uzan (64370)' => '64548',
+ 'Uzein (64230)' => '64549',
+ 'Uzerche (19140)' => '19276',
+ 'Uzeste (33730)' => '33537',
+ 'Uzos (64110)' => '64550',
+ 'Val d\'Issoire (87330)' => '87097',
+ 'Val de Virvée (33240)' => '33018',
+ 'Val des Vignes (16250)' => '16175',
+ 'Valdivienne (86300)' => '86233',
+ 'Valence (16460)' => '16392',
+ 'Valeuil (24310)' => '24561',
+ 'Valeyrac (33340)' => '33538',
+ 'Valiergues (19200)' => '19277',
+ 'Vallans (79270)' => '79335',
+ 'Vallereuil (24190)' => '24562',
+ 'Vallière (23120)' => '23257',
+ 'Valojoulx (24290)' => '24563',
+ 'Vançais (79120)' => '79336',
+ 'Vandré (17700)' => '17457',
+ 'Vanxains (24600)' => '24564',
+ 'Vanzac (17500)' => '17458',
+ 'Vanzay (79120)' => '79338',
+ 'Varaignes (24360)' => '24565',
+ 'Varaize (17400)' => '17459',
+ 'Vareilles (23300)' => '23258',
+ 'Varennes (24150)' => '24566',
+ 'Varennes (86110)' => '86277',
+ 'Varès (47400)' => '47316',
+ 'Varetz (19240)' => '19278',
+ 'Vars (16330)' => '16393',
+ 'Vars-sur-Roseix (19130)' => '19279',
+ 'Varzay (17460)' => '17460',
+ 'Vasles (79340)' => '79339',
+ 'Vaulry (87140)' => '87198',
+ 'Vaunac (24800)' => '24567',
+ 'Vausseroux (79420)' => '79340',
+ 'Vautebis (79420)' => '79341',
+ 'Vaux (86700)' => '86278',
+ 'Vaux-Lavalette (16320)' => '16394',
+ 'Vaux-Rouillac (16170)' => '16395',
+ 'Vaux-sur-Mer (17640)' => '17461',
+ 'Vaux-sur-Vienne (86220)' => '86279',
+ 'Vayres (33870)' => '33539',
+ 'Vayres (87600)' => '87199',
+ 'Végennes (19120)' => '19280',
+ 'Veix (19260)' => '19281',
+ 'Vélines (24230)' => '24568',
+ 'Vellèches (86230)' => '86280',
+ 'Vendays-Montalivet (33930)' => '33540',
+ 'Vendeuvre-du-Poitou (86380)' => '86281',
+ 'Vendoire (24320)' => '24569',
+ 'Vénérand (17100)' => '17462',
+ 'Vensac (33590)' => '33541',
+ 'Ventouse (16460)' => '16396',
+ 'Vérac (33240)' => '33542',
+ 'Verdelais (33490)' => '33543',
+ 'Verdets (64400)' => '64551',
+ 'Verdille (16140)' => '16397',
+ 'Verdon (24520)' => '24570',
+ 'Vergeroux (17300)' => '17463',
+ 'Vergné (17330)' => '17464',
+ 'Vergt (24380)' => '24571',
+ 'Vergt-de-Biron (24540)' => '24572',
+ 'Vérines (17540)' => '17466',
+ 'Verneiges (23170)' => '23259',
+ 'Verneuil (16310)' => '16398',
+ 'Verneuil-Moustiers (87360)' => '87200',
+ 'Verneuil-sur-Vienne (87430)' => '87201',
+ 'Vernon (86340)' => '86284',
+ 'Vernoux-en-Gâtine (79240)' => '79342',
+ 'Vernoux-sur-Boutonne (79170)' => '79343',
+ 'Verrières (16130)' => '16399',
+ 'Verrières (86410)' => '86285',
+ 'Verrue (86420)' => '86286',
+ 'Verruyes (79310)' => '79345',
+ 'Vert (40420)' => '40323',
+ 'Verteillac (24320)' => '24573',
+ 'Verteuil-d\'Agenais (47260)' => '47317',
+ 'Verteuil-sur-Charente (16510)' => '16400',
+ 'Vertheuil (33180)' => '33545',
+ 'Vervant (16330)' => '16401',
+ 'Vervant (17400)' => '17467',
+ 'Veyrac (87520)' => '87202',
+ 'Veyrières (19200)' => '19283',
+ 'Veyrignac (24370)' => '24574',
+ 'Veyrines-de-Domme (24250)' => '24575',
+ 'Veyrines-de-Vergt (24380)' => '24576',
+ 'Vézac (24220)' => '24577',
+ 'Vézières (86120)' => '86287',
+ 'Vialer (64330)' => '64552',
+ 'Viam (19170)' => '19284',
+ 'Vianne (47230)' => '47318',
+ 'Vibrac (16120)' => '16402',
+ 'Vibrac (17130)' => '17468',
+ 'Vicq-d\'Auribat (40380)' => '40324',
+ 'Vicq-sur-Breuilh (87260)' => '87203',
+ 'Vicq-sur-Gartempe (86260)' => '86288',
+ 'Vidaillat (23250)' => '23260',
+ 'Videix (87600)' => '87204',
+ 'Vielle-Saint-Girons (40560)' => '40326',
+ 'Vielle-Soubiran (40240)' => '40327',
+ 'Vielle-Tursan (40320)' => '40325',
+ 'Viellenave-d\'Arthez (64170)' => '64554',
+ 'Viellenave-de-Navarrenx (64190)' => '64555',
+ 'Vielleségure (64150)' => '64556',
+ 'Viennay (79200)' => '79347',
+ 'Viersat (23170)' => '23261',
+ 'Vieux-Boucau-les-Bains (40480)' => '40328',
+ 'Vieux-Mareuil (24340)' => '24579',
+ 'Vieux-Ruffec (16350)' => '16404',
+ 'Vigeois (19410)' => '19285',
+ 'Vigeville (23140)' => '23262',
+ 'Vignes (64410)' => '64557',
+ 'Vignolles (16300)' => '16405',
+ 'Vignols (19130)' => '19286',
+ 'Vignonet (33330)' => '33546',
+ 'Vilhonneur (16220)' => '16406',
+ 'Villac (24120)' => '24580',
+ 'Villamblard (24140)' => '24581',
+ 'Villandraut (33730)' => '33547',
+ 'Villard (23800)' => '23263',
+ 'Villars (24530)' => '24582',
+ 'Villars-en-Pons (17260)' => '17469',
+ 'Villars-les-Bois (17770)' => '17470',
+ 'Villebois-Lavalette (16320)' => '16408',
+ 'Villebramar (47380)' => '47319',
+ 'Villedoux (17230)' => '17472',
+ 'Villefagnan (16240)' => '16409',
+ 'Villefavard (87190)' => '87206',
+ 'Villefollet (79170)' => '79348',
+ 'Villefranche-de-Lonchat (24610)' => '24584',
+ 'Villefranche-du-Périgord (24550)' => '24585',
+ 'Villefranche-du-Queyran (47160)' => '47320',
+ 'Villefranque (64990)' => '64558',
+ 'Villegats (16700)' => '16410',
+ 'Villegouge (33141)' => '33548',
+ 'Villejésus (16140)' => '16411',
+ 'Villejoubert (16560)' => '16412',
+ 'Villemain (79110)' => '79349',
+ 'Villemorin (17470)' => '17473',
+ 'Villemort (86310)' => '86291',
+ 'Villenave (40110)' => '40330',
+ 'Villenave-d\'Ornon (33140)' => '33550',
+ 'Villenave-de-Rions (33550)' => '33549',
+ 'Villenave-près-Béarn (65500)' => '65476',
+ 'Villeneuve (33710)' => '33551',
+ 'Villeneuve-de-Duras (47120)' => '47321',
+ 'Villeneuve-de-Marsan (40190)' => '40331',
+ 'Villeneuve-la-Comtesse (17330)' => '17474',
+ 'Villeneuve-sur-Lot (47300)' => '47323',
+ 'Villeréal (47210)' => '47324',
+ 'Villeton (47400)' => '47325',
+ 'Villetoureix (24600)' => '24586',
+ 'Villexavier (17500)' => '17476',
+ 'Villiers (86190)' => '86292',
+ 'Villiers-Couture (17510)' => '17477',
+ 'Villiers-en-Bois (79360)' => '79350',
+ 'Villiers-en-Plaine (79160)' => '79351',
+ 'Villiers-le-Roux (16240)' => '16413',
+ 'Villiers-sur-Chizé (79170)' => '79352',
+ 'Villognon (16230)' => '16414',
+ 'Vinax (17510)' => '17478',
+ 'Vindelle (16430)' => '16415',
+ 'Viodos-Abense-de-Bas (64130)' => '64559',
+ 'Virazeil (47200)' => '47326',
+ 'Virelade (33720)' => '33552',
+ 'Virollet (17260)' => '17479',
+ 'Virsac (33240)' => '33553',
+ 'Virson (17290)' => '17480',
+ 'Vitrac (24200)' => '24587',
+ 'Vitrac-Saint-Vincent (16310)' => '16416',
+ 'Vitrac-sur-Montane (19800)' => '19287',
+ 'Viven (64450)' => '64560',
+ 'Viville (16120)' => '16417',
+ 'Vivonne (86370)' => '86293',
+ 'Voeuil-et-Giget (16400)' => '16418',
+ 'Voissay (17400)' => '17481',
+ 'Vouharte (16330)' => '16419',
+ 'Vouhé (17700)' => '17482',
+ 'Vouhé (79310)' => '79354',
+ 'Vouillé (79230)' => '79355',
+ 'Vouillé (86190)' => '86294',
+ 'Voulême (86400)' => '86295',
+ 'Voulgézac (16250)' => '16420',
+ 'Voulmentin (79150)' => '79242',
+ 'Voulon (86700)' => '86296',
+ 'Vouneuil-sous-Biard (86580)' => '86297',
+ 'Vouneuil-sur-Vienne (86210)' => '86298',
+ 'Voutezac (19130)' => '19288',
+ 'Vouthon (16220)' => '16421',
+ 'Vouzailles (86170)' => '86299',
+ 'Vouzan (16410)' => '16422',
+ 'Xaintrailles (47230)' => '47327',
+ 'Xaintray (79220)' => '79357',
+ 'Xambes (16330)' => '16423',
+ 'Ychoux (40160)' => '40332',
+ 'Ygos-Saint-Saturnin (40110)' => '40333',
+ 'Yssandon (19310)' => '19289',
+ 'Yversay (86170)' => '86300',
+ 'Yves (17340)' => '17483',
+ 'Yviers (16210)' => '16424',
+ 'Yvrac (33370)' => '33554',
+ 'Yvrac-et-Malleyrand (16110)' => '16425',
+ 'Yzosse (40180)' => '40334'
+ );
+}
diff --git a/bridges/AutoJMBridge.php b/bridges/AutoJMBridge.php
index e8490d95..25fb2cb8 100644
--- a/bridges/AutoJMBridge.php
+++ b/bridges/AutoJMBridge.php
@@ -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');
diff --git a/bridges/BAEBridge.php b/bridges/BAEBridge.php
index caa2cf72..6c5d8ba2 100644
--- a/bridges/BAEBridge.php
+++ b/bridges/BAEBridge.php
@@ -55,9 +55,7 @@ class BAEBridge extends BridgeAbstract {
$content .= '
';
$content .= $htmlDetail->find('section', 0)->innertext;
- $content = str_replace('src="/', 'src="' . parent::getURI() . '/', $content);
- $content = str_replace('href="/', 'href="' . parent::getURI() . '/', $content);
- $item['content'] = $content;
+ $item['content'] = defaultLinkTo($content, parent::getURI());
$image = $htmlDetail->find('#zoom', 0);
if ($image) {
$item['enclosures'] = array(parent::getURI() . $image->getAttribute('src'));
diff --git a/bridges/BandcampBridge.php b/bridges/BandcampBridge.php
index 6c75ed5e..fa071465 100644
--- a/bridges/BandcampBridge.php
+++ b/bridges/BandcampBridge.php
@@ -1,73 +1,262 @@
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'] = "
$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'] = "
$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'] = "
$full_title
";
+ if ($tralbum_data->type === 'a') {
+ $item['content'] .= '';
+ foreach ($tralbum_data->tracks as $track) {
+ $item['content'] .= "- $track->title
";
+ }
+ $item['content'] .= '
';
+ }
+ if (!empty($tralbum_data->about)) {
+ $item['content'] .= ''
+ . nl2br($tralbum_data->about)
+ . '
';
+ }
+
+ 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;
+ }
}
diff --git a/bridges/BastaBridge.php b/bridges/BastaBridge.php
index 17d3da76..613005fd 100644
--- a/bridges/BastaBridge.php
+++ b/bridges/BastaBridge.php
@@ -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++;
}
diff --git a/bridges/BingSearchBridge.php b/bridges/BingSearchBridge.php
index eb8a5fc9..357feb6c 100644
--- a/bridges/BingSearchBridge.php
+++ b/bridges/BingSearchBridge.php
@@ -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);
diff --git a/bridges/BloombergBridge.php b/bridges/BloombergBridge.php
deleted file mode 100644
index 9eb12191..00000000
--- a/bridges/BloombergBridge.php
+++ /dev/null
@@ -1,69 +0,0 @@
- 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 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 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;
- }
- }
-}
diff --git a/bridges/BrutBridge.php b/bridges/BrutBridge.php
index 432cb502..32265b69 100644
--- a/bridges/BrutBridge.php
+++ b/bridges/BrutBridge.php
@@ -92,6 +92,21 @@ class BrutBridge extends BridgeAbstract {
return parent::getURI();
}
+ public function getName() {
+
+ if (!is_null($this->getInput('edition')) && !is_null($this->getInput('category'))) {
+ $parameters = $this->getParameters();
+
+ $editionValues = array_flip($parameters[0]['edition']['values']);
+ $categoryValues = array_flip($parameters[0]['category']['values']);
+
+ return $categoryValues[$this->getInput('category')] . ' - ' .
+ $editionValues[$this->getInput('edition')] . ' - Brut.';
+ }
+
+ return parent::getName();
+ }
+
private function processDate($description) {
if ($this->getInput('edition') === 'uk') {
diff --git a/bridges/CNETFranceBridge.php b/bridges/CNETFranceBridge.php
new file mode 100644
index 00000000..d005fd1f
--- /dev/null
+++ b/bridges/CNETFranceBridge.php
@@ -0,0 +1,63 @@
+ 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;
+ }
+}
diff --git a/bridges/CachetBridge.php b/bridges/CachetBridge.php
index a60b8f73..75b18017 100644
--- a/bridges/CachetBridge.php
+++ b/bridges/CachetBridge.php
@@ -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;
diff --git a/bridges/CastorusBridge.php b/bridges/CastorusBridge.php
index 3ed1331e..fbd5007c 100644
--- a/bridges/CastorusBridge.php
+++ b/bridges/CastorusBridge.php
@@ -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';
@@ -83,7 +83,7 @@ class CastorusBridge extends BridgeAbstract {
if(!$html)
returnServerError('Could not load data from ' . self::URI . '!');
- $activities = $html->find('div#activite/li');
+ $activities = $html->find('div#activite > li');
if(!$activities)
returnServerError('Failed to find activities!');
diff --git a/bridges/CollegeDeFranceBridge.php b/bridges/CollegeDeFranceBridge.php
index 1f816832..9640c867 100644
--- a/bridges/CollegeDeFranceBridge.php
+++ b/bridges/CollegeDeFranceBridge.php
@@ -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';
diff --git a/bridges/ComicsKingdomBridge.php b/bridges/ComicsKingdomBridge.php
new file mode 100644
index 00000000..b6228dc6
--- /dev/null
+++ b/bridges/ComicsKingdomBridge.php
@@ -0,0 +1,65 @@
+ 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'] = '';
+
+ $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();
+ }
+}
diff --git a/bridges/ContainerLinuxReleasesBridge.php b/bridges/ContainerLinuxReleasesBridge.php
index d2f63256..d459b0fc 100644
--- a/bridges/ContainerLinuxReleasesBridge.php
+++ b/bridges/ContainerLinuxReleasesBridge.php
@@ -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;
diff --git a/bridges/CuriousCatBridge.php b/bridges/CuriousCatBridge.php
new file mode 100644
index 00000000..0ebc8bd6
--- /dev/null
+++ b/bridges/CuriousCatBridge.php
@@ -0,0 +1,109 @@
+ 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 = <<{$post['senderData']['username']}
+EOD;
+ }
+
+ $question = $this->formatUrls($post['comment']);
+ $answer = $this->formatUrls($post['reply']);
+
+ $content = <<{$author} asked:
+{$question}
+{$post['addresseeData']['username']} answered:
+{$answer}
+EOD;
+
+ return $content;
+ }
+
+ private function ellipsisTitle($text) {
+ $length = 150;
+
+ if (strlen($text) > $length) {
+ $text = explode('
', wordwrap($text, $length, '
'));
+ return $text[0] . '...';
+ }
+
+ return $text;
+ }
+
+ private function formatUrls($content) {
+
+ return preg_replace(
+ '/(http[s]{0,1}\:\/\/[a-zA-Z0-9.\/\?\&=\-_]{4,})/ims',
+ '$1 ',
+ $content
+ );
+
+ }
+}
diff --git a/bridges/DailymotionBridge.php b/bridges/DailymotionBridge.php
index ff8d4828..dc4f5d3f 100644
--- a/bridges/DailymotionBridge.php
+++ b/bridges/DailymotionBridge.php
@@ -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'] = '
+
' . $apiItem['description'] . '
';
+ $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'] = '
'
- . $item['title']
- . '';
+ . $item['uri']
+ . '">
'
+ . $item['title']
+ . '';
$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;
+ }
+ }
}
diff --git a/bridges/DanbooruBridge.php b/bridges/DanbooruBridge.php
index 755399f4..ea4b2be8 100644
--- a/bridges/DanbooruBridge.php
+++ b/bridges/DanbooruBridge.php
@@ -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;
diff --git a/bridges/DarkReadingBridge.php b/bridges/DarkReadingBridge.php
new file mode 100644
index 00000000..3baaad75
--- /dev/null
+++ b/bridges/DarkReadingBridge.php
@@ -0,0 +1,79 @@
+ 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(
+ '');
+
+ return $content;
+ }
+}
diff --git a/bridges/DavesTrailerPageBridge.php b/bridges/DavesTrailerPageBridge.php
new file mode 100644
index 00000000..90afec46
--- /dev/null
+++ b/bridges/DavesTrailerPageBridge.php
@@ -0,0 +1,27 @@
+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;
+ }
+ }
+}
diff --git a/bridges/DealabsBridge.php b/bridges/DealabsBridge.php
index b64bb1dc..1657b8b7 100644
--- a/bridges/DealabsBridge.php
+++ b/bridges/DealabsBridge.php
@@ -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;
diff --git a/bridges/DemonoidBridge.php b/bridges/DemonoidBridge.php
deleted file mode 100644
index 842b4214..00000000
--- a/bridges/DemonoidBridge.php
+++ /dev/null
@@ -1,169 +0,0 @@
- array(
- 'q' => array(
- 'name' => 'keywords',
- 'exampleValue' => 'keyword1 keyword2…',
- 'required' => true,
- ),
- 'category' => array(
- 'name' => 'Category',
- 'type' => 'list',
- 'values' => array(
- 'All' => 0,
- 'Movies' => 1,
- 'Music' => 2,
- 'TV' => 3,
- 'Games' => 4,
- 'Applications' => 5,
- 'Pictures' => 8,
- 'Anime' => 9,
- 'Comics' => 10,
- 'Books' => 11,
- 'Audiobooks' => 17
- )
- )
- ),
- 'Category Only' => array(
- 'catOnly' => array(
- 'name' => 'Category',
- 'type' => 'list',
- 'values' => array(
- 'All' => 0,
- 'Movies' => 1,
- 'Music' => 2,
- 'TV' => 3,
- 'Games' => 4,
- 'Applications' => 5,
- 'Pictures' => 8,
- 'Anime' => 9,
- 'Comics' => 10,
- 'Books' => 11,
- 'Audiobooks' => 17
- )
- )
- ),
- 'User ID' => array(
- 'userid' => array(
- 'name' => 'user id',
- 'exampleValue' => '00000',
- 'required' => true,
- 'type' => 'number'
- ),
- 'category' => array(
- 'name' => 'Category',
- 'type' => 'list',
- 'values' => array(
- 'All' => 0,
- 'Movies' => 1,
- 'Music' => 2,
- 'TV' => 3,
- 'Games' => 4,
- 'Applications' => 5,
- 'Pictures' => 8,
- 'Anime' => 9,
- 'Comics' => 10,
- 'Books' => 11,
- 'Audiobooks' => 17
- )
- )
- )
- );
-
- public function collectData() {
-
- if(!empty($this->getInput('q'))) {
-
- $html = getSimpleHTMLDOM(
- self::URI .
- 'files/?category=' .
- rawurlencode($this->getInput('category')) .
- '&subcategory=All&quality=All&seeded=2&external=2&query=' .
- urlencode($this->getInput('q')) .
- '&uid=0&sort='
- ) or returnServerError('Could not request Demonoid.');
-
- } elseif(!empty($this->getInput('catOnly'))) {
-
- $html = getSimpleHTMLDOM(
- self::URI .
- 'files/?uid=0&category=' .
- rawurlencode($this->getInput('catOnly')) .
- '&subcategory=0&language=0&seeded=2&quality=0&query=&sort='
- ) or returnServerError('Could not request Demonoid.');
-
- } elseif(!empty($this->getInput('userid'))) {
-
- $html = getSimpleHTMLDOM(
- self::URI .
- 'files/?uid=' .
- rawurlencode($this->getInput('userid')) .
- '&seeded=2'
- ) or returnServerError('Could not request Demonoid.');
-
- } else {
- returnServerError('Invalid parameters !');
- }
-
- if(preg_match('~No torrents found~', $html)) {
- return;
- }
-
- $table = $html->find('td[class=ctable_content_no_pad]', 0);
- $cursorCount = 4;
- $elementCount = 0;
- while($elementCount != 40) {
- $elementCount++;
- $currentElement = $table->find('tr', $cursorCount);
- if(preg_match('~items total~', $currentElement)) {
- break;
- }
- $item = array();
- //Do we have a date ?
- if(preg_match('~Added.*?(.*)~', $currentElement->plaintext, $dateStr)) {
- if(preg_match('~today~', $dateStr[0])) {
- date_default_timezone_set('UTC');
- $timestamp = mktime(0, 0, 0, gmdate('n'), gmdate('j'), gmdate('Y'));
- } else {
- preg_match('~(?<=ed on ).*\d+~', $currentElement->plaintext, $fullDateStr);
- date_default_timezone_set('UTC');
- $dateObj = strptime($fullDateStr[0], '%A, %b %d, %Y');
- $timestamp = mktime(0, 0, 0, $dateObj['tm_mon'] + 1, $dateObj['tm_mday'], 1900 + $dateObj['tm_year']);
- }
- $cursorCount++;
- }
-
- $content = $table->find('tr', $cursorCount)->find('a', 1);
- $cursorCount++;
- $torrentInfo = $table->find('tr', $cursorCount);
- $item['timestamp'] = $timestamp;
- $item['title'] = $content->plaintext;
- $item['id'] = self::URI . $content->href;
- $item['uri'] = self::URI . $content->href;
- $item['author'] = $torrentInfo->find('a[class=user]', 0)->plaintext;
- $item['seeders'] = $torrentInfo->find('font[class=green]', 0)->plaintext;
- $item['leechers'] = $torrentInfo->find('font[class=red]', 0)->plaintext;
- $item['size'] = $torrentInfo->find('td', 3)->plaintext;
- $item['content'] = 'Uploaded by ' . $item['author']
- . ' , Size ' . $item['size']
- . '
seeders: '
- . $item['seeders']
- . ' | leechers: '
- . $item['leechers']
- . '
info page';
-
- $this->items[] = $item;
-
- $cursorCount++;
- }
- }
-}
diff --git a/bridges/DesoutterBridge.php b/bridges/DesoutterBridge.php
index 4a7b0a94..38761ed8 100644
--- a/bridges/DesoutterBridge.php
+++ b/bridges/DesoutterBridge.php
@@ -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,19 +162,23 @@ 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();
- $item['uri'] = $article->find('[itemprop="name"]', 0)->href;
- $item['title'] = $article->find('[itemprop="name"]', 0)->title;
+ $item['uri'] = $article->find('a', 0)->href;
+ $item['title'] = $article->find('a[title]', 0)->title;
if($this->getInput('full')) {
$item['content'] = $this->getFullNewsArticle($item['uri']);
} else {
- $item['content'] = $article->find('[itemprop="description"]', 0)->plaintext;
+ $item['content'] = $article->find('div.tile-body p', 0)->plaintext;
}
$this->items[] = $item;
+
+ if ($limit > 0 && count($this->items) >= $limit) break;
}
}
diff --git a/bridges/DiarioDoAlentejoBridge.php b/bridges/DiarioDoAlentejoBridge.php
new file mode 100644
index 00000000..806f8031
--- /dev/null
+++ b/bridges/DiarioDoAlentejoBridge.php
@@ -0,0 +1,60 @@
+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 .
+ '
' . $element->find('a.category', 0) . '';
+ $item['content'] = defaultLinkTo($content, self::URI);
+
+ $this->items[] = $item;
+ }
+ }
+}
diff --git a/bridges/DollbooruBridge.php b/bridges/DollbooruBridge.php
deleted file mode 100644
index 5ed4119f..00000000
--- a/bridges/DollbooruBridge.php
+++ /dev/null
@@ -1,9 +0,0 @@
- array(
+ 'country' => array(
+ 'type' => 'list',
+ 'name' => 'Country',
+ 'values' => array(
+ 'Argentina' => 'https://downdetector.com.ar',
+ 'Australia' => 'https://downdetector.com.au',
+ 'België' => 'https://allestoringen.be',
+ 'Brasil' => 'https://downdetector.com.br',
+ 'Canada' => 'https://downdetector.ca',
+ 'Chile' => 'https://downdetector.cl',
+ 'Colombia' => 'https://downdetector.com.co',
+ 'Danmark' => 'https://downdetector.dk',
+ 'Deutschland' => 'https://allestörungen.de',
+ 'Ecuador' => 'https://downdetector.ec',
+ 'España' => 'https://downdetector.es',
+ 'France' => 'https://downdetector.fr',
+ 'Hong Kong' => 'https://downdetector.hk',
+ 'Hrvatska' => 'https://downdetector.hr',
+ 'India' => 'https://downdetector.in',
+ 'Indonesia' => 'https://downdetector.id',
+ 'Ireland' => 'https://downdetector.ie',
+ 'Italia' => 'https://downdetector.it',
+ 'Magyarország' => 'https://downdetector.hu',
+ 'Malaysia' => 'https://downdetector.my',
+ 'México' => 'https://downdetector.mx',
+ 'Nederland' => 'https://allestoringen.nl',
+ 'New Zealand' => 'https://downdetector.co.nz',
+ 'Norge' => 'https://downdetector.no',
+ 'Pakistan' => 'https://downdetector.pk',
+ 'Perú' => 'https://downdetector.pe',
+ 'Pilipinas' => 'https://downdetector.ph',
+ 'Polska' => 'https://downdetector.pl',
+ 'Portugal' => 'https://downdetector.pt',
+ 'România' => 'https://downdetector.ro',
+ 'Schweiz' => 'https://allestörungen.ch',
+ 'Singapore' => 'https://downdetector.sg',
+ 'Slovensko' => 'https://downdetector.sk',
+ 'South Africa' => 'https://downdetector.co.za',
+ 'Suomi' => 'https://downdetector.fi',
+ 'Sverige' => 'https://downdetector.se',
+ 'Türkiye' => 'https://downdetector.web.tr',
+ 'UAE' => 'https://downdetector.ae',
+ 'UK' => 'https://downdetector.co.uk',
+ 'United States' => 'https://downdetector.com',
+ 'Österreich' => 'https://allestörungen.at',
+ 'Česko' => 'https://downdetector.cz',
+ 'Ελλάς' => 'https://downdetector.gr',
+ 'Россия' => 'https://downdetector.ru',
+ '日本' => 'https://downdetector.jp'
+ )
+ )
+ ),
+ 'Specific Website' => array(
+ 'website' => array(
+ 'type' => 'list',
+ 'name' => 'Website',
+ 'values' => array(
+ 'Österreich' => array(
+ '1&1' => 35086,
+ '3 (Drei)' => 33546,
+ 'A1' => 33543,
+ 'Alexa' => 36919,
+ 'Amazon' => 33506,
+ 'Amazon Prime Video' => 35085,
+ 'Amino Apps' => 39034,
+ 'Anthem' => 38200,
+ 'Apex Legends' => 38117,
+ 'App Store' => 35584,
+ 'Bank Austria' => 34715,
+ 'Battlefield' => 38051,
+ 'BAWAG' => 34716,
+ 'Binance' => 36938,
+ 'Blizzard Battle.net' => 35087,
+ 'Bob' => 34953,
+ 'Boom Beach' => 34781,
+ 'Bwin' => 35071,
+ 'Call of Duty' => 34156,
+ 'Car2Go' => 34554,
+ 'Clash of Clans' => 35088,
+ 'Clash Royale' => 38357,
+ 'Coinbase' => 36804,
+ 'Counter-strike' => 35055,
+ 'Crunchyroll' => 38092,
+ 'Dazn' => 36508,
+ 'Dead By Daylight' => 37414,
+ 'Deezer' => 33832,
+ 'Destiny' => 34954,
+ 'DHL' => 36747,
+ 'Discord' => 36768,
+ 'Dota 2' => 35398,
+ 'Dropbox' => 33509,
+ 'EA' => 34502,
+ 'Easybank' => 36992,
+ 'eBay' => 33510,
+ 'Emerion' => 34613,
+ 'Epic Games Store' => 39021,
+ 'Erste Bank und Sparkasse' => 36724,
+ 'Facebook' => 33511,
+ 'Facebook Messenger' => 33512,
+ 'Fifa' => 37605,
+ 'Flickr' => 33513,
+ 'For Honor' => 35996,
+ 'Fortnite' => 36689,
+ 'Ghost Recon' => 36009,
+ 'Gmail' => 33514,
+ 'GMX' => 33515,
+ 'Google' => 33516,
+ 'Google Hangouts' => 33517,
+ 'Google Play' => 33518,
+ 'GTA 5' => 35082,
+ 'Guild Wars 2' => 36473,
+ 'Handy Parken' => 34316,
+ 'Hay Day' => 34854,
+ 'Hello Bank' => 37010,
+ 'HoT' => 38751,
+ 'iCloud' => 35501,
+ 'ICQ' => 33520,
+ 'ING DiBa' => 35129,
+ 'Instagram' => 33522,
+ 'iTunes' => 33523,
+ 'Kabelplus' => 34473,
+ 'Kik' => 33524,
+ 'Kraken' => 36770,
+ 'League of Legends' => 34350,
+ 'LinkedIn' => 33525,
+ 'Liwest' => 34471,
+ 'Lovoo' => 35079,
+ 'Magenta' => 38440,
+ 'Maxdome' => 35084,
+ 'Minecraft' => 36432,
+ 'Mittwald' => 36987,
+ 'N26' => 38834,
+ 'Netatmo' => 37956,
+ 'Netflix' => 34631,
+ 'Nintendo Network' => 35523,
+ 'Nitrado' => 35548,
+ 'NordVPN' => 38587,
+ 'Office 365' => 35120,
+ 'OneDrive' => 35412,
+ 'ORF' => 35600,
+ 'Origin' => 36889,
+ 'Outlook' => 35083,
+ 'Overwatch' => 36153,
+ 'Path of Exile' => 37730,
+ 'Paypal' => 35399,
+ 'Playerunknown\'s Battlegrounds' => 36488,
+ 'Playstation Network' => 33526,
+ 'Pokémon Go' => 35745,
+ 'Quizduell' => 34528,
+ 'Raiffeisen Bank' => 38750,
+ 'Rainbow Six' => 35563,
+ 'Red Dead Redemption' => 37739,
+ 'Reddit' => 36827,
+ 'Rocket League' => 35485,
+ 'roNET' => 37041,
+ 'Salzburg AG Cablelink' => 35601,
+ 'Shpock' => 38681,
+ 'Sky' => 35081,
+ 'Sky Ticket' => 35142,
+ 'Skype' => 33527,
+ 'Smart Hub' => 35400,
+ 'Snapchat' => 33528,
+ 'Spotify' => 33529,
+ 'Spusu' => 35598,
+ 'Steam' => 34117,
+ 'Teamviewer' => 35686,
+ 'Tele2' => 34339,
+ 'Telegram' => 34903,
+ 'Telering' => 34952,
+ 'The Division' => 35599,
+ 'The elder scrolls online' => 37160,
+ 'The Simpsons Tapped Out' => 37283,
+ 'Threema' => 34255,
+ 'Tinder' => 34243,
+ 'Tipico' => 36515,
+ 'Tumblr' => 33530,
+ 'Twitch' => 35024,
+ 'Twitter' => 33531,
+ 'Uplay PC' => 34689,
+ 'Viber' => 33532,
+ 'Viewster' => 34326,
+ 'Vimeo' => 33533,
+ 'Volksbank' => 34717,
+ 'Warface' => 37524,
+ 'Warframe' => 37136,
+ 'Waze' => 33534,
+ 'Whatsapp' => 33535,
+ 'Wikipedia' => 33536,
+ 'Willhaben.at' => 35859,
+ 'World of Tanks' => 36674,
+ 'World of Warcraft' => 36998,
+ 'World of Warships' => 38009,
+ 'Xbox Live' => 33538,
+ 'Yahoo Mail' => 33539,
+ 'Yahoo Messenger' => 33540,
+ 'Yesss' => 35346,
+ 'Youtube' => 33541,
+ 'Z1 Battle Royale' => 35489,
+ ),
+ 'Deutschland' => array(
+ '1&1' => 32554,
+ '1blu' => 37319,
+ '2k' => 37731,
+ '3CX' => 36439,
+ '3sat' => 35797,
+ 'Afterbuy' => 37015,
+ 'Airbnb' => 35422,
+ 'Albion Online' => 38799,
+ 'Aldi Talk' => 32579,
+ 'Alexa' => 35912,
+ 'All-inkl' => 35162,
+ 'Amazon' => 32572,
+ 'Amazon Prime Music' => 37584,
+ 'Amazon Prime Video' => 34495,
+ 'Amazon Web Services' => 36326,
+ 'Amino Apps' => 39033,
+ 'Amplus' => 35446,
+ 'Anthem' => 38055,
+ 'Anydesk' => 37487,
+ 'AOL' => 34377,
+ 'Apex Legends' => 38112,
+ 'App Store' => 35579,
+ 'Apple Music' => 35246,
+ 'Apple Store' => 34448,
+ 'Arche NetVision' => 35593,
+ 'ArcheAge' => 35062,
+ 'Arcor' => 34374,
+ 'ARK: Survival Evolved' => 39065,
+ 'Arma 3' => 35063,
+ 'Asana' => 38024,
+ 'Assassin\'s Creed' => 35010,
+ 'Badoo' => 35011,
+ 'Base' => 32568,
+ 'Battlefield' => 36092,
+ 'Baur' => 35043,
+ 'Bet3000' => 35035,
+ 'Bet365' => 35034,
+ 'Bethesda' => 38317,
+ 'BILDmobil' => 32609,
+ 'Binance' => 36942,
+ 'Bing' => 34708,
+ 'Bitfinex' => 36833,
+ 'Bitstamp' => 36808,
+ 'Blackberry' => 3,
+ 'Blade &Soul' => 35512,
+ 'Blau' => 37655,
+ 'Blau' => 34108,
+ 'Blizzard Battle.net' => 34483,
+ 'Blogger' => 32606,
+ 'BMW ConnectedDrive' => 38034,
+ 'Boom Beach' => 34365,
+ 'Bornet' => 34944,
+ 'Bwin' => 34491,
+ 'Call of Duty' => 34155,
+ 'Candy Crush' => 35148,
+ 'Candy Crush Soda Saga' => 35174,
+ 'Car-Net' => 37031,
+ 'Car2Go' => 34555,
+ 'Centurylink' => 35624,
+ 'Checkdomain' => 36096,
+ 'Clash of Clans' => 34289,
+ 'Clash Royale' => 35588,
+ 'Cloudflare' => 34881,
+ 'Coinbase' => 36798,
+ 'Colt' => 37653,
+ 'Comdirect' => 34774,
+ 'Comedy Central' => 34122,
+ 'Commerzbank' => 32607,
+ 'Comunio' => 34719,
+ 'Congstar' => 32590,
+ 'Consors Bank' => 35545,
+ 'CosmosDirect' => 35042,
+ 'Counter-strike' => 34887,
+ 'Crunchyroll' => 36718,
+ 'DABbank' => 35921,
+ 'Das Erste' => 34386,
+ 'Dazn' => 35956,
+ 'Dead By Daylight' => 35951,
+ 'Deezer' => 33831,
+ 'DeGiro' => 38203,
+ 'Deliveroo' => 37546,
+ 'Destiny' => 34895,
+ 'Deutsche Bahn' => 32569,
+ 'Deutsche Bank' => 32611,
+ 'Deutsche Glasfaser' => 36087,
+ 'Deutsche Telefon' => 35851,
+ 'DeutschlandSIM' => 34635,
+ 'DFP' => 35004,
+ 'DHL' => 34896,
+ 'Discord' => 35796,
+ 'DKB' => 32767,
+ 'DNSNET' => 35670,
+ 'DomainFactory' => 37381,
+ 'Dota 2' => 34900,
+ 'dpd' => 36120,
+ 'Dragon Ball' => 36380,
+ 'Drillisch' => 36019,
+ 'Driveclub' => 34928,
+ 'Dropbox' => 32585,
+ 'E-Plus' => 10121,
+ 'EA' => 34497,
+ 'Easybell' => 34370,
+ 'eBay' => 32567,
+ 'Ecotel' => 36794,
+ 'Elite: Dangerous' => 37077,
+ 'Emailn' => 34687,
+ 'Emerion' => 34614,
+ 'Entega' => 37039,
+ 'Epic Games Store' => 38827,
+ 'Escape from Tarkov' => 37977,
+ 'Etoro' => 36965,
+ 'Eurosport Player' => 36559,
+ 'Eventim' => 37621,
+ 'Evernote ' => 36506,
+ 'Ewe TEL' => 33559,
+ 'Facebook' => 32552,
+ 'Facebook Messenger' => 32560,
+ 'Faceit' => 37147,
+ 'Facetime' => 34603,
+ 'Fallout' => 35433,
+ 'Farm Heroes Saga' => 35175,
+ 'Fidor Bank' => 35894,
+ 'Fifa' => 35469,
+ 'Finya' => 37060,
+ 'Fitbit' => 37972,
+ 'Fl!nk' => 35170,
+ 'Flickr' => 32604,
+ 'Fonic' => 32594,
+ 'For Honor' => 35985,
+ 'Fortnite' => 36626,
+ 'Forza' => 37568,
+ 'Freenet' => 34354,
+ 'Friday the 13th The Game' => 37561,
+ 'Fyve' => 34378,
+ 'G-Portal' => 37155,
+ 'Game of war' => 35230,
+ 'Gameduell' => 37566,
+ 'Gardena Smart' => 37350,
+ 'Garmin' => 37049,
+ 'Gears of War' => 35940,
+ 'Gems of war' => 38802,
+ 'Geocaching' => 37178,
+ 'Ghost Recon' => 36001,
+ 'Giropay' => 38204,
+ 'GitHub' => 35348,
+ 'GLS' => 36119,
+ 'Gmail' => 32584,
+ 'GMX' => 32563,
+ 'Go Daddy' => 34874,
+ 'Goneo' => 37576,
+ 'Google' => 32553,
+ 'Google Drive' => 36603,
+ 'Google Hangouts' => 32600,
+ 'Google Kalender' => 38603,
+ 'Google Play' => 32593,
+ 'Gran Turismo' => 36917,
+ 'Grindr' => 35532,
+ 'GTA 5' => 34754,
+ 'Guild Wars 2' => 35061,
+ 'Halo' => 35419,
+ 'Halo Wars' => 36034,
+ 'Harry Potter: Wizards Unite' => 38686,
+ 'Hay Day' => 34366,
+ 'Helinet' => 34959,
+ 'Hermes' => 35196,
+ 'Hetzner ' => 35943,
+ 'Hipchat' => 34869,
+ 'Hitbox.TV' => 35932,
+ 'Hitman' => 35586,
+ 'Homematic' => 36795,
+ 'Hosteurope' => 37958,
+ 'Htp' => 34400,
+ 'Hue' => 37810,
+ 'Hunt: Showdown' => 38787,
+ 'HypoVereinsbank' => 34965,
+ 'iCloud' => 32549,
+ 'ICQ' => 33123,
+ 'Idealo' => 37156,
+ 'iMessage' => 32671,
+ 'Inexio' => 36371,
+ 'ING DiBa' => 35156,
+ 'Ingress' => 35765,
+ 'Innogy Highspeed' => 37459,
+ 'Instagram' => 32599,
+ 'Intercity Express (ICE)' => 32581,
+ 'iTunes' => 32672,
+ 'Jappy' => 32596,
+ 'Jira' => 36066,
+ 'Jobst DSL' => 36727,
+ 'Jodel' => 36793,
+ 'Joyn' => 38604,
+ 'Jurassic World Alive' => 37310,
+ 'K-Classic Mobil' => 33986,
+ 'Kabel eins' => 34392,
+ 'Kickbase' => 37451,
+ 'Kicker' => 37450,
+ 'Kicktipp' => 35824,
+ 'Kik' => 33353,
+ 'Klarmobil' => 32605,
+ 'KMS' => 34399,
+ 'Knuddels' => 35865,
+ 'Kraken' => 36749,
+ 'Ladbrokes' => 35161,
+ 'LastPass' => 34780,
+ 'League of Legends' => 34112,
+ 'Lebara' => 35791,
+ 'Line' => 34300,
+ 'LinkedIn' => 32557,
+ 'Lotto24' => 35040,
+ 'Lottohelden' => 36882,
+ 'Lovoo' => 34814,
+ 'Lufthansa' => 32574,
+ 'Lycamobile' => 34939,
+ 'M-net' => 32571,
+ 'Madden' => 38906,
+ 'Mail.de' => 34909,
+ 'Mailbox' => 34832,
+ 'Markt.de' => 37465,
+ 'Maxdome' => 34749,
+ 'MDCC' => 34760,
+ 'MDDSL' => 37056,
+ 'Mercedes Me' => 37153,
+ 'MiCoach' => 34936,
+ 'Microsoft Azure' => 36093,
+ 'Microsoft Teams' => 38185,
+ 'Minecraft' => 32556,
+ 'Mittwald' => 33412,
+ 'Mixer' => 38770,
+ 'Mobilcom Debitel' => 32610,
+ 'MTV' => 34120,
+ 'My Fitness Pal' => 37637,
+ 'Mybet' => 35036,
+ 'N26' => 35719,
+ 'NBA 2k' => 38595,
+ 'Need for Speed' => 35429,
+ 'Netatmo' => 37452,
+ 'Netbeat' => 35906,
+ 'NetCologne' => 32587,
+ 'netcombw' => 35638,
+ 'netcup' => 35632,
+ 'Netflix' => 34630,
+ 'Netkom' => 37201,
+ 'Neverwinter' => 35768,
+ 'Nfon' => 35792,
+ 'Nintendo Network' => 35519,
+ 'Nintendo Switch Online' => 37244,
+ 'Nitrado' => 34941,
+ 'No Man\'s Sky' => 35794,
+ 'NordVPN' => 38584,
+ 'Norisbank' => 34964,
+ 'Nvidia' => 37462,
+ 'Nvidia' => 39069,
+ 'O2' => 10122,
+ 'Office 365' => 34730,
+ 'OkCupid' => 37330,
+ 'OLB' => 37057,
+ 'OneDrive' => 35408,
+ 'Onleihe' => 35635,
+ 'Origin' => 34371,
+ 'Osnatel' => 33873,
+ 'Otelo' => 35480,
+ 'Otto' => 32598,
+ 'Outlook' => 32546,
+ 'Overwatch' => 35684,
+ 'Paladins' => 35925,
+ 'Path of Exile' => 36479,
+ 'Payback' => 37654,
+ 'Paypal' => 34375,
+ 'pcvisit' => 37643,
+ 'PES' => 37952,
+ 'Pet Rescue Saga' => 35176,
+ 'Pinterest' => 37585,
+ 'Placetel' => 35128,
+ 'Playerunknown\'s Battlegrounds' => 36332,
+ 'Playstation Network' => 32551,
+ 'Pokémon Duel' => 35976,
+ 'Pokémon Go' => 35724,
+ 'Pokerstars' => 37439,
+ 'Postbank' => 32589,
+ 'Posteo' => 34309,
+ 'Primacom' => 33502,
+ 'ProSieben' => 34390,
+ 'PŸUR' => 32592,
+ 'QSC' => 33560,
+ 'Quizduell' => 34292,
+ 'Rainbow Six' => 35479,
+ 'Razer' => 38699,
+ 'Realm Royale' => 37288,
+ 'Red Dead Redemption' => 37601,
+ 'Reddit' => 35882,
+ 'RFT Kabel' => 35831,
+ 'Roblox' => 35815,
+ 'Rocket League' => 35252,
+ 'Royal Games' => 38217,
+ 'RTL II' => 34393,
+ 'RTL Television' => 34387,
+ 'Runescape' => 35108,
+ 'RWE' => 36116,
+ 'RWW' => 37955,
+ 'Ryanair' => 37518,
+ 'Salesforce' => 34733,
+ 'Santander Consumer Bank' => 32603,
+ 'Sat.1' => 34389,
+ 'Save.TV' => 37193,
+ 'Sea of Thieves' => 37112,
+ 'Shpock' => 37311,
+ 'Signal' => 37072,
+ 'Simply' => 36376,
+ 'Simquadrat' => 34945,
+ 'Simsme' => 34795,
+ 'Simyo' => 32591,
+ 'Sipgate' => 34127,
+ 'SKL' => 35041,
+ 'Sky' => 32562,
+ 'Sky Ticket' => 35141,
+ 'Skype' => 32561,
+ 'Skype for Business' => 35351,
+ 'Slack' => 35936,
+ 'Smart Hub' => 35181,
+ 'Smite' => 34803,
+ 'Snapchat' => 33377,
+ 'Soundcloud' => 34353,
+ 'Sparda' => 35370,
+ 'Sparkasse' => 32757,
+ 'Speedtest' => 38780,
+ 'Spiegel' => 36050,
+ 'Spotify' => 32564,
+ 'Star Citizen' => 39035,
+ 'Star Wars Battlefront' => 35460,
+ 'Steam' => 32559,
+ 'Strato' => 33413,
+ 'Strava' => 38731,
+ 'Streetspotr' => 34920,
+ 'Summoners War' => 37420,
+ 'SWB' => 34515,
+ 'Tado' => 36634,
+ 'Tango' => 33731,
+ 'Targobank' => 34966,
+ 'Teamspeak' => 33346,
+ 'Teamviewer' => 34344,
+ 'Tele2' => 33503,
+ 'Tele5' => 35727,
+ 'Telegram' => 34229,
+ 'Telekom' => 10117,
+ 'Tellonym' => 37248,
+ 'The Crew 2' => 37252,
+ 'The Division' => 35570,
+ 'The elder scrolls online' => 34480,
+ 'The Simpsons Tapped Out' => 34701,
+ 'Threema' => 34253,
+ 'Tiktok' => 39044,
+ 'Tinder' => 34242,
+ 'Tipico' => 34714,
+ 'TNG' => 38772,
+ 'Todoist' => 36462,
+ 'TomTom' => 35813,
+ 'Tumblr' => 32588,
+ 'TuneIn' => 38763,
+ 'TV Now' => 35143,
+ 'Tweakbox' => 38349,
+ 'Twitch' => 34376,
+ 'Twitter' => 32583,
+ 'Udemy' => 38388,
+ 'Unitymedia' => 32548,
+ 'Uplay PC' => 34380,
+ 'Usenext' => 36117,
+ 'Vero' => 37064,
+ 'Versatel' => 33561,
+ 'Viber' => 33372,
+ 'Viewster' => 34324,
+ 'Vimeo' => 32582,
+ 'Visa' => 37238,
+ 'VIVA' => 34121,
+ 'Vodafone' => 10120,
+ 'Volksbanken und Raiffeisenbanken' => 32758,
+ 'VOX' => 34391,
+ 'Warface' => 35944,
+ 'Warframe' => 36366,
+ 'Watchbox' => 36537,
+ 'Wattpad' => 34700,
+ 'Waze' => 33411,
+ 'Web.de' => 32586,
+ 'WeChat' => 34301,
+ 'Weebly' => 34914,
+ 'Weight Watchers' => 35662,
+ 'WeTransfer' => 36433,
+ 'Wetter.com' => 36051,
+ 'WetterOnline' => 34810,
+ 'Whatsapp' => 32555,
+ 'Wikipedia' => 32565,
+ 'Wilhelm.tel' => 34398,
+ 'Wish' => 38153,
+ 'WiSoTEL' => 37543,
+ 'Wobcom' => 35991,
+ 'Wordpress' => 32570,
+ 'World of Tanks' => 35524,
+ 'World of Warcraft' => 34373,
+ 'World of Warships' => 36638,
+ 'Wüstenrot' => 36977,
+ 'Xbox Live' => 32573,
+ 'Xing' => 34822,
+ 'Yahoo Mail' => 32597,
+ 'Yahoo Messenger' => 32580,
+ 'Yourfone' => 33415,
+ 'Youtube' => 32578,
+ 'Youtube Music' => 37586,
+ 'Z1 Battle Royale' => 35147,
+ 'Zattoo' => 35367,
+ 'ZDF' => 34388,
+ 'Zynga' => 32608,
+ ),
+ 'Nederland' => array(
+ '112' => 10011,
+ '3FM' => 10174,
+ '9292.nl' => 33376,
+ 'ABN-Amro' => 29,
+ 'ABP pensioenfonds' => 35862,
+ 'Adobe Creative Cloud' => 34916,
+ 'ADP' => 32669,
+ 'Adyen' => 37610,
+ 'Aegon Bank' => 10132,
+ 'Afas' => 10093,
+ 'Afterpay' => 34769,
+ 'Airbnb' => 35423,
+ 'Airmiles' => 37516,
+ 'Albert Heijn' => 34010,
+ 'Alex' => 10056,
+ 'Algemeen Dagblad' => 1,
+ 'AliExpress' => 37384,
+ 'Amazon' => 37379,
+ 'Amazon Prime Video' => 36976,
+ 'AMSIX' => 10008,
+ 'Antagonist' => 35160,
+ 'Anthem' => 38054,
+ 'Apex Legends' => 38118,
+ 'App Store' => 35580,
+ 'Apple Music' => 35245,
+ 'Apple Store' => 34446,
+ 'Argenta' => 10158,
+ 'Argeweb' => 35073,
+ 'Arriva' => 2,
+ 'ASN Bank' => 10048,
+ 'Assassin\'s Creed' => 35009,
+ 'Badoo' => 37650,
+ 'Battlefield' => 37094,
+ 'Belastingdienst' => 10002,
+ 'Ben' => 10053,
+ 'Bibliotheek' => 34132,
+ 'Binance' => 36879,
+ 'Binck' => 10055,
+ 'Bing' => 34707,
+ 'BlaBlaCar' => 36088,
+ 'Bliep' => 33998,
+ 'Blizzard Battle.net' => 34485,
+ 'Bol.com' => 10112,
+ 'Booking.com' => 37574,
+ 'Boom Beach' => 34363,
+ 'Brabant Water' => 10071,
+ 'Brawl Stars' => 38818,
+ 'Budgetphone' => 35132,
+ 'Buienalarm' => 37245,
+ 'Buienradar' => 33565,
+ 'Bunq' => 35820,
+ 'Byte' => 32764,
+ 'Caiway' => 4,
+ 'CAK' => 37289,
+ 'Call of Duty' => 34154,
+ 'CanalDigitaal' => 31,
+ 'Candy Crush' => 34025,
+ 'Cbizz' => 37659,
+ 'Centraal Beheer Achmea' => 10157,
+ 'Centraal Bureau voor de Statistiek' => 10168,
+ 'CheapConnect' => 36122,
+ 'Chelloo' => 35546,
+ 'Choozze' => 35096,
+ 'Clash of Clans' => 34290,
+ 'Clash Royale' => 37522,
+ 'Cloudflare' => 38623,
+ 'Cloudhosting.nl' => 33486,
+ 'Coinbase' => 36236,
+ 'Comedy Central' => 34033,
+ 'Concepts' => 10043,
+ 'Connexxion' => 5,
+ 'Coolblue' => 35543,
+ 'Counter-strike' => 36644,
+ 'Credit Europe Bank' => 10194,
+ 'Crunchyroll' => 36722,
+ 'Dead By Daylight' => 37412,
+ 'Deezer' => 33769,
+ 'DeGiro' => 34019,
+ 'Delight Mobile' => 33987,
+ 'Delta' => 10123,
+ 'Destiny' => 34891,
+ 'Deutsche Bank' => 10119,
+ 'DFP' => 35003,
+ 'DHL' => 35223,
+ 'DigiD' => 10001,
+ 'Digipoort' => 34520,
+ 'Discord' => 36043,
+ 'Disney+' => 38319,
+ 'Dota 2' => 36397,
+ 'Dpd' => 35619,
+ 'Dropbox' => 10192,
+ 'Dumpert' => 33375,
+ 'Dunea' => 10074,
+ 'EA' => 34501,
+ 'Easynet' => 33997,
+ 'eBay' => 33857,
+ 'Elite: Dangerous' => 37777,
+ 'Enduris' => 35999,
+ 'Eneco' => 10124,
+ 'Energielabel voor Woningen' => 35117,
+ 'Enexis' => 10014,
+ 'Ennatuurlijk' => 35159,
+ 'Epic Games Store' => 39020,
+ 'Escape from Tarkov' => 39059,
+ 'Esprit Telecom' => 10150,
+ 'Essent' => 10114,
+ 'Etoro' => 36958,
+ 'Eurosport Player' => 35821,
+ 'Eventim' => 37622,
+ 'Evides' => 10072,
+ 'Eweka' => 10039,
+ 'Exact Online' => 34815,
+ 'Exchange Online' => 34728,
+ 'Facebook' => 6,
+ 'Facebook Messenger' => 10185,
+ 'Facetime' => 34607,
+ 'Fallout' => 35434,
+ 'FBTO' => 34015,
+ 'Feedly' => 34712,
+ 'Fiber' => 10103,
+ 'Fifa' => 35474,
+ 'Flickr' => 37948,
+ 'Flitsmeister' => 34369,
+ 'For Honor' => 35987,
+ 'Fortnite' => 36625,
+ 'Fox Sports' => 10096,
+ 'Friday the 13th The Game' => 37560,
+ 'Funda' => 32576,
+ 'Game of war' => 35231,
+ 'Garmin' => 38165,
+ 'Garmin Connect' => 38171,
+ 'Gatehub' => 36957,
+ 'Ghost Recon' => 36010,
+ 'GitHub' => 35347,
+ 'Glashart Media' => 10105,
+ 'GLS' => 36118,
+ 'Gmail' => 10041,
+ 'Google' => 10010,
+ 'Google Agenda' => 38602,
+ 'Google Drive' => 34276,
+ 'Google Hangouts' => 10062,
+ 'Google Play' => 10038,
+ 'Gran Turismo' => 36873,
+ 'Greenchoice' => 37959,
+ 'Greenwheels' => 34007,
+ 'Grindr' => 35531,
+ 'GTA 5' => 34753,
+ 'Guild Wars 2' => 36464,
+ 'GVB' => 7,
+ 'Halo' => 35420,
+ 'Happn' => 37014,
+ 'Harry Potter: Wizards Unite' => 38689,
+ 'Hay Day' => 34368,
+ 'Hearthstone' => 38564,
+ 'Hollandsnieuwe' => 10040,
+ 'Hosted.nl' => 33483,
+ 'Hostnet' => 32761,
+ 'HTM' => 8,
+ 'Hue' => 37812,
+ 'iCloud' => 30,
+ 'iDeal' => 10025,
+ 'IEX' => 35782,
+ 'IG' => 34307,
+ 'iMessage' => 10130,
+ 'Indeed' => 35941,
+ 'Infopact' => 37565,
+ 'ING' => 9,
+ 'Instagram' => 32558,
+ 'International Card Services (ICS)' => 10169,
+ 'InterNLnet' => 34252,
+ 'Interpolis' => 34017,
+ 'ITDev Solutions' => 35533,
+ 'iTunes' => 10151,
+ 'Jira' => 36067,
+ 'Jonaz' => 37348,
+ 'Jumbo' => 36184,
+ 'Jurassic World Alive' => 37334,
+ 'JustEat' => 10164,
+ 'Kabel Noord' => 10113,
+ 'Kabeltex' => 10138,
+ 'Kadaster' => 37652,
+ 'Kamer van Koophandel' => 10167,
+ 'KickXL' => 10098,
+ 'Kik' => 10191,
+ 'Kliksafe' => 10109,
+ 'KLM' => 10028,
+ 'Knab' => 10131,
+ 'KNMI' => 34013,
+ 'KPN' => 11,
+ 'Kraken' => 36748,
+ 'Kruidvat' => 37312,
+ 'LastPass' => 34779,
+ 'League of Legends' => 35169,
+ 'LeasePlan Bank' => 10160,
+ 'Leaseweb' => 32762,
+ 'Lebara' => 10128,
+ 'Lexa.nl' => 35838,
+ 'Liander' => 10013,
+ 'Lijbrandt' => 10045,
+ 'LinkedIn' => 10201,
+ 'Litebit' => 36836,
+ 'Lloyds Bank' => 10154,
+ 'LOI' => 34947,
+ 'Lotto' => 34923,
+ 'Lycamobile' => 10129,
+ 'Lynx' => 10085,
+ 'Magister' => 35274,
+ 'Marktplaats' => 10170,
+ 'Microsoft Azure' => 36114,
+ 'Microsoft Teams' => 38186,
+ 'Mijnbroker' => 10088,
+ 'Mijndomein' => 34348,
+ 'MijnOverheid' => 10015,
+ 'Minecraft' => 36173,
+ 'Mobicross' => 34898,
+ 'Moneyou' => 10155,
+ 'Motto' => 35482,
+ 'MTV' => 34031,
+ 'Multisafepay' => 34877,
+ 'My Fitness Pal' => 37636,
+ 'MyOrder' => 34291,
+ 'Nationale Nederlanden' => 10166,
+ 'Nest' => 35189,
+ 'Net 5' => 10181,
+ 'Netatmo' => 37453,
+ 'Netflix' => 33849,
+ 'Neverwinter' => 36676,
+ 'NIBC Direct' => 10159,
+ 'Nintendo eShop' => 34105,
+ 'Nintendo Network' => 35520,
+ 'Nintendo Switch Online' => 37242,
+ 'Nitrado' => 35549,
+ 'NLE' => 37651,
+ 'NLziet' => 34219,
+ 'NordVPN' => 38585,
+ 'NOS.nl' => 10063,
+ 'NPO 1' => 10179,
+ 'NPO 2' => 10180,
+ 'NPO 3' => 10182,
+ 'NPO Start' => 10064,
+ 'NS' => 21,
+ 'Nu.nl' => 12,
+ 'Nuon' => 10084,
+ 'Oasen' => 10078,
+ 'Office 365' => 35119,
+ 'OHRA' => 34819,
+ 'ON' => 10097,
+ 'OneDrive' => 32779,
+ 'Online.nl' => 10193,
+ 'OnlineWerkplekken.nl' => 33485,
+ 'Onsbrabantnet' => 10044,
+ 'Origin' => 34372,
+ 'Outlook' => 10042,
+ 'OV-chipkaart' => 10145,
+ 'Overwatch' => 35723,
+ 'Paladins' => 37407,
+ 'Park Mobile' => 10139,
+ 'Park-line' => 10137,
+ 'Path of Exile' => 37729,
+ 'Pathé Thuis' => 33848,
+ 'Paypal' => 10021,
+ 'Paysafecard' => 10152,
+ 'PCextreme' => 32763,
+ 'Picnic' => 37503,
+ 'PIN' => 10009,
+ 'Player Unknown\'s Battlegrounds' => 36336,
+ 'Playstation Network' => 10006,
+ 'Plinq' => 10108,
+ 'Pokémon Go' => 35731,
+ 'Pokerstars' => 37438,
+ 'PostNL' => 32766,
+ 'PWN Waterleidingbedrijf Noord-Holland' => 10073,
+ 'Q-Park' => 10111,
+ 'Quizduel' => 34293,
+ 'Qurrent' => 35729,
+ 'Rabobank' => 13,
+ 'Radio 1' => 10175,
+ 'Radio 2' => 10176,
+ 'Raet' => 32670,
+ 'Rainbow Six' => 35564,
+ 'Realm Royale' => 37315,
+ 'Red Dead Redemption' => 38286,
+ 'Reddit' => 36822,
+ 'Redworks' => 34773,
+ 'Reggefiber' => 10104,
+ 'RegioBank' => 10050,
+ 'Rekam' => 10195,
+ 'Rendo Netwerken' => 10126,
+ 'RET' => 14,
+ 'Reviced' => 36061,
+ 'Robeco' => 10153,
+ 'Robin Mobile' => 35695,
+ 'Roblox' => 35814,
+ 'Rocket League' => 35254,
+ 'RoutIT' => 34349,
+ 'RTL 5' => 10183,
+ 'RTL 8' => 10184,
+ 'RTL XL' => 10066,
+ 'RTL4' => 10178,
+ 'Runescape' => 35701,
+ 'Ruzzle' => 33968,
+ 'Ryanair' => 37517,
+ 'Salesforce' => 10016,
+ 'SBS6' => 10177,
+ 'Schiphol' => 10027,
+ 'Scorito' => 34726,
+ 'Sea of Thieves' => 37113,
+ 'SEOshop' => 35592,
+ 'Sharepoint Online' => 34732,
+ 'Signal' => 37073,
+ 'Signet' => 34407,
+ 'Simpel' => 10037,
+ 'Simyo' => 10054,
+ 'Skype' => 10024,
+ 'Skype for Business' => 34729,
+ 'Slack' => 37336,
+ 'Smart Hub' => 35180,
+ 'Snapchat' => 10187,
+ 'Snappet' => 37613,
+ 'SNS' => 10019,
+ 'Sociale Verzekeringsbank (SVB)' => 10186,
+ 'Sofort Banking' => 10149,
+ 'Solcon' => 10099,
+ 'Soundcloud' => 34352,
+ 'Sparql' => 34519,
+ 'Speurders' => 34805,
+ 'Spotify' => 10003,
+ 'Staatsloterij' => 10094,
+ 'Star Wars Battlefront' => 36918,
+ 'Steam' => 34106,
+ 'Stedin' => 10012,
+ 'Steep' => 37823,
+ 'Stemwijzer' => 35980,
+ 'Stichting Kabeltelevisie Pijnacker' => 34143,
+ 'Stipte' => 10102,
+ 'Strato' => 35045,
+ 'Strava' => 36046,
+ 'Studiemeter' => 37075,
+ 'Studystore' => 36600,
+ 'Surfnet' => 34396,
+ 'T-Mobile' => 19,
+ 'Tado' => 36633,
+ 'Tango' => 33740,
+ 'Teamviewer' => 34342,
+ 'Tele2' => 16,
+ 'Telegraaf' => 17,
+ 'Telegram' => 34221,
+ 'Telfort' => 18,
+ 'The Division' => 35569,
+ 'The elder scrolls online' => 37329,
+ 'Threema' => 34254,
+ 'Thuisbezorgd.nl' => 10163,
+ 'Ticketmaster' => 10133,
+ 'Tickney ' => 35923,
+ 'Tikkie' => 36737,
+ 'Tiktok' => 39074,
+ 'Tinder' => 34238,
+ 'Today\'s' => 10087,
+ 'TomTom Live' => 34917,
+ 'Toto' => 35590,
+ 'TradersOnly' => 10090,
+ 'Transavia' => 35168,
+ 'TransIP' => 32771,
+ 'Trined' => 10107,
+ 'Triodos Bank' => 10051,
+ 'True' => 36576,
+ 'Tumblr' => 36714,
+ 'TuneIn' => 38762,
+ 'Tweak' => 10100,
+ 'Tweakbox' => 38348,
+ 'Tweakers' => 37376,
+ 'Twinfield' => 36725,
+ 'Twitch' => 35025,
+ 'Twitter' => 22,
+ 'Unet' => 34408,
+ 'Uplay PC' => 34379,
+ 'UWV en Werk.nl' => 10017,
+ 'Van Lanschot Bankiers' => 10052,
+ 'VDX' => 35780,
+ 'Vectone' => 35237,
+ 'Veolia' => 32612,
+ 'Veronica' => 34493,
+ 'Versio' => 32765,
+ 'VGZ' => 34946,
+ 'Viber' => 10172,
+ 'Videoland' => 33850,
+ 'Vimeo' => 38534,
+ 'Vimexx' => 35924,
+ 'VirtualComputing.nl' => 35698,
+ 'Vitens' => 10070,
+ 'Vodafone' => 24,
+ 'VoIPmobiel' => 34692,
+ 'Volgjezorg' => 37645,
+ 'VPSHosting.nl' => 33487,
+ 'VPSServer.nl' => 33484,
+ 'VVV Cadeaubon' => 35134,
+ 'Warface' => 38764,
+ 'Warframe' => 36490,
+ 'Waterbedrijf Groningen' => 10077,
+ 'Waterleiding Maatschappij Limburg' => 10076,
+ 'Waterleidingmaatschappij Drenthe' => 10079,
+ 'Waternet' => 10075,
+ 'Waze' => 33409,
+ 'Webex' => 34164,
+ 'Webreus' => 32778,
+ 'Weebly' => 34913,
+ 'Weeronline.nl' => 34014,
+ 'Weerplaza' => 35115,
+ 'Wehkamp' => 33980,
+ 'Westland Infra' => 10125,
+ 'WeTransfer' => 10190,
+ 'Whatsapp' => 10000,
+ 'Wikipedia' => 10092,
+ 'Wish' => 38152,
+ 'Wisper' => 10101,
+ 'Woningnet' => 36758,
+ 'Wordfeud' => 25,
+ 'WordOn' => 34036,
+ 'World of Tanks' => 35526,
+ 'World of Warcraft' => 34382,
+ 'World of Warships' => 38010,
+ 'Wrts' => 34215,
+ 'Xbox Live' => 10148,
+ 'XS4ALL' => 26,
+ 'XSyou' => 10106,
+ 'Yahoo Mail' => 34020,
+ 'Yellowbrick' => 10140,
+ 'Youfone' => 10144,
+ 'Yourhosting' => 34360,
+ 'YouTube' => 10005,
+ 'Z1 Battle Royale' => 35495,
+ 'Zalando' => 33981,
+ 'Ziggo' => 28,
+ 'Zilveren Kruis' => 34727,
+ 'Zwitserleven' => 34262,
+ ),
+ 'UK' => array(
+ '123 Reg' => 35870,
+ '1and1' => 34818,
+ '2k' => 36427,
+ '3 (Three)' => 32674,
+ '4chan' => 38596,
+ 'Abebooks' => 36536,
+ 'Adobe Creative Cloud' => 34552,
+ 'Airbnb' => 35424,
+ 'Alexa' => 35911,
+ 'AliExpress' => 39062,
+ 'Amazon' => 32613,
+ 'Amazon Prime Music' => 36426,
+ 'Amazon Prime Video' => 34494,
+ 'Amazon Web Services' => 36327,
+ 'American Express' => 37398,
+ 'Amino Apps' => 38196,
+ 'Ancestry' => 38680,
+ 'Anthem' => 38053,
+ 'Anydesk' => 37489,
+ 'Apex Legends' => 38114,
+ 'App Store' => 35578,
+ 'Apple Music' => 35242,
+ 'Apple Store' => 34454,
+ 'Argos' => 34018,
+ 'ARK: Survival Evolved' => 36551,
+ 'Arlo' => 37076,
+ 'Asana' => 38023,
+ 'Asda' => 37979,
+ 'Ask4' => 34411,
+ 'Assassin\'s Creed' => 35006,
+ 'Autotrader' => 34793,
+ 'Badoo' => 37501,
+ 'Bank of Ireland UK' => 38228,
+ 'Bank of Scotland' => 32692,
+ 'Barclaycard' => 37762,
+ 'Barclays' => 32685,
+ 'Battlefield' => 36585,
+ 'Bet365' => 35030,
+ 'Betfair' => 35031,
+ 'Binance' => 36877,
+ 'Bing' => 33615,
+ 'Bitbucket' => 36683,
+ 'Black Desert Online' => 38492,
+ 'Blizzard Battle.net' => 34487,
+ 'Blogger' => 32617,
+ 'Booking.com' => 38703,
+ 'Boom Beach' => 34783,
+ 'Brawl Stars' => 38819,
+ 'British Airways' => 37644,
+ 'British Gas' => 35653,
+ 'BT' => 32673,
+ 'Bumble' => 36401,
+ 'Bwin' => 34490,
+ 'Cablecom' => 34413,
+ 'Cahoot' => 34826,
+ 'Call of Duty' => 34151,
+ 'Candy Crush' => 35094,
+ 'Capital One' => 37223,
+ 'Car-Net' => 37030,
+ 'Cdkeys' => 37575,
+ 'Centurylink' => 35625,
+ 'CEX' => 34897,
+ 'Channel 4' => 34948,
+ 'Chelsea Building Society' => 38223,
+ 'Clash of Clans' => 34444,
+ 'Clash Royale' => 36856,
+ 'Cloudflare' => 34880,
+ 'CMC Markets' => 37629,
+ 'Coinbase' => 36797,
+ 'Colt Group ' => 35961,
+ 'Conan Exiles' => 38392,
+ 'ConnectWise' => 38469,
+ 'Conventry Building Society' => 38225,
+ 'Counter-strike' => 35057,
+ 'Craigslist' => 32619,
+ 'Crunchyroll' => 36715,
+ 'Daisy' => 34410,
+ 'Danske Bank' => 38227,
+ 'Dark Souls' => 36953,
+ 'Dauntless' => 38541,
+ 'DayZ' => 38781,
+ 'Dazn' => 39010,
+ 'Dead By Daylight' => 36425,
+ 'Deezer' => 33829,
+ 'DeGiro' => 38202,
+ 'Deliveroo' => 37548,
+ 'Demon' => 34412,
+ 'Destiny' => 34893,
+ 'DeviantArt' => 38491,
+ 'DFP' => 34999,
+ 'Discord' => 36020,
+ 'Dota 2' => 35403,
+ 'Driveclub' => 34927,
+ 'Dropbox' => 32620,
+ 'EA' => 34506,
+ 'EA Sports UFC' => 38468,
+ 'Easynet' => 33881,
+ 'eBay' => 32621,
+ 'Eclipse' => 32680,
+ 'EE' => 32661,
+ 'Elite: Dangerous' => 36684,
+ 'Epic Games Store' => 38828,
+ 'Escape from Tarkov' => 38108,
+ 'Etoro' => 36959,
+ 'Etsy' => 36422,
+ 'Eurosport Player' => 35819,
+ 'Evernote' => 34705,
+ 'Exchange Online' => 34536,
+ 'Expedia' => 36421,
+ 'Experian' => 38156,
+ 'Exponential-E' => 34409,
+ 'Facebook' => 32622,
+ 'Facebook Messenger' => 32623,
+ 'Faceit' => 37145,
+ 'Facetime' => 34604,
+ 'Fallout' => 35431,
+ 'Fandom' => 38690,
+ 'Far Cry' => 37176,
+ 'Fasthosts' => 35705,
+ 'Fifa' => 35471,
+ 'Find my iPhone' => 36123,
+ 'First Direct' => 33978,
+ 'First Trust Bank' => 38226,
+ 'Fitbit' => 36630,
+ 'Flickr' => 32624,
+ 'Fling' => 35165,
+ 'For Honor' => 35988,
+ 'Fortnite' => 36624,
+ 'Forza' => 37569,
+ 'Freedompop' => 35639,
+ 'Freesat' => 37594,
+ 'Freeview' => 35771,
+ 'Friday the 13th The Game' => 37563,
+ 'Funimation' => 37745,
+ 'Game of war' => 35229,
+ 'Gamma' => 34899,
+ 'Garmin' => 36990,
+ 'Garmin Connect' => 37192,
+ 'Gears of War' => 35939,
+ 'Ghost Recon' => 36007,
+ 'Giffgaff' => 33985,
+ 'Gigler' => 35790,
+ 'GitHub' => 32625,
+ 'Glide' => 38901,
+ 'Gmail' => 32626,
+ 'Go Daddy' => 34873,
+ 'Google' => 32627,
+ 'Google Calendar' => 38598,
+ 'Google Cloud' => 38536,
+ 'Google Drive' => 34273,
+ 'Google Hangouts' => 32629,
+ 'Google Home' => 36138,
+ 'Google Play' => 32628,
+ 'GoToMeeting' => 36505,
+ 'Gran Turismo' => 36680,
+ 'Great Western Railway' => 37808,
+ 'Grindr' => 35530,
+ 'GTA 5' => 34752,
+ 'Guild Wars 2' => 36407,
+ 'Gumtree' => 32756,
+ 'Halifax' => 32689,
+ 'Halo' => 35451,
+ 'Harry Potter: Wizards Unite' => 38688,
+ 'Hay Day' => 34788,
+ 'Hayu' => 38052,
+ 'Heart Internet' => 37623,
+ 'Hearthstone' => 38563,
+ 'Hermes' => 34949,
+ 'Hipchat' => 34866,
+ 'Hitman' => 36880,
+ 'Hive' => 39072,
+ 'HMRC' => 36961,
+ 'Home telecom' => 35959,
+ 'Hootsuite' => 32630,
+ 'HQ Trivia' => 37161,
+ 'HSBC' => 32686,
+ 'Hue' => 37811,
+ 'Hunt: showdown' => 38823,
+ 'Hyperoptic' => 36520,
+ 'iCloud' => 32663,
+ 'IG' => 37630,
+ 'iMessage' => 32664,
+ 'Imgur' => 32632,
+ 'IMVU' => 37177,
+ 'Indeed' => 38589,
+ 'Instagram' => 32633,
+ 'Internet Movie Database (IMDb)' => 36752,
+ 'Iomart' => 37180,
+ 'iPlayer' => 32755,
+ 'iTunes' => 32665,
+ 'ITV' => 34302,
+ 'Janet' => 35452,
+ 'Jira' => 34871,
+ 'John Lewis' => 36782,
+ 'Jurassic World Alive' => 37332,
+ 'Just Eat' => 36031,
+ 'KC' => 34130,
+ 'Kik' => 33352,
+ 'Kraken' => 36751,
+ 'Ladbrokes' => 35033,
+ 'LastPass' => 34778,
+ 'League of Legends' => 35048,
+ 'LinkedIn' => 32634,
+ 'Litebit' => 36837,
+ 'Lloyds Bank' => 32691,
+ 'M&S Bank' => 35457,
+ 'M24Seven' => 37150,
+ 'Madden' => 38458,
+ 'Mail.com' => 34800,
+ 'Mailbox' => 34836,
+ 'Mailchimp' => 38728,
+ 'Manx Telecom' => 35534,
+ 'Mastercard' => 37250,
+ 'McDonalds app' => 37374,
+ 'Meetup' => 34246,
+ 'Metro Bank' => 34970,
+ 'Microsoft Azure' => 32651,
+ 'Microsoft Teams' => 38184,
+ 'Minecraft' => 32635,
+ 'Mixer' => 38768,
+ 'Monzo' => 37591,
+ 'Moonfruit' => 35456,
+ 'MTV' => 34124,
+ 'Musical.ly' => 37140,
+ 'My Fitness Pal' => 36956,
+ 'My Vue' => 36329,
+ 'Namesco Ltd' => 35900,
+ 'National Lottery' => 34950,
+ 'Nationwide' => 32690,
+ 'NatWest' => 32684,
+ 'NBA 2k' => 37721,
+ 'Nest' => 35192,
+ 'Netflix' => 32636,
+ 'Neverwinter' => 35767,
+ 'NikePlus' => 38783,
+ 'Nintendo eShop' => 34100,
+ 'Nintendo Network' => 35516,
+ 'Nintendo Switch Online' => 37241,
+ 'No Man\'s Sky' => 37405,
+ 'NordVPN' => 38248,
+ 'Now TV' => 34406,
+ 'Nvidia' => 39068,
+ 'O2' => 32660,
+ 'Office 365' => 34538,
+ 'OkCupid' => 37331,
+ 'Omegle' => 39043,
+ 'OneDrive' => 32781,
+ 'Online.net' => 36174,
+ 'ooVoo' => 36586,
+ 'Orange' => 32676,
+ 'Origin' => 36419,
+ 'Origin Broadband' => 36052,
+ 'Outlook' => 32631,
+ 'Overwatch' => 35681,
+ 'OVH' => 36160,
+ 'Paddy Power' => 35032,
+ 'Paladins' => 36377,
+ 'Path of Exile' => 36487,
+ 'Paypal' => 35557,
+ 'PES' => 38590,
+ 'Photobucket' => 32638,
+ 'Pinterest' => 32639,
+ 'Player Unknown\'s Battlegrounds' => 36335,
+ 'Playstation Network' => 32668,
+ 'PlentyOfFish' => 36023,
+ 'Plusnet' => 32683,
+ 'Pokémon Go' => 35730,
+ 'Pokerstars' => 37440,
+ 'Post Office' => 32682,
+ 'Project Online' => 34539,
+ 'ProtonMail' => 38963,
+ 'Quickbooks Online' => 36583,
+ 'Quizup' => 34298,
+ 'Rackspace' => 34610,
+ 'Rainbow Six' => 35476,
+ 'Rakuten TV' => 37597,
+ 'RBS (Royal Bank of Scotland)' => 32688,
+ 'Realm Royale' => 37309,
+ 'Red Dead Redemption' => 37743,
+ 'Redbox' => 38791,
+ 'Reddit' => 34844,
+ 'Relish' => 34797,
+ 'Revolut' => 37365,
+ 'Roblox' => 35786,
+ 'Rocket League' => 35355,
+ 'Royal Mail' => 35776,
+ 'Runescape' => 35107,
+ 'Ruzzle' => 34479,
+ 'Ryanair' => 36003,
+ 'Sagepay' => 36781,
+ 'Sainsbury\'s Bank' => 35116,
+ 'Salesforce' => 38495,
+ 'Santander' => 32687,
+ 'Sarahah' => 36373,
+ 'Scottish Power' => 36041,
+ 'Sea of Thieves' => 37110,
+ 'Sharepoint Online' => 34540,
+ 'Shopify' => 38538,
+ 'Shutterstock' => 37448,
+ 'Signal' => 37070,
+ 'Sky' => 32782,
+ 'Sky Bet' => 35076,
+ 'Skype' => 32640,
+ 'Skype for Business' => 34537,
+ 'Slack' => 35934,
+ 'Slideshare' => 32641,
+ 'Smart Hub' => 35179,
+ 'Smartsheet' => 38723,
+ 'Smile' => 36661,
+ 'Smite' => 35407,
+ 'Snapchat' => 32642,
+ 'Soundcloud' => 36954,
+ 'Sourceforge' => 32643,
+ 'South Western Railway' => 37809,
+ 'Spotify' => 32644,
+ 'Squarespace' => 38784,
+ 'SSE' => 37058,
+ 'Star Citizen' => 39036,
+ 'Star Wars Battlefront' => 35551,
+ 'Starling Bank' => 38719,
+ 'Steam' => 32667,
+ 'Steep' => 37822,
+ 'Strava' => 37046,
+ 'Streamcenter' => 37016,
+ 'Summoners War' => 37419,
+ 'T-Mobile' => 32679,
+ 'Tado' => 36632,
+ 'Talkmobile' => 33988,
+ 'TalkTalk' => 32677,
+ 'Tango' => 33739,
+ 'Teamviewer' => 34615,
+ 'Telegram' => 34222,
+ 'Tesco Bank' => 34969,
+ 'Tesco Broadband' => 34086,
+ 'Tesco Mobile' => 35149,
+ 'The Co-operative Bank' => 34971,
+ 'The Crew 2' => 36555,
+ 'The Division' => 35561,
+ 'The elder scrolls online' => 36400,
+ 'The People\'s Operator' => 35150,
+ 'The Simpsons Tapped out' => 35077,
+ 'Thinkmoney' => 36331,
+ 'Ticketmaster' => 36786,
+ 'Tiktok' => 38726,
+ 'Tinder' => 34240,
+ 'TomTom Live' => 34918,
+ 'Transferwise' => 38565,
+ 'Trove' => 38826,
+ 'TSB Bank' => 33970,
+ 'Tumblr' => 32645,
+ 'TuneIn' => 37973,
+ 'Tweakbox' => 38342,
+ 'Twitch' => 35022,
+ 'Twitter' => 32646,
+ 'Uber' => 37498,
+ 'Uber Eats' => 38271,
+ 'Udemy' => 37717,
+ 'UKfast' => 36838,
+ 'Ulster Bank' => 36071,
+ 'Unibet' => 36055,
+ 'Uplay PC' => 34691,
+ 'UPS' => 38744,
+ 'Viber' => 33349,
+ 'Vimeo' => 32647,
+ 'Virgin Media' => 32675,
+ 'Visa' => 37237,
+ 'Vistaprint' => 35130,
+ 'Vodafone' => 32659,
+ 'Waitrose' => 36964,
+ 'Warface' => 38766,
+ 'Warframe' => 36367,
+ 'Wattpad' => 34696,
+ 'Waze' => 33407,
+ 'Webex' => 34161,
+ 'Weebly' => 38547,
+ 'Weight Watchers' => 35663,
+ 'WeTransfer' => 32648,
+ 'Whatsapp' => 32649,
+ 'Whisper' => 37028,
+ 'Wikipedia' => 32650,
+ 'William Hill' => 35029,
+ 'Wish' => 39040,
+ 'Wix' => 37233,
+ 'Wordpress.com' => 32652,
+ 'World of Tanks' => 35525,
+ 'World of Warcraft' => 35052,
+ 'World of Warships' => 36639,
+ 'WWE Network' => 38085,
+ 'Xbox Live' => 32666,
+ 'Xero' => 37500,
+ 'XLN Telecom' => 33989,
+ 'Yahoo' => 32653,
+ 'Yahoo Mail' => 32654,
+ 'Yahoo Messenger' => 32655,
+ 'Yammer' => 36955,
+ 'Yelp' => 32656,
+ 'Yorkshire Bank' => 34968,
+ 'Yorkshire Building Society' => 38224,
+ 'Youtube' => 32657,
+ 'Youtube Music' => 37587,
+ 'Yubo' => 37246,
+ 'Z1 Battle Royale' => 35490,
+ 'Zen Internet' => 33874,
+ 'ZoHo' => 36039,
+ 'Zone Broadband' => 36994,
+ 'Zoom' => 37946,
+ 'Zynga' => 32658,
+ ),
+ 'United States' => array(
+ '1and1' => 34817,
+ '1Password' => 36441,
+ '2600hertz' => 37692,
+ '2k' => 34768,
+ '4chan' => 37567,
+ '7 Days to Die' => 35947,
+ '8x8' => 34314,
+ 'ABC' => 38902,
+ 'Abebooks' => 36535,
+ 'Absolver' => 37366,
+ 'Access One' => 36063,
+ 'Acorns' => 37647,
+ 'Adams Networks' => 33556,
+ 'Adobe Connect' => 34134,
+ 'Adobe Creative Cloud' => 34551,
+ 'ADP' => 37657,
+ 'ADT' => 38502,
+ 'Adyen' => 37611,
+ 'Agar.io' => 38216,
+ 'Aio Wireless' => 34098,
+ 'Air Canada' => 20003,
+ 'Airbnb' => 34335,
+ 'AireSpring' => 35893,
+ 'Airnow.gov' => 37658,
+ 'Akamai' => 35447,
+ 'Alaska Airlines' => 20004,
+ 'Alaska Communications' => 36040,
+ 'Albion Online' => 38795,
+ 'Alexa' => 35910,
+ 'AliExpress' => 37395,
+ 'Allegiant Air' => 20005,
+ 'Ally' => 34791,
+ 'Amazon' => 20006,
+ 'Amazon Prime Music' => 34710,
+ 'Amazon Prime Video' => 33370,
+ 'Amazon Web Services' => 10147,
+ 'American Airlines' => 20007,
+ 'American Express' => 35958,
+ 'American Messaging' => 34128,
+ 'Amino Apps' => 37290,
+ 'Ammyy' => 36575,
+ 'Amtrak' => 20008,
+ 'Ancestry' => 38296,
+ 'Anno 1800' => 38413,
+ 'Anthem' => 37746,
+ 'Antietam' => 37051,
+ 'Anydesk' => 37406,
+ 'AOL' => 33419,
+ 'Apex Legends' => 38111,
+ 'App Store' => 35575,
+ 'Apple Maps' => 36125,
+ 'Apple Music' => 35241,
+ 'Apple News' => 36126,
+ 'Apple Store' => 34447,
+ 'Apple TV' => 36058,
+ 'Apple TV+' => 38318,
+ 'Appriver' => 37074,
+ 'AppValley' => 37749,
+ 'Arbuckle' => 34489,
+ 'ArcheAge' => 34876,
+ 'ARK: Survival Evolved' => 36350,
+ 'Arlo' => 36779,
+ 'Armstrong' => 34027,
+ 'Arvest Bank' => 37148,
+ 'Arvig' => 34919,
+ 'Asana' => 35828,
+ 'Assassin\'s Creed' => 35005,
+ 'AT&T' => 20010,
+ 'Atlantic Broadband' => 33437,
+ 'Authorize.net' => 34821,
+ 'Autotask' => 37579,
+ 'BabyTEL' => 35631,
+ 'Backblaze' => 35483,
+ 'Badoo' => 35993,
+ 'Bandwidth' => 35667,
+ 'Bank of America' => 20011,
+ 'Barclays' => 37639,
+ 'BART' => 20012,
+ 'Batman The Telltale Series' => 35896,
+ 'Battalion 1944' => 37002,
+ 'Battleborn' => 35718,
+ 'Battlefield' => 36091,
+ 'Battlerite' => 36820,
+ 'BB&T' => 35658,
+ 'Beats Music' => 34294,
+ 'beIN' => 37355,
+ 'Bejeweled' => 35904,
+ 'Bendbroad' => 34884,
+ 'Best Buy' => 34329,
+ 'BET' => 34125,
+ 'Bethesda' => 38316,
+ 'Binance' => 36818,
+ 'Bing' => 20014,
+ 'Birch Communications' => 33549,
+ 'Bitbucket' => 35414,
+ 'Bitfinex' => 36787,
+ 'Bitflyer' => 36789,
+ 'Bitstamp' => 34023,
+ 'Bittrex' => 36788,
+ 'Black Desert Online' => 37108,
+ 'BlackBerry' => 20015,
+ 'Blade and Soul' => 35511,
+ 'Blizzard Battle.net' => 34486,
+ 'Blogger' => 20016,
+ 'Blue Jay Wireless' => 35464,
+ 'Bluebird' => 37362,
+ 'Bluehost' => 33418,
+ 'BlueJeans' => 37509,
+ 'BMW ConnectedDrive' => 38471,
+ 'BNY Mellon' => 35674,
+ 'Boingo' => 37377,
+ 'Booking.com' => 34330,
+ 'Boom Beach' => 34364,
+ 'Box' => 32775,
+ 'Brawl Stars' => 38312,
+ 'BT' => 38338,
+ 'Buckeye Cablesystem' => 34029,
+ 'Buffer' => 36013,
+ 'Bullet Force' => 37109,
+ 'Bumble' => 35823,
+ 'C Spire' => 38219,
+ 'Cable One' => 20019,
+ 'Call of Duty' => 34148,
+ 'CallTower' => 35365,
+ 'Caltrain' => 20021,
+ 'Candy Crush' => 34024,
+ 'Candy Crush Soda Saga' => 35171,
+ 'Canva' => 36778,
+ 'Capital One' => 20022,
+ 'Cash App' => 38685,
+ 'CBSSports' => 35826,
+ 'Century National Bank' => 37690,
+ 'CenturyLink' => 20023,
+ 'Change.org' => 35616,
+ 'Charles Schwab' => 20078,
+ 'Chase' => 20025,
+ 'Chegg' => 36054,
+ 'Chicago Transit Authority' => 20026,
+ 'Chime' => 39050,
+ 'Chrome Web Store' => 34116,
+ 'Cincinnati Bell' => 33422,
+ 'Cirra Networks' => 36357,
+ 'Citi' => 20027,
+ 'Citizens Bank' => 37748,
+ 'Civilization' => 35869,
+ 'Clash of Clans' => 33550,
+ 'Clash Royale' => 35587,
+ 'Classlink' => 36821,
+ 'Clear' => 33423,
+ 'Cloudflare' => 32542,
+ 'Cloudsmith' => 35946,
+ 'Cloudtrax' => 37528,
+ 'CNET' => 33341,
+ 'CNN' => 34141,
+ 'Cogent' => 34026,
+ 'Coinbase' => 36175,
+ 'Comcast' => 20029,
+ 'Comedy Central' => 34126,
+ 'Common App' => 36726,
+ 'Comporium' => 36032,
+ 'Conan Exiles' => 37195,
+ 'Concur' => 36730,
+ 'Confluence' => 37540,
+ 'ConnectWise' => 37493,
+ 'Consolidated' => 34030,
+ 'Consolidated Edison' => 35679,
+ 'Consumer Cellular' => 37761,
+ 'Copy.com' => 34231,
+ 'Coredial' => 35454,
+ 'Costco' => 37061,
+ 'Counter-strike' => 34886,
+ 'Cox' => 20030,
+ 'Crackle' => 35349,
+ 'Craigslist' => 20031,
+ 'Credit One Bank' => 37760,
+ 'Cricket Wireless' => 20032,
+ 'Crown Castle' => 35812,
+ 'Crunchyroll' => 35114,
+ 'Cryptopia' => 36973,
+ 'Dailymotion' => 37598,
+ 'Dark Souls.' => 35710,
+ 'Dauntless' => 37204,
+ 'DayZ' => 37751,
+ 'Dazn' => 37497,
+ 'DC Universe Online' => 37059,
+ 'Dead By Daylight' => 35760,
+ 'Deezer' => 36424,
+ 'Defiance 2050' => 37364,
+ 'Delta Air Lines' => 20033,
+ 'Destiny' => 34761,
+ 'DeviantArt' => 34888,
+ 'Di.fm' => 35645,
+ 'Diablo' => 34385,
+ 'DirecTV' => 20034,
+ 'Directv Now' => 35933,
+ 'Discord' => 35795,
+ 'Dish Network' => 20035,
+ 'Disney World' => 35837,
+ 'Disney+' => 38800,
+ 'Disqus' => 34145,
+ 'Diversity Lottery' => 38472,
+ 'Dlive' => 38470,
+ 'Docker' => 37817,
+ 'Doom' => 35717,
+ 'Doordash' => 37581,
+ 'Dota 2' => 34304,
+ 'Doublelist' => 37580,
+ 'Downforeveryoneorjustme' => 34758,
+ 'Draftkings' => 37036,
+ 'Dragon Ball' => 35858,
+ 'Dramafever' => 36064,
+ 'DreamHost' => 35926,
+ 'Driveclub' => 35891,
+ 'Dropbox' => 20036,
+ 'Duckduckgo' => 35650,
+ 'Duke Energy' => 35871,
+ 'Duolingo' => 35942,
+ 'Dyl' => 37383,
+ 'Dyn' => 35707,
+ 'E-Trade' => 20037,
+ 'EA' => 34499,
+ 'EA Sports UFC' => 35715,
+ 'Eagle Communications ' => 36027,
+ 'Earthlink' => 32776,
+ 'Eatel' => 35448,
+ 'eBay' => 20038,
+ 'Ecobee' => 37163,
+ 'eFax' => 38664,
+ 'Electric Power Board' => 35778,
+ 'Elite: Dangerous' => 36478,
+ 'Ello' => 34906,
+ 'Endicia' => 36839,
+ 'Engine Yard' => 33966,
+ 'eNom' => 35901,
+ 'EPB Chattanooga' => 35513,
+ 'Epic Games Store' => 37756,
+ 'Escape from Tarkov' => 37424,
+ 'ESPN' => 33335,
+ 'ESPN Plus' => 38320,
+ 'Etherdelta' => 36858,
+ 'Etsy' => 33366,
+ 'Eve Online' => 35721,
+ 'Eve Valkyrie' => 35884,
+ 'Evernote' => 34706,
+ 'Everquest' => 38337,
+ 'Exchange Online' => 34534,
+ 'Exede' => 33557,
+ 'Expedia' => 34332,
+ 'Experian' => 38155,
+ 'ExpressVPN' => 37544,
+ 'Faceapp' => 38695,
+ 'Facebook' => 10198,
+ 'Facebook Messenger' => 20039,
+ 'Faceit' => 38543,
+ 'Facetime' => 34606,
+ 'FAFSA' => 37691,
+ 'FairPoint' => 33994,
+ 'Fallout' => 35430,
+ 'Family Search' => 33439,
+ 'Fandango' => 34481,
+ 'Fandom' => 34317,
+ 'Fanduel' => 37035,
+ 'Fanfiction' => 37101,
+ 'Far Cry' => 35713,
+ 'FastMail' => 35779,
+ 'Fatcow' => 34882,
+ 'Fax2mail' => 38665,
+ 'FedEx' => 35843,
+ 'Feedly' => 34704,
+ 'Fidelity' => 36738,
+ 'Fifa' => 35470,
+ 'Final Fantasy' => 35860,
+ 'Find my iPhone' => 38662,
+ 'First Communications ' => 36062,
+ 'FirstEnergy' => 35994,
+ 'FirstLight' => 34879,
+ 'Fitbit' => 35800,
+ 'Fite' => 38794,
+ 'Fiverr' => 37054,
+ 'Flickr' => 20040,
+ 'Fling' => 37353,
+ 'Flipboard' => 33491,
+ 'Florida Power & Light' => 35699,
+ 'Fonality' => 34312,
+ 'For Honor' => 35984,
+ 'Forge Of Empires' => 35886,
+ 'Fortnite' => 36375,
+ 'Forza' => 36817,
+ 'Fox News' => 35880,
+ 'Fox Sports Go' => 37164,
+ 'Frankfort PlantBoard' => 35466,
+ 'Freedompop' => 35657,
+ 'Freepik' => 36739,
+ 'Friday the 13th The Game' => 36348,
+ 'Frii' => 35668,
+ 'Frontier' => 20041,
+ 'Frontier Airlines' => 20042,
+ 'FuboTV' => 36658,
+ 'Funimation' => 36665,
+ 'Fuze' => 35847,
+ 'FXNOW' => 37504,
+ 'G-Portal' => 37154,
+ 'G2A.com' => 35898,
+ 'Game of Thrones Conquest' => 37669,
+ 'Game of war' => 35228,
+ 'Gamebattles' => 37753,
+ 'Gameloft' => 35964,
+ 'Gamestop' => 36780,
+ 'Gang Beasts' => 36926,
+ 'Garmin' => 35554,
+ 'Garmin Connect' => 35801,
+ 'Gatehub' => 36841,
+ 'GCI' => 35633,
+ 'GDAX' => 36791,
+ 'Gears of War' => 35848,
+ 'Geeking' => 37251,
+ 'Gemini' => 36811,
+ 'Gems of war' => 38801,
+ 'Geocaching' => 35770,
+ 'Gfycat' => 38504,
+ 'Ghost' => 38720,
+ 'Ghost Recon' => 36000,
+ 'Ghosttunes' => 34883,
+ 'GitHub' => 10004,
+ 'Gitlab' => 37146,
+ 'Glassdoor' => 35740,
+ 'Glide' => 34016,
+ 'Glitch' => 38721,
+ 'Gmail' => 20043,
+ 'Go Daddy' => 33416,
+ 'GOG.com' => 34287,
+ 'Google' => 10200,
+ 'Google Calendar' => 38597,
+ 'Google Cloud' => 37380,
+ 'Google Drive' => 34271,
+ 'Google Fiber' => 33421,
+ 'Google Hangouts' => 20046,
+ 'Google Home' => 36137,
+ 'Google Play' => 20045,
+ 'GoToMeeting' => 33489,
+ 'Gran Turismo' => 36347,
+ 'Grande' => 34404,
+ 'Greenlight' => 37628,
+ 'Grindr' => 35529,
+ 'Groove Music' => 33972,
+ 'Groupme' => 35739,
+ 'Groupon' => 35709,
+ 'Growtopia' => 36835,
+ 'Grubhub' => 37693,
+ 'GTA 5' => 33995,
+ 'Guild Wars 2' => 35060,
+ 'H&R Block' => 34328,
+ 'Halo' => 35418,
+ 'Halo Wars' => 36035,
+ 'Happn' => 37013,
+ 'Hargray' => 34142,
+ 'Harry Potter: Wizards Unite' => 38593,
+ 'Hashflare' => 37038,
+ 'Hawaiian Airlines' => 20047,
+ 'Hawaiian Telcom' => 33436,
+ 'Hay Day' => 34367,
+ 'Hayu' => 37198,
+ 'HBO Go' => 34340,
+ 'HBO Now' => 38697,
+ 'Healthcare.gov' => 34011,
+ 'Hearthstone' => 36834,
+ 'Heartland' => 38803,
+ 'Heroku' => 33965,
+ 'HiDive' => 37527,
+ 'HInge' => 37625,
+ 'Hipchat' => 34865,
+ 'HitBTC' => 36925,
+ 'Hitman' => 35585,
+ 'Hive' => 37571,
+ 'Honeywell' => 38110,
+ 'Hootsuite' => 20048,
+ 'Hostgator' => 33417,
+ 'Hostmonster' => 35931,
+ 'Hotels.com' => 34334,
+ 'Hotwire' => 34331,
+ 'HouseParty' => 37356,
+ 'HQ Trivia' => 36815,
+ 'HSBC' => 35673,
+ 'Hue' => 37514,
+ 'HughesNet' => 32787,
+ 'Hulu' => 33331,
+ 'Humanity' => 36735,
+ 'Hunt: showdown' => 37006,
+ 'Hurricane Electric' => 35811,
+ 'I3 Broadband' => 36286,
+ 'IBM Cloud' => 37649,
+ 'iCloud' => 20049,
+ 'ICQ' => 34766,
+ 'iFunny' => 38294,
+ 'IGTV' => 37317,
+ 'iHeartRadio' => 33369,
+ 'Illinois Century Network' => 36086,
+ 'IMDb Freedrive' => 37960,
+ 'iMessage' => 20050,
+ 'Imgur' => 20051,
+ 'IMVU' => 36523,
+ 'Inbox' => 34318,
+ 'inContact' => 35836,
+ 'Indeed' => 35864,
+ 'Ingress' => 35764,
+ 'Injustice 2' => 36526,
+ 'Inmotion' => 34759,
+ 'Instagram' => 20052,
+ 'Integra' => 33554,
+ 'Interactive Brokers' => 34115,
+ 'Intermedia' => 33973,
+ 'Internet Movie Database (IMDb)' => 33337,
+ 'Intralinks' => 38667,
+ 'Ipage' => 35659,
+ 'iRacing' => 35957,
+ 'Iridium' => 35652,
+ 'Ironsight' => 37142,
+ 'IRS' => 35542,
+ 'iTunes' => 20053,
+ 'iTunes Connect' => 36489,
+ 'iTunes Match' => 36059,
+ 'iWork' => 36124,
+ 'Jabber' => 36524,
+ 'Jackbox' => 38790,
+ 'JetBlue Airways' => 20054,
+ 'Jira' => 34870,
+ 'Jive' => 35453,
+ 'JP Morgan' => 37034,
+ 'Juno' => 32786,
+ 'Jurassic World Alive' => 37256,
+ 'Kayak' => 34338,
+ 'Keek' => 33770,
+ 'Kik' => 32770,
+ 'Kraken' => 36660,
+ 'Kucoin' => 36962,
+ 'last.fm' => 35634,
+ 'LastPass' => 35541,
+ 'LawBreakers' => 36527,
+ 'Layer3 TV' => 37378,
+ 'Leaco' => 35665,
+ 'League of Legends' => 34111,
+ 'Lifesize' => 37003,
+ 'Lightpath' => 35685,
+ 'Limebike' => 37205,
+ 'Limelight Networks' => 35462,
+ 'Line' => 33738,
+ 'LinkedIn' => 20055,
+ 'Linode' => 35907,
+ 'Liquid Web' => 34825,
+ 'LiveLeak' => 38273,
+ 'Liveperson' => 35417,
+ 'Logix' => 38295,
+ 'Logmein' => 33488,
+ 'Lola Wireless' => 35905,
+ 'Lowe\'s' => 38297,
+ 'LS Networks' => 35703,
+ 'Lumos Networks' => 34217,
+ 'Lyft' => 36571,
+ 'Lynda' => 35781,
+ 'Madden' => 37747,
+ 'Maguss' => 37047,
+ 'Mail.com' => 34798,
+ 'Mailbox' => 34831,
+ 'Mailchimp' => 36017,
+ 'MARTA' => 20056,
+ 'Marvel' => 38792,
+ 'Marvel Contest of Champions' => 37005,
+ 'Maryland Transit Administration (MTA)' => 20017,
+ 'Mass Effect ' => 36057,
+ 'Mastercard' => 37249,
+ 'MaxxSouth ' => 36095,
+ 'MBTA' => 20057,
+ 'McDonalds app' => 36369,
+ 'Media Temple' => 35950,
+ 'Mediacom' => 20058,
+ 'Meetme' => 37042,
+ 'Meetup' => 34245,
+ 'Megapath' => 33420,
+ 'Merrill Lynch' => 37032,
+ 'Metra' => 20059,
+ 'Metro PCS' => 20060,
+ 'MetroCast' => 34405,
+ 'Metrolink' => 20061,
+ 'Metronetinc' => 35890,
+ 'MHz Choice' => 37297,
+ 'Miami Dade Transit' => 20062,
+ 'Microsoft Azure' => 32547,
+ 'Microsoft Teams' => 38018,
+ 'Microsoft VLSC' => 38187,
+ 'Midcontinent Media' => 33552,
+ 'Mimecast' => 35783,
+ 'Minecraft' => 20063,
+ 'Mint' => 36589,
+ 'MintSim' => 36438,
+ 'Mitel' => 36005,
+ 'Mixer' => 36525,
+ 'MLB The Show' => 35711,
+ 'MLB TV' => 35712,
+ 'Mobile legends' => 37394,
+ 'Mobile Strike' => 36476,
+ 'Momentum Telecom' => 36733,
+ 'Moneylion' => 38303,
+ 'Monster Hunter' => 37007,
+ 'Mordhau' => 38497,
+ 'Moviepass' => 37004,
+ 'Movies Anywhere' => 38771,
+ 'MovieTickets' => 36178,
+ 'MSL Live' => 37107,
+ 'MTV' => 34133,
+ 'MU Legend' => 36792,
+ 'Music Unlimited' => 35909,
+ 'Musical.ly' => 36659,
+ 'My Fitness Pal' => 35803,
+ 'My Social Security' => 37627,
+ 'Naruto-Storm' => 35845,
+ 'National Grid NY' => 36024,
+ 'NBA 2k' => 35714,
+ 'NBC Sports Live Extra' => 34295,
+ 'NBCNews.com' => 33340,
+ 'NCTC' => 36344,
+ 'Need for Speed' => 35428,
+ 'NEMR' => 35514,
+ 'Nest' => 35190,
+ 'Net10 Wireless' => 34090,
+ 'netBlazr' => 35656,
+ 'Neteller' => 38321,
+ 'Netflix' => 20065,
+ 'Netsuite' => 35743,
+ 'Nettalk' => 36732,
+ 'Network Solutions' => 33548,
+ 'NetZero' => 32785,
+ 'Neverwinter' => 35603,
+ 'New Jersey Transit' => 20066,
+ 'New York MTA' => 20067,
+ 'New York Times' => 33339,
+ 'Newegg' => 35438,
+ 'NewWave Communications' => 36026,
+ 'Nextiva' => 34850,
+ 'NFL Network' => 36572,
+ 'NHL.tv' => 36816,
+ 'Nicehash' => 37037,
+ 'NikePlus' => 35805,
+ 'Nintendo eShop' => 34099,
+ 'Nintendo Network' => 35515,
+ 'Nintendo Switch Online' => 37240,
+ 'No Man\'s Sky' => 35793,
+ 'NordVPN' => 37545,
+ 'North State' => 36840,
+ 'Northland Communications' => 38035,
+ 'Norwood Light' => 35738,
+ 'NTT Communications' => 35856,
+ 'NuGet' => 36740,
+ 'Oculus' => 38253,
+ 'Office 365' => 34532,
+ 'OkCupid' => 35902,
+ 'Okta' => 35742,
+ 'Omegle' => 36522,
+ 'OneDrive' => 32780,
+ 'Onelogin' => 38439,
+ 'Ooma' => 35628,
+ 'ooVoo' => 34085,
+ 'Opera' => 38406,
+ 'Optimum / Cablevision' => 20020,
+ 'OptionsHouse' => 20068,
+ 'optionsXpress' => 20069,
+ 'Oracle Cloud' => 38033,
+ 'Orbitz' => 34337,
+ 'Origin' => 34113,
+ 'OS X Update' => 36127,
+ 'Outlook' => 10205,
+ 'Overdrive' => 35855,
+ 'Overwatch' => 35680,
+ 'Pacific Northern Gas ' => 35807,
+ 'Page Plus' => 34012,
+ 'Pagely' => 33974,
+ 'Paladins' => 35857,
+ 'Pandora' => 20070,
+ 'Paperspace' => 37200,
+ 'Paragon' => 36359,
+ 'PATCO' => 20071,
+ 'PATH' => 20072,
+ 'Path of Exile' => 34823,
+ 'Patreon' => 38503,
+ 'Paypal' => 20073,
+ 'pCloud' => 36588,
+ 'PenTeleData' => 34401,
+ 'Periscope' => 35595,
+ 'Personal Capital' => 36969,
+ 'PES' => 37951,
+ 'PG&E' => 35677,
+ 'PGA Tour Live' => 37235,
+ 'Phonepower' => 35361,
+ 'Photobucket' => 20074,
+ 'Pinterest' => 20075,
+ 'Planetside2' => 35787,
+ 'Player Unknown\'s Battlegrounds' => 36135,
+ 'Playstation Network' => 20076,
+ 'Playstation Vue' => 35188,
+ 'PlentyOfFish' => 35799,
+ 'Plex' => 35573,
+ 'PNC' => 33333,
+ 'Pokémon Duel' => 35966,
+ 'Pokémon Go' => 35725,
+ 'Pokerstars' => 38749,
+ 'Poloniex' => 36349,
+ 'Postmates' => 36774,
+ 'Powerschool' => 35867,
+ 'Prey' => 37295,
+ 'Priceline' => 34333,
+ 'Project Online' => 34535,
+ 'Proofpoint' => 38666,
+ 'ProtonMail' => 36136,
+ 'Quake Champions' => 36773,
+ 'Quickbooks Online' => 35761,
+ 'Quicken' => 36614,
+ 'Quizlet' => 34322,
+ 'Quizup' => 34296,
+ 'Quora' => 34341,
+ 'Rabb.it' => 36819,
+ 'Rackspace' => 34609,
+ 'Rain World Game' => 36042,
+ 'Rainbow Six' => 35463,
+ 'Razer' => 38698,
+ 'RCN' => 32784,
+ 'Realm Royale' => 37287,
+ 'Red Dead Redemption' => 37600,
+ 'Redbox' => 37213,
+ 'Reddit' => 33342,
+ 'Reflexion' => 38733,
+ 'RingCentral' => 34311,
+ 'Rise Broadband' => 35458,
+ 'Robinhood' => 35706,
+ 'Roblox' => 34820,
+ 'Rocket League' => 35251,
+ 'RocketRez' => 35784,
+ 'Roku' => 37194,
+ 'Royal Games' => 38218,
+ 'Runescape' => 34804,
+ 'Runkeeper' => 35802,
+ 'Runtastic' => 35804,
+ 'Rust' => 37755,
+ 'Ruzzle' => 33969,
+ 'Safelink Wireless' => 35467,
+ 'Safenet' => 37538,
+ 'Salesforce' => 32773,
+ 'Santander Bank' => 38656,
+ 'Sarahah' => 36365,
+ 'Schooldesk' => 35468,
+ 'Scottrade' => 20079,
+ 'ScreenConnect' => 35661,
+ 'Scum' => 37464,
+ 'Sea of Thieves' => 36995,
+ 'SEC Network' => 37757,
+ 'Secom' => 36972,
+ 'Second Life' => 37612,
+ 'SEPTA' => 20080,
+ 'Service Electric' => 33553,
+ 'Sharebuilder' => 20081,
+ 'Sharefile' => 37513,
+ 'Sharepoint Online' => 34533,
+ 'Shentel' => 35666,
+ 'Shopify' => 37555,
+ 'Shoretel' => 35846,
+ 'Showtime Anytime' => 35591,
+ 'Shutterstock' => 35704,
+ 'Signal' => 37071,
+ 'SignupGenius' => 35636,
+ 'Simple' => 34790,
+ 'Simple Mobile' => 33979,
+ 'Siri' => 38339,
+ 'SiriusXM' => 37048,
+ 'Skillshare' => 35775,
+ 'Skrill' => 37257,
+ 'Skype' => 20082,
+ 'Skype for Business' => 35350,
+ 'Skyscanner' => 38645,
+ 'SkySwitch' => 36664,
+ 'SkyWest' => 20084,
+ 'Slack' => 35437,
+ 'Slashdot' => 36615,
+ 'Sleeper' => 37455,
+ 'Slideshare' => 20085,
+ 'Sling' => 35155,
+ 'Smart Hub' => 35178,
+ 'Smartsheet' => 38310,
+ 'SmartThings' => 37102,
+ 'Smite' => 34802,
+ 'SNAP EBT' => 34006,
+ 'Snapchat' => 20086,
+ 'Socket' => 37411,
+ 'Sonic.net' => 34403,
+ 'Soundcloud' => 34351,
+ 'Sourceforge' => 20087,
+ 'South Central Communications' => 35759,
+ 'Southern California Edison' => 35700,
+ 'Southwest Airlines' => 20088,
+ 'Spectrum' => 20024,
+ 'Speedtest' => 37354,
+ 'Spirit Communications' => 35808,
+ 'SplashID' => 35594,
+ 'Spotify' => 20090,
+ 'Sprint' => 20091,
+ 'Square' => 37053,
+ 'Squarespace' => 36573,
+ 'Stack Exchange' => 34319,
+ 'Stackoverflow' => 34258,
+ 'Stadia' => 38302,
+ 'Staminus' => 34608,
+ 'Stamps.com' => 36662,
+ 'Star Citizen' => 36477,
+ 'Star Wars Battlefront' => 35459,
+ 'Starbucks' => 37210,
+ 'Starz' => 38291,
+ 'Steam' => 20092,
+ 'Steep' => 36011,
+ 'Straight Talk' => 34097,
+ 'Strava' => 36047,
+ 'Streamlabs' => 37206,
+ 'Strife' => 35853,
+ 'Stripe' => 38668,
+ 'Suddenlink' => 20093,
+ 'Suitebox' => 37211,
+ 'Summit Broadband' => 35617,
+ 'Summoners War' => 37409,
+ 'SunTrust Bank' => 37346,
+ 'Surfline' => 36341,
+ 'SurveyMonkey' => 35963,
+ 'T-Mobile' => 20097,
+ 'T. Rowe Price' => 37033,
+ 'Talkray' => 34750,
+ 'Tango' => 33728,
+ 'Target' => 34320,
+ 'Taxslayer' => 36018,
+ 'TD Ameritrade' => 20094,
+ 'TD Bank' => 35672,
+ 'TDS Telecom' => 33558,
+ 'Teamviewer' => 34343,
+ 'Tekken' => 37196,
+ 'Telecharge' => 37220,
+ 'Telegram' => 34227,
+ 'Tennis TV' => 37040,
+ 'TERA' => 37105,
+ 'Tesla' => 37152,
+ 'The Crew 2' => 35809,
+ 'The Culling' => 37752,
+ 'The Division' => 35558,
+ 'The elder scrolls online' => 35535,
+ 'The Huffington Post' => 33334,
+ 'The Simpsons Tapped out' => 34702,
+ 'The Weather Channel' => 33336,
+ 'Thingiverse' => 38732,
+ 'Threads' => 38983,
+ 'Threema' => 35892,
+ 'TIAA ' => 35597,
+ 'Ticketmaster' => 36534,
+ 'Tidal' => 37512,
+ 'Tiktok' => 38367,
+ 'Tinder' => 34237,
+ 'Titanfall' => 35897,
+ 'Tivo' => 37001,
+ 'Todoist' => 36461,
+ 'Toggl' => 36993,
+ 'TPx Communications' => 34402,
+ 'TracFone Wireless' => 20099,
+ 'Tradeking' => 33768,
+ 'TradeSatoshi' => 37106,
+ 'TradeStation' => 20100,
+ 'Transferwise' => 37199,
+ 'Travelocity' => 34336,
+ 'Trello' => 34796,
+ 'Trove' => 35363,
+ 'Trusted Id' => 36636,
+ 'TSYS' => 36517,
+ 'Tumblr' => 20101,
+ 'TuneIn' => 35221,
+ 'TurboTax' => 34327,
+ 'TV Time' => 37197,
+ 'TW Telecom' => 32783,
+ 'Tweakbox' => 37750,
+ 'Tweetdeck' => 39009,
+ 'Twitch' => 34308,
+ 'Twitter' => 10204,
+ 'Uber' => 36570,
+ 'Uber Eats' => 37648,
+ 'Udacity' => 35842,
+ 'Udemy' => 35644,
+ 'UFC' => 36736,
+ 'UMG Gaming' => 36528,
+ 'Uncharted' => 35716,
+ 'United Airlines' => 20104,
+ 'Untappd' => 37363,
+ 'Uplay PC' => 34381,
+ 'UPS' => 34321,
+ 'Upwork' => 35436,
+ 'US Bank' => 20106,
+ 'US Cellular' => 20103,
+ 'USA Mobility' => 34129,
+ 'USPS' => 34259,
+ 'Utah Broadband' => 36342,
+ 'Vainglory' => 37203,
+ 'Vanguard' => 37011,
+ 'Vectren' => 35872,
+ 'Venmo' => 37209,
+ 'Verizon' => 20107,
+ 'Vero' => 37062,
+ 'Viaero' => 37508,
+ 'Viasat' => 38298,
+ 'Viber' => 32769,
+ 'Viewster' => 34323,
+ 'Viki' => 36065,
+ 'Vimeo' => 20110,
+ 'Vine' => 33368,
+ 'Virgin Mobile' => 34724,
+ 'Visa' => 37239,
+ 'Visible' => 38605,
+ 'Visual Studio Team Services' => 37463,
+ 'Vlive' => 36574,
+ 'Vonage' => 34315,
+ 'Voxer' => 33492,
+ 'VRChat' => 36991,
+ 'VRV' => 36640,
+ 'Vudu' => 34109,
+ 'Vyve Broadband' => 36012,
+ 'W3Schools' => 35887,
+ 'Waiter.com' => 36776,
+ 'Walmart Family' => 35465,
+ 'Walmart.com' => 33338,
+ 'War Thunder' => 37318,
+ 'Warface' => 37510,
+ 'Warframe' => 35825,
+ 'Washington Metropolitan Area Transit Authority' => 20111,
+ 'Wattpad' => 34693,
+ 'Wave Broadband' => 34028,
+ 'Waze' => 33382,
+ 'Webassign' => 35873,
+ 'Webex' => 33490,
+ 'Webhosting.net' => 35435,
+ 'Webs' => 35895,
+ 'Weebly' => 34910,
+ 'Weight Watchers' => 35646,
+ 'Wells Fargo' => 20112,
+ 'Wemo' => 37593,
+ 'Westman' => 35359,
+ 'WeTransfer' => 20113,
+ 'Whatsapp' => 10136,
+ 'Whisper' => 36881,
+ 'Wikipedia' => 20115,
+ 'Wilcoinc' => 35696,
+ 'Wildstar' => 36475,
+ 'William Hill' => 38900,
+ 'Windstream' => 20117,
+ 'Wish' => 38154,
+ 'Wix' => 35596,
+ 'Wordpress.com' => 20118,
+ 'Workday' => 37314,
+ 'World of Tanks' => 35357,
+ 'World of Warcraft' => 34263,
+ 'World of Warships' => 36637,
+ 'World War 3' => 37599,
+ 'World War Z' => 38405,
+ 'WOW' => 20114,
+ 'WP Engine' => 33975,
+ 'Wunderground' => 35948,
+ 'Wunderlist' => 35839,
+ 'WWE Network' => 36056,
+ 'Xbox Live' => 20119,
+ 'Xfinity Flex' => 38412,
+ 'XO' => 32777,
+ 'Yahoo' => 10203,
+ 'Yahoo Mail' => 20120,
+ 'Yahoo Messenger' => 20121,
+ 'Yammer' => 35559,
+ 'Yelp' => 20122,
+ 'Youtube' => 34651,
+ 'Youtube Music' => 37461,
+ 'Youtube TV' => 37296,
+ 'Yubo' => 37720,
+ 'Z1 Battle Royale' => 35146,
+ 'Zayo' => 35866,
+ 'Zelle' => 38683,
+ 'Zendesk' => 35854,
+ 'Zillow' => 33367,
+ 'ZoHo' => 34144,
+ 'Zoom' => 37349,
+ 'Zwift' => 38167,
+ 'Zynga' => 20123,
+ ),
+ 'New Zealand' => array(
+ '2degrees' => 33886,
+ '4chan' => 38270,
+ 'Alexa' => 36920,
+ 'Amurit.net' => 37710,
+ 'Anthem' => 38210,
+ 'ANZ' => 33888,
+ 'Apex Legends' => 38134,
+ 'Apple Store' => 34457,
+ 'ASB' => 33905,
+ 'beIN' => 38493,
+ 'Bigpipe' => 37704,
+ 'Bing' => 33889,
+ 'Blizzard Battle.net' => 38465,
+ 'BNZ' => 33907,
+ 'Call of Duty' => 34150,
+ 'CallPlus' => 33885,
+ 'CCL' => 37705,
+ 'Counter-strike' => 36854,
+ 'Crunchyroll' => 38146,
+ 'Cryptopia' => 36963,
+ 'Dead By Daylight' => 37434,
+ 'Destiny' => 38942,
+ 'Discord' => 38609,
+ 'Dota 2' => 36415,
+ 'EA' => 34505,
+ 'eBay' => 33891,
+ 'Evernote' => 35914,
+ 'Facebook' => 33892,
+ 'Facebook Messenger' => 33893,
+ 'Facetime' => 34587,
+ 'Fifa' => 38400,
+ 'Flip' => 37706,
+ 'Fortnite' => 36655,
+ 'Gmail' => 33894,
+ 'Google' => 33895,
+ 'Google Drive' => 36759,
+ 'Google Hangouts' => 33896,
+ 'Google Play' => 38525,
+ 'Gran Turismo' => 36948,
+ 'Grindr' => 38402,
+ 'GTA 5' => 37277,
+ 'iCloud' => 33921,
+ 'iMessage' => 33922,
+ 'Inspire Net' => 35445,
+ 'Instagram' => 33915,
+ 'iTunes' => 33923,
+ 'Kik' => 33914,
+ 'Kiwibank' => 33906,
+ 'League of Legends' => 38692,
+ 'Lightbox' => 35504,
+ 'LinkedIn' => 33897,
+ 'Minecraft' => 38617,
+ 'My Fitness Pal' => 37635,
+ 'myob' => 38926,
+ 'MyRepublic' => 34931,
+ 'NBA 2k' => 38785,
+ 'Neon' => 37707,
+ 'Netflix' => 34857,
+ 'Nzdating' => 37708,
+ 'Office 365' => 37171,
+ 'OneDrive' => 36863,
+ 'Orcon' => 33883,
+ 'Origin' => 36887,
+ 'Outlook' => 33918,
+ 'Overwatch' => 36416,
+ 'Pandora' => 33919,
+ 'Pinterest' => 33898,
+ 'Player Unknown\'s Battlegrounds' => 36362,
+ 'Playstation Network' => 34858,
+ 'Pokémon Go' => 35733,
+ 'Rabodirect' => 34980,
+ 'Rainbow Six' => 36610,
+ 'Reddit' => 34860,
+ 'Roblox' => 37662,
+ 'Skinny' => 37709,
+ 'Sky TV' => 33887,
+ 'Skype' => 33912,
+ 'Slingshot' => 34737,
+ 'Snapchat' => 33908,
+ 'Spark' => 33884,
+ 'Spotify' => 33910,
+ 'Steam' => 34225,
+ 'Tinder' => 34859,
+ 'Trade Me' => 33916,
+ 'Trustpower' => 37703,
+ 'Twitch' => 36540,
+ 'Twitter' => 33899,
+ 'Viber' => 33909,
+ 'Vodafone' => 33882,
+ 'Voxer' => 33913,
+ 'Wattpad' => 34697,
+ 'WeChat' => 33920,
+ 'Westpac' => 33900,
+ 'Whatsapp' => 33917,
+ 'Wikipedia' => 33901,
+ 'World of Warcraft' => 38814,
+ 'Xbox Live' => 34284,
+ 'Xero' => 35929,
+ 'Yahoo' => 33902,
+ 'Yahoo Mail' => 33903,
+ 'Youtube' => 33904,
+ 'Z1 Battle Royale' => 37266,
+ ),
+ 'België' => array(
+ '2dehands.be' => 34257,
+ 'Apex Legends' => 38141,
+ 'App Store' => 38555,
+ 'Apple Store' => 38925,
+ 'Argenta' => 32491,
+ 'Bancontact Mister Cash' => 33976,
+ 'Base' => 10207,
+ 'Battlefield' => 38444,
+ 'Belfius' => 32545,
+ 'Binance' => 36934,
+ 'Binck' => 32388,
+ 'Blizzard Battle.net' => 38810,
+ 'BNP Paribas Fortis' => 32543,
+ 'Bolero' => 37619,
+ 'Bpost' => 37157,
+ 'Bwin' => 34872,
+ 'Call of Duty' => 35536,
+ 'Clash Royale' => 38948,
+ 'Counter-strike' => 38245,
+ 'Crelan' => 37614,
+ 'Dead By Daylight' => 37445,
+ 'Deezer' => 33828,
+ 'Destiny' => 38559,
+ 'Deutsche Bank' => 34140,
+ 'Discord' => 37391,
+ 'Dommel' => 33977,
+ 'Dota 2' => 38478,
+ 'Dropbox' => 32525,
+ 'EA' => 34841,
+ 'eBay' => 33858,
+ 'EDPnet' => 32855,
+ 'Engie Electrabel' => 37616,
+ 'Epic Games Store' => 39027,
+ 'Facebook' => 22339,
+ 'Facebook Messenger' => 34851,
+ 'Facetime' => 34605,
+ 'Fifa' => 36906,
+ 'Fintro' => 37617,
+ 'Fluvius' => 37615,
+ 'Fortnite' => 36696,
+ 'Ghost Recon' => 39001,
+ 'Gmail' => 32374,
+ 'Google' => 32343,
+ 'Google Hangouts' => 32395,
+ 'Google Play' => 32371,
+ 'Gran Turismo' => 36913,
+ 'Grindr' => 38743,
+ 'GTA 5' => 35500,
+ 'Guild Wars 2' => 36474,
+ 'iCloud' => 22363,
+ 'iMessage' => 32463,
+ 'ING' => 22342,
+ 'Instagram' => 34286,
+ 'Isabel' => 34004,
+ 'Itsme' => 37454,
+ 'iTunes' => 32484,
+ 'Jim Mobile' => 37618,
+ 'KBC' => 32544,
+ 'Kik' => 32524,
+ 'Kraken' => 36828,
+ 'League of Legends' => 38631,
+ 'Lebara' => 32461,
+ 'LinkedIn' => 32534,
+ 'Lycamobile' => 32462,
+ 'Mobile Vikings' => 32854,
+ 'Moneyou' => 32488,
+ 'Multisafepay' => 34878,
+ 'Nest' => 38517,
+ 'Netflix' => 34629,
+ 'Numéricable' => 32856,
+ 'Office 365' => 36409,
+ 'OneDrive' => 35410,
+ 'Orange' => 10208,
+ 'Origin' => 36888,
+ 'Outlook' => 32375,
+ 'Overwatch' => 36383,
+ 'OVH' => 36746,
+ 'Paypal' => 32354,
+ 'Paysafecard' => 32485,
+ 'Play Sports' => 37582,
+ 'Player Unknown\'s Battlegrounds' => 36568,
+ 'Playstation Network' => 32339,
+ 'Pokémon Go' => 35736,
+ 'Proximus' => 10206,
+ 'Rainbow Six' => 36180,
+ 'Reddit' => 37474,
+ 'Roblox' => 38757,
+ 'Rocket League' => 35978,
+ 'Runescape' => 38654,
+ 'Salesforce' => 32349,
+ 'Scarlet' => 32435,
+ 'Simyo' => 32387,
+ 'Skype' => 32357,
+ 'Smartschool' => 37620,
+ 'Snapchat' => 32520,
+ 'Sofort Banking' => 32482,
+ 'Spotify' => 32336,
+ 'Steam' => 34278,
+ 'Strava' => 38161,
+ 'Teamviewer' => 34907,
+ 'Telegram' => 35971,
+ 'Telenet' => 10196,
+ 'Tinder' => 35499,
+ 'TuneIn' => 38759,
+ 'TV Vlaanderen' => 22364,
+ 'Tweakbox' => 38786,
+ 'Twitch' => 36542,
+ 'Twitter' => 22355,
+ 'Unibet' => 38830,
+ 'Uplay PC' => 35614,
+ 'Viber' => 32505,
+ 'Voo' => 32853,
+ 'VTM' => 38394,
+ 'Waze' => 36657,
+ 'WeTransfer' => 32523,
+ 'Whatsapp' => 32333,
+ 'Wikipedia' => 32425,
+ 'Wordfeud' => 22358,
+ 'Xbox Live' => 32481,
+ 'Yahoo Mail' => 34840,
+ 'Yellowbrick' => 37609,
+ 'Yeloplay' => 37359,
+ 'YouTube' => 32338,
+ 'Z1 Battle Royale' => 37258,
+ ),
+ 'Australia' => array(
+ '2k' => 36402,
+ '4chan' => 38290,
+ 'AAPT telecommunications' => 35849,
+ 'ABC' => 37712,
+ 'Activ8me' => 36814,
+ 'Adam Internet' => 33877,
+ 'Adelaide Bank' => 36729,
+ 'Adobe Creative Cloud' => 34553,
+ 'Airbnb' => 36403,
+ 'Alexa' => 36922,
+ 'Amaysim' => 35654,
+ 'Amazon' => 36404,
+ 'Amazon Prime Video' => 36968,
+ 'Amazon Web Services' => 34093,
+ 'American Express' => 38173,
+ 'Amino Apps' => 39045,
+ 'Amnet' => 33878,
+ 'Anthem' => 38176,
+ 'ANZ' => 33364,
+ 'Apex Legends' => 38125,
+ 'App Store' => 35577,
+ 'Apple Music' => 35243,
+ 'Apple Store' => 34455,
+ 'ARK: Survival Evolved' => 38746,
+ 'Assassin\'s Creed' => 35008,
+ 'AusBBS' => 36812,
+ 'Aussie Broadband' => 37219,
+ 'Australia Post' => 37052,
+ 'Bank Australia' => 38682,
+ 'Bank of Melbourne' => 35356,
+ 'Bank of Queensland' => 37458,
+ 'Bank SA' => 35354,
+ 'Bankwest' => 34972,
+ 'Battlefield' => 37468,
+ 'Belong' => 37179,
+ 'Bendigo Bank' => 37715,
+ 'BigPond' => 33993,
+ 'Binance' => 36878,
+ 'Bing' => 32874,
+ 'Blizzard Battle.net' => 35053,
+ 'Blogger' => 32875,
+ 'Boom Beach' => 36871,
+ 'Box' => 32876,
+ 'Brawl Stars' => 38816,
+ 'Bumble' => 36809,
+ 'Call of Duty' => 36405,
+ 'Candy Crush' => 35092,
+ 'Centrelink' => 37549,
+ 'Centurylink' => 34131,
+ 'Citibank' => 36507,
+ 'Clash of Clans' => 37302,
+ 'Clash Royale' => 38953,
+ 'Cloudflare' => 32877,
+ 'ClubTelco' => 34792,
+ 'Coinbase' => 36802,
+ 'Coinspot' => 36927,
+ 'Commander' => 37714,
+ 'Commonwealth Bank' => 33362,
+ 'Counter-strike' => 35059,
+ 'Crackle' => 35112,
+ 'Craigslist' => 32878,
+ 'Crunchyroll' => 36717,
+ 'Dead By Daylight' => 37415,
+ 'Deezer' => 33833,
+ 'Deliveroo' => 38197,
+ 'Destiny' => 34894,
+ 'DFP' => 35002,
+ 'Discord' => 36022,
+ 'Dodo' => 34003,
+ 'Doordash' => 39006,
+ 'Dota 2' => 35405,
+ 'Dropbox' => 32879,
+ 'EA' => 34509,
+ 'eBay' => 32880,
+ 'Eftel' => 35553,
+ 'Elite: Dangerous' => 37104,
+ 'Escape from Tarkov' => 38626,
+ 'ESPN' => 38387,
+ 'Etsy' => 37481,
+ 'Exchange Online' => 34545,
+ 'Exetel' => 34478,
+ 'Facebook' => 32881,
+ 'Facebook Messenger' => 32882,
+ 'Facetime' => 34601,
+ 'Fallout' => 35432,
+ 'Far Cry' => 37190,
+ 'Fifa' => 35473,
+ 'Fitbit' => 38358,
+ 'Flickr' => 32883,
+ 'Fonality' => 34313,
+ 'For Honor' => 35989,
+ 'Fortnite' => 36622,
+ 'Foxtel' => 33355,
+ 'Funimation' => 38583,
+ 'Game of war' => 35416,
+ 'Gears of War' => 38894,
+ 'Ghost Recon' => 36008,
+ 'GitHub' => 36529,
+ 'Gmail' => 32885,
+ 'Go Daddy' => 36619,
+ 'Google' => 32886,
+ 'Google Drive' => 34274,
+ 'Google Hangouts' => 32887,
+ 'Google Home' => 37343,
+ 'Google Play' => 32888,
+ 'Gran Turismo' => 36875,
+ 'Grindr' => 35834,
+ 'GTA 5' => 34755,
+ 'Guild Wars 2' => 36408,
+ 'Gumtree' => 33360,
+ 'Halo' => 35449,
+ 'Hay Day' => 34786,
+ 'Hayu' => 38366,
+ 'Hearthstone' => 38660,
+ 'Heronet' => 35949,
+ 'Hipchat' => 34867,
+ 'Hootsuite' => 32889,
+ 'iCloud' => 32890,
+ 'iiNet' => 33555,
+ 'iMessage' => 32891,
+ 'Imgur' => 32892,
+ 'Instagram' => 32893,
+ 'Internode' => 34477,
+ 'iPrimus' => 33876,
+ 'iTunes' => 32894,
+ 'Kik' => 32895,
+ 'Kraken' => 36849,
+ 'Ladbrokes' => 37640,
+ 'LastPass' => 34776,
+ 'League of Legends' => 35049,
+ 'LinkedIn' => 32896,
+ 'Mail.com' => 34799,
+ 'Mailbox' => 34834,
+ 'Mate Communicate' => 36813,
+ 'McDonalds app' => 37525,
+ 'ME Bank' => 34973,
+ 'Microsoft Azure' => 32917,
+ 'Microsoft Teams' => 38694,
+ 'Minecraft' => 32897,
+ 'My Fitness Pal' => 37633,
+ 'My Republic' => 35982,
+ 'MyGov' => 37550,
+ 'MyNetFone' => 35481,
+ 'myob' => 36343,
+ 'NAB' => 33365,
+ 'National Broadband Network, NBN' => 35992,
+ 'NBA 2k' => 37736,
+ 'Netflix' => 35177,
+ 'Netregistry' => 36060,
+ 'Netspeed' => 34492,
+ 'Nine' => 37000,
+ 'Nintendo eShop' => 34102,
+ 'Nintendo Network' => 35517,
+ 'Nintendo Switch Online' => 37243,
+ 'No Man\'s Sky' => 37404,
+ 'Office 365' => 34543,
+ 'OkCupid' => 38489,
+ 'OneDrive' => 32904,
+ 'Optus' => 33356,
+ 'Origin' => 35091,
+ 'Outlook' => 32899,
+ 'Overwatch' => 35683,
+ 'Paladins' => 37656,
+ 'Pandora' => 33343,
+ 'Panthur' => 36346,
+ 'Path of Exile' => 36498,
+ 'Paypal' => 35607,
+ 'Photobucket' => 32901,
+ 'Pinterest' => 32902,
+ 'Player Unknown\'s Battlegrounds' => 36356,
+ 'Playstation Network' => 34842,
+ 'PlentyOfFish' => 37416,
+ 'Pokémon Go' => 35732,
+ 'Project Online' => 34542,
+ 'Quickbooks Online' => 38220,
+ 'Rabobank' => 34974,
+ 'Rabodirect' => 34975,
+ 'Rainbow Six' => 35478,
+ 'Red Dead Redemption' => 37740,
+ 'Reddit' => 34843,
+ 'Roblox' => 36832,
+ 'Rocket League' => 35497,
+ 'Runescape' => 35106,
+ 'Salesforce' => 32903,
+ 'Sarahah' => 36374,
+ 'SBS' => 34703,
+ 'Sea of Thieves' => 37115,
+ 'Sharepoint Online' => 34541,
+ 'Skype' => 32905,
+ 'Skype for Business' => 34544,
+ 'Slack' => 35937,
+ 'Slideshare' => 32906,
+ 'Smite' => 35406,
+ 'Snapchat' => 32907,
+ 'Soundcloud' => 37230,
+ 'Spintel' => 35962,
+ 'Sportsbet' => 37713,
+ 'Spotify' => 33225,
+ 'St. George Bank' => 35353,
+ 'Stan' => 35503,
+ 'Star Wars Battlefront' => 35552,
+ 'Steam' => 34114,
+ 'Steep' => 37922,
+ 'Summoners War' => 37418,
+ 'Suncorp Bank' => 34432,
+ 'Sure Telecom' => 34394,
+ 'Tango' => 33742,
+ 'Teamviewer' => 34617,
+ 'Telegram' => 34220,
+ 'Telstra' => 33354,
+ 'The Division' => 35572,
+ 'The elder scrolls online' => 36554,
+ 'The Simpsons Tapped out' => 38166,
+ 'Think Mobile' => 34527,
+ 'Tiktok' => 39061,
+ 'Tinder' => 34241,
+ 'TPG Telecom' => 33359,
+ 'Tumblr' => 32910,
+ 'TuneIn' => 38761,
+ 'Tweakbox' => 38346,
+ 'Twitch' => 35026,
+ 'Twitter' => 32911,
+ 'Uber' => 37766,
+ 'Uber Eats' => 38272,
+ 'Uberglobal' => 35669,
+ 'Udemy' => 37719,
+ 'Uplay PC' => 36211,
+ 'V4 Telecom' => 37357,
+ 'Vaya' => 34518,
+ 'Viber' => 32912,
+ 'Vimeo' => 32913,
+ 'Virgin Mobile' => 34516,
+ 'Vocus' => 37711,
+ 'Vodafone' => 33357,
+ 'War Thunder' => 38361,
+ 'Warframe' => 36495,
+ 'Wattpad' => 34694,
+ 'Waze' => 33405,
+ 'Webex' => 34162,
+ 'Weebly' => 34911,
+ 'Westnet' => 35163,
+ 'Westpac' => 33363,
+ 'WeTransfer' => 32914,
+ 'Whatsapp' => 32915,
+ 'Wikipedia' => 32916,
+ 'Wish' => 39042,
+ 'Wix' => 37255,
+ 'Wordpress.com' => 32918,
+ 'World of Tanks' => 37191,
+ 'World of Warcraft' => 35054,
+ 'WWE Network' => 38722,
+ 'Xbox Live' => 34283,
+ 'Xero' => 36177,
+ 'Yahoo' => 32919,
+ 'Yahoo Mail' => 32920,
+ 'Yahoo Messenger' => 32921,
+ 'Yelp' => 32922,
+ 'Youtube' => 32923,
+ 'Youtube Music' => 37590,
+ 'Z1 Battle Royale' => 35494,
+ 'Zettanet' => 35655,
+ 'ZoHo' => 37551,
+ ),
+ 'Canada' => array(
+ '2k' => 36723,
+ 'Acanac' => 34107,
+ 'Access' => 33069,
+ 'Aeroplan' => 35789,
+ 'Air Canada' => 37029,
+ 'Air Miles' => 35788,
+ 'Airbnb' => 37423,
+ 'Alaska Airlines' => 32926,
+ 'Alexa' => 37067,
+ 'Allegiant Air' => 32927,
+ 'Allstream' => 34001,
+ 'Altima Telecom' => 37247,
+ 'Amazon' => 32928,
+ 'Amazon Prime Music' => 38907,
+ 'Amazon Prime Video' => 37393,
+ 'Amazon Web Services' => 32929,
+ 'American Airlines' => 32930,
+ 'American Express' => 38172,
+ 'Amino Apps' => 38716,
+ 'Anthem' => 38056,
+ 'Anydesk' => 37488,
+ 'Apex Legends' => 38113,
+ 'App Store' => 35576,
+ 'Apple Music' => 35244,
+ 'Apple Store' => 34456,
+ 'ARK: Survival Evolved' => 38747,
+ 'Assassin\'s Creed' => 35007,
+ 'BabyTEL' => 35360,
+ 'Battlefield' => 36923,
+ 'BC Hydro' => 35806,
+ 'Beanfield' => 36663,
+ 'beIN' => 38788,
+ 'Bell' => 32936,
+ 'Bell Aliant' => 32937,
+ 'Bet365' => 39064,
+ 'Binance' => 36876,
+ 'Bing' => 32938,
+ 'Black Desert Online' => 38829,
+ 'Blizzard Battle.net' => 34488,
+ 'Blogger' => 32940,
+ 'BMO' => 34525,
+ 'Boom Beach' => 36558,
+ 'Box' => 32941,
+ 'Brama Telecom' => 34146,
+ 'Brawl Stars' => 38833,
+ 'Bumble' => 36330,
+ 'Call of Duty' => 34149,
+ 'Candy Crush' => 35093,
+ 'Capital One' => 36410,
+ 'Carry Telecom' => 37695,
+ 'CBC' => 34267,
+ 'Centurylink' => 35627,
+ 'Chatr' => 34005,
+ 'CIBC' => 34526,
+ 'CIK Telecom' => 34428,
+ 'Clash of Clans' => 34445,
+ 'Clash Royale' => 37523,
+ 'Cloudflare' => 32952,
+ 'CNN' => 37372,
+ 'Cogeco' => 32953,
+ 'Cogent' => 35556,
+ 'Coinbase' => 36800,
+ 'Colbanet' => 37542,
+ 'CommStream' => 36028,
+ 'Comwave' => 36641,
+ 'Counter-strike' => 35056,
+ 'Crackle' => 35111,
+ 'Craigslist' => 32956,
+ 'Crave TV' => 35621,
+ 'Crunchyroll' => 36716,
+ 'Dark Souls' => 36557,
+ 'DayZ' => 39007,
+ 'Dazn' => 36510,
+ 'Dead By Daylight' => 36595,
+ 'Delta Air Lines' => 32958,
+ 'Destiny' => 34892,
+ 'DFP' => 35001,
+ 'Diablo' => 39071,
+ 'Discord' => 36411,
+ 'Distributel' => 33870,
+ 'Doordash' => 38385,
+ 'Dota 2' => 35404,
+ 'Dropbox' => 32961,
+ 'EA' => 34504,
+ 'EA Sports UFC' => 36552,
+ 'Eastlink' => 33414,
+ 'eBay' => 32964,
+ 'Ebox' => 35555,
+ 'Ecobee' => 38021,
+ 'Elite: Dangerous' => 37103,
+ 'Epic Games Store' => 38767,
+ 'Equitable Bank' => 36731,
+ 'Escape from Tarkov' => 37975,
+ 'Etsy' => 38265,
+ 'Exchange Online' => 34546,
+ 'Execulink' => 34942,
+ 'Expedia' => 37401,
+ 'Facebook' => 32965,
+ 'Facebook Messenger' => 32966,
+ 'Facetime' => 34595,
+ 'Fallout' => 37663,
+ 'FedEx' => 36500,
+ 'Fido' => 34000,
+ 'Fifa' => 35472,
+ 'Fitbit' => 38359,
+ 'Flickr' => 32967,
+ 'For Honor' => 35986,
+ 'Fortnite' => 36623,
+ 'Forza' => 38836,
+ 'Fox News' => 37665,
+ 'Freedom Mobile' => 33869,
+ 'Friday the 13th The Game' => 37562,
+ 'Funimation' => 37744,
+ 'Game of war' => 35415,
+ 'Garmin Connect' => 38540,
+ 'Gears of War' => 35938,
+ 'Ghost Recon' => 37772,
+ 'GitHub' => 32970,
+ 'Gmail' => 32971,
+ 'Go Daddy' => 34943,
+ 'GO Transit' => 32972,
+ 'Google' => 32973,
+ 'Google Calendar' => 38599,
+ 'Google Cloud' => 38537,
+ 'Google Drive' => 34272,
+ 'Google Hangouts' => 32974,
+ 'Google Home' => 36141,
+ 'Google Play' => 32975,
+ 'GoToMeeting' => 36504,
+ 'Gran Turismo' => 36679,
+ 'Grindr' => 35835,
+ 'GTA 5' => 34756,
+ 'Guild Wars 2' => 36406,
+ 'Halo' => 35450,
+ 'Harry Potter: Wizards Unite' => 38687,
+ 'Hay Day' => 34789,
+ 'Hayu' => 38789,
+ 'Hearthstone' => 38289,
+ 'Hipchat' => 34868,
+ 'Hootsuite' => 32977,
+ 'HQ Trivia' => 37027,
+ 'HSBC' => 39048,
+ 'Hue' => 37814,
+ 'HughesNet' => 32978,
+ 'iCloud' => 32979,
+ 'iMessage' => 33249,
+ 'Imgur' => 32981,
+ 'IMVU' => 37472,
+ 'Indeed' => 38782,
+ 'Instagram' => 32982,
+ 'Interac' => 39049,
+ 'iTunes' => 32983,
+ 'JetBlue Airways' => 32984,
+ 'Jira' => 38545,
+ 'Juno' => 32985,
+ 'Kijiji' => 34303,
+ 'Kik' => 32986,
+ 'Koodo' => 34002,
+ 'Kraken' => 36750,
+ 'LastPass' => 36785,
+ 'League of Legends' => 35047,
+ 'Lightspeed' => 37066,
+ 'LinkedIn' => 32987,
+ 'Madden' => 38459,
+ 'Mail.com' => 34801,
+ 'Mailbox' => 34837,
+ 'McDonalds app' => 37642,
+ 'MCSNet' => 35889,
+ 'Metro Loop' => 37646,
+ 'Microsoft Azure' => 33057,
+ 'Minecraft' => 32996,
+ 'Mixer' => 38769,
+ 'MLB The Show' => 36989,
+ 'MLB TV' => 37189,
+ 'Montréal Metro' => 32997,
+ 'Mordhau' => 38734,
+ 'MTS' => 33868,
+ 'My Fitness Pal' => 37632,
+ 'NBA 2k' => 37722,
+ 'Nest' => 35191,
+ 'Netflix' => 32998,
+ 'NetZero' => 32999,
+ 'Neverwinter' => 35766,
+ 'Nintendo eShop' => 34101,
+ 'Nintendo Network' => 35518,
+ 'No Man\'s Sky' => 38779,
+ 'NorthernTel' => 34636,
+ 'Northwestel' => 35104,
+ 'Office 365' => 34548,
+ 'OkCupid' => 36678,
+ 'OneDrive' => 33020,
+ 'Oricom Internet' => 35829,
+ 'Origin' => 36584,
+ 'Outlook' => 33005,
+ 'Overwatch' => 35682,
+ 'Paladins' => 37664,
+ 'Path of Exile' => 36481,
+ 'Paypal' => 33010,
+ 'PC Optimum' => 37216,
+ 'Peer 1' => 34824,
+ 'Photobucket' => 33011,
+ 'Pinterest' => 33012,
+ 'Piper' => 38323,
+ 'Player Unknown\'s Battlegrounds' => 36337,
+ 'Playstation Network' => 33013,
+ 'PlentyOfFish' => 35798,
+ 'Pokémon Go' => 35734,
+ 'Pokerstars' => 38174,
+ 'Primus' => 33872,
+ 'Qtrade Financial Group' => 36025,
+ 'Quickbooks Online' => 37607,
+ 'Quizup' => 34297,
+ 'Rabb.it' => 37974,
+ 'Rainbow Six' => 35477,
+ 'RBC' => 34521,
+ 'Red Dead Redemption' => 37742,
+ 'Reddit' => 34845,
+ 'Roblox' => 35785,
+ 'Rocket League' => 35498,
+ 'Rogers' => 33015,
+ 'Runescape' => 35105,
+ 'Salesforce' => 33016,
+ 'Sarahah' => 36514,
+ 'Sasktel' => 33871,
+ 'Scotiabank' => 34523,
+ 'Sea of Thieves' => 37111,
+ 'Sharepoint Online' => 34550,
+ 'Shaw' => 33551,
+ 'Shopify' => 35863,
+ 'Signal' => 36328,
+ 'Simplii' => 37541,
+ 'SiriusXM' => 38561,
+ 'SkipTheDishes' => 38322,
+ 'Skype' => 33021,
+ 'Skype for Business' => 34547,
+ 'Slack' => 35935,
+ 'Slideshare' => 33023,
+ 'Smartsheet' => 38724,
+ 'Smite' => 37026,
+ 'Snapchat' => 33024,
+ 'Soundcloud' => 37229,
+ 'Sourceforge' => 33025,
+ 'Spotify' => 33028,
+ 'Star Wars Battlefront' => 35550,
+ 'Start Communications' => 34426,
+ 'Steam' => 33030,
+ 'Steep' => 37923,
+ 'Strava' => 38745,
+ 'Summoners War' => 37422,
+ 'Tangerine' => 37694,
+ 'Tango' => 33741,
+ 'Tbaytel' => 34612,
+ 'TD Canada Trust' => 34522,
+ 'Teamviewer' => 34618,
+ 'TekSavvy' => 33867,
+ 'Télébec' => 34110,
+ 'Telegram' => 34228,
+ 'Telnet' => 34158,
+ 'Telus' => 33034,
+ 'TeraGo' => 34427,
+ 'Tesla' => 38835,
+ 'The Division' => 35571,
+ 'The elder scrolls online' => 36399,
+ 'The Simpsons Tapped out' => 35109,
+ 'The Weather Channel' => 38837,
+ 'Ticketmaster' => 39008,
+ 'Tiktok' => 38727,
+ 'Tinder' => 34239,
+ 'Toronto Transit Commission' => 33036,
+ 'Translink' => 33039,
+ 'Trello' => 39066,
+ 'Trove' => 38825,
+ 'Tumblr' => 33040,
+ 'TuneIn' => 36037,
+ 'TurboTax' => 38213,
+ 'Tweakbox' => 38343,
+ 'Twitch' => 35023,
+ 'Twitter' => 33042,
+ 'Uber' => 37765,
+ 'Uber Eats' => 38022,
+ 'Udemy' => 37718,
+ 'United Airlines' => 33044,
+ 'Uplay PC' => 34688,
+ 'UPS' => 36831,
+ 'Vancity' => 37570,
+ 'Velcom' => 36978,
+ 'Viber' => 33348,
+ 'Vidéotron' => 33050,
+ 'Vimeo' => 33051,
+ 'Virgin Mobile' => 34096,
+ 'Visa' => 38693,
+ 'VMedia' => 34265,
+ 'Warface' => 38777,
+ 'Warframe' => 36370,
+ 'Wattpad' => 34695,
+ 'Waveapps' => 35960,
+ 'Waze' => 33406,
+ 'Webex' => 34160,
+ 'Weight Watchers' => 35664,
+ 'WestJet' => 35648,
+ 'WeTransfer' => 33054,
+ 'Whatsapp' => 33055,
+ 'Whisper' => 34625,
+ 'Wiband' => 34771,
+ 'Wikipedia' => 33056,
+ 'Wish' => 39041,
+ 'Wisp' => 37218,
+ 'Wordpress.com' => 33059,
+ 'World of Tanks' => 35527,
+ 'World of Warcraft' => 34264,
+ 'WWE Network' => 38717,
+ 'Xbox Live' => 33061,
+ 'Xplornet' => 34270,
+ 'Yahoo' => 33063,
+ 'Yahoo Mail' => 33064,
+ 'Yahoo Messenger' => 33065,
+ 'Yak' => 34269,
+ 'Yelp' => 33066,
+ 'Youtube' => 33067,
+ 'Youtube Music' => 37588,
+ 'Yubo' => 38961,
+ 'Z1 Battle Royale' => 35491,
+ 'ZoHo' => 36038,
+ 'Zoom' => 37947,
+ 'Zynga' => 33068,
+ ),
+ 'Hong Kong' => array(
+ '3 (Three)' => 36202,
+ 'Apex Legends' => 38178,
+ 'App Store' => 37734,
+ 'Binance' => 37023,
+ 'Blizzard Battle.net' => 36186,
+ 'China Mobile' => 36199,
+ 'Cloudflare' => 38641,
+ 'Counter-strike' => 38725,
+ 'CSL' => 36200,
+ 'Discord' => 38608,
+ 'EA' => 36700,
+ 'eBay' => 36207,
+ 'Facebook' => 36185,
+ 'Fifa' => 37604,
+ 'For Honor' => 36187,
+ 'Ghost Recon' => 38990,
+ 'Gmail' => 36188,
+ 'Google' => 36210,
+ 'Google Drive' => 38264,
+ 'Google Play' => 38526,
+ 'GTA 5' => 37279,
+ 'Hong Kong Broadband Network' => 36204,
+ 'iCloud' => 38842,
+ 'Instagram' => 36392,
+ 'Kraken' => 36984,
+ 'Netflix' => 36213,
+ 'Netvigator' => 36203,
+ 'Office 365' => 37172,
+ 'Origin' => 38396,
+ 'Outlook' => 36189,
+ 'Overwatch' => 36190,
+ 'PCCW' => 36205,
+ 'Player Unknown\'s Battlegrounds' => 36617,
+ 'Playstation Network' => 36191,
+ 'Pokémon Go' => 36192,
+ 'Rainbow Six' => 36193,
+ 'Reddit' => 37479,
+ 'Skype' => 36194,
+ 'Smartone' => 36198,
+ 'Snapchat' => 36208,
+ 'Steam' => 38650,
+ 'Telegram' => 36195,
+ 'Three Home Broadband' => 36206,
+ 'Tinder' => 36196,
+ 'TuneIn' => 36036,
+ 'Twitter' => 38671,
+ 'Uplay PC' => 36212,
+ 'Whatsapp' => 36197,
+ 'Yahoo Mail' => 36769,
+ 'Youtube' => 36209,
+ ),
+ 'Ireland' => array(
+ '3 (Three)' => 34214,
+ 'AIB (Allied Irish Banks)' => 34209,
+ 'Amazon' => 34165,
+ 'Apex Legends' => 38133,
+ 'App Store' => 38557,
+ 'Apple Store' => 34460,
+ 'Bank of Ireland' => 37754,
+ 'Battlefield' => 38451,
+ 'Binance' => 36931,
+ 'Bing' => 34166,
+ 'Boom Beach' => 35153,
+ 'Call of Duty' => 36709,
+ 'Counter-strike' => 38233,
+ 'Craigslist' => 34167,
+ 'Crunchyroll' => 38094,
+ 'Dead By Daylight' => 37444,
+ 'Deezer' => 34168,
+ 'Destiny' => 38615,
+ 'Digiweb' => 34875,
+ 'Discord' => 37390,
+ 'DoneDeal' => 34208,
+ 'EA' => 36413,
+ 'eBay' => 34169,
+ 'Eir' => 34210,
+ 'eMobile' => 34211,
+ 'Facebook' => 34170,
+ 'Facebook Messenger' => 34171,
+ 'Facetime' => 34584,
+ 'Fifa' => 36907,
+ 'Fortnite' => 36692,
+ 'Ghost Recon' => 38992,
+ 'Gmail' => 34172,
+ 'Google' => 34173,
+ 'Google Hangouts' => 34174,
+ 'Google Play' => 34175,
+ 'Gran Turismo' => 36949,
+ 'Grindr' => 36755,
+ 'GTA 5' => 37275,
+ 'Hootsuite' => 34176,
+ 'iCloud' => 34177,
+ 'Imagine' => 34425,
+ 'iMessage' => 34178,
+ 'Imgur' => 34179,
+ 'Instagram' => 34180,
+ 'iTunes' => 34181,
+ 'Kik' => 34182,
+ 'Kraken' => 36850,
+ 'Ladbrokes' => 37469,
+ 'LinkedIn' => 34183,
+ 'Magnet' => 34424,
+ 'Meteor' => 34213,
+ 'My Fitness Pal' => 37634,
+ 'Nest' => 35193,
+ 'Netflix' => 34184,
+ 'Now TV' => 38403,
+ 'O2' => 34206,
+ 'Office 365' => 35441,
+ 'Origin' => 36890,
+ 'Outlook' => 34185,
+ 'Overwatch' => 36484,
+ 'Paddy Power' => 38360,
+ 'Pinterest' => 34186,
+ 'Playstation Network' => 34187,
+ 'PlentyOfFish' => 37417,
+ 'Pokémon Go' => 35758,
+ 'Rabodirect' => 34979,
+ 'Rainbow Six' => 36414,
+ 'Red Dead Redemption' => 38288,
+ 'Reddit' => 36826,
+ 'Roblox' => 38756,
+ 'Rocket League' => 36670,
+ 'Ryanair' => 37253,
+ 'Sky' => 34212,
+ 'Skype' => 37018,
+ 'Sleepless' => 38314,
+ 'Snapchat' => 34188,
+ 'Spotify' => 34189,
+ 'Steam' => 34190,
+ 'Strava' => 38643,
+ 'Teamviewer' => 37631,
+ 'Telegram' => 37226,
+ 'Tinder' => 34864,
+ 'Tumblr' => 34191,
+ 'TuneIn' => 38760,
+ 'Twitch' => 36538,
+ 'Twitter' => 34192,
+ 'Ulster Bank' => 36070,
+ 'Viber' => 34193,
+ 'Vimeo' => 34194,
+ 'Virgin Media' => 34207,
+ 'Vodafone' => 34196,
+ 'Wattpad' => 34699,
+ 'Waze' => 34197,
+ 'Webex' => 34198,
+ 'Whatsapp' => 34199,
+ 'Wikipedia' => 34200,
+ 'Xbox Live' => 34201,
+ 'Yahoo' => 34202,
+ 'Yahoo Mail' => 34203,
+ 'Yahoo Messenger' => 34204,
+ 'Youtube' => 34205,
+ 'Youtube Music' => 37589,
+ 'Z1 Battle Royale' => 37261,
+ ),
+ 'Danmark' => array(
+ '3 (Tre)' => 33694,
+ 'Amazon' => 33651,
+ 'Anthem' => 38212,
+ 'Apex Legends' => 38128,
+ 'App Store' => 38554,
+ 'Apple Store' => 34452,
+ 'Battlefield' => 37097,
+ 'Bet365' => 38813,
+ 'Bibob' => 33841,
+ 'Binance' => 36930,
+ 'Blizzard Battle.net' => 36162,
+ 'Call of Duty' => 36707,
+ 'CBB Mobil' => 33840,
+ 'Counter-strike' => 36171,
+ 'Crunchyroll' => 36721,
+ 'Danske Bank' => 33687,
+ 'DBA' => 33700,
+ 'Dead By Daylight' => 37427,
+ 'Deezer' => 33835,
+ 'Destiny' => 36593,
+ 'Discord' => 36682,
+ 'DMI' => 36121,
+ 'Dota 2' => 36398,
+ 'EA' => 35496,
+ 'eBay' => 34862,
+ 'EnergiMidt' => 34435,
+ 'Epic Games Store' => 39025,
+ 'Ewii' => 34436,
+ 'Facebook' => 33652,
+ 'Facebook Messenger' => 33653,
+ 'Faceit' => 38542,
+ 'Facetime' => 34590,
+ 'Fifa' => 36621,
+ 'For Honor' => 36163,
+ 'Fortnite' => 36690,
+ 'Fullrate' => 34433,
+ 'Ghost Recon' => 39002,
+ 'Gmail' => 33654,
+ 'Google' => 33655,
+ 'Google Drive' => 36606,
+ 'Google Hangouts' => 33656,
+ 'Google Play' => 33657,
+ 'Gran Turismo' => 36915,
+ 'GTA 5' => 36170,
+ 'Guild Wars 2' => 36469,
+ 'HBO Nordic' => 35605,
+ 'iCloud' => 33658,
+ 'iMessage' => 33659,
+ 'Instagram' => 33660,
+ 'iTunes' => 33661,
+ 'Kik' => 33705,
+ 'Kraken' => 36851,
+ 'Kviknet' => 38016,
+ 'League of Legends' => 36166,
+ 'LinkedIn' => 33701,
+ 'Minecraft' => 37282,
+ 'Net 1' => 33725,
+ 'Netflix' => 33689,
+ 'Nordea' => 33688,
+ 'Office 365' => 35440,
+ 'OneDrive' => 36867,
+ 'Origin' => 36894,
+ 'Outlook' => 33662,
+ 'Overwatch' => 36165,
+ 'Path of Exile' => 36482,
+ 'Playerunknown\'s Battlegrounds' => 36334,
+ 'Playstation Network' => 33663,
+ 'Pokémon Go' => 35753,
+ 'Rainbow Six' => 36161,
+ 'Realm Royale' => 37306,
+ 'Red Dead Redemption' => 38306,
+ 'Reddit' => 36824,
+ 'Roblox' => 36612,
+ 'Rocket League' => 35977,
+ 'Sea of Thieves' => 37122,
+ 'Skype' => 33686,
+ 'Snapchat' => 33696,
+ 'Spotify' => 33697,
+ 'Steam' => 34277,
+ 'Stofa' => 34434,
+ 'TDC' => 33691,
+ 'Teamviewer' => 35693,
+ 'Telegram' => 37181,
+ 'Telenor' => 33692,
+ 'Telia' => 33693,
+ 'Tinder' => 34861,
+ 'Twitch' => 35508,
+ 'Twitter' => 33664,
+ 'Uplay PC' => 35610,
+ 'Viaplay' => 37292,
+ 'Viber' => 33698,
+ 'Warframe' => 36703,
+ 'Whatsapp' => 33703,
+ 'Wikipedia' => 33665,
+ 'Wordfeud' => 33702,
+ 'World of Warcraft' => 36168,
+ 'World of Warships' => 38013,
+ 'Xbox Live' => 33666,
+ 'Yahoo Mail' => 37043,
+ 'Youmusic' => 33699,
+ 'Yousee' => 33695,
+ 'Youtube' => 33667,
+ 'Z1 Battle Royale' => 37265,
+ ),
+ 'Sverige' => array(
+ '3 (Tre)' => 33722,
+ 'A3' => 38249,
+ 'Amazon' => 33668,
+ 'Anthem' => 38190,
+ 'Apex Legends' => 38115,
+ 'App Store' => 38101,
+ 'Apple Store' => 34450,
+ 'Bahnhof' => 34417,
+ 'BankID' => 33845,
+ 'Battlefield' => 37093,
+ 'Bet365' => 38812,
+ 'Binance' => 36933,
+ 'Blizzard Battle.net' => 34484,
+ 'Blocket' => 33716,
+ 'Boxer' => 37572,
+ 'Bredband2' => 34418,
+ 'Bredbandsbolaget' => 33843,
+ 'Bredbandsson' => 36049,
+ 'Call of Duty' => 35539,
+ 'Com Hem' => 34416,
+ 'Counter-strike' => 36172,
+ 'Crunchyroll' => 36719,
+ 'Dead By Daylight' => 36598,
+ 'Destiny' => 35510,
+ 'Discord' => 36764,
+ 'Dota 2' => 35876,
+ 'EA' => 35509,
+ 'eBay' => 34863,
+ 'Epic Games Store' => 39023,
+ 'Escape from Tarkov' => 38107,
+ 'Eurosport Player' => 36613,
+ 'Facebook' => 33669,
+ 'Facebook Messenger' => 33670,
+ 'Faceit' => 39039,
+ 'Facetime' => 34589,
+ 'Fifa' => 36909,
+ 'For Honor' => 35998,
+ 'Fortnite' => 36685,
+ 'Ghost Recon' => 39003,
+ 'Glocalnet' => 33842,
+ 'Gmail' => 33671,
+ 'Google' => 33672,
+ 'Google Drive' => 36604,
+ 'Google Hangouts' => 33673,
+ 'Google Play' => 33674,
+ 'Gran Turismo' => 36914,
+ 'Grindr' => 38739,
+ 'GTA 5' => 35528,
+ 'Guild Wars 2' => 36466,
+ 'Halebop' => 33726,
+ 'Handelsbanken' => 33718,
+ 'Hay Day' => 38214,
+ 'HBO Nordic' => 35606,
+ 'Hearthstone' => 38659,
+ 'Hue' => 37816,
+ 'iCloud' => 33675,
+ 'Ikano Bank' => 36578,
+ 'iMessage' => 33676,
+ 'Instagram' => 33677,
+ 'iTunes' => 33678,
+ 'Kik' => 33704,
+ 'Kraken' => 36829,
+ 'League of Legends' => 36167,
+ 'LinkedIn' => 33714,
+ 'Minecraft' => 37281,
+ 'Net 1' => 33724,
+ 'Netflix' => 33706,
+ 'Nordea' => 33717,
+ 'Office 365' => 35439,
+ 'OneDrive' => 36869,
+ 'Origin' => 36898,
+ 'Outlook' => 33679,
+ 'Overwatch' => 36150,
+ 'Path of Exile' => 36480,
+ 'Playerunknown\'s Battlegrounds' => 36333,
+ 'Playstation Network' => 33707,
+ 'Pokémon Go' => 35749,
+ 'Rainbow Six' => 36148,
+ 'Realm Royale' => 37307,
+ 'Red Dead Redemption' => 38285,
+ 'Reddit' => 35883,
+ 'Riksnet' => 37486,
+ 'Roblox' => 36611,
+ 'Rocket League' => 35487,
+ 'Sea of Thieves' => 37114,
+ 'SEB' => 33719,
+ 'SF Anytime' => 37573,
+ 'SkandiaBanken' => 34021,
+ 'Skype' => 33708,
+ 'Slack' => 37495,
+ 'Snapchat' => 33709,
+ 'Soundcloud' => 37228,
+ 'Sparbanken Öresund' => 34310,
+ 'Spotify' => 33838,
+ 'Steam' => 34135,
+ 'Swedbank' => 33713,
+ 'Swish' => 35777,
+ 'Teamviewer' => 35692,
+ 'Tele2' => 33721,
+ 'Telegram' => 35969,
+ 'Telenor' => 33723,
+ 'Telia' => 33720,
+ 'Telldus' => 36653,
+ 'The Elder Scrolls Online' => 36033,
+ 'Tinder' => 36458,
+ 'Tradera' => 33715,
+ 'Tumblr' => 36713,
+ 'Tweakbox' => 38905,
+ 'Twitch' => 35507,
+ 'Twitter' => 33710,
+ 'Uplay PC' => 35609,
+ 'Viaplay' => 37291,
+ 'Viber' => 37502,
+ 'Warframe' => 36701,
+ 'Whatsapp' => 33711,
+ 'Wikipedia' => 33682,
+ 'Wordfeud' => 33712,
+ 'World of Tanks' => 35488,
+ 'World of Warcraft' => 36169,
+ 'World of Warships' => 38011,
+ 'Xbox Live' => 33683,
+ 'Yahoo Mail' => 37044,
+ 'Youtube' => 33684,
+ 'Z1 Battle Royale' => 35493,
+ ),
+ 'Italia' => array(
+ '3 Italia' => 33154,
+ 'Airbnb' => 37345,
+ 'Aircomm' => 36015,
+ 'Alexa' => 38031,
+ 'AlternatYva' => 37556,
+ 'Amazon' => 33174,
+ 'Amazon Prime Video' => 36975,
+ 'Anthem' => 38189,
+ 'Anydesk' => 37491,
+ 'Apex Legends' => 38119,
+ 'App Store' => 37733,
+ 'Apple Store' => 34468,
+ 'Aruba' => 37217,
+ 'Banco di Napoli' => 33181,
+ 'Battlefield' => 37095,
+ 'Betclic' => 39047,
+ 'Binance' => 36937,
+ 'Blizzard Battle.net' => 37092,
+ 'BNL' => 33180,
+ 'Brawl Stars' => 38821,
+ 'Bwin' => 35072,
+ 'Call of Duty' => 35537,
+ 'Clash of Clans' => 34361,
+ 'Clash Royale' => 38956,
+ 'Cloudflare' => 38620,
+ 'Coinbase' => 36801,
+ 'Coopvoce' => 34921,
+ 'Counter-strike' => 36650,
+ 'Credito Emiliano - Credem' => 33182,
+ 'Crunchyroll' => 38095,
+ 'Dazn' => 37442,
+ 'Dead By Daylight' => 36627,
+ 'Deezer' => 33839,
+ 'Destiny' => 36590,
+ 'Discord' => 36767,
+ 'Dota 2' => 35878,
+ 'Dropbox' => 33175,
+ 'EA' => 34498,
+ 'EA Sports UFC' => 36553,
+ 'eBay' => 32816,
+ 'Elite: Dangerous' => 37774,
+ 'Eolo' => 37763,
+ 'Epic Games Store' => 39024,
+ 'Escape from Tarkov' => 39056,
+ 'Facebook' => 32812,
+ 'Facebook Messenger' => 32813,
+ 'Facetime' => 34598,
+ 'Fallout' => 37668,
+ 'Fastweb' => 33160,
+ 'Fifa' => 36910,
+ 'Fineco' => 33167,
+ 'Flickr' => 37949,
+ 'For Honor' => 35990,
+ 'Fortnite' => 36686,
+ 'Friday the 13th The Game' => 37564,
+ 'Ghost Recon' => 38999,
+ 'Gmail' => 32815,
+ 'Google' => 32805,
+ 'Google Drive' => 36607,
+ 'Google Hangouts' => 32806,
+ 'Google Play' => 32807,
+ 'Gran Turismo' => 36874,
+ 'Grindr' => 36757,
+ 'GTA5 ' => 35918,
+ 'Guild Wars 2' => 36472,
+ 'Ho' => 37408,
+ 'Hue' => 37813,
+ 'iCloud' => 33169,
+ 'Iliad' => 37214,
+ 'iMessage' => 33164,
+ 'Infinity' => 35972,
+ 'Infostrada' => 34711,
+ 'ING Direct' => 33168,
+ 'Instagram' => 33170,
+ 'Intesa Sanpaolo' => 33179,
+ 'iTunes' => 33159,
+ 'Kena Mobile' => 36364,
+ 'Kraken' => 36771,
+ 'League of Legends' => 37759,
+ 'Libero' => 33178,
+ 'LinkedIn' => 33177,
+ 'Linkem' => 34475,
+ 'Lottomatica' => 35068,
+ 'Lycamobile' => 38691,
+ 'Mc-link' => 34476,
+ 'Mediaset Premium' => 33162,
+ 'Mediaset TV Free' => 35973,
+ 'My Fitness Pal' => 37638,
+ 'Netflix' => 35568,
+ 'Neverwinter' => 36675,
+ 'NGI' => 34474,
+ 'Office 365' => 35443,
+ 'Onedrive' => 35411,
+ 'Origin' => 36897,
+ 'Outlook' => 33163,
+ 'Overwatch' => 36384,
+ 'Paddy Power' => 35069,
+ 'Paladins' => 36379,
+ 'Path of Exile' => 37726,
+ 'Paypal' => 33166,
+ 'Player Unknown\'s Battlegrounds' => 36567,
+ 'Playstation Network' => 32818,
+ 'Pokémon Go' => 35737,
+ 'Poste Italiane' => 37361,
+ 'PosteMobile' => 37360,
+ 'Quizduello' => 34299,
+ 'Rainbow Six' => 36045,
+ 'Red Dead Redemption' => 38284,
+ 'Reddit' => 37367,
+ 'Roblox' => 38916,
+ 'Rocket League' => 35979,
+ 'Ruzzle' => 33967,
+ 'Ryanair' => 37519,
+ 'Sea of Thieves' => 37121,
+ 'Sisal' => 35066,
+ 'Sky' => 33155,
+ 'Skype' => 32821,
+ 'Slack' => 37341,
+ 'SNAI' => 35067,
+ 'Snapchat' => 36446,
+ 'Spotify' => 33176,
+ 'Steam' => 34136,
+ 'Tango' => 33743,
+ 'Teamviewer' => 35455,
+ 'Telegram' => 34904,
+ 'The elder scrolls online' => 37327,
+ 'TIM' => 33158,
+ 'TimMusic' => 35913,
+ 'TimVision' => 35908,
+ 'Tinder' => 36549,
+ 'Tiscali' => 33161,
+ 'Tumblr' => 36712,
+ 'Tweakbox' => 38344,
+ 'Twitch' => 35604,
+ 'Twitter' => 32814,
+ 'UniCredit' => 33171,
+ 'Uplay PC' => 35613,
+ 'Viber' => 33403,
+ 'Virgilio' => 36459,
+ 'Vodafone' => 33156,
+ 'Warface' => 38765,
+ 'Warframe' => 36492,
+ 'WeBank' => 33173,
+ 'WeTransfer' => 36445,
+ 'Whatsapp' => 32820,
+ 'Wifi Trenitalia' => 35974,
+ 'Wikipedia' => 33172,
+ 'William Hill' => 35070,
+ 'Wind' => 33157,
+ 'World of Warships' => 38012,
+ 'Xbox Live' => 32819,
+ 'Yahoo' => 32809,
+ 'Yahoo Mail' => 32810,
+ 'Yahoo Messenger' => 32811,
+ 'Youtube' => 32808,
+ 'Z1 Battle Royale' => 37254,
+ 'ZoHo' => 37554,
+ ),
+ 'South Africa' => array(
+ 'ABSA' => 33599,
+ 'Afrihost' => 35660,
+ 'Amazon' => 33578,
+ 'Apex Legends' => 38157,
+ 'Apple Store' => 34458,
+ 'Axxess' => 35618,
+ 'Bidorbuy' => 33596,
+ 'Binance' => 36941,
+ 'Bing' => 33579,
+ 'Blizzard Battle.net' => 37661,
+ 'Call of Duty' => 34152,
+ 'Capitec' => 36966,
+ 'Cell C' => 33602,
+ 'Clash Royale' => 38946,
+ 'Cool Ideas' => 38326,
+ 'Counter-strike' => 36512,
+ 'Cybersmart' => 34619,
+ 'Dead By Daylight' => 37435,
+ 'Deezer' => 33834,
+ 'Destiny' => 38981,
+ 'DirecTV Now' => 35915,
+ 'Discord' => 38611,
+ 'Discovery' => 38334,
+ 'Dota 2' => 36428,
+ 'DSTv' => 33607,
+ 'EA' => 34507,
+ 'eBay' => 33581,
+ 'Eskom' => 38333,
+ 'Facebook' => 33582,
+ 'Facebook Messenger' => 37737,
+ 'Fifa' => 38436,
+ 'First National Bank (FNB)' => 33597,
+ 'Fortnite' => 36710,
+ 'Ghost Recon' => 38994,
+ 'Gmail' => 33583,
+ 'Google' => 33584,
+ 'Google Hangouts' => 33585,
+ 'Google Play' => 33586,
+ 'Gov.za' => 38328,
+ 'GTA 5' => 37273,
+ 'Guild Wars 2' => 36501,
+ 'Gumtree' => 33592,
+ 'iBurst' => 34620,
+ 'iCloud' => 33587,
+ 'Imaginet' => 38325,
+ 'Instagram' => 33588,
+ 'Internet Solutions' => 38332,
+ 'iTunes' => 33589,
+ 'Kik' => 33590,
+ 'Kraken' => 36985,
+ 'League of Legends' => 38633,
+ 'LinkedIn' => 33591,
+ 'Luno' => 37012,
+ 'Metrofibre' => 38335,
+ 'MTN' => 33601,
+ 'MWEB' => 33606,
+ 'Nedbank' => 35461,
+ 'Neotel' => 35426,
+ 'Netflix' => 36390,
+ 'Octotel' => 38327,
+ 'Office 365' => 35444,
+ 'OLX' => 33255,
+ 'Openserve' => 38331,
+ 'Origin' => 36886,
+ 'Outlook' => 33566,
+ 'Overwatch' => 36149,
+ 'Paypal' => 33567,
+ 'Pinterest' => 33568,
+ 'Player Unknown\'s Battlegrounds' => 36642,
+ 'Playstation Network' => 33569,
+ 'Pokémon Go' => 35756,
+ 'Rain' => 37397,
+ 'Rainbow Six' => 37738,
+ 'Reddit' => 38362,
+ 'Safricom' => 35139,
+ 'SARS' => 38336,
+ 'Seacom' => 38329,
+ 'Showmax' => 37149,
+ 'Skype' => 33570,
+ 'Snapchat' => 33571,
+ 'Standard Bank' => 33598,
+ 'Steam' => 34223,
+ 'Takealot' => 36777,
+ 'Teamviewer' => 37480,
+ 'Telegram' => 37184,
+ 'Telkom' => 33603,
+ 'The elder scrolls online' => 37325,
+ 'The Simpsons Tapped out' => 35903,
+ 'Tinder' => 37166,
+ 'Twitch' => 38702,
+ 'Twitter' => 33572,
+ 'Viber' => 33595,
+ 'Virgin Mobile' => 38324,
+ 'Vodacom' => 33600,
+ 'Vox' => 35368,
+ 'Vumatel' => 37660,
+ 'Warframe' => 37125,
+ 'Webafrica' => 38330,
+ 'WeChat' => 33594,
+ 'Whatsapp' => 33573,
+ 'Wikipedia' => 33574,
+ 'Xbox Live' => 34285,
+ 'Yahoo' => 33575,
+ 'Yahoo Mail' => 33576,
+ 'Youtube' => 33577,
+ ),
+ 'India' => array(
+ 'ACT' => 34764,
+ 'Aircel' => 33257,
+ 'Airtel' => 33233,
+ 'Alexa' => 36921,
+ 'AliExpress' => 37824,
+ 'Amazon' => 35118,
+ 'Amazon Prime Music' => 37583,
+ 'Amazon Prime Video' => 36974,
+ 'Amazon Web Services' => 38411,
+ 'Apex Legends' => 38180,
+ 'App Store' => 38556,
+ 'Apple Store' => 34459,
+ 'Bank of Baroda' => 37676,
+ 'Bank of India' => 33250,
+ 'Bharat Sanchar Nigam Limited (BSNL)' => 33232,
+ 'Binance' => 36932,
+ 'Bitbucket' => 36988,
+ 'Boom Beach' => 34782,
+ 'Brawl Stars' => 38822,
+ 'Call of Duty' => 34153,
+ 'Cherrinet' => 37677,
+ 'Clash of Clans' => 36872,
+ 'Clash Royale' => 38955,
+ 'Cloudflare' => 38652,
+ 'Counter-strike' => 38240,
+ 'Destiny' => 38989,
+ 'Discord' => 38106,
+ 'Dota 2' => 36393,
+ 'Dropbox' => 33237,
+ 'EA' => 34508,
+ 'eBay' => 32863,
+ 'Facebook' => 32860,
+ 'Facebook Messenger' => 33238,
+ 'Facetime' => 34596,
+ 'Fifa' => 38399,
+ 'Flipkart' => 33252,
+ 'Fortnite' => 36843,
+ 'GitHub' => 37606,
+ 'Gmail' => 32862,
+ 'Go Daddy' => 37499,
+ 'Google' => 32857,
+ 'Google Drive' => 38261,
+ 'Google Hangouts' => 32869,
+ 'Google Home' => 37344,
+ 'Google Play' => 33239,
+ 'GTA 5' => 37272,
+ 'Haptik' => 34762,
+ 'Hathway' => 37673,
+ 'Hay Day' => 34787,
+ 'HDFC Bank' => 33231,
+ 'ICICI Bank' => 37674,
+ 'iCloud' => 33240,
+ 'IDBI Bank' => 37678,
+ 'Idea Cellular' => 33234,
+ 'Idian Bank' => 37670,
+ 'iMessage' => 33248,
+ 'Instagram' => 33241,
+ 'IRCTC' => 37671,
+ 'iTunes' => 33230,
+ 'Jio' => 37460,
+ 'Kik' => 33351,
+ 'Line' => 33400,
+ 'LinkedIn' => 33242,
+ 'Mahanagar Telephone Nigam Limited (MTNL)' => 33258,
+ 'Microsoft Azure' => 36112,
+ 'MTS' => 33260,
+ 'Naukri' => 33253,
+ 'Netflix' => 37299,
+ 'Office 365' => 37174,
+ 'OLX' => 33593,
+ 'ooVoo' => 34084,
+ 'Origin' => 38378,
+ 'Outlook' => 33243,
+ 'Paypal' => 33244,
+ 'Paytm' => 33401,
+ 'Pinterest' => 37231,
+ 'Player Unknown\'s Battlegrounds' => 36643,
+ 'Playstation Network' => 32865,
+ 'Pokémon Go' => 35755,
+ 'Quora' => 37473,
+ 'Rainbow Six' => 36412,
+ 'Reddit' => 38061,
+ 'Reliance' => 33236,
+ 'Roblox' => 38921,
+ 'Salesforce' => 34734,
+ 'Sarahah' => 36513,
+ 'Skype' => 32868,
+ 'Snapchat' => 36741,
+ 'Spotify' => 37079,
+ 'Standard Chartered' => 33235,
+ 'State Bank of India (SBI)' => 33251,
+ 'Steam' => 35930,
+ 'Swiggy' => 37675,
+ 'Tango' => 33744,
+ 'Tata Docomo' => 33256,
+ 'Teamviewer' => 34346,
+ 'Telegram' => 36582,
+ 'Tiktok' => 38661,
+ 'Tinder' => 37167,
+ 'Tumblr' => 37641,
+ 'Twitch' => 38355,
+ 'Twitter' => 32861,
+ 'Uber Eats' => 38898,
+ 'Udemy' => 38389,
+ 'Uninor' => 33259,
+ 'Uplay PC' => 36628,
+ 'Viber' => 33049,
+ 'Videocon' => 33261,
+ 'Vimeo' => 37232,
+ 'Vodafone' => 33254,
+ 'Warframe' => 37124,
+ 'Wattpad' => 34698,
+ 'Waze' => 33408,
+ 'Webex' => 34163,
+ 'WeChat' => 33396,
+ 'Whatsapp' => 32867,
+ 'Wikipedia' => 33245,
+ 'Xbox Live' => 32866,
+ 'Yahoo' => 32859,
+ 'Yahoo Mail' => 33246,
+ 'Yahoo Messenger' => 33247,
+ 'You Broadband' => 34763,
+ 'Youtube' => 32858,
+ 'Zee5' => 38647,
+ 'Zerodha' => 37207,
+ 'ZoHo' => 37552,
+ 'Zomato' => 37672,
+ ),
+ 'Portugal' => array(
+ 'ActivoBank' => 33959,
+ 'Apex Legends' => 38127,
+ 'Apple Store' => 34464,
+ 'Banco Espírito Santo' => 33961,
+ 'Banco Santander Totta' => 33960,
+ 'Binance' => 36945,
+ 'Bing' => 33925,
+ 'Blizzard Battle.net' => 37225,
+ 'Cabovisão' => 33956,
+ 'Caixa Geral de Depósitos' => 33957,
+ 'Call of Duty' => 38982,
+ 'Clash Royale' => 38957,
+ 'Counter-Strike' => 36651,
+ 'Dead By Daylight' => 37429,
+ 'Deezer' => 33927,
+ 'Destiny' => 38964,
+ 'Discord' => 37388,
+ 'Dota 2' => 38479,
+ 'EA' => 36452,
+ 'eBay' => 33928,
+ 'Facebook' => 33929,
+ 'Facebook Messenger' => 34847,
+ 'Facetime' => 34586,
+ 'Fifa' => 36905,
+ 'Fortnite' => 36695,
+ 'Gmail' => 33930,
+ 'Go Daddy' => 34849,
+ 'Google' => 33931,
+ 'Google Play' => 33932,
+ 'GTA 5' => 36857,
+ 'HBO' => 38379,
+ 'iCloud' => 33933,
+ 'iMessage' => 33934,
+ 'Instagram' => 33963,
+ 'iTunes' => 33935,
+ 'Kik' => 33943,
+ 'Kraken' => 36980,
+ 'League of Legends' => 38635,
+ 'LinkedIn' => 33936,
+ 'MEO' => 33950,
+ 'Millennium Bcp' => 33958,
+ 'Montepio' => 33962,
+ 'Netflix' => 37301,
+ 'NOS' => 33952,
+ 'Office 365' => 37175,
+ 'OLX' => 33949,
+ 'OneDrive' => 36870,
+ 'Optimus' => 33951,
+ 'Origin' => 36892,
+ 'Outlook' => 33937,
+ 'Overwatch' => 37082,
+ 'Paypal' => 33938,
+ 'Pinterest' => 37373,
+ 'Player Unknown\'s Battlegrounds' => 36566,
+ 'Playstation Network' => 33939,
+ 'Pokémon Go' => 35754,
+ 'Rainbow Six' => 36454,
+ 'Reddit' => 38068,
+ 'Roblox' => 38920,
+ 'Rocket League' => 36668,
+ 'Skype' => 33940,
+ 'Snapchat' => 36742,
+ 'Spotify' => 33964,
+ 'Steam' => 34138,
+ 'Teamviewer' => 35691,
+ 'Telegram' => 37186,
+ 'Tinder' => 37921,
+ 'Twitch' => 36453,
+ 'Twitter' => 33941,
+ 'Uplay PC' => 38651,
+ 'Uzo' => 33954,
+ 'Viber' => 33942,
+ 'Vodafone' => 33953,
+ 'Warframe' => 37135,
+ 'Whatsapp' => 34855,
+ 'Wikipedia' => 33944,
+ 'Xbox Live' => 33945,
+ 'Yahoo' => 33946,
+ 'Yahoo Mail' => 33947,
+ 'Youtube' => 33948,
+ 'Z1 Battle Royale' => 37264,
+ ),
+ 'Schweiz' => array(
+ 'Airbnb' => 37666,
+ 'Anthem' => 38205,
+ 'Apex Legends' => 38130,
+ 'App Store' => 35583,
+ 'Apple Store' => 34462,
+ 'Battlefield' => 38050,
+ 'Bet365' => 38831,
+ 'Binance' => 36935,
+ 'Blizzard Battle.net' => 36569,
+ 'Call of Duty' => 34157,
+ 'Clash Royale' => 38958,
+ 'Coinbase' => 36805,
+ 'Counter-strike' => 36646,
+ 'Crunchyroll' => 38144,
+ 'Dazn' => 36509,
+ 'Dead By Daylight' => 37413,
+ 'Deezer' => 34038,
+ 'Destiny' => 36591,
+ 'Discord' => 37389,
+ 'Dota 2' => 35402,
+ 'Dropbox' => 34039,
+ 'EA' => 34503,
+ 'eBay' => 34040,
+ 'Epic Games Store' => 39026,
+ 'Evard' => 35642,
+ 'Facebook' => 34041,
+ 'Facebook Messenger' => 34885,
+ 'Facetime' => 34585,
+ 'Fifa' => 37089,
+ 'flashcable' => 37202,
+ 'Flickr' => 34042,
+ 'For Honor' => 35997,
+ 'Fortnite' => 36688,
+ 'Ghost Recon' => 36006,
+ 'Gmail' => 34043,
+ 'GMX' => 34044,
+ 'Google' => 34045,
+ 'Google Hangouts' => 34046,
+ 'Google Play' => 34047,
+ 'Green' => 36971,
+ 'Grindr' => 38742,
+ 'GTA 5' => 35502,
+ 'Guild Wars 2' => 36465,
+ 'Hay Day' => 34853,
+ 'iCloud' => 34048,
+ 'ICQ' => 34049,
+ 'iMessage' => 34050,
+ 'Instagram' => 34051,
+ 'iTunes' => 34052,
+ 'Kik' => 34054,
+ 'Kraken' => 36807,
+ 'League of Legends' => 35574,
+ 'LinkedIn' => 34055,
+ 'Lovoo' => 35401,
+ 'Mail.de' => 38629,
+ 'Netatmo' => 38467,
+ 'Netflix' => 34632,
+ 'Nitrado' => 35547,
+ 'Office 365' => 36581,
+ 'OneDrive' => 35413,
+ 'Origin' => 36891,
+ 'Outlook' => 34057,
+ 'Overwatch' => 36152,
+ 'Peoplefone' => 36602,
+ 'Playerunknown\'s Battlegrounds' => 36560,
+ 'Playstation Network' => 34087,
+ 'Pokémon Go' => 35744,
+ 'PostFinance' => 36048,
+ 'Protonmail' => 35643,
+ 'Quickline' => 36970,
+ 'Quizduell' => 34529,
+ 'Rainbow Six' => 36147,
+ 'Red Dead Redemption' => 37741,
+ 'Reddit' => 38067,
+ 'Ricardo' => 34079,
+ 'Rocket League' => 35486,
+ 'Salt' => 35602,
+ 'Search.ch' => 34080,
+ 'Sky Sport' => 37069,
+ 'Skype' => 34059,
+ 'Snapchat' => 34060,
+ 'Spotify' => 34061,
+ 'SRF' => 34081,
+ 'Steam' => 34226,
+ 'Sunrise' => 34078,
+ 'Swisscom' => 34077,
+ 'Tango' => 34062,
+ 'Teamviewer' => 34616,
+ 'Telegram' => 35968,
+ 'The elder scrolls online' => 35763,
+ 'Threema' => 34256,
+ 'Tinder' => 34244,
+ 'Tumblr' => 34063,
+ 'TuneIn' => 38775,
+ 'Tutti' => 36014,
+ 'Tweakbox' => 38904,
+ 'Twint' => 38183,
+ 'Twitch' => 35505,
+ 'Twitter' => 34064,
+ 'UBS' => 34082,
+ 'UPC' => 34075,
+ 'Uplay PC' => 34690,
+ 'Viber' => 34065,
+ 'Viewster' => 34325,
+ 'Vimeo' => 34066,
+ 'Waze' => 34067,
+ 'Web.de' => 34068,
+ 'Whatsapp' => 34069,
+ 'Wikipedia' => 34070,
+ 'World of Warships' => 38015,
+ 'Xbox Live' => 34088,
+ 'Yahoo Mail' => 34072,
+ 'Yahoo Messenger' => 34073,
+ 'Yallo' => 37298,
+ 'Youtube' => 34074,
+ 'Z1 Battle Royale' => 35492,
+ 'Zattoo' => 35366,
+ ),
+ 'Singapore' => array(
+ 'Airbnb' => 37608,
+ 'Apex Legends' => 38182,
+ 'App Store' => 37735,
+ 'Apple Store' => 34682,
+ 'Binance' => 36944,
+ 'Bing' => 34652,
+ 'Blizzard Battle.net' => 36431,
+ 'Brawl Stars' => 38820,
+ 'Call of Duty' => 39051,
+ 'Clash of Clans' => 34667,
+ 'Clash Royale' => 38952,
+ 'Counter-strike' => 36556,
+ 'DBS' => 34658,
+ 'Destiny' => 38986,
+ 'Discord' => 37100,
+ 'Dota 2' => 35875,
+ 'Dropbox' => 34671,
+ 'EA' => 36429,
+ 'Facebook' => 34645,
+ 'Facebook Messenger' => 34646,
+ 'Facetime' => 34681,
+ 'Fifa' => 38398,
+ 'Fortnite' => 37321,
+ 'Ghost Recon' => 38995,
+ 'Gmail' => 34647,
+ 'Google' => 34648,
+ 'Google Drive' => 34649,
+ 'Google Hangouts' => 38364,
+ 'Google Play' => 38519,
+ 'Grindr' => 36754,
+ 'GTA 5' => 37278,
+ 'HSBC' => 34672,
+ 'iCloud' => 34685,
+ 'iMessage' => 34684,
+ 'Instagram' => 34665,
+ 'iTunes' => 34683,
+ 'League of Legends' => 38748,
+ 'Line' => 34740,
+ 'LinkedIn' => 34656,
+ 'M1' => 34662,
+ 'Microsoft Azure' => 36113,
+ 'Minecraft' => 38738,
+ 'My Republic' => 36463,
+ 'Netflix' => 36391,
+ 'Office 365' => 37173,
+ 'OkCupid' => 38490,
+ 'Origin' => 38377,
+ 'Outlook' => 34673,
+ 'Overwatch' => 36389,
+ 'Path of Exile' => 37723,
+ 'Player Unknown\'s Battlegrounds' => 36361,
+ 'Playstation Network' => 34668,
+ 'Pokémon Go' => 35757,
+ 'POSB' => 34738,
+ 'Rainbow Six' => 36382,
+ 'Reddit' => 37475,
+ 'Roblox' => 37286,
+ 'SingTel' => 34661,
+ 'Skype' => 36418,
+ 'Snapchat' => 34666,
+ 'Spotify' => 34739,
+ 'Standard Chartered' => 34743,
+ 'Starhub' => 34663,
+ 'Steam' => 36430,
+ 'Summoners War' => 37421,
+ 'Taobao' => 34660,
+ 'Telegram' => 36381,
+ 'Tinder' => 37137,
+ 'Tumblr' => 36711,
+ 'Tweakbox' => 38345,
+ 'Twitch' => 36539,
+ 'Twitter' => 34659,
+ 'Uplay PC' => 37521,
+ 'Viber' => 34670,
+ 'Viewqwest' => 37506,
+ 'Warframe' => 36494,
+ 'WeChat' => 34674,
+ 'Whatsapp' => 34664,
+ 'Whisper' => 34624,
+ 'Wikipedia' => 34655,
+ 'Xbox Live' => 34669,
+ 'Yahoo' => 34654,
+ 'Yahoo Mail' => 34653,
+ 'Youtube' => 34650,
+ ),
+ 'Türkiye' => array(
+ 'Akbank' => 33783,
+ 'Anthem' => 38195,
+ 'Apex Legends' => 38121,
+ 'Apple Store' => 34451,
+ 'Battlefield' => 38449,
+ 'Binance' => 36952,
+ 'Blizzard Battle.net' => 37224,
+ 'Call of Duty' => 38940,
+ 'Counter-strike' => 36652,
+ 'D-Smart' => 34422,
+ 'Dead By Daylight' => 37433,
+ 'Denizbank' => 33788,
+ 'Destiny' => 38984,
+ 'Discord' => 36766,
+ 'Dota 2' => 36396,
+ 'EA' => 36157,
+ 'Facebook' => 33748,
+ 'Facebook Messenger' => 33749,
+ 'Facetime' => 34588,
+ 'Fifa' => 37087,
+ 'Finansbank' => 33786,
+ 'For Honor' => 37369,
+ 'Fortnite' => 36698,
+ 'Garanti' => 33780,
+ 'GittiGidiyor' => 33779,
+ 'Gmail' => 33750,
+ 'Google' => 33751,
+ 'Google Hangouts' => 33752,
+ 'Google Play' => 33753,
+ 'Gran Turismo' => 36947,
+ 'GTA 5' => 37276,
+ 'HalkBank' => 33784,
+ 'HSBC' => 33790,
+ 'iCloud' => 33754,
+ 'iMessage' => 33755,
+ 'ING Bank' => 33789,
+ 'Instagram' => 33732,
+ 'iTunes' => 33756,
+ 'Kik' => 33757,
+ 'Kraken' => 36986,
+ 'LinkedIn' => 33758,
+ 'Messageme' => 33727,
+ 'Netflix' => 37300,
+ 'Office 365' => 38058,
+ 'Origin' => 36901,
+ 'Outlook' => 33759,
+ 'Overwatch' => 36386,
+ 'Player Unknown\'s Battlegrounds' => 36456,
+ 'Playstation Network' => 33760,
+ 'Rainbow Six' => 36154,
+ 'Reddit' => 38064,
+ 'Roblox' => 38915,
+ 'Rocket League' => 36669,
+ 'Sahibinden' => 33778,
+ 'Skype' => 33761,
+ 'Snapchat' => 33762,
+ 'Steam' => 37322,
+ 'Tango' => 33730,
+ 'Telegram' => 37090,
+ 'Tinder' => 37911,
+ 'Ttnet' => 33776,
+ 'Türk Ekonomi Bankası (TEB)' => 33791,
+ 'Türk Telekom' => 33771,
+ 'Turkcell' => 33774,
+ 'Türkiye İş Bankası' => 33781,
+ 'Türksat Kablo' => 33772,
+ 'Twitch' => 35506,
+ 'Twitter' => 33613,
+ 'Uplay PC' => 36156,
+ 'VakıfBank' => 33785,
+ 'Viber' => 33733,
+ 'Vodafone' => 33775,
+ 'Warframe' => 37128,
+ 'Whatsapp' => 33764,
+ 'Xbox Live' => 33766,
+ 'Yahoo Mail' => 38885,
+ 'Yandex' => 33777,
+ 'Yapı Kredi' => 33782,
+ 'Youtube' => 33767,
+ 'Ziraat Bankası' => 33787,
+ ),
+ 'Suomi' => array(
+ 'Aktia' => 36308,
+ 'Apex Legends' => 38123,
+ 'Battlefield' => 37096,
+ 'Binance' => 36929,
+ 'Blizzard Battle.net' => 36299,
+ 'Call of Duty' => 36706,
+ 'Clash Royale' => 38947,
+ 'Counter-strike' => 36645,
+ 'Crunchyroll' => 38097,
+ 'Dankse Bank' => 36307,
+ 'Dead By Daylight' => 37428,
+ 'Destiny' => 38531,
+ 'Discord' => 36681,
+ 'DNA' => 36304,
+ 'Dota 2' => 36434,
+ 'EA' => 36295,
+ 'Elisa' => 36303,
+ 'Epic Games Store' => 39030,
+ 'Escape from Tarkov' => 39057,
+ 'Facebook' => 36237,
+ 'Facebook Messenger' => 36240,
+ 'Faceit' => 39038,
+ 'Fifa' => 37603,
+ 'Fortnite' => 36691,
+ 'Ghost Recon' => 38998,
+ 'Gmail' => 36254,
+ 'Google' => 36253,
+ 'Google Play' => 38522,
+ 'Gran Turismo' => 36912,
+ 'GTA 5' => 36844,
+ 'Guild Wars 2' => 36471,
+ 'HBO Nordic' => 36302,
+ 'iCloud' => 38841,
+ 'Instagram' => 36435,
+ 'Kraken' => 36852,
+ 'League of Legends' => 38632,
+ 'Minecraft' => 38616,
+ 'Netflix' => 36252,
+ 'Nordea' => 36306,
+ 'Office 365' => 36297,
+ 'OP' => 36305,
+ 'Origin' => 36904,
+ 'Outlook' => 36251,
+ 'Overwatch' => 36250,
+ 'Path of Exile' => 36530,
+ 'Paypal' => 37347,
+ 'Playerunknown\'s Battlegrounds' => 36339,
+ 'Playstation Network' => 36249,
+ 'Pokémon Go' => 36248,
+ 'Rainbow Six' => 36247,
+ 'Reddit' => 36823,
+ 'Roblox' => 38913,
+ 'Rocket League' => 36300,
+ 'Runescape' => 38655,
+ 'Sea of Thieves' => 37118,
+ 'Skype' => 36246,
+ 'Slack' => 37496,
+ 'Snapchat' => 36245,
+ 'Spotify' => 37078,
+ 'Steam' => 36296,
+ 'Telegram' => 37091,
+ 'Telia' => 36287,
+ 'Tinder' => 36298,
+ 'Twitch' => 36436,
+ 'Twitter' => 36677,
+ 'Uplay PC' => 36244,
+ 'Viaplay' => 37294,
+ 'Viber' => 37494,
+ 'Warframe' => 36704,
+ 'Whatsapp' => 36243,
+ 'Wikipedia' => 38871,
+ 'World of Tanks' => 36673,
+ 'Xbox Live' => 36301,
+ 'Yahoo Mail' => 38883,
+ 'Youtube' => 36242,
+ 'Z1 Battle Royale' => 37259,
+ ),
+ 'France' => array(
+ 'Albion Online' => 38796,
+ 'Alexa' => 38030,
+ 'Alice' => 34009,
+ 'Amazon' => 32693,
+ 'Amazon Web Services' => 38109,
+ 'Ameli' => 38705,
+ 'Amen' => 35888,
+ 'Anthem' => 38207,
+ 'Apex Legends' => 38116,
+ 'App Store' => 35582,
+ 'Apple Store' => 34469,
+ 'AXA Banque' => 33427,
+ 'Bankin\'' => 37592,
+ 'Banque Populaire' => 33432,
+ 'Battlefield' => 38057,
+ 'Betclic' => 39046,
+ 'Binance' => 36939,
+ 'Bing' => 32696,
+ 'BlaBlaCar' => 36089,
+ 'Blizzard Battle.net' => 36437,
+ 'Blogger' => 32697,
+ 'BNP Paribas' => 32749,
+ 'Boom Beach' => 35154,
+ 'Bouygues Télécom' => 32747,
+ 'Brawl Stars' => 38815,
+ 'Caisse d\'allocations familiales' => 38708,
+ 'Caisse d\'Epargne' => 33428,
+ 'Call of Duty' => 35037,
+ 'CanalSat' => 34034,
+ 'Candy Crush' => 35881,
+ 'Cdiscount' => 35038,
+ 'CIC' => 33431,
+ 'Clash Royale' => 38151,
+ 'Cloudflare' => 38622,
+ 'Coinbase' => 36830,
+ 'Completel' => 33982,
+ 'Compte Nickel' => 35983,
+ 'Counter-Strike' => 36649,
+ 'Crédit Agricole' => 32753,
+ 'Crédit Mutuel' => 33429,
+ 'Crunchyroll' => 38096,
+ 'Dartybox' => 32848,
+ 'Dead By Daylight' => 37425,
+ 'Deezer' => 32843,
+ 'Destiny' => 34951,
+ 'Discord' => 36763,
+ 'Dofus Touch' => 38711,
+ 'Dota 2' => 35877,
+ 'Dropbox' => 32700,
+ 'Duel Quiz' => 34530,
+ 'EA' => 32844,
+ 'eBay' => 32701,
+ 'Elite: Dangerous' => 37776,
+ 'Epic Games Store' => 39029,
+ 'Escape from Tarkov' => 39058,
+ 'Facebook' => 32702,
+ 'Facebook Messenger' => 32703,
+ 'Facetime' => 34602,
+ 'Fallout' => 37667,
+ 'Fifa' => 35475,
+ 'Flickr' => 32704,
+ 'Fnac' => 35065,
+ 'For Honor' => 35995,
+ 'Fortnite' => 36656,
+ 'Free' => 32744,
+ 'Gandi' => 38712,
+ 'Ghost Recon' => 38959,
+ 'GitHub' => 32705,
+ 'Gmail' => 32706,
+ 'Google' => 32707,
+ 'Google Agenda' => 38601,
+ 'Google Drive' => 36608,
+ 'Google Hangouts' => 32709,
+ 'Google Play' => 32708,
+ 'Gran Turismo' => 36911,
+ 'Grindr' => 36756,
+ 'GTA 5' => 35039,
+ 'Guild Wars 2' => 36468,
+ 'Hearthstone' => 38562,
+ 'Hootsuite' => 32710,
+ 'Hue' => 37815,
+ 'Hunt: showdown' => 38824,
+ 'iCloud' => 32712,
+ 'Idealo' => 34890,
+ 'iMessage' => 32713,
+ 'ING Direct' => 33426,
+ 'Instagram' => 32715,
+ 'iTunes' => 32716,
+ 'JeuxVidéo' => 32847,
+ 'K-net' => 37790,
+ 'Kraken' => 36753,
+ 'La Banque Postale' => 32754,
+ 'La Poste' => 37215,
+ 'La Poste Mobile' => 32752,
+ 'LCL (Crédit Lyonnais)' => 33424,
+ 'Le Bon Coin' => 32840,
+ 'League of Legends' => 35369,
+ 'LinkedIn' => 32717,
+ 'LycaMobile' => 32849,
+ 'M6 Mobile' => 32845,
+ 'Magic' => 37456,
+ 'Météo France' => 38706,
+ 'Microsoft Azure' => 36115,
+ 'Minecraft' => 32718,
+ 'Molotov.TV' => 35965,
+ 'myCanal' => 38710,
+ 'Netatmo' => 37957,
+ 'Netflix' => 34633,
+ 'Nintendo eShop' => 34103,
+ 'Nordnet' => 38709,
+ 'NRJ Mobile' => 32846,
+ 'Numéricable' => 32748,
+ 'OCS' => 38707,
+ 'Office 365' => 35126,
+ 'Onedrive' => 35409,
+ 'Online.net' => 36159,
+ 'Orange' => 32745,
+ 'Orange Bank' => 37511,
+ 'Origin' => 36896,
+ 'Outlook' => 32711,
+ 'Overwatch' => 36151,
+ 'OVH' => 34744,
+ 'Paladins' => 36378,
+ 'Path of Exile' => 36855,
+ 'Paypal' => 32842,
+ 'Photobucket' => 32721,
+ 'Pinterest' => 32722,
+ 'Player Unknown\'s Battlegrounds' => 36363,
+ 'Playstation Network' => 32741,
+ 'Pokémon Go' => 35735,
+ 'Prime Video' => 38457,
+ 'Rainbow Six' => 35562,
+ 'Rakuten TV' => 37596,
+ 'Realm Royale' => 37308,
+ 'Red Dead Redemption' => 38287,
+ 'Reddit' => 37368,
+ 'Roblox' => 38755,
+ 'Rocket League' => 35253,
+ 'Salesforce' => 38501,
+ 'Sea of Thieves' => 37117,
+ 'SFR' => 32746,
+ 'Shadow' => 37212,
+ 'Skype' => 32723,
+ 'Skyrock.com' => 33430,
+ 'Slack' => 37337,
+ 'Slideshare' => 32724,
+ 'Snapchat' => 33378,
+ 'Société Générale' => 33425,
+ 'Sosh' => 34830,
+ 'Spotify' => 32727,
+ 'Star Citizen' => 39037,
+ 'Steam' => 32742,
+ 'Streamlabs' => 38927,
+ 'Syma' => 37449,
+ 'Tango' => 34482,
+ 'Teamviewer' => 34908,
+ 'Telegram' => 34230,
+ 'The elder scrolls online' => 37326,
+ 'Tinder' => 34808,
+ 'Tumblr' => 32728,
+ 'TuneIn' => 38758,
+ 'Tweakbox' => 38347,
+ 'Twitch' => 35027,
+ 'Twitter' => 32729,
+ 'Uber Eats' => 38897,
+ 'Unibet' => 38274,
+ 'Uplay PC' => 35544,
+ 'Viber' => 33373,
+ 'Vimeo' => 32730,
+ 'Warface' => 38776,
+ 'Warframe' => 36368,
+ 'Waze' => 33410,
+ 'WeTransfer' => 32731,
+ 'Whatsapp' => 32575,
+ 'Wibox' => 37055,
+ 'Wikipedia' => 32732,
+ 'Wordpress.com' => 32734,
+ 'World of Tanks' => 36030,
+ 'Xbox Live' => 32743,
+ 'Yahoo' => 32735,
+ 'Yahoo Mail' => 32736,
+ 'Yahoo Messenger' => 32737,
+ 'Youtube' => 32739,
+ 'Z1 Battle Royale' => 37260,
+ 'Zynga' => 32740,
+ ),
+ 'Brasil' => array(
+ 'Albion Online' => 38797,
+ 'Alelo' => 37410,
+ 'Algar' => 37507,
+ 'Alog' => 34746,
+ 'Amazon' => 33206,
+ 'Amazon Prime Video' => 38754,
+ 'Amazon Web Services' => 34091,
+ 'America Net' => 35127,
+ 'Anthem' => 38211,
+ 'Anydesk' => 37492,
+ 'Apex Legends' => 38126,
+ 'App Store' => 39054,
+ 'Apple Store' => 34449,
+ 'Avianca' => 36519,
+ 'Azul' => 36358,
+ 'Banco Central do Brasil' => 36002,
+ 'Banco do Brasil' => 34037,
+ 'Banco Inter' => 37559,
+ 'Banco Itaú' => 33205,
+ 'Banco Safra' => 37151,
+ 'Banco Santander' => 33381,
+ 'Banestes' => 37065,
+ 'Banrisul' => 35425,
+ 'Battlefield' => 38447,
+ 'Betfair' => 38188,
+ 'Binance' => 36940,
+ 'Bing' => 33215,
+ 'Blizzard Battle.net' => 36671,
+ 'Bradesco' => 33197,
+ 'Brisanet' => 36967,
+ 'Buscapé' => 35167,
+ 'C6 Bank' => 38773,
+ 'Cabo Telecom' => 33991,
+ 'Cabonnet' => 35919,
+ 'Caixa Econômica Federal' => 33191,
+ 'Call of Duty' => 38163,
+ 'Claro' => 33199,
+ 'Clash of Clans' => 37303,
+ 'Clash Royale' => 36053,
+ 'Clear' => 37557,
+ 'Cloudflare' => 38621,
+ 'Clusterweb' => 35967,
+ 'Copel Telecom' => 35671,
+ 'Correios' => 34611,
+ 'Counter-Strike' => 38162,
+ 'Credit Suisse' => 37978,
+ 'Crunchyroll' => 37138,
+ 'Dataprev' => 35137,
+ 'Dead By Daylight' => 36596,
+ 'Deezer' => 33385,
+ 'Destiny' => 38943,
+ 'Discord' => 37392,
+ 'Dota 2' => 35879,
+ 'Dropbox' => 33214,
+ 'EA' => 34500,
+ 'eBay' => 33207,
+ 'eCAC' => 34961,
+ 'Embratel' => 33999,
+ 'Enem' => 35136,
+ 'Epic Games Store' => 39031,
+ 'eSocial' => 35427,
+ 'Faceapp' => 38696,
+ 'Facebook' => 33184,
+ 'Facebook Messenger' => 34846,
+ 'Facetime' => 34597,
+ 'Feedly' => 34723,
+ 'Fifa' => 37769,
+ 'For Honor' => 37371,
+ 'Fortnite' => 36699,
+ 'Free Fire' => 38804,
+ 'Garena' => 38301,
+ 'Getnet' => 36745,
+ 'GitHub' => 38591,
+ 'Globo' => 34721,
+ 'Gmail' => 33194,
+ 'Go Daddy' => 34848,
+ 'Gol' => 38600,
+ 'Google' => 33186,
+ 'Google Cloud' => 38535,
+ 'Google Play' => 33211,
+ 'GTA 5' => 34757,
+ 'GVT' => 33562,
+ 'HBO' => 36354,
+ 'Hostgator' => 34745,
+ 'Hostnet' => 35850,
+ 'HSBC' => 34644,
+ 'iCloud' => 33219,
+ 'ICQ' => 34765,
+ 'iFood ' => 36960,
+ 'iMessage' => 33165,
+ 'Instagram' => 33204,
+ 'ITMNetworks' => 36094,
+ 'iTunes' => 33208,
+ 'Jurassic World Alive' => 37335,
+ 'Kik' => 34856,
+ 'KingHost' => 34957,
+ 'Kraken' => 36979,
+ 'League of Legends' => 39018,
+ 'Ligue Telecom' => 37526,
+ 'Line' => 33736,
+ 'LinkedIn' => 33210,
+ 'Locaweb' => 34722,
+ 'Mandic' => 36090,
+ 'Mercado Bitcoin' => 36790,
+ 'Mercado Livre' => 34234,
+ 'Microsoft Azure' => 36111,
+ 'Multiplay' => 37221,
+ 'NET' => 33190,
+ 'Netflix' => 33222,
+ 'Neverwinter' => 36158,
+ 'Nextel' => 33202,
+ 'Nota fiscal eletrônica' => 34718,
+ 'Nubank' => 37063,
+ 'Office 365' => 37170,
+ 'Oi' => 33196,
+ 'OLX' => 34235,
+ 'OneDrive' => 36866,
+ 'Origin' => 36903,
+ 'Outlook' => 33221,
+ 'Overwatch' => 36502,
+ 'PagSeguro' => 37313,
+ 'Path of Exile' => 37725,
+ 'Paypal' => 33213,
+ 'Pinterest' => 38164,
+ 'Player Unknown\'s Battlegrounds' => 36565,
+ 'Playstation Network' => 33217,
+ 'Pokémon Go' => 35751,
+ 'Polícia Federal' => 35135,
+ 'Porto Seguro Conecta' => 35981,
+ 'QConcursos' => 36601,
+ 'Rainbow Six' => 36451,
+ 'Receita Federal' => 34960,
+ 'Red Dead Redemption' => 38909,
+ 'Reddit' => 37477,
+ 'Roblox' => 37399,
+ 'Rocket League' => 38558,
+ 'Salesforce' => 38494,
+ 'Sefaz' => 37626,
+ 'Sercomtel' => 33990,
+ 'Sicoob' => 37624,
+ 'Sicredi' => 37505,
+ 'SiSU' => 38029,
+ 'SKY' => 33875,
+ 'Skype' => 33203,
+ 'Slack' => 37339,
+ 'Snapchat' => 33379,
+ 'Spotify' => 36594,
+ 'Steam' => 34137,
+ 'Submarino' => 35074,
+ 'SuperDigital' => 36744,
+ 'Teamviewer' => 35690,
+ 'Telegram' => 34937,
+ 'Terra' => 33193,
+ 'TIM' => 34686,
+ 'Tinder' => 36550,
+ 'Tribunal Superior Eleitoral' => 37578,
+ 'Twitch' => 36372,
+ 'Twitter' => 33195,
+ 'Uber' => 37767,
+ 'Udemy' => 38313,
+ 'Umbler' => 37577,
+ 'UOL' => 33189,
+ 'UOLHost' => 34748,
+ 'Uplay PC' => 38636,
+ 'Viber' => 33386,
+ 'Vimeo' => 38533,
+ 'Vivo' => 33192,
+ 'Vono' => 35138,
+ 'Warframe' => 36702,
+ 'Waze' => 33383,
+ 'WeChat' => 33735,
+ 'Whatsapp' => 32837,
+ 'Wikipedia' => 33198,
+ 'World of Warcraft' => 38879,
+ 'Xbox Live' => 33216,
+ 'Yahoo' => 33187,
+ 'Yahoo Mail' => 33201,
+ 'Youtube' => 33185,
+ 'Zello' => 33387,
+ ),
+ 'Россия' => array(
+ 'Albion Online' => 38798,
+ 'Anthem' => 38191,
+ 'Apex Legends' => 38138,
+ 'Apple Music' => 39012,
+ 'Apple Store' => 34470,
+ 'avito' => 37696,
+ 'Battlefield' => 38448,
+ 'Binance' => 37021,
+ 'Blizzard Battle.net' => 36784,
+ 'Call of Duty' => 39052,
+ 'Citilink' => 37700,
+ 'Cloudflare' => 38619,
+ 'Counter-strike' => 38234,
+ 'Dead By Daylight' => 37430,
+ 'Destiny' => 38941,
+ 'DNS Shop' => 37701,
+ 'Dota 2' => 36395,
+ 'EA' => 36924,
+ 'eBay' => 32799,
+ 'Elite: Dangerous' => 37775,
+ 'Facetime' => 34599,
+ 'Fifa' => 38428,
+ 'For Honor' => 37370,
+ 'Fortnite' => 37143,
+ 'GitHub' => 38713,
+ 'Gmail' => 32798,
+ 'GTA 5' => 36845,
+ 'iCloud' => 38840,
+ 'ICQ' => 33122,
+ 'iMessage' => 33115,
+ 'Interzet' => 33563,
+ 'iTunes' => 33114,
+ 'Ivi' => 34807,
+ 'Kraken' => 36982,
+ 'Last FM' => 36533,
+ 'Mail.Ru' => 33113,
+ 'Megogo' => 38198,
+ 'NetByNet' => 35927,
+ 'Okko' => 38793,
+ 'Origin' => 36900,
+ 'Outlook' => 33116,
+ 'Overwatch' => 36385,
+ 'Ozon' => 37702,
+ 'Path of Exile' => 37724,
+ 'Paypal' => 33118,
+ 'Player Unknown\'s Battlegrounds' => 36455,
+ 'Playstation Network' => 32801,
+ 'Qip' => 37697,
+ 'Qiwi' => 33504,
+ 'Rainbow Six' => 36457,
+ 'Reddit' => 38066,
+ 'Roblox' => 38922,
+ 'SkyNet' => 35103,
+ 'Slack' => 37340,
+ 'Snapchat' => 37144,
+ 'Teamviewer' => 34347,
+ 'Telegram' => 36734,
+ 'The elder scrolls online' => 37323,
+ 'Tumblr' => 37284,
+ 'Twitter' => 32797,
+ 'Uplay PC' => 35611,
+ 'Viber ' => 33402,
+ 'Warframe' => 37126,
+ 'WebMoney' => 33119,
+ 'Wildberries' => 37699,
+ 'World of Tanks' => 35051,
+ 'Xbox Live' => 32802,
+ 'Yahoo' => 32792,
+ 'Yahoo Mail' => 32793,
+ 'Yahoo Messenger' => 32794,
+ 'Yota' => 34356,
+ 'Акадо' => 34429,
+ 'Альфа-Банк' => 33110,
+ 'Банк ДОМ.РФ' => 38704,
+ 'Билайн' => 32850,
+ 'Ватсап' => 32803,
+ 'Википедия' => 33120,
+ 'ВКонтакте' => 32851,
+ 'ВТБ 24' => 33109,
+ 'ГИС ЖКХ' => 35822,
+ 'Гугл' => 32788,
+ 'Гугл Hangouts' => 32789,
+ 'Гугл Плей' => 32790,
+ 'Дискорд ' => 37386,
+ 'Дом.ru' => 34395,
+ 'Инстаграм' => 36069,
+ 'Кинопаб' => 38199,
+ 'Летай' => 34430,
+ 'МГТС' => 33108,
+ 'МегаФон' => 33104,
+ 'Мотив' => 33864,
+ 'МТС' => 33121,
+ 'НСПК ' => 35608,
+ 'Одноклассники' => 33112,
+ 'Почта России' => 36587,
+ 'Рамблер' => 37441,
+ 'Релком' => 34357,
+ 'Росреестр' => 38311,
+ 'Ростелеком' => 33105,
+ 'Сбербанк' => 33106,
+ 'СДЭК' => 38729,
+ 'Скай Линк' => 34358,
+ 'Скайп' => 32804,
+ 'Стим' => 34266,
+ 'Твич' => 36543,
+ 'Теле2' => 33818,
+ 'Тиндер' => 37919,
+ 'Тинькофф Банк ' => 37698,
+ 'ТТК' => 34359,
+ 'Уфанет' => 34431,
+ 'Фейсбук' => 32795,
+ 'Фейсбук Мессенджер' => 32796,
+ 'ФНС' => 38648,
+ 'Хоум Кредит' => 38627,
+ 'ЭлЖур ' => 37515,
+ 'Ютуб' => 32791,
+ 'Яндекс' => 33111,
+ 'Яндекс.Музыка' => 39013,
+ 'Яндекс.Навигатор' => 39014,
+ ),
+ 'España' => array(
+ 'Alexa' => 38032,
+ 'Amazon' => 33140,
+ 'Amazon Prime Video' => 38753,
+ 'Anthem' => 38201,
+ 'Anydesk' => 37490,
+ 'Apex Legends' => 38122,
+ 'App Store' => 38553,
+ 'Apple Store' => 34465,
+ 'Banco Popular' => 33135,
+ 'Banco Sabadell' => 33151,
+ 'Banco Santander' => 33132,
+ 'Bankia' => 33134,
+ 'Bankinter' => 33143,
+ 'Battlefield' => 39017,
+ 'BBVA' => 33133,
+ 'Binance' => 36936,
+ 'Blizzard Battle.net' => 36105,
+ 'Brawl Stars' => 38832,
+ 'Cableworld' => 37457,
+ 'Call of Duty' => 35538,
+ 'Clash of Clans' => 34362,
+ 'Clash Royale' => 38951,
+ 'Coinbase' => 36803,
+ 'Counter Strike' => 38239,
+ 'Crunchyroll' => 38147,
+ 'Dazn' => 39005,
+ 'Dead By Daylight' => 36597,
+ 'Deezer' => 33830,
+ 'Destiny' => 38944,
+ 'Discord' => 37387,
+ 'Dota 2' => 37188,
+ 'Dropbox' => 33142,
+ 'EA' => 36449,
+ 'eBay' => 33267,
+ 'Epic Games Store' => 39032,
+ 'Euskaltel' => 34442,
+ 'Facebook' => 32829,
+ 'Facebook Messenger' => 32830,
+ 'Facetime' => 34600,
+ 'Fifa' => 37088,
+ 'For Honor' => 36182,
+ 'Fortnite' => 36694,
+ 'Ghost Recon' => 38996,
+ 'Github' => 36521,
+ 'Gmail' => 32832,
+ 'Google' => 32822,
+ 'Google Drive' => 36605,
+ 'Google Hangouts' => 32823,
+ 'Google Play' => 33226,
+ 'Gran Turismo' => 36950,
+ 'Grindr' => 38401,
+ 'GTA 5' => 37274,
+ 'Guild Wars 2' => 36485,
+ 'HBO' => 36497,
+ 'Ibercaja' => 33153,
+ 'iCloud' => 33227,
+ 'iMessage' => 32980,
+ 'ING Direct' => 33148,
+ 'Instagram' => 33147,
+ 'iTunes' => 33144,
+ 'Jazztel' => 33129,
+ 'Kraken' => 36806,
+ 'League of Legends' => 38634,
+ 'Line' => 33394,
+ 'LinkedIn' => 33150,
+ 'Llamaya' => 38315,
+ 'Lowi' => 35832,
+ 'MásMóvil' => 36068,
+ 'Mil Anuncios' => 33141,
+ 'Movistar' => 33124,
+ 'Nest' => 38551,
+ 'Netflix' => 35565,
+ 'Office 365' => 35442,
+ 'OneDrive' => 36864,
+ 'ONO' => 33128,
+ 'Orange' => 33126,
+ 'Origin' => 36895,
+ 'Outlook' => 33220,
+ 'Overwatch' => 36387,
+ 'Pasion.com' => 37141,
+ 'Path of Exile' => 37770,
+ 'Paypal' => 33145,
+ 'Pepephone' => 33130,
+ 'Player Unknown\'s Battlegrounds' => 36561,
+ 'Playstation Network' => 32835,
+ 'Pokémon Go' => 35750,
+ 'Quantis' => 37050,
+ 'R' => 34441,
+ 'Rainbow Six' => 36145,
+ 'Rakuten TV' => 37595,
+ 'Reddit' => 37476,
+ 'Roblox' => 38928,
+ 'Rocket League' => 36181,
+ 'Sea of Thieves' => 37119,
+ 'Segunda Mano' => 33138,
+ 'Simyo' => 33131,
+ 'Sky' => 37520,
+ 'Skype' => 32838,
+ 'Slack' => 37342,
+ 'Snapchat' => 33380,
+ 'Spotify' => 33146,
+ 'Steam' => 34280,
+ 'Tango' => 33746,
+ 'Teamviewer' => 35688,
+ 'Telecable' => 34443,
+ 'Telegram' => 35352,
+ 'Tinder' => 36548,
+ 'Tuenti' => 33137,
+ 'Tweakbox' => 38350,
+ 'Twitch' => 36545,
+ 'Twitter' => 32831,
+ 'Unicaja' => 33149,
+ 'Uplay PC' => 35615,
+ 'Vibbo' => 36183,
+ 'Viber' => 33374,
+ 'Vodafone' => 33125,
+ 'Warframe' => 36705,
+ 'Whatsapp' => 33263,
+ 'Wikipedia' => 33139,
+ 'Xbox Live' => 32836,
+ 'Yahoo' => 32826,
+ 'Yahoo Mail' => 32827,
+ 'Yahoo Messenger' => 32828,
+ 'Yoigo' => 33127,
+ 'Youtube' => 32825,
+ 'Z1 Battle Royale' => 37263,
+ ),
+ 'Polska' => array(
+ 'Allegro' => 33799,
+ 'Amazon' => 33633,
+ 'Apex Legends' => 38124,
+ 'Apple Store' => 34453,
+ 'Bank Millennium' => 37535,
+ 'Bank Pekao' => 37534,
+ 'Battlefield' => 37471,
+ 'BGŻ BNP Paribas' => 37530,
+ 'Binance' => 36946,
+ 'Blizzard Battle.net' => 36783,
+ 'Call of Duty' => 38923,
+ 'Chomikuj' => 33803,
+ 'Clash Royale' => 38950,
+ 'Cloudflare' => 38624,
+ 'Counter-Strike' => 36648,
+ 'Cyfrowy Polsat' => 33866,
+ 'Dead By Daylight' => 36599,
+ 'Deezer' => 33837,
+ 'Destiny' => 38945,
+ 'Deutsche bank Polska' => 36176,
+ 'Discord' => 36761,
+ 'Dota 2' => 36447,
+ 'Dropbox' => 33796,
+ 'EA' => 36448,
+ 'East&West' => 35641,
+ 'eBay' => 38579,
+ 'Elite: Dangerous' => 37778,
+ 'Epic Games Store' => 39022,
+ 'Escape from Tarkov' => 39060,
+ 'Eurobank' => 37008,
+ 'Facebook' => 33634,
+ 'Facebook Messenger' => 33635,
+ 'Facetime' => 34591,
+ 'Fifa' => 37086,
+ 'For Honor' => 37305,
+ 'Fortnite' => 36693,
+ 'Get in Bank' => 37532,
+ 'GG (Gadu Gadu)' => 33809,
+ 'Ghost Recon' => 38997,
+ 'GitHub' => 38466,
+ 'Gmail' => 33636,
+ 'Google' => 33637,
+ 'Google Hangouts' => 33638,
+ 'Google Play' => 33639,
+ 'Gran Turismo' => 36916,
+ 'Grindr' => 38741,
+ 'GTA 5' => 36848,
+ 'Guild Wars 2' => 36467,
+ 'Gumtree' => 33805,
+ 'HBO' => 36355,
+ 'Heyah' => 33812,
+ 'Home.pl' => 37537,
+ 'iCloud' => 33640,
+ 'Idea Bank' => 37533,
+ 'iMessage' => 33641,
+ 'Inea' => 34415,
+ 'ING Bank' => 33807,
+ 'Instagram' => 33642,
+ 'iTunes' => 33643,
+ 'Kraken' => 36983,
+ 'League of Legends' => 36629,
+ 'LinkedIn' => 33806,
+ 'Mbank' => 33800,
+ 'Multimedia Polska' => 33879,
+ 'Nazwa' => 37320,
+ 'Nest Bank' => 37529,
+ 'Netflix' => 35728,
+ 'Netia' => 33815,
+ 'NK.pl (Nasza-klasa)' => 33808,
+ 'O2' => 33801,
+ 'Office 365' => 35125,
+ 'OLX' => 33802,
+ 'OneDrive' => 36868,
+ 'Orange' => 33810,
+ 'Origin' => 36899,
+ 'Outlook' => 33644,
+ 'Overwatch' => 36388,
+ 'Path of Exile' => 37728,
+ 'PKO Bank Polski' => 36044,
+ 'Play' => 33813,
+ 'Player Unknown\'s Battlegrounds' => 36580,
+ 'Playstation Network' => 33645,
+ 'Plus' => 33814,
+ 'Pokémon Go' => 35752,
+ 'Rainbow Six' => 36394,
+ 'Reddit' => 37485,
+ 'Roblox' => 37400,
+ 'Rocket League' => 36667,
+ 'Santander' => 37531,
+ 'Sea of Thieves' => 37120,
+ 'Skype' => 33793,
+ 'Slack' => 37338,
+ 'Snapchat' => 33797,
+ 'Spotify' => 33836,
+ 'Steam' => 34279,
+ 'T-Mobile' => 33811,
+ 'Teamviewer' => 35694,
+ 'Telegram' => 35970,
+ 'The elder scrolls online' => 37328,
+ 'Tinder' => 36547,
+ 'Tumblr' => 37285,
+ 'Twitch' => 36450,
+ 'Twitter' => 33646,
+ 'UPC' => 33816,
+ 'Uplay PC' => 35612,
+ 'Vectra' => 33817,
+ 'Viber' => 33794,
+ 'Warframe' => 36493,
+ 'Whatsapp' => 33792,
+ 'Wikipedia' => 33647,
+ 'World of Tanks' => 36029,
+ 'World of Warcraft' => 37446,
+ 'World of Warships' => 38008,
+ 'Xbox Live' => 33648,
+ 'Yahoo Mail' => 38887,
+ 'Youtube' => 33649,
+ 'Z1 Battle Royale' => 37267,
+ ),
+ 'Norge' => array(
+ 'Altibox' => 36318,
+ 'Anthem' => 38206,
+ 'Apex Legends' => 38120,
+ 'App Store' => 38100,
+ 'Battlefield' => 37098,
+ 'Binance' => 36928,
+ 'Blizzard Battle.net' => 36309,
+ 'Call of Duty' => 36708,
+ 'Coinbase' => 36799,
+ 'Counter-strike' => 36647,
+ 'Crunchyroll' => 36720,
+ 'Danske Bank' => 36321,
+ 'Dead By Daylight' => 37426,
+ 'Destiny' => 36592,
+ 'Discord' => 36762,
+ 'Dota 2' => 36442,
+ 'EA' => 36310,
+ 'Epic Games Store' => 39028,
+ 'Facebook' => 36238,
+ 'Facebook Messenger' => 36269,
+ 'Fifa' => 36908,
+ 'Fortnite' => 36687,
+ 'Get' => 36319,
+ 'Ghost Recon' => 39000,
+ 'Gmail' => 36255,
+ 'Google' => 36256,
+ 'Google Drive' => 36760,
+ 'Google Play' => 38528,
+ 'GTA 5' => 36847,
+ 'Guild Wars 2' => 36470,
+ 'HBO Nordic' => 36316,
+ 'iCloud' => 38844,
+ 'Ikano Bank' => 36577,
+ 'Instagram' => 36443,
+ 'Kraken' => 36853,
+ 'League of Legends' => 38630,
+ 'Minecraft' => 37280,
+ 'Netflix' => 36257,
+ 'NexGenTel' => 36320,
+ 'Nordea' => 36325,
+ 'Norges Bank' => 36324,
+ 'Office 365' => 36311,
+ 'OneDrive' => 36865,
+ 'Origin' => 36893,
+ 'Outlook' => 36258,
+ 'Overwatch' => 36259,
+ 'Path of Exile' => 37727,
+ 'Playerunknown\'s Battlegrounds' => 36338,
+ 'Playstation Network' => 36260,
+ 'Pokémon Go' => 36261,
+ 'Rainbow Six' => 36262,
+ 'Realm Royale' => 37316,
+ 'Red Dead Redemption' => 38283,
+ 'Reddit' => 36825,
+ 'Roblox' => 38919,
+ 'Rocket League' => 36312,
+ 'Sea of Thieves' => 37116,
+ 'Skype' => 36263,
+ 'Snapchat' => 36264,
+ 'Sparebanken 1' => 36323,
+ 'Spotify' => 36796,
+ 'Steam' => 36313,
+ 'Strava' => 38730,
+ 'Tele2' => 36317,
+ 'Telegram' => 37183,
+ 'Telenor' => 36288,
+ 'The elder scrolls online' => 37324,
+ 'Tinder' => 36314,
+ 'Twitch' => 36444,
+ 'Twitter' => 37268,
+ 'Uplay PC' => 36265,
+ 'Viaplay' => 37293,
+ 'Warframe' => 37129,
+ 'Whatsapp' => 36266,
+ 'Wikipedia' => 38872,
+ 'World of Warcraft' => 37447,
+ 'Xbox Live' => 36315,
+ 'Yahoo' => 36268,
+ 'Yahoo Mail' => 38890,
+ 'Youtube' => 36267,
+ 'Z1 Battle Royale' => 37262,
+ ),
+ 'México' => array(
+ 'Amazon' => 33292,
+ 'Amazon Prime Video' => 38752,
+ 'Anthem' => 38192,
+ 'Apex Legends' => 38139,
+ 'App Store' => 37732,
+ 'Apple Store' => 34466,
+ 'AT&T' => 35861,
+ 'Axtel' => 34439,
+ 'Banamex' => 33322,
+ 'Banco Santander' => 36772,
+ 'Banorte' => 33326,
+ 'Battlefield' => 38445,
+ 'BBVA Bancomer' => 33321,
+ 'Binance' => 37019,
+ 'Blizzard Battle.net' => 36106,
+ 'Cablecom' => 34035,
+ 'Cablemás' => 34438,
+ 'Call of Duty' => 35540,
+ 'Clash of Clans' => 37099,
+ 'Crunchyroll' => 37139,
+ 'Dead By Daylight' => 37432,
+ 'Deezer' => 33397,
+ 'Destiny' => 38966,
+ 'Discord' => 38606,
+ 'Dropbox' => 33294,
+ 'EA' => 36609,
+ 'eBay' => 33295,
+ 'Facebook' => 33296,
+ 'Facebook Messenger' => 33297,
+ 'Facetime' => 34594,
+ 'Fifa' => 38307,
+ 'Fortnite' => 36842,
+ 'Gears of War' => 38893,
+ 'Gmail' => 33298,
+ 'Go Daddy' => 36620,
+ 'Google' => 33299,
+ 'Google Drive' => 38260,
+ 'Google Hangouts' => 33300,
+ 'Google Play' => 33301,
+ 'Gran Turismo' => 36951,
+ 'Grindr' => 38740,
+ 'GTA 5' => 38275,
+ 'HBO' => 36351,
+ 'iCloud' => 33302,
+ 'iMessage' => 33303,
+ 'Instagram' => 33304,
+ 'iTunes' => 33305,
+ 'Izzi' => 37685,
+ 'League of Legends' => 39019,
+ 'Line' => 33395,
+ 'LinkedIn' => 33306,
+ 'Megacable' => 34437,
+ 'Mercado Libre' => 33320,
+ 'Microsoft Azure' => 37466,
+ 'Minecraft' => 38737,
+ 'Movistar' => 33307,
+ 'Netflix' => 33325,
+ 'Origin' => 38103,
+ 'Outlook' => 33308,
+ 'Overwatch' => 36483,
+ 'Paypal' => 33309,
+ 'Player Unknown\'s Battlegrounds' => 36562,
+ 'Playstation Network' => 33310,
+ 'Pokémon Go' => 36360,
+ 'Rainbow Six' => 36146,
+ 'Reddit' => 37482,
+ 'Roblox' => 38917,
+ 'Salesforce' => 38496,
+ 'Scotiabank' => 37686,
+ 'Segunda Mano' => 33327,
+ 'SKY México' => 33328,
+ 'Skype' => 33311,
+ 'Snapchat' => 34288,
+ 'SPEI' => 37689,
+ 'Spotify' => 33398,
+ 'Steam' => 34281,
+ 'Tango' => 33745,
+ 'Teamviewer' => 35689,
+ 'Telcel' => 33319,
+ 'Telegram' => 35852,
+ 'Telmex' => 33324,
+ 'Telnor' => 34440,
+ 'Tinder' => 37165,
+ 'Totalplay' => 37687,
+ 'Tweakbox' => 38903,
+ 'Twitch' => 36546,
+ 'Twitter' => 33312,
+ 'Unefon' => 37688,
+ 'WeChat' => 33399,
+ 'Whatsapp' => 33313,
+ 'Wikipedia' => 33314,
+ 'World of Warcraft' => 38880,
+ 'Xbox Live' => 33315,
+ 'Yahoo' => 33316,
+ 'Yahoo Mail' => 33317,
+ 'Yahoo Messenger' => 33318,
+ 'Youtube' => 33361,
+ 'Zello' => 33388,
+ ),
+ '日本' => array(
+ 'Amazon' => 33464,
+ 'Amazon Web Services' => 34094,
+ 'Amazon インスタント・ビデオ' => 34496,
+ 'Apex Legends' => 38177,
+ 'App Store' => 35581,
+ 'Apple Store' => 34467,
+ 'ASAHI ネット' => 33859,
+ 'Au' => 33465,
+ 'Biglobe' => 33479,
+ 'Blizzard Battle.net' => 36672,
+ 'bmobile' => 33466,
+ 'Call of Duty' => 39053,
+ 'Dazn' => 36511,
+ 'Discord' => 38613,
+ 'Disney Mobile' => 33467,
+ 'DMM' => 35818,
+ 'Dropbox' => 33441,
+ 'EA' => 37080,
+ 'eBay' => 33442,
+ 'EO Net' => 33863,
+ 'Facebook' => 33443,
+ 'Facebook Messenger' => 33444,
+ 'Facetime' => 34593,
+ 'FC2' => 34510,
+ 'Fortnite' => 37162,
+ 'Freetel' => 35620,
+ 'Ghost Recon' => 38993,
+ 'Github' => 37375,
+ 'Gmail' => 33445,
+ 'Google' => 33446,
+ 'Google Hangouts' => 33447,
+ 'Google Play' => 33448,
+ 'Hulu' => 33480,
+ 'iCloud' => 33449,
+ 'IIJ' => 34511,
+ 'iMessage' => 33450,
+ 'Instagram' => 33451,
+ 'iTunes' => 33452,
+ 'Jcom' => 33478,
+ 'League of Legends' => 38899,
+ 'Line' => 33453,
+ 'LinkedIn' => 33454,
+ 'Livedoor' => 33472,
+ 'Microsoft Azure' => 36110,
+ 'Netflix' => 36728,
+ 'Nifty' => 33473,
+ 'Nintendo Network' => 35521,
+ 'NTT Docomo' => 33474,
+ 'NTT東日本' => 33475,
+ 'NTT西日本' => 33476,
+ 'OCN' => 33564,
+ 'Office 365' => 35928,
+ 'OneDrive' => 36496,
+ 'Outlook' => 33455,
+ 'Paypal' => 33456,
+ 'Player Unknown\'s Battlegrounds' => 36616,
+ 'Playstation Network' => 33457,
+ 'Rakuten' => 34513,
+ 'Reddit' => 37478,
+ 'Skype' => 33458,
+ 'Slack' => 37358,
+ 'Snapchat' => 38511,
+ 'So-net' => 33862,
+ 'Steam' => 38649,
+ 'Telegram' => 37227,
+ 'Tinder' => 37916,
+ 'Twitter' => 33459,
+ 'UQ Wimax' => 33482,
+ 'Whatsapp' => 38255,
+ 'Xbox Live' => 33471,
+ 'Yahoo' => 33460,
+ 'Yahoo BB' => 33860,
+ 'Yahoo Mail' => 33461,
+ 'Yammer' => 35560,
+ 'Youtube' => 33462,
+ 'じぶん銀行' => 33500,
+ 'ぷらら' => 33861,
+ 'みずほ銀行' => 33493,
+ 'りそな銀行' => 33496,
+ 'アメーバブログ' => 34514,
+ 'カカオトーク' => 33469,
+ 'ジャパンネット銀行' => 33497,
+ 'スカパー' => 33477,
+ 'スポナビLive' => 36516,
+ 'ソニー銀行' => 33498,
+ 'ソフトバンク' => 33470,
+ 'ポケモン go' => 35746,
+ 'ワイモバイル' => 36503,
+ '三井住友銀行' => 33495,
+ '三菱東京UFJ銀行' => 33494,
+ '埼玉りそな銀行' => 36532,
+ '楽天銀行' => 33499,
+ '近畿大阪銀行' => 36531,
+ ),
+ 'Pilipinas' => array(
+ 'Anthem' => 38193,
+ 'Apex Legends' => 38158,
+ 'Battlefield' => 37925,
+ 'Blizzard Battle.net' => 37834,
+ 'Converge' => 38714,
+ 'Counter-strike' => 38241,
+ 'Destiny' => 38987,
+ 'Discord' => 38453,
+ 'Dota 2' => 37835,
+ 'EA' => 37931,
+ 'Facebook' => 37836,
+ 'Facebook Messenger' => 37837,
+ 'Fortnite' => 37930,
+ 'Globe' => 37941,
+ 'Gmail' => 38080,
+ 'Google' => 37838,
+ 'Google Drive' => 38259,
+ 'Google Hangouts' => 38365,
+ 'Google Play' => 38521,
+ 'GTA 5' => 38718,
+ 'iCloud' => 38853,
+ 'Instagram' => 37839,
+ 'League of Legends' => 38911,
+ 'Mobile legends' => 38175,
+ 'Netflix' => 37840,
+ 'Office 365' => 37936,
+ 'Origin' => 38397,
+ 'Outlook' => 37935,
+ 'Overwatch' => 37932,
+ 'Paypal' => 38463,
+ 'Player Unknown\'s Battlegrounds' => 37841,
+ 'Playstation Network' => 38736,
+ 'PLDT' => 37938,
+ 'Rainbow Six' => 37933,
+ 'Reddit' => 38089,
+ 'Roblox' => 37927,
+ 'Sky Cable' => 37942,
+ 'Skype' => 37929,
+ 'Smart' => 37940,
+ 'Spotify' => 38644,
+ 'Steam' => 37924,
+ 'Telegram' => 38019,
+ 'Tinder' => 37912,
+ 'TNT' => 37943,
+ 'Twitter' => 37928,
+ 'Warframe' => 37934,
+ 'Wattpad' => 38962,
+ 'Waze' => 38267,
+ 'Whatsapp' => 38254,
+ 'Yahoo' => 37926,
+ 'Yahoo Mail' => 38456,
+ 'Youtube' => 37842,
+ ),
+ 'Indonesia' => array(
+ 'Anthem' => 38194,
+ 'Apex Legends' => 38181,
+ 'Biznet' => 38975,
+ 'Blizzard Battle.net' => 37825,
+ 'Clash of Clans' => 37961,
+ 'Counter-strike' => 38410,
+ 'Discord' => 38084,
+ 'Dota 2' => 37826,
+ 'EA' => 38419,
+ 'Facebook' => 37827,
+ 'Fifa' => 38427,
+ 'First Media' => 38974,
+ 'Fortnite' => 38532,
+ 'Gmail' => 38250,
+ 'Google' => 37828,
+ 'Google Drive' => 38262,
+ 'Google Play' => 38518,
+ 'GTA 5' => 38700,
+ 'iCloud' => 38851,
+ 'Indosat Ooredoo' => 38978,
+ 'Instagram' => 37829,
+ 'Myrepublic' => 38972,
+ 'Netflix' => 37830,
+ 'Origin' => 38376,
+ 'Pinterest' => 37950,
+ 'Player Unknown\'s Battlegrounds' => 37831,
+ 'Playstation Network' => 38735,
+ 'Reddit' => 38498,
+ 'Roblox' => 38914,
+ 'Steam' => 38098,
+ 'Telegram' => 38020,
+ 'Telkom' => 38973,
+ 'Telkomsel' => 38976,
+ 'Tinder' => 37915,
+ 'Twitter' => 38160,
+ 'Uplay PC' => 37962,
+ 'Whatsapp' => 37832,
+ 'XL' => 38977,
+ 'Yahoo Mail' => 38889,
+ 'Youtube' => 37833,
+ ),
+ 'Pakistan' => array(
+ 'Apex Legends' => 38414,
+ 'App Store' => 38960,
+ 'Battlefield' => 38002,
+ 'Blizzard Battle.net' => 37992,
+ 'Call of Duty' => 38939,
+ 'Discord' => 37993,
+ 'Dota 2' => 38476,
+ 'EA' => 38418,
+ 'Facebook' => 37994,
+ 'Facebook Messenger' => 37995,
+ 'Fiberlink' => 38006,
+ 'Fifa' => 38425,
+ 'Fortnite' => 38252,
+ 'Google' => 37996,
+ 'iCloud' => 38715,
+ 'Instagram' => 37997,
+ 'Kik' => 38539,
+ 'Netflix' => 37998,
+ 'Pinterest' => 38679,
+ 'Player Unknown\'s Battlegrounds' => 37999,
+ 'Playstation Network' => 38571,
+ 'PTLC' => 38004,
+ 'Quora' => 38908,
+ 'Snapchat' => 38508,
+ 'Steam' => 38341,
+ 'Tinder' => 38000,
+ 'Tumblr' => 38546,
+ 'Twitter' => 38678,
+ 'Wateen' => 38007,
+ 'Wattpad' => 38544,
+ 'Whatsapp' => 38003,
+ 'Yahoo Mail' => 38882,
+ 'Youtube' => 38001,
+ 'Zong' => 38005,
+ ),
+ 'UAE' => array(
+ 'Apex Legends' => 38159,
+ 'beIN' => 39073,
+ 'Binance' => 37022,
+ 'Blizzard Battle.net' => 36214,
+ 'Botim' => 38393,
+ 'Call of Duty' => 38935,
+ 'Cloudflare' => 38642,
+ 'Counter-strike' => 38269,
+ 'Dead By Daylight' => 37437,
+ 'Discord' => 37773,
+ 'Dota 2' => 36423,
+ 'Du' => 36235,
+ 'EA' => 36883,
+ 'Etisalat' => 36234,
+ 'Facebook' => 36215,
+ 'Facebook Messenger' => 36233,
+ 'Fifa' => 38433,
+ 'For Honor' => 36216,
+ 'Fortnite' => 36996,
+ 'Gmail' => 36217,
+ 'Google' => 36218,
+ 'iCloud' => 38843,
+ 'Instagram' => 36420,
+ 'Netflix' => 36219,
+ 'Office 365' => 38041,
+ 'Origin' => 38422,
+ 'Outlook' => 36220,
+ 'Overwatch' => 36221,
+ 'Player Unknown\'s Battlegrounds' => 36618,
+ 'Playstation Network' => 36222,
+ 'Pokémon Go' => 36223,
+ 'Rainbow Six' => 36224,
+ 'Reddit' => 38499,
+ 'Roblox' => 37222,
+ 'Rocket League' => 36231,
+ 'Skype' => 36225,
+ 'Snapchat' => 36226,
+ 'Steam' => 38340,
+ 'Telegram' => 37185,
+ 'Tinder' => 36227,
+ 'Twitter' => 38677,
+ 'Uplay PC' => 36228,
+ 'Whatsapp' => 36229,
+ 'Wikipedia' => 38878,
+ 'Xbox Live' => 38404,
+ 'Yahoo' => 36232,
+ 'Yahoo Mail' => 38888,
+ 'Youtube' => 36230,
+ 'ZoHo' => 37553,
+ ),
+ 'Malaysia' => array(
+ 'Apex Legends' => 38179,
+ 'Blizzard Battle.net' => 37891,
+ 'Counter-strike' => 38409,
+ 'Destiny' => 38985,
+ 'Discord' => 37976,
+ 'Dota 2' => 38017,
+ 'EA' => 38420,
+ 'Facebook' => 37892,
+ 'Facebook Messenger' => 37893,
+ 'Fifa' => 38429,
+ 'Gmail' => 38251,
+ 'Google' => 37894,
+ 'Google Drive' => 38263,
+ 'Google Play' => 38529,
+ 'GTA 5' => 38488,
+ 'iCloud' => 38850,
+ 'Instagram' => 37895,
+ 'League of Legends' => 38910,
+ 'Line' => 38386,
+ 'LinkedIn' => 38150,
+ 'Maxis' => 37964,
+ 'Netflix' => 37896,
+ 'Origin' => 38395,
+ 'Overwatch' => 38895,
+ 'Paypal' => 38461,
+ 'Player Unknown\'s Battlegrounds' => 37897,
+ 'Playstation Network' => 38570,
+ 'Reddit' => 38363,
+ 'Roblox' => 38912,
+ 'Snapchat' => 38548,
+ 'Steam' => 38081,
+ 'TIME' => 37965,
+ 'Tinder' => 37917,
+ 'Twitter' => 38481,
+ 'Unifi' => 37963,
+ 'Uplay PC' => 38896,
+ 'Waze' => 38268,
+ 'Whatsapp' => 38028,
+ 'Yahoo Mail' => 38891,
+ 'Youtube' => 37898,
+ ),
+ 'Perú' => array(
+ 'Apex Legends' => 39015,
+ 'Bitel' => 37982,
+ 'Claro' => 37981,
+ 'Crunchyroll' => 38550,
+ 'Dota 2' => 38353,
+ 'EA' => 38415,
+ 'Econocable' => 37983,
+ 'Entel' => 37980,
+ 'Facebook' => 37791,
+ 'Facebook Messenger' => 38258,
+ 'Fortnite' => 37792,
+ 'Gmail' => 37944,
+ 'Google' => 37945,
+ 'HBO' => 38369,
+ 'iCloud' => 38855,
+ 'Instagram' => 37793,
+ 'Movistar' => 37794,
+ 'Netflix' => 38594,
+ 'Origin' => 38423,
+ 'Outlook' => 38581,
+ 'Playstation Network' => 38568,
+ 'Spotify' => 38580,
+ 'Steam' => 38099,
+ 'Telegram' => 38483,
+ 'Tinder' => 37909,
+ 'Twitter' => 38105,
+ 'Whatsapp' => 37795,
+ 'Youtube' => 37796,
+ ),
+ 'Argentina' => array(
+ 'Apex Legends' => 38129,
+ 'Arnet' => 37969,
+ 'Banco Galicia' => 36132,
+ 'Banco Nación' => 36131,
+ 'Banco Provincia' => 36133,
+ 'Banco Santander Río ' => 36130,
+ 'BBVA Francés' => 36134,
+ 'Binance' => 37020,
+ 'Blizzard Battle.net' => 36104,
+ 'Call of Duty' => 36743,
+ 'Claro' => 36100,
+ 'Clash of Clans' => 36128,
+ 'Correo Argentino' => 36999,
+ 'Counter-Strike' => 38247,
+ 'Crunchyroll' => 38091,
+ 'Dead By Daylight' => 37431,
+ 'Destiny' => 38965,
+ 'Discord' => 38614,
+ 'Dota 2' => 37187,
+ 'EA' => 36885,
+ 'Facebook' => 36072,
+ 'Facebook Messenger' => 36073,
+ 'Fibertel' => 36635,
+ 'Fifa' => 38435,
+ 'Flow' => 38309,
+ 'Fortnite' => 37017,
+ 'Free Fire' => 38806,
+ 'Gigared' => 38215,
+ 'Gmail' => 37716,
+ 'Google' => 36074,
+ 'Google Play' => 38520,
+ 'GTA 5' => 38276,
+ 'HBO' => 36352,
+ 'ICBC' => 38646,
+ 'iCloud' => 38858,
+ 'Instagram' => 36075,
+ 'Mercado Libre' => 36076,
+ 'Microsoft Azure' => 37467,
+ 'Movistar' => 36077,
+ 'Netflix' => 36078,
+ 'Nextel' => 36103,
+ 'OLX' => 37045,
+ 'Origin' => 36902,
+ 'Outlook' => 36079,
+ 'Overwatch' => 38774,
+ 'Path of Exile' => 37771,
+ 'Personal' => 37970,
+ 'Player Unknown\'s Battlegrounds' => 36563,
+ 'Playstation Network' => 36080,
+ 'Pokémon Go' => 36294,
+ 'Rainbow Six' => 36144,
+ 'Reddit' => 37483,
+ 'Roblox' => 38929,
+ 'Skype' => 36081,
+ 'Snapchat' => 36082,
+ 'Spotify' => 36107,
+ 'Steam' => 36129,
+ 'Teamviewer' => 36142,
+ 'Telecentro' => 36810,
+ 'Telecom' => 36097,
+ 'Telefónica' => 36099,
+ 'Telegram' => 38485,
+ 'Telered' => 37971,
+ 'Tinder' => 37169,
+ 'Tuenti' => 38628,
+ 'Twitch' => 36544,
+ 'Twitter' => 36109,
+ 'Uplay PC' => 36143,
+ 'Waze' => 37470,
+ 'Whatsapp' => 36083,
+ 'Wikipedia' => 37352,
+ 'World of Warcraft' => 38778,
+ 'Xbox Live' => 37443,
+ 'Yahoo Mail' => 36084,
+ 'Youtube' => 36085,
+ ),
+ 'Slovensko' => array(
+ 'Apex Legends' => 38131,
+ 'Blizzard Battle.net' => 37875,
+ 'Counter-strike' => 38243,
+ 'Discord' => 38612,
+ 'Dota 2' => 38480,
+ 'EA' => 38417,
+ 'Facebook' => 37876,
+ 'Facebook Messenger' => 37877,
+ 'Fifa' => 38426,
+ 'Fortnite' => 38807,
+ 'Google' => 37878,
+ 'GTA 5' => 38279,
+ 'HBO Go' => 38381,
+ 'iCloud' => 38848,
+ 'Instagram' => 37879,
+ 'Netflix' => 37880,
+ 'Origin' => 38374,
+ 'Player Unknown\'s Battlegrounds' => 37881,
+ 'Playstation Network' => 38572,
+ 'Reddit' => 38809,
+ 'Snapchat' => 38513,
+ 'Steam' => 38230,
+ 'Twitter' => 38669,
+ 'Whatsapp' => 38371,
+ 'Wikipedia' => 38877,
+ 'Xbox Live' => 38088,
+ 'Yahoo Mail' => 38881,
+ 'Youtube' => 37882,
+ ),
+ 'Ελλάς' => array(
+ 'Apex Legends' => 38132,
+ 'Bet365' => 38811,
+ 'Blizzard Battle.net' => 37843,
+ 'Clash Royale' => 38949,
+ 'Counter-strike' => 38235,
+ 'Destiny' => 38980,
+ 'Discord' => 38607,
+ 'Dota 2' => 38352,
+ 'EA' => 38416,
+ 'eBay' => 38578,
+ 'Facebook' => 37844,
+ 'Facebook Messenger' => 37845,
+ 'Fifa' => 38434,
+ 'Fortnite' => 38087,
+ 'Gmail' => 38079,
+ 'Google' => 37846,
+ 'Google Play' => 38527,
+ 'GTA 5' => 38701,
+ 'iCloud' => 38846,
+ 'Instagram' => 37847,
+ 'League of Legends' => 38618,
+ 'Netflix' => 37848,
+ 'Office 365' => 38076,
+ 'Origin' => 38375,
+ 'Paypal' => 38462,
+ 'Player Unknown\'s Battlegrounds' => 37849,
+ 'Playstation Network' => 38575,
+ 'Pokémon Go' => 38639,
+ 'Reddit' => 38516,
+ 'Snapchat' => 38505,
+ 'Steam' => 38231,
+ 'Twitch' => 38582,
+ 'Twitter' => 38676,
+ 'Viber' => 38588,
+ 'Whatsapp' => 38373,
+ 'Yahoo Mail' => 38892,
+ 'Youtube' => 37850,
+ 'Βικιπαίδεια' => 38873,
+ ),
+ 'Hrvatska' => array(
+ 'Apex Legends' => 38135,
+ 'Battlefield' => 38442,
+ 'Blizzard Battle.net' => 37859,
+ 'Clash Royale' => 38356,
+ 'Counter-strike' => 38244,
+ 'Destiny' => 39011,
+ 'Discord' => 38610,
+ 'Dota 2' => 38351,
+ 'EA' => 38036,
+ 'Facebook' => 37860,
+ 'Facebook Messenger' => 37861,
+ 'Fifa' => 38438,
+ 'Fortnite' => 38069,
+ 'Google' => 37862,
+ 'GTA 5' => 38278,
+ 'HBO Go' => 38384,
+ 'iCloud' => 38847,
+ 'Instagram' => 37863,
+ 'Netflix' => 37864,
+ 'Office 365' => 38075,
+ 'Origin' => 38149,
+ 'Player Unknown\'s Battlegrounds' => 37865,
+ 'Playstation Network' => 38574,
+ 'Reddit' => 38090,
+ 'Snapchat' => 38509,
+ 'Steam' => 38229,
+ 'Tinder' => 37920,
+ 'Twitter' => 38672,
+ 'Whatsapp' => 38026,
+ 'Wikipedia' => 38874,
+ 'Yahoo Mail' => 38884,
+ 'Youtube' => 37866,
+ ),
+ 'Česko' => array(
+ 'Apex Legends' => 38136,
+ 'Battlefield' => 38446,
+ 'Blizzard Battle.net' => 37851,
+ 'Counter-strike' => 38236,
+ 'Discord' => 38452,
+ 'Dota 2' => 38221,
+ 'EA' => 38037,
+ 'Facebook' => 37852,
+ 'Facebook Messenger' => 37853,
+ 'Fifa' => 38430,
+ 'Fortnite' => 38086,
+ 'Gmail' => 38515,
+ 'Google' => 37854,
+ 'Google Play' => 38523,
+ 'GTA 5' => 38304,
+ 'HBO Go' => 38380,
+ 'iCloud' => 38845,
+ 'Instagram' => 37855,
+ 'Netflix' => 37856,
+ 'Office 365' => 38042,
+ 'Origin' => 38043,
+ 'Player Unknown\'s Battlegrounds' => 37857,
+ 'Playstation Network' => 38573,
+ 'Reddit' => 38062,
+ 'Snapchat' => 38512,
+ 'Steam' => 38082,
+ 'Tinder' => 37914,
+ 'Twitch' => 38530,
+ 'Twitter' => 38675,
+ 'Uplay PC' => 38638,
+ 'Whatsapp' => 38025,
+ 'Xbox Live' => 38072,
+ 'Youtube' => 37858,
+ ),
+ 'Chile' => array(
+ 'Apex Legends' => 38137,
+ 'Banco Estado' => 37682,
+ 'Banco Santander' => 37351,
+ 'Blizzard Battle.net' => 38104,
+ 'Call of Duty' => 38938,
+ 'Claro' => 36290,
+ 'Clash of Clans' => 37304,
+ 'Correos' => 37025,
+ 'Counter-Strike' => 38246,
+ 'Crunchyroll' => 38093,
+ 'Dead By Daylight' => 37436,
+ 'Discord' => 37758,
+ 'Dota 2' => 38477,
+ 'EA' => 36884,
+ 'Entel' => 37680,
+ 'Facebook' => 36239,
+ 'Facebook Messenger' => 36285,
+ 'Falabella' => 37681,
+ 'Fifa' => 38432,
+ 'Fortnite' => 36997,
+ 'Gmail' => 36499,
+ 'Google' => 36284,
+ 'Google Drive' => 38560,
+ 'GTA 5' => 38305,
+ 'Gtd Manquehue' => 37683,
+ 'HBO' => 36353,
+ 'iCloud' => 38854,
+ 'Instagram' => 36283,
+ 'Itaú' => 38473,
+ 'LinkedIn' => 38625,
+ 'Movistar' => 36289,
+ 'Mundo Pacifico' => 37684,
+ 'Netflix' => 36282,
+ 'Office 365' => 38077,
+ 'Origin' => 38102,
+ 'Outlook' => 36281,
+ 'Player Unknown\'s Battlegrounds' => 36564,
+ 'Playstation Network' => 36280,
+ 'Pokémon Go' => 36293,
+ 'Rainbow Six' => 36270,
+ 'Reddit' => 37484,
+ 'Roblox' => 38930,
+ 'SII' => 37679,
+ 'Skype' => 36279,
+ 'Snapchat' => 36278,
+ 'Spotify' => 36274,
+ 'Steam' => 36272,
+ 'Teamviewer' => 37068,
+ 'Telegram' => 38170,
+ 'Telsur' => 36292,
+ 'Tinder' => 37907,
+ 'Twitch' => 36541,
+ 'Twitter' => 36273,
+ 'Uplay PC' => 36271,
+ 'VTR' => 36291,
+ 'Warframe' => 37123,
+ 'Whatsapp' => 36277,
+ 'Wom' => 38308,
+ 'Xbox Live' => 37024,
+ 'Yahoo Mail' => 36276,
+ 'Youtube' => 36275,
+ ),
+ 'Colombia' => array(
+ 'Apex Legends' => 38140,
+ 'BBVA' => 37781,
+ 'Blizzard Battle.net' => 38148,
+ 'Call of Duty' => 38937,
+ 'Claro' => 37788,
+ 'Directv' => 37968,
+ 'Discord' => 38552,
+ 'EA' => 38282,
+ 'ETB' => 37967,
+ 'Facebook' => 37782,
+ 'Facebook Messenger' => 38257,
+ 'Fifa' => 38431,
+ 'Fortnite' => 37783,
+ 'Free Fire' => 38805,
+ 'Gmail' => 37779,
+ 'Google' => 37780,
+ 'Google Drive' => 39067,
+ 'GTA 5' => 38277,
+ 'HBO' => 38370,
+ 'iCloud' => 38857,
+ 'Instagram' => 37784,
+ 'Movistar' => 37789,
+ 'Netflix' => 37820,
+ 'Origin' => 38424,
+ 'Outlook' => 38464,
+ 'Player Unknown\'s Battlegrounds' => 37785,
+ 'Playstation Network' => 38391,
+ 'Snapchat' => 38506,
+ 'Spotify' => 38168,
+ 'Steam' => 38933,
+ 'Teamviewer' => 38460,
+ 'Telegram' => 38484,
+ 'Tigo' => 37966,
+ 'Tinder' => 37908,
+ 'Twitter' => 38169,
+ 'Waze' => 39070,
+ 'Whatsapp' => 37786,
+ 'Xbox Live' => 38071,
+ 'Yahoo Mail' => 38924,
+ 'Youtube' => 37787,
+ ),
+ 'România' => array(
+ 'Apex Legends' => 38142,
+ 'Battlefield' => 38441,
+ 'Blizzard Battle.net' => 37867,
+ 'Brawl Stars' => 38817,
+ 'Call of Duty' => 38934,
+ 'Counter-strike' => 38237,
+ 'Destiny' => 38988,
+ 'Digi' => 38971,
+ 'Discord' => 37953,
+ 'Dota 2' => 38222,
+ 'EA' => 38039,
+ 'Facebook' => 37868,
+ 'Facebook Messenger' => 37869,
+ 'Fifa' => 38046,
+ 'Fortnite' => 38070,
+ 'Gmail' => 38549,
+ 'Google' => 37870,
+ 'Google Play' => 38524,
+ 'GTA 5' => 38281,
+ 'HBO Go' => 38383,
+ 'iCloud' => 38849,
+ 'Instagram' => 37871,
+ 'Netflix' => 37872,
+ 'Orange' => 38970,
+ 'Origin' => 38045,
+ 'Player Unknown\'s Battlegrounds' => 37873,
+ 'Playstation Network' => 38407,
+ 'Reddit' => 38063,
+ 'Roblox' => 38918,
+ 'Snapchat' => 38510,
+ 'Steam' => 38083,
+ 'Telekom' => 38931,
+ 'Tinder' => 37913,
+ 'Twitter' => 38674,
+ 'UPC' => 38968,
+ 'Uplay PC' => 38637,
+ 'Vodafone' => 38967,
+ 'Whatsapp' => 38027,
+ 'Wikipedia' => 38875,
+ 'Xbox Live' => 38073,
+ 'Yahoo Mail' => 38455,
+ 'Youtube' => 37874,
+ ),
+ 'Magyarország' => array(
+ 'Apex Legends' => 38143,
+ 'Battlefield' => 38450,
+ 'Blizzard Battle.net' => 37883,
+ 'Call of Duty' => 39004,
+ 'Counter-strike' => 38242,
+ 'Destiny' => 38979,
+ 'Discord' => 38454,
+ 'Dota 2' => 38475,
+ 'EA' => 38038,
+ 'Escape from Tarkov' => 39055,
+ 'Facebook' => 37884,
+ 'Facebook Messenger' => 37885,
+ 'Fifa' => 38437,
+ 'Fortnite' => 38808,
+ 'Ghost Recon' => 38991,
+ 'Gmail' => 38514,
+ 'Google' => 37886,
+ 'GTA 5' => 38280,
+ 'HBO Go' => 38382,
+ 'iCloud' => 38852,
+ 'Instagram' => 37887,
+ 'Mastercard' => 38592,
+ 'Netflix' => 37888,
+ 'Office 365' => 38040,
+ 'Origin' => 38044,
+ 'Outlook' => 38078,
+ 'Pinterest' => 37954,
+ 'Player Unknown\'s Battlegrounds' => 37889,
+ 'Playstation Network' => 38408,
+ 'Rainbow Six' => 38486,
+ 'Reddit' => 38065,
+ 'Snapchat' => 38507,
+ 'Steam' => 38232,
+ 'Tinder' => 37910,
+ 'Twitch' => 39016,
+ 'Twitter' => 38673,
+ 'Uplay PC' => 38354,
+ 'Whatsapp' => 38372,
+ 'Wikipedia' => 38876,
+ 'Xbox Live' => 38074,
+ 'Yahoo Mail' => 38886,
+ 'Youtube' => 37890,
+ ),
+ 'Ecuador' => array(
+ 'Call of Duty' => 38936,
+ 'Claro' => 37985,
+ 'CNT' => 37990,
+ 'EA' => 38421,
+ 'Facebook' => 37797,
+ 'Facebook Messenger' => 38256,
+ 'Fortnite' => 37798,
+ 'HBO' => 38368,
+ 'iCloud' => 38856,
+ 'Instagram' => 37799,
+ 'iPlanet' => 37986,
+ 'Movistar' => 37984,
+ 'Nedetel' => 37987,
+ 'Netflix' => 37819,
+ 'Netlife' => 37991,
+ 'Playstation Network' => 38577,
+ 'Puntonet' => 37988,
+ 'Steam' => 38932,
+ 'Telegram' => 38482,
+ 'TVCable' => 37989,
+ 'Twitter' => 38670,
+ 'Whatsapp' => 37800,
+ 'Youtube' => 37801,
+ ),
+ )
+ )
+ ),
+ );
+
+ const API_TOKEN = 'YW5kcm9pZF9hcGlfdXNlcl92MTpxTkRyenZSczY1bW1ESlk0ZVNIWmtobFY=';
+
+ public function collectData(){
+
+ if($this->queriedContext == 'All Websites') {
+ $html = getSimpleHTMLDOM($this->getURI() . '/archive/')
+ or returnClientError('Impossible to query website !.');
+
+ $table = $html->find('table.table-striped', 0);
+
+ $maxCount = 10;
+ foreach ($table->find('tr') as $downEvent) {
+ $downLink = $downEvent->find('td', 1)->find('a', 1);
+ $item = $this->collectArticleData($downLink->getAttribute('href'));
+ $this->items[] = $item;
+ if($maxCount == 0) break;
+ $maxCount -= 1;
+ }
+ } else {
+ $this->items = $this->collectCompanyEvents($this->getInput('website'));
+ }
+ }
+
+ protected function collectArticleData($link) {
+
+ preg_match('/\/([0-9]{3,})/', $link, $matches);
+ $eventId = $matches[1];
+
+ $header = array(
+ 'Authorization: Basic ' . self::API_TOKEN
+ );
+
+ $article = getContents('https://downdetectorapi.com/v1/events/' . $eventId, $header)
+ or returnServerError('Could not request DownDetector API.');
+ $article_json = json_decode($article);
+
+ $item = array();
+ $item['uri'] = $this->getURI() . $link;
+ $item['id'] = $article_json->id;
+ $item['title'] = $article_json->title;
+ $item['content'] = $article_json->body;
+ $item['timestamp'] = (new DateTime($article_json->started_at))->getTimestamp();
+ return $item;
+
+ }
+
+ protected function collectCompanyEvents($companyId) {
+
+ $header = array(
+ 'Authorization: Basic ' . self::API_TOKEN
+ );
+
+ $events = getContents('https://downdetectorapi.com/v1/companies/' . $companyId . '/events/', $header)
+ or returnServerError('Could not request DownDetector API.');
+ $events_json = json_decode($events);
+
+ $items = array();
+
+ foreach($events_json as $event) {
+ $item = array();
+ $item['id'] = $event->id;
+ $item['title'] = $event->title;
+ $item['content'] = $event->body;
+ $item['timestamp'] = (new DateTime($event->started_at))->getTimestamp();
+ $items[] = $item;
+ }
+
+ return $items;
+
+ }
+
+ public function getURI() {
+ if($this->getInput('country') !== null) {
+ return $this->getInput('country');
+ } else {
+ return self::URI;
+ }
+ }
+}
diff --git a/bridges/DribbbleBridge.php b/bridges/DribbbleBridge.php
index 5058da63..b1193c90 100644
--- a/bridges/DribbbleBridge.php
+++ b/bridges/DribbbleBridge.php
@@ -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) {
diff --git a/bridges/EconomistBridge.php b/bridges/EconomistBridge.php
index 1256be45..94121ac3 100644
--- a/bridges/EconomistBridge.php
+++ b/bridges/EconomistBridge.php
@@ -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 . ': '
diff --git a/bridges/EliteDangerousGalnetBridge.php b/bridges/EliteDangerousGalnetBridge.php
index dc6077b3..1afa0423 100644
--- a/bridges/EliteDangerousGalnetBridge.php
+++ b/bridges/EliteDangerousGalnetBridge.php
@@ -47,5 +47,8 @@ class EliteDangerousGalnetBridge extends BridgeAbstract {
$this->items[] = $item;
}
+
+ //Remove duplicates that sometimes show up on the website
+ $this->items = array_unique($this->items, SORT_REGULAR);
}
}
diff --git a/bridges/ElloBridge.php b/bridges/ElloBridge.php
index 1f66edc3..8bcfa922 100644
--- a/bridges/ElloBridge.php
+++ b/bridges/ElloBridge.php
@@ -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) {
@@ -120,9 +120,11 @@ class ElloBridge extends BridgeAbstract {
}
private function getAPIKey() {
- $cache = Cache::create(Configuration::getConfig('cache', 'type'));
+ $cacheFac = new CacheFactory();
+ $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) {
diff --git a/bridges/ElsevierBridge.php b/bridges/ElsevierBridge.php
index 080fe00f..01230a9e 100644
--- a/bridges/ElsevierBridge.php
+++ b/bridges/ElsevierBridge.php
@@ -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';
diff --git a/bridges/EngadgetBridge.php b/bridges/EngadgetBridge.php
new file mode 100644
index 00000000..cf200fa4
--- /dev/null
+++ b/bridges/EngadgetBridge.php
@@ -0,0 +1,26 @@
+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;
+ }
+}
diff --git a/bridges/EsquerdaNetBridge.php b/bridges/EsquerdaNetBridge.php
new file mode 100644
index 00000000..f459eb23
--- /dev/null
+++ b/bridges/EsquerdaNetBridge.php
@@ -0,0 +1,70 @@
+ 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
\d\d)/(?P\d\d)/(?P\d\d\d\d) - (?P\d\d):(?P\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;
+ }
+}
diff --git a/bridges/ExtremeDownloadBridge.php b/bridges/ExtremeDownloadBridge.php
index acdf6301..1b4aa9a9 100644
--- a/bridges/ExtremeDownloadBridge.php
+++ b/bridges/ExtremeDownloadBridge.php
@@ -1,7 +1,7 @@
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('/]* style="[^"]*url\(\'(.*?)\'\).*?><\/i>/m', function ($matches) {
- return "";
+ return "";
}, $content);
$content = str_get_html($imagecleaned);
@@ -159,7 +164,11 @@ EOD
$content = preg_replace('//m', '', $content);
//Remove the double section tags
- $content = str_replace([''], [''], $content);
+ $content = str_replace(
+ array(''),
+ array(''),
+ $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;
+ }
}
}
diff --git a/bridges/FabriceBellardBridge.php b/bridges/FabriceBellardBridge.php
new file mode 100644
index 00000000..2c24b5ea
--- /dev/null
+++ b/bridges/FabriceBellardBridge.php
@@ -0,0 +1,36 @@
+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;
+ }
+ }
+}
diff --git a/bridges/FacebookBridge.php b/bridges/FacebookBridge.php
index c0901072..5ce67f94 100644
--- a/bridges/FacebookBridge.php
+++ b/bridges/FacebookBridge.php
@@ -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
diff --git a/bridges/FicbookBridge.php b/bridges/FicbookBridge.php
new file mode 100644
index 00000000..8b8a57fc
--- /dev/null
+++ b/bridges/FicbookBridge.php
@@ -0,0 +1,164 @@
+ array(),
+ 'Fiction Updates' => array(
+ 'fiction_id' => array(
+ 'name' => 'Fanfiction ID',
+ 'type' => 'text',
+ 'pattern' => '[0-9]+',
+ 'required' => true,
+ 'title' => 'Insert fanfiction ID',
+ 'exampleValue' => '5783919',
+ ),
+ 'include_contents' => array(
+ 'name' => 'Include contents',
+ 'type' => 'checkbox',
+ 'title' => 'Activate to include contents in the feed',
+ ),
+ ),
+ 'Fiction Comments' => array(
+ 'fiction_id' => array(
+ 'name' => 'Fanfiction ID',
+ 'type' => 'text',
+ 'pattern' => '[0-9]+',
+ 'required' => true,
+ 'title' => 'Insert fanfiction ID',
+ 'exampleValue' => '5783919',
+ ),
+ ),
+ );
+
+ public function getURI() {
+ switch($this->queriedContext) {
+ case 'Site News': {
+ // For some reason this is not HTTPS
+ return 'http://ficbook.net/sitenews';
+ }
+ case 'Fiction Updates': {
+ return self::URI
+ . 'readfic/'
+ . urlencode($this->getInput('fiction_id'));
+ }
+ case 'Fiction Comments': {
+ return self::URI
+ . 'readfic/'
+ . urlencode($this->getInput('fiction_id'))
+ . '/comments#content';
+ }
+ default: return parent::getURI();
+ }
+ }
+
+ public function collectData() {
+
+ $header = array('Accept-Language: en-US');
+
+ $html = getSimpleHTMLDOM($this->getURI(), $header)
+ or returnServerError('Could not request ' . $this->getURI());
+
+ $html = defaultLinkTo($html, self::URI);
+
+ switch($this->queriedContext) {
+ case 'Site News': return $this->collectSiteNews($html);
+ case 'Fiction Updates': return $this->collectUpdatesData($html);
+ case 'Fiction Comments': return $this->collectCommentsData($html);
+ }
+
+ }
+
+ private function collectSiteNews($html) {
+ foreach($html->find('.news_view') as $news) {
+ $this->items[] = array(
+ 'title' => $news->find('h1.title', 0)->plaintext,
+ 'timestamp' => strtotime($this->fixDate($news->find('span[title]', 0)->title)),
+ 'content' => $news->find('.news_text', 0),
+ );
+ }
+ }
+
+ private function collectCommentsData($html) {
+ foreach($html->find('article.post') as $article) {
+ $this->items[] = array(
+ 'uri' => $article->find('.comment_link_to_fic > a', 0)->href,
+ 'title' => $article->find('.comment_author', 0)->plaintext,
+ 'author' => $article->find('.comment_author', 0)->plaintext,
+ 'timestamp' => strtotime($this->fixDate($article->find('time[datetime]', 0)->datetime)),
+ 'content' => $article->find('.comment_message', 0),
+ 'enclosures' => array($article->find('img', 0)->src),
+ );
+ }
+ }
+
+ private function collectUpdatesData($html) {
+ foreach($html->find('ul.table-of-contents > li') as $chapter) {
+ $item = array(
+ 'uri' => $chapter->find('a', 0)->href,
+ 'title' => $chapter->find('a', 0)->plaintext,
+ 'timestamp' => strtotime($this->fixDate($chapter->find('span[title]', 0)->title)),
+ );
+
+ if($this->getInput('include_contents')) {
+ $content = getSimpleHTMLDOMCached($item['uri']);
+ $item['content'] = $content->find('#content', 0);
+ }
+
+ $this->items[] = $item;
+
+ // Sort by time, descending
+ usort($this->items, function($a, $b){ return $b['timestamp'] - $a['timestamp']; });
+ }
+ }
+
+ private function fixDate($date) {
+
+ // FIXME: This list was generated using Google tranlator. Someone who
+ // actually knows russian should check this list! Please keep in mind
+ // that month names must match exactly the names returned by Ficbook.
+ $ru_month = array(
+ 'января',
+ 'февраля',
+ 'марта',
+ 'апреля',
+ 'мая',
+ 'июня',
+ 'июля',
+ 'августа',
+ 'Сентября',
+ 'октября',
+ 'Ноября',
+ 'Декабря',
+ );
+
+ $en_month = array(
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December',
+ );
+
+ $fixed_date = str_replace($ru_month, $en_month, $date);
+
+ if($fixed_date === $date) {
+ Debug::log('Unable to fix date: ' . $date);
+ return null;
+ }
+
+ return $fixed_date;
+
+ }
+}
diff --git a/bridges/FindACrewBridge.php b/bridges/FindACrewBridge.php
index 1dac775a..abab6e13 100644
--- a/bridges/FindACrewBridge.php
+++ b/bridges/FindACrewBridge.php
@@ -62,11 +62,16 @@ class FindACrewBridge extends BridgeAbstract {
foreach ($annonces as $annonce) {
$item = array();
- $img = parent::getURI() . $annonce->find('.lst-pic img', 0)->getAttribute('src');
+ $link = parent::getURI() . $annonce->find('.lst-ctrls a', 0)->href;
+ $htmlDetail = getSimpleHTMLDOMCached($link . '?mdl=2'); // add ?mdl=2 for xhr content not full html page
+
+ $img = parent::getURI() . $htmlDetail->find('img.img-responsive', 0)->getAttribute('src');
$item['title'] = $annonce->find('.lst-tags span', 0)->plaintext;
- $item['uri'] = parent::getURI() . $annonce->find('.lst-ctrls a', 0)->href;
- $content = $annonce->find('.lst-dtl', 0)->innertext;
- $item['content'] = "
$content";
+ $item['uri'] = $link;
+ $content = $htmlDetail->find('.panel-body div.clearfix.row > div', 1)->innertext;
+ $content .= $htmlDetail->find('.panel-body > div', 1)->innertext;
+ $content = defaultLinkTo($content, parent::getURI());
+ $item['content'] = $content;
$item['enclosures'] = array($img);
$item['categories'] = array($annonce->find('.css_AccLocCur', 0)->plaintext);
$this->items[] = $item;
diff --git a/bridges/FreeCodeCampBridge.php b/bridges/FreeCodeCampBridge.php
new file mode 100644
index 00000000..da0b5c7d
--- /dev/null
+++ b/bridges/FreeCodeCampBridge.php
@@ -0,0 +1,27 @@
+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;
+ }
+}
diff --git a/bridges/FurAffinityBridge.php b/bridges/FurAffinityBridge.php
new file mode 100644
index 00000000..2f78ee4f
--- /dev/null
+++ b/bridges/FurAffinityBridge.php
@@ -0,0 +1,918 @@
+ 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'] = <<
+
+
+
+{$description}
+
+EOD;
+ } else {
+ $item['content'] = <<
+
+
+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';
+ }
+ }
+}
diff --git a/bridges/FurAffinityUserBridge.php b/bridges/FurAffinityUserBridge.php
new file mode 100644
index 00000000..fd8a61f0
--- /dev/null
+++ b/bridges/FurAffinityUserBridge.php
@@ -0,0 +1,110 @@
+ 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;
+ }
+ }
+}
diff --git a/bridges/GQMagazineBridge.php b/bridges/GQMagazineBridge.php
index 961b3a09..14a9a56c 100644
--- a/bridges/GQMagazineBridge.php
+++ b/bridges/GQMagazineBridge.php
@@ -40,6 +40,11 @@ class GQMagazineBridge extends BridgeAbstract
'data-original' => 'src'
);
+ const POSSIBLE_TITLES = array(
+ 'h2',
+ 'h3'
+ );
+
private function getDomain() {
$domain = $this->getInput('domain');
if (empty($domain))
@@ -54,6 +59,17 @@ class GQMagazineBridge extends BridgeAbstract
return $this->getDomain() . '/' . $this->getInput('page');
}
+ private function findTitleOf($link) {
+ foreach (self::POSSIBLE_TITLES as $tag) {
+ $title = $link->parent()->find($tag, 0);
+ if($title !== null) {
+ if($title->plaintext !== null) {
+ return $title->plaintext;
+ }
+ }
+ }
+ }
+
public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI()) or returnServerError('Could not request ' . $this->getURI());
@@ -61,31 +77,36 @@ 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;
- $title = $link->find('h2', 0);
- $date = $link->find('time', 0);
+ $date = $link->parent()->find('time', 0);
$item = array();
- $author = $link->find('span[itemprop=name]', 0);
- $item['author'] = $author->plaintext;
- $item['title'] = $title->plaintext;
- if(substr($uri, 0, 1) === 'h') { // absolute uri
- $item['uri'] = $uri;
- } else if(substr($uri, 0, 1) === '/') { // domain relative url
- $item['uri'] = $this->getDomain() . $uri;
- } else {
- $item['uri'] = $this->getDomain() . '/' . $uri;
+ $author = $link->parent()->find('span[itemprop=name]', 0);
+ if($author !== null) {
+ $item['author'] = $author->plaintext;
+ $item['title'] = $this->findTitleOf($link);
+ switch(substr($uri, 0, 1)) {
+ case 'h': // absolute uri
+ $item['uri'] = $uri;
+ break;
+ case '/': // domain relative uri
+ $item['uri'] = $this->getDomain() . $uri;
+ break;
+ default:
+ $item['uri'] = $this->getDomain() . '/' . $uri;
+ }
+ $article = $this->loadFullArticle($item['uri']);
+ if($article) {
+ $item['content'] = $this->replaceUriInHtmlElement($article);
+ } else {
+ $item['content'] = "Article body couldn't be loaded. It must be a bug!";
+ }
+ $short_date = $date->datetime;
+ $item['timestamp'] = strtotime($short_date);
+ $this->items[] = $item;
}
-
- $article = $this->loadFullArticle($item['uri']);
- if($article) {
- $item['content'] = $this->replaceUriInHtmlElement($article);
- } else {
- $item['content'] = "Article body couldn't be loaded. It must be a bug!";
- }
- $short_date = $date->datetime;
- $item['timestamp'] = strtotime($short_date);
- $this->items[] = $item;
}
}
@@ -96,16 +117,7 @@ class GQMagazineBridge extends BridgeAbstract
*/
private function loadFullArticle($uri){
$html = getSimpleHTMLDOMCached($uri);
- // Once again, that generated css classes madness is an obstacle ... which i can go over easily
- foreach($html->find('div') as $div) {
- // List the CSS classes of that div
- $classes = $div->class;
- // I can't directly lookup that class since GQ since to generate random names like "ArticleBodySection-fkggUW"
- if(strpos($classes, 'ArticleBodySection') !== false) {
- return $div;
- }
- }
- return null;
+ return $html->find('section[data-test-id=MainContentWrapper]', 0);
}
/**
diff --git a/bridges/GiphyBridge.php b/bridges/GiphyBridge.php
index 26d1ebaf..202bbbbd 100644
--- a/bridges/GiphyBridge.php
+++ b/bridges/GiphyBridge.php
@@ -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';
diff --git a/bridges/GiteaBridge.php b/bridges/GiteaBridge.php
new file mode 100644
index 00000000..33247873
--- /dev/null
+++ b/bridges/GiteaBridge.php
@@ -0,0 +1,27 @@
+find('#release-list > li')
+ or returnServerError('Unable to find releases');
+
+ foreach($releases as $release) {
+ $this->items[] = array(
+ 'uri' => $release->find('a', 0)->href,
+ 'title' => 'Release ' . $release->find('h3', 0)->plaintext,
+ );
+ }
+ }
+}
diff --git a/bridges/GithubIssueBridge.php b/bridges/GithubIssueBridge.php
index 91dd45ec..2eddeb2e 100644
--- a/bridges/GithubIssueBridge.php
+++ b/bridges/GithubIssueBridge.php
@@ -66,10 +66,21 @@ class GithubIssueBridge extends BridgeAbstract {
return parent::getURI();
}
- protected function extractIssueEvent($issueNbr, $title, $comment){
- $comment = $comment->firstChild();
- $uri = static::URI . $this->getInput('u') . '/' . $this->getInput('p')
- . '/issues/' . $issueNbr . '#' . $comment->getAttribute('id');
+ private function buildGitHubIssueCommentUri($issue_number, $comment_id) {
+ // https://github.com///issues/#
+ return static::URI
+ . $this->getInput('u')
+ . '/'
+ . $this->getInput('p')
+ . '/issues/'
+ . $issue_number
+ . '#'
+ . $comment_id;
+ }
+
+ private function extractIssueEvent($issueNbr, $title, $comment){
+
+ $uri = $this->buildGitHubIssueCommentUri($issueNbr, $comment->id);
$author = $comment->find('.author', 0)->plaintext;
@@ -94,22 +105,21 @@ class GithubIssueBridge extends BridgeAbstract {
return $item;
}
- protected function extractIssueComment($issueNbr, $title, $comment){
- $uri = static::URI . $this->getInput('u') . '/'
- . $this->getInput('p') . '/issues/' . $issueNbr;
+ private function extractIssueComment($issueNbr, $title, $comment){
+
+ $uri = $this->buildGitHubIssueCommentUri($issueNbr, $comment->parent->id);
$author = $comment->find('.author', 0)->plaintext;
$title .= ' / ' . trim(
- $comment->find('.comment .timeline-comment-header-text', 0)->plaintext
+ $comment->find('.timeline-comment-header-text', 0)->plaintext
);
$content = $comment->find('.comment-body', 0)->innertext;
$item = array();
$item['author'] = $author;
- $item['uri'] = $uri
- . '#' . $comment->firstChild()->nextSibling()->getAttribute('id');
+ $item['uri'] = $uri;
$item['title'] = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
$item['timestamp'] = strtotime(
$comment->find('relative-time', 0)->getAttribute('datetime')
@@ -118,25 +128,32 @@ class GithubIssueBridge extends BridgeAbstract {
return $item;
}
- protected function extractIssueComments($issue){
+ private function extractIssueComments($issue){
$items = array();
$title = $issue->find('.gh-header-title', 0)->plaintext;
$issueNbr = trim(
substr($issue->find('.gh-header-number', 0)->plaintext, 1)
);
- $comments = $issue->find('.js-discussion', 0);
- foreach($comments->children() as $comment) {
+
+ $comments = $issue->find('
+ [id^="issue-"] > .comment,
+ [id^="issuecomment-"] > .comment,
+ [id^="event-"],
+ [id^="ref-"]
+ ');
+ foreach($comments as $comment) {
+
if (!$comment->hasChildNodes()) {
continue;
}
- $comment = $comment->firstChild();
- $classes = explode(' ', $comment->getAttribute('class'));
- if (in_array('timeline-comment-wrapper', $classes)) {
+
+ if (!$comment->hasClass('discussion-item-header')) {
$item = $this->extractIssueComment($issueNbr, $title, $comment);
$items[] = $item;
continue;
}
- while (in_array('discussion-item', $classes)) {
+
+ while ($comment->hasClass('discussion-item-header')) {
$item = $this->extractIssueEvent($issueNbr, $title, $comment);
$items[] = $item;
$comment = $comment->nextSibling();
@@ -145,6 +162,7 @@ class GithubIssueBridge extends BridgeAbstract {
}
$classes = explode(' ', $comment->getAttribute('class'));
}
+
}
return $items;
}
@@ -192,8 +210,13 @@ class GithubIssueBridge extends BridgeAbstract {
ENT_QUOTES,
'UTF-8'
);
- $comments = trim($issue->find('.col-5', 0)->plaintext);
- $item['content'] .= "\n" . 'Comments: ' . ($comments ? $comments : '0');
+
+ $comment_count = 0;
+ if($span = $issue->find('a[aria-label*="comment"] span', 0)) {
+ $comment_count = $span->plaintext;
+ }
+
+ $item['content'] .= "\n" . 'Comments: ' . $comment_count;
$item['uri'] = self::URI
. $issue->find('.js-navigation-open', 0)->getAttribute('href');
$this->items[] = $item;
@@ -216,4 +239,43 @@ class GithubIssueBridge extends BridgeAbstract {
$item['title'] = preg_replace('/\s+/', ' ', $item['title']);
});
}
+
+ public function detectParameters($url) {
+
+ if(filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) === false
+ || strpos($url, self::URI) !== 0) {
+ return null;
+ }
+
+ $url_components = parse_url($url);
+ $path_segments = array_values(array_filter(explode('/', $url_components['path'])));
+
+ switch(count($path_segments)) {
+ case 2: { // Project issues
+ list($user, $project) = $path_segments;
+ $show_comments = 'off';
+ } break;
+ case 3: { // Project issues with issue comments
+ if($path_segments[2] !== 'issues') {
+ return null;
+ }
+ list($user, $project) = $path_segments;
+ $show_comments = 'on';
+ } break;
+ case 4: { // Issue comments
+ list($user, $project, /* issues */, $issue) = $path_segments;
+ } break;
+ default: {
+ return null;
+ }
+ }
+
+ return array(
+ 'u' => $user,
+ 'p' => $project,
+ 'c' => isset($show_comments) ? $show_comments : null,
+ 'i' => isset($issue) ? $issue : null,
+ );
+
+ }
}
diff --git a/bridges/GlassdoorBridge.php b/bridges/GlassdoorBridge.php
index 68b6a137..308859d7 100644
--- a/bridges/GlassdoorBridge.php
+++ b/bridges/GlassdoorBridge.php
@@ -141,7 +141,7 @@ class GlassdoorBridge extends BridgeAbstract {
}
private function collectReviewData($html, $limit) {
- $reviews = $html->find('#EmployerReviews li[id^="empReview]')
+ $reviews = $html->find('#ReviewsFeed li[id^="empReview]')
or returnServerError('Unable to find reviews!');
foreach($reviews as $review) {
@@ -153,7 +153,19 @@ class GlassdoorBridge extends BridgeAbstract {
$item['timestamp'] = strtotime($review->find('time', 0)->datetime);
$mainText = $review->find('p.mainText', 0)->plaintext;
- $description = $review->find('div.prosConsAdvice', 0)->innertext;
+
+ $description = '';
+ foreach($review->find('div.description p') as $p) {
+
+ if ($p->hasClass('strong')) {
+ $p->tag = 'strong';
+ $p->removeClass('strong');
+ }
+
+ $description .= $p;
+
+ }
+
$item['content'] = "{$mainText}
{$description}
";
$this->items[] = $item;
diff --git a/bridges/GogsBridge.php b/bridges/GogsBridge.php
new file mode 100644
index 00000000..a08bcc0e
--- /dev/null
+++ b/bridges/GogsBridge.php
@@ -0,0 +1,206 @@
+ array(
+ 'host' => array(
+ 'name' => 'Host',
+ 'exampleValue' => 'https://gogs.io',
+ 'required' => true,
+ 'title' => 'Host name without trailing slash',
+ ),
+ 'user' => array(
+ 'name' => 'Username',
+ 'exampleValue' => 'gogs',
+ 'required' => true,
+ 'title' => 'User name as it appears in the URL',
+ ),
+ 'project' => array(
+ 'name' => 'Project name',
+ 'exampleValue' => 'gogs',
+ 'required' => true,
+ 'title' => 'Project name as it appears in the URL',
+ ),
+ ),
+ 'Commits' => array(
+ 'branch' => array(
+ 'name' => 'Branch name',
+ 'defaultValue' => 'master',
+ 'required' => true,
+ 'title' => 'Branch name as it appears in the URL',
+ ),
+ ),
+ 'Issues' => array(
+ 'include_description' => array(
+ 'name' => 'Include issue description',
+ 'type' => 'checkbox',
+ 'title' => 'Activate to include the issue description',
+ ),
+ ),
+ 'Single issue' => array(
+ 'issue' => array(
+ 'name' => 'Issue number',
+ 'type' => 'number',
+ 'exampleValue' => 102,
+ 'required' => true,
+ 'title' => 'Issue number from the issues list',
+ ),
+ ),
+ 'Releases' => array(),
+ );
+
+ private $title = '';
+
+ /**
+ * Note: detectParamters doesn't make sense for this bridge because there is
+ * no "single" host for this service. Anyone can host it.
+ */
+
+ public function getURI() {
+ switch($this->queriedContext) {
+ case 'Commits': {
+ return $this->getInput('host')
+ . '/' . $this->getInput('user')
+ . '/' . $this->getInput('project')
+ . '/commits/' . $this->getInput('branch');
+ } break;
+ case 'Issues': {
+ return $this->getInput('host')
+ . '/' . $this->getInput('user')
+ . '/' . $this->getInput('project')
+ . '/issues/';
+ } break;
+ case 'Single issue': {
+ return $this->getInput('host')
+ . '/' . $this->getInput('user')
+ . '/' . $this->getInput('project')
+ . '/issues/' . $this->getInput('issue');
+ } break;
+ case 'Releases': {
+ return $this->getInput('host')
+ . '/' . $this->getInput('user')
+ . '/' . $this->getInput('project')
+ . '/releases/';
+ } break;
+ default: return parent::getURI();
+ }
+ }
+
+ public function getName() {
+ switch($this->queriedContext) {
+ case 'Commits':
+ case 'Issues':
+ case 'Releases': return $this->title . ' ' . $this->queriedContext;
+ case 'Single issue': return $this->title . ' Issue ' . $this->getInput('issue');
+ default: return parent::getName();
+ }
+ }
+
+ public function getIcon() {
+ return 'https://gogs.io/img/favicon.ico';
+ }
+
+ public function collectData() {
+
+ $html = getSimpleHTMLDOM($this->getURI())
+ or returnServerError('Could not request ' . $this->getURI());
+
+ $html = defaultLinkTo($html, $this->getURI());
+
+ $this->title = $html->find('[property="og:title"]', 0)->content;
+
+ switch($this->queriedContext) {
+ case 'Commits': {
+ $this->collectCommitsData($html);
+ } break;
+ case 'Issues': {
+ $this->collectIssuesData($html);
+ } break;
+ case 'Single issue': {
+ $this->collectSingleIssueData($html);
+ } break;
+ case 'Releases': {
+ $this->collectReleasesData($html);
+ } break;
+ }
+
+ }
+
+ protected function collectCommitsData($html) {
+ $commits = $html->find('#commits-table tbody tr')
+ or returnServerError('Unable to find commits');
+
+ foreach($commits as $commit) {
+ $this->items[] = array(
+ 'uri' => $commit->find('a.sha', 0)->href,
+ 'title' => $commit->find('.message span', 0)->plaintext,
+ 'author' => $commit->find('.author', 0)->plaintext,
+ 'timestamp' => $commit->find('.time-since', 0)->title,
+ 'uid' => $commit->find('.sha', 0)->plaintext,
+ );
+ }
+ }
+
+ protected function collectIssuesData($html) {
+ $issues = $html->find('.issue.list li')
+ or returnServerError('Unable to find issues');
+
+ foreach($issues as $issue) {
+ $uri = $issue->find('a', 0)->href;
+
+ $item = array(
+ 'uri' => $uri,
+ 'title' => $issue->find('.label', 0)->plaintext . ' | ' . $issue->find('a.title', 0)->plaintext,
+ 'author' => $issue->find('.desc a', 0)->plaintext,
+ 'timestamp' => $issue->find('.time-since', 0)->title,
+ 'uid' => $issue->find('.label', 0)->plaintext,
+ );
+
+ if($this->getInput('include_description')) {
+ $issue_html = getSimpleHTMLDOMCached($uri, 3600)
+ or returnServerError('Unable to load issue description');
+
+ $issue_html = defaultLinkTo($issue_html, $uri);
+
+ $item['content'] = $issue_html->find('.comment .markdown', 0);
+ }
+
+ $this->items[] = $item;
+ }
+ }
+
+ protected function collectSingleIssueData($html) {
+ $comments = $html->find('.comments .comment')
+ or returnServerError('Unable to find comments');
+
+ foreach($comments as $comment) {
+ $this->items[] = array(
+ 'uri' => $comment->find('a[href*="#issue"]', 0)->href,
+ 'title' => $comment->find('span', 0)->plaintext,
+ 'author' => $comment->find('.content a', 0)->plaintext,
+ 'timestamp' => $comment->find('.time-since', 0)->title,
+ 'content' => $comment->find('.markdown', 0),
+ );
+ }
+
+ $this->items = array_reverse($this->items);
+ }
+
+ protected function collectReleasesData($html) {
+ $releases = $html->find('#release-list li')
+ or returnServerError('Unable to find releases');
+
+ foreach($releases as $release) {
+ $this->items[] = array(
+ 'uri' => $release->find('a', 0)->href,
+ 'title' => 'Release ' . $release->find('h4', 0)->plaintext,
+ );
+ }
+ }
+}
diff --git a/bridges/GoogleSearchBridge.php b/bridges/GoogleSearchBridge.php
index c3d9561f..e02aaeba 100644
--- a/bridges/GoogleSearchBridge.php
+++ b/bridges/GoogleSearchBridge.php
@@ -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';
diff --git a/bridges/HDWallpapersBridge.php b/bridges/HDWallpapersBridge.php
index f1579e02..16c08e75 100644
--- a/bridges/HDWallpapersBridge.php
+++ b/bridges/HDWallpapersBridge.php
@@ -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')
. ']';
diff --git a/bridges/HaveIBeenPwnedBridge.php b/bridges/HaveIBeenPwnedBridge.php
index f256623a..96dc7b2a 100644
--- a/bridges/HaveIBeenPwnedBridge.php
+++ b/bridges/HaveIBeenPwnedBridge.php
@@ -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,
)
));
@@ -52,13 +57,15 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
// Remove permalink
$breach->find('p', 1)->find('a', 0)->outertext = '';
- $item['title'] = $breach->find('h3', 0)->plaintext . ' - ' . $accounts[1] . ' breached accounts';
+ $item['title'] = html_entity_decode($breach->find('h3', 0)->plaintext, ENT_QUOTES)
+ . ' - ' . $accounts[1] . ' breached accounts';
$item['dateAdded'] = strtotime($dateAdded[1]);
$item['breachDate'] = strtotime($breachDate[1]);
$item['uri'] = self::URI . '/PwnedWebsites' . $permalink;
- $item['content'] = '' . $breach->find('p', 0)->innertext . '
';
- $item['content'] .= '
' . $breach->find('p', 1)->innertext . '
';
+ $item['content'] = '
' . $breach->find('p', 0)->innertext . '
';
+ $item['content'] .= '' . $this->breachType($breach) . '
';
+ $item['content'] .= '' . $breach->find('p', 1)->innertext . '
';
$this->breaches[] = $item;
}
@@ -67,6 +74,25 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
$this->createItems();
}
+ /**
+ * Extract data breach type(s)
+ */
+ private function breachType($breach) {
+
+ $content = '';
+
+ if ($breach->find('h3 > i', 0)) {
+
+ foreach ($breach->find('h3 > i') as $i) {
+ $content .= $i->title . '.
';
+ }
+
+ }
+
+ return $content;
+
+ }
+
/**
* Order Breaches by date added or date breached
*/
@@ -88,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();
@@ -97,6 +129,10 @@ class HaveIBeenPwnedBridge extends BridgeAbstract {
$item['content'] = $breach['content'];
$this->items[] = $item;
+
+ if (count($this->items) >= $limit) {
+ break;
+ }
}
}
}
diff --git a/bridges/HentaiHavenBridge.php b/bridges/HentaiHavenBridge.php
index 21a0ff5e..0e4fda46 100644
--- a/bridges/HentaiHavenBridge.php
+++ b/bridges/HentaiHavenBridge.php
@@ -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';
diff --git a/bridges/IGNBridge.php b/bridges/IGNBridge.php
new file mode 100644
index 00000000..6a254b37
--- /dev/null
+++ b/bridges/IGNBridge.php
@@ -0,0 +1,55 @@
+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);
+
+ /*
+ * 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
+ $uselessWikiElements = array(
+ '.wiki-page-tools',
+ '.feedback-container',
+ '.paging-container'
+ );
+ foreach($articlePage->find('.wiki-page') as $wikiContents) {
+ $copy = clone $wikiContents;
+ // Remove useless elements present in IGN wiki/howtos
+ foreach($uselessWikiElements as $uslElement) {
+ $toRemove = $wikiContents->find($uslElement, 0);
+ $copy = str_replace($toRemove, '', $copy);
+ }
+ $article = $article . $copy;
+ }
+
+ // Add content to feed
+ $item['content'] = $article;
+ return $item;
+ }
+}
diff --git a/bridges/IndeedBridge.php b/bridges/IndeedBridge.php
new file mode 100644
index 00000000..c1d0cfd3
--- /dev/null
+++ b/bridges/IndeedBridge.php
@@ -0,0 +1,245 @@
+ array(
+ 'name' => 'Company',
+ 'type' => 'text',
+ 'required' => true,
+ 'title' => 'Company name',
+ 'exampleValue' => 'GitHub',
+ )
+ ),
+ 'global' => array(
+ 'language' => array(
+ 'name' => 'Language Code',
+ 'type' => 'list',
+ 'title' => 'Choose your language code',
+ 'defaultValue' => 'en-US',
+ 'values' => array(
+ 'es-AR' => 'es-AR',
+ 'de-AT' => 'de-AT',
+ 'en-AU' => 'en-AU',
+ 'nl-BE' => 'nl-BE',
+ 'fr-BE' => 'fr-BE',
+ 'pt-BR' => 'pt-BR',
+ 'en-CA' => 'en-CA',
+ 'fr-CA' => 'fr-CA',
+ 'de-CH' => 'de-CH',
+ 'fr-CH' => 'fr-CH',
+ 'es-CL' => 'es-CL',
+ 'zh-CN' => 'zh-CN',
+ 'es-CO' => 'es-CO',
+ 'de-DE' => 'de-DE',
+ 'es-ES' => 'es-ES',
+ 'fr-FR' => 'fr-FR',
+ 'en-GB' => 'en-GB',
+ 'en-HK' => 'en-HK',
+ 'en-IE' => 'en-IE',
+ 'en-IN' => 'en-IN',
+ 'it-IT' => 'it-IT',
+ 'ja-JP' => 'ja-JP',
+ 'ko-KR' => 'ko-KR',
+ 'es-MX' => 'es-MX',
+ 'nl-NL' => 'nl-NL',
+ 'pl-PL' => 'pl-PL',
+ 'en-SG' => 'en-SG',
+ 'en-US' => 'en-US',
+ 'en-ZA' => 'en-ZA',
+ 'en-AE' => 'en-AE',
+ 'da-DK' => 'da-DK',
+ 'in-ID' => 'in-ID',
+ 'en-MY' => 'en-MY',
+ 'es-PE' => 'es-PE',
+ 'en-PH' => 'en-PH',
+ 'en-PK' => 'en-PK',
+ 'ro-RO' => 'ro-RO',
+ 'ru-RU' => 'ru-RU',
+ 'tr-TR' => 'tr-TR',
+ 'zh-TW' => 'zh-TW',
+ 'vi-VN' => 'vi-VN',
+ 'en-VN' => 'en-VN',
+ 'ar-EG' => 'ar-EG',
+ 'fr-MA' => 'fr-MA',
+ 'en-NG' => 'en-NG',
+ )
+ ),
+ 'limit' => array(
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'title' => 'Maximum number of items to return',
+ 'exampleValue' => 20,
+ )
+ )
+ );
+
+ const SITES = array(
+ 'es-AR' => 'https://ar.indeed.com/',
+ 'de-AT' => 'https://at.indeed.com/',
+ 'en-AU' => 'https://au.indeed.com/',
+ 'nl-BE' => 'https://be.indeed.com/',
+ 'fr-BE' => 'https://emplois.be.indeed.com/',
+ 'pt-BR' => 'https://www.indeed.com.br/',
+ 'en-CA' => 'https://ca.indeed.com/',
+ 'fr-CA' => 'https://emplois.ca.indeed.com/',
+ 'de-CH' => 'https://www.indeed.ch/',
+ 'fr-CH' => 'https://emplois.indeed.ch/',
+ 'es-CL' => 'https://www.indeed.cl/',
+ 'zh-CN' => 'https://cn.indeed.com/',
+ 'es-CO' => 'https://co.indeed.com/',
+ 'de-DE' => 'https://de.indeed.com/',
+ 'es-ES' => 'https://www.indeed.es/',
+ 'fr-FR' => 'https://www.indeed.fr/',
+ 'en-GB' => 'https://www.indeed.co.uk/',
+ 'en-HK' => 'https://www.indeed.hk/',
+ 'en-IE' => 'https://ie.indeed.com/',
+ 'en-IN' => 'https://www.indeed.co.in/',
+ 'it-IT' => 'https://it.indeed.com/',
+ 'ja-JP' => 'https://jp.indeed.com/',
+ 'ko-KR' => 'https://kr.indeed.com/',
+ 'es-MX' => 'https://www.indeed.com.mx/',
+ 'nl-NL' => 'https://www.indeed.nl/',
+ 'pl-PL' => 'https://pl.indeed.com/',
+ 'en-SG' => 'https://www.indeed.com.sg/',
+ 'en-US' => 'https://www.indeed.com/',
+ 'en-ZA' => 'https://www.indeed.co.za/',
+ 'en-AE' => 'https://www.indeed.ae/',
+ 'da-DK' => 'https://dk.indeed.com/',
+ 'in-ID' => 'https://id.indeed.com/',
+ 'en-MY' => 'https://www.indeed.com.my/',
+ 'es-PE' => 'https://www.indeed.com.pe/',
+ 'en-PH' => 'https://www.indeed.com.ph/',
+ 'en-PK' => 'https://www.indeed.com.pk/',
+ 'ro-RO' => 'https://ro.indeed.com/',
+ 'ru-RU' => 'https://ru.indeed.com/',
+ 'tr-TR' => 'https://tr.indeed.com/',
+ 'zh-TW' => 'https://tw.indeed.com/',
+ 'vi-VN' => 'https://vn.indeed.com/',
+ 'en-VN' => 'https://jobs.vn.indeed.com/',
+ 'ar-EG' => 'https://eg.indeed.com/',
+ 'fr-MA' => 'https://ma.indeed.com/',
+ 'en-NG' => 'https://ng.indeed.com/',
+ );
+
+ private $title;
+
+ public function collectData() {
+
+ $url = $this->getURI();
+ $limit = $this->getInput('limit') ?: 20;
+
+ do {
+
+ $html = getSimpleHTMLDOM($url)
+ or returnServerError('Could not request ' . $url);
+
+ $html = defaultLinkTo($html, $url);
+
+ $this->title = $html->find('h1', 0)->innertext;
+
+ // Use local translation of the word "Rating"
+ $rating_local = $html->find('a[data-id="rating_desc"]', 0)->plaintext;
+
+ foreach($html->find('#cmp-content [id^="cmp-review-"]') as $review) {
+ $item = array();
+
+ $rating = $review->find('.cmp-ratingNumber', 0)->plaintext;
+ $title = $review->find('.cmp-review-title > span', 0)->plaintext;
+ $comment = $this->beautifyComment($review->find('.cmp-review-content-container', 0));
+
+ $item['uri'] = $review->find('.cmp-review-share-popup-item-link--copylink', 0)->href;
+ $item['title'] = "{$rating_local} {$rating} / {$title}";
+ $item['timestamp'] = $review->find('.cmp-review-date-created', 0)->plaintext;
+ $item['author'] = $review->find('.cmp-reviewer', 0)->plaintext;
+ $item['content'] = $comment;
+ //$item['enclosures']
+ $item['categories'][] = $review->find('.cmp-reviewer-job-location', 0)->plaintext;
+ //$item['uid']
+
+ $this->items[] = $item;
+
+ if(count($this->items) >= $limit) {
+ break;
+ }
+ }
+
+ // Break if no more pages available.
+ if($next = $html->find('a[data-tn-element="next-page"]', 0)) {
+ $url = $next->href;
+ } else {
+ break;
+ }
+
+ } while(count($this->items) < $limit);
+
+ }
+
+ public function getURI() {
+ if($this->getInput('language')
+ && $this->getInput('c')) {
+ return self::SITES[$this->getInput('language')]
+ . 'cmp/'
+ . urlencode($this->getInput('c'))
+ . '/reviews';
+ }
+
+ return parent::getURI();
+ }
+
+ public function getName() {
+ return $this->title ?: parent::getName();
+ }
+
+ public function detectParameters($url) {
+ /**
+ * Expected: https://<...>.indeed.<...>/cmp/[/reviews][/...]
+ *
+ * Note that most users will be redirected to their localized version
+ * of the page, which adds the language code to the host. For example,
+ * "en.indeed.com" or "www.indeed.fr" (see link[rel="alternate"]). At
+ * least each of the sites have ".indeed." in the name.
+ */
+
+ if(filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) === false
+ || stristr($url, '.indeed.') === false) {
+ return null;
+ }
+
+ $url_components = parse_url($url);
+ $path_segments = array_values(array_filter(explode('/', $url_components['path'])));
+
+ if(count($path_segments) < 2 || $path_segments[0] !== 'cmp') {
+ return null;
+ }
+
+ $language = array_search('https://' . $url_components['host'] . '/', self::SITES);
+ if($language === false) {
+ return null;
+ }
+
+ $limit = self::PARAMETERS['global']['limit']['defaultValue'] ?: 20;
+ $company = $path_segments[1];
+
+ return array(
+ 'c' => $company,
+ 'language' => $language,
+ 'limit' => $limit,
+ );
+ }
+
+ private function beautifyComment($comment) {
+ foreach($comment->find('.cmp-bold') as $bold) {
+ $bold->tag = 'strong';
+ $bold->removeClass('cmp-bold');
+ }
+
+ return $comment;
+ }
+}
diff --git a/bridges/InstagramBridge.php b/bridges/InstagramBridge.php
index 1bf86072..679c4c0e 100644
--- a/bridges/InstagramBridge.php
+++ b/bridges/InstagramBridge.php
@@ -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,141 @@ 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];
- } else {
- $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l';
- $item['content'] = '';
- $item['content'] .= '';
- $item['content'] .= '
' . 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':
+ if($directLink) {
+ $mediaURI = $media->display_url;
+ } else {
+ $mediaURI = self::URI . 'p/' . $media->shortcode . '/media?size=l';
+ }
+ $item['content'] = '';
+ $item['content'] .= '';
+ $item['content'] .= '
' . nl2br(htmlentities($textContent));
+ $item['enclosures'] = array($mediaURI);
+ break;
+ case 'GraphVideo':
+ $data = $this->getInstagramVideoData($item['uri']);
+ $item['content'] = $data[0];
+ if($directLink) {
+ $item['enclosures'] = $data[1];
+ } else {
+ $item['enclosures'] = array(self::URI . 'p/' . $media->shortcode . '/media?size=l');
+ }
+ 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 = '';
-
- 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 .= '';
- $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 .= '
';
+ array_push($enclosures, $singleMedia->video_url);
+ } else {
+ if(in_array($singleMedia->display_url, $enclosures)) continue; // check if not added yet
+ $content .= '';
+ $content .= '';
+ $content .= '
';
+ array_push($enclosures, $singleMedia->display_url);
}
}
+ $content .= '
' . nl2br(htmlentities($textContent));
- return [$content, $enclosures];
+ return array($content, $enclosures);
+ }
+ // returns Video post's contents and enclosures
+ protected function getInstagramVideoData($uri) {
+ $mediaInfo = $this->getSinglePostData($uri);
+
+ $textContent = $this->getTextContent($mediaInfo);
+ $content = '
';
+ $content .= '
' . 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]);
+
+ }
}
diff --git a/bridges/InstructablesBridge.php b/bridges/InstructablesBridge.php
index 7e7d5428..e28c34b3 100644
--- a/bridges/InstructablesBridge.php
+++ b/bridges/InstructablesBridge.php
@@ -1,8 +1,7 @@
'Category',
'type' => 'list',
'values' => array(
- 'Play' => array(
- 'All' => '/play/',
- 'KNEX' => '/play/knex/',
- 'Offbeat' => '/play/offbeat/',
- 'Lego' => '/play/lego/',
- 'Airsoft' => '/play/airsoft/',
- 'Card Games' => '/play/card-games/',
- 'Guitars' => '/play/guitars/',
- 'Instruments' => '/play/instruments/',
- 'Magic Tricks' => '/play/magic-tricks/',
- 'Minecraft' => '/play/minecraft/',
- 'Music' => '/play/music/',
- 'Nerf' => '/play/nerf/',
- 'Nintendo' => '/play/nintendo/',
- 'Office Supplies' => '/play/office-supplies/',
- 'Paintball' => '/play/paintball/',
- 'Paper Airplanes' => '/play/paper-airplanes/',
- 'Party Tricks' => '/play/party-tricks/',
- 'PlayStation' => '/play/playstation/',
- 'Pranks and Humor' => '/play/pranks-and-humor/',
- 'Puzzles' => '/play/puzzles/',
- 'Siege Engines' => '/play/siege-engines/',
- 'Sports' => '/play/sports/',
- 'Table Top' => '/play/table-top/',
- 'Toys' => '/play/toys/',
- 'Video Games' => '/play/video-games/',
- 'Wii' => '/play/wii/',
- 'Xbox' => '/play/xbox/',
- 'Yo-Yo' => '/play/yo-yo/',
- ),
- 'Craft' => array(
- 'All' => '/craft/',
- 'Art' => '/craft/art/',
- 'Sewing' => '/craft/sewing/',
- 'Paper' => '/craft/paper/',
- 'Jewelry' => '/craft/jewelry/',
- 'Fashion' => '/craft/fashion/',
- 'Books & Journals' => '/craft/books-and-journals/',
- 'Cards' => '/craft/cards/',
- 'Clay' => '/craft/clay/',
- 'Duct Tape' => '/craft/duct-tape/',
- 'Embroidery' => '/craft/embroidery/',
- 'Felt' => '/craft/felt/',
- 'Fiber Arts' => '/craft/fiber-arts/',
- 'Gifts & Wrapping' => '/craft/gifts-and-wrapping/',
- 'Knitting & Crocheting' => '/craft/knitting-and-crocheting/',
- 'Leather' => '/craft/leather/',
- 'Mason Jars' => '/craft/mason-jars/',
- 'No-Sew' => '/craft/no-sew/',
- 'Parties & Weddings' => '/craft/parties-and-weddings/',
- 'Print Making' => '/craft/print-making/',
- 'Soap' => '/craft/soap/',
- 'Wallets' => '/craft/wallets/',
- ),
- 'Technology' => array(
- 'All' => '/technology/',
- 'Electronics' => '/technology/electronics/',
- 'Arduino' => '/technology/arduino/',
- 'Photography' => '/technology/photography/',
- 'Leds' => '/technology/leds/',
- 'Science' => '/technology/science/',
- 'Reuse' => '/technology/reuse/',
- 'Apple' => '/technology/apple/',
- 'Computers' => '/technology/computers/',
- '3D Printing' => '/technology/3D-Printing/',
- 'Robots' => '/technology/robots/',
- 'Art' => '/technology/art/',
- 'Assistive Tech' => '/technology/assistive-technology/',
- 'Audio' => '/technology/audio/',
- 'Clocks' => '/technology/clocks/',
- 'CNC' => '/technology/cnc/',
- 'Digital Graphics' => '/technology/digital-graphics/',
- 'Gadgets' => '/technology/gadgets/',
- 'Kits' => '/technology/kits/',
- 'Laptops' => '/technology/laptops/',
- 'Lasers' => '/technology/lasers/',
- 'Linux' => '/technology/linux/',
- 'Microcontrollers' => '/technology/microcontrollers/',
- 'Microsoft' => '/technology/microsoft/',
- 'Mobile' => '/technology/mobile/',
- 'Raspberry Pi' => '/technology/raspberry-pi/',
- 'Remote Control' => '/technology/remote-control/',
- 'Sensors' => '/technology/sensors/',
- 'Software' => '/technology/software/',
- 'Soldering' => '/technology/soldering/',
- 'Speakers' => '/technology/speakers/',
- 'Steampunk' => '/technology/steampunk/',
- 'Tools' => '/technology/tools/',
- 'USB' => '/technology/usb/',
- 'Wearables' => '/technology/wearables/',
- 'Websites' => '/technology/websites/',
- 'Wireless' => '/technology/wireless/',
+ 'Circuits' => array(
+ 'All' => '/circuits/',
+ 'Apple' => '/circuits/apple/projects/',
+ 'Arduino' => '/circuits/arduino/projects/',
+ 'Art' => '/circuits/art/projects/',
+ 'Assistive Tech' => '/circuits/assistive-tech/projects/',
+ 'Audio' => '/circuits/audio/projects/',
+ 'Cameras' => '/circuits/cameras/projects/',
+ 'Clocks' => '/circuits/clocks/projects/',
+ 'Computers' => '/circuits/computers/projects/',
+ 'Electronics' => '/circuits/electronics/projects/',
+ 'Gadgets' => '/circuits/gadgets/projects/',
+ 'Lasers' => '/circuits/lasers/projects/',
+ 'LEDs' => '/circuits/leds/projects/',
+ 'Linux' => '/circuits/linux/projects/',
+ 'Microcontrollers' => '/circuits/microcontrollers/projects/',
+ 'Microsoft' => '/circuits/microsoft/projects/',
+ 'Mobile' => '/circuits/mobile/projects/',
+ 'Raspberry Pi' => '/circuits/raspberry-pi/projects/',
+ 'Remote Control' => '/circuits/remote-control/projects/',
+ 'Reuse' => '/circuits/reuse/projects/',
+ 'Robots' => '/circuits/robots/projects/',
+ 'Sensors' => '/circuits/sensors/projects/',
+ 'Software' => '/circuits/software/projects/',
+ 'Soldering' => '/circuits/soldering/projects/',
+ 'Speakers' => '/circuits/speakers/projects/',
+ 'Tools' => '/circuits/tools/projects/',
+ 'USB' => '/circuits/usb/projects/',
+ 'Wearables' => '/circuits/wearables/projects/',
+ 'Websites' => '/circuits/websites/projects/',
+ 'Wireless' => '/circuits/wireless/projects/',
),
'Workshop' => array(
'All' => '/workshop/',
- 'Woodworking' => '/workshop/woodworking/',
- 'Tools' => '/workshop/tools/',
- 'Gardening' => '/workshop/gardening/',
- 'Cars' => '/workshop/cars/',
- 'Metalworking' => '/workshop/metalworking/',
- 'Cardboard' => '/workshop/cardboard/',
- 'Electric Vehicles' => '/workshop/electric-vehicles/',
- 'Energy' => '/workshop/energy/',
- 'Furniture' => '/workshop/furniture/',
- 'Home Improvement' => '/workshop/home-improvement/',
- 'Home Theater' => '/workshop/home-theater/',
- 'Hydroponics' => '/workshop/hydroponics/',
- 'Laser Cutting' => '/workshop/laser-cutting/',
- 'Lighting' => '/workshop/lighting/',
- 'Molds & Casting' => '/workshop/molds-and-casting/',
- 'Motorcycles' => '/workshop/motorcycles/',
- 'Organizing' => '/workshop/organizing/',
- 'Pallets' => '/workshop/pallets/',
- 'Repair' => '/workshop/repair/',
- 'Shelves' => '/workshop/shelves/',
- 'Solar' => '/workshop/solar/',
- 'Workbenches' => '/workshop/workbenches/',
+ '3D Printing' => '/workshop/3d-printing/projects/',
+ 'Cars' => '/workshop/cars/projects/',
+ 'CNC' => '/workshop/cnc/projects/',
+ 'Electric Vehicles' => '/workshop/electric-vehicles/projects/',
+ 'Energy' => '/workshop/energy/projects/',
+ 'Furniture' => '/workshop/furniture/projects/',
+ 'Home Improvement' => '/workshop/home-improvement/projects/',
+ 'Home Theater' => '/workshop/home-theater/projects/',
+ 'Hydroponics' => '/workshop/hydroponics/projects/',
+ 'Knives' => '/workshop/knives/projects/',
+ 'Laser Cutting' => '/workshop/laser-cutting/projects/',
+ 'Lighting' => '/workshop/lighting/projects/',
+ 'Metalworking' => '/workshop/metalworking/projects/',
+ 'Molds & Casting' => '/workshop/molds-and-casting/projects/',
+ 'Motorcycles' => '/workshop/motorcycles/projects/',
+ 'Organizing' => '/workshop/organizing/projects/',
+ 'Pallets' => '/workshop/pallets/projects/',
+ 'Repair' => '/workshop/repair/projects/',
+ 'Science' => '/workshop/science/projects/',
+ 'Shelves' => '/workshop/shelves/projects/',
+ 'Solar' => '/workshop/solar/projects/',
+ 'Tools' => '/workshop/tools/projects/',
+ 'Woodworking' => '/workshop/woodworking/projects/',
+ 'Workbenches' => '/workshop/workbenches/projects/',
),
- 'Home' => array(
- 'All' => '/home/',
- 'Halloween' => '/home/halloween/',
- 'Decorating' => '/home/decorating/',
- 'Organizing' => '/home/organizing/',
- 'Pets' => '/home/pets/',
- 'Life Hacks' => '/home/life-hacks/',
- 'Beauty' => '/home/beauty/',
- 'Christmas' => '/home/christmas/',
- 'Cleaning' => '/home/cleaning/',
- 'Education' => '/home/education/',
- 'Finances' => '/home/finances/',
- 'Gardening' => '/home/gardening/',
- 'Green' => '/home/green/',
- 'Health' => '/home/health/',
- 'Hiding Places' => '/home/hiding-places/',
- 'Holidays' => '/home/holidays/',
- 'Homesteading' => '/home/homesteading/',
- 'Kids' => '/home/kids/',
- 'Kitchen' => '/home/kitchen/',
- 'Life Skills' => '/home/life-skills/',
- 'Parenting' => '/home/parenting/',
- 'Pest Control' => '/home/pest-control/',
- 'Relationships' => '/home/relationships/',
- 'Reuse' => '/home/reuse/',
- 'Travel' => '/home/travel/',
+ 'Craft' => array(
+ 'All' => '/craft/',
+ 'Art' => '/craft/art/projects/',
+ 'Books & Journals' => '/craft/books-and-journals/projects/',
+ 'Cardboard' => '/craft/cardboard/projects/',
+ 'Cards' => '/craft/cards/projects/',
+ 'Clay' => '/craft/clay/projects/',
+ 'Costumes & Cosplay' => '/craft/costumes-and-cosplay/projects/',
+ 'Digital Graphics' => '/craft/digital-graphics/projects/',
+ 'Duct Tape' => '/craft/duct-tape/projects/',
+ 'Embroidery' => '/craft/embroidery/projects/',
+ 'Fashion' => '/craft/fashion/projects/',
+ 'Felt' => '/craft/felt/projects/',
+ 'Fiber Arts' => '/craft/fiber-arts/projects/',
+ 'Gift Wrapping' => '/craft/gift-wrapping/projects/',
+ 'Jewelry' => '/craft/jewelry/projects/',
+ 'Knitting & Crochet' => '/craft/knitting-and-crochet/projects/',
+ 'Leather' => '/craft/leather/projects/',
+ 'Mason Jars' => '/craft/mason-jars/projects/',
+ 'No-Sew' => '/craft/no-sew/projects/',
+ 'Paper' => '/craft/paper/projects/',
+ 'Parties & Weddings' => '/craft/parties-and-weddings/projects/',
+ 'Photography' => '/craft/photography/projects/',
+ 'Printmaking' => '/craft/printmaking/projects/',
+ 'Reuse' => '/craft/reuse/projects/',
+ 'Sewing' => '/craft/sewing/projects/',
+ 'Soapmaking' => '/craft/soapmaking/projects/',
+ 'Wallets' => '/craft/wallets/projects/',
+ ),
+ 'Cooking' => array(
+ 'All' => '/cooking/',
+ 'Bacon' => '/cooking/bacon/projects/',
+ 'BBQ & Grilling' => '/cooking/bbq-and-grilling/projects/',
+ 'Beverages' => '/cooking/beverages/projects/',
+ 'Bread' => '/cooking/bread/projects/',
+ 'Breakfast' => '/cooking/breakfast/projects/',
+ 'Cake' => '/cooking/cake/projects/',
+ 'Candy' => '/cooking/candy/projects/',
+ 'Canning & Preserving' => '/cooking/canning-and-preserving/projects/',
+ 'Cocktails & Mocktails' => '/cooking/cocktails-and-mocktails/projects/',
+ 'Coffee' => '/cooking/coffee/projects/',
+ 'Cookies' => '/cooking/cookies/projects/',
+ 'Cupcakes' => '/cooking/cupcakes/projects/',
+ 'Dessert' => '/cooking/dessert/projects/',
+ 'Homebrew' => '/cooking/homebrew/projects/',
+ 'Main Course' => '/cooking/main-course/projects/',
+ 'Pasta' => '/cooking/pasta/projects/',
+ 'Pie' => '/cooking/pie/projects/',
+ 'Pizza' => '/cooking/pizza/projects/',
+ 'Salad' => '/cooking/salad/projects/',
+ 'Sandwiches' => '/cooking/sandwiches/projects/',
+ 'Snacks & Appetizers' => '/cooking/snacks-and-appetizers/projects/',
+ 'Soups & Stews' => '/cooking/soups-and-stews/projects/',
+ 'Vegetarian & Vegan' => '/cooking/vegetarian-and-vegan/projects/',
+ ),
+ 'Living' => array(
+ 'All' => '/living/',
+ 'Beauty' => '/living/beauty/projects/',
+ 'Christmas' => '/living/christmas/projects/',
+ 'Cleaning' => '/living/cleaning/projects/',
+ 'Decorating' => '/living/decorating/projects/',
+ 'Education' => '/living/education/projects/',
+ 'Gardening' => '/living/gardening/projects/',
+ 'Halloween' => '/living/halloween/projects/',
+ 'Health' => '/living/health/projects/',
+ 'Hiding Places' => '/living/hiding-places/projects/',
+ 'Holidays' => '/living/holidays/projects/',
+ 'Homesteading' => '/living/homesteading/projects/',
+ 'Kids' => '/living/kids/projects/',
+ 'Kitchen' => '/living/kitchen/projects/',
+ 'LEGO & KNEX' => '/living/lego-and-knex/projects/',
+ 'Life Hacks' => '/living/life-hacks/projects/',
+ 'Music' => '/living/music/projects/',
+ 'Office Supply Hacks' => '/living/office-supply-hacks/projects/',
+ 'Organizing' => '/living/organizing/projects/',
+ 'Pest Control' => '/living/pest-control/projects/',
+ 'Pets' => '/living/pets/projects/',
+ 'Pranks, Tricks, & Humor' => '/living/pranks-tricks-and-humor/projects/',
+ 'Relationships' => '/living/relationships/projects/',
+ 'Toys & Games' => '/living/toys-and-games/projects/',
+ 'Travel' => '/living/travel/projects/',
+ 'Video Games' => '/living/video-games/projects/',
),
'Outside' => array(
'All' => '/outside/',
- 'Bikes' => '/outside/bikes/',
- 'Survival' => '/outside/survival/',
- 'Backyard' => '/outside/backyard/',
- 'Beach' => '/outside/beach/',
- 'Birding' => '/outside/birding/',
- 'Boats' => '/outside/boats/',
- 'Camping' => '/outside/camping/',
- 'Climbing' => '/outside/climbing/',
- 'Fire' => '/outside/fire/',
- 'Fishing' => '/outside/fishing/',
- 'Hunting' => '/outside/hunting/',
- 'Kites' => '/outside/kites/',
- 'Knives' => '/outside/knives/',
- 'Knots' => '/outside/knots/',
- 'Paracord' => '/outside/paracord/',
- 'Rockets' => '/outside/rockets/',
- 'Skateboarding' => '/outside/skateboarding/',
- 'Snow' => '/outside/snow/',
- 'Water' => '/outside/water/',
+ 'Backyard' => '/outside/backyard/projects/',
+ 'Beach' => '/outside/beach/projects/',
+ 'Bikes' => '/outside/bikes/projects/',
+ 'Birding' => '/outside/birding/projects/',
+ 'Boats' => '/outside/boats/projects/',
+ 'Camping' => '/outside/camping/projects/',
+ 'Climbing' => '/outside/climbing/projects/',
+ 'Fire' => '/outside/fire/projects/',
+ 'Fishing' => '/outside/fishing/projects/',
+ 'Hunting' => '/outside/hunting/projects/',
+ 'Kites' => '/outside/kites/projects/',
+ 'Knots' => '/outside/knots/projects/',
+ 'Launchers' => '/outside/launchers/projects/',
+ 'Paracord' => '/outside/paracord/projects/',
+ 'Rockets' => '/outside/rockets/projects/',
+ 'Siege Engines' => '/outside/siege-engines/projects/',
+ 'Skateboarding' => '/outside/skateboarding/projects/',
+ 'Snow' => '/outside/snow/projects/',
+ 'Sports' => '/outside/sports/projects/',
+ 'Survival' => '/outside/survival/projects/',
+ 'Water' => '/outside/water/projects/',
),
- 'Food' => array(
- 'All' => '/food/',
- 'Dessert' => '/food/dessert/',
- 'Snacks & Appetizers' => '/food/snacks-and-appetizers/',
- 'Bacon' => '/food/bacon/',
- 'BBQ & Grilling' => '/food/bbq-and-grilling/',
- 'Beverages' => '/food/beverages/',
- 'Bread' => '/food/bread/',
- 'Breakfast' => '/food/breakfast/',
- 'Cake' => '/food/cake/',
- 'Candy' => '/food/candy/',
- 'Canning & Preserves' => '/food/canning-and-preserves/',
- 'Cocktails & Mocktails' => '/food/cocktails-and-mocktails/',
- 'Coffee' => '/food/coffee/',
- 'Cookies' => '/food/cookies/',
- 'Cupcakes' => '/food/cupcakes/',
- 'Homebrew' => '/food/homebrew/',
- 'Main Course' => '/food/main-course/',
- 'Pasta' => '/food/pasta/',
- 'Pie' => '/food/pie/',
- 'Pizza' => '/food/pizza/',
- 'Salad' => '/food/salad/',
- 'Sandwiches' => '/food/sandwiches/',
- 'Soups & Stews' => '/food/soups-and-stews/',
- 'Vegetarian & Vegan' => '/food/vegetarian-and-vegan/',
+ 'Makeymakey' => array(
+ 'All' => '/makeymakey/',
+ 'Makey Makey on Instructables' => '/makeymakey/',
+ ),
+ 'Teachers' => array(
+ 'All' => '/teachers/',
+ 'ELA' => '/teachers/ela/projects/',
+ 'Math' => '/teachers/math/projects/',
+ 'Science' => '/teachers/science/projects/',
+ 'Social Studies' => '/teachers/social-studies/projects/',
+ 'Engineering' => '/teachers/engineering/projects/',
+ 'Coding' => '/teachers/coding/projects/',
+ 'Electronics' => '/teachers/electronics/projects/',
+ 'Robotics' => '/teachers/robotics/projects/',
+ 'Arduino' => '/teachers/arduino/projects/',
+ 'CNC' => '/teachers/cnc/projects/',
+ 'Laser Cutting' => '/teachers/laser-cutting/projects/',
+ '3D Printing' => '/teachers/3d-printing/projects/',
+ '3D Design' => '/teachers/3d-design/projects/',
+ 'Art' => '/teachers/art/projects/',
+ 'Music' => '/teachers/music/projects/',
+ 'Theatre' => '/teachers/theatre/projects/',
+ 'Wood Shop' => '/teachers/wood-shop/projects/',
+ 'Metal Shop' => '/teachers/metal-shop/projects/',
+ 'Resources' => '/teachers/resources/projects/',
),
- 'Costumes' => array(
- 'All' => '/costumes/',
- 'Props' => '/costumes/props-and-accessories/',
- 'Animals' => '/costumes/animals/',
- 'Comics' => '/costumes/comics/',
- 'Fantasy' => '/costumes/fantasy/',
- 'For Kids' => '/costumes/for-kids/',
- 'For Pets' => '/costumes/for-pets/',
- 'Funny' => '/costumes/funny/',
- 'Games' => '/costumes/games/',
- 'Historic & Futuristic' => '/costumes/historic-and-futuristic/',
- 'Makeup' => '/costumes/makeup/',
- 'Masks' => '/costumes/masks/',
- 'Scary' => '/costumes/scary/',
- 'TV & Movies' => '/costumes/tv-and-movies/',
- 'Weapons & Armor' => '/costumes/weapons-and-armor/',
- )
),
'title' => 'Select your category (required)',
- 'defaultValue' => 'Technology'
+ 'defaultValue' => 'Circuits'
),
'filter' => array(
'name' => 'Filter',
@@ -252,65 +233,70 @@ class InstructablesBridge extends BridgeAbstract {
)
);
- private $uri;
-
public function collectData() {
// Enable the following line to get the category list (dev mode)
// $this->listCategories();
- $this->uri = static::URI;
+ $html = getSimpleHTMLDOM($this->getURI())
+ or returnServerError('Error loading category ' . $this->getURI());
+ $html = defaultLinkTo($html, $this->getURI());
- switch($this->queriedContext) {
- case 'Category': $this->uri .= $this->getInput('category') . $this->getInput('filter');
- }
+ $covers = $html->find('
+ .category-projects-list > div,
+ .category-landing-projects-list > div,
+ ');
- $html = getSimpleHTMLDOM($this->uri)
- or returnServerError('Error loading category ' . $this->uri);
-
- foreach($html->find('ul.explore-covers-list li') as $cover) {
+ foreach($covers as $cover) {
$item = array();
- $item['uri'] = static::URI . $cover->find('a.cover-image', 0)->href;
- $item['title'] = $cover->find('.title', 0)->innertext;
+ $item['uri'] = $cover->find('a.ible-title', 0)->href;
+ $item['title'] = $cover->find('a.ible-title', 0)->innertext;
$item['author'] = $this->getCategoryAuthor($cover);
$item['content'] = 'src
+ . $cover->find('img', 0)->getAttribute('data-src')
. '>';
- $image = str_replace('.RECTANGLE1', '.LARGE', $cover->find('a.cover-image img', 0)->src);
- $item['enclosures'] = [$image];
+ $item['enclosures'][] = str_replace(
+ '.RECTANGLE1',
+ '.LARGE',
+ $cover->find('img', 0)->getAttribute('data-src')
+ );
$this->items[] = $item;
}
}
public function getName() {
- if(!is_null($this->getInput('category'))
- && !is_null($this->getInput('filter'))) {
- foreach(self::PARAMETERS[$this->queriedContext]['category']['values'] as $key => $value) {
- $subcategory = array_search($this->getInput('category'), $value);
+ switch($this->queriedContext) {
+ case 'Category': {
+ foreach(self::PARAMETERS[$this->queriedContext]['category']['values'] as $key => $value) {
+ $subcategory = array_search($this->getInput('category'), $value);
- if($subcategory !== false)
- break;
- }
+ if($subcategory !== false)
+ break;
+ }
- $filter = array_search(
- $this->getInput('filter'),
- self::PARAMETERS[$this->queriedContext]['filter']['values']
- );
+ $filter = array_search(
+ $this->getInput('filter'),
+ self::PARAMETERS[$this->queriedContext]['filter']['values']
+ );
- return $subcategory . ' (' . $filter . ') - ' . static::NAME;
+ return $subcategory . ' (' . $filter . ') - ' . static::NAME;
+ } break;
}
return parent::getName();
}
public function getURI() {
- if(!is_null($this->getInput('category'))
- && !is_null($this->getInput('filter'))) {
- return $this->uri;
+ switch($this->queriedContext) {
+ case 'Category': {
+ return self::URI
+ . $this->getInput('category')
+ . $this->getInput('filter');
+ } break;
}
return parent::getURI();
@@ -321,24 +307,32 @@ class InstructablesBridge extends BridgeAbstract {
* parameters list)
*/
private function listCategories(){
- // Use arbitrary category to receive full list
- $html = getSimpleHTMLDOM(self::URI . '/technology/');
- foreach($html->find('.channel a') as $channel) {
- $name = html_entity_decode(trim($channel->innertext));
+ // Use home page to acquire main categories
+ $html = getSimpleHTMLDOM(self::URI);
+ $html = defaultLinkTo($html, self::URI);
- // Remove unwanted entities
- $name = str_replace("'", '', $name);
- $name = str_replace(''', '', $name);
+ foreach($html->find('.home-content-explore-link') as $category) {
- $uri = $channel->href;
+ // Use arbitrary category to receive full list
+ $html = getSimpleHTMLDOM($category->href);
- $category = explode('/', $uri)[1];
+ foreach($html->find('.channel-thumbnail a') as $channel) {
+ $name = html_entity_decode(trim($channel->title));
- if(!isset($categories)
- || !array_key_exists($category, $categories)
- || !in_array($uri, $categories[$category]))
- $categories[$category][$name] = $uri;
+ // Remove unwanted entities
+ $name = str_replace("'", '', $name);
+ $name = str_replace(''', '', $name);
+
+ $uri = $channel->href;
+
+ $category_name = explode('/', $uri)[1];
+
+ if(!isset($categories)
+ || !array_key_exists($category_name, $categories)
+ || !in_array($uri, $categories[$category_name]))
+ $categories[$category_name][$name] = $uri;
+ }
}
// Build PHP array manually
@@ -360,9 +354,9 @@ class InstructablesBridge extends BridgeAbstract {
*/
private function getCategoryAuthor($cover) {
return 'href
+ . $cover->find('.ible-author a', 0)->href
. '>'
- . $cover->find('span.author a', 0)->innertext
+ . $cover->find('.ible-author a', 0)->innertext
. '';
}
}
diff --git a/bridges/InternetArchiveBridge.php b/bridges/InternetArchiveBridge.php
new file mode 100644
index 00000000..dca1c32a
--- /dev/null
+++ b/bridges/InternetArchiveBridge.php
@@ -0,0 +1,293 @@
+ array(
+ 'username' => array(
+ 'name' => 'Username',
+ 'type' => 'text',
+ 'required' => true,
+ 'exampleValue' => '@verifiedjoseph',
+ ),
+ 'content' => array(
+ 'name' => 'Content',
+ 'type' => 'list',
+ 'values' => array(
+ 'Uploads' => 'uploads',
+ 'Posts' => 'posts',
+ 'Reviews' => 'reviews',
+ 'Collections' => 'collections',
+ 'Web Archives' => 'web-archive',
+ ),
+ 'defaultValue' => 'uploads',
+ )
+ )
+ );
+
+ const CACHE_TIMEOUT = 900; // 15 mins
+
+ private $skipClasses = array(
+ 'item-ia mobile-header hidden-tiles',
+ 'item-ia account-ia'
+ );
+
+ public function collectData() {
+
+ $html = getSimpleHTMLDOM($this->getURI())
+ or returnServerError('Could not request: ' . $this->getURI());
+
+ $html = defaultLinkTo($html, $this->getURI());
+
+ if ($this->getInput('content') !== 'posts') {
+
+ $detailsDivNumber = 0;
+
+ foreach ($html->find('div.results > div[data-id]') as $index => $result) {
+ $item = array();
+
+ if (in_array($result->class, $this->skipClasses)) {
+ continue;
+ }
+
+ switch($result->class) {
+ case 'item-ia':
+
+ switch($this->getInput('content')) {
+ case 'reviews':
+ $item = $this->processReview($result);
+ break;
+ case 'uploads':
+ $item = $this->processUpload($result);
+ break;
+ }
+
+ break;
+ case 'item-ia url-item':
+ $item = $this->processWebArchives($result);
+ break;
+ case 'item-ia collection-ia':
+ $item = $this->processCollection($result);
+ break;
+ }
+
+ if ($this->getInput('content') !== 'reviews') {
+ $hiddenDetails = $this->processHiddenDetails($html, $detailsDivNumber, $item);
+
+ $this->items[] = array_merge($item, $hiddenDetails);
+ } else {
+
+ $this->items[] = $item;
+
+ }
+
+ $detailsDivNumber++;
+ }
+ }
+
+ if ($this->getInput('content') === 'posts') {
+ $this->items = $this->processPosts($html);
+ }
+ }
+
+ public function getURI() {
+
+ if (!is_null($this->getInput('username')) && !is_null($this->getInput('content'))) {
+ return self::URI . '/details/' . $this->processUsername() . '&tab=' . $this->getInput('content');
+ }
+
+ return parent::getURI();
+ }
+
+ public function getName() {
+
+ if (!is_null($this->getInput('username')) && !is_null($this->getInput('content'))) {
+
+ $contentValues = array_flip(self::PARAMETERS['Account']['content']['values']);
+
+ return $contentValues[$this->getInput('content')] . ' - '
+ . $this->processUsername() . ' - Internet Archive';
+ }
+
+ return parent::getName();
+ }
+
+ private function processUsername() {
+
+ if (substr($this->getInput('username'), 0, 1) !== '@') {
+ return '@' . $this->getInput('username');
+ }
+
+ return $this->getInput('username');
+ }
+
+ private function processUpload($result) {
+
+ $item = array();
+
+ $collection = $result->find('a.stealth', 0);
+ $collectionLink = self::URI . $collection->href;
+ $collectionTitle = $collection->find('div.item-parent-ttl', 0)->plaintext;
+
+ $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'] = $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;
+ }
+
+ $item['content'] = <<Media Type: {$result->attr['data-mediatype']}
+Collection: {$collectionTitle}
+EOD;
+
+ $item['enclosures'][] = self::URI . $result->find('img.item-img', 0)->source;
+
+ return $item;
+ }
+
+ private function processReview($result) {
+
+ $item = array();
+
+ $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'] = $result->find('div.review-title', 0)->children(0)->href;
+
+ if ($result->find('div.by.C.C4', 0)->children(2)) {
+ $item['author'] = $result->find('div.by.C.C4', 0)->children(2)->plaintext;
+ }
+
+ $item['content'] = <<Subject: {$result->find('div.review-title', 0)->plaintext}
+{$result->find('div.hidden-lists.review' , 0)->children(1)->plaintext}
+EOD;
+
+ $item['enclosures'][] = self::URI . $result->find('img.item-img', 0)->source;
+
+ return $item;
+ }
+
+ private function processWebArchives($result) {
+
+ $item = array();
+
+ $item['title'] = trim($result->find('div.ttl', 0)->plaintext);
+ $item['timestamp'] = strtotime($result->find('div.hidden-lists', 0)->children(0)->plaintext);
+ $item['uri'] = $result->find('div.item-ttl.C.C2 > a', 0)->href;
+
+ $item['content'] = <<processUsername()} archived {$result->find('div.ttl', 0)->plaintext}
+EOD;
+
+ $item['enclosures'][] = $result->find('img.item-img', 0)->source;
+
+ return $item;
+ }
+
+ private function processCollection($result) {
+
+ $item = array();
+
+ $title = trim($result->find('div.collection-title.C.C2', 0)->children(0)->plaintext);
+ $itemCount = strtolower(trim($result->find('div.num-items.topinblock', 0)->plaintext));
+
+ $item['title'] = $title . ' (' . $itemCount . ')';
+ $item['timestamp'] = strtotime($result->find('div.hidden-tiles.pubdate.C.C3', 0)->children(0)->plaintext);
+ $item['uri'] = $result->find('div.collection-title.C.C2 > a', 0)->href;
+
+ $item['content'] = '';
+
+ if ($result->find('img.item-img', 0)) {
+ $item['enclosures'][] = self::URI . $result->find('img.item-img', 0)->source;
+ }
+
+ return $item;
+ }
+
+ private function processHiddenDetails($html, $detailsDivNumber, $item) {
+
+ $description = '';
+
+ if ($html->find('div.details-ia.hidden-tiles', $detailsDivNumber)) {
+ $detailsDiv = $html->find('div.details-ia.hidden-tiles', $detailsDivNumber);
+
+ if ($detailsDiv->find('div.C234', 0)->children(0)) {
+ $description = $detailsDiv->find('div.C234', 0)->children(0)->plaintext;
+
+ $detailsDiv->find('div.C234', 0)->children(0)->innertext = '';
+ }
+
+ $topics = trim($detailsDiv->find('div.C234', 0)->plaintext);
+
+ if (!empty($topics)) {
+ $topics = trim($detailsDiv->find('div.C234', 0)->plaintext);
+ $topics = trim(substr($topics, 7));
+
+ $item['categories'] = explode(',', $topics);
+ }
+
+ $item['content'] = '' . $description . '
' . $item['content'];
+ }
+
+ return $item;
+ }
+
+ private function processPosts($html) {
+
+ $items = array();
+
+ foreach ($html->find('table.forumTable > tr') as $index => $tr) {
+ $item = array();
+
+ if ($index === 0) {
+ continue;
+ }
+
+ $item['title'] = $tr->find('td', 0)->plaintext;
+ $item['timestamp'] = strtotime($tr->find('td', 4)->children(0)->plaintext);
+ $item['uri'] = $tr->find('td', 0)->children(0)->href;
+
+ $formLink = <<{$tr->find('td', 2)->children(0)->plaintext}
+EOD;
+
+ $postDate = $tr->find('td', 4)->children(0)->plaintext;
+
+ $postPageHtml = getSimpleHTMLDOMCached($item['uri'], 3600)
+ or returnServerError('Could not request: ' . $item['uri']);
+
+ $postPageHtml = defaultLinkTo($postPageHtml, $this->getURI());
+
+ $post = $postPageHtml->find('div.box.well.well-sm', 0);
+
+ $parentLink = '';
+ $replyLink = <<Reply
+EOD;
+
+ if ($post->find('a', 1)->innertext = 'See parent post') {
+ $parentLink = <<View parent post
+EOD;
+ }
+
+ $post->find('h1', 0)->outertext = '';
+ $post->find('h2', 0)->outertext = '';
+
+ $item['content'] = <<{$post->innertext}{$replyLink} - {$parentLink} - Posted in {$formLink} on {$postDate}
+EOD;
+
+ $items[] = $item;
+
+ if (count($items) >= 10) {
+ break;
+ }
+ }
+ return $items;
+ }
+}
diff --git a/bridges/JapanExpoBridge.php b/bridges/JapanExpoBridge.php
index 17901711..7906ec0d 100644
--- a/bridges/JapanExpoBridge.php
+++ b/bridges/JapanExpoBridge.php
@@ -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 '';
@@ -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 = 'find('article')
or returnServerError('Unable to find articles!');
+ $limit = $this->getInput('limit') ?: 0;
+
// Go through all articles
foreach($articles as $article) {
@@ -116,7 +134,7 @@ class KununuBridge extends BridgeAbstract {
$item = array();
$item['author'] = $this->extractArticleAuthorPosition($article);
- $item['timestamp'] = strtotime($date);
+ $item['timestamp'] = strtotime($date->content);
$item['title'] = $rating->getAttribute('aria-label')
. ' : '
. strip_tags($summary->innertext);
@@ -131,6 +149,8 @@ class KununuBridge extends BridgeAbstract {
$this->items[] = $item;
+ if ($limit > 0 && count($this->items) >= $limit) break;
+
}
}
@@ -175,7 +195,32 @@ class KununuBridge extends BridgeAbstract {
$description = $article->find('[itemprop=reviewBody]', 0)
or returnServerError('Cannot find article description!');
- return $description->innertext;
+ $retVal = $description->innertext;
+
+ if($this->getInput('include_ratings')
+ && ($ratings = $article->find('.review-ratings .rating-group'))) {
+ $retVal .= (empty($retVal) ? '' : '
') . '';
+ foreach($ratings as $rating) {
+ $retVal .= <<
+ {$rating->find('.rating-title', 0)->plaintext}
+ | {$rating->find('.rating-badge', 0)->plaintext}
+
+EOD;
+ }
+ $retVal .= ' |
';
+ }
+
+ if($this->getInput('include_benefits')
+ && ($benefits = $article->find('benefit'))) {
+ $retVal .= (empty($retVal) ? '' : '
') . '';
+ foreach($benefits as $benefit) {
+ $retVal .= "- {$benefit->plaintext}
";
+ }
+ $retVal .= '
';
+ }
+
+ return $retVal;
}
/**
diff --git a/bridges/LaCentraleBridge.php b/bridges/LaCentraleBridge.php
new file mode 100644
index 00000000..baaaa582
--- /dev/null
+++ b/bridges/LaCentraleBridge.php
@@ -0,0 +1,477 @@
+ 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'] = '
+
+
Variation : ' . $item['version']
+ . '
Prix : ' . $item['price']
+ . '
Année : ' . $item['year']
+ . '
Kilométrage : ' . $item['mileage']
+ . '
Département : ' . $item['departement']
+ . '
Type de vendeur : ' . $item['sellerType'];
+
+ $this->items[] = $item;
+
+ }
+ }
+}
diff --git a/bridges/LeBonCoinBridge.php b/bridges/LeBonCoinBridge.php
index 36196cb1..fc1432e3 100644
--- a/bridges/LeBonCoinBridge.php
+++ b/bridges/LeBonCoinBridge.php
@@ -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;
diff --git a/bridges/ListverseBridge.php b/bridges/ListverseBridge.php
new file mode 100644
index 00000000..f597c0b4
--- /dev/null
+++ b/bridges/ListverseBridge.php
@@ -0,0 +1,22 @@
+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;
+ }
+}
diff --git a/bridges/MangareaderBridge.php b/bridges/MangareaderBridge.php
index 9ecb0feb..a41113a3 100644
--- a/bridges/MangareaderBridge.php
+++ b/bridges/MangareaderBridge.php
@@ -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)';
diff --git a/bridges/MastodonBridge.php b/bridges/MastodonBridge.php
new file mode 100644
index 00000000..9e131b7d
--- /dev/null
+++ b/bridges/MastodonBridge.php
@@ -0,0 +1,89 @@
+ array(
+ 'name' => 'Canonical username (ex : @sebsauvage@framapiaf.org)',
+ 'required' => true,
+ ),
+ 'norep' => array(
+ 'name' => 'Without replies',
+ 'type' => 'checkbox',
+ 'title' => 'Only return initial toots'
+ ),
+ 'noboost' => array(
+ 'name' => 'Without boosts',
+ 'required' => false,
+ 'type' => 'checkbox',
+ 'title' => 'Hide boosts'
+ )
+ ));
+
+ public function getName(){
+ switch($this->queriedContext) {
+ case 'By username':
+ return $this->getInput('canusername');
+ default: return parent::getName();
+ }
+ }
+
+ protected function parseItem($newItem){
+ $item = parent::parseItem($newItem);
+
+ $content = str_get_html($item['content']);
+ $title = str_get_html($item['title']);
+
+ $item['title'] = $content->plaintext;
+
+ if(strlen($item['title']) > 75) {
+ $item['title'] = substr($item['title'], 0, strpos(wordwrap($item['title'], 75), "\n")) . '...';
+ }
+
+ if(strpos($title, 'shared a status by') !== false) {
+ if($this->getInput('noboost')) {
+ return null;
+ }
+
+ preg_match('/shared a status by (\S{0,})/', $title, $matches);
+ $item['title'] = 'Boost ' . $matches[1] . ' ' . $item['title'];
+ $item['author'] = $matches[1];
+ } else {
+ $item['author'] = $this->getInput('canusername');
+ }
+
+ // Check if it's a initial toot or a response
+ if($this->getInput('norep') && preg_match('/^@.+/', trim($content->plaintext))) {
+ return null;
+ }
+
+ return $item;
+ }
+
+ private function getInstance(){
+ preg_match('/^@[a-zA-Z0-9_]+@(.+)/', $this->getInput('canusername'), $matches);
+ return $matches[1];
+ }
+
+ private function getUsername(){
+ preg_match('/^@([a-zA-Z_0-9_]+)@.+/', $this->getInput('canusername'), $matches);
+ return $matches[1];
+ }
+
+ public function getURI(){
+ if($this->getInput('canusername'))
+ return 'https://' . $this->getInstance() . '/users/' . $this->getUsername() . '.atom';
+
+ return parent::getURI();
+ }
+
+ public function collectData(){
+ return $this->collectExpandableDatas($this->getURI());
+ }
+}
diff --git a/bridges/MediapartBridge.php b/bridges/MediapartBridge.php
index 15d1d3ea..f7fff4ab 100644
--- a/bridges/MediapartBridge.php
+++ b/bridges/MediapartBridge.php
@@ -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;
diff --git a/bridges/N26Bridge.php b/bridges/N26Bridge.php
index dd1c423c..ac43756b 100644
--- a/bridges/N26Bridge.php
+++ b/bridges/N26Bridge.php
@@ -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;
}
diff --git a/bridges/NFLRUSBridge.php b/bridges/NFLRUSBridge.php
new file mode 100644
index 00000000..739f4ab5
--- /dev/null
+++ b/bridges/NFLRUSBridge.php
@@ -0,0 +1,60 @@
+ '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;
+ }
+ }
+}
diff --git a/bridges/NYTBridge.php b/bridges/NYTBridge.php
new file mode 100644
index 00000000..687d0889
--- /dev/null
+++ b/bridges/NYTBridge.php
@@ -0,0 +1,26 @@
+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;
+ }
+}
diff --git a/bridges/NationalGeographicBridge.php b/bridges/NationalGeographicBridge.php
new file mode 100644
index 00000000..dfccd25c
--- /dev/null
+++ b/bridges/NationalGeographicBridge.php
@@ -0,0 +1,194 @@
+ array(
+ self::PARAMETER_TOPIC => array(
+ 'name' => 'Topic',
+ 'type' => 'list',
+ 'values' => array(
+ self::TOPIC_MAGAZINE => 'magazine',
+ self::TOPIC_LATEST_STORIES => 'latest-stories'
+ ),
+ 'title' => 'Select your topic',
+ 'defaultValue' => 'Magazine'
+ )
+ ),
+ 'global' => array(
+ self::PARAMETER_FULL_ARTICLE => array(
+ 'name' => 'Full Article',
+ 'type' => 'checkbox',
+ 'title' => 'Enable to load full articles (takes longer)'
+ )
+ )
+ );
+
+ private $topicName = '';
+
+ public function getURI() {
+ switch ($this->queriedContext) {
+ case self::CONTEXT_BY_TOPIC: {
+ return self::URI . $this->getInput(self::PARAMETER_TOPIC);
+ } break;
+ default: {
+ return parent::getURI();
+ }
+ }
+ }
+
+ public function collectData() {
+ $this->topicName = $this->getTopicName($this->getInput(self::PARAMETER_TOPIC));
+
+ switch($this->topicName) {
+ case self::TOPIC_MAGAZINE: {
+ return $this->collectMagazine();
+ } break;
+ case self::TOPIC_LATEST_STORIES: {
+ return $this->collectLatestStories();
+ } break;
+ default: {
+ returnServerError('Unknown topic: "' . $this->topicName . '"');
+ }
+ }
+ }
+
+ public function getName() {
+ switch ($this->queriedContext) {
+ case self::CONTEXT_BY_TOPIC: {
+ return static::NAME . ': ' . $this->topicName;
+ } break;
+ default: {
+ return parent::getName();
+ }
+ }
+ }
+
+ private function getTopicName($topic) {
+ return array_search($topic, static::PARAMETERS[self::CONTEXT_BY_TOPIC][self::PARAMETER_TOPIC]['values']);
+ }
+
+ private function collectMagazine() {
+ $uri = $this->getURI();
+
+ $html = getSimpleHTMLDOM($uri)
+ or returnServerError('Could not request ' . $uri);
+
+ $script = $html->find('#lead-component script')[0];
+
+ $json = json_decode($script->innertext, true);
+
+ // This is probably going to break in the future, fix it then :)
+ foreach($json['body']['0']['multilayout_promo_beta']['stories'] as $story) {
+ $this->addStory($story);
+ }
+ }
+
+ private function collectLatestStories() {
+ $uri = self::URI . 'latest-stories/_jcr_content/content/hubfeed.promo-hub-feed-all-stories.json';
+
+ $json_raw = getContents($uri)
+ or returnServerError('Could not request ' . $uri);
+
+ foreach(json_decode($json_raw, true) as $story) {
+ $this->addStory($story);
+ }
+ }
+
+ private function addStory($story) {
+ $title = 'Unknown title';
+ $content = '';
+
+ foreach($story['components'] as $component) {
+ switch($component['content_type']) {
+ case 'title': {
+ $title = $component['title']['text'];
+ } break;
+ case 'dek': {
+ $content = $component['dek']['text'];
+ } break;
+ }
+ }
+
+ $item = array();
+
+ $item['uri'] = $story['uri'];
+ $item['title'] = $title;
+
+ // if full article is requested!
+ if ($this->getInput(self::PARAMETER_FULL_ARTICLE))
+ $item['content'] = $this->getFullArticle($item['uri']);
+ else
+ $item['content'] = $content;
+
+ if (isset($story['promo_image'])) {
+ switch($story['promo_image']['content_type']) {
+ case 'image': {
+ $item['enclosures'][] = $story['promo_image']['image']['uri'];
+ } break;
+ }
+ }
+
+ if (isset($story['lead_media'])) {
+ $media = $story['lead_media'];
+ switch($media['content_type']) {
+ case 'image': {
+ // Don't add if promo_image was added
+ if (empty($item['enclosures']))
+ $item['enclosures'][] = $media['image']['uri'];
+ } break;
+ case 'image_gallery': {
+ foreach($media['image_gallery']['images'] as $image) {
+ $item['enclosures'][] = $image['uri'];
+ }
+ } break;
+ }
+ }
+
+ $this->items[] = $item;
+ }
+
+ private function getFullArticle($uri) {
+ $html = getSimpleHTMLDOMCached($uri)
+ or returnServerError('Could not load ' . $uri);
+
+ $html = defaultLinkTo($html, $uri);
+
+ $content = '';
+
+ foreach($html->find('
+ .content > .smartbody.text,
+ .content > .section.image script[type="text/json"],
+ .content > .section.image span[itemprop="caption"],
+ .content > .section.inline script[type="text/json"]
+ ') as $element) {
+ if ($element->tag === 'script') {
+ $json = json_decode($element->innertext, true);
+ if (isset($json['src'])) {
+ $content .= '';
+ } elseif (isset($json['galleryType']) && isset($json['endpoint'])) {
+ $doc = getContents($json['endpoint'])
+ or returnServerError('Could not load ' . $json['endpoint']);
+ $json = json_decode($doc, true);
+ foreach($json['items'] as $item) {
+ $content .= '' . $item['caption'] . '
';
+ $content .= '';
+ }
+ }
+ } else {
+ $content .= $element->outertext;
+ }
+ }
+
+ return $content;
+ }
+}
diff --git a/bridges/NiceMatinBridge.php b/bridges/NiceMatinBridge.php
index 117c7794..b0af7608 100644
--- a/bridges/NiceMatinBridge.php
+++ b/bridges/NiceMatinBridge.php
@@ -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(){
diff --git a/bridges/NineGagBridge.php b/bridges/NineGagBridge.php
index e726c732..939ff387 100644
--- a/bridges/NineGagBridge.php
+++ b/bridges/NineGagBridge.php
@@ -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;
+ }
}
}
diff --git a/bridges/NovelUpdatesBridge.php b/bridges/NovelUpdatesBridge.php
index 729eb485..05acd8ef 100644
--- a/bridges/NovelUpdatesBridge.php
+++ b/bridges/NovelUpdatesBridge.php
@@ -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(
diff --git a/bridges/WhydBridge.php b/bridges/OpenwhydBridge.php
similarity index 81%
rename from bridges/WhydBridge.php
rename to bridges/OpenwhydBridge.php
index 04d0b30e..f80cb066 100644
--- a/bridges/WhydBridge.php
+++ b/bridges/OpenwhydBridge.php
@@ -1,9 +1,9 @@
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';
}
}
diff --git a/bridges/ParuVenduImmoBridge.php b/bridges/ParuVenduImmoBridge.php
index a2e2b335..7b2825bc 100644
--- a/bridges/ParuVenduImmoBridge.php
+++ b/bridges/ParuVenduImmoBridge.php
@@ -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.';
diff --git a/bridges/PatreonBridge.php b/bridges/PatreonBridge.php
new file mode 100644
index 00000000..57727a3e
--- /dev/null
+++ b/bridges/PatreonBridge.php
@@ -0,0 +1,203 @@
+ 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'] .= '
';
+
+ if(isset($post->attributes->content)) {
+ $item['content'] .= $post->attributes->content;
+ } elseif (isset($post->attributes->teaser_text)) {
+ $item['content'] .= ''
+ . $post->attributes->teaser_text
+ . '
';
+ }
+
+ 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;
+ }
+}
diff --git a/bridges/PickyWallpapersBridge.php b/bridges/PickyWallpapersBridge.php
index 6c26df7a..488b4483 100644
--- a/bridges/PickyWallpapersBridge.php
+++ b/bridges/PickyWallpapersBridge.php
@@ -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';
diff --git a/bridges/PikabuBridge.php b/bridges/PikabuBridge.php
index 362b87dc..a54f6bf5 100644
--- a/bridges/PikabuBridge.php
+++ b/bridges/PikabuBridge.php
@@ -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 = '';
+
+ // 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), '
');
+ $item['content'] = strip_tags(
+ backgroundToImg($post->find('.story__content-inner', 0)->innertext),
+ '
+ ');
$item['uri'] = $title->href;
$item['timestamp'] = strtotime($time->getAttribute('datetime'));
$this->items[] = $item;
diff --git a/bridges/PinterestBridge.php b/bridges/PinterestBridge.php
index 2917b267..48c0cfca 100644
--- a/bridges/PinterestBridge.php
+++ b/bridges/PinterestBridge.php
@@ -16,12 +16,6 @@ class PinterestBridge extends FeedExpander {
'name' => 'board',
'required' => true
)
- ),
- 'From search' => array(
- 'q' => array(
- 'name' => 'Keyword',
- 'required' => true
- )
)
);
@@ -29,22 +23,14 @@ class PinterestBridge extends FeedExpander {
return 'https://s.pinimg.com/webapp/style/images/favicon-9f8f9adf.png';
}
- public function collectData(){
- switch($this->queriedContext) {
- case 'By username and board':
- $this->collectExpandableDatas($this->getURI() . '.rss');
- $this->fixLowRes();
- break;
- case 'From search':
- default:
- $html = getSimpleHTMLDOMCached($this->getURI());
- $this->getSearchResults($html);
- }
+ public function collectData() {
+ $this->collectExpandableDatas($this->getURI() . '.rss');
+ $this->fixLowRes();
}
private function fixLowRes() {
- $newitems = [];
+ $newitems = array();
$pattern = '/https\:\/\/i\.pinimg\.com\/[a-zA-Z0-9]*x\//';
foreach($this->items as $item) {
@@ -55,71 +41,21 @@ class PinterestBridge extends FeedExpander {
}
- private function getSearchResults($html){
- $json = json_decode($html->find('#jsInit1', 0)->innertext, true);
- $results = $json['resourceDataCache'][0]['data']['results'];
+ public function getURI() {
- foreach($results as $result) {
- $item = array();
-
- $item['uri'] = self::URI . $result['board']['url'];
-
- // Some use regular titles, others provide 'advanced' infos, a few
- // provide even less info. Thus we attempt multiple options.
- $item['title'] = trim($result['title']);
-
- if($item['title'] === '')
- $item['title'] = trim($result['rich_summary']['display_name']);
-
- if($item['title'] === '')
- $item['title'] = trim($result['grid_description']);
-
- $item['timestamp'] = strtotime($result['created_at']);
- $item['username'] = $result['pinner']['username'];
- $item['fullname'] = $result['pinner']['full_name'];
- $item['avatar'] = $result['pinner']['image_small_url'];
- $item['author'] = $item['username'] . ' (' . $item['fullname'] . ')';
- $item['content'] = ''
- . $item['username']
- . '
'
- . $item['fullname']
- . '
'
- . $result['description']
- . '
';
-
- $item['enclosures'] = array($result['images']['orig']['url']);
-
- $this->items[] = $item;
+ if ($this->queriedContext === 'By username and board') {
+ return self::URI . '/' . urlencode($this->getInput('u')) . '/' . urlencode($this->getInput('b'));
}
+
+ return parent::getURI();
}
- public function getURI(){
- switch($this->queriedContext) {
- case 'By username and board':
- $uri = self::URI . '/' . urlencode($this->getInput('u')) . '/' . urlencode($this->getInput('b'));// . '.rss';
- break;
- case 'From search':
- $uri = self::URI . '/search/?q=' . urlencode($this->getInput('q'));
- break;
- default: return parent::getURI();
- }
- return $uri;
- }
+ public function getName() {
- public function getName(){
- switch($this->queriedContext) {
- case 'By username and board':
- $specific = $this->getInput('u') . ' - ' . $this->getInput('b');
- break;
- case 'From search':
- $specific = $this->getInput('q');
- break;
- default: return parent::getName();
+ if ($this->queriedContext === 'By username and board') {
+ return $this->getInput('u') . ' - ' . $this->getInput('b') . ' - ' . self::NAME;
}
- return $specific . ' - ' . self::NAME;
+
+ return parent::getName();
}
}
diff --git a/bridges/PirateCommunityBridge.php b/bridges/PirateCommunityBridge.php
new file mode 100644
index 00000000..fcf97b9c
--- /dev/null
+++ b/bridges/PirateCommunityBridge.php
@@ -0,0 +1,88 @@
+ 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;
+ }
+ }
+}
diff --git a/bridges/PlantUMLReleasesBridge.php b/bridges/PlantUMLReleasesBridge.php
new file mode 100644
index 00000000..66480560
--- /dev/null
+++ b/bridges/PlantUMLReleasesBridge.php
@@ -0,0 +1,67 @@
+ '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;
+ }
+ }
+}
diff --git a/bridges/ReadComicsBridge.php b/bridges/ReadComicsBridge.php
deleted file mode 100644
index 739e6ccb..00000000
--- a/bridges/ReadComicsBridge.php
+++ /dev/null
@@ -1,44 +0,0 @@
- 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;
- }
- }
- }
-}
diff --git a/bridges/RedditBridge.php b/bridges/RedditBridge.php
new file mode 100644
index 00000000..8de499f9
--- /dev/null
+++ b/bridges/RedditBridge.php
@@ -0,0 +1,40 @@
+ 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");
+ }
+ }
+}
diff --git a/bridges/Releases3DSBridge.php b/bridges/Releases3DSBridge.php
index 6c159d12..fe2df8ec 100644
--- a/bridges/Releases3DSBridge.php
+++ b/bridges/Releases3DSBridge.php
@@ -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 {
. '
Release Name: ' . $releasename
. '
Trimmed size: ' . intval(intval($trimmedsize) / 1048576)
. 'MB
Firmware: ' . $firmware
- . '
Type: ' . typeToString($type)
- . '
Card: ' . cardToString($card)
+ . '
Type: ' . $this->typeToString($type)
+ . '
Card: ' . $this->cardToString($card)
. '
';
//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 . ')';
+ }
+ }
}
diff --git a/bridges/ReporterreBridge.php b/bridges/ReporterreBridge.php
index 438c55be..41f0f70f 100644
--- a/bridges/ReporterreBridge.php
+++ b/bridges/ReporterreBridge.php
@@ -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){
diff --git a/bridges/RoadAndTrackBridge.php b/bridges/RoadAndTrackBridge.php
index b3f0acc0..22ec8b52 100644
--- a/bridges/RoadAndTrackBridge.php
+++ b/bridges/RoadAndTrackBridge.php
@@ -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;
diff --git a/bridges/Rule34Bridge.php b/bridges/Rule34Bridge.php
index b46ec00a..71f48c61 100644
--- a/bridges/Rule34Bridge.php
+++ b/bridges/Rule34Bridge.php
@@ -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;
diff --git a/bridges/Rule34pahealBridge.php b/bridges/Rule34pahealBridge.php
index 1a746162..0e13ed03 100644
--- a/bridges/Rule34pahealBridge.php
+++ b/bridges/Rule34pahealBridge.php
@@ -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'] = '
Tags: '
+ . $item['tags'];
+ return $item;
+ }
}
diff --git a/bridges/SafebooruBridge.php b/bridges/SafebooruBridge.php
index d95e5572..98da6925 100644
--- a/bridges/SafebooruBridge.php
+++ b/bridges/SafebooruBridge.php
@@ -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;
diff --git a/bridges/SakugabooruBridge.php b/bridges/SakugabooruBridge.php
deleted file mode 100644
index 1d6cee0a..00000000
--- a/bridges/SakugabooruBridge.php
+++ /dev/null
@@ -1,11 +0,0 @@
- array(
+ 'name' => 'Minimum Episodes',
+ 'type' => 'number',
+ 'title' => 'Minimum number of episodes before including in feed',
+ 'defaultValue' => 0,
+ ),
+ 'min_total_episodes' => array(
+ 'name' => 'Minimum Total Episodes',
+ 'type' => 'number',
+ 'title' => 'Minimum total number of episodes before including in feed',
+ 'defaultValue' => 0,
+ ),
+ 'require_banner' => array(
+ 'name' => 'Require Banner',
+ 'type' => 'checkbox',
+ 'title' => 'Only include anime with custom banner image',
+ 'defaultValue' => false,
+ ),
+ ),
+ );
+
+ private $uri;
+
+ public function getURI() {
+ return isset($this->uri) ? $this->uri : parent::getURI();
+ }
+
+ public function collectData(){
+ $html = $this->loadSeasonAnimeList();
+
+ $animes = $html->find('div.header_display_box_info')
+ or returnServerError('Could not find anime headers!');
+
+ $min_episodes = $this->getInput('min_episodes') ?: 0;
+ $min_total_episodes = $this->getInput('min_total_episodes') ?: 0;
+
+ foreach($animes as $anime) {
+
+ list(
+ $episodes_released,
+ /* of */,
+ $episodes_total
+ ) = explode(' ', $this->extractAnimeEpisodeInformation($anime));
+
+ // Skip if not enough episodes yet
+ if ($episodes_released < $min_episodes) {
+ continue;
+ }
+
+ // Skip if too many episodes in total
+ if ($episodes_total !== '?' && $episodes_total < $min_total_episodes) {
+ continue;
+ }
+
+ // Skip if https://static.shanaproject.com/no-art.jpg
+ if ($this->getInput('require_banner')
+ && strpos($this->extractAnimeBackgroundImage($anime), 'no-art') !== false) {
+ continue;
+ }
+
+ $this->items[] = array(
+ 'title' => $this->extractAnimeTitle($anime),
+ 'author' => $this->extractAnimeAuthor($anime),
+ 'uri' => $this->extractAnimeUri($anime),
+ 'timestamp' => $this->extractAnimeTimestamp($anime),
+ 'content' => $this->buildAnimeContent($anime),
+ );
+
+ }
+ }
// Returns an html object for the Season Anime List (latest season)
private function loadSeasonAnimeList(){
- // First we need to find the URI to the latest season from the
- // 'seasons' page searching for 'Season Anime List'
- $html = getSimpleHTMLDOM($this->getURI() . '/seasons');
- if(!$html)
- returnServerError('Could not load \'seasons\' page!');
- $season = $html->find('div.follows_menu/a', 1);
- if(!$season)
- returnServerError('Could not find \'Season Anime List\'!');
+ $html = getSimpleHTMLDOM(self::URI . '/seasons')
+ or returnServerError('Could not load \'seasons\' page!');
- $html = getSimpleHTMLDOM($this->getURI() . $season->href);
- if(!$html)
- returnServerError(
+ $html = defaultLinkTo($html, self::URI . '/seasons');
+
+ $season = $html->find('div.follows_menu > a', 1)
+ or returnServerError('Could not find \'Season Anime List\'!');
+
+ $html = getSimpleHTMLDOM($season->href)
+ or returnServerError(
'Could not load \'Season Anime List\' from \''
. $season->innertext
. '\'!'
);
+ $this->uri = $season->href;
+
+ $html = defaultLinkTo($html, $season->href);
+
return $html;
+
}
// Extracts the anime title
private function extractAnimeTitle($anime){
- $title = $anime->find('a', 0);
- if(!$title)
- returnServerError('Could not find anime title!');
+ $title = $anime->find('a', 0)
+ or returnServerError('Could not find anime title!');
return trim($title->innertext);
}
// Extracts the anime URI
private function extractAnimeUri($anime){
- $uri = $anime->find('a', 0);
- if(!$uri)
- returnServerError('Could not find anime URI!');
- return $this->getURI() . $uri->href;
+ $uri = $anime->find('a', 0)
+ or returnServerError('Could not find anime URI!');
+ return $uri->href;
}
// Extracts the anime release date (timestamp)
private function extractAnimeTimestamp($anime){
$timestamp = $anime->find('span.header_info_block', 1);
- if(!$timestamp)
+
+ if(!$timestamp) {
return null;
+ }
+
return strtotime($timestamp->innertext);
}
// Extracts the anime studio name (author)
private function extractAnimeAuthor($anime){
$author = $anime->find('span.header_info_block', 2);
- if(!$author)
- return; // Sometimes the studio is unknown, so leave empty
+
+ if(!$author) {
+ return null; // Sometimes the studio is unknown, so leave empty
+ }
+
return trim($author->innertext);
}
// Extracts the episode information (x of y released)
private function extractAnimeEpisodeInformation($anime){
- $episode = $anime->find('div.header_info_episode', 0);
- if(!$episode)
- returnServerError('Could not find anime episode information!');
- return preg_replace('/\r|\n/', ' ', $episode->plaintext);
+ $episode = $anime->find('div.header_info_episode', 0)
+ or returnServerError('Could not find anime episode information!');
+
+ $retVal = preg_replace('/\r|\n/', ' ', $episode->plaintext);
+ $retVal = preg_replace('/\s+/', ' ', $retVal);
+
+ return $retVal;
}
// Extracts the background image
@@ -73,15 +155,16 @@ class ShanaprojectBridge extends BridgeAbstract {
// Getting the picture is a little bit tricky as it is part of the style.
// Luckily the style is part of the parent div :)
- if(preg_match('/url\(\/\/([^\)]+)\)/i', $anime->parent->style, $matches))
+ if(preg_match('/url\(\/\/([^\)]+)\)/i', $anime->parent->style, $matches)) {
return $matches[1];
+ }
returnServerError('Could not extract background image!');
}
// Builds an URI to search for a specific anime (subber is left empty)
private function buildAnimeSearchUri($anime){
- return $this->getURI()
+ return self::URI
. '/search/?title='
. urlencode($this->extractAnimeTitle($anime))
. '&subber=';
@@ -102,22 +185,4 @@ class ShanaprojectBridge extends BridgeAbstract {
. $this->buildAnimeSearchUri($anime)
. '">Search episodes
';
}
-
- public function collectData(){
- $html = $this->loadSeasonAnimeList();
-
- $animes = $html->find('div.header_display_box_info');
- if(!$animes)
- returnServerError('Could not find anime headers!');
-
- foreach($animes as $anime) {
- $item = array();
- $item['title'] = $this->extractAnimeTitle($anime);
- $item['author'] = $this->extractAnimeAuthor($anime);
- $item['uri'] = $this->extractAnimeUri($anime);
- $item['timestamp'] = $this->extractAnimeTimestamp($anime);
- $item['content'] = $this->buildAnimeContent($anime);
- $this->items[] = $item;
- }
- }
}
diff --git a/bridges/Shimmie2Bridge.php b/bridges/Shimmie2Bridge.php
index 99235147..fdc97f4f 100644
--- a/bridges/Shimmie2Bridge.php
+++ b/bridges/Shimmie2Bridge.php
@@ -4,7 +4,7 @@ require_once('DanbooruBridge.php');
class Shimmie2Bridge extends DanbooruBridge {
const NAME = 'Shimmie v2';
- const URI = 'http://shimmie.shishnet.org/v2/';
+ const URI = 'https://shimmie.shishnet.org/v2/';
const DESCRIPTION = 'Returns images from given page';
const PATHTODATA = '.shm-thumb-link';
diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php
index 8938ff96..9607d33d 100644
--- a/bridges/SoundcloudBridge.php
+++ b/bridges/SoundcloudBridge.php
@@ -1,7 +1,7 @@
getInput('u'))
- . '&client_id='
- . self::CLIENT_ID
+ $res = $this->apiGet('resolve', array(
+ 'url' => 'http://www.soundcloud.com/' . $this->getInput('u')
)) or returnServerError('No results for this query');
$this->feedIcon = $res->avatar_url;
- $tracks = json_decode(getContents(
- 'https://api.soundcloud.com/users/'
- . urlencode($res->id)
- . '/tracks?client_id='
- . self::CLIENT_ID
- )) or returnServerError('No results for this user');
+ $tracks = $this->apiGet('users/' . urlencode($res->id) . '/tracks')
+ or returnServerError('No results for this user');
$numTracks = min(count($tracks), 10);
for($i = 0; $i < $numTracks; $i++) {
@@ -45,7 +36,7 @@ class SoundCloudBridge extends BridgeAbstract {
$item['content'] = $tracks[$i]->description;
$item['enclosures'] = array($tracks[$i]->uri
. '/stream?client_id='
- . self::CLIENT_ID);
+ . $this->getClientID());
$item['id'] = self::URI
. urlencode($this->getInput('u'))
@@ -75,4 +66,68 @@ class SoundCloudBridge extends BridgeAbstract {
return parent::getName();
}
+
+ private function initClientIDCache(){
+ if($this->clientIDCache !== null)
+ return;
+
+ $cacheFac = new CacheFactory();
+ $cacheFac->setWorkingDir(PATH_LIB_CACHES);
+ $this->clientIDCache = $cacheFac->create(Configuration::getConfig('cache', 'type'));
+ $this->clientIDCache->setScope(get_called_class());
+ $this->clientIDCache->setKey(array('client_id'));
+ }
+
+ private function getClientID(){
+ $this->initClientIDCache();
+
+ $clientID = $this->clientIDCache->loadData();
+
+ if($clientID == null) {
+ return $this->refreshClientID();
+ } else {
+ return $clientID;
+ }
+ }
+
+ private function refreshClientID(){
+ $this->initClientIDCache();
+
+ // Without url=http, this returns a 404
+ $playerHTML = getContents('https://w.soundcloud.com/player/?url=http')
+ or returnServerError('Unable to get player page.');
+ $regex = '/widget-.+?\.js/';
+ if(preg_match($regex, $playerHTML, $matches) == false)
+ returnServerError('Unable to find widget JS URL.');
+ $widgetURL = 'https://widget.sndcdn.com/' . $matches[0];
+
+ $widgetJS = getContents($widgetURL)
+ or returnServerError('Unable to get widget JS page.');
+ $regex = '/client_id.*?"(.+?)"/';
+ if(preg_match($regex, $widgetJS, $matches) == false)
+ returnServerError('Unable to find client ID.');
+ $clientID = $matches[1];
+
+ $this->clientIDCache->saveData($clientID);
+ return $clientID;
+ }
+
+ private function buildAPIURL($endpoint, $parameters){
+ return 'https://api.soundcloud.com/'
+ . $endpoint
+ . '?'
+ . http_build_query($parameters);
+ }
+
+ private function apiGet($endpoint, $parameters = array()){
+ $parameters['client_id'] = $this->getClientID();
+
+ try {
+ return json_decode(getContents($this->buildAPIURL($endpoint, $parameters)));
+ } catch (Exception $e) {
+ // Retry once with refreshed client ID
+ $parameters['client_id'] = $this->refreshClientID();
+ return json_decode(getContents($this->buildAPIURL($endpoint, $parameters)));
+ }
+ }
}
diff --git a/bridges/SplCenterBridge.php b/bridges/SplCenterBridge.php
new file mode 100644
index 00000000..7a690908
--- /dev/null
+++ b/bridges/SplCenterBridge.php
@@ -0,0 +1,64 @@
+ array(
+ 'name' => 'Content',
+ 'type' => 'list',
+ 'values' => array(
+ 'News' => 'news',
+ 'Hatewatch' => 'hatewatch',
+ ),
+ 'defaultValue' => 'news',
+ )
+ )
+ );
+
+ const CACHE_TIMEOUT = 3600; // 1 hour
+
+ protected function parseItem($item) {
+ $item = parent::parseItem($item);
+
+ $articleHtml = getSimpleHTMLDOMCached($item['uri'])
+ or returnServerError('Could not request: ' . $item['uri']);
+
+ foreach ($articleHtml->find('.file') as $index => $media) {
+ $articleHtml->find('div.file', $index)->outertext = '' . $media->outertext . '';
+ }
+
+ $item['content'] = $articleHtml->find('div#group-content-container', 0)->innertext;
+ $item['enclosures'][] = $articleHtml->find('meta[name="twitter:image"]', 0)->content;
+
+ return $item;
+ }
+
+ public function collectData() {
+ $this->collectExpandableDatas($this->getURI() . '/rss.xml');
+ }
+
+ public function getURI() {
+
+ if (!is_null($this->getInput('content'))) {
+ return self::URI . '/' . $this->getInput('content');
+ }
+
+ return parent::getURI();
+ }
+
+ public function getName() {
+
+ if (!is_null($this->getInput('content'))) {
+ $parameters = $this->getParameters();
+
+ $contentValues = array_flip($parameters[0]['content']['values']);
+
+ return $contentValues[$this->getInput('content')] . ' - Southern Poverty Law Center';
+ }
+
+ return parent::getName();
+ }
+}
diff --git a/bridges/SteamCommunityBridge.php b/bridges/SteamCommunityBridge.php
index 56ea257c..9919a4b5 100644
--- a/bridges/SteamCommunityBridge.php
+++ b/bridges/SteamCommunityBridge.php
@@ -20,7 +20,8 @@ class SteamCommunityBridge extends BridgeAbstract {
'values' => array(
'Artwork' => 'images',
'Screenshots' => 'screenshots',
- 'Videos' => 'videos'
+ 'Videos' => 'videos',
+ 'Workshop' => 'workshop'
)
)
)
@@ -32,7 +33,7 @@ class SteamCommunityBridge extends BridgeAbstract {
protected function getMainPage() {
$category = $this->getInput('category');
- $html = getSimpleHTMLDOM($this->getURI() . '/?p=1&browsefilter=mostrecent')
+ $html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not fetch Steam data.');
return $html;
@@ -56,12 +57,17 @@ class SteamCommunityBridge extends BridgeAbstract {
}
public function getURI() {
+ if ($this->getInput('category') === 'workshop')
+ return self::URI . '/workshop/browse/?appid='
+ . $this->getInput('i') . '&browsesort=mostrecent';
+
return self::URI . '/app/'
. $this->getInput('i') . '/'
- . $this->getInput('category');
+ . $this->getInput('category')
+ . '/?p=1&browsefilter=mostrecent';
}
- public function collectData() {
+ private function collectMedia() {
$category = $this->getInput('category');
$html = $this->getMainPage();
$cards = $html->find('div.apphub_Card');
@@ -124,4 +130,62 @@ class SteamCommunityBridge extends BridgeAbstract {
break;
}
}
+
+ private function collectWorkshop() {
+ $category = $this->getInput('category');
+ $html = $this->getMainPage();
+ $workShopItems = $html->find('div.workshopItem');
+
+ foreach($workShopItems as $workShopItem) {
+ $author = $workShopItem->find('div.workshopItemAuthorName', 0)->find('a', 0);
+ $author = $author->innertext;
+
+ $fileRating = $workShopItem->find('img.fileRating', 0);
+
+ $uri = $workShopItem->find('a.ugc', 0)->getAttribute('href');
+
+ $htmlItem = getSimpleHTMLDOMCached($uri);
+
+ $title = $htmlItem->find('div.workshopItemTitle', 0)->innertext;
+ $date = $htmlItem->find('div.detailsStatRight', 0)->innertext;
+ $description = $htmlItem->find('div.workshopItemDescription', 0)->innertext;
+
+ $previewImage = $htmlItem->find('#previewImage', 0);
+
+ $htmlTags = $htmlItem->find('div.workshopTags');
+
+ $tags = '';
+
+ foreach($htmlTags as $htmlTag) {
+ if ($tags !== '')
+ $tags .= ',';
+
+ $tags .= $htmlTag->find('a', 0)->innertext;
+ }
+
+ // create item
+ $item = array();
+ $item['title'] = $title;
+ $item['uri'] = $uri;
+ $item['timestamp'] = strtotime($date);
+ $item['author'] = $author;
+ $item['categories'] = $category;
+
+ $item['content'] = ''
+ . $previewImage . '
' . $fileRating
+ . '
' . $description . '
';
+
+ $this->items[] = $item;
+
+ if (count($this->items) >= 10)
+ break;
+ }
+ }
+
+ public function collectData() {
+ if ($this->getInput('category') === 'workshop')
+ $this->collectWorkshop();
+ else
+ $this->collectMedia();
+ }
}
diff --git a/bridges/StoriesIGBridge.php b/bridges/StoriesIGBridge.php
new file mode 100644
index 00000000..9b5f7cbf
--- /dev/null
+++ b/bridges/StoriesIGBridge.php
@@ -0,0 +1,57 @@
+ array(
+ 'name' => 'Instagram username',
+ 'type' => 'text',
+ 'required' => true,
+ 'title' => 'Insert the username here'
+ ),
+ )
+ );
+
+ public function collectData(){
+ $html = getSimpleHTMLDOM($this->getURI())
+ or returnServerError('Failed to receive ' . $this->getURI());
+
+ $results = $html->find('article');
+
+ foreach($results as $result) {
+
+ $item = array();
+
+ $item['title'] = $this->getInput('username') . ' story';
+ $item['uri'] = $result->find('div.download', 0)->find('a', 0)->href;
+ $item['author'] = $this->getInput('username');
+ $item['timestamp'] = strtotime($result->find('time', 0)->datetime);
+ $item['uid'] = $result->find('time', 0)->datetime;
+
+ $item['content'] = $result;
+
+ $this->items[] = $item;
+ }
+ }
+
+ public function getURI(){
+ $uri = self::URI . '/stories/';
+ $uri .= urlencode($this->getInput('username'));
+ return $uri;
+
+ return parent::getURI();
+ }
+
+ public function getName() {
+
+ if (!is_null($this->getInput('username'))) {
+ return $this->getInput('username') . ' - ' . self::NAME;
+ }
+
+ return parent::getName();
+ }
+}
diff --git a/bridges/SuperbWallpapersBridge.php b/bridges/SuperbWallpapersBridge.php
deleted file mode 100644
index 610dd323..00000000
--- a/bridges/SuperbWallpapersBridge.php
+++ /dev/null
@@ -1,70 +0,0 @@
- array(
- 'name' => 'category',
- 'required' => true
- ),
- 'm' => array(
- 'name' => 'Max number of wallpapers',
- 'type' => 'number'
- ),
- 'r' => array(
- 'name' => 'resolution',
- 'exampleValue' => '1920x1200, 1680x1050,…',
- 'defaultValue' => '1920x1200'
- )
- ));
-
- public function collectData(){
- $category = $this->getInput('c');
- $resolution = $this->getInput('r'); // Wide wallpaper default
-
- $num = 0;
- $max = $this->getInput('m') ?: 36;
- $lastpage = 1;
-
- // Get last page number
- $link = self::URI . '/' . $category . '/9999.html';
- $html = getSimpleHTMLDOM($link)
- or returnServerError('Could not load ' . $link);
-
- $lastpage = min($html->find('.paging .cpage', 0)->innertext(), ceil($max / 36));
-
- for($page = 1; $page <= $lastpage; $page++) {
- $link = self::URI . '/' . $category . '/' . $page . '.html';
- $html = getSimpleHTMLDOM($link)
- or returnServerError('No results for this query.');
-
- foreach($html->find('.wpl .i a') as $element) {
- $thumbnail = $element->find('img', 0);
-
- $item = array();
- $item['uri'] = str_replace('200x125', $this->resolution, $thumbnail->src);
- $item['timestamp'] = time();
- $item['title'] = $element->title;
- $item['content'] = $item['title'] . '
' . $thumbnail . '';
- $this->items[] = $item;
-
- $num++;
- if ($num >= $max)
- break 2;
- }
- }
- }
-
- public function getName(){
- if(!is_null($this->getInput('c')) && !is_null($this->getInput('r'))) {
- return self::NAME . '- ' . $this->getInput('c') . ' [' . $this->getInput('r') . ']';
- }
-
- return parent::getName();
- }
-}
diff --git a/bridges/TbibBridge.php b/bridges/TbibBridge.php
index edb761ee..819d61ee 100644
--- a/bridges/TbibBridge.php
+++ b/bridges/TbibBridge.php
@@ -5,7 +5,7 @@ class TbibBridge extends GelbooruBridge {
const MAINTAINER = 'mitsukarenai';
const NAME = 'Tbib';
- const URI = 'http://tbib.org/';
+ const URI = 'https://tbib.org/';
const DESCRIPTION = 'Returns images from given page';
const PIDBYPAGE = 50;
diff --git a/bridges/TelegramBridge.php b/bridges/TelegramBridge.php
new file mode 100644
index 00000000..3afc2831
--- /dev/null
+++ b/bridges/TelegramBridge.php
@@ -0,0 +1,301 @@
+ array(
+ 'name' => 'Username',
+ 'type' => 'text',
+ 'required' => true,
+ 'exampleValue' => '@telegram',
+ )
+ )
+ );
+
+ const CACHE_TIMEOUT = 900; // 15 mins
+
+ private $feedName = '';
+ private $enclosures = array();
+ private $itemTitle = '';
+
+ private $backgroundImageRegex = "/background-image:url\('(.*)'\)/";
+
+ public function collectData() {
+
+ $html = getSimpleHTMLDOM($this->getURI())
+ or returnServerError('Could not request: ' . $this->getURI());
+
+ $channelTitle = htmlspecialchars_decode(
+ $html->find('div.tgme_channel_info_header_title span', 0)->plaintext,
+ ENT_QUOTES
+ );
+ $this->feedName = $channelTitle . ' (@' . $this->processUsername() . ')';
+
+ foreach($html->find('div.tgme_widget_message_wrap.js-widget_message_wrap') as $index => $messageDiv) {
+ $this->itemTitle = '';
+ $this->enclosures = array();
+ $item = array();
+
+ $item['uri'] = $this->processUri($messageDiv);
+ $item['content'] = html_entity_decode($this->processContent($messageDiv), ENT_QUOTES);
+ $item['title'] = html_entity_decode($this->itemTitle, ENT_QUOTES);
+ $item['timestamp'] = $this->processDate($messageDiv);
+ $item['enclosures'] = $this->enclosures;
+ $author = trim($messageDiv->find('a.tgme_widget_message_owner_name', 0)->plaintext);
+ $item['author'] = html_entity_decode($author, ENT_QUOTES);
+
+ $this->items[] = $item;
+ }
+ $this->items = array_reverse($this->items);
+ }
+
+ public function getURI() {
+
+ if (!is_null($this->getInput('username'))) {
+ return self::URI . '/s/' . $this->processUsername();
+ }
+
+ return parent::getURI();
+ }
+
+ public function getName() {
+
+ if (!empty($this->feedName)) {
+ return $this->feedName . ' - Telegram';
+ }
+
+ return parent::getName();
+ }
+
+ private function processUsername() {
+
+ if (substr($this->getInput('username'), 0, 1) === '@') {
+ return substr($this->getInput('username'), 1);
+ }
+
+ return $this->getInput('username');
+ }
+
+ private function processUri($messageDiv) {
+ return $messageDiv->find('a.tgme_widget_message_date', 0)->href;
+ }
+
+ private function processContent($messageDiv) {
+ $message = '';
+
+ if ($messageDiv->find('div.tgme_widget_message_forwarded_from', 0)) {
+ $message = $messageDiv->find('div.tgme_widget_message_forwarded_from', 0)->innertext . '
';
+ }
+
+ if ($messageDiv->find('a.tgme_widget_message_reply', 0)) {
+ $message = $this->processReply($messageDiv);
+ }
+
+ if ($messageDiv->find('div.tgme_widget_message_sticker_wrap', 0)) {
+ $message .= $this->processSticker($messageDiv);
+ }
+
+ if ($messageDiv->find('div.tgme_widget_message_poll', 0)) {
+ $message .= $this->processPoll($messageDiv);
+ }
+
+ if ($messageDiv->find('video', 0)) {
+ $message .= $this->processVideo($messageDiv);
+ }
+
+ if ($messageDiv->find('a.tgme_widget_message_photo_wrap', 0)) {
+ $message .= $this->processPhoto($messageDiv);
+ }
+
+ if ($messageDiv->find('a.not_supported', 0)) {
+ $message .= $this->processNotSupported($messageDiv);
+ }
+
+ if ($messageDiv->find('div.tgme_widget_message_text.js-message_text', 0)) {
+ $message .= $messageDiv->find('div.tgme_widget_message_text.js-message_text', 0);
+
+ $this->itemTitle = $this->ellipsisTitle(
+ $messageDiv->find('div.tgme_widget_message_text.js-message_text', 0)->plaintext
+ );
+ }
+
+ if ($messageDiv->find('a.tgme_widget_message_link_preview', 0)) {
+ $message .= $this->processLinkPreview($messageDiv);
+ }
+
+ return $message;
+ }
+
+ private function processReply($messageDiv) {
+
+ $reply = $messageDiv->find('a.tgme_widget_message_reply', 0);
+
+ return <<{$reply->find('span.tgme_widget_message_author_name', 0)->plaintext}
+{$reply->find('div.tgme_widget_message_text', 0)->innertext}
+{$reply->href}
+EOD;
+ }
+
+ private function processSticker($messageDiv) {
+
+ if (empty($this->itemTitle)) {
+ $this->itemTitle = '@' . $this->processUsername() . ' posted a sticker';
+ }
+
+ $stickerDiv = $messageDiv->find('div.tgme_widget_message_sticker_wrap', 0);
+
+ preg_match($this->backgroundImageRegex, $stickerDiv->find('i', 0)->style, $sticker);
+
+ $this->enclosures[] = $sticker[1];
+
+ return <<
+EOD;
+ }
+
+ private function processPoll($messageDiv) {
+
+ $poll = $messageDiv->find('div.tgme_widget_message_poll', 0);
+
+ $title = $poll->find('div.tgme_widget_message_poll_question', 0)->plaintext;
+ $type = $poll->find('div.tgme_widget_message_poll_type', 0)->plaintext;
+
+ if (empty($this->itemTitle)) {
+ $this->itemTitle = $title;
+ }
+
+ $pollOptions = '';
+
+ foreach ($poll->find('div.tgme_widget_message_poll_option') as $option) {
+ $pollOptions .= '- ' . $option->children(0)->plaintext . ' - ' .
+ $option->find('div.tgme_widget_message_poll_option_text', 0)->plaintext . '
';
+ }
+ $pollOptions .= '
';
+
+ return <<$type
{$pollOptions}
+EOD;
+ }
+
+ private function processLinkPreview($messageDiv) {
+
+ $image = '';
+ $title = '';
+ $site = '';
+ $description = '';
+
+ $preview = $messageDiv->find('a.tgme_widget_message_link_preview', 0);
+
+ if (trim($preview->innertext) === '') {
+ return '';
+ }
+
+ if($preview->find('i', 0) &&
+ preg_match($this->backgroundImageRegex, $preview->find('i', 0)->style, $photo)) {
+
+ $image = '';
+ $this->enclosures[] = $photo[1];
+ }
+
+ if ($preview->find('div.link_preview_title', 0)) {
+ $title = $preview->find('div.link_preview_title', 0)->plaintext;
+ }
+
+ if ($preview->find('div.link_preview_site_name', 0)) {
+ $site = $preview->find('div.link_preview_site_name', 0)->plaintext;
+ }
+
+ if ($preview->find('div.link_preview_description', 0)) {
+ $description = $preview->find('div.link_preview_description', 0)->plaintext;
+ }
+
+ return <<$image
+{$title} - {$site}
{$description}
+EOD;
+ }
+
+ private function processVideo($messageDiv) {
+
+ if (empty($this->itemTitle)) {
+ $this->itemTitle = '@' . $this->processUsername() . ' posted a video';
+ }
+
+ if ($messageDiv->find('i.tgme_widget_message_video_thumb')) {
+ preg_match($this->backgroundImageRegex, $messageDiv->find('i.tgme_widget_message_video_thumb', 0)->style, $photo);
+ } elseif ($messageDiv->find('i.link_preview_video_thumb')) {
+ preg_match($this->backgroundImageRegex, $messageDiv->find('i.link_preview_video_thumb', 0)->style, $photo);
+ }
+
+ $this->enclosures[] = $photo[1];
+
+ return <<
+ ';
} elseif($inputEntry['type'] === 'checkbox') {
$form .= self::getCheckboxInput($inputEntry, $idArg, $id);
}
+
+ if(isset($inputEntry['title']))
+ $form .= 'i';
+ else
+ $form .= '';
}
$form .= '';
@@ -146,9 +157,6 @@ This bridge is not fetching its content through a secure connection';
if(isset($entry['pattern']))
$retVal .= ' pattern="' . $entry['pattern'] . '"';
- if(isset($entry['title']))
- $retVal .= ' title="' . filter_var($entry['title'], FILTER_SANITIZE_STRING) . '"';
-
return $retVal;
}
@@ -299,7 +307,10 @@ This bridge is not fetching its content through a secure connection';
*/
static function displayBridgeCard($bridgeName, $formats, $isActive = true){
- $bridge = Bridge::create($bridgeName);
+ $bridgeFac = new \BridgeFactory();
+ $bridgeFac->setWorkingDir(PATH_LIB_BRIDGES);
+
+ $bridge = $bridgeFac->create($bridgeName);
if($bridge == false)
return '';
diff --git a/lib/Bridge.php b/lib/BridgeFactory.php
similarity index 66%
rename from lib/Bridge.php
rename to lib/BridgeFactory.php
index 31607922..fea254f1 100644
--- a/lib/Bridge.php
+++ b/lib/BridgeFactory.php
@@ -35,17 +35,7 @@
* $bridge = Bridge::create('GitHubIssue');
* ```
*/
-class Bridge {
-
- /**
- * Holds a path to the working directory.
- *
- * Do not access this property directly!
- * Use {@see Bridge::setWorkingDir()} and {@see Bridge::getWorkingDir()} instead.
- *
- * @var string|null
- */
- protected static $workingDir = null;
+class BridgeFactory extends FactoryAbstract {
/**
* Holds a list of whitelisted bridges.
@@ -55,18 +45,7 @@ class Bridge {
*
* @var array
*/
- protected static $whitelist = array();
-
- /**
- * Throws an exception when trying to create a new instance of this class.
- * Use {@see Bridge::create()} to instanciate a new bridge from the working
- * directory.
- *
- * @throws \LogicException if called.
- */
- public function __construct(){
- throw new \LogicException('Use ' . __CLASS__ . '::create($name) to create bridge objects!');
- }
+ protected $whitelist = array();
/**
* Creates a new bridge object from the working directory.
@@ -77,13 +56,13 @@ class Bridge {
* @param string $name Name of the bridge object.
* @return object|bool The bridge object or false if the class is not instantiable.
*/
- public static function create($name){
- if(!self::isBridgeName($name)) {
+ public function create($name){
+ if(!$this->isBridgeName($name)) {
throw new \InvalidArgumentException('Bridge name invalid!');
}
- $name = self::sanitizeBridgeName($name) . 'Bridge';
- $filePath = self::getWorkingDir() . $name . '.php';
+ $name = $this->sanitizeBridgeName($name) . 'Bridge';
+ $filePath = $this->getWorkingDir() . $name . '.php';
if(!file_exists($filePath)) {
throw new \Exception('Bridge file ' . $filePath . ' does not exist!');
@@ -98,48 +77,6 @@ class Bridge {
return false;
}
- /**
- * Sets the working directory.
- *
- * @param string $dir Path to the directory containing bridges.
- * @throws \LogicException if the provided path is not a valid string.
- * @throws \Exception if the provided path does not exist.
- * @throws \InvalidArgumentException if $dir is not a directory.
- * @return void
- */
- public static function setWorkingDir($dir){
- self::$workingDir = null;
-
- if(!is_string($dir)) {
- throw new \InvalidArgumentException('Working directory is not a valid string!');
- }
-
- if(!file_exists($dir)) {
- throw new \Exception('Working directory does not exist!');
- }
-
- if(!is_dir($dir)) {
- throw new \InvalidArgumentException('Working directory is not a directory!');
- }
-
- self::$workingDir = realpath($dir) . '/';
- }
-
- /**
- * Returns the working directory.
- * The working directory must be specified with {@see Bridge::setWorkingDir()}!
- *
- * @throws \LogicException if the working directory is not set.
- * @return string The current working directory.
- */
- public static function getWorkingDir(){
- if(is_null(self::$workingDir)) {
- throw new \LogicException('Working directory is not set!');
- }
-
- return self::$workingDir;
- }
-
/**
* Returns true if the provided name is a valid bridge name.
*
@@ -149,7 +86,7 @@ class Bridge {
* @param string $name The bridge name.
* @return bool true if the name is a valid bridge name, false otherwise.
*/
- public static function isBridgeName($name){
+ public function isBridgeName($name){
return is_string($name) && preg_match('/^[A-Z][a-zA-Z0-9-]*$/', $name) === 1;
}
@@ -160,12 +97,12 @@ class Bridge {
*
* @return array List of bridge names
*/
- public static function getBridgeNames(){
+ public function getBridgeNames(){
static $bridgeNames = array(); // Initialized on first call
if(empty($bridgeNames)) {
- $files = scandir(self::getWorkingDir());
+ $files = scandir($this->getWorkingDir());
if($files !== false) {
foreach($files as $file) {
@@ -185,8 +122,8 @@ class Bridge {
* @param string $name Name of the bridge.
* @return bool True if the bridge is whitelisted.
*/
- public static function isWhitelisted($name){
- return in_array(self::sanitizeBridgeName($name), self::getWhitelist());
+ public function isWhitelisted($name){
+ return in_array($this->sanitizeBridgeName($name), $this->getWhitelist());
}
/**
@@ -205,7 +142,7 @@ class Bridge {
*
* @return array Array of whitelisted bridges
*/
- public static function getWhitelist() {
+ public function getWhitelist() {
static $firstCall = true; // Initialized on first call
@@ -220,17 +157,17 @@ class Bridge {
}
if($contents === '*') { // Whitelist all bridges
- self::$whitelist = self::getBridgeNames();
+ $this->whitelist = $this->getBridgeNames();
} else {
- //self::$whitelist = array_map('self::sanitizeBridgeName', explode("\n", $contents));
+ //$this->$whitelist = array_map('$this->sanitizeBridgeName', explode("\n", $contents));
foreach(explode("\n", $contents) as $bridgeName) {
- self::$whitelist[] = self::sanitizeBridgeName($bridgeName);
+ $this->whitelist[] = $this->sanitizeBridgeName($bridgeName);
}
}
}
- return self::$whitelist;
+ return $this->whitelist;
}
@@ -248,8 +185,8 @@ class Bridge {
* @param array $default The whitelist as array of bridge names.
* @return void
*/
- public static function setWhitelist($default = array()) {
- self::$whitelist = array_map('self::sanitizeBridgeName', $default);
+ public function setWhitelist($default = array()) {
+ $this->whitelist = array_map('$this->sanitizeBridgeName', $default);
}
/**
@@ -269,7 +206,7 @@ class Bridge {
* @return string|null The sanitized bridge name if the provided name is
* valid, null otherwise.
*/
- protected static function sanitizeBridgeName($name) {
+ protected function sanitizeBridgeName($name) {
if(is_string($name)) {
@@ -284,15 +221,15 @@ class Bridge {
}
// Improve performance for correctly written bridge names
- if(in_array($name, self::getBridgeNames())) {
- $index = array_search($name, self::getBridgeNames());
- return self::getBridgeNames()[$index];
+ if(in_array($name, $this->getBridgeNames())) {
+ $index = array_search($name, $this->getBridgeNames());
+ return $this->getBridgeNames()[$index];
}
// The name is valid if a corresponding bridge file is found on disk
- if(in_array(strtolower($name), array_map('strtolower', self::getBridgeNames()))) {
- $index = array_search(strtolower($name), array_map('strtolower', self::getBridgeNames()));
- return self::getBridgeNames()[$index];
+ if(in_array(strtolower($name), array_map('strtolower', $this->getBridgeNames()))) {
+ $index = array_search(strtolower($name), array_map('strtolower', $this->getBridgeNames()));
+ return $this->getBridgeNames()[$index];
}
Debug::log('Invalid bridge name specified: "' . $name . '"!');
diff --git a/lib/BridgeList.php b/lib/BridgeList.php
index bcd39db1..fc92dfb7 100644
--- a/lib/BridgeList.php
+++ b/lib/BridgeList.php
@@ -34,6 +34,7 @@ final class BridgeList {
RSS-Bridge
+