aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2025-03-02 19:32:33 -0800
committerGravatar GitHub <noreply@github.com> 2025-03-02 19:32:33 -0800
commit8b16dd20f6544af3eedf286e23c0d34ab525736c (patch)
treeec284e22a046c4c8e9626e3fa64a000a2747bf84
parentb183aa798af48af556496c42780d6e844172cf44 (diff)
parent00a24e2f694a319a5e6cb070dddfff2dae892378 (diff)
downloadrss-bridge-8b16dd20f6544af3eedf286e23c0d34ab525736c.tar.gz
rss-bridge-8b16dd20f6544af3eedf286e23c0d34ab525736c.tar.zst
rss-bridge-8b16dd20f6544af3eedf286e23c0d34ab525736c.zip
Merge branch 'RSS-Bridge:master' into masterHEADmaster
-rw-r--r--.github/ISSUE_TEMPLATE/bridge-request.md4
-rw-r--r--CONTRIBUTORS.md3
-rw-r--r--README.md37
-rw-r--r--actions/DisplayAction.php2
-rw-r--r--actions/FrontpageAction.php2
-rw-r--r--bridges/AO3Bridge.php5
-rw-r--r--bridges/AirBreizhBridge.php3
-rw-r--r--bridges/AmazonPriceTrackerBridge.php2
-rw-r--r--bridges/AnisearchBridge.php2
-rw-r--r--bridges/AnthropicBridge.php147
-rw-r--r--bridges/AssociatedPressNewsBridge.php3
-rw-r--r--bridges/BAEBridge.php2
-rw-r--r--bridges/BandcampDailyBridge.php6
-rw-r--r--bridges/BlizzardNewsBridge.php68
-rw-r--r--bridges/BlueskyBridge.php620
-rw-r--r--bridges/BukowskisBridge.php2
-rw-r--r--bridges/BundestagParteispendenBridge.php8
-rw-r--r--bridges/CarThrottleBridge.php6
-rw-r--r--bridges/CentreFranceBridge.php67
-rw-r--r--bridges/CeskaTelevizeBridge.php51
-rw-r--r--bridges/CrewbayBridge.php2
-rw-r--r--bridges/CubariProxyBridge.php5
-rw-r--r--bridges/DRKBlutspendeBridge.php107
-rw-r--r--bridges/DacksnackBridge.php6
-rw-r--r--bridges/DagensNyheterDirektBridge.php3
-rw-r--r--bridges/DansTonChatBridge.php6
-rw-r--r--bridges/DealabsBridge.php1852
-rw-r--r--bridges/DonnonsBridge.php108
-rw-r--r--bridges/EconomistWorldInBriefBridge.php45
-rw-r--r--bridges/EdfPricesBridge.php235
-rw-r--r--bridges/EpicGamesFreeBridge.php74
-rw-r--r--bridges/FeedMergeBridge.php45
-rw-r--r--bridges/FindACrewBridge.php2
-rw-r--r--bridges/Formula1Bridge.php12
-rw-r--r--bridges/FurAffinityUserBridge.php3
-rw-r--r--bridges/GiteaBridge.php9
-rw-r--r--bridges/GithubIssueBridge.php27
-rw-r--r--bridges/GlowficBridge.php2
-rw-r--r--bridges/GogsBridge.php3
-rw-r--r--bridges/GolemBridge.php2
-rw-r--r--bridges/GoogleScholarBridge.php4
-rw-r--r--bridges/GovTrackBridge.php84
-rw-r--r--bridges/HotUKDealsBridge.php3216
-rw-r--r--bridges/IdealoBridge.php21
-rw-r--r--bridges/ItakuBridge.php24
-rw-r--r--bridges/JohannesBlickBridge.php3
-rw-r--r--bridges/JustETFBridge.php3
-rw-r--r--bridges/KernelBugTrackerBridge.php4
-rw-r--r--bridges/LaTeX3ProjectNewslettersBridge.php2
-rw-r--r--bridges/LegifranceJOBridge.php64
-rw-r--r--bridges/LfcPlBridge.php110
-rw-r--r--bridges/MaalaimalarBridge.php3
-rw-r--r--bridges/MistralAIBridge.php70
-rw-r--r--bridges/MixologyBridge.php80
-rw-r--r--bridges/MondeDiploBridge.php20
-rw-r--r--bridges/MozillaBugTrackerBridge.php4
-rw-r--r--bridges/MydealsBridge.php1967
-rw-r--r--bridges/NordbayernBridge.php7
-rw-r--r--bridges/NurembergerNachrichtenBridge.php4
-rw-r--r--bridges/OLXBridge.php17
-rw-r--r--bridges/OMonlineBridge.php6
-rw-r--r--bridges/OllamaBridge.php61
-rw-r--r--bridges/PepperBridgeAbstract.php67
-rw-r--r--bridges/PriviblurBridge.php13
-rw-r--r--bridges/QwenBlogBridge.php49
-rw-r--r--bridges/RedditBridge.php23
-rw-r--r--bridges/RumbleBridge.php13
-rw-r--r--bridges/RutubeBridge.php2
-rw-r--r--bridges/SchweinfurtBuergerinformationenBridge.php6
-rw-r--r--bridges/ScribbleHubBridge.php50
-rw-r--r--bridges/ShadertoyBridge.php100
-rw-r--r--bridges/SkimfeedBridge.php12
-rw-r--r--bridges/StanfordSIRbookreviewBridge.php3
-rw-r--r--bridges/StockFilingsBridge.php2
-rw-r--r--bridges/StorytelBridge.php55
-rw-r--r--bridges/TapasBridge.php2
-rw-r--r--bridges/TelegramBridge.php88
-rw-r--r--bridges/TestFaktaBridge.php6
-rw-r--r--bridges/TikTokBridge.php3
-rw-r--r--bridges/TldrTechBridge.php3
-rw-r--r--bridges/UsesTechBridge.php3
-rw-r--r--bridges/VkBridge.php25
-rw-r--r--bridges/VproTegenlichtBridge.php3
-rw-r--r--bridges/WKYTNewsBridge.php27
-rw-r--r--bridges/WikipediaBridge.php4
-rw-r--r--bridges/WirecutterDealsBridge.php119
-rw-r--r--bridges/WorldCosplayBridge.php149
-rw-r--r--bridges/XenForoBridge.php12
-rw-r--r--bridges/YouTubeFeedExpanderBridge.php86
-rw-r--r--bridges/YoutubeBridge.php7
-rw-r--r--caches/FileCache.php11
-rw-r--r--config.default.ini.php29
-rw-r--r--docs/01_General/06_Public_Hosts.md2
-rw-r--r--docs/10_Bridge_Specific/Telegram.md12
-rw-r--r--formats/MrssFormat.php8
-rw-r--r--lib/BridgeAbstract.php2
-rw-r--r--lib/BridgeCard.php4
-rw-r--r--lib/Configuration.php2
-rw-r--r--lib/FeedParser.php12
-rw-r--r--lib/bootstrap.php4
-rw-r--r--lib/contents.php19
-rw-r--r--lib/http.php2
-rw-r--r--lib/logger.php2
-rw-r--r--lib/simplehtmldom/simple_html_dom.php5
-rw-r--r--lib/url.php3
-rw-r--r--middlewares/CacheMiddleware.php9
-rw-r--r--middlewares/TokenAuthenticationMiddleware.php16
-rw-r--r--static/style.css1
-rw-r--r--templates/frontpage.html.php2
-rw-r--r--templates/token.html.php4
-rw-r--r--tests/FeedParserTest.php79
-rw-r--r--tests/UrlTest.php6
112 files changed, 2818 insertions, 7710 deletions
diff --git a/.github/ISSUE_TEMPLATE/bridge-request.md b/.github/ISSUE_TEMPLATE/bridge-request.md
index 174dc095..088cc3d6 100644
--- a/.github/ISSUE_TEMPLATE/bridge-request.md
+++ b/.github/ISSUE_TEMPLATE/bridge-request.md
@@ -49,9 +49,9 @@ Please describe what you expect from the bridge. Whenever possible provide sampl
- _Default limit_: 5
- [ ] Load full articles
- _Cache articles_ (articles are stored in a local cache on first request): yes
- - _Cache timeout_ (max = 24 hours): 24 hours
+ - _Cache timeout_ : 24 hours
- [X] Balance requests (RSS-Bridge uses cached versions to reduce bandwith usage)
- - _Timeout_ (default = 5 minutes, max = 24 hours): 5 minutes
+ - _Timeout_ (default = 5 minutes): 5 minutes
<!--Be aware that some options might not be available for your specific request due to technical limitations!-->
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 922d9453..d27421aa 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -15,7 +15,7 @@
* [Astalaseven](https://github.com/Astalaseven)
* [Astyan-42](https://github.com/Astyan-42)
* [austinhuang0131](https://github.com/austinhuang0131)
-* [AxorPL](https://github.com/AxorPL)
+* [axor-mst](https://github.com/axor-mst)
* [ayacoo](https://github.com/ayacoo)
* [az5he6ch](https://github.com/az5he6ch)
* [b1nj](https://github.com/b1nj)
@@ -23,6 +23,7 @@
* [Binnette](https://github.com/Binnette)
* [BoboTiG](https://github.com/BoboTiG)
* [Bockiii](https://github.com/Bockiii)
+* [brtsos](https://github.com/brtsos)
* [captn3m0](https://github.com/captn3m0)
* [chemel](https://github.com/chemel)
* [Chouchen](https://github.com/Chouchen)
diff --git a/README.md b/README.md
index b3b12f0e..dadf7094 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ Requires minimum PHP 7.4.
|![Screenshot #3](/static/screenshot-3.png?raw=true)|![Screenshot #4](/static/screenshot-4.png?raw=true)|
|![Screenshot #5](/static/screenshot-5.png?raw=true)|![Screenshot #6](/static/screenshot-6.png?raw=true)|
-## A subset of bridges (16/447)
+## A subset of bridges (15/447)
* `CssSelectorBridge`: [Scrape out a feed using CSS selectors](https://rss-bridge.org/bridge01/#bridge-CssSelectorBridge)
* `FeedMergeBridge`: [Combine multiple feeds into one](https://rss-bridge.org/bridge01/#bridge-FeedMergeBridge)
@@ -44,7 +44,6 @@ Requires minimum PHP 7.4.
* `ThePirateBayBridge:` [Fetches torrents by search/user/category](https://rss-bridge.org/bridge01/#bridge-ThePirateBayBridge)
* `TikTokBridge`: [Fetches posts by username](https://rss-bridge.org/bridge01/#bridge-TikTokBridge)
* `TwitchBridge`: [Fetches videos from channel](https://rss-bridge.org/bridge01/#bridge-TwitchBridge)
-* `VkBridge`: [Fetches posts from user/group](https://rss-bridge.org/bridge01/#bridge-VkBridge)
* `XPathBridge`: [Scrape out a feed using XPath expressions](https://rss-bridge.org/bridge01/#bridge-XPathBridge)
* `YoutubeBridge`: [Fetches videos by username/channel/playlist/search](https://rss-bridge.org/bridge01/#bridge-YoutubeBridge)
* `YouTubeCommunityTabBridge`: [Fetches posts from a channel's community tab](https://rss-bridge.org/bridge01/#bridge-YouTubeCommunityTabBridge)
@@ -72,27 +71,27 @@ useradd --shell /bin/bash --create-home rss-bridge
cd /var/www
-# Create folder and change ownership
+# Create folder and change its ownership to rss-bridge
mkdir rss-bridge && chown rss-bridge:rss-bridge rss-bridge/
-# Become user
+# Become rss-bridge
su rss-bridge
-# Fetch latest master
+# Clone master branch into existing folder
git clone https://github.com/RSS-Bridge/rss-bridge.git rss-bridge/
cd rss-bridge
-# Copy over the default config
+# Copy over the default config (OPTIONAL)
cp -v config.default.ini.php config.ini.php
-# Give full permissions only to owner (rss-bridge)
-chmod 700 -R ./
+# Recursively give full permissions to user/owner
+chmod 700 --recursive ./
-# Give read and execute to others (nginx and php-fpm)
+# Give read and execute to others on folder ./static
chmod o+rx ./ ./static
-# Give read to others (nginx)
-chmod o+r -R ./static
+# Recursively give give read to others on folder ./static
+chmod o+r --recursive ./static
```
Nginx config:
@@ -110,17 +109,14 @@ server {
error_log /var/log/nginx/rss-bridge.error.log;
log_not_found off;
- # Intentionally not setting a root folder here
-
- # autoindex is off by default but feels good to explicitly turn off
- autoindex off;
+ # Intentionally not setting a root folder
# Static content only served here
location /static/ {
alias /var/www/rss-bridge/static/;
}
- # Pass off to php-fpm when location is exactly /
+ # Pass off to php-fpm only when location is EXACTLY == /
location = / {
root /var/www/rss-bridge/;
include snippets/fastcgi-php.conf;
@@ -128,12 +124,12 @@ server {
fastcgi_pass unix:/run/php/rss-bridge.sock;
}
- # Reduce spam
+ # Reduce log noise
location = /favicon.ico {
access_log off;
}
- # Reduce spam
+ # Reduce log noise
location = /robots.txt {
access_log off;
}
@@ -154,11 +150,11 @@ listen = /run/php/rss-bridge.sock
listen.owner = www-data
listen.group = www-data
-# Create 10 workers standing by to serve requests
+; Create 10 workers standing by to serve requests
pm = static
pm.max_children = 10
-# Respawn worker after 500 requests (workaround for memory leaks etc.)
+; Respawn worker after 500 requests (workaround for memory leaks etc.)
pm.max_requests = 500
```
@@ -464,7 +460,6 @@ See [CONTRIBUTORS.md](CONTRIBUTORS.md)
RSS-Bridge uses caching to prevent services from banning your server for repeatedly updating feeds.
The specific cache duration can be different between bridges.
-Cached files are deleted automatically after 24 hours.
RSS-Bridge allows you to take full control over which bridges are displayed to the user.
That way you can host your own RSS-Bridge service with your favorite collection of bridges!
diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php
index 845bfd84..10af8ad7 100644
--- a/actions/DisplayAction.php
+++ b/actions/DisplayAction.php
@@ -23,7 +23,7 @@ class DisplayAction implements ActionInterface
$noproxy = $request->get('_noproxy');
if (!$bridgeName) {
- return new Response(render(__DIR__ . '/../templates/error.html.php', ['message' => 'Missing bridge parameter']), 400);
+ return new Response(render(__DIR__ . '/../templates/error.html.php', ['message' => 'Missing bridge name parameter']), 400);
}
$bridgeClassName = $this->bridgeFactory->createBridgeClassName($bridgeName);
if (!$bridgeClassName) {
diff --git a/actions/FrontpageAction.php b/actions/FrontpageAction.php
index 824441b2..79ffb4f5 100644
--- a/actions/FrontpageAction.php
+++ b/actions/FrontpageAction.php
@@ -12,7 +12,7 @@ final class FrontpageAction implements ActionInterface
public function __invoke(Request $request): Response
{
- $token = $request->attribute('token');
+ $token = $request->getAttribute('token');
$messages = [];
$activeBridges = 0;
diff --git a/bridges/AO3Bridge.php b/bridges/AO3Bridge.php
index 970ed414..7e18b657 100644
--- a/bridges/AO3Bridge.php
+++ b/bridges/AO3Bridge.php
@@ -27,6 +27,7 @@ class AO3Bridge extends BridgeAbstract
'Entire work' => 'all',
],
],
+ 'limit' => self::LIMIT,
],
'Bookmarks' => [
'user' => [
@@ -84,6 +85,8 @@ class AO3Bridge extends BridgeAbstract
}
$this->title = $heading->plaintext;
+ $limit = $this->getInput('limit') ?? 3;
+ $count = 0;
foreach ($html->find('.index.group > li') as $element) {
$item = [];
@@ -118,7 +121,7 @@ class AO3Bridge extends BridgeAbstract
$item['uid'] = $item['uri'] . "/$strdate/$chapters";
// Fetch workskin of desired chapter(s) in list
- if ($this->getInput('range')) {
+ if ($this->getInput('range') && ($limit == 0 || $count++ < $limit)) {
$url = $item['uri'];
switch ($this->getInput('range')) {
case ('all'):
diff --git a/bridges/AirBreizhBridge.php b/bridges/AirBreizhBridge.php
index a822625f..272c74ee 100644
--- a/bridges/AirBreizhBridge.php
+++ b/bridges/AirBreizhBridge.php
@@ -32,8 +32,7 @@ class AirBreizhBridge extends BridgeAbstract
public function collectData()
{
$html = '';
- $html = getSimpleHTMLDOM(static::URI . 'publications/?fwp_publications_thematiques=' . $this->getInput('theme'))
- or returnClientError('No results for this query.');
+ $html = getSimpleHTMLDOM(static::URI . 'publications/?fwp_publications_thematiques=' . $this->getInput('theme'));
foreach ($html->find('article') as $article) {
$item = [];
diff --git a/bridges/AmazonPriceTrackerBridge.php b/bridges/AmazonPriceTrackerBridge.php
index b07bdb7c..5f93eb49 100644
--- a/bridges/AmazonPriceTrackerBridge.php
+++ b/bridges/AmazonPriceTrackerBridge.php
@@ -146,7 +146,7 @@ EOT;
{
$uri = $this->getURI();
- return getSimpleHTMLDOM($uri) ?: returnServerError('Could not request Amazon.');
+ return getSimpleHTMLDOM($uri);
}
private function scrapePriceFromMetrics($html)
diff --git a/bridges/AnisearchBridge.php b/bridges/AnisearchBridge.php
index c805cfcb..c6f3d291 100644
--- a/bridges/AnisearchBridge.php
+++ b/bridges/AnisearchBridge.php
@@ -67,7 +67,7 @@ class AnisearchBridge extends BridgeAbstract
$trailerlink = $domarticle->find('section#trailers > div > div.swiper > ul.swiper-wrapper > li.swiper-slide > a', 0);
if (isset($trailerlink)) {
$trailersite = getSimpleHTMLDOM($baseurl . $trailerlink->href);
- $trailer = $trailersite->find('div#player > iframe', 0);
+ $trailer = $trailersite->find('div#video > iframe', 0);
$trailer = $trailer->{'data-xsrc'};
$ytlink = <<<EOT
<br /><iframe width="560" height="315" src="$trailer" title="YouTube video player"
diff --git a/bridges/AnthropicBridge.php b/bridges/AnthropicBridge.php
new file mode 100644
index 00000000..1272d35f
--- /dev/null
+++ b/bridges/AnthropicBridge.php
@@ -0,0 +1,147 @@
+<?php
+
+class AnthropicBridge extends BridgeAbstract
+{
+ const MAINTAINER = 'sqrtminusone';
+ const NAME = 'Anthropic Research Bridge';
+ const URI = 'https://www.anthropic.com';
+
+ const CACHE_TIMEOUT = 3600; // 1 hour
+ const DESCRIPTION = 'Returns research publications from Anthropic';
+
+ const PARAMETERS = [
+ '' => [
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10
+ ],
+ ]
+ ];
+
+ public function collectData()
+ {
+ // Anthropic sometimes returns 500 for no reason. The contents are still there.
+ $html = $this->getHTMLIgnoreError(self::URI . '/research');
+ $limit = $this->getInput('limit');
+
+ $page_data = $this->extractPageData($html);
+ $pages = $this->parsePageData($page_data);
+ for ($i = 0; $i < min(count($pages), $limit); $i++) {
+ $page = $pages[$i];
+ $page['content'] = $this->parsePage($page['uri']);
+ $this->items[] = $page;
+ }
+ }
+
+ private function getHTMLIgnoreError($url, $ttl = null)
+ {
+ if ($ttl != null) {
+ $cacheKey = 'pages_' . $url;
+ $content = $this->cache->get($cacheKey);
+ if ($content) {
+ return str_get_html($content);
+ }
+ }
+
+ try {
+ $content = getContents($url);
+ } catch (HttpException $e) {
+ $content = $e->response->getBody();
+ }
+ if ($ttl != null) {
+ $this->cache->set($cacheKey, $content, $ttl);
+ }
+ return str_get_html($content);
+ }
+
+ private function extractPageData($html)
+ {
+ foreach ($html->find('script') as $script) {
+ $js_code = $script->innertext;
+ if (!str_starts_with($js_code, 'self.__next_f.push(')) {
+ continue;
+ }
+ $push_data = (string)json_decode(mb_substr($js_code, 22, mb_strlen($js_code) - 2 - 22));
+ $square_bracket = mb_strpos($push_data, '[');
+ $push_array = json_decode(mb_substr($push_data, $square_bracket), true);
+ if ($push_array == null || count($push_array) < 4) {
+ continue;
+ }
+ $page_data = $push_array[3];
+ if ($page_data != null && array_key_exists('page', $page_data)) {
+ return $page_data;
+ }
+ }
+ }
+
+ private function parsePageData($page_data)
+ {
+ $result = [];
+ foreach ($page_data['page']['sections'] as $section) {
+ if (
+ !array_key_exists('internalName', $section) ||
+ $section['internalName'] != 'Research Teams'
+ ) {
+ continue;
+ }
+ foreach ($section['tabPages'] as $tabPage) {
+ if ($tabPage['label'] != 'Overview') {
+ continue;
+ }
+ foreach ($tabPage['sections'] as $section1) {
+ if (
+ !array_key_exists('title', $section1)
+ || $section1['title'] != 'Publications'
+ ) {
+ continue;
+ }
+ foreach ($section1['posts'] as $post) {
+ $enc = [];
+ if ($post['cta'] != null && array_key_exists('url', $post['cta'])) {
+ $enc = [$post['cta']['url']];
+ }
+ $result[] = [
+ 'title' => $post['title'],
+ 'timestamp' => $post['publishedOn'],
+ 'uri' => self::URI . '/research/' . $post['slug']['current'],
+ 'categories' => array_map(
+ fn($s) => $s['label'],
+ $post['subjects'],
+ ),
+ 'enclosures' => $enc,
+ ];
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return $result;
+ }
+
+ private function parsePage($url)
+ {
+ // Again, 500 for no reason.
+ $html = $this->getHTMLIgnoreError($url, 7 * 24 * 60 * 60);
+
+ $content = '';
+
+ // Main content
+ $main = $html->find('div[class*="PostDetail_post-detail"] > article', 0);
+
+ // Mostly YouTube videos
+ $iframes = $main->find('iframe');
+ foreach ($iframes as $iframe) {
+ $iframe->parent->removeAttribute('style');
+ $iframe->outertext = '<a href="' . $iframe->src . '">' . $iframe->src . '</a>';
+ }
+
+ $main = convertLazyLoading($main);
+ $main = defaultLinkTo($main, self::URI);
+ $content .= $main;
+ return $content;
+ }
+}
diff --git a/bridges/AssociatedPressNewsBridge.php b/bridges/AssociatedPressNewsBridge.php
index 0f8846eb..db62c826 100644
--- a/bridges/AssociatedPressNewsBridge.php
+++ b/bridges/AssociatedPressNewsBridge.php
@@ -105,8 +105,7 @@ class AssociatedPressNewsBridge extends BridgeAbstract
private function collectCardData()
{
- $json = getContents($this->getTagURI())
- or returnServerError('Could not request: ' . $this->getTagURI());
+ $json = getContents($this->getTagURI());
$tagContents = json_decode($json, true);
diff --git a/bridges/BAEBridge.php b/bridges/BAEBridge.php
index 6807d548..a0e4c536 100644
--- a/bridges/BAEBridge.php
+++ b/bridges/BAEBridge.php
@@ -29,7 +29,7 @@ class BAEBridge extends BridgeAbstract
public function collectData()
{
$url = $this->getURI();
- $html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.');
+ $html = getSimpleHTMLDOM($url);
$annonces = $html->find('main article');
foreach ($annonces as $annonce) {
diff --git a/bridges/BandcampDailyBridge.php b/bridges/BandcampDailyBridge.php
index 57299a17..1f9a031d 100644
--- a/bridges/BandcampDailyBridge.php
+++ b/bridges/BandcampDailyBridge.php
@@ -93,8 +93,7 @@ class BandcampDailyBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM($this->getURI())
- or returnServerError('Could not request: ' . $this->getURI());
+ $html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, self::URI);
@@ -105,8 +104,7 @@ class BandcampDailyBridge extends BridgeAbstract
$articlePath = $article->find('a.title', 0)->href;
- $articlePageHtml = getSimpleHTMLDOMCached($articlePath, 3600)
- or returnServerError('Could not request: ' . $articlePath);
+ $articlePageHtml = getSimpleHTMLDOMCached($articlePath, 3600);
$item['uri'] = $articlePath;
$item['title'] = $articlePageHtml->find('article-title', 0)->innertext;
diff --git a/bridges/BlizzardNewsBridge.php b/bridges/BlizzardNewsBridge.php
index 993492d4..4d82b318 100644
--- a/bridges/BlizzardNewsBridge.php
+++ b/bridges/BlizzardNewsBridge.php
@@ -1,6 +1,6 @@
<?php
-class BlizzardNewsBridge extends XPathAbstract
+class BlizzardNewsBridge extends BridgeAbstract
{
const NAME = 'Blizzard News';
const URI = 'https://news.blizzard.com';
@@ -35,33 +35,73 @@ class BlizzardNewsBridge extends XPathAbstract
];
const CACHE_TIMEOUT = 3600;
- const XPATH_EXPRESSION_ITEM = '/html/body/div/div[4]/div[2]/div[2]/div/div/section/ol/li/article';
- const XPATH_EXPRESSION_ITEM_TITLE = './/div/div[2]/h2';
- const XPATH_EXPRESSION_ITEM_CONTENT = './/div[@class="ArticleListItem-description"]/div[@class="h6"]/text()';
- const XPATH_EXPRESSION_ITEM_URI = './/a[@class="ArticleLink ArticleLink"]/@href';
- const XPATH_EXPRESSION_ITEM_AUTHOR = '';
- const XPATH_EXPRESSION_ITEM_TIMESTAMP = './/time[@class="ArticleListItem-footerTimestamp"]/@timestamp';
- const XPATH_EXPRESSION_ITEM_ENCLOSURES = './/div[@class="ArticleListItem-image"]/@style';
- const XPATH_EXPRESSION_ITEM_CATEGORIES = './/div[@class="ArticleListItem-label"]';
- const SETTING_FIX_ENCODING = true;
+ private const PRODUCT_IDS = [
+ 'blt525c436e4a1b0a97',
+ 'blt54fbd3787a705054',
+ 'blt2031aef34200656d',
+ 'blt795c314400d7ded9',
+ 'blt5cfc6affa3ca0638',
+ 'blt2e50e1521bb84dc6',
+ 'blt376fb94931906b6f',
+ 'blt81d46fcb05ab8811',
+ 'bltede2389c0a8885aa',
+ 'blt24859ba8086fb294',
+ 'blte27d02816a8ff3e1',
+ 'blt2caca37e42f19839',
+ 'blt90855744d00cd378',
+ 'bltec70ad0ea4fd6d1d',
+ 'blt500c1f8b5470bfdb'
+ ];
+
+ private const API_PATH = '/api/news/blizzard?';
/**
* Source Web page URL (should provide either HTML or XML content)
* @return string
*/
- protected function getSourceUrl()
+ private function getSourceUrl(): string
{
$locale = $this->getInput('locale');
if ('zh-cn' === $locale) {
- return 'https://cn.news.blizzard.com';
+ $baseUrl = 'https://cn.news.blizzard.com' . self::API_PATH;
+ } else {
+ $baseUrl = 'https://news.blizzard.com/' . $locale . self::API_PATH;
}
- return 'https://news.blizzard.com/' . $locale;
+ return $baseUrl .= http_build_query([
+ 'feedCxpProductIds' => self::PRODUCT_IDS
+ ]);
+ }
+
+ public function collectData()
+ {
+ $feedContent = json_decode(getContents($this->getSourceUrl()), true);
+
+ foreach ($feedContent['feed']['contentItems'] as $entry) {
+ $properties = $entry['properties'];
+
+ $item = [];
+
+ $item['title'] = $this->filterChars($properties['title']);
+ $item['content'] = $this->filterChars($properties['summary']);
+ $item['uri'] = $properties['newsUrl'];
+ $item['author'] = $this->filterChars($properties['author']);
+ $item['timestamp'] = strtotime($properties['lastUpdated']);
+ $item['enclosures'] = [$properties['staticAsset']['imageUrl']];
+ $item['categories'] = [$this->filterChars($properties['cxpProduct']['title'])];
+
+ $this->items[] = $item;
+ }
+ }
+
+ private function filterChars($content)
+ {
+ return htmlspecialchars($content, ENT_XML1);
}
public function getIcon()
{
return <<<icon
-https://blznews.akamaized.net/images/favicon-cb34a003c6f2f637ee8f4f7b406f3b9b120b918c04cabec7f03a760e708977ea9689a1c638f4396def8dce7b202cd007eae91946cc3c4a578aa8b5694226cfc6.ico
+https://dfbmfbnnydoln.cloudfront.net/production/images/favicons/favicon.ba01bb119359d74970b02902472fd82e96b5aba7.ico
icon;
}
}
diff --git a/bridges/BlueskyBridge.php b/bridges/BlueskyBridge.php
new file mode 100644
index 00000000..67bb5af9
--- /dev/null
+++ b/bridges/BlueskyBridge.php
@@ -0,0 +1,620 @@
+<?php
+
+class BlueskyBridge extends BridgeAbstract
+{
+ //Initial PR by [RSSBridge contributors](https://github.com/RSS-Bridge/rss-bridge/issues/4058).
+ //Modified from [©DIYgod and contributors at RSSHub](https://github.com/DIYgod/RSSHub/tree/master/lib/routes/bsky), MIT License';
+ const NAME = 'Bluesky Bridge';
+ const URI = 'https://bsky.app';
+ const DESCRIPTION = 'Fetches posts from Bluesky';
+ const MAINTAINER = 'mruac';
+ const PARAMETERS = [
+ [
+ 'data_source' => [
+ 'name' => 'Bluesky Data Source',
+ 'type' => 'list',
+ 'defaultValue' => 'Profile',
+ 'values' => [
+ 'Profile' => 'getAuthorFeed',
+ ],
+ 'title' => 'Select the type of data source to fetch from Bluesky.'
+ ],
+ 'user_id' => [
+ 'name' => 'User Handle or DID',
+ 'type' => 'text',
+ 'required' => true,
+ 'exampleValue' => 'did:plc:z72i7hdynmk6r22z27h6tvur',
+ 'title' => 'ATProto / Bsky.app handle or DID'
+ ],
+ 'feed_filter' => [
+ 'name' => 'Feed type',
+ 'type' => 'list',
+ 'defaultValue' => 'posts_and_author_threads',
+ 'values' => [
+ 'Posts feed' => 'posts_and_author_threads',
+ 'All posts and replies' => 'posts_with_replies',
+ 'Root posts only' => 'posts_no_replies',
+ 'Media only' => 'posts_with_media',
+ ]
+ ],
+
+ 'include_reposts' => [
+ 'name' => 'Include Reposts?',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+
+ 'include_reply_context' => [
+ 'name' => 'Include Reply context?',
+ 'type' => 'checkbox'
+ ],
+
+ 'verbose_title' => [
+ 'name' => 'Use verbose feed item titles?',
+ 'type' => 'checkbox'
+ ]
+ ]
+ ];
+
+ private $profile;
+
+ public function getName()
+ {
+ if (isset($this->profile)) {
+ if ($this->profile['handle'] === 'handle.invalid') {
+ return sprintf('Bluesky - %s', $this->profile['displayName']);
+ } else {
+ return sprintf('Bluesky - %s (@%s)', $this->profile['displayName'], $this->profile['handle']);
+ }
+ }
+ return parent::getName();
+ }
+
+ public function getURI()
+ {
+ if (isset($this->profile)) {
+ if ($this->profile['handle'] === 'handle.invalid') {
+ return self::URI . '/profile/' . $this->profile['did'];
+ } else {
+ return self::URI . '/profile/' . $this->profile['handle'];
+ }
+ }
+ return parent::getURI();
+ }
+
+ public function getIcon()
+ {
+ if (isset($this->profile)) {
+ return $this->profile['avatar'];
+ }
+ return parent::getIcon();
+ }
+
+ public function getDescription()
+ {
+ if (isset($this->profile)) {
+ return $this->profile['description'];
+ }
+ return parent::getDescription();
+ }
+
+ private function parseExternal($external, $did)
+ {
+ $description = '';
+ $externalUri = $external['uri'];
+ $externalTitle = e($external['title']);
+ $externalDescription = e($external['description']);
+ $thumb = $external['thumb'] ?? null;
+
+ if (preg_match('/http(|s):\/\/media\.tenor\.com/', $externalUri)) {
+ //tenor gif embed
+ $tenorInterstitial = str_replace('media.tenor.com', 'media1.tenor.com/m', $externalUri);
+ $description .= "<figure><a href=\"$tenorInterstitial\"><img src=\"$externalUri\"/></a><figcaption>$externalTitle</figcaption></figure>";
+ } else {
+ //link embed preview
+ $host = parse_url($externalUri)['host'];
+ $thumbDesc = $thumb ? ('<img src="https://cdn.bsky.app/img/feed_thumbnail/plain/' . $did . '/' . $thumb['ref']['$link'] . '@jpeg"/>') : '';
+ $externalDescription = strlen($externalDescription) > 0 ? "<figcaption>($host) $externalDescription</figcaption>" : '';
+ $description .= '<br><blockquote><b><a href="' . $externalUri . '">' . $externalTitle . '</a></b>';
+ $description .= '<figure>' . $thumbDesc . $externalDescription . '</figure></blockquote>';
+ }
+ return $description;
+ }
+
+ private function textToDescription($record)
+ {
+ if (isset($record['value'])) {
+ $record = $record['value'];
+ }
+ $text = $record['text'];
+ $text_copy = $text;
+ $text = nl2br(e($text));
+ if (isset($record['facets'])) {
+ $facets = $record['facets'];
+ foreach ($facets as $facet) {
+ if ($facet['features'][0]['$type'] === 'app.bsky.richtext.facet#link') {
+ $substring = substr($text_copy, $facet['index']['byteStart'], $facet['index']['byteEnd'] - $facet['index']['byteStart']);
+ $text = str_replace($substring, '<a href="' . $facet['features'][0]['uri'] . '">' . $substring . '</a>', $text);
+ }
+ }
+ }
+ return $text;
+ }
+
+ public function collectData()
+ {
+ $user_id = $this->getInput('user_id');
+ $handle_match = preg_match('/(?:[a-zA-Z]*\.)+([a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)/', $user_id, $handle_res); //gets the TLD in $handle_match[1]
+ $did_match = preg_match('/did:plc:[a-z2-7]{24}/', $user_id); //https://github.com/did-method-plc/did-method-plc#identifier-syntax
+ $exclude = ['alt', 'arpa', 'example', 'internal', 'invalid', 'local', 'localhost', 'onion']; //https://en.wikipedia.org/wiki/Top-level_domain#Reserved_domains
+ if ($handle_match == true && array_search($handle_res[1], $exclude) == false) {
+ //valid bsky handle
+ $did = $this->resolveHandle($user_id);
+ } elseif ($did_match == true) {
+ //valid DID
+ $did = $user_id;
+ } else {
+ returnClientError('Invalid ATproto handle or DID provided.');
+ }
+
+ $filter = $this->getInput('feed_filter') ?: 'posts_and_author_threads';
+ $replyContext = $this->getInput('include_reply_context');
+
+ $this->profile = $this->getProfile($did);
+ $authorFeed = $this->getAuthorFeed($did, $filter);
+
+ foreach ($authorFeed['feed'] as $post) {
+ $postRecord = $post['post']['record'];
+
+ $item = [];
+ $item['uri'] = self::URI . '/profile/' . $this->fallbackAuthor($post['post']['author'], 'url') . '/post/' . explode('app.bsky.feed.post/', $post['post']['uri'])[1];
+ $item['title'] = $this->getInput('verbose_title') ? $this->generateVerboseTitle($post) : strtok($postRecord['text'], "\n");
+ $item['timestamp'] = strtotime($postRecord['createdAt']);
+ $item['author'] = $this->fallbackAuthor($post['post']['author'], 'display');
+
+ $postAuthorDID = $post['post']['author']['did'];
+ $postAuthorHandle = $post['post']['author']['handle'] !== 'handle.invalid' ? '<i>@' . $post['post']['author']['handle'] . '</i> ' : '';
+ $postDisplayName = $post['post']['author']['displayName'] ?? '';
+ $postDisplayName = e($postDisplayName);
+ $postUri = $item['uri'];
+
+ if (Debug::isEnabled()) {
+ $url = explode('/', $post['post']['uri']);
+ error_log('https://bsky.app/profile/' . $url[2] . '/post/' . $url[4]);
+ }
+
+ $description = '';
+ $description .= '<p>';
+ //post
+ $description .= $this->getPostDescription(
+ $postDisplayName,
+ $postAuthorHandle,
+ $postUri,
+ $postRecord,
+ 'post'
+ );
+
+ if (isset($postRecord['embed']['$type'])) {
+ //post link embed
+ if ($postRecord['embed']['$type'] === 'app.bsky.embed.external') {
+ $description .= $this->parseExternal($postRecord['embed']['external'], $postAuthorDID);
+ } elseif (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $postRecord['embed']['media']['$type'] === 'app.bsky.embed.external'
+ ) {
+ $description .= $this->parseExternal($postRecord['embed']['media']['external'], $postAuthorDID);
+ }
+
+ //post images
+ if (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.images' ||
+ (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $postRecord['embed']['media']['$type'] === 'app.bsky.embed.images'
+ )
+ ) {
+ $images = $post['post']['embed']['images'] ?? $post['post']['embed']['media']['images'];
+ foreach ($images as $image) {
+ $description .= $this->getPostImageDescription($image);
+ }
+ }
+
+ //post video
+ if (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.video' ||
+ (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $postRecord['embed']['media']['$type'] === 'app.bsky.embed.video'
+ )
+ ) {
+ $description .= $this->getPostVideoDescription(
+ $postRecord['embed']['video'] ?? $postRecord['embed']['media']['video'],
+ $postAuthorDID
+ );
+ }
+ }
+ $description .= '</p>';
+
+ //quote post
+ if (
+ isset($postRecord['embed']) &&
+ (
+ $postRecord['embed']['$type'] === 'app.bsky.embed.record' ||
+ $postRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia'
+ ) &&
+ isset($post['post']['embed']['record'])
+ ) {
+ $description .= '<p>';
+ $quotedRecord = $post['post']['embed']['record']['record'] ?? $post['post']['embed']['record'];
+
+ if (isset($quotedRecord['notFound']) && $quotedRecord['notFound']) { //deleted post
+ $description .= 'Quoted post deleted.';
+ } elseif (isset($quotedRecord['detached']) && $quotedRecord['detached']) { //detached quote
+ $uri_explode = explode('/', $quotedRecord['uri']);
+ $uri_reconstructed = self::URI . '/profile/' . $uri_explode[2] . '/post/' . $uri_explode[4];
+ $description .= '<a href="' . $uri_reconstructed . '">Quoted post detached.</a>';
+ } elseif (isset($quotedRecord['blocked']) && $quotedRecord['blocked']) { //blocked by quote author
+ $description .= 'Author of quoted post has blocked OP.';
+ } elseif (($quotedRecord['$type'] ?? '') === 'app.bsky.feed.defs#generatorView') {
+ $description .= '</p>';
+ $description .= $this->getGeneratorViewDescription($quotedRecord);
+ $description .= '<p>';
+ } else {
+ $quotedAuthorDid = $quotedRecord['author']['did'];
+ $quotedDisplayName = $quotedRecord['author']['displayName'] ?? '';
+ $quotedDisplayName = e($quotedDisplayName);
+ $quotedAuthorHandle = $quotedRecord['author']['handle'] !== 'handle.invalid' ? '<i>@' . $quotedRecord['author']['handle'] . '</i>' : '';
+
+ $parts = explode('/', $quotedRecord['uri']);
+ $quotedPostId = end($parts);
+ $quotedPostUri = self::URI . '/profile/' . $this->fallbackAuthor($quotedRecord['author'], 'url') . '/post/' . $quotedPostId;
+
+ //quoted post - post
+ $description .= $this->getPostDescription(
+ $quotedDisplayName,
+ $quotedAuthorHandle,
+ $quotedPostUri,
+ $quotedRecord,
+ 'quote'
+ );
+
+ if (isset($quotedRecord['value']['embed']['$type'])) {
+ //quoted post - post link embed
+ if ($quotedRecord['value']['embed']['$type'] === 'app.bsky.embed.external') {
+ $description .= $this->parseExternal($quotedRecord['value']['embed']['external'], $quotedAuthorDid);
+ }
+
+ //quoted post - post video
+ if (
+ $quotedRecord['value']['embed']['$type'] === 'app.bsky.embed.video' ||
+ (
+ $quotedRecord['value']['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $quotedRecord['value']['embed']['media']['$type'] === 'app.bsky.embed.video'
+ )
+ ) {
+ $description .= $this->getPostVideoDescription(
+ $quotedRecord['value']['embed']['video'] ?? $quotedRecord['value']['embed']['media']['video'],
+ $quotedAuthorDid
+ );
+ }
+
+ //quoted post - post images
+ if (
+ $quotedRecord['value']['embed']['$type'] === 'app.bsky.embed.images' ||
+ (
+ $quotedRecord['value']['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $quotedRecord['value']['embed']['media']['$type'] === 'app.bsky.embed.images'
+ )
+ ) {
+ foreach ($quotedRecord['embeds'] as $embed) {
+ if (
+ $embed['$type'] === 'app.bsky.embed.images#view' ||
+ ($embed['$type'] === 'app.bsky.embed.recordWithMedia#view' && $embed['media']['$type'] === 'app.bsky.embed.images#view')
+ ) {
+ $images = $embed['images'] ?? $embed['media']['images'];
+ foreach ($images as $image) {
+ $description .= $this->getPostImageDescription($image);
+ }
+ }
+ }
+ }
+ }
+ }
+ $description .= '</p>';
+ }
+
+ //reply
+ if ($replyContext && isset($post['reply']) && !isset($post['reply']['parent']['notFound'])) {
+ $replyPost = $post['reply']['parent'];
+ $replyPostRecord = $replyPost['record'];
+ $description .= '<hr/>';
+ $description .= '<p>';
+
+ $replyPostAuthorDID = $replyPost['author']['did'];
+ $replyPostAuthorHandle = $replyPost['author']['handle'] !== 'handle.invalid' ? '<i>@' . $replyPost['author']['handle'] . '</i> ' : '';
+ $replyPostDisplayName = $replyPost['author']['displayName'] ?? '';
+ $replyPostDisplayName = e($replyPostDisplayName);
+ $replyPostUri = self::URI . '/profile/' . $this->fallbackAuthor($replyPost['author'], 'url') . '/post/' . explode('app.bsky.feed.post/', $replyPost['uri'])[1];
+
+ // reply post
+ $description .= $this->getPostDescription(
+ $replyPostDisplayName,
+ $replyPostAuthorHandle,
+ $replyPostUri,
+ $replyPostRecord,
+ 'reply'
+ );
+
+ if (isset($replyPostRecord['embed']['$type'])) {
+ //post link embed
+ if ($replyPostRecord['embed']['$type'] === 'app.bsky.embed.external') {
+ $description .= $this->parseExternal($replyPostRecord['embed']['external'], $replyPostAuthorDID);
+ } elseif (
+ $replyPostRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $replyPostRecord['embed']['media']['$type'] === 'app.bsky.embed.external'
+ ) {
+ $description .= $this->parseExternal($replyPostRecord['embed']['media']['external'], $replyPostAuthorDID);
+ }
+
+ //post images
+ if (
+ $replyPostRecord['embed']['$type'] === 'app.bsky.embed.images' ||
+ (
+ $replyPostRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $replyPostRecord['embed']['media']['$type'] === 'app.bsky.embed.images'
+ )
+ ) {
+ $images = $replyPost['embed']['images'] ?? $replyPost['embed']['media']['images'];
+ foreach ($images as $image) {
+ $description .= $this->getPostImageDescription($image);
+ }
+ }
+
+ //post video
+ if (
+ $replyPostRecord['embed']['$type'] === 'app.bsky.embed.video' ||
+ (
+ $replyPostRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $replyPostRecord['embed']['media']['$type'] === 'app.bsky.embed.video'
+ )
+ ) {
+ $description .= $this->getPostVideoDescription(
+ $replyPostRecord['embed']['video'] ?? $replyPostRecord['embed']['media']['video'],
+ $replyPostAuthorDID
+ );
+ }
+ }
+ $description .= '</p>';
+
+ //quote post
+ if (
+ isset($replyPostRecord['embed']) &&
+ ($replyPostRecord['embed']['$type'] === 'app.bsky.embed.record' || $replyPostRecord['embed']['$type'] === 'app.bsky.embed.recordWithMedia') &&
+ isset($replyPost['embed']['record'])
+ ) {
+ $description .= '<p>';
+ $replyQuotedRecord = $replyPost['embed']['record']['record'] ?? $replyPost['embed']['record'];
+
+ if (isset($replyQuotedRecord['notFound']) && $replyQuotedRecord['notFound']) { //deleted post
+ $description .= 'Quoted post deleted.';
+ } elseif (isset($replyQuotedRecord['detached']) && $replyQuotedRecord['detached']) { //detached quote
+ $uri_explode = explode('/', $replyQuotedRecord['uri']);
+ $uri_reconstructed = self::URI . '/profile/' . $uri_explode[2] . '/post/' . $uri_explode[4];
+ $description .= '<a href="' . $uri_reconstructed . '">Quoted post detached.</a>';
+ } elseif (isset($replyQuotedRecord['blocked']) && $replyQuotedRecord['blocked']) { //blocked by quote author
+ $description .= 'Author of quoted post has blocked OP.';
+ } elseif (($replyQuotedRecord['$type'] ?? '') === 'app.bsky.feed.defs#generatorView') {
+ $description .= '</p>';
+ $description .= $this->getGeneratorViewDescription($replyQuotedRecord);
+ $description .= '<p>';
+ } else {
+ $quotedAuthorDid = $replyQuotedRecord['author']['did'];
+ $quotedDisplayName = $replyQuotedRecord['author']['displayName'] ?? '';
+ $quotedDisplayName = e($quotedDisplayName);
+ $quotedAuthorHandle = $replyQuotedRecord['author']['handle'] !== 'handle.invalid' ? '<i>@' . $replyQuotedRecord['author']['handle'] . '</i>' : '';
+
+ $parts = explode('/', $replyQuotedRecord['uri']);
+ $quotedPostId = end($parts);
+ $quotedPostUri = self::URI . '/profile/' . $this->fallbackAuthor($replyQuotedRecord['author'], 'url') . '/post/' . $quotedPostId;
+
+ //quoted post - post
+ $description .= $this->getPostDescription(
+ $quotedDisplayName,
+ $quotedAuthorHandle,
+ $quotedPostUri,
+ $replyQuotedRecord,
+ 'quote'
+ );
+
+ if (isset($replyQuotedRecord['value']['embed']['$type'])) {
+ //quoted post - post link embed
+ if ($replyQuotedRecord['value']['embed']['$type'] === 'app.bsky.embed.external') {
+ $description .= $this->parseExternal($replyQuotedRecord['value']['embed']['external'], $quotedAuthorDid);
+ }
+
+ //quoted post - post video
+ if (
+ $replyQuotedRecord['value']['embed']['$type'] === 'app.bsky.embed.video' ||
+ (
+ $replyQuotedRecord['value']['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $replyQuotedRecord['value']['embed']['media']['$type'] === 'app.bsky.embed.video'
+ )
+ ) {
+ $description .= $this->getPostVideoDescription(
+ $replyQuotedRecord['value']['embed']['video'] ?? $replyQuotedRecord['value']['embed']['media']['video'],
+ $quotedAuthorDid
+ );
+ }
+
+ //quoted post - post images
+ if (
+ $replyQuotedRecord['value']['embed']['$type'] === 'app.bsky.embed.images' ||
+ (
+ $replyQuotedRecord['value']['embed']['$type'] === 'app.bsky.embed.recordWithMedia' &&
+ $replyQuotedRecord['value']['embed']['media']['$type'] === 'app.bsky.embed.images'
+ )
+ ) {
+ foreach ($replyQuotedRecord['embeds'] as $embed) {
+ if (
+ $embed['$type'] === 'app.bsky.embed.images#view' ||
+ ($embed['$type'] === 'app.bsky.embed.recordWithMedia#view' && $embed['media']['$type'] === 'app.bsky.embed.images#view')
+ ) {
+ $images = $embed['images'] ?? $embed['media']['images'];
+ foreach ($images as $image) {
+ $description .= $this->getPostImageDescription($image);
+ }
+ }
+ }
+ }
+ }
+ }
+ $description .= '</p>';
+ }
+ }
+
+ $item['content'] = $description;
+ $this->items[] = $item;
+ }
+ }
+
+ private function getPostVideoDescription(array $video, $authorDID)
+ {
+ //https://video.bsky.app/watch/$did/$cid/thumbnail.jpg
+ $videoCID = $video['ref']['$link'];
+ $videoMime = $video['mimeType'];
+ $thumbnail = "poster=\"https://video.bsky.app/watch/$authorDID/$videoCID/thumbnail.jpg\"" ?? '';
+ $videoURL = "https://bsky.social/xrpc/com.atproto.sync.getBlob?did=$authorDID&cid=$videoCID";
+ return "<figure><video loop $thumbnail controls src=\"$videoURL\" type=\"$videoMime\"/></figure>";
+ }
+
+ private function getPostImageDescription(array $image)
+ {
+ $thumbnailUrl = $image['thumb'];
+ $fullsizeUrl = $image['fullsize'];
+ $alt = strlen($image['alt']) > 0 ? '<figcaption>' . e($image['alt']) . '</figcaption>' : '';
+ return "<figure><a href=\"$fullsizeUrl\"><img src=\"$thumbnailUrl\"></a>$alt</figure>";
+ }
+
+ private function getPostDescription(
+ string $postDisplayName,
+ string $postAuthorHandle,
+ string $postUri,
+ array $postRecord,
+ string $type
+ ) {
+ $description = '';
+ if ($type === 'quote') {
+ // Quoted post/reply from bbb @bbb.com:
+ $postType = isset($postRecord['reply']) ? 'reply' : 'post';
+ $description .= "<a href=\"$postUri\">Quoted $postType</a> from <b>$postDisplayName</b> $postAuthorHandle:<br>";
+ } elseif ($type === 'reply') {
+ // Replying to aaa @aaa.com's post/reply:
+ $postType = isset($postRecord['reply']) ? 'reply' : 'post';
+ $description .= "Replying to <b>$postDisplayName</b> $postAuthorHandle's <a href=\"$postUri\">$postType</a>:<br>";
+ } else {
+ // aaa @aaa.com posted:
+ $description .= "<b>$postDisplayName</b> $postAuthorHandle <a href=\"$postUri\">posted</a>:<br>";
+ }
+ $description .= $this->textToDescription($postRecord);
+ return $description;
+ }
+
+ //used if handle verification fails, fallsback to displayName or DID depending on context.
+ private function fallbackAuthor($author, $reason)
+ {
+ if ($author['handle'] === 'handle.invalid') {
+ switch ($reason) {
+ case 'url':
+ return $author['did'];
+ case 'display':
+ $displayName = $author['displayName'] ?? '';
+ return e($displayName);
+ }
+ }
+ return $author['handle'];
+ }
+
+ private function generateVerboseTitle($post)
+ {
+ //use "Post by A, replying to B, quoting C" instead of post contents
+ $title = '';
+ if (isset($post['reason']) && str_contains($post['reason']['$type'], 'reasonRepost')) {
+ $title .= 'Repost by ' . $this->fallbackAuthor($post['reason']['by'], 'display') . ', post by ' . $this->fallbackAuthor($post['post']['author'], 'display');
+ } else {
+ $title .= 'Post by ' . $this->fallbackAuthor($post['post']['author'], 'display');
+ }
+
+ if (isset($post['reply'])) {
+ if (isset($post['reply']['parent']['blocked'])) {
+ $replyAuthor = 'blocked user';
+ } elseif (isset($post['reply']['parent']['notFound'])) {
+ $replyAuthor = 'deleted post';
+ } else {
+ $replyAuthor = $this->fallbackAuthor($post['reply']['parent']['author'], 'display');
+ }
+ $title .= ', replying to ' . $replyAuthor;
+ }
+ if (isset($post['post']['embed']) && isset($post['post']['embed']['record'])) {
+ if (isset($post['post']['embed']['record']['blocked'])) {
+ $quotedAuthor = 'blocked user';
+ } elseif (isset($post['post']['embed']['record']['notFound'])) {
+ $quotedAuthor = 'deleted post';
+ } elseif (isset($post['post']['embed']['record']['detached'])) {
+ $quotedAuthor = 'detached post';
+ } else {
+ $quotedAuthor = $this->fallbackAuthor($post['post']['embed']['record']['record']['author'] ?? $post['post']['embed']['record']['author'], 'display');
+ }
+ $title .= ', quoting ' . $quotedAuthor;
+ }
+ return $title;
+ }
+
+ private function resolveHandle($handle)
+ {
+ $uri = 'https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle);
+ $response = json_decode(getContents($uri), true);
+ return $response['did'];
+ }
+
+ private function getProfile($did)
+ {
+ $uri = 'https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=' . urlencode($did);
+ $response = json_decode(getContents($uri), true);
+ return $response;
+ }
+
+ private function getAuthorFeed($did, $filter)
+ {
+ $uri = 'https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=' . urlencode($did) . '&filter=' . urlencode($filter) . '&limit=30';
+ if (Debug::isEnabled()) {
+ error_log($uri);
+ }
+ $response = json_decode(getContents($uri), true);
+ return $response;
+ }
+
+ private function getGeneratorViewDescription(array $record): string
+ {
+ $avatar = e($record['avatar']);
+ $displayName = e($record['displayName']);
+ $displayHandle = e($record['creator']['handle']);
+ $likeCount = e($record['likeCount']);
+ preg_match('/\/([^\/]+)$/', $record['uri'], $matches);
+ $uri = e('https://bsky.app/profile/' . $record['creator']['did'] . '/feed/' . $matches[1]);
+
+ return <<<END
+<a href="{$uri}" style="color: inherit;">
+ <div style="border: 1px solid #333; padding: 10px;">
+ <div style="display: flex; margin-bottom: 10px;">
+ <img src="{$avatar}" height="50" width="50" style="margin-right: 10px;">
+ <div style="display: flex; flex-direction: column; justify-content: center;">
+ <h3>{$displayName}</h3>
+ <span>Feed by @{$displayHandle}</span>
+ </div>
+ </div>
+ <span>Liked by {$likeCount} users</span>
+ </div>
+</a>
+END;
+ }
+}
diff --git a/bridges/BukowskisBridge.php b/bridges/BukowskisBridge.php
index 14889889..3573c206 100644
--- a/bridges/BukowskisBridge.php
+++ b/bridges/BukowskisBridge.php
@@ -206,7 +206,7 @@ class BukowskisBridge extends BridgeAbstract
$this->items[] = [
'title' => $title,
'uri' => $baseUrl . $relative_url,
- 'uid' => $lot->getAttribute('data-lot-id'),
+ 'uid' => $relative_url,
'content' => count($images) > 0 ? "<img src='$images[0]'/><br/>$title" : $title,
'enclosures' => array_slice($images, 1),
];
diff --git a/bridges/BundestagParteispendenBridge.php b/bridges/BundestagParteispendenBridge.php
index cdf398e8..773f9129 100644
--- a/bridges/BundestagParteispendenBridge.php
+++ b/bridges/BundestagParteispendenBridge.php
@@ -26,18 +26,16 @@ TMPL;
https://www.bundestag.de/ajax/filterlist/de/parlament/praesidium/parteienfinanzierung/fundstellen50000/462002-462002
URI;
// Get the main page
- $html = getSimpleHTMLDOMCached($ajaxUri, self::CACHE_TIMEOUT)
- or returnServerError('Could not request AJAX list.');
+ $html = getSimpleHTMLDOMCached($ajaxUri, self::CACHE_TIMEOUT);
// Build the URL from the first anchor element. The list is sorted by year, descending, so the first element is the current year.
$firstAnchor = $html->find('a', 0)
or returnServerError('Could not find the proper HTML element.');
- $url = 'https://www.bundestag.de' . $firstAnchor->href;
+ $url = $firstAnchor->href;
// Get the actual page with the soft money donations
- $html = getSimpleHTMLDOMCached($url, self::CACHE_TIMEOUT)
- or returnServerError('Could not request ' . $url);
+ $html = getSimpleHTMLDOMCached($url, self::CACHE_TIMEOUT);
$rows = $html->find('table.table > tbody > tr')
or returnServerError('Could not find the proper HTML elements.');
diff --git a/bridges/CarThrottleBridge.php b/bridges/CarThrottleBridge.php
index 2b8ca2b2..8475a414 100644
--- a/bridges/CarThrottleBridge.php
+++ b/bridges/CarThrottleBridge.php
@@ -66,7 +66,7 @@ class CarThrottleBridge extends BridgeAbstract
foreach ($categoryPage->find('div.cmg-card') as $post) {
$item = [];
- $titleElement = $post->find('div.title a')[0];
+ $titleElement = $post->find('a.title')[0];
$post_uri = self::URI . $titleElement->getAttribute('href');
if (!isset($post_uri) || $post_uri == '') {
@@ -80,8 +80,8 @@ class CarThrottleBridge extends BridgeAbstract
$item['author'] = $this->parseAuthor($articlePage);
- $articleImage = $articlePage->find('div.block-layout-field-image')[0];
- $article = $articlePage->find('div.block-layout-body')[1];
+ $articleImage = $articlePage->find('figure')[0];
+ $article = $articlePage->find('div.first-column div.body')[0];
//remove ads
foreach ($article->find('aside') as $ad) {
diff --git a/bridges/CentreFranceBridge.php b/bridges/CentreFranceBridge.php
index a6dea227..1abfae98 100644
--- a/bridges/CentreFranceBridge.php
+++ b/bridges/CentreFranceBridge.php
@@ -48,6 +48,11 @@ class CentreFranceBridge extends BridgeAbstract
]
];
+ private static array $monthNumberByFrenchName = [
+ 'janvier' => 1, 'février' => 2, 'mars' => 3, 'avril' => 4, 'mai' => 5, 'juin' => 6, 'juillet' => 7,
+ 'août' => 8, 'septembre' => 9, 'octobre' => 10, 'novembre' => 11, 'décembre' => 12
+ ];
+
public function collectData()
{
$value = $this->getInput('limit');
@@ -130,28 +135,22 @@ class CentreFranceBridge extends BridgeAbstract
'enclosures' => [],
];
- $articleInformations = $html->find('.c-article-informations p');
+ $articleInformations = $html->find('#content hgroup > div.typo-p3 > *');
if (is_array($articleInformations) && $articleInformations !== []) {
- $authorPosition = 1;
-
- // Article publication date
- if (preg_match('/(\d{2})\/(\d{2})\/(\d{4})( à (\d{2})h(\d{2}))?/', $articleInformations[0]->innertext, $articleDateParts) > 0) {
- $articleDate = new \DateTime('midnight');
- $articleDate->setDate($articleDateParts[3], $articleDateParts[2], $articleDateParts[1]);
+ $publicationDateIndex = 0;
- if (count($articleDateParts) === 7) {
- $articleDate->setTime($articleDateParts[5], $articleDateParts[6]);
- }
-
- $item['timestamp'] = $articleDate->getTimestamp();
+ // Article author
+ $probableAuthorName = strip_tags($articleInformations[0]->innertext);
+ if (str_starts_with($probableAuthorName, 'Par ')) {
+ $publicationDateIndex = 1;
+ $item['author'] = substr($probableAuthorName, 4);
}
- // Article update date
- if (count($articleInformations) >= 2 && preg_match('/(\d{2})\/(\d{2})\/(\d{4})( à (\d{2})h(\d{2}))?/', $articleInformations[1]->innertext, $articleDateParts) > 0) {
- $authorPosition = 2;
-
+ // Article publication date
+ preg_match('/Publié le (\d{2}) (.+) (\d{4})( à (\d{2})h(\d{2}))?/', strip_tags($articleInformations[$publicationDateIndex]->innertext), $articleDateParts);
+ if ($articleDateParts !== [] && array_key_exists($articleDateParts[2], self::$monthNumberByFrenchName)) {
$articleDate = new \DateTime('midnight');
- $articleDate->setDate($articleDateParts[3], $articleDateParts[2], $articleDateParts[1]);
+ $articleDate->setDate($articleDateParts[3], self::$monthNumberByFrenchName[$articleDateParts[2]], $articleDateParts[1]);
if (count($articleDateParts) === 7) {
$articleDate->setTime($articleDateParts[5], $articleDateParts[6]);
@@ -159,43 +158,31 @@ class CentreFranceBridge extends BridgeAbstract
$item['timestamp'] = $articleDate->getTimestamp();
}
-
- if (count($articleInformations) === ($authorPosition + 1)) {
- $item['author'] = $articleInformations[$authorPosition]->innertext;
- }
}
- $articleContent = $html->find('.b-article .contenu > *');
- if (is_array($articleContent)) {
- $item['content'] = '';
-
- foreach ($articleContent as $contentPart) {
- if (in_array($contentPart->getAttribute('id'), ['cf-audio-player', 'poool-widget'], true)) {
- continue;
+ $articleContent = $html->find('#content>div.flex+div.grid section>.z-10')[0] ?? null;
+ if ($articleContent instanceof \simple_html_dom_node) {
+ $articleHiddenParts = $articleContent->find('.ad-slot, #cf-digiteka-player');
+ if (is_array($articleHiddenParts)) {
+ foreach ($articleHiddenParts as $articleHiddenPart) {
+ $articleContent->removeChild($articleHiddenPart);
}
-
- $articleHiddenParts = $contentPart->find('.bloc, .p402_hide');
- if (is_array($articleHiddenParts)) {
- foreach ($articleHiddenParts as $articleHiddenPart) {
- $contentPart->removeChild($articleHiddenPart);
- }
- }
-
- $item['content'] .= $contentPart->innertext;
}
+
+ $item['content'] = $articleContent->innertext;
}
- $articleIllustration = $html->find('.photo-wrapper .photo-box img');
+ $articleIllustration = $html->find('#content>div.flex+div.grid section>figure>img');
if (is_array($articleIllustration) && count($articleIllustration) === 1) {
$item['enclosures'][] = $articleIllustration[0]->getAttribute('src');
}
- $articleAudio = $html->find('#cf-audio-player-container audio');
+ $articleAudio = $html->find('audio[src^="https://api.octopus.saooti.com/"]');
if (is_array($articleAudio) && count($articleAudio) === 1) {
$item['enclosures'][] = $articleAudio[0]->getAttribute('src');
}
- $articleTags = $html->find('.b-article > ul.c-tags > li > a.t-simple');
+ $articleTags = $html->find('#content>div.flex+div.grid section>.bg-gray-light>a.border-gray-dark');
if (is_array($articleTags)) {
$item['categories'] = array_map(static fn ($articleTag) => $articleTag->innertext, $articleTags);
}
diff --git a/bridges/CeskaTelevizeBridge.php b/bridges/CeskaTelevizeBridge.php
index 026e8c2a..a996bd11 100644
--- a/bridges/CeskaTelevizeBridge.php
+++ b/bridges/CeskaTelevizeBridge.php
@@ -18,25 +18,6 @@ class CeskaTelevizeBridge extends BridgeAbstract
]
];
- private function fixChars($text)
- {
- return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
- }
-
- private function getUploadTimeFromString($string)
- {
- if (strpos($string, 'dnes') !== false) {
- return strtotime('today');
- } elseif (strpos($string, 'včera') !== false) {
- return strtotime('yesterday');
- } elseif (!preg_match('/(\d+).\s(\d+).(\s(\d+))?/', $string, $match)) {
- returnServerError('Could not get date from Česká televize string');
- }
-
- $date = sprintf('%04d-%02d-%02d', $match[3] ?? date('Y'), $match[2], $match[1]);
- return strtotime($date);
- }
-
public function collectData()
{
$url = $this->getInput('url');
@@ -58,24 +39,38 @@ class CeskaTelevizeBridge extends BridgeAbstract
}
foreach ($html->find('#episodeListSection a[data-testid=card]') as $element) {
- $itemTitle = $element->find('h3', 0);
$itemContent = $element->find('p[class^=content-]', 0);
$itemDate = $element->find('div[class^=playTime-] span, [data-testid=episode-item-broadcast] span', 0);
- $itemThumbnail = $element->find('img', 0);
- $itemUri = self::URI . $element->getAttribute('href');
-
$item = [
- 'title' => $this->fixChars($itemTitle->plaintext),
- 'uri' => $itemUri,
- 'content' => '<img src="' . $itemThumbnail->getAttribute('src') . '" /><br />'
- . $this->fixChars($itemContent->plaintext),
- 'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext)
+ 'title' => $this->fixChars($element->find('h3', 0)->plaintext),
+ 'uri' => self::URI . $element->getAttribute('href'),
+ 'content' => '<img src="' . $element->find('img', 0)->getAttribute('srcset') . '" /><br />' . $this->fixChars($itemContent->plaintext),
+ 'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext),
];
$this->items[] = $item;
}
}
+ private function getUploadTimeFromString($string)
+ {
+ if (strpos($string, 'dnes') !== false) {
+ return strtotime('today');
+ } elseif (strpos($string, 'včera') !== false) {
+ return strtotime('yesterday');
+ } elseif (!preg_match('/(\d+).\s(\d+).(\s(\d+))?/', $string, $match)) {
+ returnServerError('Could not get date from Česká televize string');
+ }
+
+ $date = sprintf('%04d-%02d-%02d', $match[3] ?? date('Y'), $match[2], $match[1]);
+ return strtotime($date);
+ }
+
+ private function fixChars($text)
+ {
+ return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
+ }
+
public function getURI()
{
return $this->feedUri ?? parent::getURI();
diff --git a/bridges/CrewbayBridge.php b/bridges/CrewbayBridge.php
index 0ca017c2..0fb9def6 100644
--- a/bridges/CrewbayBridge.php
+++ b/bridges/CrewbayBridge.php
@@ -109,7 +109,7 @@ class CrewbayBridge extends BridgeAbstract
public function collectData()
{
$url = $this->getURI();
- $html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.');
+ $html = getSimpleHTMLDOM($url);
$annonces = $html->find('#SearchResults div.result');
$limit = 0;
diff --git a/bridges/CubariProxyBridge.php b/bridges/CubariProxyBridge.php
index 492d5fbb..8afd2275 100644
--- a/bridges/CubariProxyBridge.php
+++ b/bridges/CubariProxyBridge.php
@@ -121,4 +121,9 @@ class CubariProxyBridge extends BridgeAbstract
}
return $uri;
}
+
+ public function getIcon()
+ {
+ return parent::getURI() . '/static/favicon.png';
+ }
}
diff --git a/bridges/DRKBlutspendeBridge.php b/bridges/DRKBlutspendeBridge.php
new file mode 100644
index 00000000..15075898
--- /dev/null
+++ b/bridges/DRKBlutspendeBridge.php
@@ -0,0 +1,107 @@
+<?php
+
+class DRKBlutspendeBridge extends FeedExpander
+{
+ const MAINTAINER = 'User123698745';
+ const NAME = 'DRK-Blutspende';
+ const BASE_URI = 'https://www.drk-blutspende.de';
+ const URI = self::BASE_URI;
+ const CACHE_TIMEOUT = 60 * 60 * 1; // 1 hour
+ const DESCRIPTION = 'German Red Cross (Deutsches Rotes Kreuz) blood donation service feed with more details';
+ const CONTEXT_APPOINTMENTS = 'Termine';
+ const PARAMETERS = [
+ self::CONTEXT_APPOINTMENTS => [
+ 'term' => [
+ 'name' => 'PLZ / Ort',
+ 'required' => true,
+ 'exampleValue' => '12555',
+ ],
+ 'radius' => [
+ 'name' => 'Umkreis in km',
+ 'type' => 'number',
+ 'exampleValue' => 10,
+ ],
+ 'limit_days' => [
+ 'name' => 'Limit von Tagen',
+ 'title' => 'Nur Termine innerhalb der nächsten x Tagen',
+ 'type' => 'number',
+ 'exampleValue' => 28,
+ ],
+ 'limit_items' => [
+ 'name' => 'Limit von Terminen',
+ 'title' => 'Nicht mehr als x Termine',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 20,
+ ]
+ ]
+ ];
+
+ public function collectData()
+ {
+ $limitItems = intval($this->getInput('limit_items'));
+ $this->collectExpandableDatas(self::buildAppointmentsURI(), $limitItems);
+ }
+
+ protected function parseItem(array $item)
+ {
+ $html = getSimpleHTMLDOM($item['uri']);
+
+ $detailsElement = $html->find('.details', 0);
+
+ $dateElement = $detailsElement->find('.datum', 0);
+ $dateLines = self::explodeLines($dateElement->plaintext);
+
+ $addressElement = $detailsElement->find('.adresse', 0);
+ $addressLines = self::explodeLines($addressElement->plaintext);
+
+ $infoElement = $detailsElement->find('.angebote > h4 + p', 0);
+ $info = $infoElement ? $infoElement->innertext : '';
+
+ $imageElements = $detailsElement->find('.fotos img');
+
+ $item['title'] = $dateLines[0] . ' ' . $dateLines[1] . ' ' . $addressLines[0] . ' - ' . $addressLines[1];
+
+ $item['content'] = <<<HTML
+ <p><b>{$dateLines[0]} {$dateLines[1]}</b></p>
+ <p>{$addressElement->innertext}</p>
+ <p>{$info}</p>
+ HTML;
+
+ foreach ($imageElements as $imageElement) {
+ $src = $imageElement->getAttribute('src');
+ $item['content'] .= <<<HTML
+ <p><img src="{$src}"></p>
+ HTML;
+ }
+
+ $item['description'] = null;
+
+ return $item;
+ }
+
+ public function getURI()
+ {
+ if ($this->queriedContext === self::CONTEXT_APPOINTMENTS) {
+ return str_replace('.rss?', '?', self::buildAppointmentsURI());
+ }
+ return parent::getURI();
+ }
+
+ private function buildAppointmentsURI()
+ {
+ $term = $this->getInput('term') ?? '';
+ $radius = $this->getInput('radius') ?? '';
+ $limitDays = intval($this->getInput('limit_days'));
+ $dateTo = $limitDays > 0 ? date('Y-m-d', time() + (60 * 60 * 24 * $limitDays)) : '';
+ return self::BASE_URI . '/blutspendetermine/termine.rss?date_to=' . $dateTo . '&radius=' . $radius . '&term=' . $term;
+ }
+
+ /**
+ * Returns an array of strings, each of which is a substring of string formed by splitting it on boundaries formed by line breaks.
+ */
+ private function explodeLines(string $text): array
+ {
+ return array_map('trim', preg_split('/(\s*(\r\n|\n|\r)\s*)+/', $text));
+ }
+}
diff --git a/bridges/DacksnackBridge.php b/bridges/DacksnackBridge.php
index 7aab48d1..a031706e 100644
--- a/bridges/DacksnackBridge.php
+++ b/bridges/DacksnackBridge.php
@@ -53,8 +53,7 @@ class DacksnackBridge extends BridgeAbstract
public function collectData()
{
$NEWSURL = self::URI;
- $html = getSimpleHTMLDOMCached($NEWSURL, 18000) or
- returnServerError('Could not request: ' . $NEWSURL);
+ $html = getSimpleHTMLDOMCached($NEWSURL, 18000);
foreach ($html->find('a.main-news-item') as $element) {
// Debug::log($element);
@@ -64,8 +63,7 @@ class DacksnackBridge extends BridgeAbstract
$url = self::URI . $element->getAttribute('href');
$published = $this->parseSwedishDates(trim($element->find('.published', 0)->plaintext));
- $article_html = getSimpleHTMLDOMCached($url, 18000) or
- returnServerError('Could not request: ' . $url);
+ $article_html = getSimpleHTMLDOMCached($url, 18000);
$article_content = $article_html->find('#ctl00_ContentPlaceHolder1_NewsArticleVeiw_pnlArticle', 0);
$figure = self::URI . $article_content->find('img.news-image', 0)->getAttribute('src');
diff --git a/bridges/DagensNyheterDirektBridge.php b/bridges/DagensNyheterDirektBridge.php
index f0748b76..fa219a09 100644
--- a/bridges/DagensNyheterDirektBridge.php
+++ b/bridges/DagensNyheterDirektBridge.php
@@ -18,8 +18,7 @@ class DagensNyheterDirektBridge extends BridgeAbstract
{
$NEWSURL = self::BASEURL . '/ajax/direkt/';
- $html = getSimpleHTMLDOM($NEWSURL) or
- returnServerError('Could not request: ' . $NEWSURL);
+ $html = getSimpleHTMLDOM($NEWSURL);
foreach ($html->find('article') as $element) {
$link = $element->find('button', 0)->getAttribute('data-link');
diff --git a/bridges/DansTonChatBridge.php b/bridges/DansTonChatBridge.php
index 9712ec9d..a7765f19 100644
--- a/bridges/DansTonChatBridge.php
+++ b/bridges/DansTonChatBridge.php
@@ -10,9 +10,11 @@ class DansTonChatBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM(self::URI . 'latest.html');
+ $url = self::URI . 'latest.html';
+ $dom = getSimpleHTMLDOM($url);
- foreach ($html->find('div.item') as $element) {
+ $items = $dom->find('div.item');
+ foreach ($items as $element) {
$item = [];
$item['uri'] = $element->find('a', 0)->href;
$titleContent = $element->find('h3 a', 0);
diff --git a/bridges/DealabsBridge.php b/bridges/DealabsBridge.php
index 3ee1c6f5..7bad79b5 100644
--- a/bridges/DealabsBridge.php
+++ b/bridges/DealabsBridge.php
@@ -40,1837 +40,24 @@ class DealabsBridge extends PepperBridgeAbstract
'Deals par groupe' => [
'group' => [
'name' => 'Groupe',
- 'type' => 'list',
- 'title' => 'Groupe dont il faut afficher les deals',
- 'values' => [
- 'Abattants WC' => 'abattants-wc',
- 'Abonnement PlayStation Plus' => 'playstation-plus',
- 'Abonnements cinéma' => 'abonnements-cinema',
- 'Abonnements de train' => 'abonnements-de-train',
- 'Abonnements internet' => 'abonnements-internet',
- 'Abonnements presse' => 'abonnements-presse',
- 'Accessoires aquarium' => 'accessoires-aquarium',
- 'Accessoires auto' => 'auto',
- 'Accessoires électroniques' => 'accessoires-gadgets',
- 'Accessoires gamers PC' => 'accessoires-gamers-pc',
- 'Accessoires gaming' => 'accessoires-gaming',
- 'Accessoires iPhone' => 'accessoires-iphone',
- 'Accessoires mode' => 'accessoires-mode',
- 'Accessoires moto' => 'moto',
- 'Accessoires Nintendo' => 'accessoires-nintendo',
- 'Accessoires PC portables' => 'accessoires-pc-portables',
- 'Accessoires photo' => 'accessoires-photo',
- 'Accessoires PlayStation' => 'accessoires-playstation',
- 'Accessoires pour barbecue' => 'accessoires-barbecue',
- 'Accessoires studio photo' => 'accessoires-studio-photo',
- 'Accessoires téléphonie' => 'accessoires-telephonie',
- 'Accessoires TV' => 'accessoires-tv',
- 'Accessoires vélo' => 'accessoires-velo',
- 'Accessoires Xbox' => 'accessoires-xbox',
- 'Acer' => 'acer',
- 'Acer Predator' => 'acer-predator',
- 'Achats / Ventes' => 'achats-ventes-echanges-estimations-dons',
- 'Achats à l&#039;étranger' => 'limport-sites-avis-questions-langues',
- 'Adaptateurs' => 'adaptateurs',
- 'Adhérents Fnac' => 'adherents-fnac',
- 'Adhésions &amp; Souscriptions' => 'adhesions-souscriptions-abonnements',
- 'adidas' => 'adidas',
- 'Adidas Gazelle' => 'adidas-gazelle',
- 'adidas Stan Smith' => 'adidas-stan-smith',
- 'adidas Superstar' => 'adidas-superstar',
- 'adidas Ultraboost' => 'adidas-ultraboost',
- 'adidas Yung-1' => 'adidas-yung-1',
- 'adidas ZX Flux' => 'adidas-zx-flux',
- 'Adoucissant' => 'adoucissant',
- 'Agendas' => 'agendas',
- 'Age of Empires' => 'age-of-empires',
- 'Age of Empires: Definitive Edition' => 'age-of-empires-definitive-edition',
- 'Alarmes' => 'alarmes',
- 'Albums photo' => 'albums-photo',
- 'Alcools' => 'alcools',
- 'Alcools forts' => 'alcools-forts',
- 'Alimentation' => 'epicerie',
- 'Alimentation bébés' => 'alimentation-bebes',
- 'Alimentation PC' => 'alimentation-pc',
- 'Alimentation sportifs' => 'alimentation-sportifs',
- 'Amazfit Bip' => 'xiaomi-amazfit-bip',
- 'Amazon Echo' => 'amazon-echo',
- 'Amazon Echo Dot' => 'amazon-echo-dot',
- 'Amazon Echo Plus' => 'amazon-echo-plus',
- 'Amazon Echo Show' => 'amazon-echo-show',
- 'Amazon Echo Show 5' => 'amazon-echo-show-5',
- 'Amazon Echo Spot' => 'amazon-echo-spot',
- 'Amazon Fire TV' => 'amazon-fire-tv',
- 'Amazon Kindle' => 'amazon-kindle',
- 'Amazon Prime' => 'amazon-prime',
- 'AMD Radeon' => 'amd-radeon',
- 'AMD Ryzen' => 'amd-ryzen',
- 'AMD Ryzen 5 5600X' => 'amd-ryzen-5-5600x',
- 'AMD Ryzen 7 5800X' => 'amd-ryzen-7-5800x',
- 'AMD Ryzen 9 5900X' => 'amd-ryzen-9-5900x',
- 'AMD Ryzen 9 5950X' => 'amd-ryzen-9-5950x',
- 'AMD Vega' => 'amd-vega',
- 'amiibo' => 'amiibo',
- 'Amplis (guitare/basse)' => 'amplis-guitare-basse',
- 'Amplis audio' => 'amplis',
- 'Ampoules' => 'ampoules',
- 'Ampoules à LED' => 'ampoules-a-led',
- 'Angleterre' => 'angleterre',
- 'Animal Crossing' => 'animal-crossing',
- 'Animal Crossing: New Horizons' => 'animal-crossing-new-horizons',
- 'Animaux' => 'animaux',
- 'Anker' => 'anker',
- 'Anno 1800' => 'anno-1800',
- 'Annonces officielles' => 'annonces-officielles',
- 'Anthem' => 'anthem',
- 'Anti-nuisibles' => 'anti-nuisibles',
- 'Anti-puces' => 'anti-puces',
- 'Antivirus' => 'antivirus',
- 'Antivols' => 'antivols',
- 'Apex Legends' => 'apex-legends',
- 'Appareils à raclette' => 'appareils-raclette',
- 'Appareils de musculation' => 'appareils-de-musculation',
- 'Appareils photo' => 'appareils-photo',
- 'Appareils photo Canon' => 'appareils-photo-canon',
- 'Appareils photo compacts' => 'appareils-photo-compacts',
- 'Appareils photo instantanés' => 'appareils-photo-instantanes',
- 'Appareils photo Nikon' => 'appareils-photo-nikon',
- 'Appareils photo Olympus' => 'appareils-photo-olympus',
- 'Appareils photo Panasonic' => 'appareils-photo-panasonic',
- 'Appareils photo Sony' => 'appareils-photo-sony',
- 'Apple' => 'apple',
- 'Apple AirPods' => 'apple-airpods',
- 'Apple AirPods 2' => 'apple-airpods-2',
- 'Apple AirPods Max' => 'apple-airpods-max',
- 'Apple AirPods Pro' => 'apple-airpods-pro',
- 'Apple HomePod' => 'apple-homepod',
- 'Apple HomePod Mini' => 'apple-homepod-mini',
- 'Apple TV' => 'apple-tv',
- 'Apple TV+' => 'apple-tv-plus',
- 'Apple Watch' => 'apple-watch',
- 'Apple Watch 3' => 'apple-watch-3',
- 'Apple Watch 4' => 'apple-watch-4',
- 'Apple Watch 5' => 'apple-watch-5',
- 'Apple Watch 6' => 'apple-watch-6',
- 'Apple Watch SE' => 'apple-watch-se',
- 'Applications' => 'applications',
- 'Applications Android' => 'applications-android',
- 'Applications iOS' => 'applications-ios',
- 'Appliques murales' => 'appliques-murales',
- 'Applis &amp; logiciels' => 'applis-logiciels',
- 'Après-shampooings' => 'apres-shampooings',
- 'Aquariums' => 'aquariums',
- 'Arbres à chat' => 'arbres-a-chat',
- 'Arduino' => 'arduino',
- 'Armoires &amp; placards' => 'armoires-et-placards',
- 'Articles de cuisine et d&#039;entretien' => 'articles-de-cuisine',
- 'Arts culinaires' => 'arts-culinaires',
- 'Arts de la table' => 'arts-de-la-table',
- 'ASICS' => 'asics',
- 'Asmodée' => 'asmodee',
- 'Aspirateurs' => 'aspirateurs',
- 'Aspirateurs balais' => 'aspirateurs-balais',
- 'Aspirateurs Dreame' => 'aspirateurs-xiaomi',
- 'Aspirateurs Dyson' => 'aspirateurs-dyson',
- 'Aspirateurs robot' => 'aspirateurs-robot',
- 'Aspirateurs Rowenta' => 'apsirateurs-rowenta',
- 'Aspirateurs sans sac' => 'aspirateurs-sans-sac',
- 'Assassin&#039;s Creed' => 'assassin-s-creed',
- 'Assassin&#039;s Creed: Unity' => 'assassins-creed-unity',
- 'Assassin&#039;s Creed: Valhalla' => 'assassin-s-creed-valhalla',
- 'Assassin&#039;s Creed Odyssey' => 'assassin-s-creed-odyssey',
- 'Assassin&#039;s Creed Origins' => 'assassin-s-creed-origins',
- 'Assurances' => 'assurances',
- 'Astuces pour économiser' => 'vos-astuces-pour-faire-des-economies',
- 'Asus' => 'asus',
- 'Asus ROG' => 'asus-rog',
- 'Asus ROG Phone' => 'asus-rog-phone',
- 'Asus ROG Phone 2' => 'asus-rog-phone-2',
- 'ASUS Transformer' => 'asus-transformer',
- 'Asus VivoBook' => 'asus-vivobook',
- 'Asus ZenBook' => 'asus-zenbook',
- 'Asus ZenFone 2' => 'asus-zenfone-2',
- 'Asus ZenFone 3' => 'asus-zenfone-3',
- 'Asus ZenFone 4' => 'asus-zenfone-4',
- 'Asus ZenFone 6' => 'asus-zenfone-6',
- 'Asus ZenFone GO' => 'asus-zenfone-go',
- 'Asus ZenFone Zoom' => 'asus-zenfone-zoom',
- 'Audio &amp; Hi-fi' => 'audio-et-hi-fi',
- 'Aukey' => 'aukey',
- 'Auto-Moto' => 'auto-moto',
- 'Autoradios' => 'autoradios',
- 'Azzaro Wanted' => 'azzaro-wanted',
- 'Baby foot' => 'baby-foot',
- 'BabyLiss' => 'babyliss',
- 'Babyphones' => 'babyphones',
- 'Badminton' => 'badminton',
- 'Bagagerie' => 'bagagerie',
- 'Baignoires pour bébé' => 'baignoires-pour-bebe',
- 'Bains de bouche' => 'bains-de-bouche',
- 'Balais &amp; serpillères' => 'balais-et-serpilleres',
- 'Balances connectées' => 'balances-connectees',
- 'Balançoires' => 'balancoires',
- 'Ballet &amp; danse' => 'ballet-et-danse',
- 'Ballons de football' => 'ballons-de-football',
- 'Bandes dessinées' => 'bandes-dessinees',
- 'Banques' => 'banques',
- 'Barbecue' => 'barbecue',
- 'Barbecue électrique' => 'barbecue-electrique',
- 'Barbecue Weber' => 'barbecue-weber',
- 'Barbie' => 'barbie',
- 'Barres de son' => 'barres-de-son',
- 'Barres de son Yamaha' => 'barres-de-son-yamaha',
- 'Batman Arkham' => 'batman-arkham',
- 'Batteries externes' => 'batteries-externes',
- 'Batteries voiture' => 'batteries-voiture',
- 'Batteurs' => 'batteurs-electriques',
- 'Battlefield' => 'battlefield',
- 'Battlefield 1' => 'battlefield-1',
- 'Battlefield V' => 'battlefield-5',
- 'Béaba' => 'beaba',
- 'Beats by Dre' => 'beats-by-dre',
- 'Beats Solo 3' => 'beats-solo-3',
- 'Beats Studio 3' => 'beats-studio-3',
- 'Beauté' => 'beaute',
- 'Bébés' => 'bebes-nouveaux-nes',
- 'BenQ' => 'benq',
- 'Be quiet!' => 'be-quiet',
- 'Beyerdynamic MMX 300' => 'beyerdynamic-mmx-300',
- 'Biberons' => 'biberons',
- 'Bien-être &amp; santé' => 'bien-etre-et-massages',
- 'Bières' => 'bieres',
- 'Bijoux' => 'bijoux',
- 'Bikinis' => 'bikinis',
- 'Bilans de santé &amp; dépistages' => 'bilans-de-sante-et-depistages',
- 'Billets de bus' => 'billets-de-bus',
- 'Billets de train' => 'billets-de-train',
- 'BioShock' => 'bioshock',
- 'BioShock Infinite' => 'bioshock-infinite',
- 'Bitdefender' => 'bitdefender',
- 'Blabla' => 'blabla-parlez-de-tout-et-de-rien',
- 'Black &amp; Decker' => 'black-decker',
- 'Blackberry' => 'blackberry',
- 'Black Desert Online' => 'black-desert-online',
- 'Blédina' => 'bledina',
- 'Blenders' => 'blenders',
- 'Bleu de Chanel' => 'bleu-de-chanel',
- 'Blousons de moto' => 'blousons-de-moto',
- 'Blu-Ray' => 'blu-ray',
- 'Bodys pour bébé' => 'bodys-pour-bebe',
- 'Boissons' => 'boissons',
- 'Boîtes à outils' => 'boites-a-outils',
- 'Boîtiers PC' => 'boitiers-pc',
- 'Boîtiers TV' => 'boitiers-tv',
- 'Bonbons' => 'bonbons',
- 'Bonnets' => 'bonnets',
- 'Bonnets de bain' => 'bonnets-de-bain',
- 'Borderlands' => 'borderlands',
- 'Borderlands 3' => 'borderlands-3',
- 'Bosch' => 'bosch',
- 'Bose' => 'bose',
- 'Bose Headphones 700' => 'bose-headphones-700',
- 'Bose Home Speaker 500' => 'bose-home-speaker-500',
- 'Bose QuietComfort' => 'bose-quietcomfort',
- 'Bose QuietComfort 35 II' => 'bose-quietcomfort-35ii',
- 'Bose SoundLink' => 'bose-soundlink',
- 'Bose SoundTouch' => 'bose-soundtouch',
- 'Bottes' => 'bottes',
- 'Bottes de moto' => 'bottes-de-moto',
- 'Bottes de neige' => 'bottes-neige',
- 'Bottes de pluie' => 'bottes-pluie',
- 'Bottes femme' => 'bottes-femme',
- 'Bottes homme' => 'bottes-homme',
- 'Bougies &amp; bougeoirs' => 'bougies-et-bougeoirs',
- 'Box beauté' => 'box-beaute',
- 'Bracelet fitness' => 'bracelet-fitness',
- 'Brandt' => 'brandt',
- 'Braun Series 3' => 'braun-series-3',
- 'Braun Series 5' => 'braun-series-5',
- 'Braun Series 7' => 'braun-series-7',
- 'Braun Series 9' => 'braun-series-9',
- 'Braun Silk Épil' => 'braun-silk-epil',
- 'Brita' => 'brita',
- 'Brosses à dents' => 'brosses-a-dents',
- 'Brosses à dents électriques' => 'brosses-a-dents-electriques',
- 'Brosses à dents électriques Oral-B' => 'brosses-a-dents-electriques-oral-b',
- 'Brosses pour animaux' => 'brosses-pour-animaux',
- 'Cable management' => 'cable-management',
- 'Câbles' => 'cables',
- 'Câbles Ethernet' => 'cables-ethernet',
- 'Câbles HDMI' => 'cables-hdmi',
- 'Câbles Jack' => 'cables-jack',
- 'Câbles USB' => 'cables-usb',
- 'Cadeaux' => 'cadeaux',
- 'Cadres' => 'cadres',
- 'Cadres de vélo' => 'cadres-de-velo',
- 'Café' => 'cafe',
- 'Café en dosettes' => 'cafe-en-dosettes',
- 'Café en grain' => 'cafe-en-grain',
- 'Cafetières' => 'cafetieres',
- 'Cafetières expresso' => 'cafetieres-expresso',
- 'Cafetières filtre' => 'cafetieres-filtre',
- 'Cafetières italiennes' => 'cafetieres-italiennes',
- 'Cahiers' => 'cahiers',
- 'Caissons de basses' => 'caissons-de-basses',
- 'Calendrier de l&#039;Avent Lego' => 'calendriers-avent-lego',
- 'Calendriers' => 'calendriers',
- 'Calendriers de l&#039;Avent' => 'calendriers-avent',
- 'Call of Duty' => 'call-of-duty',
- 'Call of Duty: Black Ops Cold War' => 'call-of-duty-black-ops-cold-war',
- 'Call of Duty: Black Ops III' => 'call-of-duty-black-ops-3',
- 'Call of Duty: Black Ops IIII' => 'call-of-duty-black-ops-4',
- 'Call of Duty: Infinite Warfare' => 'call-of-duty-infinite-warfare',
- 'Call of Duty: Modern Warfare' => 'call-of-duty-modern-warfare',
- 'Call of Duty: WW2' => 'call-of-duty-ww2',
- 'Calor' => 'calor',
- 'Caméras' => 'cameras',
- 'Caméras IP' => 'cameras-ip',
- 'Caméras sportives' => 'cameras-sportives',
- 'Camping' => 'camping',
- 'Canapés' => 'canape',
- 'Canon' => 'canon',
- 'Captain Toad: Treasure Tracker' => 'captain-toad-treasure-tracker',
- 'Caravanes' => 'caravanes',
- 'Carburant' => 'carburant',
- 'Cartables' => 'cartables',
- 'Cartes &amp; programmes de fidélité' => 'cartes-et-programmes-de-fidelite',
- 'Cartes bancaires' => 'cartes-bancaires',
- 'Cartes de développement' => 'cartes-developpement',
- 'Cartes graphiques' => 'cartes-graphiques',
- 'Cartes mémoire' => 'cartes-memoire',
- 'Cartes mères' => 'cartes-meres',
- 'Cartes postales' => 'cartes-postales',
- 'Cartes prépayées Playstation Store' => 'playstation-store',
- 'Cartes SD' => 'cartes-sd',
- 'Cartes son' => 'cartes-son',
- 'Casio' => 'casio',
- 'Casque sans fil Xbox' => 'casque-sans-fil-xbox',
- 'Casques Apple' => 'casques-apple',
- 'Casques à réduction de bruit' => 'casque-reduction-active-bruit',
- 'Casques audio' => 'casques-audio',
- 'Casques Bose' => 'casques-bose',
- 'Casques de moto' => 'casques-de-moto',
- 'Casques de vélo' => 'casques-de-velo',
- 'Casques Jabra' => 'casques-jabra',
- 'Casques Samsung' => 'casques-samsung',
- 'Casques sans fil' => 'casques-sans-fil',
- 'Casques Sennheiser' => 'casques-sennheiser',
- 'Casques Sony' => 'casques-sony',
- 'Casques VR' => 'vr',
- 'Casquettes' => 'casquettes',
- 'Casseroles' => 'casseroles',
- 'Catit' => 'catit',
- 'Caves à vin' => 'caves-a-vin',
- 'CD &amp; vinyles' => 'cd-vinyles',
- 'CDAV' => 'cdav',
- 'Ceintures' => 'ceintures',
- 'Centrales vapeur' => 'centrales-vapeur',
- 'Chaînes hi-fi' => 'chaines-hi-fi',
- 'Chaises' => 'chaises',
- 'Chaises hautes' => 'chaises-hautes',
- 'Chambre' => 'chambre',
- 'Champagne' => 'champagne',
- 'Chapeaux' => 'chapeaux',
- 'Chapeaux &amp; casquettes' => 'chapeaux-casquettes',
- 'Chargeurs' => 'chargeurs',
- 'Chargeurs allume-cigare' => 'chargeurs-allume-cigare',
- 'Chargeurs de piles' => 'chargeurs-de-piles',
- 'Chargeurs sans fil' => 'chargeurs-sans-fil',
- 'Chasse' => 'chasse',
- 'Chatières' => 'chatieres',
- 'Chats' => 'chats',
- 'Chauffage' => 'chauffage',
- 'Chaussettes &amp; collants' => 'chaussettes-et-collants',
- 'Chaussons' => 'chaussons',
- 'Chaussures' => 'chaussures',
- 'Chaussures adidas' => 'chaussures-adidas',
- 'Chaussures de football' => 'chaussures-de-football',
- 'Chaussures de randonnée' => 'chaussures-de-randonnee',
- 'Chaussures de ski' => 'chaussures-de-ski',
- 'Chaussures de ville' => 'chaussures-de-ville',
- 'Chaussures New Balance' => 'chaussures-new-balance',
- 'Chaussures Nike' => 'chaussures-nike',
- 'Chaussures pour enfants' => 'chaussures-enfants',
- 'Chaussures pour femme' => 'chaussures-femme',
- 'Chaussures pour homme' => 'chaussures-homme',
- 'Chaussures Puma' => 'chaussures-puma',
- 'Chaussures Reebok' => 'chaussures-reebok',
- 'Chaussures running' => 'chaussures-de-running',
- 'Chelsea boots' => 'chelsea-boots',
- 'Chemises' => 'chemises',
- 'Chiens' => 'chiens',
- 'Chocolat' => 'chocolat',
- 'Chuck Taylor' => 'chuck-taylor',
- 'Cinéma' => 'cinema',
- 'Cire dépilatoire' => 'cire-depilatoire',
- 'Cirque &amp; arts de rue' => 'cirque-et-arts-de-rue',
- 'Citytrips' => 'citytrips',
- 'Civilization' => 'civilization',
- 'Civilization VI' => 'civilization-vi',
- 'CK One' => 'ck-one',
- 'Clarks' => 'clarks',
- 'Claviers' => 'claviers',
- 'Claviers (musique)' => 'claviers-musique',
- 'Claviers gamer' => 'claviers-gamer',
- 'Claviers Logitech' => 'claviers-logitech',
- 'Claviers mécaniques' => 'claviers-mecaniques',
- 'Claviers sans fil' => 'claviers-sans-fil',
- 'Clés USB' => 'cles-usb',
- 'Climatisation' => 'climatisation',
- 'Climatiseurs' => 'climatiseurs',
- 'Cocottes' => 'cocottes',
- 'Coffrets de livres' => 'coffrets-de-livres',
- 'Coffrets DVD' => 'coffrets-dvd',
- 'Coffrets maquillage' => 'coffrets-maquillage',
- 'Colliers &amp; laisses' => 'colliers-et-laisses',
- 'Compléments alimentaires' => 'complements-alimentaires',
- 'Composteurs' => 'composteurs',
- 'Concerts' => 'concerts',
- 'Concours' => 'concours',
- 'Congélateurs' => 'congelateurs',
- 'Connectiques' => 'connectiques',
- 'Console Google Stadia' => 'google-stadia',
- 'Console Nintendo Classic Mini' => 'nintendo-classic-mini',
- 'Console Nintendo Classic Mini: SNES' => 'nintendo-classic-mini-snes',
- 'Console Nintendo Switch' => 'nintendo-switch',
- 'Console Nintendo Switch Lite' => 'nintendo-switch-lite',
- 'Console PS4' => 'playstation-4',
- 'Console PS4 Pro' => 'playstation-4-pro',
- 'Console PS5' => 'playstation-5',
- 'Consoles' => 'consoles',
- 'Consoles &amp; jeux vidéo' => 'consoles-jeux-video',
- 'Console Sega Mega Drive Mini' => 'sega-mega-drive-mini',
- 'Console Xbox One S' => 'xbox-one-s',
- 'Console Xbox One X' => 'xbox-one-x',
- 'Console Xbox Series S' => 'xbox-series-s',
- 'Console Xbox Series X' => 'xbox-series-x',
- 'Consommables imprimantes' => 'consommables-imprimantes',
- 'Converse' => 'converse',
- 'Coques iPhone' => 'coques-iphone',
- 'Corsair Void PRO' => 'corsair-void-pro',
- 'Costumes' => 'costumes',
- 'Costumes &amp; déguisements' => 'costumes-et-deguisements',
- 'Couches' => 'couches',
- 'Couettes' => 'couettes',
- 'Coupes menstruelles' => 'coupes-menstruelles',
- 'Cours &amp; formations' => 'cours-et-formations',
- 'Courses hippiques' => 'courses-hippiques',
- 'Couteaux de cuisine' => 'couteaux-de-cuisine',
- 'Couture' => 'couture',
- 'Couverts' => 'couverts',
- 'Couverts pour bébés' => 'couverts-pour-bebes',
- 'Covoiturage' => 'covoiturage',
- 'Crash Team Racing Nitro-Fueled' => 'crash-team-racing-nitro-fueled',
- 'Cravates' => 'cravates',
- 'Crédits' => 'credits',
- 'Crèmes hydratantes' => 'cremes-hydratantes',
- 'Crèmes solaires' => 'cremes-solaires',
- 'Croisières' => 'croisieres',
- 'Croquettes pour chat' => 'croquettes-pour-chat',
- 'Croquettes pour chien' => 'croquettes-pour-chien',
- 'Cuiseurs à riz' => 'cuiseur-riz',
- 'Cuisinières' => 'cuisinieres',
- 'Culottes menstruelles' => 'culottes-menstruelles',
- 'Culture &amp; divertissement' => 'culture-divertissement',
- 'Cyberpunk 2077' => 'cyberpunk-2077',
- 'Cyclisme' => 'cyclisme',
- 'Cyclisme &amp; sports urbains' => 'cyclisme-sports-urbains',
- 'Darksiders' => 'darksiders',
- 'Dashcams' => 'dashcams',
- 'DDR3' => 'ddr3',
- 'DDR4' => 'ddr4',
- 'Dead Rising' => 'dead-rising',
- 'Death Stranding' => 'death-stranding',
- 'Décoration' => 'decoration',
- 'Décorations de Noël' => 'decoration-noel',
- 'Deebot' => 'ecovacs-deebot',
- 'Deezer' => 'deezer',
- 'Dell' => 'dell',
- 'Dell XPS' => 'dell-xps',
- 'Delsey' => 'delsey',
- 'Demandes de deals' => 'les-demandes-de-deals',
- 'Denon' => 'denon',
- 'Dentifrices' => 'dentifrices',
- 'Déodorants' => 'deodorants',
- 'Désherbants' => 'desherbants',
- 'Déshumidificateurs' => 'deshumidificateurs',
- 'Désinfectant' => 'desinfectants',
- 'Désodorisants &amp; parfums d&#039;intérieur' => 'desodorisants-et-parfums-d-interieur',
- 'Destiny' => 'destiny',
- 'Destiny 2' => 'destiny-2',
- 'Détecteurs de fumée' => 'detecteurs-de-fumee',
- 'Detroit: Become Human' => 'detroit-become-human',
- 'Deus Ex' => 'deus-ex',
- 'Deus Ex: Mankind Divided' => 'deus-ex-mankind-divided',
- 'Devil May Cry 5' => 'devil-may-cry-5',
- 'Dishonored' => 'dishonored',
- 'Dishonored 2' => 'dishonored-2',
- 'Disney+' => 'disney-plus',
- 'Disneyland Paris' => 'disneyland-paris',
- 'Disques durs (internes)' => 'hdd',
- 'Disques durs externes' => 'disques-durs-externes',
- 'Divers' => 'divers',
- 'DJI' => 'dji',
- 'DJI Mavic Air 2' => 'dji-mavic-air-2',
- 'DJI Mavic Mini' => 'dji-mavic-mini',
- 'Dolce Gusto' => 'dolce-gusto',
- 'Domotique' => 'smart-home',
- 'Doom Eternal' => 'doom-eternal',
- 'Dosettes Dolce Gusto' => 'dosettes-dolce-guste',
- 'Dosettes Nespresso' => 'dosettes-nespresso',
- 'Dosettes Senseo' => 'dosettes-senseo',
- 'Dosettes Tassimo' => 'dosettes-tassimo',
- 'Dr. Martens' => 'dr-martens',
- 'Dragon Age' => 'dragon-age',
- 'Dragon Ball' => 'dragon-ball',
- 'Dragon Ball FighterZ' => 'dragon-ball-fighterz',
- 'Dragon Ball Z: Kakarot' => 'dragon-ball-z-kakarot',
- 'Dragon Quest' => 'dragon-quest',
- 'Dragon Quest Builders' => 'dragon-quest-builders',
- 'Dragon Quest Builders 2' => 'dragon-quest-builders-2',
- 'Draisiennes' => 'draisiennes',
- 'Draps &amp; housses' => 'draps-et-housses',
- 'Dreame V10' => 'xiaomi-dreame-v10',
- 'Dreame V11' => 'xiaomi-dreame-v11',
- 'Drones' => 'drones',
- 'Durex' => 'durex',
- 'DVD' => 'dvd',
- 'Dying Light' => 'dying-light',
- 'Dying Light 2' => 'dying-light-2',
- 'Dyson' => 'dyson',
- 'Dyson V10' => 'dyson-v10',
- 'Dyson V11' => 'dyson-v11',
- 'Eastpak' => 'eastpak',
- 'Ebooks' => 'ebooks',
- 'Écharpes &amp; foulards' => 'echarpes-et-foulards',
- 'Éclairage intelligent' => 'smart-light',
- 'Écouteurs' => 'ecouteurs',
- 'Écouteurs sans fil' => 'ecouteurs-sans-fil',
- 'Écouteurs sport' => 'ecouteurs-sport',
- 'Ecovacs' => 'ecovacs',
- 'Ecovacs Deebot 900' => 'ecovacs-deebot-900',
- 'Ecovacs Deebot OZMO 930' => 'ecovacs-deebot-ozmo-930',
- 'Écrans' => 'ecrans',
- 'Écrans 4K / UHD' => 'ecrans-4k-uhd',
- 'Écrans 21&quot; et moins' => 'ecrans-21-pouces-et-moins',
- 'Écrans 24&quot;' => 'ecrans-24-pouces',
- 'Écrans 27&quot;' => 'ecrans-27-pouces',
- 'Écrans 29&quot; et plus' => 'ecrans-29-pouces-et-plus',
- 'Écrans Acer' => 'ecrans-acer',
- 'Écrans Asus' => 'ecrans-asus',
- 'Écrans BenQ' => 'ecrans-benq',
- 'Écrans Dell' => 'ecrans-dell',
- 'Écrans de projection' => 'ecrans-de-projection',
- 'Écrans FreeSync' => 'ecrans-freesync',
- 'Écrans gaming' => 'ecrans-gamer',
- 'Écrans incurvés' => 'ecrans-incurves',
- 'Écrans Philips' => 'ecrans-philips',
- 'Écrans Samsung' => 'ecrans-samsung',
- 'Électricité (matériel)' => 'electricite',
- 'Electrolux' => 'electrolux',
- 'Électroménager' => 'electromenager',
- 'Embauchoirs' => 'embauchoirs',
- 'Enceintes' => 'enceintes',
- 'Enceintes Bluetooth' => 'enceintes-bluetooth',
- 'Enceintes connectées' => 'enceintes-connectees',
- 'Enceintes portables sans fil' => 'enceintes-portables-sans-fil',
- 'Énergie' => 'energie',
- 'Engrais' => 'engrais',
- 'Épicerie &amp; courses' => 'epicerie-courses-supermarches',
- 'Épilateurs à lumière pulsée' => 'epilateurs-a-lumiere-pulsee',
- 'Épilateurs électriques' => 'epilateurs-electriques',
- 'Épilation' => 'epilation',
- 'Équipement motard' => 'equipement-motard',
- 'Équipement running' => 'equipement-running',
- 'Équipement sportif' => 'equipement-sportif',
- 'Érotisme' => 'erotisme',
- 'Escarpins' => 'escarpins',
- 'Événements sportifs' => 'evenements-sportifs',
- 'Expositions' => 'expositions',
- 'Extracteurs de jus' => 'extracteurs-de-jus',
- 'F1 2017' => 'f1-2017',
- 'F1 2019' => 'f1-2019',
- 'Facom' => 'facom',
- 'Fallout' => 'fallout',
- 'Fallout 4' => 'fallout-4',
- 'Fallout 76' => 'fallout-76',
- 'Famille &amp; enfants' => 'famille-enfants',
- 'Far Cry' => 'far-cry',
- 'Far Cry New Dawn' => 'far-cry-new-dawn',
- 'Fards à paupières' => 'fards-a-paupieres',
- 'Fast-foods' => 'fast-foods',
- 'Fauteuils' => 'fauteuils',
- 'Fauteuils gamer' => 'fauteuils-gaming',
- 'Fe' => 'fe',
- 'Fers à lisser / à friser' => 'fers-a-lisser-a-friser',
- 'Fers à repasser' => 'fers-a-repasser',
- 'Fers à souder' => 'fers-a-souder',
- 'Festivals' => 'festivals',
- 'Feutres' => 'feutres',
- 'FIFA' => 'fifa',
- 'FIFA 17' => 'fifa-17',
- 'FIFA 18' => 'fifa-18',
- 'FIFA 19' => 'fifa-19',
- 'FIFA 20' => 'fifa-20',
- 'FIFA 21' => 'fifa-21',
- 'Figurines' => 'figurines',
- 'Films &amp; Séries' => 'films',
- 'Final Fantasy' => 'final-fantasy',
- 'Final Fantasy XII' => 'final-fantasy-xii',
- 'Finances &amp; Assurances' => 'finances-assurances',
- 'fitbit' => 'fitbit',
- 'Fitness &amp; yoga' => 'fitness-yoga',
- 'Flash' => 'flash',
- 'Fluval' => 'fluval',
- 'Foires &amp; salons' => 'foires-et-salons',
- 'Fonds de teint' => 'fonds-de-teint',
- 'Football' => 'football',
- 'Forfaits de ski' => 'forfaits-ski',
- 'Forfaits mobiles' => 'forfaits-mobiles',
- 'Forfaits mobiles et internet' => 'telecommunications',
- 'For Honor' => 'for-honor',
- 'Formations premiers secours' => 'formations-premiers-secours',
- 'Formule 1' => 'formule-1',
- 'Fortnite' => 'fortnite',
- 'Fortnite: Pack Feu Obscur' => 'fortnite-pack-feu-obscur',
- 'Forza' => 'forza',
- 'Forza Horizon' => 'forza-horizon',
- 'Forza Horizon 3' => 'forza-horizon-3',
- 'Forza Horizon 4' => 'forza-horizon-4',
- 'Forza Motorsport' => 'forza-motosport',
- 'Forza Motorsport 7' => 'forza-motorsport-7',
- 'Fossil' => 'fossil',
- 'Fournitures scolaires' => 'fournitures-scolaires',
- 'Fours' => 'fours',
- 'Fours à poser' => 'fours-a-poser',
- 'Fours encastrables' => 'fours-encastrables',
- 'Friandises pour chat' => 'friandises-pour-chat',
- 'Friandises pour chien' => 'friandises-pour-chien',
- 'Friskies' => 'friskies',
- 'Friteuses' => 'friteuses',
- 'Friteuses sans huile' => 'friteuses-sans-huile',
- 'Fruits &amp; légumes' => 'fruits-et-legumes',
- 'Fujifilm' => 'fujifilm',
- 'Funko Pop' => 'funko-pop',
- 'FURminator' => 'furminator',
- 'Futuroscope' => 'futuroscope',
- 'Gamelles' => 'gamelles',
- 'Game of Thrones' => 'game-of-thrones',
- 'Gaming' => 'le-laboratoire-des-gamers',
- 'Gants' => 'gants',
- 'Gants moto' => 'gants-moto',
- 'Garmin' => 'garmin',
- 'Garmin Fenix' => 'garmin-fenix',
- 'Garmin Forerunner' => 'garmin-forerunner',
- 'Garmin Vivoactive' => 'garmin-vivoactive',
- 'Garmin Vivomove' => 'garmin-vivomove',
- 'Gâteaux &amp; biscuits' => 'gateaux-et-biscuits',
- 'Gears 5' => 'gears-5',
- 'Gel hydroalcoolique' => 'gel-hydroalcoolique',
- 'Gels douche' => 'gels-douche',
- 'Geox' => 'geox',
- 'Ghost of Tsushima' => 'ghost-of-tsushima',
- 'Gigoteuses' => 'gigoteuses',
- 'Gillette Fusion' => 'gillette-fusion',
- 'Gillette Mach3' => 'gillette-mach3',
- 'Glaces' => 'glaces',
- 'Glacières' => 'glacieres',
- 'Glisse urbaine' => 'glisse-urbaine',
- 'God of War' => 'god-of-war',
- 'Google Chromecast' => 'google-chromecast',
- 'Google Home' => 'google-home',
- 'Google Home Max' => 'google-home-max',
- 'Google Home Mini' => 'google-home-mini',
- 'Google Nest Hub' => 'google-nest-hub',
- 'Google Nest Mini' => 'google-nest-mini',
- 'Google Pixel' => 'google-pixel',
- 'Google Pixel 2' => 'google-pixel-2',
- 'Google Pixel 2 XL' => 'google-pixel-2-xl',
- 'Google Pixel 3' => 'google-pixel-3',
- 'Google Pixel 3 XL' => 'google-pixel-3-xl',
- 'Google Pixel 3a' => 'google-pixel-3a',
- 'Google Pixel 4' => 'google-pixel-4',
- 'Google Pixel 4 XL' => 'google-pixel-4xl',
- 'Google Pixel 4a' => 'google-pixel-4a',
- 'Google Pixel 5' => 'google-pixel-5',
- 'Google Pixel XL' => 'google-pixel-xl',
- 'GoPro' => 'gopro-hero',
- 'GoPro Hero 9' => 'gopro-hero-9',
- 'Gran Turismo' => 'gran-turismo',
- 'Grille-pain' => 'grille-pain',
- 'Grossesse &amp; maternité' => 'grossesse-maternite',
- 'GTA' => 'gta',
- 'GTA V' => 'gta-v',
- 'GTX 1060' => 'nvidia-geforce-gtx-1060',
- 'GTX 1070' => 'nvidia-geforce-gtx-1070',
- 'GTX 1080' => 'nvidia-geforce-gtx-1080',
- 'GTX 1080 Ti' => 'nvidia-geforce-gtx-1080-ti',
- 'GTX 1650' => 'gtx-1650',
- 'GTX 1660' => 'gtx-1660',
- 'GTX 1660 Ti' => 'gtx-1660-ti',
- 'Guerlain La Petite Robe Noire' => 'guerlain-petite-robe-noire',
- 'Guirlandes lumineuses' => 'guirlandes-lumineuses',
- 'Guitares' => 'guitares',
- 'Gyropodes' => 'gyropodes',
- 'Half Life' => 'half-life',
- 'Half Life 2' => 'half-life-2',
- 'Half Life Alyx' => 'half-life-alyx',
- 'Halloween' => 'halloween',
- 'Haltères &amp; poids' => 'halteres-et-poids',
- 'Hama' => 'hama',
- 'Hamacs' => 'hamacs',
- 'Hand spinners' => 'hand-spinners',
- 'Harnais pour chien' => 'harnais-pour-chien',
- 'Harry Potter' => 'harry-potter',
- 'Havaianas' => 'havaianas',
- 'High-Tech' => 'high-tech',
- 'High-tech &amp; informatique' => 'le-laboratoire-high-tech-informatique',
- 'Hisense' => 'hisense',
- 'Home Cinéma' => 'home-cinema',
- 'Honor' => 'honor',
- 'Honor 6X' => 'honor-6x',
- 'Honor 8' => 'honor-8',
- 'Honor 8 Pro' => 'honor-8-pro',
- 'Honor 8X' => 'honor-8x',
- 'Honor 8X Max' => 'honor-8x-max',
- 'Honor 9' => 'honor-9',
- 'Honor 10' => 'honor-10',
- 'Honor 20' => 'honor-20',
- 'Honor 20 Lite' => 'honor-20-lite',
- 'Honor 20 Pro' => 'honor-20-pro',
- 'Honor Band 5' => 'honor-band-5',
- 'Honor MagicBook' => 'honor-magicbook',
- 'Honor MagicWatch 2' => 'honor-magicwatch-2',
- 'Honor View 20' => 'honor-view-20',
- 'Horizon Zero Dawn' => 'horizon-zero-dawn',
- 'Hôtels &amp; Hébergements' => 'hotels',
- 'Hoverboards' => 'hoverboards',
- 'HTC 10' => 'htc-10',
- 'HTC Desire' => 'htc-desire',
- 'HTC One M9' => 'htc-one-m9',
- 'HTC U11' => 'htc-u11',
- 'HTC U Play' => 'htc-u-play',
- 'HTC U Ultra' => 'htc-u-ultra',
- 'HTC Vive' => 'htc-vive',
- 'Huawei' => 'huawei',
- 'Huawei FreeBuds 3' => 'huawei-freebuds-3',
- 'Huawei Mate 9' => 'huawei-mate-9',
- 'Huawei Mate 10' => 'huawei-mate-10',
- 'Huawei Mate 10 Pro' => 'huawei-mate-10-pro',
- 'Huawei Mate 20' => 'huawei-mate-20',
- 'Huawei Mate 20 Lite' => 'huawei-mate-20-lite',
- 'Huawei Mate 20 Pro' => 'huawei-mate-20-pro',
- 'Huawei Mate 20 RS' => 'huawei-mate-20-rs',
- 'Huawei Mate 30' => 'huawei-mate-30',
- 'Huawei Mate 30 Lite' => 'huawei-mate-30-lite',
- 'Huawei Mate 30 Pro' => 'huawei-mate-30-pro',
- 'Huawei P8 Lite' => 'huawei-p8-lite',
- 'Huawei P9 Lite' => 'huawei-p9-lite',
- 'Huawei P10' => 'huawei-p10',
- 'Huawei P10 Lite' => 'huawei-p10-lite',
- 'Huawei P10 Plus' => 'huawei-p10-plus',
- 'Huawei P20' => 'huawei-p20',
- 'Huawei P20 Lite' => 'huawei-p20-lite',
- 'Huawei P20 Pro' => 'huawei-p20-pro',
- 'Huawei P30' => 'huawei-p30',
- 'Huawei P30 Lite' => 'huawei-p30-lite',
- 'Huawei P30 Pro' => 'huawei-p30-pro',
- 'Huawei P40' => 'huawei-p40',
- 'Huawei P40 Lite' => 'huawei-p40-lite',
- 'Huawei P40 Pro' => 'huawei-p40-pro',
- 'Huawei Watch' => 'huawei-watch',
- 'Huawei Watch 2' => 'huawei-watch-2',
- 'Hubs' => 'hubs',
- 'Hugo Boss Bottled' => 'hugo-boss-bottled',
- 'Huile moteur' => 'huile-moteur',
- 'Hygiène &amp; soins' => 'hygiene-soins',
- 'Hygiène de la maison' => 'hygiene-de-la-maison',
- 'Hygiène des bébés' => 'hygiene-des-bebes',
- 'Hygiène intime' => 'hygiene-intime',
- 'iMac' => 'mac-de-bureau',
- 'iMac 2021' => 'imac-2021',
- 'Image, son, photo' => 'le-laboratoire-audiovisuel',
- 'Impressions photo' => 'impressions-photo',
- 'Imprimantes' => 'imprimantes',
- 'Imprimantes 3D' => 'imprimantes-3d',
- 'Imprimantes Brother' => 'imprimantes-brother',
- 'Imprimantes Canon' => 'imprimantes-canon',
- 'Imprimantes Epson' => 'imprimantes-epson',
- 'Imprimantes HP' => 'imprimantes-hp',
- 'Imprimantes laser' => 'imprimantes-laser',
- 'Imprimantes multifonctions' => 'imprimantes-multifonctions',
- 'Informatique' => 'informatique',
- 'Instax Mini' => 'instax-mini',
- 'Instruments de musique' => 'instruments-de-musique',
- 'Intel i5' => 'intel-i5',
- 'Intel i7' => 'intel-i7',
- 'Intel i9' => 'intel-i9',
- 'iPad' => 'apple-ipad',
- 'iPad 2019' => 'ipad-2019',
- 'iPad 2020' => 'ipad-2020',
- 'iPad Air' => 'ipad-air',
- 'iPad Air 2019' => 'ipad-air-2019',
- 'iPad Air 2020' => 'ipad-air-2020',
- 'iPad Mini' => 'apple-ipad-mini',
- 'iPad Pro' => 'apple-ipad-pro',
- 'iPad Pro 11' => 'ipad-pro-11',
- 'iPad Pro 12.9' => 'ipad-pro-12-9',
- 'iPad Pro 2020' => 'ipad-pro-2020',
- 'iPhone' => 'apple-iphone',
- 'iPhone 6' => 'apple-iphone-6',
- 'iPhone 7' => 'apple-iphone-7',
- 'iPhone 7 Plus' => 'apple-iphone-7-plus',
- 'iPhone 8' => 'apple-iphone-8',
- 'iPhone 8 Plus' => 'apple-iphone-8-plus',
- 'iPhone 11' => 'iphone-11',
- 'iPhone 11 Pro' => 'iphone-11-pro',
- 'iPhone 11 Pro Max' => 'iphone-11-pro-max',
- 'iPhone 12' => 'iphone-12',
- 'iPhone 12 Mini' => 'iphone-12-mini',
- 'iPhone 12 Pro' => 'iphone-12-pro',
- 'iPhone 12 Pro Max' => 'iphone-12-pro-max',
- 'iPhone SE' => 'apple-iphone-se',
- 'iPhone X' => 'apple-iphone-x',
- 'iPhone XR' => 'apple-iphone-xr',
- 'iPhone XS' => 'apple-iphone-xs',
- 'iPhone XS Max' => 'apple-iphone-xs-max',
- 'iRobot Roomba' => 'irobot-roomba',
- 'Isolation' => 'isolation',
- 'Jabra Elite 75t' => 'jabra-elite-75t',
- 'Jabra Elite 85h' => 'jabra-elite-85h',
- 'Jabra Elite 85t' => 'jabra-elite-85t',
- 'Jabra Elite Active 65t' => 'jabra-elite-active-65t',
- 'Jacuzzis' => 'jacuzzis',
- 'Jardin' => 'jardin',
- 'Jardin &amp; bricolage' => 'jardin-bricolage',
- 'Jardinage' => 'entretien-du-jardin',
- 'JBL' => 'jbl',
- 'JBL Charge 4' => 'jbl-charge-4',
- 'JBL Flip' => 'jbl-flip',
- 'JBL GO' => 'jbl-go',
- 'JBL Xtreme 2' => 'jbl-xtreme-2',
- 'Jeans' => 'jeans',
- 'Jets dentaires' => 'jets-dentaires',
- 'Jeux &amp; jouets' => 'jeux-jouets',
- 'Jeux &amp; sports de café' => 'jeux-sports-cafe-bar',
- 'Jeux d&#039;adresse' => 'jeux-adresse',
- 'Jeux d&#039;apprentissage' => 'jeux-d-apprentissage',
- 'Jeux d&#039;eau' => 'jeux-jouets-eau',
- 'Jeux d&#039;extérieur' => 'jeux-d-exterieur',
- 'Jeux d&#039;imitation' => 'jeux-d-imitation',
- 'Jeux de cartes et de plateau' => 'jeux-cartes-plateau-societe',
- 'Jeux de construction' => 'jeux-de-construction',
- 'Jeux de hasard &amp; paris' => 'jeux-et-paris',
- 'Jeux de société' => 'jeux-de-societe',
- 'Jeux Nintendo 3DS' => 'jeux-3ds',
- 'Jeux Nintendo Switch' => 'jeux-nintendo-switch',
- 'Jeux PC' => 'jeux-pc',
- 'Jeux PC dématérialisés' => 'jeux-pc-dematerialises',
- 'Jeux pour bébés' => 'jeux-pour-bebes',
- 'Jeux PS4' => 'jeux-playstation-4',
- 'Jeux PS4 dématérialisés' => 'jeux-ps4-dematerialises',
- 'Jeux PS5' => 'jeux-playstation-5',
- 'Jeux PS5 dématérialisés' => 'jeux-playstation-5-dematerialises',
- 'Jeux PS Plus' => 'jeux-ps-plus',
- 'Jeux vidéo' => 'jeux-video',
- 'Jeux VR' => 'jeux-vr',
- 'Jeux Wii U' => 'jeux-wii-u',
- 'Jeux Xbox One' => 'jeux-xbox-one',
- 'Jeux Xbox One dématérialisés' => 'jeux-xbox-dematerialises',
- 'Jeux Xbox Series X' => 'jeux-xbox-series-x',
- 'Jeux Xbox with Gold' => 'jeux-xbox-with-gold',
- 'Jouets' => 'jouets',
- 'Jouets pour chat' => 'jouets-pour-chat',
- 'Jouets pour chien' => 'jouets-pour-chien',
- 'Journaux numériques' => 'journaux-numeriques',
- 'Journaux papier' => 'journaux-papier',
- 'Joy-Con' => 'manettes-nintendo-switch-joy-con',
- 'Jungle Speed' => 'jungle-speed',
- 'Just Cause' => 'just-cause',
- 'Just Cause 3' => 'just-cause-3',
- 'Just Cause 4' => 'just-cause-4',
- 'Kärcher' => 'karcher',
- 'Kaspersky' => 'kaspersky',
- 'Kinder' => 'kinder',
- 'Kindle Oasis' => 'kindle-oasis',
- 'Kindle Paperwhite' => 'kindle-paperwhite',
- 'Kindle Voyage' => 'kindle-voyage',
- 'Kingdom Hearts' => 'kingdom-hearts',
- 'Kingdom Hearts 3' => 'kingdom-hearts-3',
- 'Kingston HyperX Cloud II' => 'kingston-hyperx-cloud-2',
- 'Kits premiers secours' => 'premiers-secours',
- 'Kobo' => 'kobo',
- 'Kobo Aura 2' => 'kobo-aura-2',
- 'Kobo Aura H2o' => 'kobo-aura-h2o',
- 'Kobo Aura One' => 'kobo-aura-one',
- 'L&#039;annale du destin' => 'l-annale-du-destin',
- 'L&#039;ombre de la guerre' => 'l-ombre-de-la-guerre',
- 'L&#039;ombre du Mordor' => 'l-ombre-du-mordor',
- 'Lacoste' => 'lacoste',
- 'Lampadaires' => 'lampadaires',
- 'Lampes' => 'lampes',
- 'Lampes de table' => 'lampes-de-table',
- 'Lampes solaires' => 'lampes-solaires',
- 'Lancôme La Vie est Belle' => 'lancome-la-vie-est-belle',
- 'Lapeyre' => 'lapeyre',
- 'La Terre du Milieu' => 'la-terre-du-milieu',
- 'Lavage auto' => 'lavage-auto',
- 'Lavazza' => 'lavazza',
- 'Lave-linge' => 'lave-linge',
- 'Lave-linge frontal' => 'lave-linge-frontal',
- 'Lave-linge séchant' => 'lave-linge-sechant',
- 'Lave-linge top' => 'lave-linge-top',
- 'Lave-vaisselle' => 'lave-vaisselle',
- 'Lay-Z-Spa' => 'lay-z-spa',
- 'Leasing voiture' => 'leasing-voiture',
- 'Le bâton de la vérité' => 'le-baton-de-la-verite',
- 'Lecteurs Blu-Ray' => 'lecteurs-blu-ray',
- 'Lecteurs CD' => 'lecteurs-cd',
- 'Lecteurs DVD' => 'lecteurs-dvd',
- 'Lego' => 'lego',
- 'Lego Architecture' => 'lego-architecture',
- 'Lego Batman' => 'lego-batman',
- 'Lego City' => 'lego-city',
- 'Lego Creator' => 'lego-creator',
- 'Lego Dimensions' => 'lego-dimensions',
- 'Lego Duplo' => 'lego-duplo',
- 'Lego Friends' => 'lego-friends',
- 'Lego Harry Potter' => 'lego-harry-potter',
- 'Lego Ideas' => 'lego-ideas',
- 'Lego Marvel' => 'lego-marvel',
- 'Lego Nexo Knights' => 'lego-nexo-knights',
- 'Lego Ninjago' => 'lego-ninjago',
- 'Lego Star Wars' => 'lego-star-wars',
- 'Lego Technic' => 'lego-technic',
- 'Lenovo' => 'lenovo',
- 'Lenovo IdeaPad' => 'lenovo-ideapad',
- 'Lenovo K6 Note' => 'lenovo-k6-note',
- 'Lenovo P8' => 'lenovo-p8',
- 'Lenovo Tab 3' => 'lenovo-tab-3',
- 'Lenovo Tab 4' => 'lenovo-tab-4',
- 'Lenovo ThinkPad' => 'lenovo-thinkpad',
- 'Lenovo Yoga' => 'lenovo-yoga',
- 'Lenovo Yoga Tab 3' => 'lenovo-yoga-tab-3',
- 'Lentilles de contact' => 'lentilles-de-contact',
- 'Le Seigneur des anneaux' => 'le-seigneur-des-anneaux',
- 'Les Sims' => 'les-sims',
- 'Les Sims 4' => 'les-sims-4',
- 'Lessive' => 'lessive',
- 'Levi&#039;s' => 'levi-s',
- 'LG' => 'lg',
- 'LG G4' => 'lg-g4',
- 'LG G5' => 'lg-g5',
- 'LG G6' => 'lg-g6',
- 'LG OLED TV' => 'lg-oled-tv',
- 'LG Q6' => 'lg-q6',
- 'LG Q8' => 'lg-q8',
- 'Life is Strange' => 'life-is-strange',
- 'Linge de maison' => 'linge-de-maison',
- 'Lingerie' => 'lingerie',
- 'Lingettes désinfectantes' => 'lingettes-desinfectantes',
- 'Lingettes pour bébés' => 'lingettes-pour-bebes',
- 'Liseuses' => 'liseuses',
- 'Litière pour chat' => 'litiere-pour-chat',
- 'Lits' => 'lits',
- 'Lits pour bébé' => 'lits-pour-bebe',
- 'Lits pour enfants' => 'lits-pour-enfants',
- 'Little Nightmares' => 'little-nightmares',
- 'Livraison de repas' => 'service-de-livraison-de-repas',
- 'Livres &amp; littérature' => 'livres-litterature',
- 'Livres &amp; Magazines' => 'livres',
- 'Livres audio' => 'livres-audio',
- 'Livres photo' => 'livres-photo',
- 'Location de voiture' => 'location-de-voiture',
- 'Logiciels' => 'logiciels',
- 'Logiciels de sécurité' => 'logiciels-de-securite',
- 'Logiciels Microsoft' => 'logiciels-microsoft',
- 'Logitech' => 'logitech',
- 'Logitech G502' => 'logitech-g502',
- 'Logitech G703' => 'logitech-g703',
- 'Logitech G Pro X' => 'logitech-g-pro-x',
- 'Logitech Harmony' => 'logitech-harmony',
- 'Logitech MX Master' => 'logitech-mx-master',
- 'Logitech MX Master 2S' => 'logitech-mx-master-2s',
- 'Loisirs créatifs' => 'loisirs-creatifs',
- 'Lolita Lempicka' => 'lolita-lempicka-premier-parfum',
- 'Loup-Garou' => 'loup-garou',
- 'Lubrifiants' => 'lubrifiants',
- 'Luges' => 'luges',
- 'Luigi&#039;s Mansion 3' => 'luigi-mansion-3',
- 'Luminaires' => 'luminaires',
- 'Lunettes de natation' => 'lunettes-de-natation',
- 'Lunettes de soleil' => 'lunettes-de-soleil',
- 'M&amp;M&#039;s' => 'metm-s',
- 'MacBook' => 'macbook',
- 'MacBook Air' => 'apple-macbook-air',
- 'MacBook Pro' => 'apple-macbook-pro',
- 'MacBook Pro 13' => 'macbook-pro-13',
- 'MacBook Pro 15' => 'macbook-pro-15',
- 'MacBook Pro 16' => 'macbook-pro-16',
- 'Machines à café à dosettes' => 'machines-a-cafe-a-dosettes',
- 'Machines à café en grain' => 'machines-a-cafe-en-grain',
- 'Machines à coudre' => 'machines-a-coudre',
- 'Machines à pain' => 'machines-a-pain',
- 'Machines de sport' => 'machines-sport',
- 'Machines Dolce Gusto' => 'machines-dolce-gusto',
- 'Machines Nespresso' => 'machines-nespresso',
- 'Machines Senseo' => 'machines-senseo',
- 'Machines Tassimo' => 'machines-tassimo',
- 'Mac mini' => 'mac-mini',
- 'Madden NFL 20' => 'madden-nfl-20',
- 'Magasins d&#039;usine' => 'magasins-usine',
- 'Magazines' => 'magazines',
- 'Maillots de bain' => 'maillots-de-bain',
- 'Maillots de football' => 'maillots-de-football',
- 'Maison &amp; Habitat' => 'maison-habitat',
- 'Maisons de poupées' => 'maisons-poupees',
- 'Makita' => 'makita',
- 'Manettes' => 'manettes-accessoires-consoles',
- 'Manettes DualSense' => 'manettes-playstation-5',
- 'Manettes Nintendo Switch' => 'manettes-nintendo-switch',
- 'Manettes Nintendo Switch Pro' => 'manettes-nintendo-switch-pro',
- 'Manettes PlayStation 4' => 'manettes-playstation-4',
- 'Manettes Xbox' => 'manettes-xbox',
- 'Manettes Xbox One' => 'manettes-xbox-one',
- 'Manettes Xbox One Elite' => 'manettes-xbox-one-elite',
- 'Manettes Xbox Series X' => 'manettes-xbox-series-x',
- 'Manix' => 'manix',
- 'Manteaux' => 'manteaux',
- 'Maquillage' => 'maquillage',
- 'Marchands et leurs offres' => 'vos-avisdemandes-sur-les-marchands-et-leurs-offres',
- 'Mario &amp; Sonic aux Jeux Olympiques de Tokyo 2020' => 'mario-sonic-jeux-olympiques-tokyo-2020',
- 'Mario Kart' => 'mario-kart',
- 'Marques' => 'marques',
- 'Marteaux &amp; maillets' => 'marteaux-et-maillets',
- 'Marvel&#039;s Avengers' => 'marvels-avengers',
- 'Mascara' => 'mascara',
- 'Masques cheveux' => 'masques-cheveux',
- 'Masques de protection' => 'masques-de-protection-respiratoire',
- 'Masques de ski' => 'masques-de-ski',
- 'Mass Effect' => 'mass-effect',
- 'Mass Effect: Andromeda' => 'mass-effect-andromeda',
- 'Matchs de football' => 'matchs-de-football',
- 'Matelas' => 'matelas',
- 'Matelas gonflables' => 'matelas-gonflables',
- 'Matériaux de construction' => 'materiaux-de-construction',
- 'Matériel de ski' => 'materiel-de-ski',
- 'Medion' => 'medion',
- 'Metro' => 'metro',
- 'Metro 2033' => 'metro-2033',
- 'Metro Exodus' => 'metro-exodus',
- 'Meubles pour aquarium' => 'meubles-pour-aquarium',
- 'Meubles pour chat' => 'meubles-pour-chat',
- 'Meubles salle de bain' => 'salle-de-bain',
- 'Micro-casques gaming' => 'micro-casques-gaming',
- 'Micro-ondes' => 'micro-ondes',
- 'Microphones' => 'microphones',
- 'Micro SD' => 'micro-sd',
- 'Microsoft Flight Simulator' => 'microsoft-flight-simulator',
- 'Microsoft Office' => 'microsoft-office',
- 'Microsoft Surface Book' => 'microsoft-surface-book',
- 'Microsoft Surface Pro 6' => 'microsoft-surface-pro-6',
- 'Microsoft Surface Pro 7' => 'microsoft-surface-pro-7',
- 'Miele' => 'miele',
- 'Minecraft' => 'minecraft',
- 'Mini PC' => 'mini-pc',
- 'Mini réfrigérateurs' => 'mini-refrigerateurs',
- 'Miroirs' => 'miroirs',
- 'Mixeurs &amp; Blenders' => 'mixeurs-blenders',
- 'Mixeurs plongeants' => 'mixeur-plongeant',
- 'Mobilier' => 'mobilier',
- 'Mobilier de bureau' => 'fournitures-de-bureau',
- 'Mobilier de jardin' => 'mobilier-jardin',
- 'Mobilier de salon' => 'mobilier-salon',
- 'Mobvoi Ticwatch' => 'mobvoi-ticwatch',
- 'Mode' => 'mode',
- 'Mode &amp; accessoires' => 'mode-accessoires',
- 'Mode &amp; beauté' => 'le-laboratoire-de-la-mode-beaute',
- 'Mode enfants' => 'mode-enfants',
- 'Mode femme' => 'mode-femme',
- 'Mode homme' => 'mode-homme',
- 'Modélisme' => 'modelisme',
- 'Monopoly' => 'monopoly',
- 'Montage PC' => 'montage-pc',
- 'Montre connectée Amazfit' => 'montres-connectees-amazfit',
- 'Montre connectée Garmin' => 'montres-connectees-garmin',
- 'Montre connectée Honor' => 'montres-connectees-honor',
- 'Montre connectée Samsung' => 'smartwatch-samsung',
- 'Montres' => 'montres',
- 'Montres connectées' => 'smartwatch',
- 'Mortal Kombat' => 'mortal-kombat',
- 'Mortal Kombat 11' => 'mortal-kombat-11',
- 'Moto C Plus' => 'moto-c-plus',
- 'Moto E4' => 'moto-e4',
- 'Moto G5' => 'moto-g5',
- 'Moto G5 Plus' => 'moto-g5-plus',
- 'Moto G5S' => 'moto-g5s',
- 'Moto G5S Plus' => 'moto-g5s-plus',
- 'Moto G6' => 'moto-g6',
- 'Moto G6 Play' => 'moto-g6-play',
- 'Moto G6 Plus' => 'moto-g6-plus',
- 'Moto G7 Play' => 'moto-g7-play',
- 'Moto G7 Plus' => 'moto-g7-plus',
- 'Moto G7 Power' => 'moto-g7-power',
- 'Moto M' => 'moto-m',
- 'Motorola' => 'motorola',
- 'Moto Z2' => 'moto-z2',
- 'Moto Z2 Force' => 'moto-z2-force',
- 'Moto Z2 Play' => 'moto-z2-play',
- 'Moto Z3' => 'moto-z3',
- 'Moto Z3 Play' => 'moto-z3-play',
- 'Moulinex' => 'moulinex',
- 'Mousses à raser' => 'mousses-a-raser',
- 'MSI' => 'msi',
- 'Musées' => 'musees',
- 'Musique' => 'musique',
- 'NAS' => 'nas',
- 'Natation' => 'natation',
- 'Nature &amp; sports d&#039;hiver' => 'nature-sports-hiver',
- 'Navigation' => 'navigation',
- 'NBA 2K' => 'nba-2k',
- 'NBA 2K20' => 'nba-2k20',
- 'NERF' => 'nerf',
- 'Nescafé' => 'nescafe',
- 'Nespresso' => 'nespresso',
- 'Nest Learning Thermostat' => 'nest-learning-thermostat',
- 'Nest Protect' => 'nest-protect',
- 'Netflix' => 'netflix',
- 'Nettoyeurs haute-pression' => 'nettoyeurs-haute-pression',
- 'Nettoyeurs haute pression Karcher' => 'nettoyeurs-haute-pression-karcher',
- 'Nettoyeurs vapeur' => 'nettoyeurs-vapeur',
- 'New Balance' => 'new-balance',
- 'New Balance 574' => 'new-balance-574',
- 'NHL 20' => 'nhl-20',
- 'Nike' => 'nike',
- 'Nike Air Force' => 'nike-air-force',
- 'Nike Air Jordan' => 'nike-air-jordan',
- 'Nike Air Max' => 'nike-air-max',
- 'Nike Air Max 90' => 'nike-air-max-90',
- 'Nike Air Max 200' => 'nike-air-max-200',
- 'Nike Air Max 270' => 'nike-air-max-270',
- 'Nike Air Max 720' => 'nike-air-max-720',
- 'Nike Free' => 'nike-free',
- 'Nike Huarache' => 'nike-huarache',
- 'Nike Roshe Run' => 'nike-roshe-run',
- 'Nikon' => 'nikon',
- 'Nikon D3500' => 'nikon-d3500',
- 'Ni no Kuni' => 'ni-no-kuni',
- 'Ni No Kuni: Wrath of the White Witch' => 'ni-no-kuni-wrath-white-witch',
- 'Ni No Kuni II: Revenant Kingdom' => 'ni-no-kuni-2-revenant-kingdom',
- 'Nintendo' => 'nintendo',
- 'Nioh' => 'nioh',
- 'Nivea' => 'nivea',
- 'Nocciolata' => 'nocciolata',
- 'Nokia' => 'nokia',
- 'Nokia 5' => 'nokia-5',
- 'Nokia 6' => 'nokia-6',
- 'Nokia 8' => 'nokia-8',
- 'Nokia 9 PureView' => 'nokia-9-pureview',
- 'Nougats' => 'nougats',
- 'Nourriture pour chat' => 'nourriture-pour-chat',
- 'Nourriture pour chien' => 'nourriture-pour-chien',
- 'Nourriture pour poissons' => 'nourriture-pour-poissons',
- 'Nutella' => 'nutella',
- 'Nvidia' => 'nvidia',
- 'Nvidia GeForce' => 'nvidia-geforce',
- 'Nvidia Shield' => 'nvidia-shield',
- 'Objectifs' => 'objectifs',
- 'Objets connectés' => 'objets-connectes',
- 'Oculus Go' => 'oculus-go',
- 'Oculus Rift' => 'oculus-rift',
- 'Oiseaux' => 'oiseaux',
- 'One Piece: Pirate Warriors' => 'one-piece-pirate-warriors',
- 'OnePlus 5' => 'oneplus-5',
- 'OnePlus 5T' => 'oneplus-5t',
- 'OnePlus 6' => 'oneplus-6',
- 'OnePlus 6T' => 'oneplus-6t',
- 'OnePlus 7' => 'oneplus-7',
- 'OnePlus 7 Pro' => 'oneplus-7-pro',
- 'OnePlus 7T' => 'oneplus-7t',
- 'OnePlus 7T Pro' => 'oneplus-7t-pro',
- 'OnePlus 8' => 'oneplus-8',
- 'OnePlus 8 Pro' => 'oneplus-8-pro',
- 'OnePlus 8T' => 'oneplus-8t',
- 'OnePlus 9' => 'oneplus-9',
- 'OnePlus 9 Pro' => 'oneplus-9-pro',
- 'OnePlus Nord' => 'oneplus-nord',
- 'Onkyo' => 'onkyo',
- 'Oppo Find X2 Lite' => 'oppo-find-x2-lite',
- 'Oppo Find X2 Neo' => 'oppo-find-x2-neo',
- 'Oppo Find X2 Pro' => 'oppo-find-x2-pro',
- 'Oppo Reno' => 'oppo-reno',
- 'Optique' => 'optique',
- 'Oral-B' => 'oral-b',
- 'Ordinateurs de bureau' => 'ordinateurs-de-bureau',
- 'Ordinateurs tout-en-un' => 'pc-de-bureau-complets',
- 'Oreillers' => 'oreillers',
- 'Osram Smart+' => 'osram-smart-plus',
- 'Outillage' => 'outillage',
- 'Outils à main' => 'outils-main',
- 'Outils de jardinage' => 'outils-de-jardinage',
- 'Outils électriques' => 'outils-electriques',
- 'Overwatch' => 'overwatch',
- 'Packs clavier-souris' => 'packs-clavier-souris',
- 'Packs consoles' => 'packs-consoles',
- 'Paco Rabanne Invictus' => 'paco-rabanne-invictus',
- 'Paco Rabanne Lady Million' => 'paco-rabanne-lady-million',
- 'Paco Rabanne One Million' => 'paco-rabanne-one-million',
- 'Pain &amp; pâtisseries' => 'pain-patisseries',
- 'Pampers' => 'pampers',
- 'Panasonic' => 'panasonic',
- 'Panasonic Lumix' => 'panasonic-lumix',
- 'Panier Plus' => 'panier-plus',
- 'Pantalons' => 'pantalons',
- 'Papeterie' => 'papeterie',
- 'Papeterie et bureautique' => 'papeterie-bureautique',
- 'Papier bureautique' => 'papier-bureautique',
- 'Papier peint' => 'papier-peint',
- 'Papier toilette' => 'papier-toilette',
- 'Parapharmacie' => 'parapharmacie',
- 'Parasols' => 'parasols',
- 'Parc Astérix' => 'parc-asterix',
- 'Parcs d&#039;attraction' => 'parcs-d-attraction',
- 'Parfums' => 'parfums',
- 'Parfums femme' => 'parfums-femme',
- 'Parfums homme' => 'parfums-homme',
- 'Parkas' => 'parkas',
- 'Parrot' => 'parrot',
- 'Partitions' => 'partitions',
- 'Pâtée pour chat' => 'patee-pour-chat',
- 'Pâtée pour chien' => 'patee-pour-chien',
- 'Pâtes à tartiner' => 'pates-tartiner',
- 'Pâtisserie' => 'patisserie',
- 'PC Barebones' => 'pc-barebones',
- 'PC gamer fixe' => 'pc-gamer-complets',
- 'PC gaming' => 'pc-gaming',
- 'PC hybrides' => 'hybrides',
- 'PC Microsoft Surface' => 'pc-microsoft-surface',
- 'PC portables' => 'pc-portables',
- 'PC portables Acer' => 'pc-portables-acer',
- 'PC portables ASUS' => 'pc-portables-asus',
- 'PC portables Dell' => 'pc-portables-dell',
- 'PC portables gaming' => 'portables-gamer',
- 'PC portables Honor' => 'pc-portables-honor',
- 'PC portables HP' => 'pc-portables-hp',
- 'PC portables Lenovo' => 'pc-portables-lenovo',
- 'PC portables Lenovo Legion' => 'lenovo-legion',
- 'PC portables Xiaomi' => 'pc-portables-xiaomi',
- 'Pêche' => 'peche',
- 'Peignes &amp; brosses à cheveux' => 'peignes-et-brosses-a-cheveux',
- 'Peignoirs' => 'peignoirs',
- 'Peintures' => 'peintures',
- 'Peluches' => 'peluches',
- 'Perceuses' => 'perceuses',
- 'Périphériques PC' => 'peripheriques-pc',
- 'Persona 5' => 'persona-5',
- 'Persona 5 Royal' => 'persona-5-royal',
- 'PES' => 'pro-evolution-soccer',
- 'Pèse-personnes' => 'pese-personnes',
- 'Petites voitures' => 'petites-voitures',
- 'Pharmacie &amp; parapharmacie' => 'pharmacie-parapharmacie',
- 'Philips' => 'philips',
- 'Philips Hue' => 'philips-hue',
- 'Philips Hue E14' => 'philips-hue-e14',
- 'Philips Hue E27' => 'philips-hue-e27',
- 'Philips Hue Go' => 'philips-hue-go',
- 'Philips Hue GU10' => 'philips-hue-gu10',
- 'Philips Hue LightStrip' => 'philips-hue-lightstrip',
- 'Philips Hue Play HDMI Sync Box' => 'philips-hue-play-hdmi-sync-box',
- 'Philips Lumea' => 'philips-lumea',
- 'Philips OneBlade' => 'philips-one-blade',
- 'Philips Sonicare' => 'philips-sonicare',
- 'Photo' => 'photo',
- 'Pièces auto' => 'pieces-auto',
- 'Pièces moto' => 'pieces-moto',
- 'Pièces vélo' => 'pieces-velo',
- 'Piles' => 'piles',
- 'Piles rechargeables' => 'piles-rechargeables',
- 'Pinceaux maquillage' => 'pinceaux-maquillage',
- 'Pinces' => 'pinces',
- 'Ping-pong' => 'ping-pong',
- 'Pioneer' => 'pioneer',
- 'Piscines' => 'piscines',
- 'Pizza' => 'pizza',
- 'Places de cinéma' => 'places-de-cinema',
- 'Plafonniers' => 'plafonniers',
- 'Plancha' => 'planchas',
- 'Plantes &amp; semis' => 'plantes',
- 'Plaques de cuisson' => 'plaques-de-cuisson',
- 'Platines vinyle' => 'platines-vinyle',
- 'Plats &amp; moules' => 'plats-et-moules',
- 'PlayerUnknown&#039;s Battlegrounds' => 'playerunknown-s-battleground',
- 'Playmobil' => 'playmobil',
- 'PlayStation' => 'playstation',
- 'Pneus' => 'pneus',
- 'PocketBook' => 'pocketbook',
- 'PocketBook Touch Lux 3' => 'pocketbook-touch-lux-3',
- 'POCO F2 Pro' => 'poco-f2-pro',
- 'POCO F3' => 'poco-f3',
- 'POCO M3' => 'poco-m3',
- 'POCO X3' => 'poco-x3',
- 'POCO X3 Pro' => 'poco-x3-pro',
- 'Poêles' => 'poeles',
- 'Pokémon' => 'pokemon',
- 'Pokémon: Let&#039;s Go' => 'pokemon-letsgo',
- 'Pokémon Épée et Bouclier' => 'pokemon-epee-bouclier',
- 'Pokémon Tournament' => 'pokemon-tournament',
- 'Pokémon Ultra Sun / Moon' => 'pokemon-ultra-sun-moon',
- 'Polaroid' => 'polaroid',
- 'Polos' => 'polos',
- 'Pompes à vélo' => 'pompes-velo',
- 'Porte-bébé' => 'porte-bebe',
- 'Portefeuilles' => 'portefeuilles',
- 'Posters' => 'posters',
- 'Potager' => 'potager',
- 'Pots &amp; cache-pots' => 'pots-et-cache-pots',
- 'Poubelles' => 'poubelles',
- 'Poulaillers' => 'poulaillers',
- 'Poupées' => 'poupees',
- 'Poussettes' => 'poussettes-bebe',
- 'Présentez-vous !' => 'mieux-se-connaitre-presentez-vous',
- 'Préservatifs' => 'preservatifs',
- 'Princesse Tam-Tam' => 'princesse-tam-tam',
- 'Prises connectées' => 'prises-connectees',
- 'Processeurs' => 'processeurs',
- 'Produit pour lentilles' => 'produit-pour-lentilles',
- 'Produits de massage' => 'produits-de-massage',
- 'Produits frais' => 'produits-frais',
- 'Produits reconditionnés' => 'reconditionne',
- 'Produits vétérinaires' => 'produits-veterinaires',
- 'Programme d&#039;Entraînement Cérébral du Dr. Kawashima' => 'dr-kawashima-brain-training',
- 'Project Cars 2' => 'project-cars-2',
- 'Protection de la maison' => 'protection-de-la-maison',
- 'Protections intimes' => 'protections-intimes',
- 'Protection solaire' => 'protection-solaire',
- 'Puériculture' => 'puericulture',
- 'Pulls' => 'pulls',
- 'Puma' => 'puma',
- 'Purificateurs d&#039;air' => 'purificateurs-d-air',
- 'Purina' => 'purina',
- 'Puzzles' => 'puzzles',
- 'Pyjamas' => 'pyjamas',
- 'Pyjamas &amp; chemises de nuit' => 'pyjamas-chemises-de-nuit',
- 'Pyjamas pour bébés' => 'pyjamas-pour-bebes',
- 'Qobuz' => 'qobuz',
- 'Quiksilver' => 'quiksilver',
- 'Radiateurs' => 'radiateurs',
- 'Ralph Lauren' => 'ralph-lauren',
- 'RAM' => 'ram',
- 'Randonnée' => 'randonnee',
- 'Raquettes de ping-pong' => 'raquettes-de-ping-pong',
- 'Raquettes de tennis' => 'raquettes-de-tennis',
- 'Rasage et épilation' => 'rasage-epilation',
- 'Rasoirs Braun' => 'rasoirs-braun',
- 'Rasoirs électriques' => 'rasoirs-electriques',
- 'Rasoirs Gillette' => 'gillette',
- 'Rasoirs manuels' => 'rasoirs-manuels',
- 'Rasoirs Philips' => 'rasoirs-philips',
- 'Rasoirs Wilkinson' => 'rasoirs-wilkinson-sword',
- 'Raspberry Pi' => 'raspberry-pi',
- 'Ray-Ban' => 'ray-ban',
- 'Razer' => 'razer',
- 'Razer DeathAdder' => 'razer-deathadder',
- 'Realme 5 Pro' => 'realme-5-pro',
- 'Realme X2 Pro' => 'realme-x2-pro',
- 'Red Dead Redemption' => 'red-dead-redemption',
- 'Red Dead Redemption 2' => 'red-dead-redemption-2',
- 'Réductions étudiants &amp; jeunes' => 'reductions-etudiants-et-jeunes',
- 'Reebok' => 'reebok',
- 'Reebok Club C' => 'reebok-club-c',
- 'Réfrigérateurs' => 'refrigerateurs',
- 'Réfrigérateurs américains' => 'refrigerateurs-americains',
- 'Refroidissement PC' => 'refroidissement-pc',
- 'Réhausseurs' => 'rehausseurs',
- 'Remington' => 'remington',
- 'Repas de fête' => 'repas-fete-reveillon',
- 'Repassage' => 'repassage',
- 'Répéteurs' => 'repeteurs',
- 'Réseau' => 'reseau',
- 'Resident Evil' => 'resident-evil',
- 'Resident Evil 3' => 'resident-evil-3',
- 'Resident Evil 7' => 'resident-evil-7',
- 'Restaurants' => 'restaurants',
- 'Revêtements de sols' => 'revetements-de-sols',
- 'Revêtements muraux' => 'revetements-muraux',
- 'Rhum' => 'rhum',
- 'Richelieus' => 'richelieus',
- 'Ring Fit Adventure' => 'ring-fit-adventure',
- 'Risk' => 'risk',
- 'Robes &amp; jupes' => 'robes-et-jupes',
- 'Roborock' => 'roborock',
- 'Roborock S5 MAX' => 'roborock-s5-max',
- 'Roborock S6' => 'roborock-s6',
- 'Robots cuiseurs' => 'robots-cuiseurs',
- 'Robots ménagers' => 'robots-menagers',
- 'Robot tondeuse' => 'robot-tondeuse',
- 'ROCCAT' => 'roccat',
- 'Rollers' => 'rollers',
- 'Rouges à lèvres' => 'rouges-a-levres',
- 'Routeurs' => 'routeurs',
- 'Rowenta' => 'rowenta',
- 'Royal Canin' => 'royal-canin',
- 'RTX 2060' => 'rtx-2060',
- 'RTX 2070' => 'rtx-2070',
- 'RTX 2080' => 'rtx-2080',
- 'RTX 2080 Ti' => 'rtx-2080-ti',
- 'RTX 3070' => 'rtx-3070',
- 'RTX 3080' => 'rtx-3080',
- 'RTX 3090' => 'rtx-3090',
- 'RX 480' => 'rx-480',
- 'RX 580' => 'rx-580',
- 'RX 590' => 'radeon-rx-590',
- 'RX Vega 56' => 'rx-vega-56',
- 'RX Vega 64' => 'rx-vega-64',
- 'Sacs à déjections' => 'sacs-a-dejections',
- 'Sacs à dos' => 'sacs-a-dos',
- 'Sacs à langer' => 'sacs-a-langer',
- 'Sacs à main' => 'sacs-a-main',
- 'Sacs bandoulière' => 'sacs-bandouliere',
- 'Sacs de couchage' => 'sacs-de-couchage',
- 'Sacs de randonnée' => 'sacs-de-randonnee',
- 'Sacs de sport' => 'sacs-de-sport',
- 'Sacs de voyage' => 'sacs-de-voyage',
- 'Salle à manger' => 'salle-manger',
- 'Samsonite' => 'samsonite',
- 'Samsung' => 'samsung',
- 'Samsung Galaxy A5' => 'samsung-galaxy-a5',
- 'Samsung Galaxy A50' => 'samsung-galaxy-a50',
- 'Samsung Galaxy A51' => 'samsung-galaxy-a51',
- 'Samsung Galaxy A51 5G' => 'samsung-galaxy-a51-5g',
- 'Samsung Galaxy A70' => 'samsung-galaxy-a70',
- 'Samsung Galaxy A80' => 'samsung-galaxy-a80',
- 'Samsung Galaxy Buds' => 'samsung-galaxy-buds',
- 'Samsung Galaxy Buds+' => 'samsung-galaxy-buds-plus',
- 'Samsung Galaxy Buds Live' => 'samsung-galaxy-buds-live',
- 'Samsung Galaxy Buds Pro' => 'samsung-galaxy-buds-pro',
- 'Samsung Galaxy Fold' => 'samsung-galaxy-fold',
- 'Samsung Galaxy Note 8' => 'samsung-galaxy-note-8',
- 'Samsung Galaxy Note 9' => 'samsung-galaxy-note-9',
- 'Samsung Galaxy Note 10' => 'samsung-galaxy-note-10',
- 'Samsung Galaxy Note 10 Lite' => 'samsung-galaxy-note-10-lite',
- 'Samsung Galaxy Note 10 Plus' => 'samsung-galaxy-note-10-plus',
- 'Samsung Galaxy Note20' => 'samsung-galaxy-note-20',
- 'Samsung Galaxy Note20 Ultra' => 'samsung-galaxy-note-20-ultra',
- 'Samsung Galaxy S7' => 'samsung-galaxy-s7',
- 'Samsung Galaxy S7 Edge' => 'samsung-galaxy-s7-edge',
- 'Samsung Galaxy S8' => 'samsung-galaxy-s8',
- 'Samsung Galaxy S8+' => 'samsung-galaxy-s8plus',
- 'Samsung Galaxy S9' => 'samsung-galaxy-s9',
- 'Samsung Galaxy S9 Plus' => 'samsung-galaxy-s9-plus',
- 'Samsung Galaxy S10' => 'samsung-galaxy-s10',
- 'Samsung Galaxy S10 Lite' => 'samsung-galaxy-s10-lite',
- 'Samsung Galaxy S10+' => 'samsung-galaxy-s10-plus',
- 'Samsung Galaxy S10e' => 'samsung-galaxy-s10e',
- 'Samsung Galaxy S20' => 'samsung-galaxy-s20',
- 'Samsung Galaxy S20 FE' => 'samsung-galaxy-s20-fe',
- 'Samsung Galaxy S20 Ultra' => 'samsung-galaxy-s20-ultra',
- 'Samsung Galaxy S20+' => 'samsung-galaxy-s20-plus',
- 'Samsung Galaxy S21 5G' => 'samsung-galaxy-s21-5g',
- 'Samsung Galaxy S21 Ultra 5G' => 'samsung-galaxy-s21-ultra-5g',
- 'Samsung Galaxy S21+ 5G' => 'samsung-galaxy-s21-plus-5g',
- 'Samsung Galaxy Tab A' => 'samsung-galaxy-tab-a',
- 'Samsung Galaxy Tab S2' => 'samsung-galaxy-tab-s2',
- 'Samsung Galaxy Tab S3' => 'samsung-galaxy-tab-s3',
- 'Samsung Galaxy Tab S4' => 'samsung-galaxy-tab-s4',
- 'Samsung Galaxy Tab S5e' => 'samsung-galaxy-tab-s5e',
- 'Samsung Galaxy Tab S6' => 'samsung-galaxy-tab-s6',
- 'Samsung Galaxy Tab S7' => 'samsung-galaxy-tab-s7',
- 'Samsung Galaxy Watch' => 'samsung-galaxy-watch',
- 'Samsung Galaxy Watch3' => 'samsung-galaxy-watch-3',
- 'Samsung Galaxy Watch Active 2' => 'samsung-galaxy-watch-active2',
- 'Samsung Galaxy Z Flip' => 'galaxy-z-flip',
- 'Samsung Gear' => 'samsung-gear',
- 'Samsung Gear S3' => 'samsung-gear-s3',
- 'Samsung Gear VR' => 'samsung-gear-vr',
- 'Sandales' => 'sandales',
- 'SanDisk' => 'sandisk',
- 'Sanitaires et robinetterie' => 'sanitaires-robinetterie',
- 'Santé &amp; Cosmétiques' => 'sante-et-cosmetiques',
- 'Sapins de Noël' => 'sapins-noel',
- 'Savons' => 'savons',
- 'Scanners' => 'scanners',
- 'Scanners A3' => 'scanners-a3',
- 'Scanners A4' => 'scanners-a4',
- 'Scies' => 'scies',
- 'Scooters' => 'scooters',
- 'Seagate' => 'seagate',
- 'Sécateurs' => 'secateurs',
- 'Sèche-cheveux' => 'seche-cheveux',
- 'Sèche-linge' => 'seche-linge',
- 'Seiko' => 'seiko',
- 'Séjours' => 'sejours',
- 'Sekiro: Shadows Die Twice' => 'sekiro',
- 'Semis &amp; graines' => 'semis-et-graines',
- 'Sennheiser' => 'sennheiser',
- 'Senseo' => 'senseo',
- 'Séries TV' => 'series-tv',
- 'Service &amp; réparation auto-moto' => 'service-reparation-auto-moto',
- 'Services' => 'services-divers',
- 'Services auto' => 'services-auto',
- 'Services de livraison' => 'services-livraisons',
- 'Services moto' => 'services-moto',
- 'Services photo' => 'services-photo',
- 'Serviettes' => 'serviettes',
- 'Serviettes hygiéniques' => 'serviettes-hygieniques',
- 'Sextoys' => 'sextoys',
- 'Shadow of the Colossus' => 'shadow-of-the-colossus',
- 'Shadow of the Tomb Raider' => 'shadow-tomb-raider',
- 'Shalimar' => 'shalimar',
- 'Shampooings &amp; soins' => 'shampooings-et-soins',
- 'Shenmue' => 'shenmue',
- 'Shenmue I &amp; II' => 'shenmue-i-ii',
- 'Shenmue III' => 'shenmue-iii',
- 'Shorts' => 'shorts',
- 'Shorts de bain' => 'shorts-de-bain',
- 'Sièges auto' => 'sieges-auto',
- 'Siemens' => 'siemens',
- 'Skates &amp; longboards' => 'skates-et-longboards',
- 'Skechers' => 'sketchers',
- 'Ski' => 'ski',
- 'Skyrim' => 'skyrim',
- 'Slips &amp; boxers' => 'slips-et-boxers',
- 'Smartphones' => 'smartphones',
- 'Smartphones à moins de 100€' => 'smartphones-moins-de-100',
- 'Smartphones à moins de 200€' => 'smartphones-moins-de-200',
- 'Smartphones Android' => 'smartphones-android',
- 'Smartphones Asus' => 'smartphones-asus',
- 'Smartphones Google' => 'smartphones-google',
- 'Smartphones Honor' => 'smartphones-honor',
- 'Smartphones HTC' => 'smartphones-htc',
- 'Smartphones Huawei' => 'smartphones-huawei',
- 'Smartphones Lenovo Motorola' => 'smartphones-lenovo-motorola',
- 'Smartphones LG' => 'smartphones-lg',
- 'Smartphones Nokia' => 'smartphones-nokia',
- 'Smartphones OnePlus' => 'smartphones-oneplus',
- 'Smartphones Oppo' => 'smartphones-oppo',
- 'Smartphones Realme' => 'smartphones-realme',
- 'Smartphones Samsung' => 'smartphones-samsung',
- 'Smartphones Sony' => 'smartphones-sony',
- 'Smartphones Xiaomi' => 'smartphones-xiaomi',
- 'Smartphones ZTE' => 'smartphones-zte',
- 'Smart TV' => 'smart-tv',
- 'Sneakers' => 'sneakers',
- 'SodaStream' => 'sodastream',
- 'Sofas gonflable' => 'sofas-gonflable',
- 'Soin barbe et rasage' => 'soin-barbe-rasage',
- 'Soin de la peau' => 'soin-peau',
- 'Soin des cheveux' => 'soin-des-cheveux',
- 'Soin des ongles' => 'soin-ongles',
- 'Soins dentaires' => 'soins-dentaires',
- 'Sonos' => 'sonos',
- 'Sonos Beam' => 'sonos-beam',
- 'Sonos Move' => 'sonos-move',
- 'Sonos One' => 'sonos-one',
- 'Sonos PLAY:1' => 'sonos-play-1',
- 'Sonos PLAY:3' => 'sonos-play-3',
- 'Sonos PLAY:5' => 'sonos-play-5',
- 'Sonos PLAYBAR' => 'sonos-playbar',
- 'Sony' => 'sony',
- 'Sony PlayStation VR' => 'sony-playstation-vr',
- 'Sony Pulse 3D sans fil' => 'casque-audio-sony-pulse-3d',
- 'Sony WF-1000XM3' => 'sony-wf-1000xm3',
- 'Sony WH-1000XM3' => 'sony-wh-1000xm3',
- 'Sony WH-1000XM4' => 'sony-wh-1000xm4',
- 'Sony Xperia XA1' => 'sony-xperia-xa1',
- 'Sony Xperia X Compact' => 'sony-xperia-x-compact',
- 'Sony Xperia XZ1' => 'sony-xperia-xz1',
- 'Sony Xperia XZ1 Compact' => 'sony-xperia-xz1-compact',
- 'Sony Xperia XZ Premium' => 'sony-xperia-xz-premium',
- 'Sony Xperia Z3' => 'sony-xperia-z3',
- 'Soulcalibur' => 'soulcalibur',
- 'Souris' => 'souris',
- 'Souris gamer' => 'souris-gamer',
- 'Souris Logitech' => 'souris-logitech',
- 'Souris sans fil' => 'souris-sans-fil',
- 'Sous-vêtements' => 'sous-vetements',
- 'Sous-vêtements de sport' => 'sous-vetements-de-sport',
- 'South Park' => 'south-park',
- 'Soutiens-gorge' => 'soutiens-gorge',
- 'Spas' => 'spa',
- 'Spectacles' => 'spectacles',
- 'Spectacles &amp; Billetterie' => 'sorties',
- 'Spectacles comiques' => 'spectacles-comiques',
- 'Spectacles pour enfants' => 'spectacles-pour-enfants',
- 'Sports &amp; plein air' => 'sports-plein-air',
- 'Sports collectifs' => 'sports-collectifs',
- 'Sports nautiques' => 'sports-nautiques',
- 'Sportswear' => 'sportswear',
- 'Spotify' => 'spotify',
- 'SSD' => 'ssd',
- 'Star Wars: Jedi Fallen Order' => 'star-wars-jedi-fallen-order',
- 'Star Wars: Squadrons' => 'star-wars-squadrons',
- 'Star Wars Battlefront' => 'star-wars-battlefront',
- 'Stations météo' => 'stations-meteo',
- 'Stickers muraux' => 'stickers-muraux',
- 'Stihl' => 'stihl',
- 'Stockage externe' => 'stockage',
- 'Streaming' => 'streaming',
- 'Streaming musical' => 'streaming-musical',
- 'Streaming vidéo' => 'streaming-video',
- 'Stylos' => 'stylos',
- 'Sucettes' => 'sucettes',
- 'Super Mario' => 'super-mario',
- 'Super Mario 3D All-Stars' => 'super-mario-3d-all-stars',
- 'Super Mario Maker 2' => 'super-mario-maker-2',
- 'Super Mario Party' => 'super-mario-party',
- 'Super Smash Bros. Ultimate' => 'super-smash-bros-ultimate',
- 'Support GPS &amp; smartphone' => 'support-gps-et-smartphone',
- 'Supports TV' => 'supports-tv',
- 'Surface Pro 4' => 'surface-pro-4',
- 'Surgelés' => 'surgeles',
- 'Surveillance' => 'surveillance',
- 'Suspensions' => 'suspensions',
- 'Swatch' => 'swatch',
- 'Switch réseau' => 'switch-reseau',
- 'Systèmes d&#039;exploitation' => 'systemes-d-exploitation',
- 'Systèmes multiroom' => 'systemes-multiroom',
- 'T-shirts' => 't-shirts',
- 'Tables' => 'tables',
- 'Tables à langer' => 'tables-a-langer',
- 'Tables à repasser' => 'tables-a-repasser',
- 'Tables basses' => 'tables-basses',
- 'Tables de camping' => 'tables-de-camping',
- 'Tables de mixage' => 'tables-de-mixage',
- 'Tables de ping-pong' => 'tables-ping-pong',
- 'Tablettes' => 'tablettes',
- 'Tablettes graphiques' => 'tablettes-graphiques',
- 'Tablettes graphiques Huion' => 'huion',
- 'Tablettes graphiques Wacom' => 'wacom',
- 'Tablettes Huawei' => 'tablettes-huawei',
- 'Tablettes Lenovo' => 'tablettes-lenovo',
- 'Tablettes Microsoft Surface' => 'tablettes-microsoft-surface',
- 'Tablettes Samsung' => 'tablettes-samsung',
- 'Tablettes Xiaomi' => 'tablettes-xiaomi',
- 'Tampons' => 'tampons',
- 'Tapis' => 'tapis',
- 'Tapis de souris' => 'tapis-de-souris',
- 'Tassimo' => 'tassimo',
- 'Taxis' => 'taxis',
- 'Tefal' => 'tefal',
- 'Tekken' => 'tekken',
- 'Tekken 7' => 'tekken-7',
- 'Télécommandes' => 'telecommandes',
- 'Téléphones fixes' => 'telephones-fixes',
- 'Téléphonie' => 'telephonie',
- 'Téléviseurs' => 'televiseurs',
- 'Tentes' => 'tentes',
- 'Tentes Quechua' => 'tentes-quechua',
- 'Têtes de brosse à dents de rechange' => 'tetes-de-brosse-a-dents-de-rechange',
- 'Théâtre' => 'theatre',
- 'The Last of Us' => 'the-last-of-us',
- 'The Last of Us Part II' => 'the-last-of-us-part-2',
- 'The Legend of Zelda' => 'the-legend-of-zelda',
- 'The Legend of Zelda: Breath of the Wild' => 'zelda-breath-of-the-wild',
- 'The Legend of Zelda: Link&#039;s Awakening' => 'legend-of-zelda-link-s-awakening',
- 'The Legend of Zelda: Skyward Sword HD' => 'the-legend-of-zelda-skyward-sword-hd',
- 'Thermomètres' => 'thermometres',
- 'Thermomix' => 'thermomix',
- 'Thermostats connectés' => 'thermostat-connecte',
- 'Thés' => 'thes',
- 'Thés glacés' => 'thes-glaces',
- 'The Walking dead' => 'the-walking-dead',
- 'The Witcher' => 'the-witcher',
- 'The Witcher 3' => 'the-witcher-3',
- 'Time&#039;s Up!' => 'time-s-up',
- 'Tokyo Laundry' => 'tokyo-laundry',
- 'Tomb Raider' => 'tomb-raider',
- 'Tom Clancy&#039;s' => 'tom-clancy-s',
- 'Tom Clancy&#039;s Ghost Recon: Wildlands' => 'tom-clancy-s-ghost-recon-wildlands',
- 'Tom Clancy&#039;s Ghost Recon Breakpoint' => 'tom-clancy-s-ghost-recon-breakpoint',
- 'Tom Clancy&#039;s The Division' => 'tom-clancy-s-the-division',
- 'TomTom' => 'tomtom',
- 'Tondeuses' => 'tondeuses',
- 'Tondeuses à gazon' => 'tondeuses-a-gazon',
- 'Toner' => 'toner',
- 'Tongs' => 'tongs',
- 'Torchons' => 'torchons',
- 'Toshiba' => 'toshiba',
- 'Total War' => 'total-war',
- 'Total War: Warhammer' => 'total-war-warhammer',
- 'Total War: Warhammer II' => 'total-war-warhammer-ii',
- 'Tournevis' => 'tournevis-et-visseuses',
- 'TP-Link' => 'tp-link',
- 'Trains &amp; Bus' => 'trains-bus',
- 'Trampolines' => 'trampolines',
- 'Transats &amp; cosys' => 'transats-et-cosys',
- 'Transport bébé' => 'poussettes',
- 'Transport d&#039;animaux' => 'transport-d-animaux',
- 'Transports en commun' => 'transports-en-commun',
- 'Transports urbains' => 'transports-urbains',
- 'Travaux &amp; matériaux' => 'travaux-materiaux',
- 'Trépieds' => 'trepieds',
- 'Trixie' => 'trixie',
- 'Tronçonneuses' => 'tronconneuses',
- 'Tropico' => 'tropico',
- 'Tropico 6' => 'tropico-6',
- 'Trottinettes' => 'trottinettes',
- 'Trottinettes électriques' => 'trottinettes-electriques',
- 'Trottinettes électriques en libre-service' => 'location-trottinettes-electriques',
- 'Trottinettes Xiaomi' => 'trottinettes-xiaomi',
- 'TV &amp; Vidéo' => 'tv-video',
- 'TV 4K' => 'tv-4k',
- 'TV 40&#039;&#039; à 64&#039;&#039;' => 'tv-40-pouces-a-64-pouces',
- 'TV 65&#039;&#039; et plus' => 'tv-65-pouces-et-plus',
- 'TV Hisense' => 'tv-hisense',
- 'TV LG' => 'tv-lg',
- 'TV OLED' => 'tv-oled',
- 'TV Panasonic' => 'tv-panasonic',
- 'TV Philips' => 'tv-philips',
- 'TV Samsung' => 'tv-samsung',
- 'TV Samsung QLED' => 'tv-samsung-qled',
- 'TV Samsung The Frame' => 'tv-samsung-the-frame',
- 'TV Sony' => 'tv-sony',
- 'TV TCL' => 'tv-tcl',
- 'TV Toshiba' => 'tv-toshiba',
- 'TV Xiaomi' => 'tv-xiaomi',
- 'UE Boom 2' => 'ue-boom-2',
- 'UE Boom 3' => 'ue-boom-3',
- 'UE Megaboom' => 'ue-megaboom',
- 'UE Megaboom 3' => 'ue-megaboom-3',
- 'UE Wonderboom' => 'ue-wonderboom',
- 'Ultraportables' => 'ultraportables',
- 'Uncharted' => 'uncharted',
- 'Uncharted 4' => 'uncharted-4',
- 'Uncharted: The Lost Legacy' => 'uncharted-the-lost-legacy',
- 'Under Armour' => 'under-armour',
- 'Until Dawn' => 'until-dawn',
- 'Ustensiles de cuisine' => 'ustensiles-de-cuisine',
- 'Ustensiles de cuisson' => 'ustensiles-de-cuisson',
- 'Vacances et séjours' => 'vacances-sejours',
- 'Vaisselle' => 'vaisselle',
- 'Valises' => 'valises',
- 'Valises cabine' => 'valises-cabine',
- 'Valises rigides' => 'valises-rigides',
- 'Vans Old Skool' => 'vans-old-skool',
- 'Variétés &amp; revues' => 'varietes-et-revues',
- 'Vases' => 'vases',
- 'Veet' => 'veet',
- 'Veilleuses' => 'veilleuses',
- 'Vélos' => 'velos',
- 'Vélos d&#039;appartement' => 'velos-d-appartement',
- 'Vélos électriques' => 'velos-electriques',
- 'Ventilateurs' => 'ventilateurs',
- 'Ventirad' => 'ventirad',
- 'Vernis à ongles' => 'vernis-a-ongles',
- 'Verres' => 'verres',
- 'Vestes' => 'vestes',
- 'Vestes polaires' => 'vestes-polaires',
- 'Vêtements d&#039;été' => 'vetements-d-ete',
- 'Vêtements d&#039;hiver' => 'vetements-d-hiver',
- 'Vêtements de grossesse' => 'vetements-de-grossesse',
- 'Vêtements de montagne' => 'vetements-techniques',
- 'Vêtements de running' => 'vetements-de-running',
- 'Vêtements de ski' => 'vetements-de-ski',
- 'Vêtements de sport' => 'vetements-de-sport',
- 'Vêtements pour bébé' => 'vetements-pour-bebe',
- 'Vidéoprojecteurs' => 'projecteurs',
- 'Vidéoprojecteurs 3D' => 'videoprojecteurs-3d',
- 'Vidéoprojecteurs Acer' => 'videoprojecteurs-acer',
- 'Vidéoprojecteurs BenQ' => 'videoprojecteurs-benq',
- 'Vidéoprojecteurs Epson' => 'videoprojecteurs-epson',
- 'Vidéoprojecteurs HD' => 'videoprojecteurs-hd',
- 'Vidéoprojecteurs LG' => 'videoprojecteurs-lg',
- 'Vidéoprojecteurs Optoma' => 'videoprojecteurs-optoma',
- 'Vins' => 'vins',
- 'Visites &amp; patrimoine' => 'visites-et-patrimoine',
- 'Visseuses' => 'visseuses',
- 'VOD' => 'vod',
- 'Voitures &amp; motos' => 'voitures-motos',
- 'Voitures télécommandées' => 'voitures-telecommandees',
- 'Volants' => 'volants-de-course',
- 'Vols' => 'billets-d-avion',
- 'Voyages' => 'voyages',
- 'Voyages &amp; loisirs' => 'le-laboratoire-des-voyages-loisirs',
- 'VPN' => 'vpn',
- 'VTC' => 'vtc',
- 'VTT' => 'vtt',
- 'Wacom Cintiq' => 'cintiq',
- 'Watch Dogs' => 'watch-dogs',
- 'Watch Dogs 2' => 'watch-dogs-2',
- 'Watch Dogs: Legion' => 'watch-dogs-legion',
- 'Watercooling' => 'watercooling',
- 'WD (Western Digital)' => 'western-digital',
- 'Wearables' => 'wearables',
- 'Webcams' => 'webcams',
- 'Whey' => 'whey',
- 'Whirlpool' => 'whirlpool',
- 'Whiskas' => 'whiskas',
- 'Whisky' => 'whisky',
- 'Wiko' => 'wiko',
- 'Wilkinson Sword Hydro 5' => 'wilkinson-sword-hydro-5',
- 'Windows' => 'windows',
- 'WindScribe' => 'windscribe',
- 'Wolfenstein' => 'wolfenstein',
- 'Wolfenstein II: The New Colossus' => 'wolfenstein-ii-the-new-colossus',
- 'Xbox' => 'xbox',
- 'Xbox Game Pass' => 'xbox-game-pass',
- 'Xbox Live' => 'xbox-live',
- 'XCOM' => 'xcom',
- 'XCOM 2' => 'xcom-2',
- 'Xiaomi' => 'xiaomi',
- 'Xiaomi AirDots' => 'xiaomi-airdots',
- 'Xiaomi Black Shark' => 'xiaomi-black-shark',
- 'Xiaomi Black Shark 2' => 'xiaomi-black-shark-2',
- 'Xiaomi Mi6' => 'xiaomi-mi6',
- 'Xiaomi Mi8' => 'xiaomi-mi8',
- 'Xiaomi Mi8 Lite' => 'xiaomi-mi8-lite',
- 'Xiaomi Mi8 Pro' => 'xiaomi-mi8-pro',
- 'Xiaomi Mi8 SE' => 'xoaimi-mi8-se',
- 'Xiaomi Mi9' => 'xiaomi-mi9',
- 'Xiaomi Mi 9 Lite' => 'xiaomi-mi-9-lite',
- 'Xiaomi Mi 9 Pro' => 'xiaomi-mi-9-pro',
- 'Xiaomi Mi 9 SE' => 'xiaomi-mi-9-se',
- 'Xiaomi Mi 9T' => 'xiaomi-mi-9t',
- 'Xiaomi Mi 9T Pro' => 'xiaomi-mi-9t-pro',
- 'Xiaomi Mi 10' => 'xiaomi-mi-10',
- 'Xiaomi Mi 10 Lite' => 'xiaomi-mi-10-lite',
- 'Xiaomi Mi 10 Pro' => 'xiaomi-mi-10-pro',
- 'Xiaomi Mi 10T' => 'xiaomi-mi-10t',
- 'Xiaomi Mi 10T Lite' => 'xiaomi-mi-10t-lite',
- 'Xiaomi Mi 10T Pro' => 'xiaomi-mi-10t-pro',
- 'Xiaomi Mi 11' => 'xiaomi-mi-11',
- 'Xiaomi Mi 11 Lite' => 'xiaomi-mi-11-lite',
- 'Xiaomi Mi A1' => 'xiaomi-mi-a1',
- 'Xiaomi Mi A2' => 'xiaomi-mi-a2',
- 'Xiaomi Mi A2 Lite' => 'xiaomi-mi-a2-lite',
- 'Xiaomi Mi Airdots Pro' => 'xiaomi-mi-airdots-pro',
- 'Xiaomi Mi Band' => 'xiaomi-mi-band',
- 'Xiaomi Mi Band 4' => 'xiaomi-mi-band-4',
- 'Xiaomi Mi Band 5' => 'xiaomi-mi-band-5',
- 'Xiaomi Mi Band 6' => 'xiaomi-mi-band-6',
- 'Xiaomi Mi Box' => 'xiaomi-mi-box',
- 'Xiaomi Mi Electric Scooter M365' => 'xiaomi-mi-electric-scooter-m365',
- 'Xiaomi Mi Max' => 'xiaomi-mi-max',
- 'Xiaomi Mi Mix' => 'xiaomi-mi-mix',
- 'Xiaomi Mi Mix 2' => 'xiaomi-mi-mix-2',
- 'Xiaomi Mi Note 10' => 'xiaomi-mi-note-10',
- 'Xiaomi Mi Note 10 Pro' => 'xiaomi-mi-note-10-pro',
- 'Xiaomi Mi Pad 3' => 'xiaomi-mi-pad-3',
- 'Xiaomi Mi Watch' => 'xiaomi-mi-watch',
- 'Xiaomi Pocophone F1' => 'xiaomi-pocophone-f1',
- 'Xiaomi Redmi 4A' => 'xiaomi-redmi-4a',
- 'Xiaomi Redmi 4X' => 'xiaomi-redmi-4x',
- 'Xiaomi Redmi 7' => 'xiaomi-redmi-7',
- 'Xiaomi Redmi 9' => 'xiaomi-redmi-9',
- 'Xiaomi Redmi AirDots' => 'xiaomi-redmi-airdots',
- 'Xiaomi Redmi Note 4' => 'xiaomi-redmi-note-4',
- 'Xiaomi Redmi Note 5' => 'xiaomi-redmi-note-5',
- 'Xiaomi Redmi Note 6' => 'xiaomi-redmi-note-6',
- 'Xiaomi Redmi Note 7' => 'xiaomi-redmi-note-7',
- 'Xiaomi Redmi Note 8' => 'xiaomi-redmi-note-8',
- 'Xiaomi Redmi Note 8 Pro' => 'xiaomi-redmi-note-8-pro',
- 'Xiaomi Redmi Note 9' => 'xiaomi-redmi-note-9',
- 'Xiaomi Redmi Note 9 Pro' => 'xiaomi-redmi-note-9-pro',
- 'Xiaomi Redmi Note 9S' => 'xiaomi-redmi-note-9s',
- 'Xiaomi Redmi Note 10' => 'xiaomi-redmi-note-10',
- 'Xiaomi Redmi Note 10 Pro' => 'xiaomi-redmi-10-pro',
- 'Xiaomi Smart Home' => 'xiaomi-smart-home',
- 'Yamaha' => 'yamaha',
- 'Yeelight' => 'xiaomi-yeelight',
- 'Yoshi&#039;s Crafted World' => 'yoshi-crafted-world',
- 'Zoos' => 'zoos',
- ]
- ],
+ 'type' => 'text',
+ 'exampleValue' => 'abonnements-internet',
+ 'title' => 'Nom du groupe dans l\'URL : Il faut entrer le nom du groupe qui est présent après "https://www.dealabs.com/groupe/" et avant tout éventuel "?"
+Exemple : Si l\'URL du groupe affichées dans le navigateur est :
+https://www.dealabs.com/groupe/abonnements-internet?sortBy=lowest_price
+Il faut alors saisir :
+abonnements-internet',
+ ],
+ 'subgroups' => [
+ 'name' => 'Catégorie',
+ 'type' => 'text',
+ 'exampleValue' => '1071',
+ 'title' => 'Numéro du ou des catégories dans l\'URL : Il faut entrer le ou les numéros de catégories qui sont présent après "groups=" et avant tout éventuel "&"
+Exemple : Si l\'URL du groupe affichées dans le navigateur est :
+https://www.dealabs.com/groupe/telecommunications?groups=1071%2C1070&sortBy=new
+Il faut alors saisir :
+1071%2C1070',
+ ],
'order' => [
'name' => 'Trier par',
'type' => 'list',
@@ -1911,6 +98,7 @@ class DealabsBridge extends PepperBridgeAbstract
'uri-group' => 'groupe/',
'uri-deal' => 'bons-plans/',
'uri-merchant' => 'search/bons-plans?merchant-id=',
+ 'image-host' => 'https://static-pepper.dealabs.com/',
'request-error' => 'Impossible de joindre Dealabs',
'thread-error' => 'Impossible de déterminer l\'ID de la discussion. Vérifiez l\'URL que vous avez entré',
'currency' => '€',
@@ -1923,5 +111,7 @@ class DealabsBridge extends PepperBridgeAbstract
'title-talk' => 'Surveillance Discussion',
'deal-type' => 'Type de deal',
'localdeal' => 'Deal Local',
+ 'context-hot' => '-hot',
+ 'context-new' => '-nouveaux',
];
}
diff --git a/bridges/DonnonsBridge.php b/bridges/DonnonsBridge.php
index a33a1013..1afdc4f2 100644
--- a/bridges/DonnonsBridge.php
+++ b/bridges/DonnonsBridge.php
@@ -1,5 +1,7 @@
<?php
+declare(strict_types=1);
+
/**
* Retourne les dons d'une recherche filtrée sur le site Donnons.org
* Example: https://donnons.org/Sport/Ile-de-France
@@ -44,58 +46,60 @@ class DonnonsBridge extends BridgeAbstract
{
$uri = $this->getPageURI($page);
- $html = getSimpleHTMLDOM($uri);
-
- $searchDiv = $html->find('div[id=search]', 0);
-
- if (!is_null($searchDiv)) {
- $elements = $searchDiv->find('a.lst-annonce');
- foreach ($elements as $element) {
- $item = [];
-
- // Lien vers le don
- $item['uri'] = self::URI . $element->href;
- // Id de l'objet
- $item['uid'] = $element->getAttribute('data-id');
-
- // Grab info from json
- $jsonString = $element->find('script', 0)->innertext;
- $json = json_decode($jsonString, true);
-
- $name = $json['name'];
- $category = $json['category'];
- $date = $json['availabilityStarts'];
- $description = $json['description'];
- $city = $json['availableAtOrFrom']['address']['addressLocality'];
- $region = $json['availableAtOrFrom']['address']['addressRegion'];
-
- // Grab info from HTML
- $imageSrc = $element->find('img.ima-center', 0)->getAttribute('src');
- // Use large image instead of small one
- $imageSrc = str_replace('/xs/', '/lg/', $imageSrc);
- $image = self::URI . $imageSrc;
- $author = $element->find('div.avatar-holder', 0)->plaintext;
-
- $content = '
- <img style="margin-right:1em;" src="' . $image . '">
- <div>
- <h1>' . $name . '</h1>
- <p>' . $description . '</p>
- <p>Lieu : <b>' . $city . '</b> - ' . $region . '</p>
- <p>Par : ' . $author . '</p>
- <p>Date : ' . $date . '</p>
- </div>
- ';
-
- // Titre du don
- $item['title'] = '[' . $category . '] ' . $name;
- $item['timestamp'] = $date;
- $item['author'] = $author;
- $item['content'] = $content;
- $item['enclosures'] = [$image];
-
- $this->items[] = $item;
- }
+ $dom = getSimpleHTMLDOM($uri);
+
+ $searchDiv = $dom->find('div[id=search]', 0);
+
+ if (! $searchDiv) {
+ return;
+ }
+
+ $elements = $searchDiv->find('a.lst-annonce');
+ foreach ($elements as $element) {
+ $item = [];
+
+ // Lien vers le don
+ $item['uri'] = self::URI . $element->href;
+ // Id de l'objet
+ $item['uid'] = $element->getAttribute('data-id');
+
+ // Grab info from json
+ $jsonString = $element->find('script', 0)->innertext;
+ $json = json_decode($jsonString, true);
+
+ $name = $json['name'];
+ $category = $json['category'];
+ $date = $json['availabilityStarts'];
+ $description = $json['description'];
+ $city = $json['availableAtOrFrom']['address']['addressLocality'];
+ $region = $json['availableAtOrFrom']['address']['addressRegion'];
+
+ // Grab info from HTML
+ $imageSrc = $element->find('img.ima-center', 0)->getAttribute('src');
+ // Use large image instead of small one
+ $imageSrc = str_replace('/xs/', '/lg/', $imageSrc);
+ $image = self::URI . $imageSrc;
+ $author = $element->find('div.avatar-holder', 0)->plaintext;
+
+ $content = '
+ <img style="margin-right:1em;" src="' . $image . '">
+ <div>
+ <h1>' . $name . '</h1>
+ <p>' . $description . '</p>
+ <p>Lieu : <b>' . $city . '</b> - ' . $region . '</p>
+ <p>Par : ' . $author . '</p>
+ <p>Date : ' . $date . '</p>
+ </div>
+ ';
+
+ // Titre du don
+ $item['title'] = '[' . $category . '] ' . $name;
+ $item['timestamp'] = $date;
+ $item['author'] = $author;
+ $item['content'] = $content;
+ $item['enclosures'] = [$image];
+
+ $this->items[] = $item;
}
}
diff --git a/bridges/EconomistWorldInBriefBridge.php b/bridges/EconomistWorldInBriefBridge.php
index 72b66198..3b717d81 100644
--- a/bridges/EconomistWorldInBriefBridge.php
+++ b/bridges/EconomistWorldInBriefBridge.php
@@ -41,6 +41,12 @@ class EconomistWorldInBriefBridge extends BridgeAbstract
'quote' => [
'name' => 'Include the quote of the day',
'type' => 'checkbox'
+ ],
+ 'mergeEverything' => [
+ 'name' => 'Merge everything into one entry',
+ 'type' => 'checkbox',
+ 'defaultValue' => false,
+ 'title' => 'Whether to merge all the stories into one entry'
]
]
];
@@ -61,7 +67,7 @@ class EconomistWorldInBriefBridge extends BridgeAbstract
}
$html = getSimpleHTMLDOM(self::URI, $headers);
$gobbets = $html->find('p[data-component="the-world-in-brief-paragraph"]');
- if ($this->getInput('splitGobbets') == 1) {
+ if ($this->getInput('splitGobbets') == 1 && !$this->getInput('mergeEverything')) {
$this->splitGobbets($gobbets);
} else {
$this->mergeGobbets($gobbets);
@@ -77,6 +83,9 @@ class EconomistWorldInBriefBridge extends BridgeAbstract
$quote = $html->find('blockquote[data-test-id="inspirational-quote"]', 0);
$this->addQuote($quote);
}
+ if ($this->getInput('mergeEverything') == 1) {
+ $this->mergeEverything();
+ }
}
private function splitGobbets($gobbets)
@@ -131,6 +140,9 @@ class EconomistWorldInBriefBridge extends BridgeAbstract
if ($element->tag != 'div') {
continue;
}
+ if ($element->find('._newsletterContentPromo', 0) != null) {
+ continue;
+ }
$image = $element->find('figure', 0);
$title = $element->find('h3', 0)->plaintext;
$content = $element->find('h3', 0)->parent();
@@ -165,4 +177,35 @@ class EconomistWorldInBriefBridge extends BridgeAbstract
'uid' => 'quote-' . $today->format('U')
];
}
+
+ private function mergeEverything()
+ {
+ $today = new Datetime();
+ $today->setTime(0, 0, 0, 0);
+ $contents = '';
+
+ foreach ($this->items as $item) {
+ $header = null;
+ if (str_contains($item['uid'], 'story-')) {
+ $header = $item['title'];
+ } elseif (str_contains($item['uid'], 'quote-')) {
+ $header = 'Quote of the day';
+ } elseif (str_contains($item['uid'], 'world-in-brief-')) {
+ $header = 'World in brief';
+ }
+ if ($header != null) {
+ $contents .= "<h2>{$header}</h2>";
+ }
+ $contents .= $item['content'];
+ }
+
+ $item = [
+ 'uri' => self::URI,
+ 'title' => 'The Economist World in Brief ' . $today->format('d.m.Y'),
+ 'content' => $contents,
+ 'timestamp' => $today->format('U'),
+ 'uid' => 'world-in-brief-merged' . $today->format('U')
+ ];
+ $this->items = [$item];
+ }
}
diff --git a/bridges/EdfPricesBridge.php b/bridges/EdfPricesBridge.php
index f67ed30b..06cd30b7 100644
--- a/bridges/EdfPricesBridge.php
+++ b/bridges/EdfPricesBridge.php
@@ -12,8 +12,28 @@ class EdfPricesBridge extends BridgeAbstract
'contract' => [
'name' => 'Choisir un contrat',
'type' => 'list',
- // we can add later HCHP, EJP, base
- 'values' => ['Tempo' => '/energie/edf/tarifs/tempo'],
+ // we can add later more option prices
+ 'values' => [
+ 'Base' => '/energie/edf/tarifs/tarif-bleu#base',
+ 'HPHC' => '/energie/edf/tarifs/tarif-bleu#hphc',
+ 'EJP' => '/energie/edf/tarifs/tarif-bleu#ejp',
+ 'Tempo' => '/energie/edf/tarifs/tempo'
+ ],
+ ],
+ 'power' => [
+ 'name' => 'Choisir une puissance',
+ 'type' => 'list',
+ 'values' => [
+ '3 kVA' => 3,
+ '6 kVA' => 6,
+ '9 kVA' => 9,
+ '12 kVA' => 12,
+ '15 kVA' => 15,
+ '18 kVA' => 18,
+ '24 kVA' => 24,
+ '30 kVA' => 30,
+ '36 kVA' => 36
+ ]
]
]
];
@@ -24,36 +44,20 @@ class EdfPricesBridge extends BridgeAbstract
* @param string $contractUri
* @return void
*/
- private function tempo(simple_html_dom $html, string $contractUri): void
+ private function tempo(simple_html_dom $html, string $contractUri, int $power): void
{
- // current color and next
- $daysDom = $html->find('#calendrier', 0)->nextSibling()->find('.card--ejp');
- if ($daysDom && count($daysDom) === 2) {
- foreach ($daysDom as $dayDom) {
- $day = trim($dayDom->find('.card__title', 0)->innertext) . '/' . (new \DateTime('now'))->format(('Y'));
- $dayColor = $dayDom->find('.card-ejp__icon span', 0)->innertext;
-
- $text = $day . ' - ' . $dayColor;
- $item['uri'] = self::URI . $contractUri;
- $item['title'] = $text;
- $item['author'] = self::MAINTAINER;
- $item['content'] = $text;
- $item['uid'] = hash('sha256', $item['title']);
-
- $this->items[] = $item;
- }
- }
-
// colors
- $ulDom = $html->find('#tarif-de-l-offre-edf-tempo-current-date-html-year', 0)->nextSibling()->nextSibling()->nextSibling();
+ $ulDom = $html->find('#tarif-de-l-offre-tempo-edf-template-date-now-y', 0)->nextSibling()->nextSibling()->nextSibling();
$elementsDom = $ulDom->find('li');
if ($elementsDom && count($elementsDom) === 3) {
+ // price per kWh is same for all powers
foreach ($elementsDom as $elementDom) {
$item = [];
$matches = [];
- preg_match_all('/Jour (.*) : Heures (.*) : (.*) € \/ Heures (.*) : (.*) €/um', $elementDom->innertext, $matches, PREG_SET_ORDER, 0);
+ preg_match_all('/Jour (.*) : Heures (.*) : (.*)&nbsp;€ \/ Heures (.*) : (.*)&nbsp;€/um', $elementDom->innertext, $matches, PREG_SET_ORDER, 0);
+ // for tempo contract we have 2x3 colors
if ($matches && count($matches[0]) === 6) {
for ($i = 0; $i < 2; $i++) {
$text = 'Jour ' . $matches[0][1] . ' - Heures ' . $matches[0][2 + 2 * $i] . ' : ' . $matches[0][3 + 2 * $i] . '€';
@@ -69,26 +73,166 @@ class EdfPricesBridge extends BridgeAbstract
}
}
- // powers
- $ulPowerContract = $ulDom->nextSibling()->nextSibling();
- $elementsPowerContractDom = $ulPowerContract->find('li');
- if ($elementsPowerContractDom && count($elementsPowerContractDom) === 4) {
- foreach ($elementsPowerContractDom as $elementPowerContractDom) {
+ // add subscription power info
+ $tablePrices = $ulDom->nextSibling()->nextSibling()->nextSibling()->find('.table--responsive', 0);
+ $this->addSubscriptionPowerInfo($tablePrices, $contractUri, $power, 7);
+ }
+
+ /**
+ * @param simple_html_dom $html
+ * @param string $contractUri
+ * @return void
+ */
+ private function base(simple_html_dom $html, string $contractUri, int $power): void
+ {
+ $tablePrices = $html
+ ->find('#grille-tarifaire-et-prix-du-kwh-du-tarif-reglemente-edf-en-option-base', 0)
+ ->nextSibling()
+ ->nextSibling()
+ ->nextSibling();
+
+ $prices = $tablePrices->find('.table--stripped tbody tr');
+ // last element is useless because part of another table
+ array_pop($prices);
+
+ // price per kWh is same for all powers
+ if ($prices && count($prices) === 9) {
+ $item = [];
+
+ $text = 'Base : ' . $prices[0]->children(2);
+ $item['uri'] = self::URI . $contractUri;
+ $item['title'] = $text;
+ $item['author'] = self::MAINTAINER;
+ $item['content'] = $text;
+ $item['uid'] = hash('sha256', $item['title']);
+
+ $this->items[] = $item;
+ }
+
+ $this->addSubscriptionPowerInfo($tablePrices, $contractUri, $power, 9);
+ }
+
+ /**
+ * @param simple_html_dom $html
+ * @param string $contractUri
+ * @return void
+ */
+ private function hphc(simple_html_dom $html, string $contractUri, int $power): void
+ {
+ $tablePrices = $html
+ ->find('#grille-tarifaire-et-prix-du-kwh-du-tarif-reglemente-edf-en-option-heures-pleines-heures-creuses', 0)
+ ->nextSibling()
+ ->nextSibling()
+ ->nextSibling();
+
+ $prices = $tablePrices->find('.table--stripped tbody tr');
+ // last element is useless because part of another table
+ array_pop($prices);
+
+ // price per kWh is same for all powers
+ if ($prices && count($prices) === 8) {
+ $values = ['HC', 'HP'];
+ foreach ($values as $key => $value) {
+ $i++;
$item = [];
- $matches = [];
- preg_match_all('/(.*) kVA : (.*) €/um', $elementPowerContractDom->innertext, $matches, PREG_SET_ORDER, 0);
+ $text = $values[$key] . ' : ' . $prices[0]->children($key + 2);
+ $item['uri'] = self::URI . $contractUri;
+ $item['title'] = $text;
+ $item['author'] = self::MAINTAINER;
+ $item['content'] = $text;
+ $item['uid'] = hash('sha256', $item['title']);
+
+ $this->items[] = $item;
+ }
+ }
+
+ $this->addSubscriptionPowerInfo($tablePrices, $contractUri, $power, 8);
+ }
+
+ /**
+ * @param simple_html_dom $html
+ * @param string $contractUri
+ * @return void
+ */
+ private function ejp(simple_html_dom $html, string $contractUri, int $power): void
+ {
+ $tablePrices = $html
+ ->find('#grille-tarifaire-et-prix-du-kwh-du-tarif-reglemente-edf-en-option-ejp', 0)
+ ->nextSibling()
+ ->nextSibling()
+ ->nextSibling();
+
+ $prices = $tablePrices->find('.table--stripped tbody tr');
+ // last element is useless because part of another table
+ array_pop($prices);
- if ($matches && count($matches[0]) === 3) {
- $text = $matches[0][1] . ' kVA : ' . $matches[0][2] . '€';
- $item['uri'] = self::URI . $contractUri;
- $item['title'] = $text;
- $item['author'] = self::MAINTAINER;
- $item['content'] = $text;
- $item['uid'] = hash('sha256', $item['title']);
+ // price per kWh is same for all powers
+ if ($prices && count($prices) === 5) {
+ $values = ['Non EJP', 'EJP'];
+ foreach ($values as $key => $value) {
+ $i++;
+ $item = [];
+
+ $text = $values[$key] . ' : ' . $prices[0]->children($key + 2);
+ $item['uri'] = self::URI . $contractUri;
+ $item['title'] = $text;
+ $item['author'] = self::MAINTAINER;
+ $item['content'] = $text;
+ $item['uid'] = hash('sha256', $item['title']);
- $this->items[] = $item;
+ $this->items[] = $item;
+ }
+ }
+
+ $this->addSubscriptionPowerInfo($tablePrices, $contractUri, $power, 5);
+ }
+
+ private function addSubscriptionPowerInfo(simple_html_dom_node $tablePrices, string $contractUri, int $power, int $numberOfPrices): void
+ {
+ $prices = $tablePrices->find('.table--stripped tbody tr');
+ // last element is useless because part of another table
+ array_pop($prices);
+
+ // 7 contracts for tempo: 6, 9, 12, 15, 18, 30 and 36 kVA
+ // 9 contracts for base: 3, 6, 9, 12, 15, 18, 24, 30 and 36 kVA
+ // 7 contracts for HPHC: 6, 9, 12, 15, 18, 24, 30 and 36 kVA
+ // 5 contracts for EJP: 9, 12, 15, 18 and 36 kVA
+ if ($prices && count($prices) === $numberOfPrices) {
+ $powerFound = false;
+ foreach ($prices as $price) {
+ $powerText = $price->firstChild()->firstChild()->innertext;
+ $powerValue = (int)substr($powerText, 0, strpos($powerText, ' kVA'));
+
+ if ($powerValue !== $power) {
+ continue;
}
+
+ $item = [];
+
+ $text = $powerText . ' : ' . $price->children(1) . '/an';
+ $item['uri'] = self::URI . $contractUri;
+ $item['title'] = $text;
+ $item['author'] = self::MAINTAINER;
+ $item['content'] = $text;
+ $item['uid'] = hash('sha256', $item['title']);
+
+ $this->items[] = $item;
+ $powerFound = true;
+ break;
+ }
+
+ if (!$powerFound) {
+ $item = [];
+
+ $text = 'Pas de tarif abonnement pour cette puissance et ce contrat';
+ $item['uri'] = self::URI . $contractUri;
+ $item['title'] = $text;
+ $item['author'] = self::MAINTAINER;
+ $item['content'] = $text;
+ $item['uid'] = hash('sha256', $item['title']);
+
+ $this->items[] = $item;
}
}
}
@@ -97,10 +241,23 @@ class EdfPricesBridge extends BridgeAbstract
{
$contract = $this->getKey('contract');
$contractUri = $this->getInput('contract');
+ $power = $this->getInput('power');
$html = getSimpleHTMLDOM(self::URI . $contractUri);
if ($contract === 'Tempo') {
- $this->tempo($html, $contractUri);
+ $this->tempo($html, $contractUri, $power);
+ }
+
+ if ($contract === 'Base') {
+ $this->base($html, $contractUri, $power);
+ }
+
+ if ($contract === 'HPHC') {
+ $this->hphc($html, $contractUri, $power);
+ }
+
+ if ($contract === 'EJP') {
+ $this->ejp($html, $contractUri, $power);
}
}
}
diff --git a/bridges/EpicGamesFreeBridge.php b/bridges/EpicGamesFreeBridge.php
new file mode 100644
index 00000000..087b95be
--- /dev/null
+++ b/bridges/EpicGamesFreeBridge.php
@@ -0,0 +1,74 @@
+<?php
+
+class EpicGamesFreeBridge extends BridgeAbstract
+{
+ const NAME = 'Epic Games Free Games';
+ const MAINTAINER = 'phantop';
+ const URI = 'https://store.epicgames.com/';
+ const DESCRIPTION = 'Returns the latest free games from Epic Games';
+ const PARAMETERS = [ [
+ 'locale' => [
+ 'name' => 'Language',
+ 'type' => 'list',
+ 'values' => [
+ 'English' => 'en-US',
+ 'العربية' => 'ar',
+ 'Deutsch' => 'de',
+ 'Español (Spain)' => 'es-ES',
+ 'Español (LA)' => 'es-MX',
+ 'Français' => 'fr',
+ 'Italiano' => 'it',
+ '日本語' => 'ja',
+ '한국어' => 'ko',
+ 'Polski' => 'pl',
+ 'Português (Brasil)' => 'pt-BR',
+ 'Русский' => 'ru',
+ 'ไทย' => 'th',
+ 'Türkçe' => 'tr',
+ '简体中文' => 'zh-CN',
+ '繁體中文' => 'zh-Hant',
+ ],
+ 'title' => 'Language for game information',
+ 'defaultValue' => 'en-US',
+ ],
+ 'country' => [
+ 'name' => 'Country',
+ 'title' => 'Country store to check for deals',
+ 'defaultValue' => 'US',
+ ]
+ ]];
+
+ public function collectData()
+ {
+ $url = 'https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?';
+ $params = [
+ 'locale' => $this->getInput('locale'),
+ 'country' => $this->getInput('country'),
+ 'allowCountries' => $this->getInput('country'),
+ ];
+ $url .= http_build_query($params);
+ $json = Json::decode(getContents($url));
+
+ $data = $json['data']['Catalog']['searchStore']['elements'];
+ foreach ($data as $element) {
+ if (!isset($element['promotions']['promotionalOffers'][0])) {
+ continue;
+ }
+ $item = [
+ 'author' => $element['seller']['name'],
+ 'content' => $element['description'],
+ 'enclosures' => array_map(fn($item) => $item['url'], $element['keyImages']),
+ 'timestamp' => strtotime($element['promotions']['promotionalOffers'][0]['promotionalOffers'][0]['startDate']),
+ 'title' => $element['title'],
+ 'url' => parent::getURI() . $this->getInput('locale') . '/p/' . $element['urlSlug'],
+ ];
+ $this->items[] = $item;
+ }
+ }
+
+ public function getURI()
+ {
+ $uri = parent::getURI() . $this->getInput('locale') . '/free-games';
+ return $uri;
+ }
+}
diff --git a/bridges/FeedMergeBridge.php b/bridges/FeedMergeBridge.php
index 37b574b6..4fe42013 100644
--- a/bridges/FeedMergeBridge.php
+++ b/bridges/FeedMergeBridge.php
@@ -6,8 +6,10 @@ class FeedMergeBridge extends FeedExpander
const NAME = 'FeedMerge';
const URI = 'https://github.com/RSS-Bridge/rss-bridge';
const DESCRIPTION = <<<'TEXT'
-This bridge merges two or more feeds into a single feed. Max 10 items are fetched from each feed.
-TEXT;
+ This bridge merges two or more feeds into a single feed. <br>
+ Max 10 latest items are fetched from each individual feed. <br>
+ Items with identical url or title are considered duplicates (and are removed). <br>
+ TEXT;
const PARAMETERS = [
[
@@ -36,11 +38,11 @@ TEXT;
];
/**
- * todo: Consider a strategy which produces a shorter feed url
+ * TODO: Consider a strategy which produces a shorter feed url
*/
public function collectData()
{
- $limit = (int)($this->getInput('limit') ?: 10);
+ $limit = (int)($this->getInput('limit') ?: 99);
$feeds = [
$this->getInput('feed_1'),
$this->getInput('feed_2'),
@@ -61,7 +63,7 @@ TEXT;
if (count($feeds) > 1) {
// Allow one or more feeds to fail
try {
- $this->collectExpandableDatas($feed);
+ $this->collectExpandableDatas($feed, 10);
} catch (HttpException $e) {
$this->logger->warning(sprintf('Exception in FeedMergeBridge: %s', create_sane_exception_message($e)));
// This feed item might be spammy. Considering dropping it.
@@ -80,31 +82,48 @@ TEXT;
throw $e;
}
} else {
- $this->collectExpandableDatas($feed);
+ $this->collectExpandableDatas($feed, 10);
}
}
// If $this->items is empty we should consider throw exception here
- // Sort by timestamp descending
+ // Sort by timestamp, uri, title in descending order
usort($this->items, function ($a, $b) {
$t1 = $a['timestamp'] ?? $a['uri'] ?? $a['title'];
$t2 = $b['timestamp'] ?? $b['uri'] ?? $b['title'];
return $t2 <=> $t1;
});
- // Remove duplicates by using url as unique key
+ // Remove duplicates by url
$items = [];
foreach ($this->items as $item) {
- $index = $item['uri'] ?? null;
- if ($index) {
- // Overwrite duplicates
- $items[$index] = $item;
+ $uri = $item['uri'] ?? null;
+ if ($uri) {
+ // Insert or override the existing duplicate
+ $items[$uri] = $item;
} else {
+ // The item doesn't have a uri!
$items[] = $item;
}
}
- $this->items = array_slice(array_values($items), 0, $limit);
+ $this->items = array_values($items);
+
+ // Remove duplicates by title
+ $items = [];
+ foreach ($this->items as $item) {
+ $title = $item['title'] ?? null;
+ if ($title) {
+ // Insert or override the existing duplicate
+ $items[$title] = $item;
+ } else {
+ // The item doesn't have a title!
+ $items[] = $item;
+ }
+ }
+ $this->items = array_values($items);
+
+ $this->items = array_slice($this->items, 0, $limit);
}
public function getIcon()
diff --git a/bridges/FindACrewBridge.php b/bridges/FindACrewBridge.php
index 9119535b..0bc181f3 100644
--- a/bridges/FindACrewBridge.php
+++ b/bridges/FindACrewBridge.php
@@ -60,7 +60,7 @@ class FindACrewBridge extends BridgeAbstract
CURLOPT_POSTFIELDS => http_build_query($data) . "\n"
];
- $html = getSimpleHTMLDOM($url, $header, $opts) or returnClientError('No results for this query.');
+ $html = getSimpleHTMLDOM($url, $header, $opts);
$annonces = $html->find('.css_SrhRst');
$limit = $this->getInput('limit') ?? 10;
diff --git a/bridges/Formula1Bridge.php b/bridges/Formula1Bridge.php
index f84b1ca7..19a84e2e 100644
--- a/bridges/Formula1Bridge.php
+++ b/bridges/Formula1Bridge.php
@@ -5,13 +5,13 @@ class Formula1Bridge extends BridgeAbstract
const NAME = 'Formula1 Bridge';
const URI = 'https://formula1.com/';
const DESCRIPTION = 'Returns latest official Formula 1 news';
- const MAINTAINER = 'AxorPL';
+ const MAINTAINER = 'axor-mst';
- const API_KEY = 'qPgPPRJyGCIPxFT3el4MF7thXHyJCzAP';
+ const API_KEY = 'xZ7AOODSjiQadLsIYWefQrpCSQVDbHGC';
const API_URL = 'https://api.formula1.com/v1/editorial/articles?limit=%u';
const ARTICLE_AUTHOR = 'Formula 1';
- const ARTICLE_URL = 'https://formula1.com/en/latest/article.%s.%s.html';
+ const ARTICLE_URL = 'https://formula1.com/en/latest/article/%s.%s';
const LIMIT_MIN = 1;
const LIMIT_DEFAULT = 10;
@@ -36,7 +36,11 @@ class Formula1Bridge extends BridgeAbstract
$limit = min(self::LIMIT_MAX, max(self::LIMIT_MIN, $limit));
$url = sprintf(self::API_URL, $limit);
- $json = json_decode(getContents($url, ['apikey: ' . self::API_KEY]));
+ $json = json_decode(getContents($url, [
+ 'Accept: application/json',
+ 'apikey: ' . self::API_KEY,
+ 'locale: en'
+ ]));
if (property_exists($json, 'error')) {
returnServerError($json->message);
}
diff --git a/bridges/FurAffinityUserBridge.php b/bridges/FurAffinityUserBridge.php
index fa10d7ae..1866308d 100644
--- a/bridges/FurAffinityUserBridge.php
+++ b/bridges/FurAffinityUserBridge.php
@@ -34,8 +34,7 @@ class FurAffinityUserBridge extends BridgeAbstract
$url = self::URI . '/gallery/' . $this->getInput('searchUsername');
- $html = getSimpleHTMLDOM($url, [], $opt)
- or returnServerError('Could not load the user\'s gallery page.');
+ $html = getSimpleHTMLDOM($url, [], $opt);
$submissions = $html->find('section[id=gallery-gallery]', 0)->find('figure');
foreach ($submissions as $submission) {
diff --git a/bridges/GiteaBridge.php b/bridges/GiteaBridge.php
index f7f426e9..8433b6dd 100644
--- a/bridges/GiteaBridge.php
+++ b/bridges/GiteaBridge.php
@@ -155,8 +155,7 @@ class GiteaBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM($this->getURI())
- or returnServerError('Could not request ' . $this->getURI());
+ $html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());
$this->title = $html->find('[property="og:title"]', 0)->content;
@@ -246,8 +245,7 @@ class GiteaBridge extends BridgeAbstract
];
if ($this->getInput('include_description')) {
- $issue_html = getSimpleHTMLDOMCached($uri, 3600)
- or returnServerError('Unable to load issue description');
+ $issue_html = getSimpleHTMLDOMCached($uri, 3600);
$issue_html = defaultLinkTo($issue_html, $uri);
@@ -308,8 +306,7 @@ class GiteaBridge extends BridgeAbstract
];
if ($this->getInput('include_description')) {
- $issue_html = getSimpleHTMLDOMCached($uri, 3600)
- or returnServerError('Unable to load issue description');
+ $issue_html = getSimpleHTMLDOMCached($uri, 3600);
$issue_html = defaultLinkTo($issue_html, $uri);
diff --git a/bridges/GithubIssueBridge.php b/bridges/GithubIssueBridge.php
index 7f56abbd..0c3c0471 100644
--- a/bridges/GithubIssueBridge.php
+++ b/bridges/GithubIssueBridge.php
@@ -192,15 +192,18 @@ class GithubIssueBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM($this->getURI());
+ $url = $this->getURI();
+ $html = getSimpleHTMLDOM($url);
switch ($this->queriedContext) {
case static::BRIDGE_OPTIONS[1]: // Issue comments
$this->items = $this->extractIssueComments($html);
break;
case static::BRIDGE_OPTIONS[0]: // Project Issues
- foreach ($html->find('.js-active-navigation-container .js-navigation-item') as $issue) {
- $info = $issue->find('.opened-by', 0);
+ $issues = $html->find('.js-active-navigation-container .js-navigation-item');
+ $issues = $html->find('.IssueRow-module__row--XmR1f');
+ foreach ($issues as $issue) {
+ $info = $issue->find('.issue-item-module__authorCreatedLink--wFZvk', 0);
preg_match('/\/([0-9]+)$/', $issue->find('a', 0)->href, $match);
$issueNbr = $match[1];
@@ -222,24 +225,24 @@ class GithubIssueBridge extends BridgeAbstract
$item['content'] = 'Can not extract comments from ' . $uri;
}
- $item['author'] = $info->find('a', 0)->plaintext;
+ $item['author'] = $issue->find('a', 1)->plaintext;
$item['timestamp'] = strtotime(
- $info->find('relative-time', 0)->getAttribute('datetime')
+ $issue->find('relative-time', 0)->getAttribute('datetime')
);
$item['title'] = html_entity_decode(
- $issue->find('.js-navigation-open', 0)->plaintext,
+ $issue->find('h3', 0)->plaintext,
ENT_QUOTES,
'UTF-8'
);
- $comment_count = 0;
- if ($span = $issue->find('a[aria-label*="comment"] span', 0)) {
- $comment_count = $span->plaintext;
- }
+ //$comment_count = 0;
+ //if ($span = $issue->find('a[aria-label*="comment"] span', 0)) {
+ // $comment_count = $span->plaintext;
+ //}
- $item['content'] .= "\n" . 'Comments: ' . $comment_count;
+ //$item['content'] .= "\n" . 'Comments: ' . $comment_count;
$item['uri'] = self::URI
- . trim($issue->find('.js-navigation-open', 0)->getAttribute('href'), '/');
+ . trim($issue->find('a', 0)->getAttribute('href'), '/');
$this->items[] = $item;
}
break;
diff --git a/bridges/GlowficBridge.php b/bridges/GlowficBridge.php
index 0e4b8d93..7a58a08f 100644
--- a/bridges/GlowficBridge.php
+++ b/bridges/GlowficBridge.php
@@ -28,7 +28,7 @@ class GlowficBridge extends BridgeAbstract
public function collectData()
{
$url = $this->getAPIURI();
- $metadata = get_headers($url . '/replies', true) or returnClientError('Post did not return reply headers.');
+ $metadata = get_headers($url . '/replies', true);
$metadata['Last-Page'] = ceil($metadata['Total'] / $metadata['Per-Page']);
if (
!is_null($this->getInput('start_page')) &&
diff --git a/bridges/GogsBridge.php b/bridges/GogsBridge.php
index 685e5ba2..d838674f 100644
--- a/bridges/GogsBridge.php
+++ b/bridges/GogsBridge.php
@@ -171,8 +171,7 @@ class GogsBridge extends BridgeAbstract
];
if ($this->getInput('include_description')) {
- $issue_html = getSimpleHTMLDOMCached($uri, 3600)
- or returnServerError('Unable to load issue description');
+ $issue_html = getSimpleHTMLDOMCached($uri, 3600);
$issue_html = defaultLinkTo($issue_html, $uri);
diff --git a/bridges/GolemBridge.php b/bridges/GolemBridge.php
index 7f59ee90..219233f4 100644
--- a/bridges/GolemBridge.php
+++ b/bridges/GolemBridge.php
@@ -53,7 +53,7 @@ class GolemBridge extends FeedExpander
]
]];
const LIMIT = 5;
- const HEADERS = ['Cookie: golem_consent20=simple|220101;'];
+ const HEADERS = ['Cookie: golem_consent20=simple|250101;'];
public function collectData()
{
diff --git a/bridges/GoogleScholarBridge.php b/bridges/GoogleScholarBridge.php
index 11dc123b..3004180f 100644
--- a/bridges/GoogleScholarBridge.php
+++ b/bridges/GoogleScholarBridge.php
@@ -109,7 +109,7 @@ class GoogleScholarBridge extends BridgeAbstract
case 'user':
$userId = $this->getInput('userId');
$uri = self::URI . '/citations?hl=en&view_op=list_works&sortby=pubdate&user=' . $userId;
- $html = getSimpleHTMLDOM($uri) or returnServerError('Could not fetch Google Scholar data.');
+ $html = getSimpleHTMLDOM($uri);
$publications = $html->find('tr[class="gsc_a_tr"]');
@@ -184,7 +184,7 @@ class GoogleScholarBridge extends BridgeAbstract
$uri .= $sortBy ? '&scisbd=1' : '';
$uri .= $numResults ? '&num=' . $numResults : '';
- $html = getSimpleHTMLDOM($uri) or returnServerError('Could not fetch Google Scholar data.');
+ $html = getSimpleHTMLDOM($uri);
$publications = $html->find('div[class="gs_r gs_or gs_scl"]');
diff --git a/bridges/GovTrackBridge.php b/bridges/GovTrackBridge.php
index 4674668a..a2c18d9f 100644
--- a/bridges/GovTrackBridge.php
+++ b/bridges/GovTrackBridge.php
@@ -1,6 +1,6 @@
<?php
-class GovTrackBridge extends BridgeAbstract
+class GovTrackBridge extends FeedExpander
{
const NAME = 'GovTrack';
const MAINTAINER = 'phantop';
@@ -18,64 +18,50 @@ class GovTrackBridge extends BridgeAbstract
'Major Legislative Activity' => 'major-bill-activity',
'New Bills and Resolutions' => 'introduced-bills',
'New Laws' => 'enacted-bills',
- 'Posts from Us' => 'posts'
- ]
- ],
- 'limit' => self::LIMIT
+ 'News from Us' => 'posts'
+ ]
+ ],
+ 'limit' => self::LIMIT
]];
public function collectData()
{
- $html = getSimpleHTMLDOMCached($this->getURI());
- if ($this->getInput('feed') != 'posts') {
- $this->collectEvent($html);
- return;
+ $limit = $this->getInput('limit') ?? 15;
+ if ($this->getInput('feed') == 'posts') {
+ $this->collectExpandableDatas($this->getURI() . '.rss', $limit);
+ } else {
+ $this->collectEvent($this->getURI(), $limit);
}
+ }
+ protected function parseItem(array $item)
+ {
+ $html = getSimpleHTMLDOMCached($item['uri']);
$html = defaultLinkTo($html, parent::getURI());
- $limit = $this->getInput('limit') ?? 10;
- foreach ($html->find('section') as $element) {
- if (--$limit == 0) {
- break;
- }
-
- $info = explode(' ', $element->find('p', 0)->innertext);
- $item = [
- 'categories' => [implode(' ', array_slice($info, 4))],
- 'timestamp' => strtotime(implode(' ', array_slice($info, 0, 3))),
- 'title' => $element->find('a', 0)->innertext,
- 'uri' => $element->find('a', 0)->href,
- ];
-
- $html = getSimpleHTMLDOMCached($item['uri']);
- $html = defaultLinkTo($html, parent::getURI());
- $content = $html->find('#content .col-md', 1);
- $info = explode(' by ', $content->find('p', 0)->plaintext);
- $content->removeChild($content->firstChild());
+ $item['categories'] = [$html->find('.breadcrumb-item', 1)->plaintext];
+ $content = $html->find('#content .col-md', 1);
+ $item['author'] = explode(' by ', $content->firstChild()->plaintext)[1];
+ $content->removeChild($content->firstChild());
+ $item['content'] = $content->innertext;
- $item['author'] = implode(' ', array_slice($info, 1));
- $item['content'] = $content->innertext;
-
- $this->items[] = $item;
- }
+ return $item;
}
- private function collectEvent($html)
+ private function collectEvent($uri, $limit)
{
- $opt = [];
- preg_match('/"csrfmiddlewaretoken" value="(.*)"/', $html, $opt);
+ $html = getSimpleHTMLDOMCached($uri);
+ preg_match('/"csrfmiddlewaretoken" value="(.*)"/', $html, $preg);
$header = [
- "cookie: csrftoken=$opt[1]",
- "x-csrftoken: $opt[1]",
+ "cookie: csrftoken=$preg[1]",
+ "x-csrftoken: $preg[1]",
'referer: ' . parent::getURI(),
];
- preg_match('/var selected_feed = "(.*)";/', $html, $opt);
- $post = [
- 'count' => $this->getInput('limit') ?? 20,
- 'feed' => $opt[1]
- ];
- $opt = [ CURLOPT_POSTFIELDS => $post ];
+ preg_match('/var selected_feed = "(.*)";/', $html, $preg);
+ $opt = [ CURLOPT_POSTFIELDS => [
+ 'count' => $limit,
+ 'feed' => $preg[1]
+ ]];
$html = getContents(parent::getURI() . 'events/_load_events', $header, $opt);
$html = defaultLinkTo(str_get_html($html), parent::getURI());
@@ -83,10 +69,10 @@ class GovTrackBridge extends BridgeAbstract
foreach ($html->find('.tracked_event') as $event) {
$bill = $event->find('.event_title a, .event_body a', 0);
$date = explode(' ', $event->find('.event_date', 0)->plaintext);
- preg_match('/Sponsor:(.*)\n/', $event->plaintext, $opt);
+ preg_match('/Sponsor:(.*)\n/', $event->plaintext, $preg);
$item = [
- 'author' => $opt[1] ?? '',
+ 'author' => $preg[1] ?? '',
'content' => $event->find('td', 1)->innertext,
'enclosures' => [$event->find('img', 0)->src],
'timestamp' => strtotime(implode(' ', array_slice($date, 2))),
@@ -115,10 +101,10 @@ class GovTrackBridge extends BridgeAbstract
public function getURI()
{
- if ($this->getInput('feed') != 'posts') {
- $url = parent::getURI() . 'events/' . $this->getInput('feed');
- } else {
+ if ($this->getInput('feed') == 'posts') {
$url = parent::getURI() . $this->getInput('feed');
+ } else {
+ $url = parent::getURI() . 'events/' . $this->getInput('feed');
}
return $url;
}
diff --git a/bridges/HotUKDealsBridge.php b/bridges/HotUKDealsBridge.php
index 6958220e..7450c6f0 100644
--- a/bridges/HotUKDealsBridge.php
+++ b/bridges/HotUKDealsBridge.php
@@ -40,3202 +40,23 @@ class HotUKDealsBridge extends PepperBridgeAbstract
'Deals per group' => [
'group' => [
'name' => 'Group',
- 'type' => 'list',
- 'title' => 'Group whose deals must be displayed',
- 'values' => [
- '3D Blu-ray' => '3d-bluray',
- '3D Printer' => '3d-printer',
- '3D TV' => '3d-tv',
- '4K Blu-ray' => '4k-bluray',
- '4K Monitor' => '4k-monitor',
- '4K TV' => '4k-tv',
- '5G Phones' => '5g-phones',
- '7 Up' => '7up',
- '8K TV' => '8k-tv',
- '32 inch TV' => '32-inch-tv',
- '40 inch TV' => '40-inch-tv',
- '55 inch TV' => '55-inch-tv',
- '65 inch TV' => '65-inch-tv',
- '75 inch TV' => '75-inch-tv',
- '144Hz Monitor' => '144hz',
- 'A4 Paper' => 'a4-paper',
- 'AAA Battery' => 'aaa',
- 'AA Battery' => 'aa',
- 'Abercrombie' => 'abercrombie',
- 'Aberlour' => 'aberlour',
- 'Accommodation' => 'accomodation',
- 'Accurist' => 'accurist',
- 'Ace Combat 7: Skies Unknown' => 'ace-combat-7',
- 'Acer' => 'acer',
- 'Acer Aspire' => 'acer-aspire',
- 'Acer Laptop' => 'acer-laptop',
- 'Acer PC Monitor' => 'acer-pc-monitor',
- 'Acer Predator' => 'acer-predator',
- 'Action Camera' => 'action-camera',
- 'Action Figure &amp; Playsets' => 'playsets',
- 'Activewear' => 'sports-clothes',
- 'Activia' => 'activia',
- 'adidas' => 'adidas',
- 'adidas Continental' => 'continental',
- 'Adidas Gazelle' => 'gazelle',
- 'Adidas Originals' => 'adidas-originals',
- 'Adidas Samba' => 'samba',
- 'Adidas Stan Smith' => 'stan-smith',
- 'Adidas Superstar' => 'adidas-superstar',
- 'Adidas Trainers' => 'adidas-shoes',
- 'Adidas Ultraboost' => 'adidas-ultraboost',
- 'Adidas ZX Flux' => 'adidas-zx-flux',
- 'Adobe' => 'adobe',
- 'Adobe Lightroom' => 'lightroom',
- 'Adobe Photoshop' => 'photoshop',
- 'Adult Products' => 'adult',
- 'Advent Calendar' => 'advent-calendar',
- 'Adventure Time' => 'adventure-time',
- 'AEG' => 'aeg',
- 'Aftershave' => 'aftershave',
- 'Age Of Empires' => 'age-of-empires',
- 'Air Bed' => 'air-bed',
- 'Air Conditioner' => 'air-con',
- 'Airer' => 'airer',
- 'Airfix' => 'airfix',
- 'Air Fryer' => 'air-fryer',
- 'Airline' => 'airline',
- 'Airport' => 'airport',
- 'Airport Parking' => 'airport-parking',
- 'Air Purifier' => 'air-purifier',
- 'AirTag' => 'airtag',
- 'Air Treatment' => 'air-treatment',
- 'AKG' => 'akg',
- 'Alarm Clock' => 'alarm-clock',
- 'Alarm System' => 'alarm-system',
- 'Alcatel' => 'alcatel',
- 'Alcohol' => 'alcohol',
- 'Alesis' => 'alesis',
- 'Alien: Isolation' => 'alien-isolation',
- 'Alienware' => 'alienware',
- 'All-in-One PC' => 'all-in-one-pc',
- 'All-in-One Printer' => 'all-in-one-printer',
- 'Alloy Wheel' => 'alloy-wheels',
- 'All Saints' => 'all-saints',
- 'Almonds' => 'almonds',
- 'Alpro' => 'alpro',
- 'Alton Towers' => 'alton-towers',
- 'Amazfit' => 'xiaomi-amazfit',
- 'Amazfit Bip' => 'xiaomi-amazfit-bip',
- 'Amazfit GTS' => 'amazfit-gts',
- 'Amazfit Verge' => 'amazfit-verge',
- 'Amazfit Verge Lite' => 'amazfit-verge-lite',
- 'Amazfit Watch' => 'amazfit-watch',
- 'Amazon Add On Item' => 'add-on-item',
- 'Amazon Business' => 'amazon-business',
- 'Amazon Echo' => 'amazon-echo',
- 'Amazon Echo Dot' => 'amazon-echo-dot',
- 'Amazon Echo Plus' => 'amazon-echo-plus',
- 'Amazon Echo Show' => 'amazon-echo-show',
- 'Amazon Echo Show 5' => 'echo-show-5',
- 'Amazon Echo Show 8' => 'amazon-echo-show-8',
- 'Amazon Echo Spot' => 'amazon-echo-spot',
- 'Amazon Fire 7' => 'amazon-fire-7',
- 'Amazon Fire HD 8' => 'amazon-fire-hd-7',
- 'Amazon Fire HD 10 Tablet' => 'amazon-fire-hd-10',
- 'Amazon Fire Tablet' => 'amazon-tablet',
- 'Amazon Fire TV Cube' => 'fire-tv-cube',
- 'Amazon Fire TV Stick' => 'amazon-fire-stick',
- 'Amazon Pantry' => 'amazon-pantry',
- 'Amazon Prime' => 'amazon-prime',
- 'Amazon Prime Video' => 'amazon-video',
- 'Amazon Warehouse' => 'amazon-warehouse',
- 'AMD' => 'amd',
- 'AMD Radeon' => 'radeon',
- 'AMD Ryzen' => 'amd-ryzen',
- 'AMD Ryzen 5 5600X' => 'amd-ryzen-5-5600x',
- 'AMD Ryzen 7 5800X' => 'amd-ryzen-7-5800x',
- 'AMD Ryzen 9 5900X' => 'amd-ryzen-9-5900x',
- 'AMD Ryzen 9 5950X' => 'amd-ryzen-9-5950x',
- 'Amex' => 'amex',
- 'Amiibo' => 'amiibo',
- 'Amplifier' => 'amplifier',
- 'Anchor Butter' => 'anchor-butter',
- 'Andrex' => 'andrex',
- 'Android Apps' => 'android-app',
- 'Android Smartphone' => 'android-smartphone',
- 'Android Tablet' => 'android-tablet',
- 'Angelcare' => 'angelcare',
- 'Angle Grinder' => 'grinder',
- 'Anglepoise' => 'anglepoise',
- 'Angry Birds' => 'angry-birds',
- 'Animal Crossing' => 'animal-crossing',
- 'Anime' => 'anime',
- 'Anker' => 'anker',
- 'Ankle Boots' => 'ankle-boots',
- 'Anno 1800' => 'anno-1800',
- 'Anthem' => 'anthem',
- 'Antibacterial Hand Gel' => 'hand-gel',
- 'Antibacterial Wipes' => 'cleaning-wipes',
- 'Antivirus' => 'antivirus',
- 'Antler' => 'antler',
- 'AOC' => 'aoc',
- 'Apex Legends' => 'apex-legends',
- 'A Plague Tale: Innocence' => 'a-plague-tale-innocence',
- 'App' => 'app',
- 'Apple' => 'apple',
- 'Apple AirPods' => 'apple-airpods',
- 'Apple Airpods 2' => 'airpods-2',
- 'Apple Airpods Max' => 'airpods-max',
- 'Apple Airpods Pro' => 'airpods-pro',
- 'Apple EarPods' => 'earpods',
- 'Apple Headphones' => 'apple-headphones',
- 'Apple HomePod' => 'apple-homepod',
- 'Apple HomePod mini' => 'apple-homepod-mini',
- 'Apple Keyboard' => 'apple-keyboard',
- 'Apple Pencil' => 'apple-pencil',
- 'Apple TV' => 'apple-tv',
- 'Apple TV 4K' => 'apple-tv-4k',
- 'Apple Watch' => 'apple-watch',
- 'Apple Watch 3' => 'apple-watch-3',
- 'Apple Watch 4' => 'apple-watch-4',
- 'Apple Watch 5' => 'apple-watch-5',
- 'Apple Watch 6' => 'apple-watch-6',
- 'Apple Watch SE' => 'apple-watch-se',
- 'Apron' => 'apron',
- 'Aquadoodle' => 'aquadoodle',
- 'Aqua Optima' => 'aqua-optima',
- 'Aquarium' => 'aquarium',
- 'Aramis' => 'aramis',
- 'Argan Oil' => 'argan-oil',
- 'Ariel' => 'ariel',
- 'Ark' => 'ark',
- 'Armani' => 'armani',
- 'Armchair' => 'armchair',
- 'Armed Forces Discount' => 'armed-forces',
- 'Arsenal F. C.' => 'arsenal',
- 'Arts and Crafts' => 'craft',
- 'Asics' => 'asics',
- 'Ask' => 'ask',
- 'ASRock' => 'asrock',
- 'Assassin&#039;s Creed' => 'assassins-creed',
- 'Assassin&#039;s Creed: Odyssey' => 'assassins-creed-odyssey',
- 'Assassin&#039;s Creed: Origins' => 'assassins-creed-origins',
- 'Assassin&#039;s Creed: Unity' => 'assassins-creed-unity',
- 'Assassin&#039;s Creed: Valhalla' => 'assasins-creed-valhalla',
- 'Astral Chain' => 'astral-chain',
- 'ASTRO Gaming' => 'astro-gaming',
- 'Astro Gaming A40' => 'astro-gaming-a40',
- 'Astro Gaming A50' => 'astro-gaming-a50',
- 'Asus' => 'asus',
- 'ASUS Laptop' => 'asus-laptop',
- 'ASUS Monitor' => 'asus-monitor',
- 'ASUS ROG' => 'asus-rog',
- 'Asus ROG Phone' => 'asus-rog-phone',
- 'Asus ROG Phone 2' => 'asus-rog-phone-2',
- 'ASUS Router' => 'asus-router',
- 'Asus Smartphone' => 'asus-smartphone',
- 'ASUS Vivobook' => 'asus-vivobook',
- 'ASUS Zenbook' => 'zenbook',
- 'Asus ZenFone 6' => 'asus-zenfone-6',
- 'Atari' => 'atari',
- 'Audi' => 'audi',
- 'Audio &amp; Hi-Fi' => 'audio',
- 'Audio Accessories' => 'audio-accessories',
- 'Audiobook' => 'audiobook',
- 'Audio Technica' => 'audio-technica',
- 'Aukey' => 'aukey',
- 'Aussie' => 'aussie',
- 'Autoglym' => 'autoglym',
- 'Aveeno' => 'aveeno',
- 'Avengers' => 'avengers',
- 'AVG' => 'avg',
- 'Aviva' => 'aviva',
- 'Avon' => 'avon',
- 'AV Receiver' => 'av-receiver',
- 'Axe' => 'axe',
- 'Baby Annabell' => 'baby-annabell',
- 'Baby Bath' => 'baby-bath',
- 'Baby Born' => 'baby-born',
- 'Baby Bottle' => 'baby-bottles',
- 'Baby Bouncer' => 'bouncer',
- 'Baby Carrier' => 'baby-carrier',
- 'Baby Clothes' => 'baby-clothes',
- 'Baby Food' => 'baby-food',
- 'Baby Gym' => 'baby-gym',
- 'Baby Jogger' => 'baby-jogger',
- 'Babyliss' => 'babyliss',
- 'Baby Monitor' => 'baby-monitor',
- 'Baby Shoes' => 'baby-shoes',
- 'Baby Swing' => 'baby-swing',
- 'Baby Walker' => 'baby-walker',
- 'Baby Wipes' => 'wipes',
- 'Bacardi' => 'bacardi',
- 'Backpack' => 'backpack',
- 'Back to the Future' => 'back-to-the-future',
- 'Bacon' => 'bacon',
- 'Badminton' => 'badminton',
- 'Bag' => 'bag',
- 'Bagless Vacuum Cleaner' => 'bagless-vacuum-cleaner',
- 'Bahco' => 'bahco',
- 'Baileys' => 'baileys',
- 'Baked Beans' => 'baked-beans',
- 'Bakery Products' => 'bakery-products',
- 'Baking' => 'baking',
- 'Ball Pit' => 'ball-pit',
- 'Ballpoint Pen' => 'pen',
- 'Band of Brothers' => 'band-of-brothers',
- 'Bang &amp; Olufsen' => 'bang-olufsen',
- 'Bank' => 'bank',
- 'Bank Account' => 'bank-account',
- 'Banks &amp; Credit Cards' => 'bank-credit-card',
- 'Barbell' => 'barbell',
- 'Barbie' => 'barbie',
- 'Barbour' => 'barbour',
- 'Barclaycard' => 'barclaycard',
- 'Barclays' => 'barclays',
- 'Barebones PC' => 'barebones',
- 'bareMinerals' => 'bareminerals',
- 'Barry M' => 'barry-m',
- 'Bar Stools' => 'bar-stools',
- 'Base Layer' => 'base-layer',
- 'Basket' => 'basket',
- 'Basketball' => 'basketball',
- 'Basmati Rice' => 'basmati-rice',
- 'Bath Mat' => 'bath-mat',
- 'Bathroom Accessories' => 'bathroom',
- 'Bathroom Cabinet' => 'bathroom-cabinet',
- 'Bathroom Scale' => 'bathroom-scales',
- 'Bathroom Tap' => 'tap',
- 'Batman' => 'batman',
- 'Battery' => 'battery',
- 'Battleborn' => 'battleborn',
- 'Battlefield' => 'battlefield',
- 'Battlefield 1' => 'battlefield-1',
- 'Battlefield 4' => 'battlefield-4',
- 'Battlefield 5' => 'battlefield-5',
- 'Battlestar Galactica' => 'battlestar-galactica',
- 'Baylis &amp; Harding' => 'baylis-and-harding',
- 'Bayonetta' => 'bayonetta',
- 'Bayonetta 2' => 'bayonetta-2',
- 'Baywatch' => 'baywatch',
- 'BB-8' => 'bb-8',
- 'BBC' => 'bbc',
- 'BBQ Food' => 'bbq',
- 'BBQs and Grills' => 'grill',
- 'Bean Bag' => 'bean-bag',
- 'Beanie Hat' => 'beanie-hat',
- 'Bean to Cup Machine' => 'bean-to-cup',
- 'Beard Trimmer' => 'beard-trimmer',
- 'Beats by Dre' => 'beats-by-dre',
- 'Beats Solo 3' => 'beats-solo-3',
- 'Beats Studio 3' => 'beats-studio-3',
- 'Beauty' => 'beauty-care',
- 'Beauty and the Beast' => 'beauty-and-the-beast',
- 'Becks' => 'becks',
- 'Bed' => 'bed',
- 'Bedding' => 'bedding',
- 'Bedding &amp; Linens' => 'bedding-linens',
- 'Bed Frame' => 'bed-frame',
- 'Bedroom' => 'bedroom-furniture',
- 'Beef' => 'beef',
- 'Beer' => 'beer',
- 'Beer Advent Calendar' => 'beer-advent-calendar',
- 'Beko' => 'beko',
- 'Belkin' => 'belkin',
- 'Belstaff' => 'belstaff',
- 'Belt' => 'belt',
- 'BelVita' => 'belvita',
- 'Ben &amp; Jerry&#039;s' => 'ben-jerrys',
- 'Benefit Cosmetics' => 'benefit-cosmetics',
- 'BenQ' => 'benq',
- 'BenQ Monitor' => 'benq-monitor',
- 'Ben Sherman' => 'ben-sherman',
- 'BeoPlay Headphones' => 'beoplay-headphones',
- 'Beoplay Speakers' => 'beoplay',
- 'Berghaus' => 'berghaus',
- 'Bestway' => 'bestway',
- 'Betting' => 'betting',
- 'Beyerdynamic' => 'beyerdynamic',
- 'Bic' => 'bic',
- 'Bike' => 'bike',
- 'Bike Accessories' => 'bike-accessories',
- 'Bike Brake' => 'brakes',
- 'Bike Computer' => 'bike-computer',
- 'Bike Helmet' => 'bicycle-helmet',
- 'Bike Inner Tube' => 'inner-tube',
- 'Bike Lights' => 'bike-lights',
- 'Bike Lock' => 'bike-lock',
- 'Bike Parts' => 'bike-parts',
- 'Bike Pump' => 'bike-pump',
- 'Biker Equipment' => 'biker-equipment',
- 'Bike Saddle' => 'saddle',
- 'Biking &amp; Urban Sports' => 'biking-urban-sports',
- 'Bikini' => 'bikini',
- 'Billabong' => 'billabong',
- 'Bin' => 'bin',
- 'Binatone' => 'binatone',
- 'Bingo' => 'bingo',
- 'Binoculars' => 'binoculars',
- 'Bio Oil' => 'bio-oil',
- 'Bioshock' => 'bioshock',
- 'Birds Eye' => 'birds-eye',
- 'Birkenstock' => 'birkenstock',
- 'Biscuits' => 'biscuits',
- 'Bissell' => 'bissell',
- 'Bistro Set' => 'bistro-set',
- 'Bitdefender' => 'bitdefender',
- 'Black &amp; Decker' => 'black-decker',
- 'Blackberry Smartphone' => 'blackberry',
- 'Blanket' => 'blanket',
- 'Blaupunkt' => 'blaupunkt',
- 'Blazer' => 'blazer',
- 'Bleach' => 'bleach',
- 'Blended Malt' => 'malt',
- 'Blender' => 'blender',
- 'Blinds' => 'blinds',
- 'Blink XT2 Smart Security Camera' => 'blink-xt2',
- 'Blizzard' => 'blizzard',
- 'Blood &amp; Truth' => 'blood-and-truth',
- 'Bloodborne' => 'bloodborne',
- 'Blood Pressure Monitor' => 'blood-pressure',
- 'Blu-ray' => 'blu-ray',
- 'Blu-ray Player' => 'blu-ray-player',
- 'Bluetooth Headphones' => 'bluetooth-headphones',
- 'Bluetooth Speaker' => 'bluetooth-speaker',
- 'BMW' => 'bmw',
- 'BMW Mini Cooper' => 'mini-cooper',
- 'BMX' => 'bmx',
- 'Board Game' => 'board-game',
- 'Boardman' => 'boardman',
- 'Boat Shoes' => 'boat-shoes',
- 'Bodum' => 'bodum',
- 'Bogof' => 'bogof',
- 'Boiler' => 'boiler',
- 'Bold' => 'bold',
- 'Bombay Sapphire' => 'bombay-sapphire',
- 'Bomber Jacket' => 'bomber-jacket',
- 'Bonne Maman' => 'bonne-maman',
- 'Bonsai' => 'bonsai',
- 'Book' => 'book',
- 'Bookcase' => 'bookcase',
- 'Books &amp; Magazines' => 'books-magazines',
- 'Booster Seat' => 'booster-seat',
- 'Boots' => 'boots',
- 'Borderlands' => 'borderlands',
- 'Borderlands 3' => 'borderlands-3',
- 'Bosch' => 'bosch',
- 'Bosch Dishwasher' => 'bosch-dishwasher',
- 'Bosch Drill' => 'bosch-drill',
- 'Bosch Fridge' => 'bosch-fridge',
- 'Bosch Rotak' => 'rotak',
- 'Bosch Washing Machine' => 'bosch-washing-machine',
- 'Bose' => 'bose',
- 'Bose Headphones' => 'bose-headphones',
- 'Bose Noise Cancelling Headphones 700' => 'bose-headphones-700',
- 'Bose QuietComfort' => 'bose-quietcomfort',
- 'Bose QuietComfort 35 II' => 'bose-quietcomfort-35-ii',
- 'Bose SoundLink' => 'bose-soundlink',
- 'Bose SoundLink Around-Ear II' => 'bose-soundlink-2',
- 'Bose SoundTouch' => 'bose-soundtouch',
- 'BOSS' => 'hugo-boss',
- 'Boss Bottled' => 'boss-bottled',
- 'Bouncy Castle' => 'bouncy-castle',
- 'Bourbon' => 'bourbon',
- 'Bourjois' => 'bourjois',
- 'Bowers &amp; Wilkins' => 'bowers-wilkins',
- 'Bowling' => 'bowling',
- 'Bowmore' => 'bowmore',
- 'Boxers' => 'boxers',
- 'Boxing' => 'boxing',
- 'Boxing Gloves' => 'boxing-gloves',
- 'Boy&#039;s Clothes' => 'clothes-for-boys',
- 'Bra' => 'bra',
- 'Brabantia' => 'brabantia',
- 'Bracelet' => 'bracelet',
- 'Brands' => 'brand',
- 'Brandy' => 'brandy',
- 'Branston' => 'branston',
- 'Branston Beans' => 'branston-beans',
- 'Braun' => 'braun',
- 'Braun Series 3' => 'braun-series-3',
- 'Braun Series 5' => 'braun-series-5',
- 'Braun Series 7' => 'braun-series-7',
- 'Braun Series 9' => 'braun-series-9',
- 'Braun Shaver' => 'braun-shaver',
- 'Bread' => 'bread',
- 'Breadmaker' => 'breadmaker',
- 'Breakdown Cover' => 'breakdown',
- 'Breaking Bad' => 'breaking-bad',
- 'Breast Pump' => 'breast-pump',
- 'Breville' => 'breville',
- 'Breville Blend Active' => 'blendactive',
- 'Brewdog' => 'brewdog',
- 'Bridge Camera' => 'bridge-camera',
- 'Briefcase' => 'briefcase',
- 'Brita' => 'brita',
- 'Britax' => 'britax',
- 'British Airways' => 'british-airways',
- 'Broadband' => 'broadband',
- 'Broadband &amp; Phone Contracts' => 'broadband-phone-service',
- 'Brogues' => 'brogues',
- 'Brother' => 'brother',
- 'Brother Printer' => 'brother-printer',
- 'Brownie' => 'brownie',
- 'BT' => 'bt',
- 'BT Sport' => 'bt-sport',
- 'Budweiser' => 'budweiser',
- 'Buffalo' => 'buffalo',
- 'Bugaboo' => 'bugaboo',
- 'Buggy' => 'buggy',
- 'Build-A-Bear' => 'build-a-bear',
- 'Bulb' => 'bulbs',
- 'Bulletstorm' => 'bulletstorm',
- 'Bulmers' => 'bulmers',
- 'Bulova' => 'bulova',
- 'Burberry' => 'burberry',
- 'Burger' => 'burger',
- 'Burnout Paradise' => 'burnout-paradise',
- 'Burt&#039;s Bees' => 'burts-bees',
- 'Bus and Coach Ticket' => 'bus',
- 'Bush' => 'bush',
- 'Bushmills' => 'bushmills',
- 'Butter' => 'butter',
- 'Buying From Abroad' => 'buying-from-abroad',
- 'Bvlgari' => 'bvlgari',
- 'Cabin Case' => 'cabin-case',
- 'Cabinet' => 'cabinet',
- 'Cable Reel' => 'cable-reel',
- 'Cables' => 'cables',
- 'Cadbury&#039;s' => 'cadbury',
- 'Café Rouge' => 'cafe-rouge',
- 'Cafetière' => 'cafetiere',
- 'Caffè Nero' => 'cafe-nero',
- 'Cake' => 'cake',
- 'Calculator' => 'calculator',
- 'Calendar' => 'calendar',
- 'Call of Duty' => 'call-of-duty',
- 'Call of Duty: Black Ops' => 'black-ops',
- 'Call of Duty: Black Ops 3' => 'black-ops-3',
- 'Call of Duty: Black Ops 4' => 'black-ops-4',
- 'Call of Duty: Black Ops Cold War' => 'call-of-duty-black-ops-cold-war',
- 'Call of Duty: Infinite Warfare' => 'call-of-duty-infinite-warfare',
- 'Call of Duty: Modern Warfare' => 'modern-warfare',
- 'Call of Duty: WW2' => 'call-of-duty-ww2',
- 'Calpol' => 'calpol',
- 'Calvin Klein' => 'calvin-klein',
- 'Camcorder' => 'camcorder',
- 'Camelbak' => 'camelbak',
- 'Camera' => 'camera',
- 'Camera Accessories' => 'camera-accessories',
- 'Camera Bag' => 'camera-bag',
- 'Camera Lens' => 'lens',
- 'Camping' => 'camping',
- 'Campingaz' => 'campingaz',
- 'Candle' => 'candle',
- 'Cannondale' => 'cannondale',
- 'Canon' => 'canon',
- 'Canon Camera' => 'canon-camera',
- 'Canon EOS' => 'canon-eos',
- 'Canon Lens' => 'canon-lens',
- 'Canon Pixma' => 'canon-pixma',
- 'Canon PowerShot' => 'canon-powershot',
- 'Canon PowerShot SX430 IS' => 'canon-powershot-sx430-is',
- 'Canon Printer' => 'canon-printer',
- 'Canterbury' => 'canterbury',
- 'Canton' => 'canton',
- 'Canvas Print' => 'canvas-print',
- 'Cap' => 'cap',
- 'Capsule Machine' => 'capsule-machine',
- 'Captain America' => 'captain-america',
- 'Captain Morgan' => 'captain-morgan',
- 'Captain Toad: Treasure Tracker' => 'captain-toad-treasure-tracker',
- 'Car' => 'car',
- 'Car &amp; Motorcycle' => 'car-motorcycle',
- 'Car Accessories' => 'car-accessories',
- 'Caravan' => 'caravan',
- 'Car Battery' => 'car-battery',
- 'Carbon Monoxide Detector' => 'carbon-monoxide',
- 'Car Care' => 'car-care',
- 'Car Charger' => 'car-charger',
- 'Cardhu' => 'cardhu',
- 'Cardigan' => 'cardigan',
- 'Card Reader' => 'card-reader',
- 'Carex' => 'carex',
- 'Carhartt' => 'carhartt',
- 'Car Hire' => 'car-hire',
- 'Car Insurance' => 'car-insurance',
- 'Car Leasing' => 'car-lease',
- 'Carling' => 'carling',
- 'Car Lock' => 'lock',
- 'Carlsberg' => 'carlsberg',
- 'Car Mats' => 'car-mats',
- 'Carolina Herrera' => 'carolina-herrera',
- 'Car Parts' => 'car-parts',
- 'Carpet' => 'carpet',
- 'Carpet Cleaner' => 'carpet-cleaner',
- 'CarPlan' => 'carplan',
- 'Car Polish' => 'car-polish',
- 'Carrera Bikes' => 'carrera',
- 'Car Seat' => 'car-seat',
- 'Car Service' => 'car-service',
- 'Car Stereo' => 'car-stereo',
- 'Car Wash' => 'car-wash',
- 'Car Wax' => 'car-wax',
- 'Casio' => 'casio',
- 'Casio Eco-Drive' => 'eco-drive',
- 'Casio Edifice' => 'edifice',
- 'Casio G-Shock' => 'g-shock',
- 'Casserole' => 'casserole',
- 'Cast Iron Pots and Pans' => 'cast-iron',
- 'Castrol' => 'castrol',
- 'Caterpillar' => 'caterpillar',
- 'Cat Flap' => 'cat-flap',
- 'Cat Food' => 'cat-food',
- 'Cath Kidston' => 'cath-kidston',
- 'Cat Supplies' => 'cat-supplies',
- 'CCTV' => 'cctv',
- 'CD' => 'cd',
- 'CD Player' => 'cd-player',
- 'Ceiling Light' => 'ceiling-light',
- 'Celebrations' => 'celebrations',
- 'Cereal' => 'cereal',
- 'Cetirizine' => 'cetirizine',
- 'Chad Valley' => 'chad-valley',
- 'Chainsaw' => 'chainsaw',
- 'Champagne' => 'champagne',
- 'Champneys' => 'champneys',
- 'Chanel' => 'chanel',
- 'Chanel Coco Mademoiselle' => 'coco-mademoiselle',
- 'Changing Bag' => 'changing-bag',
- 'Channel 4' => 'channel-4',
- 'Charger' => 'charger',
- 'Cheese' => 'cheese',
- 'Chelsea Boots' => 'chelsea-boots',
- 'Chelsea F. C.' => 'chelsea',
- 'Chess' => 'chess',
- 'Chessington' => 'chessington',
- 'Chest Freezer' => 'chest-freezer',
- 'Chest of Drawers' => 'chest-of-drawers',
- 'Chicco' => 'chicco',
- 'Chicken' => 'chicken',
- 'Childcare' => 'baby',
- 'Children&#039;s Books' => 'childrens-books',
- 'Chino' => 'chino',
- 'Chisel' => 'chisel',
- 'Chloe' => 'chloe',
- 'Chocolate' => 'chocolate',
- 'Chocolate Advent Calendar' => 'chocolate-advent-calendar',
- 'Chopper' => 'chopper',
- 'Chopping Board' => 'chopping-board',
- 'Christmas Card' => 'christmas-card',
- 'Christmas Decoration' => 'christmas-decorations',
- 'Christmas Gift' => 'christmas-gifts',
- 'Christmas Jumper' => 'christmas-jumper',
- 'Christmas Lights' => 'christmas-lights',
- 'Christmas Stocking Fillers' => 'christmas-stocking-fillers',
- 'Christmas Toys' => 'christmas-toys',
- 'Christmas Tree' => 'christmas-tree',
- 'Chromebook' => 'chromebook',
- 'Chromecast' => 'chromecast',
- 'Chromecast Ultra' => 'chromecast-ultra',
- 'Chromecast with Google TV' => 'chromecast-google-tv',
- 'Chronograph' => 'chronograph',
- 'Chupa Chups' => 'chupa-chups',
- 'Chuwi' => 'chuwi',
- 'Cider' => 'cider',
- 'Cinema' => 'cinema',
- 'Cineworld' => 'cineworld',
- 'Circular Saw' => 'circular-saw',
- 'Circulon' => 'circulon',
- 'Ciroc' => 'ciroc',
- 'Cities Skylines' => 'cities-skylines',
- 'Citizen' => 'citizen',
- 'Citroen' => 'citroen',
- 'City Break' => 'city-breaks',
- 'Civilization' => 'civilization',
- 'Clarins' => 'clarins',
- 'Clarks' => 'clarks',
- 'Clearance' => 'clearance',
- 'Climbing' => 'climbing',
- 'Climbing Frame' => 'climbing-frame',
- 'Clinique' => 'clinique',
- 'Clothes' => 'clothes',
- 'Cloud Service' => 'cloud',
- 'Clutch Bag' => 'clutch',
- 'Coat' => 'coat',
- 'Coca Cola' => 'coke',
- 'Cocktail' => 'cocktail',
- 'Coconut Oil' => 'coconut',
- 'Coffee' => 'coffee',
- 'Coffee Beans' => 'coffee-beans',
- 'Coffee Machine' => 'coffee-machine',
- 'Coffee Pods' => 'coffee-pods',
- 'Coffee Table' => 'coffee-table',
- 'Cognac' => 'cognac',
- 'Cola' => 'cola',
- 'Coleman' => 'coleman',
- 'Colgate' => 'colgate',
- 'Combi Drill' => 'combi',
- 'Comfort' => 'comfort',
- 'Comic' => 'comic',
- 'Command &amp; Conquer' => 'command-and-conquer',
- 'Compact Camera' => 'compact-camera',
- 'Compact Flash' => 'compact-flash',
- 'Competitions' => 'competitions',
- 'Compost' => 'compost',
- 'Compressor' => 'compressor',
- 'Computer Accessories' => 'computer-accessories',
- 'Computers &amp; Tablets' => 'computers',
- 'Concert' => 'concert',
- 'Condé Nast' => 'conde-nast',
- 'Conditioner' => 'conditioner',
- 'Condom' => 'condom',
- 'Connectors' => 'connectors',
- 'Contact Lenses' => 'contact-lenses',
- 'Contents Insurance' => 'contents-insurance',
- 'Controller' => 'controller',
- 'Converse' => 'converse',
- 'Converse Chuck Taylor' => 'chuck-taylor',
- 'Cooker' => 'cooker',
- 'Cooking Oil' => 'cooking-oil',
- 'Cookware' => 'cooking',
- 'Cookware Set' => 'cookware-set',
- 'Cookworks' => 'cookworks',
- 'Cool Box' => 'cool-box',
- 'Coors Light' => 'coors-light',
- 'Cordless Drill' => 'cordless-drill',
- 'Cordless Phone' => 'cordless-phone',
- 'Cornetto' => 'cornetto',
- 'Corona Beer' => 'corona',
- 'Corsair' => 'corsair',
- 'Cosatto' => 'cosatto',
- 'Costa Coffee' => 'costa-coffee',
- 'Costume' => 'costume',
- 'Cot' => 'cot',
- 'Counter Strike' => 'counter-strike',
- 'Courses and Training' => 'education',
- 'Cow &amp; Gate' => 'cow-and-gate',
- 'Cozy Coupe' => 'cozy-coupe',
- 'CPU' => 'cpu',
- 'CPU Cooler' => 'cpu-cooler',
- 'Craghoppers' => 'craghoppers',
- 'Crash Bandicoot' => 'crash-bandicoot',
- 'Crash Team Racing Nitro-Fueled' => 'crash-team-racing-nitro-fueled',
- 'Crayola' => 'crayola',
- 'Creatine' => 'creatine',
- 'Credit Card' => 'credit-card',
- 'Creme Egg' => 'creme-egg',
- 'Cricket' => 'cricket',
- 'Crisps' => 'crisps',
- 'Crocs' => 'crocs',
- 'Cross Trainer' => 'cross-trainer',
- 'Crown Paint' => 'crown',
- 'Crucial' => 'crucial',
- 'Cruelty Free Makeup' => 'cruelty-free-makeup',
- 'Cruises' => 'cruise',
- 'Cube Bikes' => 'cube',
- 'Cubot' => 'cubot',
- 'Cufflinks' => 'cufflinks',
- 'Culture &amp; Leisure' => 'entertainment',
- 'Cuphead' => 'cuphead',
- 'Cuprinol' => 'cuprinol',
- 'Curling Wand' => 'curling-wand',
- 'Curtain' => 'curtain',
- 'Cushelle' => 'cushelle',
- 'Cushion' => 'cushion',
- 'Cutlery' => 'cutlery',
- 'CyberLink' => 'cyberlink',
- 'Cyberpunk 2077' => 'cyberpunk-2077',
- 'Cybex' => 'cybex',
- 'Cycling' => 'cycling',
- 'Cycling Jacket' => 'cycling-jacket',
- 'D-Link' => 'd-link',
- 'DAB Radio' => 'dab-radio',
- 'Dacia' => 'dacia',
- 'Daily Mail' => 'daily-mail',
- 'Dairy Milk' => 'dairy-milk',
- 'Darksiders' => 'darksiders',
- 'Dark Souls' => 'dark-souls',
- 'Dark Souls 3' => 'dark-souls-3',
- 'Dartboard' => 'dartboard',
- 'Darts' => 'darts',
- 'Dash Cam' => 'dash-cam',
- 'Data Storage' => 'storage',
- 'Davidoff' => 'davidoff',
- 'Days Gone' => 'days-gone',
- 'Days Out' => 'days-out',
- 'Daz' => 'daz',
- 'DC Comic' => 'dc',
- 'DDR3' => 'ddr3',
- 'DDR4' => 'ddr4',
- 'Dead Island' => 'dead-island',
- 'Dead or Alive 6' => 'dead-or-alive-6',
- 'Deadpool' => 'deadpool',
- 'Dead Rising' => 'dead-rising',
- 'Death Stranding' => 'death-stranding',
- 'Deezer' => 'deezer',
- 'Dehumidifier' => 'dehumidifier',
- 'Dell' => 'dell',
- 'Dell Laptop' => 'dell-laptop',
- 'Dell Monitor' => 'dell-monitor',
- 'Dell XPS' => 'xps',
- 'Delonghi' => 'delonghi',
- 'Demon&#039;s Souls' => 'demon-souls',
- 'Denby' => 'denby',
- 'Denon' => 'denon',
- 'Deodorant' => 'deodorant',
- 'Desk' => 'desk',
- 'Desperados Beer' => 'desperados',
- 'Despicable Me' => 'despicable-me',
- 'Destiny' => 'destiny',
- 'Destiny 2' => 'destiny-2',
- 'Detergent' => 'detergent',
- 'Detroit: Become Human' => 'detroit-become-human',
- 'Dettol' => 'dettol',
- 'Deus Ex' => 'deus-ex',
- 'Deus Ex: Mankind Divided' => 'deus-ex-mankind-divided',
- 'Development Boards' => 'development-boards',
- 'Devil May Cry 5' => 'devil-may-cry-5',
- 'DeWalt' => 'dewalt',
- 'DFDS' => 'dfds',
- 'Diablo 3' => 'diablo-3',
- 'Diary' => 'diary',
- 'Dickies' => 'dickies',
- 'Diesel' => 'diesel',
- 'Diet' => 'diet',
- 'Diggerland' => 'diggerland',
- 'Digihome' => 'digihome',
- 'Digimon' => 'digimon',
- 'Digital Camera' => 'digital-camera',
- 'Digital Watch' => 'digital-watch',
- 'Dildo' => 'dildo',
- 'Dimplex' => 'dimplex',
- 'Dining Room' => 'dining-room',
- 'Dining Room Chair' => 'chair',
- 'Dining Set' => 'dining-set',
- 'Dining Table' => 'dining-table',
- 'Dinner Plate' => 'plates',
- 'Dinner Set' => 'dinner-set',
- 'Dinosaur' => 'dinosaur',
- 'Dior' => 'dior',
- 'Dior Sauvage' => 'dior-sauvage',
- 'Dirt' => 'dirt',
- 'Dirt 4' => 'dirt-4',
- 'DIRT 5' => 'dirt-5',
- 'Dirt Rally 2.0' => 'dirt-rally-2',
- 'Disaronno' => 'disaronno',
- 'Discord Nitro' => 'discord-nitro',
- 'Disgaea' => 'disgaea',
- 'Dishonored' => 'dishonored',
- 'Dishonored 2' => 'dishonored-2',
- 'Dishwasher' => 'dishwasher',
- 'Dishwasher Tablets' => 'dishwasher-tablets',
- 'Disinfectants' => 'disinfectants',
- 'Disney' => 'disney',
- 'Disney&#039;s Cars' => 'disney-cars',
- 'Disney&#039;s Frozen' => 'disney-frozen',
- 'Disney+' => 'disney-plus',
- 'Disney Infinity' => 'disney-infinity',
- 'Disneyland' => 'disneyland',
- 'Disney Princess' => 'disney-princess',
- 'Disney Tsum Tsum' => 'tsum-tsum',
- 'Disney World' => 'disney-world',
- 'Divan' => 'divan',
- 'DIY' => 'diy',
- 'DJ Equipment' => 'dj',
- 'DJI Phantom' => 'dji-phantom',
- 'DKNY' => 'dkny',
- 'Doctor Who' => 'doctor-who',
- 'Dog Bed' => 'dog-bed',
- 'Dog Food' => 'dog-food',
- 'Dog Supplies' => 'dog',
- 'Dolce &amp; Gabbana' => 'dolce',
- 'Dolce Gusto' => 'dolce-gusto',
- 'Dolce Gusto Coffee Machine' => 'dolce-gusto-coffee-machine',
- 'Doll' => 'doll',
- 'Dolls House' => 'dolls-house',
- 'Domain Service' => 'domain',
- 'Doogee' => 'doogee',
- 'Doom' => 'doom',
- 'Door' => 'door',
- 'Doorbell' => 'doorbell',
- 'Door Handles' => 'door-handles',
- 'Doormat' => 'doormat',
- 'Doritos' => 'doritos',
- 'Dove' => 'dove',
- 'Down Jacket' => 'down-jacket',
- 'Downton Abbey' => 'downton-abbey',
- 'Dr. Martens' => 'dr-martens',
- 'Dragon Age' => 'dragon-age',
- 'Dragon Ball' => 'dragon-ball',
- 'Dragon Ball: FighterZ' => 'dragon-ball-fighterz',
- 'Dragon Quest' => 'dragon-quest',
- 'Dragon Quest Builders' => 'dragon-quest-builders',
- 'Dragon Quest Builders 2' => 'dragon-quest-builders-2',
- 'Dragon Quest XI: Echoes of an Elusive Age' => 'dragon-quest-xi',
- 'Draper' => 'draper',
- 'Drayton Manor' => 'drayton-manor',
- 'Dreame T20' => 'dreame-t20',
- 'Dreame V9' => 'dreame-v9',
- 'Dreame V9P' => 'dreame-v9p',
- 'Dreame V10' => 'dreame-v10',
- 'Dreame V11' => 'dreame-v11',
- 'Dreame Vacuum Cleaner' => 'xiaomi-vacuum-cleaner',
- 'Dremel' => 'dremel',
- 'Dress' => 'dress',
- 'Dressing Gown' => 'dressing-gown',
- 'Drill' => 'drill',
- 'Drill Driver' => 'driver',
- 'Drinks' => 'drinks',
- 'Driveclub' => 'driveclub',
- 'Driving Lessons' => 'driving-lessons',
- 'Drone' => 'drone',
- 'Dryer' => 'dryer',
- 'DSLR Camera' => 'dslr',
- 'Dual Fuel Cooker' => 'dual-fuel',
- 'Dualit' => 'dualit',
- 'Dual Sim' => 'sim',
- 'Dulux' => 'dulux',
- 'Duracell' => 'duracell',
- 'Durex' => 'durex',
- 'Duvet' => 'duvet',
- 'DVD' => 'dvd',
- 'DVD Player' => 'dvd-player',
- 'Dying Light' => 'dying-light',
- 'Dymo' => 'dymo',
- 'Dyson' => 'dyson',
- 'Dyson Supersonic' => 'dyson-supersonic',
- 'Dyson V6' => 'dyson-v6',
- 'Dyson V7' => 'dyson-v7',
- 'Dyson V8' => 'dyson-v8',
- 'Dyson V10' => 'dyson-v10',
- 'Dyson V11' => 'dyson-v11',
- 'Dyson Vacuum Cleaner' => 'dyson-vacuum-cleaner',
- 'e-Reader' => 'ereader',
- 'EA' => 'ea',
- 'EA Access' => 'ea-access',
- 'Earphones' => 'earphones',
- 'Earrings' => 'earrings',
- 'EA Sports' => 'ea-sports',
- 'EA Sports UFC' => 'ufc',
- 'Easter Eggs' => 'egg',
- 'Eastpak' => 'eastpak',
- 'eBook' => 'ebook',
- 'Ecovacs' => 'ecovacs',
- 'Ecover' => 'ecover',
- 'Educational Toys' => 'educational-toys',
- 'EE' => 'ee',
- 'eFootball PES 2021' => 'pes-2021',
- 'ELC Happyland' => 'happyland',
- 'Electrical Accessories' => 'electrical-accessories',
- 'Electric Bike' => 'electric-bike',
- 'Electric Blanket' => 'electric-blanket',
- 'Electric Cooker' => 'electric-cooker',
- 'Electric Fires' => 'electric-fire',
- 'Electric Scooter' => 'electric-scooter',
- 'Electric Shower' => 'electric-shower',
- 'Electric Toothbrush' => 'electric-toothbrush',
- 'Electronic Accessories' => 'electronics-accessories',
- 'Electronics' => 'electronics',
- 'Elemis' => 'elemis',
- 'Elephone' => 'elephone',
- 'Elgato' => 'elgato',
- 'Elite Dangerous' => 'elite-dangerous',
- 'Elizabeth Arden' => 'elizabeth-arden',
- 'Emirates' => 'emirates',
- 'Endura' => 'endura',
- 'Eneloop' => 'eneloop',
- 'Energizer' => 'energizer',
- 'Energy' => 'energy',
- 'Energy, Heating &amp; Gas' => 'energy-heating-gas',
- 'Energy Drinks' => 'energy-drinks',
- 'Engine Oil' => 'engine-oil',
- 'Epilator' => 'epilator',
- 'Epson' => 'epson',
- 'Epson Printer' => 'epson-printer',
- 'Espresso' => 'espresso',
- 'Espresso Machine' => 'espresso-machine',
- 'Esprit' => 'esprit',
- 'Estée Lauder' => 'estee-lauder',
- 'Ethernet' => 'ethernet',
- 'Etnies' => 'etnies',
- 'Eurostar Ticket' => 'eurostar',
- 'Eurotunnel' => 'eurotunnel',
- 'Everton F. C.' => 'everton',
- 'EVGA' => 'evga',
- 'Evian' => 'evian',
- 'Exercise Equipment' => 'exercise-equipment',
- 'Exercise Weights' => 'weight',
- 'Extension Lead' => 'extension-lead',
- 'External Hard Drive' => 'external-hard-drive',
- 'F1' => 'formula-one',
- 'F1 2017' => 'f1-2017',
- 'F1 2018' => 'f1-2018',
- 'F1 2019' => 'f1-2019',
- 'F1 2020' => 'f1-2020',
- 'Fabric Conditioner' => 'fabric-conditioner',
- 'Face Cream' => 'face-cream',
- 'Face Mask' => 'face-mask',
- 'Fairy' => 'fairy',
- 'Fairy Light' => 'fairy-light',
- 'Fallout' => 'fallout',
- 'Fallout 4' => 'fallout-4',
- 'Fallout 76' => 'fallout-76',
- 'Family &amp; Kids' => 'kids',
- 'Family Break' => 'family-break',
- 'Family Guy' => 'family-guy',
- 'Famous Grouse' => 'famous-grouse',
- 'Fancy Dress' => 'fancy-dress',
- 'Fans' => 'fan',
- 'Fanta' => 'fanta',
- 'Far Cry' => 'far-cry',
- 'Far Cry 4' => 'far-cry-4',
- 'Far Cry 5' => 'far-cry-5',
- 'Far Cry New Dawn' => 'far-cry-new-dawn',
- 'Far Cry Primal' => 'far-cry-primal',
- 'Farming Simulator' => 'farming-simulator',
- 'Fashion &amp; Accessories' => 'fashion',
- 'Fashion Accessories' => 'fashion-accessories',
- 'Fashion for Men' => 'mens-clothing',
- 'Fashion for Women' => 'womens-clothes',
- 'Fast and Furious' => 'fast-and-furious',
- 'Father&#039;s Day' => 'fathers-day',
- 'FatMax' => 'fatmax',
- 'FC Barcelona' => 'fc-barcelona',
- 'Felix' => 'felix',
- 'Fence' => 'fence',
- 'Fender Guitar' => 'fender',
- 'Ferrero Rocher' => 'ferrero-rocher',
- 'Ferry' => 'ferry',
- 'Festival' => 'festival',
- 'Fever Thermometer' => 'thermometer',
- 'Fiat' => 'fiat',
- 'Fidget Spinner' => 'spinner',
- 'FIFA' => 'fifa',
- 'FIFA 17' => 'fifa-17',
- 'FIFA 18' => 'fifa-18',
- 'FIFA 19' => 'fifa-19',
- 'FIFA 20' => 'fifa-20',
- 'FIFA 21' => 'fifa-21',
- 'FightStick' => 'fightstick',
- 'Figures' => 'figures',
- 'Fila Trainers' => 'fila-trainers',
- 'Filing Cabinet' => 'filing-cabinet',
- 'Final Fantasy' => 'final-fantasy',
- 'Final Fantasy 15' => 'final-fantasy-15',
- 'Finance &amp; Insurance' => 'personal-finance',
- 'Finish' => 'finish',
- 'Finlux' => 'finlux',
- 'Fiorelli' => 'fiorelli',
- 'Fire Emblem' => 'fire-emblem',
- 'Fire Pit' => 'fire-pit',
- 'Fireplace' => 'fireplace',
- 'Firewall: Zero Hour' => 'firewall-zero-hour',
- 'First Aid' => 'first-aid',
- 'Fish &amp; Seafood' => 'fish-and-seafood',
- 'Fish and Aquatic Pet Supplies' => 'fish',
- 'Fisher Price' => 'fisher-price',
- 'Fisher Price Imaginext' => 'imaginext',
- 'Fisher Price Jumperoo' => 'jumperoo',
- 'Fisher Price Little People' => 'little-people',
- 'Fishing' => 'fishing',
- 'Fiskars' => 'fiskars',
- 'Fitbit' => 'fitbit',
- 'Fitbit Alta' => 'fitbit-alta',
- 'Fitbit Blaze' => 'fitbit-blaze',
- 'Fitbit Charge 2' => 'fitbit-charge-2',
- 'Fitbit Inspire' => 'fitbit-inspire',
- 'Fitbit Versa' => 'fitbit-versa',
- 'Fitness &amp; Running' => 'fitness',
- 'Fitness App' => 'fitness-app',
- 'Fitness Tracker' => 'fitness-tracker',
- 'Flamingo Land' => 'flamingo-land',
- 'Flea Treatment' => 'flea',
- 'Fleece Clothing' => 'fleece',
- 'Flights' => 'flight',
- 'Flip Flops' => 'flip-flops',
- 'Floodlight' => 'floodlight',
- 'Flooring' => 'flooring',
- 'Flowers' => 'flowers',
- 'Flymo' => 'flymo',
- 'FM Transmitter' => 'fm-transmitter',
- 'Food' => 'food',
- 'Food Containers' => 'food-containers',
- 'Food Processor' => 'food-processor',
- 'Food Server' => 'food-server',
- 'Football' => 'football',
- 'Football Boots' => 'football-boots',
- 'Football Manager' => 'football-manager',
- 'Football Matches' => 'football-matches',
- 'Football Shirt' => 'football-shirt',
- 'Foot Pump' => 'foot-pump',
- 'Ford' => 'ford',
- 'For Honor' => 'for-honor',
- 'Fortnite' => 'fortnite',
- 'Fortnite: Darkfire' => 'fortnite-darkfire',
- 'Forza' => 'forza',
- 'Forza 7' => 'forza-7',
- 'Forza Horizon' => 'forza-horizon',
- 'Forza Horizon 3' => 'forza-horizon-3',
- 'Forza Horizon 4' => 'forza-horizon-4',
- 'Forza Motorsport' => 'forza-motorsport',
- 'Foscam' => 'foscam',
- 'Fossil' => 'fossil',
- 'Foster&#039;s' => 'fosters',
- 'Foundation' => 'foundation',
- 'Fountain Pen' => 'fountain-pen',
- 'Fred Perry' => 'fred-perry',
- 'Freesat' => 'freesat',
- 'Freeview' => 'freeview',
- 'Freezer' => 'freezer',
- 'Fridge' => 'fridge',
- 'Fridge Freezer' => 'fridge-freezer',
- 'Frontline' => 'frontline',
- 'Frozen Food' => 'frozen',
- 'Fruit' => 'fruit',
- 'Fruit and Vegetables' => 'fruit-and-vegetable',
- 'Fruit of the Loom' => 'fruit-of-the-loom',
- 'Fryer' => 'fryer',
- 'Frying Pan' => 'frying-pan',
- 'Fujifilm' => 'fuji',
- 'Fujitsu' => 'fujitsu',
- 'Funko Pop' => 'funko-pop',
- 'Furby' => 'furby',
- 'Furniture' => 'furniture',
- 'G-Star' => 'g-star',
- 'G-Sync Monitor' => 'g-sync',
- 'Gaggia' => 'gaggia',
- 'Gambling' => 'gambling',
- 'Game App' => 'game-app',
- 'Game of Thrones' => 'game-of-thrones',
- 'Games &amp; Board Games' => 'board-games',
- 'Games Consoles' => 'console',
- 'Gaming' => 'gaming',
- 'Gaming Accessories' => 'gaming-accessories',
- 'Gaming Chair' => 'gaming-chair',
- 'Gaming Headset' => 'gaming-headset',
- 'Gaming Keyboard' => 'gaming-keyboard',
- 'Gaming Laptop' => 'gaming-laptop',
- 'Gaming Monitor' => 'gaming-monitor',
- 'Gaming Mouse' => 'gaming-mouse',
- 'Gaming PC' => 'gaming-pc',
- 'Gant' => 'gant',
- 'Garage' => 'garage',
- 'Garage &amp; Service' => 'garage-service',
- 'Garden' => 'garden',
- 'Garden &amp; Do It Yourself' => 'garden-diy',
- 'Garden Furniture' => 'garden-furniture',
- 'Gardening' => 'gardening',
- 'Garden Storage' => 'garden-storage',
- 'Garden Table' => 'table',
- 'Garmin' => 'garmin',
- 'Garmin Fenix' => 'garmin-fenix',
- 'Garmin Fenix 6' => 'garmin-fenix-6',
- 'Garmin Fenix 6 Pro' => 'garmin-fenix-6-pro',
- 'Garmin Forerunner' => 'garmin-forerunner',
- 'Garmin Vivoactive' => 'garmin-vivoactive',
- 'Garmin Watch' => 'garmin-watch',
- 'Garnier' => 'garnier',
- 'Gas' => 'gas',
- 'Gas Canister' => 'butane',
- 'Gas Cooker' => 'gas-cooker',
- 'Gatwick' => 'gatwick',
- 'Gazebo' => 'gazebo',
- 'GBK' => 'gbk',
- 'Gears 5' => 'gears-5',
- 'Gears of War' => 'gears-of-war',
- 'Gears of War 4' => 'gears-of-war-4',
- 'George Foreman' => 'george-foreman',
- 'Geox' => 'geox',
- 'GHD' => 'ghd',
- 'Ghostbusters' => 'ghostbusters',
- 'Ghostbusters: The Video Game Remastered' => 'ghostbusters-the-video-game',
- 'Ghost of Tsushima' => 'ghost-of-tsushima',
- 'Gibson Guitar' => 'gibson',
- 'giffgaff' => 'giffgaff',
- 'Gift Card' => 'gift-card',
- 'Gift Hamper' => 'hamper',
- 'Gifts' => 'gifts',
- 'Gift Set' => 'gift-set',
- 'GIGABYTE' => 'gigabyte',
- 'Gigaset' => 'gigaset',
- 'Gilet' => 'gilet',
- 'Gillette Fusion' => 'fusion',
- 'Gillette Mach3' => 'mach-3',
- 'Gillette Razor' => 'gillette',
- 'Gimbal' => 'gimbal',
- 'Gin' => 'gin',
- 'Girl&#039;s Clothes' => 'girls-clothes',
- 'Glasses' => 'glasses',
- 'Glassware' => 'glassware',
- 'Glenfiddich' => 'glenfiddich',
- 'Glenlivet' => 'glenlivet',
- 'Glenmorangie' => 'glenmorangie',
- 'Gloves' => 'gloves',
- 'Glue' => 'glue',
- 'Glue Gun' => 'glue-gun',
- 'Gluten-Free' => 'gluten-free',
- 'God of War' => 'god-of-war',
- 'Go Kart' => 'go-kart',
- 'Golf' => 'golf',
- 'Golf Balls' => 'golf-balls',
- 'Golf Clubs' => 'golf-clubs',
- 'Goodfellas' => 'goodfellas',
- 'Goodmans' => 'goodmans',
- 'Goodyear' => 'goodyear',
- 'Google' => 'google',
- 'Google Home' => 'google-home',
- 'Google Home Max' => 'google-home-max',
- 'Google Home Mini' => 'google-home-mini',
- 'Google Nest' => 'nest',
- 'Google Nest Audio' => 'google-nest-audio',
- 'Google Nest Hub' => 'google-home-hub',
- 'Google Nest Mini' => 'nest-mini',
- 'Google Nest Protect' => 'google-nest-protect',
- 'Google Nexus' => 'nexus',
- 'Google Pixel' => 'google-pixel',
- 'Google Pixel 2' => 'google-pixel-2',
- 'Google Pixel 2 XL' => 'google-pixel-2-xl',
- 'Google Pixel 3' => 'google-pixel-3',
- 'Google Pixel 3 XL' => 'google-pixel-3-xl',
- 'Google Pixel 3a' => 'google-pixel-3a',
- 'Google Pixel 3a XL' => 'google-pixel-3a-xl',
- 'Google Pixel 4' => 'google-pixel-4',
- 'Google Pixel 4 XL' => 'google-pixel-4-xl',
- 'Google Pixel 4a' => 'google-pixel-4a',
- 'Google Pixel 4a 5G' => 'google-pixel-4a-5g',
- 'Google Pixel 5' => 'google-pixel-5',
- 'Google Pixelbook' => 'google-pixelbook',
- 'Google Pixel XL' => 'google-pixel-xl',
- 'Google Smartphone' => 'google-smartphone',
- 'Google Stadia' => 'google-stadia',
- 'GoPro' => 'gopro',
- 'GoPro HERO 6' => 'gopro-hero-6',
- 'GoPro HERO 7' => 'gopro-hero-7',
- 'GoPro HERO 8' => 'gopro-hero-8',
- 'GoPro HERO 9' => 'gopro-hero-9',
- 'Gore-Tex Clothing and Shoes' => 'gore-tex',
- 'Graco' => 'graco',
- 'Grand National' => 'grand-national',
- 'Gran Turismo' => 'gran-turismo',
- 'Gran Turismo Sport' => 'gran-turismo-sport',
- 'Graphics Card' => 'graphics-card',
- 'Gravity Rush' => 'gravity-rush',
- 'Graze' => 'graze',
- 'GreedFall' => 'greedfall',
- 'Greenhouse' => 'greenhouse',
- 'Greeting Cards and Wrapping Paper' => 'wrapping-paper-and-cards',
- 'Greggs' => 'greggs',
- 'Grey Goose' => 'grey-goose',
- 'Griffin Technology' => 'griffin',
- 'GroBag' => 'grobag',
- 'Groceries' => 'groceries',
- 'Gruffalo' => 'gruffalo',
- 'Grundig' => 'grundig',
- 'GTA' => 'gta',
- 'GTA V' => 'gta-v',
- 'GTX 970' => 'gtx-970',
- 'GTX 980' => 'gtx-980',
- 'GTX 1060' => 'gtx-1060',
- 'GTX 1070' => 'gtx-1070',
- 'GTX 1080' => 'gtx-1080',
- 'GTX 1080 Ti' => 'gtx-1080-ti',
- 'GTX 1660' => 'gtx-1660',
- 'GTX 1660 Ti' => 'gtx-1660-ti',
- 'Guardians of the Galaxy' => 'guardians-of-the-galaxy',
- 'Gucci' => 'gucci',
- 'Guinness' => 'guinness',
- 'Guitar' => 'guitar',
- 'Guitar Amp' => 'guitar-amp',
- 'Guitar Hero' => 'guitar-hero',
- 'Gulliver&#039;s' => 'gullivers',
- 'Gym' => 'gym',
- 'Gym Membership' => 'gym-membership',
- 'H1Z1' => 'h1z1',
- 'Häagen Dazs' => 'haagen-dazs',
- 'Habitat' => 'habitat',
- 'Hacksaw' => 'hacksaw',
- 'Hair Brush' => 'hair-brush',
- 'Hair Care' => 'hair',
- 'Hair Clipper' => 'hair-clipper',
- 'Hair Colour' => 'hair-colour',
- 'Haircut' => 'haircut',
- 'Hair Dryer' => 'hair-dryer',
- 'Hair Dye' => 'hair-dye',
- 'Hair Removal Devices' => 'hair-removal-devices',
- 'Halifax' => 'halifax',
- 'Hall' => 'hall',
- 'Halloween' => 'halloween',
- 'Halo' => 'halo',
- 'Halo 5' => 'halo-5',
- 'Ham' => 'ham',
- 'Hammer' => 'hammer',
- 'Hammer Drill' => 'hammer-drill',
- 'Hammock' => 'hammock',
- 'Handbag' => 'handbag',
- 'Hand Blender' => 'hand-blender',
- 'Hand Cream' => 'hand-cream',
- 'Hand Mixer' => 'hand-mixer',
- 'Hand Tools' => 'hand-tools',
- 'Handwash' => 'handwash',
- 'Hard Drive' => 'hard-drive',
- 'Haribo' => 'haribo',
- 'Harman Kardon' => 'harman-kardon',
- 'Harry Potter' => 'harry-potter',
- 'Hasbro' => 'hasbro',
- 'Hat' => 'hat',
- 'Hatchimals' => 'hatchimals',
- 'Hats &amp; Caps' => 'hats-caps',
- 'Hauck' => 'hauck',
- 'Hayfever Remedies' => 'hayfever',
- 'Headboard' => 'headboard',
- 'Headphones' => 'headphones',
- 'Headset' => 'headset',
- 'Health &amp; Beauty' => 'beauty',
- 'Healthcare' => 'health-care',
- 'Heart Rate Monitor' => 'heart-rate-monitor',
- 'Heater' => 'heater',
- 'Heating' => 'heating',
- 'Heating Appliances' => 'heating-appliances',
- 'Hedge Trimmer' => 'hedge-trimmer',
- 'Heineken' => 'heineken',
- 'Heinz' => 'heinz',
- 'Heinz Beanz' => 'heinz-baked-beans',
- 'Hello Kitty' => 'hello-kitty',
- 'Hello Neighbour' => 'hello-neighbour',
- 'Helly Hansen' => 'helly-hansen',
- 'Henry Hoover' => 'henry-hoover',
- 'Hermes' => 'hermes',
- 'High5' => 'high-5',
- 'Highchair' => 'highchair',
- 'Hiking' => 'hiking',
- 'Hilton' => 'hilton',
- 'Hisense' => 'hisense',
- 'Hisense TVs' => 'hisense-tv',
- 'Hitachi' => 'hitachi',
- 'Hitman' => 'hitman',
- 'Hive' => 'hive',
- 'Hive Active Heating' => 'hive-active-heating',
- 'Hob' => 'hob',
- 'Hobbit' => 'hobbit',
- 'Hockey' => 'hockey',
- 'Holiday Inn' => 'holiday-inn',
- 'Holiday Park' => 'holiday-parks',
- 'Holidays and Trips' => 'holidays-and-trips',
- 'Hollow Knight' => 'hollow-knight',
- 'Home &amp; Living' => 'home',
- 'Home Accessories' => 'home-accessories',
- 'Home Appliances' => 'home-appliances',
- 'Home Care' => 'home-care',
- 'Home Cinema' => 'home-cinema',
- 'HoMedics' => 'homedics',
- 'Homefront' => 'homefront',
- 'Home Networking' => 'network',
- 'Homeplug' => 'homeplug',
- 'Home Security' => 'home-security',
- 'Homeware' => 'homeware',
- 'Honda' => 'honda',
- 'Honey' => 'honey',
- 'Honeywell' => 'honeywell',
- 'Honor 6X' => 'honor-6x',
- 'Honor 7' => 'honor-7',
- 'Honor 8S' => 'honor-8s',
- 'Honor 8X' => 'honor-8x',
- 'Honor 8X Max' => 'honor-8x-max',
- 'Honor 9' => 'honor-9',
- 'Honor 9X' => 'honor-9x',
- 'Honor 10' => 'honor-10',
- 'Honor Band 5' => 'honor-band-5',
- 'Honor Play' => 'honor-play',
- 'Honor Smartphone' => 'honor',
- 'Honor View 20' => 'honor-view-20',
- 'Hoodie' => 'hoodie',
- 'Hoover' => 'hoover',
- 'Hori' => 'hori',
- 'Horizon: Zero Dawn' => 'horizon-zero-dawn',
- 'Hornby' => 'hornby',
- 'Horse Races' => 'horse-races',
- 'Hose' => 'hose',
- 'HOTAS' => 'hotas',
- 'Hotel' => 'hotel',
- 'Hotpoint' => 'hotpoint',
- 'Hotspot' => 'hotspot',
- 'Hot Tub' => 'hot-tub',
- 'Hot Water Bottle' => 'hot-water-bottle',
- 'Hot Wheels' => 'hot-wheels',
- 'Hozelock' => 'hozelock',
- 'HP' => 'hp',
- 'HP Envy' => 'hp-envy',
- 'HP Laptop' => 'hp-laptop',
- 'HP Omen' => 'hp-omen',
- 'HP Printer' => 'hp-printer',
- 'HTC' => 'htc',
- 'HTC 10' => 'htc-10',
- 'HTC Desire' => 'htc-desire',
- 'HTC One' => 'htc-one',
- 'HTC Smartphone' => 'htc-smartphone',
- 'HTC U11' => 'htc-u11',
- 'HTC Vive' => 'htc-vive',
- 'Huawei' => 'huawei',
- 'Huawei Freebuds 3' => 'huawei-freebuds-3',
- 'Huawei Headphones' => 'huawei-headphones',
- 'Huawei Mate 20' => 'huawei-mate-20',
- 'Huawei Mate 20 Pro' => 'huawei-mate-20-pro',
- 'Huawei Mate 30' => 'huawei-mate-30',
- 'Huawei Mate 30 Lite' => 'huawei-mate-30-lite',
- 'Huawei Mate 30 Pro' => 'huawei-mate-30-pro',
- 'Huawei Matebook' => 'huawei-matebook',
- 'Huawei MediaPad M3' => 'huawei-mediapad-m3',
- 'Huawei MediaPad M5' => 'huawei-mediapad-m5',
- 'Huawei MediaPad T3' => 'huawei-mediapad-t3',
- 'Huawei MediaPad T5' => 'huawei-mediapad-t5',
- 'Huawei P9' => 'huawei-p9',
- 'Huawei P10' => 'huawei-p10',
- 'Huawei P20' => 'huawei-p20',
- 'Huawei P20 Lite' => 'huawei-p20-lite',
- 'Huawei P20 Pro' => 'huawei-p20-pro',
- 'Huawei P30' => 'huawei-p30',
- 'Huawei P30 Lite' => 'huawei-p30-lite',
- 'Huawei P30 Pro' => 'huawei-p30-pro',
- 'Huawei P40' => 'huawei-p40',
- 'Huawei P40 Lite' => 'huawei-p40-lite',
- 'Huawei P40 Pro' => 'huawei-p40-pro',
- 'Huawei P Smart' => 'huawei-p-smart',
- 'Huawei Smartphone' => 'huawei-smartphone',
- 'Huawei Smartwatch' => 'huawei-smartwatch',
- 'Huawei Tablet' => 'huawei-tablet',
- 'Huawei Watch 2' => 'huawei-watch-2',
- 'Huawei Watch GT' => 'huawei-watch-gt',
- 'Huawei Watch GT2' => 'huawei-watch-gt2',
- 'Huawei Watch GT 2 Pro' => 'huawei-watch-gt-2-pro',
- 'Huawei Y7' => 'huawei-y7',
- 'Huggies' => 'huggies',
- 'Hulk' => 'hulk',
- 'Humax' => 'humax',
- 'Humidifier' => 'humidifier',
- 'Hunter' => 'hunter',
- 'HyperX' => 'hyperx',
- 'Hyrule Warriors' => 'hyrule-warriors',
- 'Hyundai' => 'hyundai',
- 'IAMS' => 'iams',
- 'iCandy' => 'icandy',
- 'Ice-Watch' => 'ice-watch',
- 'Ice Cream' => 'ice-cream',
- 'Ice Cream Maker' => 'ice-cream-maker',
- 'iMac' => 'apple-imac',
- 'iMac 2021' => 'imac-2021',
- 'Impact Driver' => 'impact-driver',
- 'Indesit' => 'indesit',
- 'Inflatable Boats' => 'boat',
- 'Inflatable Toys' => 'inflatable',
- 'Injustice' => 'injustice',
- 'Injustice 2' => 'injustice-2',
- 'Ink Cartridge' => 'ink',
- 'Inkjet Printer' => 'inkjet-printer',
- 'Innocent' => 'innocent',
- 'Instant Cameras' => 'instant-cameras',
- 'Instant Ink' => 'instant-ink',
- 'Instax Mini 9' => 'instax-mini-9',
- 'Insulation' => 'insulation',
- 'Insurance' => 'insurance',
- 'Intel' => 'intel',
- 'Intel Atom' => 'atom',
- 'Intel i3' => 'i3',
- 'Intel i5' => 'i5',
- 'Intel i7' => 'i7',
- 'Intel i9' => 'intel-i9',
- 'Internet' => 'internet',
- 'Internet Security' => 'internet-security',
- 'In the Night Garden' => 'in-the-night-garden',
- 'Intimate Care' => 'intimate-care',
- 'Introduce Yourself' => 'introduce-yourself',
- 'iOS Apps' => 'ios-apps',
- 'iPad' => 'ipad',
- 'iPad 2019' => 'ipad-2019',
- 'iPad 2020' => 'ipad-2020',
- 'iPad Air' => 'ipad-air',
- 'iPad Air 2019' => 'ipad-air-2019',
- 'iPad Air 2020' => 'ipad-air-2020',
- 'iPad Case' => 'ipad-case',
- 'iPad mini' => 'ipad-mini',
- 'iPad Pro' => 'ipad-pro',
- 'iPad Pro 11' => 'ipad-pro-11',
- 'iPad Pro 12.9' => 'ipad-pro-12-9',
- 'iPad Pro 2020' => 'ipad-pro-2020',
- 'iPad Pro 2021' => 'ipad-pro-2021',
- 'IP Camera' => 'ip-camera',
- 'iPhone' => 'iphone',
- 'iPhone 5s' => 'iphone-5s',
- 'iPhone 6' => 'iphone-6',
- 'iPhone 6 Plus' => 'iphone-6-plus',
- 'iPhone 6s' => 'iphone-6s',
- 'iPhone 6s Plus' => 'iphone-6s-plus',
- 'iPhone 7' => 'iphone-7',
- 'iPhone 7 Plus' => 'iphone-7-plus',
- 'iPhone 8' => 'iphone-8',
- 'iPhone 8 Plus' => 'iphone-8-plus',
- 'iPhone 11' => 'iphone-11',
- 'iPhone 11 Pro' => 'iphone-11-pro',
- 'iPhone 11 Pro Max' => 'iphone-11-pro-max',
- 'iPhone 12' => 'iphone-12',
- 'iPhone 12 mini' => 'iphone-12-mini',
- 'iPhone 12 Pro' => 'iphone-12-pro',
- 'iPhone 12 Pro Max' => 'iphone-12-pro-max',
- 'iPhone Accessories' => 'iphone-accessories',
- 'iPhone Case' => 'iphone-case',
- 'iPhone SE' => 'iphone-se',
- 'iPhone X' => 'iphone-x',
- 'iPhone Xr' => 'iphone-xr',
- 'iPhone Xs' => 'iphone-xs',
- 'iPhone Xs Max' => 'iphone-xs-max',
- 'iPod' => 'ipod',
- 'iPod Nano' => 'ipod-nano',
- 'iPod Shuffle' => 'ipod-shuffle',
- 'iPod Touch' => 'ipod-touch',
- 'Irish Whiskey' => 'irish-whisky',
- 'Irn Bru' => 'irn-bru',
- 'iRobot' => 'irobot',
- 'Iron' => 'iron',
- 'Ironing' => 'ironing',
- 'Ironing Board' => 'ironing-board',
- 'Iron Man' => 'iron-man',
- 'Issey Miyake' => 'issey-miyake',
- 'ITV' => 'itv',
- 'Jabra' => 'jabra',
- 'Jabra Elite 85h' => 'jabra-elite-85h',
- 'Jabra Elite Active 65t' => 'jabra-elite-active-65t',
- 'Jabra Elite Active 75t' => 'jabra-elite-active-75t',
- 'Jabra Headphones' => 'jabra-headphones',
- 'Jack &amp; Jones' => 'jack-and-jones',
- 'Jack Daniel&#039;s' => 'jack-daniels',
- 'Jacket' => 'jacket',
- 'Jack Wills' => 'jack-wills',
- 'Jack Wolfskin' => 'jack-wolfskin',
- 'Jaffa Cakes' => 'jaffa-cakes',
- 'Jägermeister' => 'jagermeister',
- 'Jameson' => 'jameson',
- 'Jamie Oliver' => 'jamie-oliver',
- 'Jaybird' => 'jaybird',
- 'JBL' => 'jbl',
- 'JBL Flip' => 'jbl-flip',
- 'JBL GO' => 'jbl-go',
- 'JBL Headphones' => 'jbl-headphones',
- 'JBL Link' => 'jbl-link',
- 'JBL Live' => 'jbl-live',
- 'JBL Tune' => 'jbl-tune',
- 'JCB' => 'jcb',
- 'Jean Paul Gaultier' => 'jean-paul-gautier',
- 'Jean Paul Gaultier Le Male' => 'le-male',
- 'Jeans' => 'jeans',
- 'Jelly Belly' => 'jelly-belly',
- 'Jewellery' => 'jewellery',
- 'Jigsaw' => 'jigsaw',
- 'Jim Beam' => 'jim-beam',
- 'Jimmy Choo' => 'jimmy-choo',
- 'JML' => 'jml',
- 'Jogging Bottoms' => 'jogging-bottoms',
- 'Johnnie Walker' => 'johnnie-walker',
- 'Johnson&#039;s' => 'johnsons',
- 'John West' => 'john-west',
- 'John Wick' => 'john-wick',
- 'JoJo Siwa' => 'jojo',
- 'Joop' => 'joop',
- 'Joseph Joseph' => 'joseph-joseph',
- 'Joules' => 'joules',
- 'Juice' => 'juice',
- 'Juicer' => 'juicer',
- 'Jumper' => 'jumper',
- 'Jurassic World' => 'jurassic-world',
- 'Jura Whisky' => 'jura',
- 'Just Cause' => 'just-cause',
- 'Just Cause 3' => 'just-cause-3',
- 'Just Cause 4' => 'just-cause-4',
- 'Just Dance' => 'just-dance',
- 'JVC' => 'jvc',
- 'K-Swiss' => 'k-swiss',
- 'Karcher' => 'karcher',
- 'Karcher Window Vacuum' => 'karcher-window-cleaner',
- 'Karen Millen' => 'karen-millen',
- 'Karrimor' => 'karrimor',
- 'Kaspersky' => 'kaspersky',
- 'Kayak' => 'kayak',
- 'Keg' => 'keg',
- 'Kellogg&#039;s' => 'kelloggs',
- 'Kellogg&#039;s Cornflakes' => 'cornflakes',
- 'Kellogg&#039;s Crunchy Nut' => 'crunchy-nut',
- 'Kenco' => 'kenco',
- 'Kenwood' => 'kenwood',
- 'Kenwood kMix' => 'kmix',
- 'Kenzo' => 'kenzo',
- 'Ketchup' => 'ketchup',
- 'Keter' => 'keter',
- 'Kettle' => 'kettle',
- 'Kettlebell' => 'kettlebell',
- 'Keyboard' => 'keyboard',
- 'KIA' => 'kia',
- 'Kickers' => 'kickers',
- 'Kid&#039;s Bike' => 'kids-bike',
- 'Kid&#039;s Clothes' => 'kids-clothes',
- 'Kid&#039;s Room' => 'kids-rooms',
- 'Kid&#039;s Shoes' => 'kids-shoes',
- 'Kidizoom' => 'kidizoom',
- 'Killzone' => 'killzone',
- 'Kilner' => 'kilner',
- 'Kinder' => 'kinder',
- 'Kindle' => 'kindle',
- 'Kindle Book' => 'kindle-book',
- 'Kindle Fire' => 'kindle-fire',
- 'Kindle Oasis' => 'kindle-oasis',
- 'Kindle Paperwhite' => 'kindle-paperwhite',
- 'Kingdom Come: Deliverance' => 'kingdom-come-deliverance',
- 'Kingdom Hearts' => 'kingdom-hearts',
- 'Kingdom Hearts 3' => 'kingdom-hearts-3',
- 'Kingdom Hearts: The Story So Far' => 'kingdom-hearts-the-story-so-far',
- 'King Kong' => 'king-kong',
- 'King Size Bed' => 'king-size',
- 'Kingsmill' => 'kingsmill',
- 'Kingston' => 'kingston',
- 'Kitchen' => 'kitchen',
- 'KitchenAid' => 'kitchenaid',
- 'Kitchen Appliances' => 'kitchen-appliances',
- 'Kitchen Knife' => 'knife',
- 'Kitchen Roll' => 'kitchen-roll',
- 'Kitchen Scale' => 'kitchen-scales',
- 'Kitchen Tap' => 'kitchen-tap',
- 'Kitchen Utensils' => 'kitchen-utensils',
- 'Kite' => 'kite',
- 'KitSound' => 'kitsound',
- 'Knickers' => 'knickers',
- 'Kobo' => 'kobo',
- 'Kodak' => 'kodak',
- 'Kodi' => 'kodi',
- 'Kohinoor' => 'kohinoor',
- 'Kopparberg' => 'kopparberg',
- 'Kraken' => 'kraken',
- 'Krispy Kreme' => 'krispy-kreme',
- 'Krups' => 'krups',
- 'KTC' => 'ktc',
- 'Kurt Geiger' => 'kurt-geiger',
- 'L&#039;Occitane' => 'loccitane',
- 'L.O.L. Surprise!' => 'lol-surprise',
- 'Lacoste' => 'lacoste',
- 'Ladder' => 'ladder',
- 'Lamaze' => 'lamaze',
- 'Lamb' => 'lamb',
- 'Laminate' => 'laminate',
- 'Laminator' => 'laminator',
- 'Lamp' => 'lamp',
- 'Lancôme' => 'lancome',
- 'Landmann' => 'landmann',
- 'Lantern' => 'lantern',
- 'Laphroaig' => 'laphroaig',
- 'Laptop' => 'laptop',
- 'Laptop Accessories' => 'laptop-accessories',
- 'Laptop Case' => 'laptop-case',
- 'Laptop Sleeve' => 'laptop-sleeve',
- 'Laser Printer' => 'laser-printer',
- 'Last Minute' => 'last-minute',
- 'Laundry Basket' => 'laundry-basket',
- 'Laura Ashley' => 'laura-ashley',
- 'Lavazza' => 'lavazza',
- 'Lavender' => 'lavender',
- 'Lawnmower' => 'lawnmower',
- 'Lay-Z-Spa' => 'lay-z-spa',
- 'LeapFrog' => 'leapfrog',
- 'Le Creuset' => 'le-creuset',
- 'LED Bulb' => 'led-bulbs',
- 'LED Light' => 'led-light',
- 'LED Strip Lights' => 'led-strip-lights',
- 'LED TV' => 'led-tv',
- 'Lee Stafford' => 'lee-stafford',
- 'Leffe' => 'leffe',
- 'Leggings' => 'leggings',
- 'Lego' => 'lego',
- 'Lego Advent Calendar' => 'lego-advent-calendar',
- 'Lego Architecture' => 'lego-architecture',
- 'Lego Art' => 'lego-art',
- 'Lego Batman' => 'lego-batman',
- 'Lego BrickHeadz' => 'lego-brickheadz',
- 'Lego City' => 'lego-city',
- 'Lego Classic' => 'lego-classic',
- 'Lego Creator' => 'lego-creator',
- 'Lego Dimensions' => 'lego-dimensions',
- 'Lego Disney' => 'lego-disney',
- 'Lego Dots' => 'lego-dots',
- 'Lego Duplo' => 'lego-duplo',
- 'Lego Friends' => 'lego-friends',
- 'LEGO Harry Potter' => 'lego-harry-potter',
- 'Lego Hidden Side' => 'lego-hidden-side',
- 'Legoland' => 'legoland',
- 'Lego Marvel' => 'lego-marvel',
- 'Lego Mindstorms' => 'lego-mindstorms',
- 'Lego Nexo Knights' => 'lego-nexo-knights',
- 'Lego Ninjago' => 'lego-ninjago',
- 'Lego Porsche' => 'lego-porsche',
- 'Lego Simpsons' => 'lego-simpsons',
- 'Lego Speed Champions' => 'lego-speed-champions',
- 'Lego Star Wars' => 'lego-star-wars',
- 'Lego Star Wars Millennium Falcon' => 'lego-star-wars-millennium-falcon',
- 'Lego Super Mario' => 'lego-mario',
- 'Lego Technic' => 'lego-technic',
- 'Lego VIDIYO' => 'lego-vidiyo',
- 'Lemonade' => 'lemonade',
- 'Lenor' => 'lenor',
- 'Lenovo' => 'lenovo',
- 'Lenovo IdeaPad' => 'lenovo-ideapad',
- 'Lenovo Laptop' => 'lenovo-laptop',
- 'Lenovo Tablet' => 'lenovo-tablet',
- 'Lenovo Thinkpad' => 'thinkpad',
- 'Lenovo Yoga Laptop' => 'lenovo-yoga-laptop',
- 'Lenovo Yoga Tablet' => 'lenovo-yoga',
- 'Les Paul' => 'les-paul',
- 'Levi&#039;s' => 'levi',
- 'Lexar' => 'lexar',
- 'LG' => 'lg',
- 'LG G3' => 'lg-g3',
- 'LG G5' => 'lg-g5',
- 'LG G6' => 'lg-g6',
- 'LG G7' => 'lg-g7',
- 'LG G8S ThinQ' => 'lg-g8s-thinq',
- 'LG OLED TV' => 'lg-oled-tv',
- 'LG Smartphone' => 'lg-smartphone',
- 'LG TV' => 'lg-tv',
- 'LG V30' => 'lg-v30',
- 'LG V40 ThinQ' => 'lg-v40-thinq',
- 'Life Insurance' => 'life-insurance',
- 'Life is Strange' => 'life-is-strange',
- 'Light Box' => 'light-box',
- 'Lighting' => 'lighting',
- 'Lightning Cable' => 'lightning-cable',
- 'Lightsaber' => 'lightsaber',
- 'Lindor' => 'lindor',
- 'Lindt' => 'lindt',
- 'Lingerie' => 'lingerie',
- 'Linksys' => 'linksys',
- 'Linx' => 'linx',
- 'Lion King' => 'lion-king',
- 'Lipstick' => 'lipstick',
- 'Lipsy' => 'lipsy',
- 'Little Tikes' => 'little-tikes',
- 'Liverpool F. C.' => 'liverpool-fc',
- 'Living Room' => 'living-room',
- 'Local Traffic' => 'local-traffic',
- 'Lodge' => 'lodge',
- 'Loft' => 'loft',
- 'Logitech' => 'logitech',
- 'Logitech G430' => 'logitech-g430',
- 'Logitech G703' => 'logitech-g703',
- 'Logitech G903' => 'logitech-g903',
- 'Logitech Harmony' => 'harmony',
- 'Logitech Keyboard' => 'logitech-keyboard',
- 'Logitech Mouse' => 'logitech-mouse',
- 'Logitech MX Master' => 'logitech-mx-master',
- 'Logitech MX Master 2S' => 'logitech-mx-master-2s',
- 'London Eye' => 'london-eye',
- 'London Zoo' => 'london-zoo',
- 'Longleat' => 'longleat',
- 'Long Sleeve' => 'long-sleeve',
- 'Lord of the Rings' => 'lord-of-the-rings',
- 'Lottery' => 'lottery',
- 'Lounger' => 'lounger',
- 'Lowepro' => 'lowepro',
- 'Lucozade' => 'lucozade',
- 'Luigi' => 'luigi',
- 'Luigi&#039;s Mansion' => 'luigis-manison',
- 'Luigi&#039;s Mansion 3' => 'luigis-mansion-3',
- 'Lunch Bag' => 'lunch-bag',
- 'Lunch Box' => 'lunch-box',
- 'Lurpak' => 'lurpak',
- 'Luton' => 'luton',
- 'Lyle &amp; Scott' => 'lyle-and-scott',
- 'Lynx' => 'lynx',
- 'M.2 SSD' => 'm2-ssd',
- 'MacBook' => 'macbook',
- 'MacBook Air' => 'macbook-air',
- 'MacBook Pro' => 'macbook-pro',
- 'MacBook Pro 13' => 'macbook-pro-13',
- 'MacBook Pro 15' => 'macbook-pro-15',
- 'MacBook Pro 16' => 'macbook-pro-16',
- 'Maclaren' => 'maclaren',
- 'Mac mini' => 'mac-mini',
- 'Madame Tussauds' => 'madame-tussauds',
- 'Mad Catz' => 'madcatz',
- 'Madden NFL' => 'madden',
- 'Madden NFL 20' => 'madden-nfl-20',
- 'Mad Max' => 'mad-max',
- 'Mafia 3' => 'mafia-3',
- 'Magazine' => 'magazine',
- 'Magimix' => 'magimix',
- 'Magners' => 'magners',
- 'Magnum' => 'magnum',
- 'Make Up' => 'make-up',
- 'Makeup Advent Calendar' => 'makeup-advent-calendar',
- 'Make Up Brush' => 'make-up-brush',
- 'Makita' => 'makita',
- 'Makita Drill' => 'makita-drill',
- 'Malibu' => 'malibu',
- 'Maltesers' => 'maltesers',
- 'MAM' => 'mam',
- 'Mamas &amp; Papas' => 'mamas-and-papas',
- 'Manchester United' => 'manchester-united',
- 'Manfrotto' => 'manfrotto',
- 'Manga' => 'manga',
- 'Manuka Honey' => 'manuka-honey',
- 'Marantz' => 'marantz',
- 'Marc Jacobs' => 'marc-jacobs',
- 'Marc Jacobs Daisy' => 'daisy',
- 'Mario &amp; Sonic at the Olympic Games: Tokyo 2020' => 'mario-and-sonic-tokyo-2020',
- 'Mario + Rabbids Kingdom Battle' => 'mario-rabbids-kingdom-battle',
- 'Mario Kart' => 'mario-kart',
- 'Mario Kart 8' => 'mario-kart-8',
- 'Mario Kart 8 Deluxe' => 'mario-kart-8-deluxe',
- 'Marmite' => 'marmite',
- 'Mars' => 'mars',
- 'Marshall' => 'marshall',
- 'Marshall Headphones' => 'marshall-headphones',
- 'Marvel' => 'marvel',
- 'Marvel&#039;s Spider-Man (PS4)' => 'spider-man-2018',
- 'Marvel&#039;s Spider-Man: Miles Morales' => 'spiderman-miles-morales',
- 'Mascara' => 'mascara',
- 'Massage' => 'massage',
- 'Mass Effect' => 'mass-effect',
- 'Mass Effect: Andromeda' => 'mass-effect-andromeda',
- 'Mastercard' => 'mastercard',
- 'Masterplug' => 'masterplug',
- 'Maternity &amp; Pregnancy' => 'maternity',
- 'Mattress' => 'mattress',
- 'Mattress Protector' => 'mattress-protector',
- 'Mattress Topper' => 'mattress-topper',
- 'Mavic' => 'mavic',
- 'Max Factor' => 'max-factor',
- 'Maxi Cosi' => 'maxi-cosi',
- 'Maximuscle' => 'maximuscle',
- 'Maxtor' => 'maxtor',
- 'Maybelline' => 'maybelline',
- 'Mayo' => 'mayo',
- 'Mazda' => 'mazda',
- 'McAfee' => 'mcafee',
- 'Meat &amp; Sausages' => 'meat',
- 'Meccano' => 'meccano',
- 'Mechanical Keyboard' => 'mechanical-keyboard',
- 'Medal of Honor' => 'medal-of-honor',
- 'Medela' => 'medela',
- 'Media Player' => 'media-player',
- 'Medievil' => 'medievil',
- 'Medion' => 'medion',
- 'Mega Bloks' => 'mega-bloks',
- 'Megathread' => 'megathread',
- 'Melissa &amp; Doug' => 'melissa',
- 'Memory Cards' => 'memory-cards',
- 'Memory Foam Mattress' => 'memory-foam',
- 'Men&#039;s Boots' => 'mens-boots',
- 'Men&#039;s Fragrance' => 'mens-fragrance',
- 'Men&#039;s Shoes' => 'mens-shoes',
- 'Men&#039;s Suit' => 'suit',
- 'Mercedes' => 'mercedes',
- 'Meridian' => 'meridian',
- 'Merlin' => 'merlin',
- 'Merrell' => 'merrell',
- 'Messenger Bag' => 'messenger-bag',
- 'Metal Gear Solid' => 'metal-gear-solid',
- 'Metro Exodus' => 'metro-exodus',
- 'Metroid' => 'metroid',
- 'Metro Series' => 'metro-series',
- 'Michael Kors' => 'michael-kors',
- 'Michelin' => 'michelin',
- 'Microphone' => 'microphone',
- 'Micro SD Card' => 'micro-sd',
- 'Micro SDHC' => 'micro-sdhc',
- 'Micro SDXC' => 'micro-sdxc',
- 'Microserver' => 'microserver',
- 'Microsoft' => 'microsoft',
- 'Microsoft Flight Simulator' => 'microsoft-flight-simulator',
- 'Microsoft Office' => 'microsoft-office',
- 'Microsoft Points' => 'microsoft-points',
- 'Microsoft Software' => 'microsoft-software',
- 'Microsoft Surface Book' => 'surface-book',
- 'Microsoft Surface Laptop' => 'surface',
- 'Microsoft Surface Pro 6' => 'surface-pro-6',
- 'Microsoft Surface Pro 7' => 'surface-pro-7',
- 'Microsoft Surface Tablet' => 'microsoft-surface-tablet',
- 'Microwave' => 'microwave',
- 'Middle Earth' => 'middle-earth',
- 'Middle Earth: Shadow of Mordor' => 'shadow-of-mordor',
- 'Middle Earth: Shadow of War' => 'middle-earth-shadow-of-war',
- 'Miele' => 'miele',
- 'Miele Vacuum Cleaner' => 'miele-vacuum-cleaner',
- 'Milk' => 'milk',
- 'Milk Frother' => 'milk-frother',
- 'Milk Tray' => 'milk-tray',
- 'Milwaukee' => 'milwaukee',
- 'Mince' => 'mince',
- 'Minecraft Game' => 'minecraft',
- 'Mineral Water' => 'mineral-water',
- 'Mini Fridge' => 'mini-fridge',
- 'Minions' => 'minions',
- 'Mini PC' => 'mini-pc',
- 'Minky' => 'minky',
- 'Mira' => 'mira',
- 'Mirror' => 'mirror',
- 'Mirror&#039;s Edge' => 'mirrors-edge',
- 'Misc' => 'misc',
- 'Misfit' => 'misfit',
- 'Mitre Saw' => 'mitre-saw',
- 'Mitsubishi' => 'mitsubishi',
- 'Mixer &amp; Blender' => 'mixer-and-blender',
- 'Mobile Contracts' => 'mobile-contract',
- 'Mobile Phone' => 'mobile-phone',
- 'Model Building' => 'model-building',
- 'Moët' => 'moet',
- 'Molton Brown' => 'molton-brown',
- 'Money Saving Tips and Tricks' => 'money-saving-tips',
- 'Monitor' => 'monitor',
- 'Monopoly' => 'monopoly',
- 'Monsoon' => 'monsoon',
- 'Monster Energy' => 'monster-energy',
- 'Monster High' => 'monster-high',
- 'Monster Hunter' => 'monster-hunter',
- 'Monster Hunter World' => 'monster-hunter-world',
- 'Mont Blanc' => 'mont-blanc',
- 'Mop' => 'mop',
- 'Morphy Richards' => 'morphy-richards',
- 'Mortal Kombat' => 'mortal-kombat',
- 'Mortal Kombat 11' => 'mortal-kombat-11',
- 'Mortgage' => 'mortgage',
- 'Moschino' => 'moschino',
- 'Moses Basket' => 'moses-basket',
- 'MOT' => 'mot',
- 'Motherboard' => 'motherboard',
- 'Moto 360' => 'moto-360',
- 'Moto E' => 'moto-e',
- 'Moto G' => 'moto-g',
- 'Moto G4' => 'moto-g4',
- 'Moto G5' => 'moto-g5',
- 'Moto G6' => 'moto-g6',
- 'Moto G7' => 'moto-g7',
- 'Motorcycle' => 'motorcycle',
- 'Motorcycle Accessories' => 'motorcycle-accessories',
- 'Motorcycle Helmet' => 'motorcycle-helmet',
- 'Motorola' => 'motorola',
- 'Motorola Smartphone' => 'motorola-smartphone',
- 'Moto X' => 'moto-x',
- 'Moto Z' => 'moto-z',
- 'Mountain Bike' => 'mountain-bike',
- 'Mouse &amp; Keyboard Bundles' => 'mouse-and-keyboard-bundle',
- 'Mouse Mat' => 'mouse-mat',
- 'Mouthwash' => 'mouthwash',
- 'Movie and TV Box Set' => 'box-set',
- 'Movies &amp; Series' => 'movie',
- 'MP3 Player' => 'mp3-player',
- 'Mr Kipling' => 'mr-kipling',
- 'Mr Men' => 'mr-men',
- 'MSI' => 'msi',
- 'MSI Laptop' => 'msi-laptop',
- 'Muc-Off' => 'muc-off',
- 'Mug' => 'mug',
- 'Muller' => 'muller',
- 'Multi-Room Audio System' => 'multi-room-audio-system',
- 'Multitool' => 'multitool',
- 'Museums' => 'museums',
- 'Music' => 'music',
- 'Musical Instruments' => 'musical-instrument',
- 'Music App' => 'music-app',
- 'Music Streaming' => 'music-streaming',
- 'My Little Pony' => 'my-little-pony',
- 'Nail Gun' => 'nail-gun',
- 'Nail Polish' => 'nail-polish',
- 'Nails' => 'nails',
- 'Nails Inc.' => 'nails-inc',
- 'Nakd' => 'nakd',
- 'Nando&#039;s' => 'nandos',
- 'Nappy' => 'nappy',
- 'NAS' => 'nas',
- 'National Express Ticket' => 'national-express',
- 'National Trust' => 'national-trust',
- 'Nature Observation' => 'nature-observation',
- 'NatWest' => 'natwest',
- 'NBA 2K' => 'nba-2k',
- 'NBA Live' => 'nba',
- 'Necklace' => 'necklace',
- 'Need for Speed' => 'need-for-speed',
- 'Need for Speed: Payback' => 'need-for-speed-payback',
- 'Need for Speed Heat' => 'need-for-speed-heat',
- 'Neff' => 'neff',
- 'Nerf Guns' => 'nerf',
- 'Nescafé Azera' => 'azera',
- 'Nescafé Coffee' => 'nescafe',
- 'Nespresso' => 'nespresso',
- 'Nespresso Coffee Machine' => 'nespresso-coffee-machine',
- 'Nest Hello' => 'nest-hello',
- 'Nestlé' => 'nestle',
- 'Nest Learning Thermostat' => 'nest-learning-thermostat',
- 'Nestlé Cheerios' => 'cheerios',
- 'Nestlé Shreddies' => 'shreddies',
- 'Netatmo' => 'netatmo',
- 'Netflix' => 'netflix',
- 'Netgear' => 'netgear',
- 'Netgear Arlo' => 'arlo',
- 'New Balance' => 'new-balance',
- 'New Balance Trainers' => 'new-balance-trainers',
- 'New Look' => 'new-look',
- 'Newspapers' => 'newspapers',
- 'Nextbase' => 'nextbase',
- 'NFL' => 'nfl',
- 'NHL' => 'nhl',
- 'NHL 20' => 'nhl-20',
- 'NHS' => 'nhs',
- 'NieR: Automata' => 'nier',
- 'Night Light' => 'night-light',
- 'Nike' => 'nike',
- 'Nike Air Max' => 'nike-air-max',
- 'Nike Air Max 200' => 'nike-air-max-200',
- 'Nike Air Max 270' => 'nike-air-max-270',
- 'Nike Air Max 720' => 'nike-air-max-720',
- 'Nike Free' => 'nike-free',
- 'Nike Huarache' => 'nike-huarache',
- 'Nike Jordan' => 'jordan',
- 'Nike Presto' => 'nike-presto',
- 'Nike Roshe' => 'nike-roshe',
- 'Nike Trainers' => 'nike-shoes',
- 'Nikon' => 'nikon',
- 'Nikon Camera' => 'nikon-camera',
- 'Nikon Coolpix' => 'nikon-coolpix',
- 'Nikon D3400' => 'nikon-d3400',
- 'Nikon Lens' => 'nikon-lens',
- 'Nilfisk' => 'nilfisk',
- 'Ni No Kuni' => 'ni-no-kuni',
- 'Ni No Kuni: Wrath of the White Witch' => 'ni-no-kuni-white-witch',
- 'Ni No Kuni II: Revenant Kingdom' => 'ni-no-kuni-2',
- 'Nintendo' => 'nintendo',
- 'Nintendo 2DS' => '2ds',
- 'Nintendo 3DS' => '3ds',
- 'Nintendo 3DS Game' => '3ds-games',
- 'Nintendo 3DS XL' => 'nintendo-3ds-xl',
- 'Nintendo Accessories' => 'nintendo-accessories',
- 'Nintendo Classic Mini' => 'nintendo-classic-mini',
- 'Nintendo DS Game' => 'ds-games',
- 'Nintendo Labo' => 'switch-labo',
- 'Nintendo Switch' => 'nintendo-switch',
- 'Nintendo Switch Accessories' => 'switch-accessories',
- 'Nintendo Switch Case' => 'switch-case',
- 'Nintendo Switch Controller' => 'switch-controller',
- 'Nintendo Switch Game' => 'switch-game',
- 'Nintendo Switch Joy-Con' => 'switch-joy-con',
- 'Nintendo Switch Lite' => 'nintendo-switch-lite',
- 'Nintendo Switch Pro Controller' => 'switch-pro-controller',
- 'Nioh' => 'nioh',
- 'Nissan' => 'nissan',
- 'Nivea' => 'nivea',
- 'No7' => 'no7',
- 'Noise Cancelling Headphones' => 'noise-cancelling-headphones',
- 'Nokia' => 'nokia',
- 'Nokia Smartphones' => 'nokia-mobile',
- 'No Man&#039;s Sky' => 'no-man-s-sky',
- 'Noodles' => 'noodles',
- 'Norton' => 'norton',
- 'Now' => 'now-tv',
- 'Numatic' => 'numatic',
- 'Nursery' => 'nursery',
- 'Nutella' => 'nutella',
- 'NutriBullet' => 'nutribullet',
- 'Nutri Ninja' => 'nutri-ninja',
- 'Nuts' => 'nuts',
- 'Nvidia' => 'nvidia',
- 'Nvidia GeForce' => 'geforce',
- 'Nvidia Shield' => 'nvidia-shield',
- 'NYX' => 'nyx',
- 'NZXT' => 'nzxt',
- 'O2' => 'o2',
- 'O2 Refresh' => 'o2-refresh',
- 'Oakley' => 'oakley',
- 'Octonauts' => 'octonauts',
- 'Oculus Game' => 'oculus-game',
- 'Oculus Go' => 'oculus-go',
- 'Oculus Quest' => 'oculus-quest',
- 'Oculus Rift' => 'oculus',
- 'Oculus Rift S' => 'oculus-rift-s',
- 'Odeon' => 'odeon',
- 'Office' => 'office',
- 'Office Chair' => 'office-chair',
- 'Official Announcements' => 'official-announcements',
- 'Olay' => 'olay',
- 'OLED TV' => 'oled',
- 'Olive Oil' => 'olive-oil',
- 'Olympus' => 'olympus',
- 'Omega Seamaster' => 'omega-seamaster',
- 'Omega Speedmaster' => 'omega-speedmaster',
- 'Omega Watches' => 'omega-watch',
- 'OnePlus 3' => 'oneplus-3',
- 'OnePlus 5' => 'oneplus-5',
- 'OnePlus 6' => 'oneplus-6',
- 'OnePlus 6T' => 'oneplus-6t',
- 'OnePlus 7' => 'oneplus-7',
- 'OnePlus 7 Pro' => 'oneplus-7-pro',
- 'OnePlus 7T' => 'oneplus-7t',
- 'OnePlus 7T Pro' => 'one-plus-7t-pro',
- 'OnePlus 8' => 'oneplus-8',
- 'OnePlus 8 Pro' => 'oneplus-8-pro',
- 'OnePlus 8T' => 'oneplus-8t',
- 'OnePlus 9' => 'oneplus-9',
- 'OnePlus 9 Pro' => 'oneplus-9-pro',
- 'OnePlus Nord' => 'oneplus-nord',
- 'OnePlus Nord N10 5G' => 'oneplus-n10',
- 'OnePlus Nord N100' => 'oneplus-n100',
- 'OnePlus Smartphone' => 'oneplus',
- 'Onesie' => 'onesie',
- 'Onkyo' => 'onkyo',
- 'Online Courses' => 'online-courses',
- 'Operating System' => 'operating-system',
- 'Oppo Find X2 Lite' => 'oppo-find-x2-lite',
- 'Oppo Find X2 Neo' => 'oppo-find-x2-neo',
- 'Oppo Find X2 Pro' => 'oppo-find-x2-pro',
- 'Oppo Reno' => 'oppo-reno',
- 'Oppo Reno4 5G' => 'oppo-reno4',
- 'Oppo Reno4 Z 5G' => 'oppo-reno4-z',
- 'Oppo Smartphone' => 'oppo-smartphone',
- 'Opticians' => 'opticians',
- 'Optoma' => 'optoma',
- 'Oral-B' => 'oral-b',
- 'Oral-B Toothbrush' => 'oral-b-toothbrush',
- 'Oreo' => 'oreo',
- 'Origin' => 'origin',
- 'Original Penguin' => 'penguin',
- 'Orla Kiely' => 'orla-kiely',
- 'Osprey' => 'osprey',
- 'Osram' => 'osram',
- 'Other' => 'other-deals',
- 'Ottoman' => 'ottoman',
- 'Oukitel' => 'oukitel',
- 'Outdoor Clothing' => 'outdoor-clothing',
- 'Outdoor Lighting' => 'outdoor-lighting',
- 'Outdoor Sports &amp; Camping' => 'outdoor',
- 'Outdoor Toys' => 'outdoor-toys',
- 'Outlast' => 'outlast',
- 'Outlet' => 'outlet',
- 'Outwell' => 'outwell',
- 'Oven' => 'oven',
- 'Overcooked' => 'overcooked',
- 'Overcooked 2' => 'overcooked-2',
- 'Overwatch' => 'overwatch',
- 'Oyster Card' => 'oyster',
- 'Package Holidays' => 'holiday',
- 'Paco Rabanne' => 'paco-rabanne',
- 'Paco Rabanne 1 Million' => 'paco-rabanne-1-million',
- 'Paco Rabanne Lady Million' => 'lady-million',
- 'Paddling Pool' => 'paddling-pool',
- 'Padlock' => 'padlock',
- 'Paint' => 'paint',
- 'Paint Brush' => 'paint-brush',
- 'Pampers' => 'pampers',
- 'Panasonic' => 'panasonic',
- 'Panasonic Camera' => 'panasonic-camera',
- 'Panasonic Lumix' => 'lumix',
- 'Panasonic TV' => 'panasonic-tv',
- 'Pandora' => 'pandora',
- 'Panini' => 'panini',
- 'Panini Stickers' => 'panini-stickers',
- 'Papa Johns' => 'papa-johns',
- 'Paper Mario' => 'paper-mario',
- 'Parasol' => 'parasol',
- 'Parcel and Delivery Services' => 'parcel',
- 'Parka' => 'parka',
- 'Parking' => 'parking',
- 'Parrot' => 'parrot',
- 'Paul Smith' => 'paul-smith',
- 'PAW Patrol' => 'paw-patrol',
- 'Payday' => 'payday',
- 'Payday 2' => 'payday-2',
- 'PAYG' => 'payg',
- 'Pay Monthly' => 'pay-monthly',
- 'PC' => 'pc',
- 'PC Case' => 'pc-case',
- 'PC Game' => 'pc-game',
- 'PC Gaming Accessories' => 'pc-gaming-accessories',
- 'PC Gaming Systems' => 'pc-gaming-systems',
- 'PC Mouse' => 'mouse',
- 'PC Parts' => 'pc-parts',
- 'Peanut Butter' => 'peanut-butter',
- 'Peanuts' => 'peanuts',
- 'Pedometer' => 'pedometer',
- 'Pentax' => 'pentax',
- 'Peppa Pig' => 'peppa-pig',
- 'PepperBonus' => 'pepperbonus',
- 'Pepsi' => 'pepsi',
- 'Perfume' => 'perfume',
- 'Persil' => 'persil',
- 'Persona' => 'persona',
- 'Persona 5' => 'persona-5',
- 'Personal Care &amp; Hygiene' => 'personal-care-hygiene',
- 'Petrol and Diesel' => 'petrol',
- 'Pet Supplies' => 'pets',
- 'Peugeot' => 'peugeot',
- 'PG Tips' => 'pg-tips',
- 'Philips' => 'philips',
- 'Philips Alarm Clock' => 'philips-alarm-clock',
- 'Philips Avent' => 'avent',
- 'Philips Hue' => 'philips-hue',
- 'Philips Lumea' => 'lumea',
- 'Philips OneBlade' => 'philips-one-blade',
- 'Philips Senseo' => 'philips-senseo',
- 'Philips Senseo Coffee Machine' => 'philips-senseo-coffee-machine',
- 'Philips Shaver' => 'philips-shaver',
- 'Philips Sonicare' => 'sonicare',
- 'Philips TV' => 'philips-tv',
- 'Phone Holder' => 'phone-holder',
- 'Phones &amp; Accessories' => 'phone',
- 'Photo &amp; Cameras' => 'photo-video',
- 'Photo &amp; Video App' => 'photo-video-app',
- 'Photo Editing' => 'photo-editing',
- 'Photo Frame' => 'photo-frame',
- 'Photo Paper' => 'photo-paper',
- 'Piano' => 'piano',
- 'Picnic &amp; Outdoor Cooking' => 'picnic',
- 'Pikmin 3 Deluxe' => 'pikmin-3-deluxe',
- 'Pillow' => 'pillow',
- 'Pimm&#039;s' => 'pimms',
- 'Pioneer' => 'pioneer',
- 'Pirate Toys' => 'pirates',
- 'PIR Lights' => 'pir',
- 'Pixel C' => 'pixel-c',
- 'Piz Buin' => 'piz-buin',
- 'Pizza' => 'pizza',
- 'Pizza Stone' => 'pizza-stone',
- 'Planer' => 'planer',
- 'Planet Earth' => 'planet-earth',
- 'Plant' => 'plant',
- 'Plant Pot' => 'plant-pots',
- 'Plants vs. Zombies: Battle for Neighborville' => 'battle-for-neighborville',
- 'Plants vs Zombies' => 'plants-vs-zombies',
- 'Play-Doh' => 'play-doh',
- 'PlayerUnknown&#039;s Battlegrounds' => 'playerunknown-s-battlegrounds',
- 'Playhouse' => 'playhouse',
- 'Playing Cards' => 'playing-cards',
- 'Playmat' => 'playmat',
- 'Playmobil' => 'playmobil',
- 'Playmobil Advent Calendar' => 'playmobil-advent-calendar',
- 'PlayStation' => 'playstation',
- 'PlayStation 5 DualSense Controller' => 'ps5-controller',
- 'PlayStation Accessories' => 'playstation-accessories',
- 'PlayStation Classic' => 'playstation-classic',
- 'PlayStation Move' => 'playstation-move',
- 'PlayStation Now' => 'playstation-now',
- 'PlayStation Plus' => 'playstation-plus',
- 'PlayStation VR' => 'playstation-vr',
- 'PlayStation VR Aim Controller' => 'aim-controller-ps4',
- 'Pliers' => 'pliers',
- 'Plumbing &amp; Fittings' => 'plumbing-and-fitting',
- 'Plus Size' => 'plus-size',
- 'PNY' => 'pny',
- 'POCO F2 Pro' => 'poco-f2-pro',
- 'POCO F3' => 'poco-f3',
- 'Poco M3' => 'poco-m3',
- 'POCO X3' => 'poco-x3',
- 'POCO X3 Pro' => 'poco-x3-pro',
- 'Pokémon' => 'pokemon',
- 'Pokémon: Let&#039;s Go' => 'pokemon-lets-go',
- 'Pokémon Go' => 'pokemon-go',
- 'Pokemon Sword and Shield' => 'pokemon-sword-and-shield',
- 'Pokémon Ultra Sun and Ultra Moon' => 'pokemon-ultra-sun-ultra-moon',
- 'Poker' => 'poker',
- 'Pokken Tournament' => 'pokken-tournament',
- 'Polaroid' => 'polaroid',
- 'Police Toys' => 'police',
- 'Polo Shirt' => 'polo-shirt',
- 'Pool' => 'pool',
- 'Pool &amp; Snooker' => 'pool-table',
- 'Popcorn' => 'popcorn',
- 'Pork' => 'pork',
- 'Porridge &amp; Oats' => 'porridge-and-oats',
- 'Portable Wireless Speaker' => 'wireless-speaker',
- 'Poster' => 'poster',
- 'Pots and Pans' => 'pan',
- 'Potty' => 'potty',
- 'Power Bank' => 'power-bank',
- 'Powerbeats Pro' => 'powerbeats-pro',
- 'Power Dental Flosser' => 'floss',
- 'Powerline' => 'powerline',
- 'Power Rangers' => 'power-rangers',
- 'Power Tool' => 'power-tool',
- 'Prada' => 'prada',
- 'Pram' => 'pram',
- 'Pregnancy' => 'pregnancy',
- 'Prescription Glasses' => 'prescription-glasses',
- 'Pressure Cooker' => 'pressure-cooker',
- 'Pressure Washer' => 'pressure-washer',
- 'Price Glitch' => 'price-glitch',
- 'Prime Gaming' => 'twitch',
- 'Pringles' => 'pringles',
- 'Printer &amp; Printer Supplies' => 'printer',
- 'Printer Supplies' => 'printer-supplies',
- 'Productivity App' => 'productivity-app',
- 'Pro Evolution Soccer' => 'pro-evolution-soccer',
- 'Pro Evolution Soccer 2018' => 'pro-evolution-soccer-2018',
- 'Pro Evolution Soccer 2019' => 'pro-evolution-soccer-2019',
- 'Pro Evolution Soccer 2020' => 'pes-2020',
- 'Project Cars' => 'project-cars',
- 'Project Cars 2' => 'project-cars-2',
- 'Projector' => 'projector',
- 'Protein' => 'protein',
- 'Protein Bars' => 'protein-bars',
- 'Protein Shaker' => 'shaker',
- 'PS4' => 'ps4-slim',
- 'PS4 Camera' => 'ps4-camera',
- 'PS4 Controller' => 'ps4-controller',
- 'PS4 Games' => 'ps4-games',
- 'PS4 Headset' => 'ps4-headset',
- 'PS4 Pro' => 'ps4-pro',
- 'PS5' => 'ps5',
- 'PS5 Games' => 'ps5-game',
- 'PSU' => 'psu',
- 'Public Transport' => 'public-transport',
- 'Pukka' => 'pukka',
- 'Pulse Light Epilator' => 'pulse-light-epilator',
- 'Puma' => 'puma',
- 'Puma Trainers' => 'puma-trainers',
- 'Puppy Supplies' => 'puppy',
- 'Purse' => 'purse',
- 'Pushchair' => 'pushchair',
- 'Pushchairs and Strollers' => 'baby-transport',
- 'Puzzle' => 'puzzle',
- 'PVR' => 'pvr',
- 'Pyjamas' => 'pyjamas',
- 'Pyrex' => 'pyrex',
- 'Q Acoustics' => 'q-acoustics',
- 'QNAP' => 'qnap',
- 'Qualcast' => 'qualcast',
- 'Quality Street' => 'quality-street',
- 'Quantum Break' => 'quantum-break',
- 'Quechua' => 'quechua',
- 'Quick Charge' => 'quick-charge',
- 'Quiksilver' => 'quiksilver',
- 'Quinny' => 'quinny',
- 'Quorn' => 'quorn',
- 'Rab' => 'rab',
- 'Radeon RX 480' => 'rx-480',
- 'Radeon RX 5700' => 'radeon-rx-5700',
- 'Radeon RX 5700 XT' => 'radeon-rx-5700-xt',
- 'Radeon RX 6800' => 'radeon-rx-6800',
- 'Radeon RX 6800 XT' => 'radeon-rx-6800-xt',
- 'Radeon RX 6900 XT' => 'radeon-rx-6900-xt',
- 'Radiator' => 'radiator',
- 'Radio' => 'radio',
- 'Radley' => 'radley',
- 'Rage 2' => 'rage-2',
- 'Railcard' => 'railcard',
- 'Rainbow Six' => 'rainbow-six',
- 'Rake' => 'rake',
- 'Ralph Lauren' => 'ralph-lauren',
- 'RAM' => 'ram',
- 'Raspberry Pi' => 'raspberry-pi',
- 'Ratchet' => 'ratchet',
- 'Ratchet and Clank' => 'ratchet-and-clank',
- 'Rattan Garden Furniture' => 'rattan',
- 'RAVPower' => 'ravpower',
- 'Ray Ban' => 'ray-ban',
- 'Razer' => 'razer',
- 'Razor' => 'razor',
- 'Razor Blade' => 'razor-blade',
- 'Real Madrid' => 'real-madrid',
- 'Realme Smartphones' => 'realme-smartphone',
- 'Real Techniques' => 'real-techniques',
- 'Recliner' => 'recliner',
- 'ReCore' => 'recore',
- 'Recreational Sports' => 'recreational-sports',
- 'Red Bull' => 'red-bull',
- 'Red Dead Redemption' => 'red-dead-redemption',
- 'Red Dead Redemption 2' => 'red-dead-redemption-2',
- 'Redex' => 'redex',
- 'Red Kite' => 'red-kite',
- 'Reebok' => 'reebok',
- 'Reese&#039;s' => 'reeses',
- 'Regatta' => 'regatta',
- 'Regina' => 'regina',
- 'Remington' => 'remington',
- 'Remote Control Car' => 'remote-control-car',
- 'Renault' => 'renault',
- 'Resident Evil' => 'resident-evil',
- 'Resident Evil 2' => 'resident-evil-2',
- 'Resident Evil 7' => 'resident-evil-7',
- 'Restaurant, Café &amp; Pub' => 'restaurant',
- 'Retailer Offers and Issues' => 'retailer-offers-and-issues',
- 'Ribena' => 'ribena',
- 'Rice' => 'rice',
- 'Rice Cooker' => 'rice-cooker',
- 'Rick and Morty' => 'rick-and-morty',
- 'Ricoh' => 'ricoh',
- 'Ride On' => 'ride-on',
- 'Ring' => 'ring',
- 'Ring Door View Cam' => 'ring-door-view-cam',
- 'Ring Fit Adventures' => 'ring-fit-adventures',
- 'Ring Stick Up Cam' => 'ring-stick-up-cam',
- 'Ring Video Doorbell' => 'ring-video-doorbell',
- 'Ring Video Doorbell 2' => 'ring-video-doorbell-2',
- 'Ring Video Doorbell 3' => 'ring-video-doorbell-3',
- 'Ring Video Doorbell Pro' => 'ring-video-doorbell-pro',
- 'Road Bike' => 'road-bike',
- 'Roaming' => 'roaming',
- 'Robinsons' => 'robinsons',
- 'Robotic Lawnmower' => 'robotic-lawnmower',
- 'Robot Vacuum Cleaner' => 'robot-vacuum-cleaner',
- 'Rock Band' => 'rock-band',
- 'Rocket League' => 'rocket-league',
- 'Rocking Horse' => 'rocking-horse',
- 'Rogue One: A Star Wars Story' => 'rogue-one',
- 'Roku' => 'roku',
- 'Rolex' => 'rolex',
- 'Rollerskates' => 'skate',
- 'Ronseal' => 'ronseal',
- 'Roof Box' => 'roof-box',
- 'Roses' => 'roses',
- 'Rotary' => 'rotary',
- 'Router' => 'router',
- 'Rowenta' => 'rowenta',
- 'RTX 2060' => 'rtx-2060',
- 'RTX 2070' => 'rtx-2070',
- 'RTX 2080' => 'rtx-2080',
- 'RTX 2080 Ti' => 'rtx-2080-ti',
- 'RTX 3070' => 'rtx-3070',
- 'RTX 3080' => 'rtx-3080',
- 'RTX 3090' => 'rtx-3090',
- 'Rug' => 'rug',
- 'Rugby' => 'rugby',
- 'Rum' => 'rum',
- 'Running' => 'running',
- 'Running Shoes' => 'running-shoes',
- 'Russell Hobbs' => 'russell-hobbs',
- 'RX 570' => 'rx-570',
- 'RX 580' => 'rx-580',
- 'RX 590' => 'rx-590',
- 'RX Vega 56' => 'rx-vega-56',
- 'RX Vega 64' => 'rx-vega-64',
- 'Ryanair' => 'ryanair',
- 'Ryobi' => 'ryobi',
- 'Safari' => 'safari',
- 'Safety Boots' => 'safety-boots',
- 'Sage by Heston Blumenthal' => 'sage',
- 'Saints Row' => 'saints-row',
- 'Saitek' => 'saitek',
- 'Sale' => 'sale',
- 'Salmon' => 'salmon',
- 'Salomon' => 'salomon',
- 'Salter' => 'salter',
- 'Samsonite' => 'samsonite',
- 'Samsung' => 'samsung',
- 'Samsung Ecobubble' => 'ecobubble',
- 'Samsung Fridge' => 'samsung-fridge',
- 'Samsung Galaxy' => 'samsung-galaxy',
- 'Samsung Galaxy A10' => 'samsung-galaxy-a10',
- 'Samsung Galaxy A20e' => 'samsung-galaxy-a20e',
- 'Samsung Galaxy A40' => 'samsung-galaxy-a40',
- 'Samsung Galaxy A42 5G' => 'samsung-galaxy-a42-5g',
- 'Samsung Galaxy A50' => 'samsung-galaxy-a50',
- 'Samsung Galaxy A51' => 'samsung-galaxy-a51',
- 'Samsung Galaxy A52 5G' => 'samsung-galaxy-a52',
- 'Samsung Galaxy A60' => 'samsung-galaxy-a60',
- 'Samsung Galaxy A70' => 'samsung-galaxy-a70',
- 'Samsung Galaxy A71' => 'samsung-galaxy-a71',
- 'Samsung Galaxy A72' => 'samsung-galaxy-a72',
- 'Samsung Galaxy A80' => 'samsung-galaxy-a80',
- 'Samsung Galaxy A90' => 'samsung-galaxy-a90',
- 'Samsung Galaxy Buds' => 'samsung-galaxy-buds',
- 'Samsung Galaxy Buds+' => 'samsung-galaxy-buds-plus',
- 'Samsung Galaxy Buds Live' => 'samsung-galaxy-buds-live',
- 'Samsung Galaxy Buds Pro' => 'samsung-galaxy-buds-pro',
- 'Samsung Galaxy Fold' => 'samsung-galaxy-fold',
- 'Samsung Galaxy J5' => 'galaxy-j5',
- 'Samsung Galaxy Note' => 'samsung-galaxy-note',
- 'Samsung Galaxy Note 8' => 'samsung-galaxy-note-8',
- 'Samsung Galaxy Note 9' => 'samsung-galaxy-note-9',
- 'Samsung Galaxy Note 10' => 'samsung-galaxy-note-10',
- 'Samsung Galaxy Note 10+' => 'samsung-galaxy-note-10-plus',
- 'Samsung Galaxy Note20' => 'samsung-galaxy-note20',
- 'Samsung Galaxy Note20 Ultra' => 'samsung-galaxy-note20-ultra',
- 'Samsung Galaxy S6' => 'samsung-galaxy-s6',
- 'Samsung Galaxy S7' => 'samsung-galaxy-s7',
- 'Samsung Galaxy S7 Edge' => 'samsung-galaxy-s7-edge',
- 'Samsung Galaxy S8' => 'samsung-galaxy-s8',
- 'Samsung Galaxy S8+' => 'samsung-s8-plus',
- 'Samsung Galaxy S9' => 'samsung-galaxy-s9',
- 'Samsung Galaxy S9 Plus' => 'samsung-s9-plus',
- 'Samsung Galaxy S10' => 'samsung-galaxy-s10',
- 'Samsung Galaxy S10 Lite' => 'samsung-galaxy-s10-lite',
- 'Samsung Galaxy S10 Plus' => 'samsung-galaxy-s10-plus',
- 'Samsung Galaxy S10e' => 'samsung-galaxy-s10e',
- 'Samsung Galaxy S20' => 'samsung-galaxy-s20',
- 'Samsung Galaxy S20 FE' => 'samsung-galaxy-s20-fe',
- 'Samsung Galaxy S20 Ultra' => 'samsung-galaxy-s20-ultra',
- 'Samsung Galaxy S20+' => 'samsung-galaxy-s20-plus',
- 'Samsung Galaxy S21 5G' => 'samsung-galaxy-s21-5g',
- 'Samsung Galaxy S21 Ultra 5G' => 'samsung-galaxy-s21-ultra-5g',
- 'Samsung Galaxy S21+ 5G' => 'samsung-galaxy-s21-plus-5g',
- 'Samsung Galaxy Tab' => 'samsung-galaxy-tab',
- 'Samsung Galaxy Tab A' => 'samsung-galaxy-tab-a',
- 'Samsung Galaxy Tab A7' => 'samsung-galaxy-tab-a7',
- 'Samsung Galaxy Tab S' => 'samsung-galaxy-tab-s',
- 'Samsung Galaxy Tab S4' => 'samsung-galaxy-tab-s4',
- 'Samsung Galaxy Tab S5e' => 'samsung-galaxy-tab-s5e',
- 'Samsung Galaxy Tab S6' => 'samsung-galaxy-tab-s6',
- 'Samsung Galaxy Watch' => 'samsung-galaxy-watch',
- 'Samsung Galaxy Watch3' => 'samsung-galaxy-watch3',
- 'Samsung Galaxy Watch Active2' => 'samsung-galaxy-watch-active-2',
- 'Samsung Gear' => 'samsung-gear',
- 'Samsung Gear S3' => 'gear-s3',
- 'Samsung Gear VR' => 'samsung-gear-vr',
- 'Samsung Headphones' => 'samsung-headphones',
- 'Samsung Monitor' => 'samsung-monitor',
- 'Samsung QLED TVs' => 'samsung-qled-tv',
- 'Samsung Smartphone' => 'samsung-smartphone',
- 'Samsung SSD' => 'samsung-ssd',
- 'Samsung The Frame TV' => 'samsung-the-frame',
- 'Samsung TV' => 'samsung-tv',
- 'Samsung Washing Machine' => 'samsung-washing-machine',
- 'Samsung Watch' => 'samsung-watch',
- 'Sandals' => 'sandals',
- 'Sander' => 'sander',
- 'SanDisk' => 'sandisk',
- 'SanDisk SSD' => 'sandisk-ssd',
- 'Sand Pit' => 'sand-pit',
- 'Sandwich Maker' => 'sandwich',
- 'San Miguel' => 'san-miguel',
- 'Santander' => 'santander',
- 'Satchel' => 'satchel',
- 'Sat Nav' => 'sat-nav',
- 'Sauce' => 'sauce',
- 'Saw' => 'saw',
- 'Scalextric' => 'scalextric',
- 'Scanner' => 'scanner',
- 'School Bag' => 'school-bag',
- 'School Supplies' => 'school',
- 'School Uniform' => 'school-uniform',
- 'Schwalbe' => 'schwalbe',
- 'Scooby Doo' => 'scooby-doo',
- 'Scooter' => 'scooter',
- 'Scotch Whisky' => 'scotch',
- 'Scrabble' => 'scrabble',
- 'Screen Protector' => 'screen-protector',
- 'Screenwash' => 'screenwash',
- 'Screwdriver' => 'screwdriver',
- 'Screws' => 'screws',
- 'SD Cards' => 'sd-card',
- 'SDHC' => 'sdhc',
- 'SDXC' => 'sdxc',
- 'Seagate' => 'seagate',
- 'Sea Life' => 'sea-life',
- 'Sea of Thieves' => 'sea-of-thieves',
- 'Season Pass' => 'season-pass',
- 'Seaworld' => 'seaworld',
- 'Security Camera' => 'security-camera',
- 'Seeds &amp; Bulbs' => 'seeds-and-bulbs',
- 'Sega' => 'sega',
- 'SEGA Mega Drive Mini' => 'sega-mega-drive-mini',
- 'Segway' => 'segway',
- 'Seiko' => 'seiko',
- 'Sekiro: Shadows Die Twice' => 'sekiro',
- 'Sekonda' => 'sekonda',
- 'Selfie Stick' => 'selfie-stick',
- 'Sennheiser' => 'sennheiser',
- 'Sennheiser Headphones' => 'sennheiser-headphones',
- 'Sensodyne' => 'sensodyne',
- 'Server' => 'server',
- 'Services &amp; Contracts' => 'services-contracts',
- 'Services and Subscriptions' => 'service-contract',
- 'Sewing' => 'sewing',
- 'Sewing Machine' => 'sewing-machine',
- 'Sex Toys' => 'sex-toys',
- 'Shadow of the Tomb Raider' => 'shadow-of-the-tomb-raider',
- 'Shampoo' => 'shampoo',
- 'Shark' => 'shark',
- 'Shark DuoClean' => 'shark-duoclean',
- 'Shark Vacuum Cleaner' => 'shark-vacuum-cleaner',
- 'Sharp' => 'sharp',
- 'Sharpener' => 'sharpener',
- 'Sharpie' => 'sharpie',
- 'Shaver' => 'shaver',
- 'Shaving &amp; Beard Care' => 'shaving',
- 'Shaving, Trimming, &amp; Hair Removal' => 'hair-removal',
- 'Shaving Foam' => 'shaving-foam',
- 'Shears' => 'shears',
- 'Sheba' => 'sheba',
- 'Shed' => 'shed',
- 'Shelter' => 'shelter',
- 'Shelves' => 'shelves',
- 'Shenmue I &amp; II' => 'shenmue-one-and-two',
- 'Shenmue III' => 'shenmue-3',
- 'Shenmue Series' => 'shenmue-series',
- 'Shimano' => 'shimano',
- 'Shirt' => 'shirt',
- 'Shoe Rack' => 'shoe-rack',
- 'Shoes' => 'shoe',
- 'Shopkins' => 'shopkins',
- 'Shortbread' => 'shortbread',
- 'Shorts' => 'shorts',
- 'Short Trip' => 'break',
- 'Shoulder Bag' => 'shoulder-bag',
- 'Shovel' => 'shovel',
- 'Shower Curtain' => 'shower-curtain',
- 'Shower Enclosure' => 'shower-enclosure',
- 'Shower Fittings' => 'shower',
- 'Shower Gel' => 'shower-gel',
- 'Shower Head' => 'shower-head',
- 'Shredder' => 'shredder',
- 'Side-by-Side-Fridge' => 'side-by-side-fridge',
- 'Sideboard' => 'sideboard',
- 'Sid Meier&#039;s Civilization VI' => 'civilization-vi',
- 'Siemens' => 'siemens',
- 'Siemens Washing Machine' => 'siemens-washing-machine',
- 'Sigma' => 'sigma',
- 'Silentnight' => 'silentnight',
- 'Silvercrest' => 'silvercrest',
- 'Silver Cross' => 'silver-cross',
- 'Sim Free' => 'sim-free',
- 'Sim Only' => 'sim-only',
- 'Simplehuman' => 'simplehuman',
- 'Simpsons' => 'simpsons',
- 'Single Malt' => 'single-malt',
- 'Sink' => 'sink',
- 'Sistema' => 'sistema',
- 'Skateboard' => 'skateboard',
- 'Skating' => 'skating',
- 'Skechers' => 'skechers',
- 'Skiing' => 'ski',
- 'Skin Care' => 'skincare',
- 'Skittles' => 'skittles',
- 'Skoda' => 'skoda',
- 'Skullcandy' => 'skullcandy',
- 'Sky' => 'sky',
- 'Sky Cinema' => 'sky-cinema',
- 'Skylanders' => 'skylanders',
- 'Skylanders Battlecast' => 'skylanders-battlecast',
- 'Skylanders Imaginators' => 'skylanders-imaginators',
- 'Sleeping Bag' => 'sleeping-bag',
- 'Sleeping Dogs' => 'sleeping-dogs',
- 'Sleepwear' => 'sleepwear',
- 'Slide' => 'slide',
- 'Slimming World' => 'slimming-world',
- 'Slippers' => 'slippers',
- 'Slow Cooker' => 'slow-cooker',
- 'Smart Clock' => 'clock',
- 'Smart Doorbells' => 'smart-doorbell',
- 'Smart Home' => 'smart-home',
- 'Smart Light' => 'smart-light',
- 'Smart Lock' => 'smart-lock',
- 'Smartphone Accessories' => 'smartphone-accessories',
- 'Smartphone Case' => 'smartphone-case',
- 'Smartphone under £200' => 'smartphone-under-200-pounds',
- 'Smartphone under £400' => 'smartphone-under-400-pounds',
- 'Smart Plugs' => 'smart-plugs',
- 'Smart Speaker' => 'smart-speaker',
- 'Smart Tech &amp; Gadgets' => 'smart-tech',
- 'Smart Thermostat' => 'thermostat',
- 'SmartThings' => 'smartthings',
- 'Smart TV' => 'smart-tv',
- 'Smart Watch' => 'smartwatch',
- 'Smeg' => 'smeg',
- 'Smirnoff' => 'smirnoff',
- 'Smoke Alarm' => 'smoke-alarm',
- 'Smoothie' => 'smoothie',
- 'Smoothie Maker' => 'smoothie-maker',
- 'Snacks' => 'snacks',
- 'Sneakers' => 'sneakers',
- 'SNES Nintendo Classic Mini' => 'snes-nintendo-classic',
- 'Snickers' => 'snickers',
- 'Sniper Elite' => 'sniper-elite',
- 'Snowboard' => 'snowboard',
- 'Snow Boots' => 'snow-boots',
- 'Soap' => 'soap',
- 'Soap and Glory' => 'soap-and-glory',
- 'Socket Set' => 'socket-set',
- 'Socks' => 'socks',
- 'SodaStream' => 'soda-stream',
- 'Sofa' => 'sofa',
- 'Soft Drinks' => 'soft-drinks',
- 'Soft Toy' => 'soft-toy',
- 'Software' => 'software',
- 'Software &amp; Apps' => 'software-apps',
- 'Solar Lights' => 'solar-lights',
- 'Soldering Iron' => 'soldering',
- 'Sonic' => 'sonic',
- 'Sonos' => 'sonos',
- 'Sonos Beam' => 'sonos-beam',
- 'Sonos Move' => 'sonos-move',
- 'Sonos One' => 'sonos-one',
- 'Sonos PLAY:1' => 'sonos-play-1',
- 'Sonos PLAY:3' => 'sonos-play-3',
- 'Sonos PLAY:5' => 'sonos-play-5',
- 'Sonos PLAYBAR' => 'sonos-playbar',
- 'Sonos PLAYBASE' => 'sonos-playbase',
- 'Sony' => 'sony',
- 'Sony Camera' => 'sony-camera',
- 'Sony Headphones' => 'sony-headphones',
- 'Sony Pulse 3D Wireless Headset' => 'pulse-3d-wireless-headsets',
- 'Sony TV' => 'sony-tv',
- 'Sony WF-1000XM3' => 'sony-wf1000xm3',
- 'Sony WH-1000XM3' => 'sony-wh-1000xm3',
- 'Sony WH-1000XM4' => 'sony-wh1000xm4',
- 'Sony Xperia' => 'xperia',
- 'Sony Xperia 5' => 'sony-xperia-5',
- 'Sony Xperia 10' => 'sony-xperia-10',
- 'Sony Xperia Xa' => 'sony-xperia-xa',
- 'Sony Xperia Z3' => 'xperia-z3',
- 'Sony Xperia Z5' => 'xperia-z5',
- 'Soulcalibur' => 'soulcalibur',
- 'Soundbar' => 'soundbar',
- 'Soundbase' => 'soundbase',
- 'Sound Card' => 'sound-card',
- 'Soundmagic' => 'soundmagic',
- 'Soup' => 'soup',
- 'Soup Maker' => 'soup-maker',
- 'Sous-Vide' => 'sousvide',
- 'Southern Comfort' => 'southern-comfort',
- 'South Park' => 'south-park',
- 'Spa' => 'spa',
- 'Spade' => 'spade',
- 'Spanner' => 'spanner',
- 'Speaker' => 'speakers',
- 'Specialized' => 'specialized',
- 'Speedo' => 'speedo',
- 'Sphero' => 'sphero',
- 'Spice Rack' => 'spice-rack',
- 'Spiderman' => 'spiderman',
- 'Spiralizer' => 'spiralizer',
- 'Spirit &amp; Liqueur' => 'spirits',
- 'Spirit Level' => 'spirit-level',
- 'Splatoon' => 'splatoon',
- 'Sports &amp; Outdoors' => 'sports-fitness',
- 'Sports Events' => 'sports-events',
- 'Sports Nutrition' => 'nutrition',
- 'Spreads' => 'spreads',
- 'Spyro Reignited Trilogy' => 'spyro-reignited-trilogy',
- 'SSD' => 'ssd',
- 'SSHD' => 'sshd',
- 'Staedtler' => 'staedtler',
- 'Stair Gate' => 'stair-gate',
- 'Stanley' => 'stanley',
- 'Stapler' => 'stapler',
- 'Starbucks' => 'starbucks',
- 'Starlink: Battle for Atlas' => 'starlink-battle-for-atlas',
- 'Star Ocean' => 'star-ocean',
- 'Star Trek' => 'star-trek',
- 'Star Wars' => 'star-wars',
- 'Star Wars: Battlefront' => 'star-wars-battlefront',
- 'Star Wars: Battlefront II' => 'star-wars-battlefront-2',
- 'Star Wars: Squadrons' => 'star-wars-squadrons',
- 'Star Wars Jedi: Fallen Order' => 'star-wars-jedi-fallen-order',
- 'Stationery' => 'stationery',
- 'Stationery &amp; Office Supplies' => 'stationery-office-supplies',
- 'Staycation' => 'staycation',
- 'Steak' => 'steak',
- 'Steam Cleaner' => 'steam-cleaner',
- 'Steam Controller' => 'steam-controller',
- 'Steamer' => 'steamer',
- 'Steam Gaming' => 'steam',
- 'Steam Iron' => 'steam-iron',
- 'Steam Link' => 'steam-link',
- 'Steam Mop' => 'steam-mop',
- 'SteelSeries' => 'steelseries',
- 'Steering Wheel' => 'steering-wheel',
- 'Stella' => 'stella',
- 'Stool' => 'stool',
- 'Storage Box' => 'storage-box',
- 'Stormtrooper' => 'stormtrooper',
- 'Straightener' => 'straightener',
- 'Streaming' => 'streaming',
- 'Street Fighter' => 'street-fighter',
- 'Street Fighter V' => 'street-fighter-v',
- 'Streetwear' => 'streetwear',
- 'Strimmer' => 'strimmer',
- 'Strongbow' => 'strongbow',
- 'Student Discount' => 'student-discount',
- 'Subwoofer' => 'subwoofer',
- 'Suitcase' => 'suitcase',
- 'Suncare' => 'suncare',
- 'Sun Cream' => 'sun-cream',
- 'Sunglasses' => 'sunglasses',
- 'Superdry' => 'superdry',
- 'Superfast Broadband' => 'superfast-broadband',
- 'Superking' => 'superking',
- 'Super Mario' => 'mario',
- 'Super Mario 3D All-Stars' => 'super-mario-3d-all-stars',
- 'Super Mario 3D World' => 'super-mario-3d-world',
- 'Super Mario Maker 2' => 'super-mario-maker-2',
- 'Super Mario Odyssey' => 'super-mario-odyssey',
- 'Super Mario Party' => 'mario-party',
- 'Supermarket' => 'supermarket',
- 'Super Smash Bros.' => 'super-smash-bros',
- 'Surf' => 'surf',
- 'Swarovski' => 'swarovski',
- 'Sweets' => 'sweets',
- 'Swimming' => 'swimming',
- 'Swimming Goggles' => 'goggles',
- 'Swimwear' => 'swimwear',
- 'Swing' => 'swing',
- 'Swingball' => 'swingball',
- 'Syberia' => 'syberia',
- 'Sylvanian' => 'sylvanian',
- 'Synology' => 'synology',
- 'T-Mobile' => 't-mobile',
- 'T-Shirt' => 't-shirt',
- 'Table Lamp' => 'table-lamp',
- 'Tablet' => 'tablet',
- 'Tablet Accessories' => 'tablet-accessories',
- 'Table Tennis' => 'table-tennis',
- 'Tableware' => 'tableware',
- 'Tacx' => 'tacx',
- 'Tado' => 'tado',
- 'Tag Heuer' => 'tag-heuer',
- 'Takeaway and Food Delivery' => 'takeaway',
- 'Tales of Vesperia: Definitive Edition' => 'tales-of-vesperia-definitive-edition',
- 'Talisker' => 'talisker',
- 'Talkmobile' => 'talkmobile',
- 'Tamron' => 'tamron',
- 'Tangle Teezer' => 'tangle-teezer',
- 'Tank Top' => 'tank-top',
- 'Tannoy' => 'tannoy',
- 'Tanqueray' => 'tanqueray',
- 'Tape' => 'tape',
- 'Tassimo' => 'tassimo',
- 'Tassimo Coffee Machine' => 'tassimo-coffee-machine',
- 'tastecard' => 'tastecard',
- 'Taxi' => 'taxi',
- 'Tea' => 'tea',
- 'Team Sonic Racing' => 'team-sonic-racing',
- 'Team Sports' => 'team-sports',
- 'Teapot' => 'teapot',
- 'Technika' => 'technika',
- 'Techwood' => 'techwood',
- 'Ted Baker' => 'ted-baker',
- 'Teddy Bear' => 'teddy-bear',
- 'Teenage Mutant Ninja Turtles' => 'turtle',
- 'Teeth Care' => 'teeth-care',
- 'Teeth Whitening' => 'teeth-whitening',
- 'Tefal' => 'tefal',
- 'Tefal Actifry' => 'actifry',
- 'Tefal Pan' => 'tefal-pan',
- 'Tekken' => 'tekken',
- 'Tekken 7' => 'tekken-7',
- 'Telegraph' => 'telegraph',
- 'Telescope' => 'telescope',
- 'Telltale' => 'telltale',
- 'Tennis' => 'tennis',
- 'Tent' => 'tent',
- 'Tequila' => 'tequila',
- 'Tesco Clothing' => 'tesco-clothing',
- 'Tesla' => 'tesla',
- 'Tetris' => 'tetris',
- 'Tetris 99' => 'tetris-99',
- 'Theatre &amp; Musical' => 'theatre',
- 'The Beatles' => 'beatles',
- 'The Big Bang Theory' => 'big-bang-theory',
- 'The Crew' => 'the-crew',
- 'The Dark Pictures: Anthology Man of Medan' => 'the-dark-pictures-anthology-man-of-medan',
- 'The Elder Scrolls' => 'elder-scrolls',
- 'The Elder Scrolls V: Skyrim' => 'skyrim',
- 'The Evil Within' => 'the-evil-within',
- 'The Evil Within 2' => 'the-evil-within-2',
- 'The Last Guardian' => 'the-last-guardian',
- 'The Last of Us' => 'the-last-of-us',
- 'The Last of Us Part II' => 'the-last-of-us-part-2',
- 'The Legend of Zelda' => 'zelda',
- 'The Legend of Zelda: Breath of the Wild' => 'zelda-breath-of-the-wild',
- 'The Legend of Zelda: Link&#039;s Awakening' => 'the-legend-of-zelda-links-awakening',
- 'The Legend of Zelda: Skyward Sword HD' => 'the-legend-of-zelda-skyward-sword-hd',
- 'Theme Park' => 'theme-park',
- 'The North Face' => 'north-face',
- 'The Outer Worlds' => 'the-outer-worlds',
- 'Thermos Storage' => 'thermos',
- 'The Sims' => 'sims',
- 'The Sims 4' => 'the-sims-4',
- 'The Sinking City' => 'the-sinking-city',
- 'The Sun' => 'the-sun',
- 'The Sunday Times' => 'sunday-times',
- 'The Walking Dead' => 'walking-dead',
- 'The Witcher' => 'witcher',
- 'The Witcher 3' => 'the-witcher-3',
- 'Thierry Mugler' => 'thierry-mugler',
- 'Thomas Sabo' => 'thomas-sabo',
- 'Thomas The Tank Engine' => 'thomas-the-tank',
- 'Thornton&#039;s' => 'thorntons',
- 'Thorpe Park' => 'thorpe-park',
- 'Throw' => 'throw',
- 'Thrustmaster' => 'thrustmaster',
- 'Thule' => 'thule',
- 'Tickets &amp; Shows' => 'tickets-shows',
- 'Tie' => 'tie',
- 'Tights' => 'tights',
- 'TIGI' => 'tigi',
- 'Tilda' => 'tilda',
- 'Tile' => 'tile',
- 'Timberland' => 'timberland',
- 'Timex' => 'timex',
- 'Tissot' => 'tissot',
- 'Tissues' => 'tissues',
- 'Titanfall' => 'titanfall',
- 'Titanfall 2' => 'titanfall-2',
- 'Toaster' => 'toaster',
- 'Toblerone' => 'toblerone',
- 'Toddler Bed' => 'toddler-bed',
- 'Toilet Brush' => 'brush',
- 'Toilet Cleaner' => 'toilet',
- 'Toilet Roll' => 'toilet-roll',
- 'Toilet Seat' => 'toilet-seat',
- 'Tokyo Laundry' => 'tokyo-laundry',
- 'Tomb Raider' => 'tomb-raider',
- 'Tom Clancy&#039;s' => 'tom-clancy',
- 'Tom Clancy&#039;s: Ghost Recon' => 'ghost-recon',
- 'Tom Clancy&#039;s Ghost Recon: Wildlands' => 'ghost-recon-wildlands',
- 'Tom Clancy&#039;s Ghost Recon Breakpoint' => 'tom-clancys-ghost-recon-breakpoint',
- 'Tom Clancy&#039;s The Division' => 'tom-clancy-the-division',
- 'Tom Clancy&#039;s The Division 2' => 'tom-clancy-the-division-2',
- 'Tom Ford' => 'tom-ford',
- 'Tommee Tippee' => 'tommee-tippee',
- 'Tommy Hilfiger' => 'tommy-hilfiger',
- 'Toms' => 'toms',
- 'TomTom' => 'tomtom',
- 'Tonic Water' => 'tonic-water',
- 'Tony Hawk&#039;s Pro Skater 1 + 2' => 'tony-hawks-pro-skater-1-2',
- 'Tools' => 'tool',
- 'Toothbrush' => 'toothbrush',
- 'Toothpaste' => 'toothpaste',
- 'Torch' => 'torch',
- 'Torque Wrench' => 'torque-wrench',
- 'Toshiba' => 'toshiba',
- 'Toshiba Laptop' => 'toshiba-laptop',
- 'Toshiba TV' => 'toshiba-tv',
- 'Total War' => 'total-war',
- 'Tottenham Hotspur F. C.' => 'tottenham',
- 'Towel' => 'towel',
- 'Toy Box' => 'toy-box',
- 'Toy Cars' => 'toy-cars',
- 'Toy Castle' => 'castle',
- 'Toy Digger' => 'digger',
- 'Toy Helicopter' => 'helicopter',
- 'Toy Kitchen' => 'toy-kitchen',
- 'Toy Mask' => 'mask',
- 'Toyota' => 'toyota',
- 'Toys' => 'toy',
- 'Toy Story' => 'toy-story',
- 'Toy Tractor' => 'tractor',
- 'Toy Train' => 'train',
- 'TP-Link' => 'tp-link',
- 'TP-Link Archer' => 'archer',
- 'TP-Link Router' => 'tp-link-router',
- 'Tracksuit' => 'tracksuit',
- 'Trainers' => 'trainers',
- 'Trains &amp; Buses' => 'train-and-bus-ticket',
- 'Train Ticket' => 'train-ticket',
- 'Trampoline' => 'trampoline',
- 'Transcend' => 'transcend',
- 'Transformers' => 'transformers',
- 'Travel' => 'travel',
- 'Travel App' => 'travel-app',
- 'Travel Insurance' => 'travel-insurance',
- 'Travelodge' => 'travelodge',
- 'Travel System' => 'travel-system',
- 'Treadmill' => 'treadmill',
- 'TRESemmé' => 'tresemme',
- 'Trespass' => 'trespass',
- 'Triathlon' => 'triathlon',
- 'Trike' => 'trike',
- 'Trine 4' => 'trine-4',
- 'Tripod' => 'tripod',
- 'Tripp' => 'tripp',
- 'Triton Shower' => 'triton',
- 'Trolley Bag' => 'trolley',
- 'Tropico 5' => 'tropico-5',
- 'Tropico 6' => 'tropico-6',
- 'Tropico Series' => 'tropico-deals',
- 'Trousers' => 'trousers',
- 'True Wireless Earbuds' => 'wireless-earphones',
- 'Trunki' => 'trunki',
- 'Tumble Dryer' => 'tumble-dryer',
- 'Tuna' => 'tuna',
- 'Turbo Trainer' => 'turbo-trainer',
- 'Turntable' => 'turntable',
- 'Turtle Beach' => 'turtle-beach',
- 'TV' => 'tv',
- 'TV &amp; Video' => 'tv-video',
- 'TV Accessories' => 'tv-accessories',
- 'TV Mount' => 'tv-mount',
- 'TV Series' => 'tv-series',
- 'TV Stand' => 'tv-stand',
- 'Twinings' => 'twinings',
- 'Twin Peaks' => 'twin-peaks',
- 'Twix' => 'twix',
- 'Typhoo' => 'typhoo',
- 'Tyres' => 'tyres',
- 'Ubisoft' => 'ubisoft',
- 'UE BOOM' => 'ue-boom',
- 'UE Boom 2' => 'ue-boom-2',
- 'UEFA' => 'uefa',
- 'UE Megablast' => 'ue-megablast',
- 'UE Megaboom' => 'ue-megaboom',
- 'UGG' => 'ugg',
- 'Ulefone' => 'ulefone',
- 'Ultrabook' => 'ultrabook',
- 'Ultrawide Monitor' => 'ultrawide',
- 'Umbrella' => 'umbrella',
- 'UMI' => 'umidigi',
- 'Uncharted' => 'uncharted',
- 'Uncharted 4: A Thief&#039;s End' => 'uncharted-4',
- 'Uncharted: The Lost Legacy' => 'uncharted-the-lost-legacy',
- 'Under Armour' => 'under-armour',
- 'Underwear' => 'underwear',
- 'Unicorn' => 'unicorn',
- 'UNiDAYS' => 'unidays',
- 'Universal Remote' => 'universal-remote',
- 'Uno' => 'uno',
- 'Uplay' => 'uplay',
- 'Urban Decay' => 'urban-decay',
- 'Urban Sports' => 'urban-sports',
- 'USB Cable' => 'usb-cable',
- 'USB Hub' => 'usb-hub',
- 'USB Memory Stick' => 'flash-drive',
- 'USB Type C' => 'usb-type-c',
- 'USN' => 'usn',
- 'Vacuum Cleaner' => 'vacuum-cleaners',
- 'Vacuum Flask' => 'flask',
- 'Valkyria Chronicles' => 'valkyria-chronicles',
- 'Valkyria Chronicles 4' => 'valkyria-chronicles-4',
- 'Vango' => 'vango',
- 'Vanish' => 'vanish',
- 'Vans' => 'vans',
- 'Vans Old Skool' => 'vans-old-skool',
- 'Vans Shoes' => 'vans-shoes',
- 'Vase' => 'vase',
- 'Vaseline' => 'vaseline',
- 'Vauxhall' => 'vauxhall',
- 'VAX' => 'vax',
- 'Vax Blade' => 'vax-blade',
- 'Vax Vacuum Cleaner' => 'vax-vacuum',
- 'Veet' => 'veet',
- 'Vega 7' => 'vega-7',
- 'Vegetables' => 'vegetables',
- 'Vegetarian' => 'vegetarian',
- 'Vehicles' => 'vehicles',
- 'Velvet Comfort' => 'velvet',
- 'Vera Wang' => 'vera-wang',
- 'Verbatim' => 'verbatim',
- 'Versace' => 'versace',
- 'Vibrator' => 'vibrator',
- 'Victorinox' => 'victorinox',
- 'Video Games' => 'videogame',
- 'Video Streaming' => 'video-streaming',
- 'Viktor &amp; Rolf Spicebomb' => 'spicebomb',
- 'Vileda' => 'vileda',
- 'Villeroy &amp; Boch' => 'villeroy-boch',
- 'Viners' => 'viners',
- 'Vinyl' => 'vinyl',
- 'Virgin' => 'virgin',
- 'Vitamins &amp; Supplements' => 'vitamins',
- 'Vitamix' => 'vitamix',
- 'Vodafone' => 'vodafone',
- 'Vodka' => 'vodka',
- 'Volvo' => 'volvo',
- 'VPN' => 'vpn',
- 'VR Headset' => 'vr-headset',
- 'VTech' => 'vtech',
- 'VTech Toot Toot' => 'toot-toot',
- 'Vue' => 'vue',
- 'VW' => 'vw',
- 'Wacom' => 'wacom',
- 'Waffle Maker' => 'waffle-maker',
- 'Wahl' => 'wahl',
- 'Walkers' => 'walkers',
- 'Walking Boots' => 'walking-boots',
- 'Wall Art' => 'wall-art',
- 'Wallet' => 'wallet',
- 'Wallpaper' => 'wallpaper',
- 'Wardrobe' => 'wardrobe',
- 'Warhammer' => 'warhammer',
- 'Washbag' => 'washbag',
- 'Washer Dryer' => 'washer-dryer',
- 'Washing Machine' => 'washing-machine',
- 'Washing Powder' => 'washing-powder',
- 'Watch' => 'watch',
- 'Watch Dogs' => 'watch-dogs',
- 'Watch Dogs 2' => 'watch-dogs-2',
- 'Watch Dogs: Legion' => 'watch-dogs-legion',
- 'Water Bottle' => 'water-bottle',
- 'Water Butt' => 'water-butt',
- 'Water Dispenser' => 'water-dispenser',
- 'Water Filter' => 'water-filter',
- 'Water Gun' => 'water-gun',
- 'Waterproof Camera' => 'waterproof-camera',
- 'Waterproof Jacket' => 'waterproof-jacket',
- 'Watersports' => 'watersport',
- 'Water Toys' => 'water-toys',
- 'Wayfarer' => 'wayfarer',
- 'WD40' => 'wd40',
- 'Wearable' => 'wearable',
- 'Weather Station' => 'weather-station',
- 'Webcam' => 'webcam',
- 'Weber' => 'weber',
- 'Web Hosting' => 'web-hosting',
- 'Wedding' => 'wedding',
- 'Weed Killer' => 'weed',
- 'Weekend Break' => 'weekend-break',
- 'Weetabix' => 'weetabix',
- 'Weightlifting' => 'weightlifting',
- 'Weight Watchers' => 'weight-watchers',
- 'Wellies' => 'wellies',
- 'Wellness and Health' => 'wellness-and-health',
- 'Wenger' => 'wenger',
- 'Western Digital' => 'western-digital',
- 'Wetsuit' => 'wetsuit',
- 'Wheelbarrow' => 'wheelbarrow',
- 'Wheelchair' => 'wheelchair',
- 'Whey' => 'whey',
- 'Whiskas' => 'whiskas',
- 'Whisky' => 'whisky',
- 'Whole Home Mesh Wi-Fi System' => 'whole-home-mesh-wifi-system',
- 'Wi-Fi Camera' => 'wifi-camera',
- 'Wi-Fi Dongle' => 'dongle',
- 'Wi-Fi Extender' => 'wifi-extender',
- 'Wii' => 'wii',
- 'Wii Game' => 'wii-games',
- 'Wii U Game' => 'wii-u-game',
- 'Wii U Pro Controller' => 'wii-u-pro-controller',
- 'Wild Turkey' => 'wild-turkey',
- 'Wileyfox' => 'wileyfox',
- 'Wilkinson Sword Hydro 5' => 'hydro-5',
- 'Wilkinson Sword Razor' => 'wilkinson-sword',
- 'Wimbledon Tennis' => 'wimbledon',
- 'Window Cleaner' => 'window-cleaner',
- 'Windows' => 'windows',
- 'Windows 8' => 'windows-8',
- 'Windows 10' => 'windows-10',
- 'Wine' => 'wine',
- 'Wine Advent Calendar' => 'wine-advent-calendar',
- 'Wine Glasses' => 'wine-glasses',
- 'Winter Jacket' => 'winter-jacket',
- 'Wiper Blades' => 'wiper-blades',
- 'Wireless Adapter' => 'wireless-adapter',
- 'Wireless Charger' => 'wireless-charger',
- 'Wireless Controller' => 'wireless-controller',
- 'Wireless Headphones' => 'wireless-headphones',
- 'Wireless Headset' => 'wireless-headset',
- 'Wireless Keyboard' => 'wireless-keyboard',
- 'Wireless Mouse' => 'wireless-mouse',
- 'Wok' => 'wok',
- 'Wolfenstein' => 'wolfenstein',
- 'Wolfenstein 2: The New Colossus' => 'wolfenstein-2',
- 'Women&#039;s Boots' => 'womens-boots',
- 'Women&#039;s Fragrance' => 'womens-fragrance',
- 'Women&#039;s Shoes' => 'womens-shoes',
- 'Workbench' => 'workbench',
- 'World of Warcraft' => 'world-of-warcraft',
- 'World War Z' => 'world-war-z',
- 'WORX' => 'worx',
- 'Wreckfest' => 'wreckfest',
- 'Wuaki' => 'wuaki',
- 'WWE 2K' => 'wwe',
- 'Xbox' => 'xbox',
- 'Xbox 360 Game' => 'xbox-360-game',
- 'Xbox Accessories' => 'xbox-accessories',
- 'Xbox Controller' => 'xbox-controller',
- 'Xbox Game Pass' => 'xbox-game-pass',
- 'Xbox Gift Card' => 'xbox-gift-card',
- 'Xbox Headset' => 'xbox-headset',
- 'Xbox Kinect' => 'kinect',
- 'Xbox Live' => 'xbox-live',
- 'Xbox One Controller' => 'xbox-one-controller',
- 'Xbox One Elite Controller' => 'xbox-one-elite-controller',
- 'Xbox One Games' => 'xbox-one-games',
- 'Xbox One S' => 'xbox-one-s',
- 'Xbox One X' => 'xbox-one-x',
- 'Xbox Series S' => 'xbox-series-s',
- 'Xbox Series X' => 'xbox-series-x',
- 'Xbox Series X Controller' => 'xbox-series-x-controller',
- 'Xbox Series X Games' => 'xbox-series-x-game',
- 'Xbox Wireless Adapter' => 'xbox-wireless-adapter',
- 'Xbox Wireless Headset' => 'xbox-wireless-headset',
- 'XCOM' => 'xcom',
- 'XCOM 2' => 'xcom-2',
- 'Xenoblade Chronicles' => 'xenoblade-chronicles',
- 'XFX' => 'xfx',
- 'Xiaomi' => 'xiaomi',
- 'Xiaomi AirDots' => 'xiaomi-airdots',
- 'Xiaomi Black Shark' => 'xiaomi-black-shark',
- 'Xiaomi Black Shark 2' => 'xiaomi-black-shark-2',
- 'Xiaomi Headphones' => 'xiaomi-headphones',
- 'Xiaomi Laptop' => 'xiaomi-laptop',
- 'Xiaomi Mi 5' => 'xiaomi-mi-5',
- 'Xiaomi Mi 6' => 'xiaomi-mi-6',
- 'Xiaomi Mi 8' => 'xiaomi-mi-8',
- 'Xiaomi Mi 8 Lite' => 'xiaomi-mi-8-lite',
- 'Xiaomi Mi 8 Pro' => 'xiaomi-mi-8-pro',
- 'Xiaomi Mi 9' => 'xiaomi-mi-9',
- 'Xiaomi Mi 9 Lite' => 'xiaomi-mi-9-lite',
- 'Xiaomi Mi 9 SE' => 'xiaomi-mi-9-se',
- 'Xiaomi Mi 9T' => 'xiaomi-mi-9t',
- 'Xiaomi Mi 9T Pro' => 'xiaomi-mi-9t-pro',
- 'Xiaomi Mi 10' => 'xiaomi-mi-10',
- 'Xiaomi Mi 10 Lite' => 'xiaomi-mi-10-lite',
- 'Xiaomi Mi 10T' => 'xiaomi-mi-10t',
- 'Xiaomi Mi 10T Lite' => 'xiaomi-mi-10t-lite',
- 'Xiaomi Mi 10T Pro' => 'xiaomi-mi-10t-pro',
- 'Xiaomi Mi 11' => 'xiaomi-mi-11',
- 'Xiaomi Mi 11 Lite 4G' => 'xiaomi-mi-11-lite-4g',
- 'Xiaomi Mi 11 Lite 5G' => 'xiaomi-mi-11-lite-5g',
- 'Xiaomi Mi 11 Pro' => 'xiaomi-mi-11-pro',
- 'Xiaomi Mi 11 Ultra' => 'xiaomi-mi-11-ultra',
- 'Xiaomi Mi 11i' => 'xiaomi-mi-11i',
- 'Xiaomi Mi A1' => 'xiaomi-mi-a1',
- 'Xiaomi Mi A2' => 'mi-a2',
- 'Xiaomi Mi A3' => 'xiaomi-mi-a3',
- 'Xiaomi Mi Band' => 'xiaomi-mi-band',
- 'Xiaomi Mi Band 3' => 'xiaomi-mi-band-3',
- 'Xiaomi Mi Band 4' => 'xiaomi-mi-band-4',
- 'Xiaomi Mi Band 5' => 'xiaomi-mi-band-5',
- 'Xiaomi Mi Box' => 'xiaomi-mi-box',
- 'Xiaomi Mi Max 3' => 'xiaomi-mi-max3',
- 'Xiaomi Mi Mix' => 'xiaomi-mi-mix',
- 'Xiaomi Mi Mix 2' => 'xiaomi-mi-mix-2',
- 'Xiaomi Mi Mix 2S' => 'xiaomi-mi-mix-2s',
- 'Xiaomi Mi Mix 3' => 'xiaomi-mi-mix-3',
- 'Xiaomi Mi Note' => 'xiaomi-mi-note',
- 'Xiaomi Mi Note 10' => 'mi-note-10',
- 'Xiaomi Mi Pad 4' => 'xiaomi-mi-pad-4',
- 'Xiaomi Pocophone F1' => 'pocophone-f1',
- 'Xiaomi Redmi' => 'redmi',
- 'Xiaomi Redmi 4' => 'xiaomi-redmi-4',
- 'Xiaomi Redmi 5' => 'redmi-5',
- 'Xiaomi Redmi 6' => 'redmi-6',
- 'Xiaomi Redmi 8' => 'redmi-8',
- 'Xiaomi Redmi Note 4' => 'note-4',
- 'Xiaomi Redmi Note 5' => 'redmi-note-5',
- 'Xiaomi Redmi Note 6' => 'redmi-note-6',
- 'Xiaomi Redmi Note 6 Pro' => 'xiaomi-redmi-note-6-pro',
- 'Xiaomi Redmi Note 7' => 'redmi-note-7',
- 'Xiaomi Redmi Note 8' => 'xiaomi-redmi-note-8',
- 'Xiaomi Redmi Note 8 Pro' => 'xiaomi-redmi-note-8-pro',
- 'Xiaomi Redmi Note 8T' => 'redmi-note-8t',
- 'Xiaomi Redmi Note 9' => 'xiaomi-redmi-note-9',
- 'Xiaomi Redmi Note 9 Pro' => 'xiaomi-redmi-note-9-pro',
- 'Xiaomi Redmi Note 9S' => 'xiaomi-redmi-note-9s',
- 'Xiaomi Roborock' => 'xiaomi-roborock',
- 'Xiaomi Roborock S5' => 'xiaomi-roborock-s5',
- 'Xiaomi Scooter' => 'xiaomi-scooter',
- 'Xiaomi Smartphones' => 'xiaomi-smartphone',
- 'Xiaomi Tablets' => 'xiaomi-tablet',
- 'Yakuza' => 'yakuza',
- 'Yale' => 'yale',
- 'Yale Smart Lock' => 'yale-smart-lock',
- 'Yamaha' => 'yamaha',
- 'Yankee Candle' => 'yankee-candle',
- 'Yeelight' => 'xiaomi-yeelight',
- 'Yoga' => 'yoga',
- 'Yoghurt' => 'yoghurt',
- 'Yoshi' => 'yoshi',
- 'Yoshi&#039;s Crafted World' => 'yoshis-crafted-world',
- 'YouView' => 'youview',
- 'Yves Saint Laurent' => 'yves-saint-laurent',
- 'Zanussi' => 'zanussi',
- 'Zippo' => 'zippo',
- 'Zizzi' => 'zizzi',
- 'Zoo' => 'zoo',
- 'Zoostorm' => 'zoostorm',
- 'ZOTAC' => 'zotac',
- 'ZTE' => 'zte',
- 'ZTE Smartphone' => 'zte-smartphone',
- 'ZyXEL' => 'zyxel',
- ]
+ 'type' => 'text',
+ 'exampleValue' => 'broadband',
+ 'title' => 'Group name in the URL : The group name that must be entered is present after "https://www.hotukdeals.com/tag/" and before any "?".
+Example: If the URL of the group displayed in the browser is :
+https://www.hotukdeals.com/tag/broadband?sortBy=temp
+Then enter :
+broadband',
+ ],
+ 'subgroups' => [
+ 'name' => 'category',
+ 'type' => 'text',
+ 'exampleValue' => '343563',
+ 'title' => 'Category number in the URL : The category number that must be entered is present after "groups=" and before any "&".
+Example: If the URL of the group displayed in the browser is :
+https://www.hotukdeals.com/tag/broadband?groups=343563&sortBy=new
+Then enter :
+343563',
],
'order' => [
'name' => 'Order by',
@@ -3275,6 +96,7 @@ class HotUKDealsBridge extends PepperBridgeAbstract
'uri-group' => 'tag/',
'uri-deal' => 'deals/',
'uri-merchant' => 'search/deals?merchant-id=',
+ 'image-host' => 'https://images.hotukdeals.com/',
'request-error' => 'Could not request HotUKDeals',
'thread-error' => 'Unable to determine the thread ID. Check the URL you entered',
'currency' => '£',
@@ -3287,5 +109,7 @@ class HotUKDealsBridge extends PepperBridgeAbstract
'title-talk' => 'Discussion Monitoring',
'deal-type' => 'Deal Type',
'localdeal' => 'Local deal',
+ 'context-hot' => '-hot',
+ 'context-new' => '-new',
];
}
diff --git a/bridges/IdealoBridge.php b/bridges/IdealoBridge.php
index 92bb30d0..55cee467 100644
--- a/bridges/IdealoBridge.php
+++ b/bridges/IdealoBridge.php
@@ -150,29 +150,34 @@ class IdealoBridge extends BridgeAbstract
$ActualNewPrice = $html->find('div[id=oopStage-conditionButton-new]', 0);
// Second Button contains the used product price
$ActualUsedPrice = $html->find('div[id=oopStage-conditionButton-used]', 0);
+ // Get the first item of the offers list to have an option if there is no New/Used Button available
+ $altPrice = $html->find('.productOffers-listItemOfferPrice', 0);
if ($ActualNewPrice) {
$PriceNew = $ActualNewPrice->find('strong', 0)->plaintext;
// Save current price
$this->saveCacheValue($KeyNEW, $PriceNew);
- } else if ($ActualNewPrice === null && $ActualUsedPrice !== null) {
- // In case there is no actual New Price and a Ured Price exists, then delete the previous value in the cache
- $this->cache->delete($this->getShortName() . '_' . $KeyNEW);
+ } else if ($altPrice) {
+ // Get price from first List item if no New/used Buttons available
+ $PriceNew = trim($altPrice->plaintext);
+ $this->saveCacheValue($KeyNEW, $PriceNew);
+ } else if (($ActualNewPrice === null || $altPrice === null) && $ActualUsedPrice !== null) {
+ // In case there is no actual New Price and a Used Price exists, then delete the previous value in the cache
+ $this->cache->delete($this->getShortName() . '_' . $KeyNEW);
}
-
// Second Button contains the used product price
if ($ActualUsedPrice) {
$PriceUsed = $ActualUsedPrice->find('strong', 0)->plaintext;
// Save current price
$this->saveCacheValue($KeyUSED, $PriceUsed);
- } else if ($ActualUsedPrice === null && $ActualNewPrice !== null) {
+ } else if ($ActualUsedPrice === null && ($ActualNewPrice !== null || $altPrice !== null)) {
// In case there is no actual Used Price and a New Price exists, then delete the previous value in the cache
- $this->cache->delete($this->getShortName() . '_' . $KeyUSED);
+ $this->cache->delete($this->getShortName() . '_' . $KeyUSED);
}
- // Only continue if a price has changed and there exists a New or Used price (sometimes no new Price _and_ Used Price are shown)
- if (!($ActualNewPrice === null && $ActualUsedPrice === null ) && ($PriceNew != $OldPriceNew || $PriceUsed != $OldPriceUsed)) {
+ // Only continue if a price has changed and there exists a New, Used or Alternative price (sometimes no new Price _and_ Used Price are shown)
+ if (!($ActualNewPrice === null && $ActualUsedPrice === null && $altPrice === null) && ($PriceNew != $OldPriceNew || $PriceUsed != $OldPriceUsed)) {
// Get Product Image
$image = $html->find('.datasheet-cover-image', 0)->src;
diff --git a/bridges/ItakuBridge.php b/bridges/ItakuBridge.php
index b231b143..22d7529f 100644
--- a/bridges/ItakuBridge.php
+++ b/bridges/ItakuBridge.php
@@ -438,8 +438,7 @@ class ItakuBridge extends BridgeAbstract
private function getOwnerID($username)
{
$url = self::URI . "/api/user_profiles/{$username}/?format=json";
- $data = $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $data = $this->getData($url, true, true);
return $data['owner'];
}
@@ -451,8 +450,7 @@ class ItakuBridge extends BridgeAbstract
}
$uri = self::URI . '/posts/' . $id;
$url = self::URI . '/api/posts/' . $id . '/?format=json';
- $data = $metadata ?? $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $data = $metadata ?? $this->getData($url, true, true);
$content_str = nl2br($data['content']);
$content = "<p>{$content_str}</p><br/>"; //TODO: Add link and itaku user mention detection and convert into links.
@@ -497,8 +495,7 @@ class ItakuBridge extends BridgeAbstract
$content .= "<a href=\"{$url}\"><b>{$title}</b></a><br/>";
if ($media['is_thumbnail_for_video']) {
$url = self::URI . '/api/galleries/images/' . $media['id'] . '/?format=json';
- $media_data = $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $media_data = $this->getData($url, true, true);
$content .= "<video controls src=\"{$media_data['video']['video']}\" poster=\"{$media['image_xl']}\"/>";
} else {
$content .= "<a href=\"{$url}\"><img src=\"{$src}\"></a>";
@@ -523,11 +520,11 @@ class ItakuBridge extends BridgeAbstract
$url = self::URI . '/api/commissions/' . $id . '/?format=json';
$uri = self::URI . '/commissions/' . $id;
- $data = $metadata ?? $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $data = $metadata ?? $this->getData($url, true, true);
$content_str = nl2br($data['description']);
- $content = "<p>{$content_str}</p><br>"; //TODO: Add link and itaku user mention detection and convert into links.
+ $content = "<p>{$content_str}</p><br>";
+ //TODO: Add link and itaku user mention detection and convert into links.
if (array_key_exists('tags', $data) && count($data['tags']) > 0) {
// $content .= "🏷 Tag(s): ";
@@ -570,8 +567,7 @@ class ItakuBridge extends BridgeAbstract
$content .= "<a href=\"{$uri}\"><b>{$data['thumbnail_detail']['title']}</b></a><br/>";
if ($data['thumbnail_detail']['is_thumbnail_for_video']) {
$url = self::URI . '/api/galleries/images/' . $data['thumbnail_detail']['id'] . '/?format=json';
- $media_data = $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $media_data = $this->getData($url, true, true);
$content .= "<video controls src=\"{$media_data['video']['video']}\" poster=\"{$data['thumbnail_detail']['image_lg']}\"/>";
} else {
$content .= "<a href=\"{$uri}\"><img src=\"{$data['thumbnail_detail']['image_lg']}\"></a>";
@@ -595,8 +591,7 @@ class ItakuBridge extends BridgeAbstract
{
$uri = self::URI . '/images/' . $id;
$url = self::URI . '/api/galleries/images/' . $id . '/?format=json';
- $data = /* $metadata ?? */ $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $data = /* $metadata ?? */ $this->getData($url, true, true);
$content_str = nl2br($data['description']);
$content = "<p>{$content_str}</p><br/>"; //TODO: Add link and itaku user mention detection and convert into links.
@@ -640,8 +635,7 @@ class ItakuBridge extends BridgeAbstract
if (array_key_exists('is_thumbnail_for_video', $data)) {
$url = self::URI . '/api/galleries/images/' . $data['id'] . '/?format=json';
- $media_data = $this->getData($url, true, true)
- or returnServerError("Could not load $url");
+ $media_data = $this->getData($url, true, true);
$content .= "<video controls src=\"{$media_data['video']['video']}\" poster=\"{$data['image_xl']}\"/>";
} else {
if (array_key_exists('video', $data) && is_null($data['video'])) {
diff --git a/bridges/JohannesBlickBridge.php b/bridges/JohannesBlickBridge.php
index 80ca9a71..90cd4ef4 100644
--- a/bridges/JohannesBlickBridge.php
+++ b/bridges/JohannesBlickBridge.php
@@ -9,8 +9,7 @@ class JohannesBlickBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM(self::URI)
- or returnServerError('Could not request: ' . self::URI);
+ $html = getSimpleHTMLDOM(self::URI);
$html = defaultLinkTo($html, self::URI);
foreach ($html->find('ul[class=easyfolderlisting] > li > a') as $index => $a) {
diff --git a/bridges/JustETFBridge.php b/bridges/JustETFBridge.php
index bcefe331..b76ef33f 100644
--- a/bridges/JustETFBridge.php
+++ b/bridges/JustETFBridge.php
@@ -181,8 +181,7 @@ class JustETFBridge extends BridgeAbstract
if ($this->getInput('full')) {
$uri = $this->extractNewsUri($article);
- $html = getSimpleHTMLDOMCached($uri)
- or returnServerError('Failed loading full article from ' . $uri);
+ $html = getSimpleHTMLDOMCached($uri);
$fullArticle = $html->find('div.article', 0)
or returnServerError('No content found! Layout might have changed!');
diff --git a/bridges/KernelBugTrackerBridge.php b/bridges/KernelBugTrackerBridge.php
index 02d31cff..8078ddde 100644
--- a/bridges/KernelBugTrackerBridge.php
+++ b/bridges/KernelBugTrackerBridge.php
@@ -64,10 +64,6 @@ Returns feeds for bug comments';
DEFAULT_SPAN_TEXT
);
- if ($html === false) {
- returnServerError('Failed to load page!');
- }
-
$html = defaultLinkTo($html, self::URI);
// Store header information into private members
diff --git a/bridges/LaTeX3ProjectNewslettersBridge.php b/bridges/LaTeX3ProjectNewslettersBridge.php
index dcf1ba0d..b3dfe98e 100644
--- a/bridges/LaTeX3ProjectNewslettersBridge.php
+++ b/bridges/LaTeX3ProjectNewslettersBridge.php
@@ -11,7 +11,7 @@ class LaTeX3ProjectNewslettersBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM(static::URI . '/news/latex3-news/') or returnServerError('No contents received!');
+ $html = getSimpleHTMLDOM(static::URI . '/news/latex3-news/');
$newsContainer = $html->find('article tbody', 0);
foreach ($newsContainer->find('tr') as $row) {
diff --git a/bridges/LegifranceJOBridge.php b/bridges/LegifranceJOBridge.php
index 2d86c2ce..cf8f9f72 100644
--- a/bridges/LegifranceJOBridge.php
+++ b/bridges/LegifranceJOBridge.php
@@ -14,6 +14,37 @@ class LegifranceJOBridge extends BridgeAbstract
private $timestamp;
private $uri;
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM(self::URI);
+
+ $title = $html->find('h2.titleJO', 0);
+
+ //$this->author = trim($title->plaintext);
+ $uri1 = $html->find('h2.titleELI', 0);
+ //$uri = $uri1->plaintext;
+ //$this->uri = trim(substr($uri, strpos($uri, 'https')));
+ $this->timestamp = strtotime(substr($this->uri, strpos($this->uri, 'eli/jo/') + strlen('eli/jo/'), -5));
+
+ foreach ($html->find('h3') as $section) {
+ $subsections = $section->nextSibling()->find('h4');
+ foreach ($subsections as $subsection) {
+ $origins = $subsection->nextSibling()->find('h5');
+ foreach ($origins as $origin) {
+ $this->items[] = $this->extractItem($section, $subsection, $origin);
+ }
+ if (!empty($origins)) {
+ continue;
+ }
+ $this->items[] = $this->extractItem($section, $subsection);
+ }
+ if (!empty($subsections)) {
+ continue;
+ }
+ $this->items[] = $this->extractItem($section);
+ }
+ }
+
private function extractItem($section, $subsection = null, $origin = null)
{
$item = [];
@@ -35,7 +66,9 @@ class LegifranceJOBridge extends BridgeAbstract
$item['content'] = '';
foreach ($data->nextSibling()->find('a') as $content) {
$text = $content->plaintext;
- $href = $content->nextSibling()->getAttribute('resource');
+ $href = '';
+ //$href = $content->nextSibling()->getAttribute('resource');
+
$item['content'] .= '<p><a href="' . $href . '">' . $text . '</a></p>';
}
return $item;
@@ -45,33 +78,4 @@ class LegifranceJOBridge extends BridgeAbstract
{
return 'https://www.legifrance.gouv.fr/img/favicon.ico';
}
-
- public function collectData()
- {
- $html = getSimpleHTMLDOM(self::URI)
- or $this->returnServer('Unable to download ' . self::URI);
-
- $this->author = trim($html->find('h2.titleJO', 0)->plaintext);
- $uri = $html->find('h2.titleELI', 0)->plaintext;
- $this->uri = trim(substr($uri, strpos($uri, 'https')));
- $this->timestamp = strtotime(substr($this->uri, strpos($this->uri, 'eli/jo/') + strlen('eli/jo/'), -5));
-
- foreach ($html->find('h3') as $section) {
- $subsections = $section->nextSibling()->find('h4');
- foreach ($subsections as $subsection) {
- $origins = $subsection->nextSibling()->find('h5');
- foreach ($origins as $origin) {
- $this->items[] = $this->extractItem($section, $subsection, $origin);
- }
- if (!empty($origins)) {
- continue;
- }
- $this->items[] = $this->extractItem($section, $subsection);
- }
- if (!empty($subsections)) {
- continue;
- }
- $this->items[] = $this->extractItem($section);
- }
- }
}
diff --git a/bridges/LfcPlBridge.php b/bridges/LfcPlBridge.php
new file mode 100644
index 00000000..dd74205d
--- /dev/null
+++ b/bridges/LfcPlBridge.php
@@ -0,0 +1,110 @@
+<?php
+
+class LfcPlBridge extends BridgeAbstract
+{
+ const NAME = 'LFC (lfc.pl)';
+ const DESCRIPTION = 'LFC.pl - największa polska strona o Liverpool FC';
+ const URI = 'https://lfc.pl';
+ const MAINTAINER = 'brtsos';
+ const PARAMETERS = [
+ [
+ 'comments' => [
+ 'type' => 'list',
+ 'name' => 'Include comments',
+ 'title' => 'Include comments in the article content',
+ 'values' => [
+ 'No' => 'no',
+ 'Yes' => 'yes',
+ ],
+ ]
+ ]
+ ];
+
+ public function collectData()
+ {
+ $dom = getSimpleHTMLDOM(self::URI . '/Archiwum/' . date('Y') . date('m'));
+
+ $list = $dom->find('#page .list-vertical li');
+ $list = array_reverse($list);
+ $list = array_slice($list, 0, 10);
+
+ foreach ($list as $li) {
+ $link = $li->find('a', 0);
+ $url = self::URI . $link->href;
+
+ $articleDom = getSimpleHTMLDOM($url);
+
+ $description = $this->getContent($articleDom);
+ if (mb_strpos($description, 'Artykuł sponsorowany') !== false) {
+ continue;
+ }
+
+ $image = '<img src="' . $this->getImage($articleDom) . '" alt="' . $link->plaintext . '" />';
+
+ $content = $image . '</br>' . $description;
+
+ $tagsToRemove = ['script', 'iframe', 'input', 'form'];
+ $content = sanitize($content, $tagsToRemove);
+
+ $footerArticle = $articleDom->find('.footer', 0)->find('.item', 0)->find('div', 1);
+ $author = $footerArticle->find('a', 0)->plaintext;
+
+ $dateTime = $footerArticle->find('div', 0)->plaintext;
+ $date = DateTime::createFromFormat('d.m.Y H:i', $dateTime);
+ $timestamp = $date->getTimestamp();
+ $this->items[] = [
+ 'title' => $link->plaintext,
+ 'uri' => $url,
+ 'timestamp' => $timestamp,
+ 'content' => $content,
+ 'author' => $author,
+ ];
+ }
+ }
+
+ private function getContent($article)
+ {
+ $content = $article->find('.news-body', 0)->innertext;
+ $commentsHtml = $article->find('#comments', 0);
+
+ $comments = '';
+ if ($this->withComment()) {
+ if ($commentsHtml) {
+ $commentsDom = $commentsHtml->find('.comment');
+
+ if (count($commentsDom) > 0) {
+ $comments = '<h3>Komentarze:</h3>';
+ }
+
+ foreach ($commentsDom as $comment) {
+ $header = $comment->find('.header', 0)->plaintext;
+ $commentContent = $comment->find('.content', 0)->plaintext;
+ $comments .= $header . '<br />' . $commentContent . '<br /><br />';
+ }
+ }
+ }
+
+ return $content . '<br /> <br />' . $comments;
+ }
+
+ private function getImage($article): ?string
+ {
+ $imgElement = $article->find('#news .img', 0);
+ if ($imgElement) {
+ $style = $imgElement->style;
+
+ if (preg_match('/background-image:\s*url\(([^)]+)\)/i', $style, $matches)) {
+ return self::URI . trim($matches[1], "'\"");
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+
+ private function withComment(): bool
+ {
+ return $this->getInput('comments') === 'yes';
+ }
+} \ No newline at end of file
diff --git a/bridges/MaalaimalarBridge.php b/bridges/MaalaimalarBridge.php
index e83eafe2..fcdbda48 100644
--- a/bridges/MaalaimalarBridge.php
+++ b/bridges/MaalaimalarBridge.php
@@ -5,6 +5,7 @@ class MaalaimalarBridge extends BridgeAbstract
const NAME = 'Maalaimalar';
const URI = 'https://www.maalaimalar.com';
const DESCRIPTION = 'Retrieve news from maalaimalar.com';
+ const CACHE_TIMEOUT = 60 * 5; // 5 minutes
const MAINTAINER = 'tillcash';
const PARAMETERS = [
[
@@ -13,7 +14,7 @@ class MaalaimalarBridge extends BridgeAbstract
'type' => 'list',
'values' => [
'news' => [
- 'tamilnadu' => '/news/state',
+ 'tamilnadu' => '/news/tamilnadu',
'puducherry' => '/news/puducherry',
'india' => '/news/national',
'world' => '/news/world',
diff --git a/bridges/MistralAIBridge.php b/bridges/MistralAIBridge.php
new file mode 100644
index 00000000..b1c357fe
--- /dev/null
+++ b/bridges/MistralAIBridge.php
@@ -0,0 +1,70 @@
+<?php
+
+class MistralAIBridge extends BridgeAbstract
+{
+ const MAINTAINER = 'sqrtminusone';
+ const NAME = 'Mistral AI Bridge';
+ const URI = 'https://mistral.ai/';
+
+ const CACHE_TIMEOUT = 3600; // 1 hour
+ const DESCRIPTION = 'Returns blog posts from Mistral AI';
+
+ const PARAMETERS = [
+ '' => [
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10
+ ],
+ ]
+ ];
+
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM(self::URI . 'news/');
+ $limit = $this->getInput('limit');
+
+ $posts = $html->find('article.news-card');
+ for ($i = 0; $i < min($limit, count($posts)); $i++) {
+ $post = $posts[$i];
+ $url = self::URI . $post->find('a', 0)->href;
+ $this->parsePage($url);
+ }
+ }
+
+ private function parsePage($url)
+ {
+ $html = getSimpleHTMLDOMCached($url, 7 * 24 * 60 * 60);
+ $title = $html->find('h1.hero-title', 0)->plaintext;
+ $timestamp_tag = $html->find('i.ti-calendar', 0)->parent;
+ $timestamp = DateTime::createFromFormat('F j, Y', $timestamp_tag->plaintext)->format('U');
+
+ $content = '';
+
+ // Subheader
+ $header = $html->find('p.hero-description', 0);
+ if ($header != null) {
+ $content .= $header->outertext;
+ }
+
+ // Main content
+ $main = $html->find('$article > div.content', 0);
+
+ // Mostly YouTube videos
+ $iframes = $main->find('iframe');
+ foreach ($iframes as $iframe) {
+ $iframe->parent->removeAttribute('style');
+ $iframe->outertext = '<a href="' . $iframe->src . '">' . $iframe->src . '</a>';
+ }
+
+ $main = defaultLinkTo($main, self::URI);
+ $content .= $main;
+ $this->items[] = [
+ 'title' => $title,
+ 'timestamp' => $timestamp,
+ 'content' => $content,
+ 'uri' => $url,
+ ];
+ }
+}
diff --git a/bridges/MixologyBridge.php b/bridges/MixologyBridge.php
new file mode 100644
index 00000000..42192471
--- /dev/null
+++ b/bridges/MixologyBridge.php
@@ -0,0 +1,80 @@
+<?php
+
+class MixologyBridge extends BridgeAbstract
+{
+ const MAINTAINER = 'swofl';
+ const NAME = 'Mixology';
+ const URI = 'https://mixology.eu';
+ const CACHE_TIMEOUT = 6 * 60 * 60; // 6h
+ const DESCRIPTION = 'Get latest blog posts from Mixology';
+
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM(self::URI);
+
+ $teasers = [];
+ $teaserElements = [];
+
+ $teaserElements[] = $html->find('.aufmacher .views-view-responsive-grid__item-inner', 0);
+ foreach ($html->find('.block-views-blockmixology-frontpage-block-2 .views-col') as $teaser) {
+ $teaserElements[] = $teaser;
+ }
+
+ foreach ($teaserElements as $teaser) {
+ $teasers[] = $this->parseTeaser($teaser);
+ }
+
+ foreach ($teasers as $article) {
+ $this->items[] = $this->parseItem($article);
+ }
+ }
+
+ protected function parseTeaser($teaser)
+ {
+ $result = [];
+
+ $title = $teaser->find('.views-field-title a', 0);
+ $result['title'] = $title->plaintext;
+ $result['uri'] = self::URI . $title->href;
+ $result['enclosures'] = [];
+ $result['enclosures'][] = self::URI . $teaser->find('img', 0)->src;
+ $result['uid'] = hash('sha256', $result['title']);
+
+ $categories = $teaser->find('.views-field-field-kategorie', 0);
+ if ($categories) {
+ $result['categories'] = [];
+ foreach ($categories->find('a') as $category) {
+ $result['categories'][] = $category->innertext;
+ }
+ }
+
+ return $result;
+ }
+
+ protected function parseItem(array $item)
+ {
+ $article = getSimpleHTMLDOMCached($item['uri']);
+
+ $authorLink = $article->find('.beitrag-author a', 0);
+ if (!empty($authorLink)) {
+ $item['author'] = $authorLink->plaintext;
+ }
+
+ $timeElement = $article->find('.beitrag-date time', 0);
+ if (!empty($timeElement)) {
+ $item['timestamp'] = strtotime($timeElement->datetime);
+ }
+
+ $content = '';
+
+ $content .= '<img src="' . $item['enclosures'][0] . '"/>';
+
+ foreach ($article->find('article .wpb_content_element>.wpb_wrapper, article .field--type-text-with-summary>.wp-block-columns>.wp-block-column') as $element) {
+ $content .= $element->innertext;
+ }
+
+ $item['content'] = $content;
+
+ return $item;
+ }
+}
diff --git a/bridges/MondeDiploBridge.php b/bridges/MondeDiploBridge.php
index 7c897f8f..e47bb5ad 100644
--- a/bridges/MondeDiploBridge.php
+++ b/bridges/MondeDiploBridge.php
@@ -19,14 +19,28 @@ class MondeDiploBridge extends BridgeAbstract
foreach ($html->find('div.unarticle') as $article) {
$element = $article->parent();
- $title = $element->find('h3', 0)->plaintext;
- $datesAuteurs = $element->find('div.dates_auteurs', 0)->plaintext;
+ $titleElement = $element->find('h3', 0);
+ if (!$titleElement) {
+ continue;
+ }
+ $title = $titleElement->plaintext;
+ $datesAuteursElement = $element->find('div.dates_auteurs', 0);
+ $datesAuteurs = is_null($datesAuteursElement) ? '' : $element->find('div.dates_auteurs', 0)->plaintext;
$item = [];
$item['uri'] = urljoin(self::URI, $element->href);
- $item['title'] = $this->cleanText($title) . ' - ' . $this->cleanText($datesAuteurs);
+ $item['title'] = $this->getItemTitle($title, $datesAuteurs);
$item['content'] = $this->cleanText(str_replace([$title, $datesAuteurs], '', $element->plaintext));
$this->items[] = $item;
}
}
+
+ private function getItemTitle($title, $datesAuteurs)
+ {
+ $itemTitle = $this->cleanText($title);
+ if (strlen($datesAuteurs) > 0) {
+ $itemTitle .= ' - ' . $this->cleanText($datesAuteurs);
+ }
+ return $itemTitle;
+ }
}
diff --git a/bridges/MozillaBugTrackerBridge.php b/bridges/MozillaBugTrackerBridge.php
index cf6d7f73..84397e00 100644
--- a/bridges/MozillaBugTrackerBridge.php
+++ b/bridges/MozillaBugTrackerBridge.php
@@ -64,10 +64,6 @@ Returns feeds for bug comments';
DEFAULT_SPAN_TEXT
);
- if ($html === false) {
- returnServerError('Failed to load page!');
- }
-
// Fix relative URLs
defaultLinkTo($html, self::URI);
diff --git a/bridges/MydealsBridge.php b/bridges/MydealsBridge.php
index 7b23f263..65d6624c 100644
--- a/bridges/MydealsBridge.php
+++ b/bridges/MydealsBridge.php
@@ -40,1952 +40,24 @@ class MydealsBridge extends PepperBridgeAbstract
'Deals pro Gruppen' => [
'group' => [
'name' => 'Gruppen',
- 'type' => 'list',
- 'title' => 'Gruppe, deren Deals angezeigt werden müssen',
- 'values' => [
- '1Password' => '1password',
- '3D Drucker' => '3d-drucker',
- '4K Fernseher' => '4k-fernseher',
- '4K Monitore' => '4k-monitor',
- '4K Ultra HD Blu-ray' => 'ultra-hd-blu-ray',
- '8K Fernseher' => '8k-fernseher',
- '32 Zoll Fernseher' => '32-zoll-fernseher',
- '55 Zoll Fernseher' => '55-zoll-fernseher',
- '65 Zoll Fernseher' => '65-zoll-fernseher',
- '75 Zoll Fernseher' => '75-zoll-fernseher',
- '1151 Mainboard' => '1151-mainboard',
- 'Abus' => 'abus',
- 'ABUS Fahrradschlösser' => 'abus-fahrradschloss',
- 'Accessoires' => 'accessoires',
- 'Acer' => 'acer',
- 'Acer Aspire' => 'acer-aspire',
- 'Acer Laptops' => 'acer-laptop',
- 'Acer Monitore' => 'acer-monitor',
- 'Acer Predator' => 'acer-predator',
- 'Action Cameras' => 'actioncam',
- 'Actionfiguren' => 'actionfiguren',
- 'adidas' => 'adidas',
- 'adidas Essentials' => 'adidas-neo',
- 'adidas Iniki' => 'adidas-iniki',
- 'adidas NMD' => 'adidas-nmd',
- 'adidas Originals' => 'adidas-originals',
- 'adidas Schuhe' => 'adidas-schuhe',
- 'adidas Superstar' => 'adidas-superstar',
- 'adidas Ultraboost' => 'adidas-ultraboost',
- 'adidas ZX Flux' => 'adidas-zx-flux',
- 'Adventskalender' => 'adventskalender',
- 'AEG' => 'aeg',
- 'AEG Waschmaschinen' => 'aeg-waschmaschine',
- 'Age of Empires' => 'age-of-empires',
- 'AiO Wasserkühlung' => 'aio-wasserkuehlung',
- 'AKG' => 'akg',
- 'Akkus' => 'akkus',
- 'Akkuschrauber' => 'akkuschrauber',
- 'Alfa Romeo' => 'alfa-romeo',
- 'Alienware' => 'alienware',
- 'Alkohol' => 'alkohol',
- 'All Inclusive Reisen' => 'all-inclusive',
- 'All in One PCs' => 'all-in-one-pcs',
- 'AM4 Mainboard' => 'am4-mainboard',
- 'Amazfit' => 'xiaomi-amazfit',
- 'Amazfit Bip' => 'amazfit-bip',
- 'Amazfit GTS' => 'amazfit-gts',
- 'Amazon Echo' => 'amazon-echo',
- 'Amazon Echo Dot' => 'amazon-echo-dot',
- 'Amazon Echo Plus' => 'amazon-echo-plus',
- 'Amazon Echo Show' => 'amazon-echo-show',
- 'Amazon Echo Show 5' => 'amazon-echo-show-5',
- 'Amazon Echo Show 8' => 'amazon-echo-show-8',
- 'Amazon Echo Spot' => 'amazon-echo-spot',
- 'Amazon Fire TV Cube' => 'fire-tv-cube',
- 'Amazon Fire TV Stick' => 'fire-tv',
- 'Amazon Fire TV Stick 4K' => 'fire-tv-stick-4k',
- 'Amazon Tablets' => 'amazon-tablet',
- 'Amazon Warehouse Deals' => 'amazon-warehouse-deals',
- 'AMD' => 'amd',
- 'AMD Radeon' => 'amd-radeon',
- 'AMD Radeon VII' => 'vega-7',
- 'AMD RX Vega' => 'amd-vega',
- 'AMD Ryzen' => 'amd-ryzen',
- 'AMD Ryzen 9 5900X' => 'amd-ryzen-9-5900x',
- 'American Express' => 'american-express',
- 'amiibo' => 'amiibo',
- 'Analoguhren' => 'analoguhren',
- 'Android Apps' => 'android-apps',
- 'Android Smartphones' => 'android-smartphones',
- 'Angelzubehör' => 'angelsport',
- 'Animal Crossing' => 'animal-crossing',
- 'Animal Crossing: New Horizons' => 'animal-crossing-new-horizons',
- 'Anime' => 'anime',
- 'Ankündigungen' => 'ankundigungen',
- 'Anno 1800' => 'anno-1800',
- 'Anthem' => 'anthem',
- 'Anzug' => 'anzug',
- 'AOC' => 'aoc',
- 'Apex Legends' => 'apex-legends',
- 'Apotheke' => 'apotheke',
- 'Apple' => 'apple',
- 'Apple AirPods' => 'airpods',
- 'Apple AirPods 2' => 'airpods-2',
- 'Apple AirPods Max' => 'airpods-max',
- 'Apple AirPods Pro' => 'airpods-pro',
- 'Apple EarPods' => 'apple-earpods',
- 'Apple HomePod' => 'homepod',
- 'Apple HomePod mini' => 'apple-homepod-mini',
- 'Apple Kopfhörer' => 'apple-kopfhoerer',
- 'Apple Magic Mouse 2' => 'apple-magic-mouse-2',
- 'Apple Pencil' => 'apple-pencil',
- 'Apple Pencil 2' => 'apple-pencil-2',
- 'Apple TV' => 'apple-tv',
- 'Apple Watch' => 'apple-watch',
- 'Apple Watch 3' => 'apple-watch-3',
- 'Apple Watch 4' => 'apple-watch-4',
- 'Apple Watch 5' => 'apple-watch-5',
- 'Apple Watch 6' => 'apple-watch-6',
- 'Apple Watch SE' => 'apple-watch-se',
- 'Apps' => 'apps',
- 'Aquaristik' => 'aquaristik',
- 'Arbeitsspeicher' => 'arbeitsspeicher',
- 'Arbeitszimmermöbel' => 'arbeitszimmer',
- 'ASICS' => 'asics',
- 'Assassin&#039;s Creed' => 'assassins-creed',
- 'Assassin&#039;s Creed: Valhalla' => 'assassins-creed-valhalla',
- 'Assassin&#039;s Creed Odyssey' => 'assassins-creed-odyssey',
- 'Assassin&#039;s Creed Origins' => 'assassins-creed-origins',
- 'ASTRO Gaming A50' => 'astro-gaming-a50',
- 'ASUS' => 'asus',
- 'ASUS Laptops' => 'asus-laptop',
- 'Asus Mainboard' => 'asus-mainboard',
- 'Asus Monitore' => 'asus-monitor',
- 'ASUS ROG' => 'asus-rog',
- 'ASUS Smartphones' => 'asus-smartphones',
- 'Asus ZenBook' => 'asus-zenbook',
- 'ASUS ZenFone 5' => 'asus-zenfone-5',
- 'ASUS ZenFone 5Z' => 'asus-zenfone-5z',
- 'Audi' => 'audi',
- 'Audio &amp; HiFi' => 'audio-hifi',
- 'Audioverstärker' => 'audioverstaerker',
- 'Audio Zubehör' => 'audio-zubehoer',
- 'Aukey' => 'aukey',
- 'Außenleuchten' => 'aussenleuchten',
- 'Auto &amp; Motorrad' => 'auto-motorrad',
- 'Auto Bild' => 'auto-bild',
- 'Auto Leasing' => 'auto-leasing',
- 'Auto Leasing Gewerbe' => 'gewerbe-leasing',
- 'Auto Leasing Privat' => 'privat-leasing',
- 'Automatikuhren' => 'automatikuhr',
- 'auto motor und sport' => 'auto-motor-sport',
- 'Autoradio' => 'autoradio',
- 'Auto Teile' => 'autoteile',
- 'Autowäsche' => 'autowaesche',
- 'Auto Zubehör' => 'auto',
- 'AVM FRITZ!Box' => 'avm-fritz-box',
- 'AVM FRITZ!Box 7490' => 'avm-fritz-box-7490',
- 'AVM FRITZ!Box 7530' => 'avm-fritz-box-7530',
- 'AVM FRITZ!Box 7580' => 'avm-fritz-box-7580',
- 'AVM FRITZ!Box 7590' => 'avm-fritz-box-7590',
- 'AVM FRITZ! DECT 301' => 'avm-fritz-dect-301',
- 'AV Receiver' => 'av-receiver',
- 'Baby &amp; Kind' => 'kinder',
- 'Baby-Erstausstattung' => 'baby-erstausstattung',
- 'Babybetten' => 'babybetten',
- 'Baby Born' => 'baby-born',
- 'Babykleidung' => 'babybekleidung',
- 'Babynahrung' => 'babynahrung',
- 'Babyphone' => 'babyphone',
- 'Backofen &amp; Herd' => 'backofen-herd',
- 'Backwaren' => 'backwaren',
- 'Backzubehör' => 'backzubehoer',
- 'Bademode' => 'bademode',
- 'Badmöbel' => 'badezimmer',
- 'Bahn-Tickets' => 'bahntickets',
- 'Bahncard' => 'bahncard',
- 'Balkonmöbel' => 'balkonmoebel',
- 'Ballerinas' => 'ballerinas',
- 'Bang &amp; Olufsen' => 'bang-olufsen',
- 'Bank' => 'bank',
- 'Barbie' => 'barbie',
- 'Barclaycard' => 'barclaycard',
- 'Bartschneider' => 'bartschneider',
- 'Batterien' => 'batterien',
- 'Battle.net' => 'battle-net',
- 'Battlefield' => 'battlefield',
- 'Battlefield 1' => 'battlefield-1',
- 'Battlefield 5' => 'battlefield-5',
- 'Bauknecht' => 'bauknecht',
- 'Bauknecht Waschmaschinen' => 'bauknecht-waschmaschine',
- 'Baumarkt' => 'baumarkt',
- 'Bayonetta' => 'bayonetta',
- 'Bayonetta 2' => 'bayonetta-2',
- 'Beamer' => 'beamer',
- 'Beamer Leinwand' => 'beamer-leinwand',
- 'Beats by Dre' => 'beats-by-dre',
- 'Beats Solo3' => 'beats-solo3',
- 'Beats Solo Pro' => 'beats-solo-pro',
- 'Beats Studio3' => 'beats-studio3',
- 'Beauty &amp; Gesundheit' => 'beauty',
- 'Beko' => 'beko',
- 'Beleuchtung' => 'beleuchtung',
- 'Belkin' => 'belkin',
- 'Ben &amp; Jerry&#039;s' => 'ben-jerrys',
- 'Bench' => 'bench',
- 'BenQ' => 'benq',
- 'BenQ Monitore' => 'benq-monitor',
- 'be quiet!' => 'be-quiet',
- 'be quiet! Netzteile' => 'be-quiet-netzteil',
- 'Besteck' => 'besteck',
- 'Bethesda' => 'bethesda',
- 'Betten' => 'betten',
- 'Bettwäsche' => 'bettwaesche',
- 'beyerdynamic' => 'beyerdynamic',
- 'Beyerdynamic MMX 300' => 'beyerdynamic-mmx-300',
- 'BHs' => 'bhs',
- 'Bier' => 'bier',
- 'Biking &amp; Urban Sport' => 'biking-urban-sport',
- 'Bildbearbeitungsprogramme' => 'bildbearbeitungsprogramme',
- 'Birkenstock' => 'birkenstock',
- 'Black &amp; Decker' => 'black-and-decker',
- 'Blackberry Smartphones' => 'blackberry',
- 'Black Desert Online' => 'black-desert-online',
- 'Blazer' => 'blazer',
- 'Blood &amp; Truth' => 'blood-truth',
- 'Blu-ray' => 'blu-ray',
- 'Blu-ray Player' => 'blu-ray-player',
- 'Bluetooth Kopfhörer' => 'bluetooth-kopfhoerer',
- 'Bluetooth Lautsprecher' => 'bluetooth-lautsprecher',
- 'Blumen' => 'blumen',
- 'Blusen' => 'blusen',
- 'BMW' => 'bmw',
- 'Bodenbelag' => 'bodenbelag',
- 'Boho-Chic wohnen' => 'boho-chich-wohnen',
- 'Bohrer' => 'bohrer',
- 'Bohrhämmer' => 'bohrhaemmer',
- 'Bohrmaschinen' => 'bohrmaschinen',
- 'Bollerwagen' => 'bollerwagen',
- 'Bombay Gin' => 'bombay',
- 'Borderlands' => 'borderlands',
- 'Borderlands 3' => 'borderlands-3',
- 'Bosch' => 'bosch',
- 'Bosch Akkuschrauber' => 'bosch-akkuschrauber',
- 'Bosch Geschirrspüler' => 'bosch-geschirrspueler',
- 'Bosch Kühlschränke' => 'bosch-kuehlschrank',
- 'Bosch Waschmaschinen' => 'bosch-waschmaschine',
- 'Bose' => 'bose',
- 'Bose Headphones 700' => 'bose-headphones-700',
- 'Bose Home Speaker 500' => 'bose-home-speaker-500',
- 'Bose Kopfhörer' => 'bose-kopfhoerer',
- 'Bose QuietComfort' => 'bose-quietcomfort',
- 'Bose QuietComfort 35 II' => 'bose-quiet-comfort-35-ii',
- 'Bose Solo 5' => 'bose-solo-5',
- 'Bose SoundLink' => 'bose-soundlink',
- 'Bose SoundTouch' => 'bose-soundtouch',
- 'BOSS' => 'boss',
- 'Bourbon' => 'bourbon',
- 'Bowers &amp; Wilkins' => 'bowers-wilkins',
- 'Boxershorts' => 'boxershorts',
- 'Boxspringbetten' => 'boxspringbetten',
- 'Braun' => 'braun',
- 'Braun Rasierer' => 'braun-rasierer',
- 'Braun Series 3' => 'braun-series-3',
- 'Braun Series 5' => 'braun-series-5',
- 'Braun Series 7' => 'braun-series-7',
- 'Braun Series 9' => 'braun-series-9',
- 'Bridgekameras' => 'bridgekamera',
- 'Brigitte' => 'brigitte',
- 'Brillen &amp; Kontaktlinsen' => 'brillen',
- 'Brita' => 'brita',
- 'Britax Römer' => 'britax-roemer',
- 'Brotaufstrich' => 'brotaufstrich',
- 'Brother Drucker' => 'brother-drucker',
- 'Bücher' => 'buecher',
- 'Bücher, Magazine &amp; Zeitschriften' => 'buecher-zeitschriften',
- 'bugatti' => 'bugatti',
- 'Bügeleisen' => 'buegeleisen',
- 'Bügeln' => 'buegeln',
- 'Buggy' => 'buggy',
- 'Burger' => 'burger',
- 'BURNHARD' => 'burnhard',
- 'Bürobedarf' => 'buerobedarf',
- 'Bürostühle' => 'buerostuhl',
- 'Bus &amp; Bahn' => 'bus-bahn',
- 'Business Mode' => 'business-mode',
- 'c&#039;t – Magazin für Computertechnik' => 'ct-magazin-computertechnik',
- 'Cafissimo' => 'cafissimo',
- 'Call of Duty' => 'call-of-duty',
- 'Call of Duty: Black Ops 4' => 'call-of-duty-black-ops-4',
- 'Call of Duty: Black Ops Cold War' => 'call-of-duty-black-ops-cold-war',
- 'Call of Duty: Infinite Warfare' => 'call-of-duty-infinite-warfare',
- 'Call of Duty: Modern Warfare' => 'call-of-duty-modern-warfare',
- 'Call of Duty: Warzone' => 'call-of-duty-warzone',
- 'Call of Duty: WW2' => 'call-of-duty-ww2',
- 'Calvin Klein' => 'calvin-klein',
- 'Camcorder' => 'camcorder',
- 'Campen' => 'campen',
- 'Canon' => 'canon',
- 'Canon Drucker' => 'canon-drucker',
- 'Canon EOS' => 'canon-eos',
- 'Canon Kameras' => 'canon-kameras',
- 'Canon PowerShot' => 'canon-powershot',
- 'CANTON' => 'canton',
- 'Caps' => 'caps',
- 'Captain Toad: Treasure Tracker' => 'captain-toad-treasure-tracker',
- 'Capture One' => 'capture-one',
- 'Carhartt' => 'carhartt',
- 'Carsharing' => 'carsharing',
- 'Casio' => 'casio',
- 'Cheap Monday' => 'cheapmonday',
- 'Chevrolet' => 'chevrolet',
- 'China Handys' => 'china-handys',
- 'Chip (Magazin)' => 'chip-magazin',
- 'Chips' => 'chips',
- 'Christbaumschmuck' => 'christbaumschmuck',
- 'Christbaumständer' => 'christbaumstaender',
- 'Chromebook' => 'chromebook',
- 'Chronographen' => 'chronograph',
- 'Chucks' => 'chucks',
- 'Citroen' => 'citroen',
- 'Coca-Cola' => 'coca-cola',
- 'Comics' => 'comics',
- 'Computer' => 'computer',
- 'Computer &amp; Tablets' => 'computer-tablet',
- 'Computer Bild' => 'computer-bild',
- 'Controller' => 'controller',
- 'Converse' => 'converse',
- 'Convertibles' => 'convertibles',
- 'Corsair' => 'corsair',
- 'Corsair VOID PRO' => 'corsair-void-pro',
- 'Couchtische' => 'couchtische',
- 'Coupons' => 'coupons',
- 'CPU-Kühler' => 'cpu-kuehler',
- 'Craghoppers' => 'craghoppers',
- 'Crocs' => 'crocs',
- 'Crucial' => 'crucial',
- 'Cupra' => 'cupra',
- 'Cyberpunk 2077' => 'cyberpunk-2077',
- 'cybex' => 'cybex',
- 'D-Link' => 'd-link',
- 'DAB Radios' => 'dab-radios',
- 'Dacia' => 'dacia',
- 'Damenbekleidung' => 'fashion-frauen',
- 'Damenschuhe' => 'damenschuhe',
- 'Dampfbügelstation' => 'dampfbuegelstation',
- 'Dampfgarer' => 'dampfgarer',
- 'Dampfreiniger' => 'dampfreiniger',
- 'Dark Souls' => 'dark-souls',
- 'Dashcam' => 'dashcam',
- 'Datentarif' => 'datentarif',
- 'Daypack' => 'daypack',
- 'Days Gone' => 'days-gone',
- 'DC Shoes' => 'dc-shoes',
- 'DDR3 RAM' => 'ddr3-ram',
- 'DDR4 RAM' => 'ddr4-ram',
- 'De&#039;Longhi' => 'delonghi',
- 'Death Stranding' => 'death-stranding',
- 'Deckenlampen' => 'deckenlampen',
- 'DECT Telefone' => 'telefone',
- 'Dekoration' => 'dekoration',
- 'Dell' => 'dell',
- 'Dell Laptops' => 'dell-laptop',
- 'Dell Monitore' => 'dell-monitor',
- 'Dell XPS' => 'dell-xps',
- 'Denon' => 'denon',
- 'Deo' => 'deo',
- 'Depot' => 'depot',
- 'DER SPIEGEL' => 'der-spiegel',
- 'Designermöbel' => 'designermoebel',
- 'Desigual' => 'desigual',
- 'Desinfektionsmittel' => 'desinfektionsmittel',
- 'Desktop PCs' => 'desktop-pc',
- 'Dessous' => 'dessous',
- 'Destiny' => 'destiny',
- 'Destiny 2' => 'destiny-2',
- 'Deus Ex' => 'deus-ex',
- 'Deus Ex: Mankind' => 'deus-ex-mankind',
- 'Deuter' => 'deuter',
- 'DeutschlandCard' => 'deutschlandcard',
- 'devolo' => 'devolo',
- 'DeWalt' => 'dewalt',
- 'Die drei Fragezeichen' => 'die-drei-fragezeichen',
- 'Die Eiskönigin' => 'die-eiskoenigin',
- 'Dienstleistungen &amp; Verträge' => 'dienstleistungen-vertraege',
- 'Dies &amp; Das' => 'dies-das',
- 'Diesel' => 'diesel',
- 'Die Sims' => 'die-sims',
- 'Die Sims 4' => 'die-sims-4',
- 'Die Zeit' => 'die-zeit',
- 'Digitalreceiver' => 'digitalreceiver',
- 'Digitaluhren' => 'digitaluhr',
- 'Direktflüge' => 'direktfluege',
- 'Dirt Devil' => 'dirt-devil',
- 'Dishonored' => 'dishonored',
- 'Dishonored 2: Das Vermächtnis der Maske' => 'dishonored-2',
- 'Disney' => 'disney',
- 'Disney+' => 'disney-plus',
- 'DJI' => 'dji',
- 'DJI Osmo Pocket' => 'dji-osmo-pocket',
- 'Dockers' => 'dockers',
- 'Dolce Gusto' => 'dolce-gusto',
- 'DOOM Eternal' => 'doom-eternal',
- 'Douglas Adventskalender' => 'douglas-adventskalender',
- 'Dr. Martens' => 'dr-martens',
- 'Dragon Ball' => 'dragon-ball',
- 'Dragon Ball FighterZ' => 'dragon-ball-fighterz',
- 'Dragon Ball Z: Kakarot' => 'dragon-ball-z-kakarot',
- 'Dragon Quest Builders' => 'dragon-quest-builders',
- 'Dragon Quest Builders 2' => 'dragon-quest-builders-2',
- 'Dreame Staubsauger' => 'xiaomi-staubsauger',
- 'Dreame T20' => 'dreame-t20',
- 'Dreame V9' => 'xiaomi-dreame-v9',
- 'Dreame V10' => 'xiaomi-dreame-v10',
- 'Dreame V11' => 'xiaomi-dreame-v11',
- 'Drohnen' => 'drohnen',
- 'Drucker' => 'drucker',
- 'Druckerpatronen' => 'druckerpatronen',
- 'Druckerzubehör' => 'druckerzubehoer',
- 'DSL &amp; Kabel' => 'dsl',
- 'Dunstabzugshauben' => 'dunstabzugshauben',
- 'Durex' => 'durex',
- 'Duscharmaturen' => 'duscharmaturen',
- 'Duschgel' => 'duschgel',
- 'Duschköpfe' => 'duschkoepfe',
- 'DVD' => 'dvd',
- 'Dyson' => 'dyson',
- 'Dyson Staubsauger' => 'dyson-staubsauger',
- 'Dyson V6' => 'dyson-v6',
- 'Dyson V7' => 'dyson-v7',
- 'Dyson V8' => 'dyson-v8',
- 'Dyson V10' => 'dyson-v10',
- 'Dyson V11' => 'dyson-v11',
- 'Dyson V11 Absolute' => 'dyson-v11-absolute',
- 'Dyson V11 Animal' => 'dyson-v11-animal',
- 'E-Bikes' => 'e-bikes',
- 'E-Scooter' => 'e-scooter',
- 'E-Scooter Sharing' => 'e-scooter-sharing',
- 'E-Zigaretten' => 'e-zigaretten',
- 'Eastpak' => 'eastpak',
- 'eBook Reader' => 'ebook-reader',
- 'eBooks' => 'ebooks',
- 'Ecovacs' => 'ecovacs',
- 'Ecovacs Deebot 900' => 'ecovacs-deebot-900',
- 'Ecovacs Deebot OZMO 930' => 'ecovacs-deebot-ozmo-930',
- 'Edifier' => 'edifier',
- 'Edifier R1280DB' => 'edifier-r1280db',
- 'Edifier R1280T' => 'edifier-r1280t',
- 'Einhell' => 'einhell',
- 'Eis' => 'eis',
- 'Elektrische Zahnbürsten' => 'elektrische-zahnbuersten',
- 'Elektrogrills' => 'elektrogrill',
- 'Elektroheizungen' => 'elektroheizungen',
- 'Elektronik' => 'elektronik',
- 'Elektronik Zubehör' => 'elektronikzubehoer',
- 'Elektrorasierer' => 'elektrorasierer',
- 'Elektroroller' => 'elektroroller',
- 'Elektrowerkzeuge' => 'elektrowerkzeug',
- 'Elephone' => 'elephone',
- 'ELLE' => 'elle',
- 'Emsa' => 'emsa',
- 'Energy Drinks' => 'energy-drinks',
- 'Entsafter' => 'entsafter',
- 'Epilierer' => 'epilierer',
- 'Epson' => 'epson',
- 'Epson Drucker' => 'epson-drucker',
- 'Erotik' => 'erotik',
- 'Error Fare' => 'error-fare',
- 'Espressomaschinen' => 'espressomaschinen',
- 'Esprit' => 'esprit',
- 'Esstische' => 'esstisch',
- 'Esszimmer' => 'esszimmer',
- 'Eterna' => 'eterna',
- 'EUROtronic Comet DECT' => 'eurotronic-comet-dect',
- 'Externe Festplatten' => 'externe-festplatten',
- 'F1 2017' => 'f1-2017',
- 'F1 2019' => 'f1-2019',
- 'F1 2020' => 'f1-2020',
- 'Fahrräder' => 'fahrraeder',
- 'Fahrradhelme' => 'fahrradhelme',
- 'Fahrradrucksäcke' => 'fahrradrucksack',
- 'Fahrradschlösser' => 'fahrradschloss',
- 'Fahrradteile' => 'fahrradteile',
- 'Fahrradträger' => 'fahrradtraeger',
- 'Fahrradzubehör' => 'fahrradzubehoer',
- 'Fahrzeuge' => 'fahrzeuge',
- 'Falke' => 'falke',
- 'Fallout' => 'fallout',
- 'Fallout 4' => 'fallout-4',
- 'Fallout 76' => 'fallout-76',
- 'Family &amp; Kids' => 'family-kids',
- 'Far Cry' => 'far-cry',
- 'Far Cry 5' => 'far-cry-5',
- 'Far Cry New Dawn' => 'far-cry-new-dawn',
- 'Fashion &amp; Accessoires' => 'fashion-accessoires',
- 'Fast Food' => 'fast-food',
- 'Felgen' => 'felgen',
- 'Fenstersauger' => 'fenstersauger',
- 'Fernbus-Tickets' => 'fernbus',
- 'Fernseher' => 'fernseher',
- 'Fertiggerichte' => 'fertiggerichte',
- 'Festplatten' => 'festplatten',
- 'Festplattengehäuse' => 'festplattengehaeuse',
- 'FFP2 Masken' => 'ffp2-masken',
- 'Fiat' => 'fiat',
- 'FIFA' => 'fifa',
- 'FIFA 17' => 'fifa-17',
- 'FIFA 18' => 'fifa-18',
- 'FIFA 19' => 'fifa-19',
- 'FIFA 20' => 'fifa-20',
- 'FIFA 21' => 'fifa-21',
- 'FILA' => 'fila',
- 'Filme &amp; Serien' => 'filme-serien',
- 'Filterkaffeemaschinen' => 'filterkaffeemaschinen',
- 'Final Fantasy' => 'final-fantasy',
- 'Final Fantasy 7' => 'final-fantasy-7',
- 'Finanzen- und Steuersoftware' => 'finanzen-und-steuersoftware',
- 'Finish' => 'finish',
- 'Fisch &amp; Meeresfrüchte' => 'fisch-meeresfruechte',
- 'Fischertechnik' => 'fischertechnik',
- 'Fisher-Price' => 'fisher-price',
- 'Fiskars' => 'fiskars',
- 'Fissler' => 'fissler',
- 'fitbit' => 'fitbit',
- 'Fitness &amp; Running' => 'fitness',
- 'Fitness Apps' => 'fitness-apps',
- 'Fitnessstudio' => 'fitnessstudio',
- 'Fitnesstracker' => 'fitnesstracker',
- 'Fjällräven' => 'fjaellraeven',
- 'Fleisch &amp; Wurst' => 'fleisch-wurst',
- 'Fliesenschneider' => 'fliesenschneider',
- 'Flüge' => 'fluege',
- 'Flurmöbel' => 'flurmoebel',
- 'FOCUS' => 'focus',
- 'Ford' => 'ford',
- 'For Honor' => 'for-honor',
- 'Formel 1 Games' => 'formel-1',
- 'Fortnite' => 'fortnite',
- 'Forza' => 'forza',
- 'Forza Horizon' => 'forza-horizon',
- 'Forza Horizon 4' => 'forza-horizon-4',
- 'Forza Motorsport' => 'forza-motorsport',
- 'Forza Motorsport 7' => 'forza-7',
- 'Fossil' => 'fossil',
- 'Foto &amp; Kamera' => 'foto-video',
- 'Foto Apps' => 'foto-apps',
- 'Fotobücher' => 'fotobuecher',
- 'Fototapete' => 'fototapete',
- 'Fragen &amp; Gesuche' => 'gesuche',
- 'Frankfurter Allgemeine Zeitung (F.A.Z.)' => 'frankfurter-allgemeine-zeitung',
- 'FreeSync Monitore' => 'freesync-monitor',
- 'Freizeitpark-Tickets' => 'freizeitpark',
- 'Freizeitsport' => 'freizeitsport',
- 'Fritteusen' => 'fritteusen',
- 'Frontlader' => 'frontlader',
- 'Frühlingsdeko' => 'fruehlingsdeko',
- 'Frühstücksflocken' => 'fruehstuecksflocken',
- 'Fruit of the Loom' => 'fruit-of-the-loom',
- 'Fujifilm' => 'fujifilm',
- 'Füller' => 'fueller',
- 'Full HD-Beamer' => 'full-hd-beamer',
- 'Fun Factory' => 'fun-factory',
- 'FurReal Friends' => 'furreal-friends',
- 'Fußball' => 'fussball',
- 'Fußball-Trikots' => 'fussball-trikots',
- 'Fußballschuhe' => 'fussballschuhe',
- 'G-Star' => 'g-star',
- 'G-Sync Monitore' => 'g-sync-monitor',
- 'Game of Thrones' => 'game-of-thrones',
- 'Gaming' => 'gaming',
- 'Gaming Headsets' => 'gaming-headset',
- 'Gaming Laptops' => 'gaming-laptop',
- 'Gaming Mäuse' => 'gaming-maus',
- 'Gaming Monitore' => 'gaming-monitor',
- 'Gaming PCs' => 'gaming-pc',
- 'Gaming Stühle' => 'gaming-stuhl',
- 'Gaming Tastaturen' => 'gaming-tastatur',
- 'Gaming Zubehör' => 'spielekonsolen-zubehoer',
- 'Ganzjahresreifen' => 'ganzjahresreifen',
- 'GAP' => 'gap',
- 'Gardena' => 'gardena',
- 'Garderobe' => 'garderobe',
- 'Garmin' => 'garmin',
- 'Garmin Fenix' => 'garmin-fenix',
- 'Garten' => 'garten',
- 'Garten &amp; Baumarkt' => 'garten-baumarkt',
- 'Gartenarbeit' => 'gartenarbeit',
- 'Gartenbank' => 'gartenbank',
- 'Gartenliegen' => 'sonnenliegen',
- 'Gartenmöbel' => 'gartenmoebel',
- 'Gartenstühle' => 'gartenstuehle',
- 'Gartentische' => 'gartentische',
- 'Gasgrills' => 'gasgrill',
- 'Gastarif' => 'gastarif',
- 'Gears 5' => 'gears-5',
- 'Gears of War' => 'gears-of-war',
- 'Gefrierschränke' => 'gefrierschrank',
- 'Geld-zurück-Aktionen' => 'geld-zurueck',
- 'Geldbörsen' => 'geldboersen',
- 'Gemüse' => 'gemuese',
- 'Geox' => 'geox',
- 'Geschirr' => 'geschirr',
- 'Geschirrspüler' => 'geschirrspueler',
- 'Gesellschaftsspiele' => 'gesellschaftsspiele',
- 'Gesichtspflege' => 'gesichtspflege',
- 'Gesundheit' => 'gesundheit',
- 'Getränke' => 'getraenke',
- 'Gewinnspiele' => 'gewinnspiele',
- 'GHD' => 'ghd',
- 'Ghost of Tsushima' => 'ghost-of-tsushima',
- 'GIGABYTE' => 'gigabyte',
- 'Gigaset' => 'gigaset',
- 'Gillette' => 'gillette',
- 'Gillette Rasierer' => 'gillette-rasierer',
- 'Gin' => 'gin',
- 'Girokonto' => 'konto',
- 'Glamour' => 'glamour',
- 'Glamourös wohnen' => 'glamouroes-wohnen',
- 'Gläser' => 'glaeser',
- 'Glätteisen' => 'glaetteisen',
- 'Gleitgel' => 'gleitgel',
- 'Glühwein' => 'gluehwein',
- 'God of War' => 'god-of-war',
- 'Google Chromecast' => 'chromecast',
- 'Google Chromecast mit Google TV' => 'chromecast-mit-google-tv',
- 'Google Chromecast Ultra' => 'chromecast-ultra',
- 'Google Home' => 'google-home',
- 'Google Home Max' => 'google-home-max',
- 'Google Home Mini' => 'google-home-mini',
- 'Google Nest Hub' => 'google-nest-hub',
- 'Google Pixel' => 'google-pixel',
- 'Google Pixel 2' => 'google-pixel-2',
- 'Google Pixel 3' => 'google-pixel-3',
- 'Google Pixel 4' => 'google-pixel-4',
- 'Google Pixel 4 XL' => 'google-pixel-4xl',
- 'Google Pixel 4a' => 'google-pixel-4a',
- 'Google Pixel 4a 5G' => 'google-pixel-4a-5g',
- 'Google Pixel 5' => 'google-pixel-5',
- 'Google Smartphones' => 'google-smartphones',
- 'Google Stadia Konsolen' => 'google-stadia',
- 'GoPro Action Cameras' => 'gopro',
- 'GoPro HERO 7' => 'gopro-hero-7',
- 'GoPro HERO 8' => 'gopro-hero-8',
- 'GoPro HERO 9' => 'gopro-hero-9',
- 'Gorenje' => 'gorenje',
- 'Grafikkarten' => 'grafikkarten',
- 'Gran Turismo' => 'gran-turismo',
- 'Gran Turismo Sport' => 'gran-turismo-sport',
- 'Grazia' => 'grazia',
- 'Grills' => 'grill',
- 'Grillzubehör' => 'grillzubehoer',
- 'Grundig' => 'grundig',
- 'GTA' => 'gta',
- 'GTA V' => 'gta-v',
- 'GTX 1060' => 'gtx-1060',
- 'GTX 1070' => 'gtx-1070',
- 'GTX 1080' => 'gtx-1080',
- 'GTX 1080 Ti' => 'gtx-1080-ti',
- 'GTX 1660' => 'gtx-1660',
- 'GTX 1660 Ti' => 'gtx-1660-ti',
- 'Gucci' => 'gucci',
- 'Gummistiefel' => 'gummistiefel',
- 'Gürtel' => 'guertel',
- 'Gutscheinfehler' => 'gutscheinfehler',
- 'Haarentfernung' => 'haarentfernung',
- 'Haargel' => 'haargel',
- 'Haarpflege' => 'haarpflege',
- 'Haarschneidemaschinen' => 'haarschneidemaschinen',
- 'Haarspray' => 'haarspray',
- 'Haartrockner' => 'haartrockner',
- 'Haftpflichtversicherung' => 'haftpflichtversicherung',
- 'Hama' => 'hama',
- 'Handelsblatt' => 'handelsblatt',
- 'Handmixer' => 'handmixer',
- 'Handtaschen' => 'handtaschen',
- 'Handtücher' => 'handtuecher',
- 'Handwerkzeuge' => 'handwerkzeug',
- 'Handy &amp; Smartphone Zubehör' => 'smartphone-zubehoer',
- 'Handyhalterung' => 'handyhalterung',
- 'Handyhüllen' => 'handyhuelle',
- 'Handys mit Vertrag' => 'handys-mit-vertrag',
- 'Handys ohne Vertrag' => 'handys-ohne-vertrag',
- 'Handyversicherung' => 'handyversicherung',
- 'Handyverträge' => 'handyvertraege',
- 'Handyverträge 3 Monate Kündigungsfrist' => 'handyvertraege-3-monate-kuendigungsfrist',
- 'Handyverträge monatlich kündbar' => 'handyvertraege-monatlich-kuendbar',
- 'Hängematten' => 'haengematten',
- 'Hanteln' => 'hanteln',
- 'Haribo' => 'haribo',
- 'Harman Kardon' => 'harman-kardon',
- 'Harry Potter' => 'harry-potter',
- 'Hasbro' => 'hasbro',
- 'Haushaltsartikel' => 'haushaltsartikel',
- 'Haushaltsgeräte' => 'haushaltsgeraete',
- 'Haushaltswaren' => 'haushaltswaren',
- 'Hausratversicherung' => 'hausratsversicherung',
- 'Hausschuhe' => 'hausschuhe',
- 'Haustier' => 'haustier',
- 'Hautpflege' => 'hautpflege',
- 'Head &amp; Shoulders' => 'head-and-shoulders',
- 'Heckenscheren' => 'heckenschere',
- 'Heimkino' => 'heimkino',
- 'Heimtextilien' => 'heimtextilien',
- 'Heißluftfritteusen' => 'heissluftfriteuse',
- 'Heizkörperthermostat' => 'heizkoerperthermostat',
- 'Heizungen' => 'heizungen',
- 'Hemden' => 'hemden',
- 'Hendrick&#039;s Gin' => 'hendricks-gin',
- 'Herbstdeko' => 'herbstdeko',
- 'Herrenbekleidung' => 'fashion-maenner',
- 'Herrenschuhe' => 'herrenschuhe',
- 'HiPP' => 'hipp',
- 'Hisense' => 'hisense',
- 'Hochbetten' => 'hochbetten',
- 'Hochdruckreiniger' => 'hochdruckreiniger',
- 'Hochstuhl' => 'hochstuhl',
- 'Hollywoodschaukel' => 'hollywoodschaukel',
- 'Home &amp; Living' => 'home-living',
- 'homee' => 'homee',
- 'Honda' => 'honda',
- 'Honor' => 'honor',
- 'Honor 5' => 'honor-5',
- 'Honor 6' => 'honor-6',
- 'Honor 7X' => 'honor-7',
- 'Honor 8' => 'honor-8',
- 'Honor 9' => 'honor-9',
- 'Honor 20' => 'honor-20',
- 'Honor 20 Lite' => 'honor-20-lite',
- 'Honor Band 4' => 'honor-band-4',
- 'Honor Band 5' => 'honor-band-5',
- 'Honor Play' => 'honor-play',
- 'Honor Smartphones' => 'honor-smartphones',
- 'Honor View 10' => 'honor-view-10',
- 'Honor View 20' => 'honor-view-20',
- 'Hoodies' => 'hoodies',
- 'Hörbücher' => 'hoerbuecher',
- 'Horizon Zero Dawn' => 'horizon-zero-dawn',
- 'Hörspiele' => 'hoerspiele',
- 'Hörzu' => 'hoerzu',
- 'Hosen' => 'hosen',
- 'Hotels &amp; Unterkünfte' => 'hotel',
- 'Hot Wheels' => 'hot-wheels',
- 'Hoverboards' => 'hoverboards',
- 'HP' => 'hp',
- 'HP Drucker' => 'hp-drucker',
- 'HP Laptops' => 'hp-laptop',
- 'HP OMEN' => 'hp-omen',
- 'HP Pavilion' => 'hp-pavilion',
- 'HTC 10' => 'htc-10',
- 'HTC Desire 12' => 'htc-desire',
- 'HTC Smartphones' => 'htc-smartphones',
- 'HTC U11' => 'htc-u11',
- 'HTC Vive' => 'htc-vive',
- 'Huawei' => 'huawei',
- 'Huawei Kopfhörer' => 'huawei-kopfhoerer',
- 'Huawei Mate 9' => 'huawei-mate-9',
- 'Huawei Mate 10' => 'huawei-mate-10',
- 'Huawei Mate 20' => 'huawei-mate-20',
- 'Huawei Mate 20 Lite' => 'huawei-mate-20-lite',
- 'Huawei Mate 20 Pro' => 'huawei-mate-20-pro',
- 'Huawei Mate 30 Pro' => 'huawei-mate-30-pro',
- 'Huawei MateBook' => 'huawei-matebook',
- 'Huawei P10' => 'huawei-p10',
- 'Huawei P20' => 'huawei-p20',
- 'Huawei P30' => 'huawei-p30',
- 'Huawei P30 Lite' => 'huawei-p30-lite',
- 'Huawei P30 Pro' => 'huawei-p30-pro',
- 'Huawei P40' => 'huawei-p40',
- 'Huawei P40 Lite' => 'huawei-p40-lite',
- 'Huawei P40 Pro' => 'huawei-p40-pro',
- 'Huawei P Smart' => 'huawei-p-smart',
- 'Huawei Smartphones' => 'huawei-smartphones',
- 'Huawei Tablets' => 'huawei-mediapad',
- 'Huawei Watch GT2' => 'huawei-watch-gt2',
- 'Huawei Y7' => 'huawei-y7',
- 'Hunde' => 'hunde',
- 'Hundefutter' => 'hundefutter',
- 'Hüte &amp; Mützen' => 'huete-muetzen',
- 'Hyrule Warriors' => 'hyrule-warriors',
- 'Hyrule Warriors: Zeit der Verheerung' => 'hyrule-warriors-zeit-der-verheerung',
- 'Hyundai' => 'hyundai',
- 'iMac' => 'imac',
- 'Immortals Fenyx Rising' => 'immortals-fenyx-rising',
- 'In-Ear Kopfhörer' => 'in-ear-kopfhoerer',
- 'Industrial Style' => 'industrial-style',
- 'Inline Skates' => 'inline-skates',
- 'Instax Mini' => 'instax-mini',
- 'Intel Core i9-9900K' => 'intel-core-i9-9900k',
- 'Intel i3' => 'intel-i3',
- 'Intel i5' => 'intel-i5',
- 'Intel i7' => 'intel-i7',
- 'Intel i9' => 'intel-i9',
- 'Intenso' => 'intenso',
- 'Internet Security' => 'internet-security',
- 'Intimpflege' => 'intimpflege',
- 'iOS Apps' => 'ios-apps',
- 'iPad' => 'ipad',
- 'iPad 2019' => 'ipad-2019',
- 'iPad 2020' => 'ipad-2020',
- 'iPad Air' => 'ipad-air-2',
- 'iPad Air 2019' => 'ipad-air-2019',
- 'iPad Air 2020' => 'ipad-air-2020',
- 'iPad mini' => 'ipad-mini',
- 'iPad Pro' => 'ipad-pro',
- 'iPad Pro 11' => 'ipad-pro-11',
- 'iPad Pro 12.9' => 'ipad-pro-12-9',
- 'iPad Pro 2020' => 'ipad-pro-2020',
- 'iPhone' => 'iphone',
- 'iPhone 6' => 'iphone-6',
- 'iPhone 6 Plus' => 'iphone-6-plus',
- 'iPhone 6s' => 'iphone-6s',
- 'iPhone 6s Plus' => 'iphone-6s-plus',
- 'iPhone 7' => 'iphone-7',
- 'iPhone 7 Plus' => 'iphone-7-plus',
- 'iPhone 8' => 'iphone-8',
- 'iPhone 8 Plus' => 'iphone-8-plus',
- 'iPhone 11' => 'iphone-11',
- 'iPhone 11 Pro' => 'iphone-11-pro',
- 'iPhone 11 Pro Max' => 'iphone-11-pro-max',
- 'iPhone 12' => 'iphone-12',
- 'iPhone 12 mini' => 'iphone-12-mini',
- 'iPhone 12 Pro' => 'iphone-12-pro',
- 'iPhone 12 Pro Max' => 'iphone-12-pro-max',
- 'iPhone SE' => 'iphone-se',
- 'iPhone X' => 'iphone-x',
- 'iPhone Xr' => 'iphone-xr',
- 'iPhone Xs' => 'iphone-xs',
- 'iPhone Xs Max' => 'iphone-xs-max',
- 'iPhone Zubehör' => 'iphone-zubehoer',
- 'Irish Whiskey' => 'irish-whiskey',
- 'iRobot' => 'irobot',
- 'iRobot Roomba' => 'irobot-roomba',
- 'iRobot Roomba 980' => 'irobot-roomba-980',
- 'iRobot Roomba i7' => 'irobot-roomba-i7',
- 'Isomatten' => 'isomatten',
- 'iTunes Guthaben' => 'itunes-guthaben',
- 'Jabra Elite 75t' => 'jabra-elite-75t',
- 'Jabra Elite 85h' => 'jabra-elite-85h',
- 'Jabra Elite 85t' => 'jabra-elite-85t',
- 'Jabra Elite Active 75t' => 'jabra-elite-active-75t',
- 'Jabra Kopfhörer' => 'jabra-kopfhoerer',
- 'JACK &amp; JONES' => 'jack-jones',
- 'Jacken' => 'jacken',
- 'JACK WOLFSKIN' => 'jack-wolfskin',
- 'Jagdzubehör' => 'jagdzubehoer',
- 'JBL' => 'jbl',
- 'JBL Charge 4' => 'jbl-charge-4',
- 'JBL Flip' => 'jbl-flip',
- 'JBL GO' => 'jbl-go',
- 'Jeans' => 'jeans',
- 'Jim Beam' => 'jim-beam',
- 'Jogginghosen' => 'jogginghosen',
- 'Joghurt' => 'joghurt',
- 'Johnnie Walker' => 'johnnie-walker',
- 'Jura Kaffeemaschinen' => 'jura',
- 'Just Cause' => 'just-cause',
- 'Just Cause 4' => 'just-cause-4',
- 'Kaffee' => 'kaffee',
- 'Kaffeekapseln' => 'kaffeekapseln',
- 'Kaffeemaschinen' => 'kaffeemaschinen',
- 'Kaffeemühlen' => 'kaffeemuehlen',
- 'Kaffeepadmaschinen' => 'kaffeepadmaschinen',
- 'Kaffeepads' => 'kaffeepads',
- 'Kaffeevollautomaten' => 'kaffeevollautomaten',
- 'Kameras' => 'kamera',
- 'Kamera Zubehör' => 'kamerazubehoer',
- 'Kamine' => 'kamine',
- 'Kapselmaschinen' => 'kapselmaschinen',
- 'Kärcher' => 'kaercher',
- 'Kärcher Fenstersauger' => 'kaercher-fenstersauger',
- 'Kärcher Hochdruckreiniger' => 'kaercher-hochdruckreiniger',
- 'Kartenspiele' => 'kartenspiel',
- 'Käse' => 'kaese',
- 'Katzen' => 'katzen',
- 'Katzenfutter' => 'katzenfutter',
- 'Kaufen im Ausland' => 'kaufen-ausland',
- 'Ketchup' => 'ketchup',
- 'KFZ Versicherung' => 'kfz-versicherung',
- 'KIA' => 'kia',
- 'kiddy' => 'kiddy',
- 'Kinder Adventskalender' => 'kinder-adventskalender',
- 'Kinderbekleidung' => 'kinderkleidung',
- 'Kinderbetten' => 'kinderbett',
- 'Kinderfahrräder' => 'kinderfahrrad',
- 'Kinderschuhe' => 'kinderschuhe',
- 'Kindersitz' => 'kindersitz',
- 'Kinderwagen' => 'kinderwagen',
- 'Kinderwagen &amp; Autositze' => 'baby-transport',
- 'Kinderzimmermöbel' => 'kinderzimmer',
- 'Kindle' => 'kindle',
- 'Kindle Oasis' => 'kindle-oasis',
- 'Kindle Paperwhite' => 'kindle-paperwhite',
- 'Kingdom Come: Deliverance' => 'kingdom-come-deliverance',
- 'Kingdom Hearts' => 'kingdom-hearts',
- 'Kingdom Hearts 3' => 'kingdom-hearts-3',
- 'Kingston HyperX Cloud Flight' => 'kingston-hyperx-cloud-flight',
- 'Kingston HyperX Cloud II' => 'hyperx-cloud-ii',
- 'Kino' => 'kino',
- 'KitchenAid' => 'kitchenaid',
- 'Kleider' => 'kleider',
- 'Kleiderschränke' => 'kleiderschraenke',
- 'Kleidung' => 'kleidung',
- 'Klemmbausteine' => 'klemmbausteine',
- 'Klimaanlagen' => 'klimaanlagen',
- 'Klimatechnik' => 'klimatechnik',
- 'Klipsch' => 'klipsch',
- 'Kochgeräte' => 'kochgeraete',
- 'Kodak' => 'kodak',
- 'Koffer' => 'koffer',
- 'Kohlenmonoxidmelder' => 'kohlenmonoxidmelder',
- 'Kolonialstil' => 'kolonialstil',
- 'Kommoden &amp; Sideboards' => 'kommoden-sideboards',
- 'Kondome' => 'kondome',
- 'König der Löwen Musical' => 'koenig-der-loewen-musical',
- 'Kontaktgrills' => 'kontaktgrill',
- 'Konto &amp; Kreditkarten' => 'konto-kreditkarten',
- 'Konzert-Tickets' => 'konzerte',
- 'Kopfhörer' => 'kopfhoerer',
- 'Körperpflege &amp; Hygiene' => 'koerperpflege',
- 'Kosmetik' => 'kosmetik',
- 'Kostüme' => 'kostuem',
- 'Kraftstoffe &amp; Betriebsstoffe' => 'kraftstoffe-betriebsstoffe',
- 'Krafttraining' => 'krafttraining',
- 'Kredit' => 'kredit',
- 'Kreditkarten' => 'kreditkarten',
- 'Kreissägen' => 'kreissaegen',
- 'Kreuzfahrten' => 'kreuzfahrten',
- 'Krups' => 'krups',
- 'Küche' => 'kueche',
- 'Küchengeräte' => 'kuechengeraete',
- 'Küchenhelfer' => 'kuechenhelfer',
- 'Küchenmaschinen' => 'kuechenmaschinen',
- 'Küchenmesser' => 'messer',
- 'Küchenutensilien' => 'kuechenutensilien',
- 'Kugelschreiber' => 'kugelschreiber',
- 'Kühl-Gefrierkombinationen' => 'kuehl-gefrierkombination',
- 'Kühlboxen' => 'kuehlboxen',
- 'Kühlschränke' => 'kuehlschrank',
- 'Kultur &amp; Freizeit' => 'kultur-freizeit',
- 'Kunst &amp; Hobby' => 'hobby',
- 'Kurse &amp; Trainings' => 'kurse-trainings',
- 'Lacoste' => 'lacoste',
- 'Ladegeräte' => 'ladegeraete',
- 'Lampen' => 'lampen',
- 'Landhausstil' => 'landhausstil',
- 'Landwirtschafts-Simulator' => 'landwirtschafts-simulator',
- 'Laptops' => 'laptop',
- 'Laserdrucker' => 'laserdrucker',
- 'Last Minute Reisen' => 'last-minute',
- 'Lattenroste' => 'lattenroste',
- 'Laubsauger' => 'laubsauger',
- 'Laufräder' => 'laufraeder',
- 'Laufschuhe' => 'laufschuhe',
- 'Laufsport' => 'laufsport',
- 'Lautsprecher' => 'lautsprecher',
- 'Lavazza' => 'lavazza',
- 'Lay-Z-Spa Whirlpools' => 'lay-z-spa-whirlpools',
- 'Lebensmittel' => 'lebensmittel',
- 'Lebensmittel &amp; Haushalt' => 'food',
- 'LED Lampen' => 'led-lampen',
- 'LEGO' => 'lego',
- 'LEGO Adventskalender' => 'lego-adventskalender',
- 'LEGO Architecture' => 'lego-architecture',
- 'LEGO Batman' => 'lego-batman',
- 'LEGO City' => 'lego-city',
- 'LEGO Creator' => 'lego-creator',
- 'LEGO Dimensions' => 'lego-dimensions',
- 'LEGO DUPLO' => 'lego-duplo',
- 'LEGO Friends' => 'lego-friends',
- 'LEGO Harry Potter' => 'lego-harry-potter',
- 'LEGO Marvel Super Heroes' => 'lego-marvel-super-heroes',
- 'LEGO Nexo Knights' => 'lego-nexo-knights',
- 'LEGO NINJAGO' => 'lego-ninjago',
- 'LEGO Star Wars' => 'lego-star-wars',
- 'LEGO Star Wars Millennium Falcon' => 'lego-star-wars-millennium-falcon',
- 'LEGO Super Mario' => 'lego-super-mario',
- 'LEGO Technic' => 'lego-technic',
- 'LEGO The Simpsons' => 'lego-simpsons',
- 'Leifheit' => 'leifheit',
- 'Lenovo' => 'lenovo',
- 'Lenovo Laptops' => 'lenovo-laptop',
- 'Lenovo Tablets' => 'lenovo-tablet',
- 'Lenovo ThinkPad' => 'lenovo-thinkpad',
- 'Lenovo Yoga' => 'lenovo-yoga',
- 'Leonardo' => 'leonardo',
- 'Leuchtmittel' => 'leuchten',
- 'Levi&#039;s' => 'levis',
- 'Lexar' => 'lexar',
- 'Lexmark' => 'lexmark',
- 'LG' => 'lg',
- 'LG Fernseher' => 'lg-fernsher',
- 'LG G5' => 'lg-g5',
- 'LG G6' => 'lg-g6',
- 'LG G7 ThinQ' => 'lg-g7-thinq',
- 'LG OLED Fernseher' => 'lg-oled-tv',
- 'LG Smartphones' => 'lg-smartphones',
- 'LG V30' => 'lg-v30',
- 'Lichterketten' => 'lichterketten',
- 'Liebeskind' => 'liebeskind',
- 'Lieferservice' => 'lieferservice',
- 'Lindt' => 'lindt',
- 'Lindt Adventskalender' => 'lindt-adventskalender',
- 'Logitech' => 'logitech',
- 'Logitech G413' => 'logitech-g413',
- 'Logitech G430' => 'logitech-g430',
- 'Logitech G502 Proteus Spectrum' => 'logitech-g502',
- 'Logitech G513' => 'logitech-g513',
- 'Logitech G533' => 'logitech-g533',
- 'Logitech G633 Artemis Spectrum' => 'logitech-g633',
- 'Logitech G703' => 'logitech-g703',
- 'Logitech G903' => 'logitech-g903',
- 'Logitech G910 Orion Spectrum' => 'logitech-g910',
- 'Logitech G915' => 'logitech-g915',
- 'Logitech G933 Artemis Spectrum' => 'logitech-g933',
- 'Logitech Harmony' => 'logitech-harmony',
- 'Logitech Mäuse' => 'logitech-maeuse',
- 'Logitech MX Master' => 'logitech-mx-master',
- 'Logitech MX Master 2S' => 'logitech-mx-master-2s',
- 'Logitech Tastaturen' => 'logitech-tastaturen',
- 'Logitech Z333' => 'logitech-z333',
- 'Logitech Z337' => 'logitech-z337',
- 'Logitech Z906' => 'logitech-z906',
- 'Luftbefeuchter' => 'luftbefeuchter',
- 'Luftentfeuchter' => 'luftentfeuchter',
- 'Luftmatratzen' => 'luftmatratzen',
- 'Luftreiniger' => 'luftreiniger',
- 'Luigi&#039;s Mansion' => 'luigis-mansion',
- 'Luigi&#039;s Mansion 3' => 'luigis-mansion-3',
- 'Lustiges Taschenbuch' => 'lustiges-taschenbuch',
- 'M.2 SSD' => 'm2-ssd',
- 'MacBook' => 'macbook',
- 'MacBook Air' => 'macbook-air',
- 'MacBook Pro' => 'macbook-pro',
- 'MacBook Pro 13' => 'macbook-pro-13',
- 'MacBook Pro 15' => 'macbook-pro-15',
- 'MacBook Pro 16' => 'macbook-pro-16',
- 'Mac mini' => 'mac-mini',
- 'Mac Software' => 'mac-software',
- 'Madden NFL' => 'madden-nfl',
- 'Magazine' => 'magazine',
- 'Magnat' => 'magnat',
- 'Magnum Eis' => 'magnum-eis',
- 'Mähroboter' => 'maehroboter',
- 'Mainboards' => 'mainboards',
- 'Make Up Adventskalender' => 'make-up-adventskalender',
- 'Makita' => 'makita',
- 'Makita Akkuschrauber' => 'makita-akkuschrauber',
- 'Malerwerkzeuge' => 'malerpinsel',
- 'Mangas' => 'mangas',
- 'Marantz' => 'marantz',
- 'Mario Kart' => 'mario-kart',
- 'Mario Kart 8 Deluxe' => 'mario-kart-8-deluxe',
- 'Marken' => 'marken',
- 'Marvel' => 'marvel',
- 'Marvel&#039;s Spider-Man: Miles Morales' => 'marvels-spider-man-miles-morales',
- 'Mass Effect' => 'mass-effect',
- 'Mass Effect: Andromeda' => 'mass-effect-andromeda',
- 'Massivholzmöbel' => 'massivholzmoebel',
- 'Mastercard' => 'mastercard',
- 'Matratzen' => 'matratzen',
- 'Maxi Cosi' => 'maxi-cosi',
- 'Mazda' => 'mazda',
- 'Medion' => 'medion',
- 'Mercedes-Benz' => 'mercedes-benz',
- 'Mesh WLAN Router' => 'mesh-wlan-router',
- 'Metabo' => 'metabo',
- 'Metro (Spiel)' => 'metro',
- 'Metro Exodus' => 'metro-exodus',
- 'Michael Kors' => 'michael-kors',
- 'microSD' => 'microsd',
- 'microSDHC' => 'microsdhc',
- 'microSDXC' => 'microsdxc',
- 'Microsoft Flight Simulator' => 'microsoft-flight-simulator',
- 'Microsoft Software' => 'microsoft-software',
- 'Microsoft Surface Notebooks' => 'microsoft-surface-notebooks',
- 'Microsoft Surface Pro 4' => 'surface-pro-4',
- 'Microsoft Surface Pro 6' => 'surface-pro-6',
- 'Microsoft Surface Pro 7' => 'microsoft-surface-pro-7',
- 'Microsoft Surface Tablets' => 'microsoft-surface',
- 'Miele' => 'miele',
- 'Miele Geschirrspüler' => 'miele-geschirrspueler',
- 'Miele Staubsauger' => 'miele-staubsauger',
- 'Miele Waschmaschinen' => 'miele-waschmaschine',
- 'Mietwagen' => 'mietwagen',
- 'Mikrofone' => 'mikrofone',
- 'Mikrowellen' => 'mikrowelle',
- 'Milchaufschäumer' => 'milchaufschaeumer',
- 'Milka' => 'milka',
- 'Minecraft' => 'minecraft',
- 'Mineralwasser' => 'mineralwasser',
- 'Minions' => 'minions',
- 'Mini PCs' => 'mini-pc',
- 'Mitsubishi' => 'mitsubishi',
- 'Mittelerde' => 'middle-earth',
- 'Mittelerde: Mordors Schatten' => 'mittelerde-mordors-schatten',
- 'Mittelerde: Schatten des Krieges' => 'mittelerde-schatten-des-krieges',
- 'Mixer &amp; Rührer' => 'mixer',
- 'Möbel' => 'moebel-deko',
- 'Modellbau' => 'modellbau',
- 'Modern wohnen' => 'modern-wohnen',
- 'Monitore' => 'monitor',
- 'Monkey 47' => 'monkey-47',
- 'Monopoly' => 'monopoly',
- 'Monster Hunter' => 'monster-hunter',
- 'Monster Hunter: World' => 'monster-hunter-world',
- 'Mortal Kombat' => 'mortal-kombat',
- 'Mortal Kombat 11' => 'mortal-kombat-11',
- 'Motorola' => 'motorola',
- 'Motorola Smartphones' => 'motorola-smartphones',
- 'Motorradbekleidung' => 'motorradbekleidung',
- 'Motorradhelm' => 'motorradhelm',
- 'Motorrad Zubehör' => 'motorrad',
- 'Moto Z' => 'moto-z',
- 'Mountainbikes' => 'mountainbikes',
- 'MSI' => 'msi',
- 'Mülleimer' => 'muelleimer',
- 'Multifunktionsdrucker' => 'multifunktionsdrucker',
- 'Multiroom Speaker' => 'multiroom',
- 'Mund- &amp; Zahnpflege' => 'mund-zahnpflege',
- 'Mundschutzmasken' => 'mundschutzmasken',
- 'Museums-Tickets' => 'museum',
- 'Musical Tickets' => 'musical',
- 'Musik' => 'musik',
- 'Musik Apps' => 'musik-apps',
- 'Musikinstrumente' => 'musikinstrumente',
- 'Musik Streaming' => 'musik-streaming',
- 'Müsli' => 'muesli',
- 'Mustang' => 'mustang',
- 'Mützen' => 'muetzen',
- 'Nachtwäsche' => 'nachtwaesche',
- 'Nähbedarf' => 'naehen',
- 'Nähmaschinen' => 'naehmaschine',
- 'Nahrungsergänzungsmittel' => 'nahrungsergaenzungsmittel',
- 'Nahverkehr' => 'nahverkehr',
- 'Naketano' => 'naketano',
- 'NAS' => 'nas',
- 'Nassrasierer' => 'rasierer',
- 'Navigationsgeräte' => 'navigationsgeraete',
- 'Neato' => 'neato',
- 'Neato Robotics Botvac D7 Connected' => 'neato-botvac-d7',
- 'Need for Speed' => 'need-for-speed',
- 'Need for Speed Heat' => 'need-for-speed-heat',
- 'Need for Speed Payback' => 'need-for-speed-payback',
- 'Nerf' => 'nerf',
- 'Nescafé' => 'nescafe',
- 'Nespresso' => 'nespresso',
- 'Nespresso Kaffeemaschinen' => 'nespresso-kaffeemaschinen',
- 'Netflix' => 'netflix',
- 'NETGEAR' => 'netgear',
- 'NETGEAR Nighthawk' => 'netgear-nighthawk',
- 'NETGEAR Orbi' => 'netgear-orbi',
- 'NETGEAR Router' => 'netgear-router',
- 'Netzteile' => 'netzteile',
- 'Netzwerk' => 'netzwerk',
- 'New Balance' => 'new-balance',
- 'Nike' => 'nike',
- 'Nike Air Force 1' => 'nike-air-force',
- 'Nike Air Max' => 'nike-air-max',
- 'Nike Air Max 270' => 'nike-air-max-270',
- 'Nike Air Max 720' => 'nike-air-max-720',
- 'Nike Air Max Thea' => 'nike-air-max-thea',
- 'Nike Air Presto' => 'nike-presto',
- 'Nike Free' => 'nike-free',
- 'Nike Huarache' => 'nike-huarache',
- 'Nike Roshe Run' => 'nike-roshe-run',
- 'Nike Schuhe' => 'nike-schuhe',
- 'Nikon' => 'nikon',
- 'Nikon DSLR' => 'nikon-dslr',
- 'Ni No Kuni' => 'ni-no-kuni',
- 'Ni No Kuni: Der Fluch der Weißen Königin' => 'ni-no-kuni-der-fluch-der-weissen-koenigin',
- 'Ni No Kuni II: Revenant Kingdom' => 'ni-no-kuni-ii',
- 'Nintendo' => 'nintendo',
- 'Nintendo 2DS Konsolen' => 'nintendo-2ds',
- 'Nintendo 3DS Konsolen' => 'nintendo-3ds',
- 'Nintendo 3DS Spiele' => 'nintendo-3ds-spiele',
- 'Nintendo 3DS Zubehör' => 'nintendo-3ds-zubehoer',
- 'Nintendo Classic Mini NES Konsolen' => 'nintendo-classic-mini-nes',
- 'Nintendo Classic Mini SNES Konsolen' => 'nintendo-classic-mini-snes',
- 'Nintendo eShop Guthaben' => 'nintendo-eshop-guthaben',
- 'Nintendo Switch Controller' => 'nintendo-switch-controller',
- 'Nintendo Switch Konsolen' => 'nintendo-switch',
- 'Nintendo Switch Lite Konsolen' => 'nintendo-switch-lite',
- 'Nintendo Switch Pro Controller' => 'nintendo-switch-pro-controller',
- 'Nintendo Switch Spiele' => 'nintendo-switch-spiele',
- 'Nintendo Switch Zubehör' => 'nintendo-switch-zubehoer',
- 'Nintendo Zubehör' => 'nintendo-zubehoer',
- 'Nissan' => 'nissan',
- 'Nivea' => 'nivea',
- 'Nokia' => 'nokia',
- 'Nokia Handys' => 'nokia-handys',
- 'Nudeln' => 'nudeln',
- 'Nuki Smart Locks' => 'nuki-smart-lock',
- 'Nüsse' => 'nuesse',
- 'Nutella' => 'nutella',
- 'Nvidia' => 'nvidia',
- 'Nvidia GeForce' => 'nvidia-geforce',
- 'Nvidia SHIELD TV' => 'nvidia-shield',
- 'o2' => 'o2-netz',
- 'Objektive' => 'objektiv',
- 'Obst' => 'obst',
- 'Obst &amp; Gemüse' => 'obst-gemuese',
- 'Oculus Quest' => 'oculus-quest',
- 'Oculus Rift' => 'oculus-rift',
- 'Office Programme' => 'office-programme',
- 'OLED Fernseher' => 'oled-fernseher',
- 'Olympus' => 'olympus',
- 'On-Ear Kopfhörer' => 'on-ear-kopfhoerer',
- 'OnePlus 3' => 'oneplus-3',
- 'OnePlus 5' => 'oneplus-5',
- 'OnePlus 6' => 'oneplus-6',
- 'OnePlus 7' => 'oneplus-7',
- 'OnePlus 7 Pro' => 'oneplus-7-pro',
- 'OnePlus 7T' => 'oneplus-7t',
- 'OnePlus 7T Pro' => 'oneplus-7t-pro',
- 'OnePlus 8' => 'oneplus-8',
- 'OnePlus 8 Pro' => 'one-plus-8-pro',
- 'OnePlus 8T' => 'oneplus-8t',
- 'OnePlus Nord' => 'oneplus-nord',
- 'OnePlus Smartphones' => 'oneplus-smartphones',
- 'Onkyo' => 'onkyo',
- 'Opel' => 'opel',
- 'OPPO Find X2 Lite' => 'oppo-find-x2-lite',
- 'OPPO Find X2 Neo' => 'oppo-find-x2-neo',
- 'OPPO Find X2 Pro' => 'oppo-find-x2-pro',
- 'OPPO Reno2' => 'oppo-reno2',
- 'OPPO Reno2 Z' => 'oppo-reno2-z',
- 'OPPO Reno4 5G' => 'oppo-reno4-5g',
- 'OPPO Reno4 Pro 5G' => 'oppo-reno4-pro-5g',
- 'OPPO Reno4 Z 5G' => 'oppo-reno4-z-5g',
- 'OPPO Smartphones' => 'oppo-smartphones',
- 'Oral-B' => 'oral-b',
- 'Oral-B Elektrische Zahnbürsten' => 'oral-b-elektrische-zahnbuersten',
- 'Origin' => 'origin',
- 'Osram' => 'osram',
- 'Osram Smart+' => 'osram-smart-plus',
- 'Osterdeko' => 'osterdeko',
- 'Outdoor &amp; Camping' => 'outdoor',
- 'Outdoorbekleidung' => 'outdoorbekleidung',
- 'Outdoorjacken' => 'outdoorjacken',
- 'Outdoor Spielzeuge' => 'outdoor-spielzeug',
- 'Over-Ear Kopfhörer' => 'over-ear-kopfhoerer',
- 'Pampers' => 'pampers',
- 'Panama Jack' => 'panama-jack',
- 'Panasonic' => 'panasonic',
- 'Panasonic Fernseher' => 'panasonic-fernseher',
- 'Panasonic Kameras' => 'panasonic-kameras',
- 'Panasonic Lumix' => 'panasonic-lumix',
- 'Paper Mario: The Origami King' => 'paper-mario-the-origami-king',
- 'Papiertapete' => 'papiertapete',
- 'Parfum' => 'parfum',
- 'Parfum Damen' => 'parfum-damen',
- 'Parfum Herren' => 'parfum-herren',
- 'Pauschalreisen' => 'pauschalreise',
- 'Pavillons' => 'pavillons',
- 'Paw Patrol' => 'paw-patrol',
- 'PAYBACK' => 'payback',
- 'Payday' => 'payday',
- 'Payday 2' => 'payday-2',
- 'paydirekt' => 'paydirekt',
- 'PC Gaming Systeme' => 'pc-gaming-systeme',
- 'PC Gaming Zubehör' => 'pc-gaming-zubehoer',
- 'PC Gehäuse' => 'pc-gehaeuse',
- 'PC Komponenten' => 'hardware',
- 'PC Lautsprecher' => 'pc-lautsprecher',
- 'PC Mäuse' => 'pc-maus',
- 'PC Spiele' => 'pc-spiele',
- 'PC Zubehör' => 'pc-zubehoer',
- 'Pendelleuchten' => 'pendelleuchten',
- 'Pentax' => 'pentax',
- 'Pepe Jeans' => 'pepe-jeans',
- 'Peppa Wutz' => 'peppa-wutz',
- 'PepperBonus' => 'pepperbonus',
- 'Pestos' => 'pestos',
- 'Peugeot' => 'peugeot',
- 'Pfannen' => 'pfannen',
- 'Pflanzen' => 'pflanzen',
- 'Philips' => 'philips',
- 'Philips Fernseher' => 'philips-fernseher',
- 'Philips Hue' => 'philips-hue',
- 'Philips Hue E14' => 'philips-hue-e14',
- 'Philips Hue E27' => 'philips-hue-e27',
- 'Philips Hue Go' => 'philips-hue-go',
- 'Philips Hue GU10' => 'philips-hue-gu10',
- 'Philips Hue LightStrip' => 'philips-hue-lightstrip',
- 'Philips Hue Play Gradient LightStrip' => 'philips-hue-play-gradient-lightstrip',
- 'Philips Hue Play HDMI Sync Box' => 'philips-hue-play-hdmi-sync-box',
- 'Philips Hue Play Lightbar' => 'philips-hue-play',
- 'Philips OneBlade' => 'philips-oneblade',
- 'Philips Rasierer' => 'philips-rasierer',
- 'Philips Sonicare' => 'philips-sonicare',
- 'Philips Staubsauger' => 'philips-staubsauger',
- 'Philips Wecker' => 'philips-wecker',
- 'Photoshop' => 'photoshop',
- 'Pioneer' => 'pioneer',
- 'Pizza' => 'pizza',
- 'Plattenspieler' => 'plattenspieler',
- 'Playboy' => 'playboy',
- 'Playerunknown&#039;s Battlegrounds' => 'playerunknowns-battlegrounds',
- 'PLAYMOBIL' => 'playmobil',
- 'PLAYMOBIL Adventskalender' => 'playmobil-adventskalender',
- 'PlayStation' => 'playstation',
- 'PlayStation 4 Controller' => 'playstation-4-controller',
- 'PlayStation 4 Konsolen' => 'playstation-4',
- 'PlayStation 4 Pro Konsolen' => 'playstation-4-pro',
- 'PlayStation 4 Spiele' => 'playstation-4-spiele',
- 'PlayStation 5 Konsolen' => 'playstation-5',
- 'PlayStation 5 Spiele' => 'playstation-5-spiele',
- 'PlayStation Classic Konsolen' => 'playstation-classic',
- 'PlayStation Now' => 'playstation-now',
- 'PlayStation Plus' => 'playstation-plus',
- 'PlayStation Zubehör' => 'playstation-zubehoer',
- 'Plüschtiere' => 'plueschtiere',
- 'Plus Size Mode' => 'plus-size-mode',
- 'POCO F2 Pro' => 'poco-f2-pro',
- 'POCO X3' => 'poco-x3',
- 'Pokémon' => 'pokemon',
- 'Pokémon: Let&#039;s Go' => 'pokemon-lets-go',
- 'Pokémon Schwert und Schild' => 'pokemon-schwert-schild',
- 'Pokémon Tekken' => 'pokemon-tekken',
- 'Pokémon Ultrasonne &amp; Ultramond' => 'pokemon-ultrasonne-ultramond',
- 'Poloshirts' => 'poloshirts',
- 'Polsterbetten' => 'polsterbetten',
- 'Polyrattan Möbel' => 'polyrattan',
- 'Pools' => 'pools',
- 'Powerbanks' => 'powerbanks',
- 'Powerbeats Pro' => 'powerbeats',
- 'Preisfehler' => 'preisfehler',
- 'Prepaid-Tarife' => 'prepaid-tarife',
- 'Prime Gaming' => 'twitch-prime',
- 'Pro Evolution Soccer' => 'pro-evolution-soccer',
- 'Pro Evolution Soccer 2018' => 'pes-2018',
- 'Pro Evolution Soccer 2019' => 'pes-2019',
- 'Pro Evolution Soccer 2020' => 'pes-2020',
- 'Proteine' => 'whey-proteine',
- 'Prozessoren' => 'prozessoren',
- 'PSN Guthaben' => 'psn-guthaben',
- 'Puky' => 'puky',
- 'Pullover' => 'pullover',
- 'PUMA' => 'puma',
- 'Pumps' => 'pumps',
- 'Puppen' => 'puppen',
- 'Puppenhäuser' => 'puppenhaeuser',
- 'Puzzles' => 'puzzle',
- 'Qeridoo' => 'qeridoo',
- 'Qeridoo Fahrradanhänger' => 'qeridoo-fahrradanhaenger',
- 'Qeridoo KidGoo 2' => 'qeridoo-kidgoo-2',
- 'Qeridoo Sportrex 2' => 'qeridoo-sportrex-2',
- 'Quiksilver' => 'quiksilver',
- 'Raclettes' => 'raclettes',
- 'Radios' => 'radios',
- 'Radsport' => 'radsport',
- 'Rasenmäher' => 'rasenmaeher',
- 'Rasentrimmer' => 'rasentrimmer',
- 'Rasierklingen' => 'rasierklingen',
- 'Raspberry Pi' => 'raspberry-pi',
- 'Rasur, Enthaarung &amp; Trimmen' => 'rasur-enthaarung',
- 'Rauchmelder' => 'rauchmelder',
- 'Ravensburger' => 'ravensburger',
- 'Ray-Ban' => 'ray-ban',
- 'Razer DeathAdder' => 'razer-deathadder',
- 'RC Autos' => 'rc-autos',
- 'Red Bull' => 'red-bull',
- 'Red Dead Redemption' => 'red-dead-redemption',
- 'Red Dead Redemption 2' => 'red-dead-redemption-2',
- 'Reebok' => 'reebok',
- 'Regale' => 'regale',
- 'Reifen' => 'reifen',
- 'Reinigungsmittel' => 'reinigungsmittel',
- 'Reise Apps' => 'reise-apps',
- 'Reisen' => 'reisen',
- 'Reiskocher' => 'reiskocher',
- 'Remington' => 'remington',
- 'Renault' => 'renault',
- 'Rennräder' => 'rennraeder',
- 'Repeater' => 'repeater',
- 'Resident Evil' => 'resident-evil',
- 'Resident Evil 2' => 'resident-evil-2',
- 'Resident Evil 7' => 'resident-evil-7',
- 'Restaurant' => 'restaurant',
- 'Retro Stil' => 'retro-stil',
- 'Rimowa' => 'rimowa',
- 'Ring Fit Adventure' => 'ring-fit-adventure',
- 'Rituals' => 'rituals',
- 'Rituals Adventskalender' => 'rituals-adventskalender',
- 'Roborock' => 'xiaomi-roborock',
- 'Roborock S5 Max' => 'roborock-s5-max',
- 'Roborock S6' => 'roborock-s6',
- 'Roborock S6 MaxV' => 'roborock-s6-maxv',
- 'ROCCAT' => 'roccat',
- 'ROCCAT Tyon' => 'roccat-tyon',
- 'Röcke' => 'roecke',
- 'Rocket League' => 'rocket-league',
- 'Roidmi Staubsauger' => 'roidmi-staubsauger',
- 'Rollei' => 'rollei',
- 'Rösle' => 'roesle',
- 'Router' => 'router',
- 'Roxy' => 'roxy',
- 'RTX 2060' => 'rtx-2060',
- 'RTX 2070' => 'rtx-2070',
- 'RTX 2080' => 'rtx-2080',
- 'RTX 2080 Ti' => 'rtx-2080-ti',
- 'RTX 3070' => 'rtx-3070',
- 'RTX 3080' => 'rtx-3080',
- 'RTX 3090' => 'rtx-3090',
- 'Rucksäcke' => 'rucksaecke',
- 'Russell Hobbs' => 'russell-hobbs',
- 'RX 480' => 'rx-480',
- 'RX 570' => 'rx-570',
- 'RX 580' => 'rx-580',
- 'RX 590' => 'rx-590',
- 'RX 5700 XT' => 'rx-5700-xt',
- 'RX 6800' => 'rx-6800',
- 'RX 6800 XT' => 'rx-6800-xt',
- 'RX 6900 XT' => 'rx-6900-xt',
- 'RX Vega 56' => 'rx-vega-56',
- 'RX Vega 64' => 'rx-vega-64',
- 'Sägen' => 'saegen',
- 'Salomon' => 'salomon',
- 'Samsonite' => 'samsonite',
- 'Samsung' => 'samsung',
- 'Samsung Fernseher' => 'samsung-fernseher',
- 'Samsung Galaxy A7' => 'samsung-galaxy-a7',
- 'Samsung Galaxy A8' => 'samsung-galaxy-a8',
- 'Samsung Galaxy A51' => 'samsung-galaxy-a51',
- 'Samsung Galaxy A71' => 'samsung-galaxy-a71',
- 'Samsung Galaxy Buds' => 'samsung-galaxy-buds',
- 'Samsung Galaxy Buds+' => 'samsung-galaxy-buds-plus',
- 'Samsung Galaxy Buds Live' => 'samsung-galaxy-buds-live',
- 'Samsung Galaxy Buds Pro' => 'samsung-galaxy-buds-pro',
- 'Samsung Galaxy Note9' => 'samsung-galaxy-note-9',
- 'Samsung Galaxy Note20' => 'samsung-galaxy-note20',
- 'Samsung Galaxy Note20 Ultra' => 'samsung-galaxy-note20-ultra',
- 'Samsung Galaxy S7' => 'samsung-galaxy-s7',
- 'Samsung Galaxy S7 Edge' => 'samsung-galaxy-s7-edge',
- 'Samsung Galaxy S8' => 'samsung-galaxy-s8',
- 'Samsung Galaxy S8+' => 'samsung-galaxy-s8-plus',
- 'Samsung Galaxy S9' => 'samsung-galaxy-s9',
- 'Samsung Galaxy S9+' => 'samsung-galaxy-s9-plus',
- 'Samsung Galaxy S10' => 'samsung-galaxy-s10',
- 'Samsung Galaxy S10+' => 'samsung-galaxy-s10-plus',
- 'Samsung Galaxy S10e' => 'samsung-galaxy-s10e',
- 'Samsung Galaxy S20' => 'samsung-galaxy-s20',
- 'Samsung Galaxy S20 FE' => 'samsung-galaxy-s20-fe',
- 'Samsung Galaxy S20 Ultra' => 'samsung-galaxy-s20-ultra',
- 'Samsung Galaxy S20+' => 'samsung-galaxy-s20-plus',
- 'Samsung Galaxy S21 5G' => 'samsung-galaxy-s21-5g',
- 'Samsung Galaxy S21 Ultra 5G' => 'samsung-galaxy-s21-ultra-5g',
- 'Samsung Galaxy S21+ 5G' => 'samsung-galaxy-s21-plus-5g',
- 'Samsung Galaxy Tab S4' => 'samsung-galaxy-tab-s4',
- 'Samsung Galaxy Tab S6' => 'samsung-galaxy-tab-s6',
- 'Samsung Galaxy Watch' => 'samsung-galaxy-watch',
- 'Samsung Galaxy Watch Active2' => 'samsung-galaxy-watch-active-2',
- 'Samsung Gear' => 'samsung-gear',
- 'Samsung Gear S3' => 'samsung-gear-s3',
- 'Samsung Gear VR' => 'samsung-gear-vr',
- 'Samsung Kopfhörer' => 'samsung-kopfhoerer',
- 'Samsung Kühlschränke' => 'samsung-kuehlschrank',
- 'Samsung Monitore' => 'samsung-monitor',
- 'Samsung QLED Fernseher' => 'samsung-qled-fernseher',
- 'Samsung Smartphones' => 'samsung-smartphone',
- 'Samsung SSD' => 'samsung-ssd',
- 'Samsung Tablets' => 'samsung-tablet',
- 'Samsung The Frame Fernseher' => 'samsung-the-frame-fernseher',
- 'Samsung Waschmaschinen' => 'samsung-waschmaschine',
- 'Sandalen' => 'sandalen',
- 'SanDisk' => 'sandisk',
- 'SanDisk SSD' => 'sandisk-ssd',
- 'Sanitär &amp; Armaturen' => 'sanitaer-armaturen',
- 'Saucen' => 'saucen',
- 'Saugroboter' => 'saugroboter',
- 'Scanner' => 'scanner',
- 'Schallplatten' => 'schallplatten',
- 'Scheppach' => 'scheppach',
- 'Schlafsäcke' => 'schlafsack',
- 'Schlafsofas' => 'schlafsofas',
- 'Schlafzimmer' => 'schlafzimmer',
- 'Schlagschrauber' => 'schlagschrauber',
- 'Schlauchboote' => 'schlauchboote',
- 'Schleich' => 'schleich',
- 'Schlitten' => 'schlitten',
- 'Schmuck' => 'schmuck',
- 'Schneefräsen' => 'schneefraesen',
- 'Schnellkochtöpfe' => 'schnellkochtoepfe',
- 'Schnürhalbschuhe' => 'schnuerhalbschuhe',
- 'Schokolade' => 'schokolade',
- 'Schraubendreher' => 'schraubendreher',
- 'Schreibgeräte' => 'schreibgeraete',
- 'Schreibtische' => 'schreibtisch',
- 'Schuhe' => 'schuhe',
- 'Schuhschränke' => 'schuhschraenke',
- 'Schulbedarf' => 'schulbedarf',
- 'Schulranzen' => 'schulranzen',
- 'Schutzfolien' => 'schutzfolien',
- 'Schwangerschaft' => 'schwangerschaft',
- 'Schwerlastregale' => 'schwerlastregale',
- 'Scooter' => 'scooter',
- 'Scotch Whisky' => 'scotch-whisky',
- 'SDHC Speicherkarten' => 'sdhc-speicherkarten',
- 'SD Karten' => 'sd-karten',
- 'Seagate' => 'seagate',
- 'Sea of Thieves' => 'sea-of-thieves',
- 'Seat' => 'seat',
- 'Sega Mega Drive Mini Konsolen' => 'sega-mega-drive-mini',
- 'Seidensticker' => 'seidensticker',
- 'Sekiro: Shadows Die Twice' => 'sekiro',
- 'Senf' => 'senf',
- 'Sennheiser' => 'sennheiser',
- 'Senseo' => 'senseo',
- 'Service-Verträge' => 'service-vertraege',
- 'Sessel' => 'sessel',
- 'Sextoys' => 'sextoys',
- 'Shadow of the Tomb Raider' => 'shadow-of-the-tomb-raider',
- 'Shampoo' => 'shampoo',
- 'Sharkoon' => 'sharkoon',
- 'Sharp' => 'sharp',
- 'Shenmue' => 'shenmue',
- 'Shenmue I &amp; II' => 'shenmue-i-ii',
- 'Shenmue III' => 'shenmue-iii',
- 'Shishas' => 'shishas',
- 'Shishas &amp; Zubehör' => 'shishas-zubehoer',
- 'Shoop' => 'shoop',
- 'Shops: Erfahrungen' => 'shops',
- 'Shorts' => 'shorts',
- 'Sicherheitstechnik' => 'sicherheitstechnik',
- 'Side-by-Side-Kühlschränke' => 'side-by-side-kuehlschrank',
- 'Sid Meier&#039;s Civilization VI' => 'sid-meiers-civilization-vi',
- 'Sid Meier’s Civilization' => 'sid-meiers-civilization',
- 'Siemens' => 'siemens',
- 'Siemens Geschirrspüler' => 'siemens-geschirrspueler',
- 'Siemens Kühlschränke' => 'siemens-kuehlschrank',
- 'Siemens Waschmaschinen' => 'siemens-waschmaschine',
- 'Silit' => 'silit',
- 'Skandi Stil' => 'skandi-stil',
- 'Skateboards' => 'skateboard',
- 'Skaten' => 'skaten',
- 'Ski &amp; Snowboard' => 'snowboard',
- 'Skoda' => 'skoda',
- 'Sky' => 'sky',
- 'Sky Ticket' => 'sky-ticket',
- 'Smarte Beleuchtung' => 'smarte-beleuchtung',
- 'Smarte Wecker' => 'smarte-wecker',
- 'Smart Home' => 'smart-home',
- 'Smart Home Steckdosen' => 'smart-home-steckdosen',
- 'Smart Locks' => 'smart-lock',
- 'Smartphones' => 'smartphone',
- 'Smartphones unter 200€' => 'smartphones-unter-200-euro',
- 'Smart Speaker' => 'smart-speaker',
- 'Smart Tech &amp; Gadgets' => 'smart-tech',
- 'Smartwatches' => 'smartwatch',
- 'Smoothie Maker' => 'smoothie-maker',
- 'Snacks &amp; Knabberzeug' => 'snacks-knabberzeug',
- 'Sneakers' => 'sneaker',
- 'Socken' => 'socken',
- 'SodaStream' => 'sodastream',
- 'Sofas' => 'sofa',
- 'Sofortbildkameras' => 'sofortbildkameras',
- 'Softdrinks' => 'softdrinks',
- 'Software' => 'software',
- 'Software &amp; Apps' => 'apps-software',
- 'Solarleuchten' => 'solarleuchten',
- 'Somat' => 'somat',
- 'Sommerreifen' => 'sommerreifen',
- 'Sonnenbrillen' => 'sonnenbrillen',
- 'Sonnencreme' => 'sonnencreme',
- 'Sonnenpflege' => 'sonnenpflege',
- 'Sonnenschirme' => 'sonnenschirme',
- 'Sonoff' => 'sonoff',
- 'Sonos' => 'sonos',
- 'Sonos Beam' => 'sonos-beam',
- 'Sonos Move' => 'sonos-move',
- 'Sonos One' => 'sonos-one',
- 'Sonos PLAY:1' => 'sonos-play-1',
- 'Sonos PLAY:3' => 'sonos-play-3',
- 'Sonos Play:5 (Five)' => 'sonos-play-5',
- 'Sonos Playbar' => 'sonos-playbar',
- 'Sonos Playbase' => 'sonos-playbase',
- 'Sonstiges' => 'diverses',
- 'Sony' => 'sony',
- 'Sony Alpha 7' => 'sony-alpha-7',
- 'Sony Alpha 7 II' => 'sony-alpha-7-ii',
- 'Sony Alpha 7 III' => 'sony-alpha-7-iii',
- 'Sony Alpha 6000' => 'sony-alpha-6000',
- 'Sony Alpha 6300' => 'sony-alpha-6300',
- 'Sony Alpha 6400' => 'sony-alpha-6400',
- 'Sony Alpha 6500' => 'sony-alpha-6500',
- 'Sony DualSense Wireless-Controller' => 'playstation-5-controller',
- 'Sony Fernseher' => 'sony-fernseher',
- 'Sony Kameras' => 'sony-kameras',
- 'Sony Kopfhörer' => 'sony-kopfhoerer',
- 'Sony PlayStation VR' => 'sony-playstation-vr',
- 'Sony PULSE 3D Wireless Headset' => 'sony-pulse-3d-wireless-headset',
- 'Sony WF-1000XM3' => 'sony-wf-1000xm3',
- 'Sony WH-1000XM3' => 'sony-wh-1000xm3',
- 'Sony WH-1000XM4' => 'sony-wh-1000xm4',
- 'Sony Xperia' => 'sony-xperia',
- 'Sony Xperia X' => 'sony-xperia-x',
- 'Sony Xperia XA' => 'sony-xperia-xa',
- 'Sony Xperia XZ' => 'sony-xperia-xz',
- 'Soundbar' => 'soundbar',
- 'Soundbase' => 'soundbase',
- 'Soundkarten' => 'soundkarten',
- 'South Park: Die rektakuläre Zerreißprobe' => 'south-park-die-rektakulaere-zerreissprobe',
- 'Spartipps' => 'spartipps',
- 'Speicherkarten' => 'speicherkarten',
- 'Speichermedien' => 'speichermedien',
- 'Speiseöle' => 'speiseoele',
- 'Spiegelreflexkameras' => 'spiegelreflexkamera',
- 'Spiele &amp; Brettspiele' => 'spiele-brettspiele',
- 'Spiele Apps' => 'spiele-apps',
- 'Spielekonsolen' => 'spielekonsolen',
- 'Spielfiguren &amp; Spielsets' => 'spielfiguren-spielsets',
- 'Spielzeuge' => 'spielzeug',
- 'Spirituosen' => 'spirituosen',
- 'Sport &amp; Outdoor' => 'sport',
- 'Sportbekleidung' => 'sportbekleidung',
- 'Sport Bild' => 'sport-bild',
- 'Sportnahrung' => 'sportlernahrung',
- 'Sporttasche' => 'sporttasche',
- 'Spotify' => 'spotify',
- 'Spülmaschinentabs' => 'spuelmaschinentabs',
- 'Spyro Reignited Trilogy' => 'spyro-reignited-trilogy',
- 'SSD' => 'ssd',
- 'Stabmixer' => 'stabmixer',
- 'Städtereisen' => 'staedtereise',
- 'Standmixer' => 'standmixer',
- 'Star Trek' => 'star-trek',
- 'Star Wars' => 'star-wars',
- 'Star Wars: Battlefront 2' => 'star-wars-battlefront-2',
- 'Star Wars: Squadrons' => 'star-wars-squadrons',
- 'Star Wars Battlefront' => 'star-wars-battlefront',
- 'Star Wars Jedi: Fallen Order' => 'star-wars-jedi-fallen-order',
- 'Staubsauger' => 'staubsauger',
- 'Staubsaugerbeutel' => 'staubsaugerbeutel',
- 'Staubsauger ohne Beutel' => 'staubsauger-ohne-beutel',
- 'Steam' => 'steam',
- 'Steckschlüssel' => 'steckschluessel',
- 'SteelSeries' => 'steelseries',
- 'Stehlampen' => 'stehlampen',
- 'Steiff' => 'steiff',
- 'Stern (Magazin)' => 'stern-magazin',
- 'Stichsägen' => 'stichsaegen',
- 'Stiefel' => 'stiefel',
- 'Stiefeletten' => 'stiefeletten',
- 'Stiftung Warentest' => 'stiftung-warentest-magazin',
- 'Streaming-Dienste' => 'streaming-dienste',
- 'Streaming Lautsprecher' => 'streaming-lautsprecher',
- 'Strom &amp; Gas' => 'strom-gas',
- 'Stromtarif' => 'stromtarif',
- 'Studentenrabatte' => 'studentenrabatte',
- 'Stühle' => 'stuehle',
- 'Subwoofer' => 'subwoofer',
- 'SUP Boards' => 'sup-boards',
- 'Superdry' => 'superdry',
- 'Super Mario' => 'super-mario',
- 'Super Mario 3D All-Stars' => 'super-mario-3d-all-stars',
- 'Super Mario Maker 2' => 'super-mario-maker-2',
- 'Super Mario Odyssey' => 'super-mario-odyssey',
- 'Super Mario Party' => 'super-mario-party',
- 'Supermarkt' => 'supermarkt',
- 'Super Smash Bros. Ultimate' => 'super-smash-bros-ultimate',
- 'Süßigkeiten' => 'suessigkeiten',
- 'Synology' => 'synology',
- 'Syoss' => 'syoss',
- 'Systemkameras' => 'systemkamera',
- 'T-Shirts' => 't-shirts',
- 'Tablets' => 'tablet',
- 'Tablet Zubehör' => 'tablet-zubehoer',
- 'tado° Smartes Heizkörper-Thermostat' => 'tado-smartes-thermostat',
- 'Tamaris' => 'tamaris',
- 'Tangle Teezer' => 'tangle-teezer',
- 'Tanqueray' => 'tanqueray',
- 'Tapeten' => 'tapeten',
- 'Taschen' => 'taschen',
- 'Taschenlampen' => 'taschenlampen',
- 'Taschentücher' => 'taschentuecher',
- 'Tassimo' => 'tassimo',
- 'Tassimo Kaffeemaschinen' => 'tassimo-kaffeemaschinen',
- 'Tastaturen' => 'tastatur',
- 'TCL Fernseher' => 'tcl-fernseher',
- 'Team Sonic Racing' => 'team-sonic-racing',
- 'Teamsport' => 'teamsport',
- 'Tee' => 'tee',
- 'Tefal' => 'tefal',
- 'Tefal OptiGrills' => 'tefal-optigrill',
- 'Tefal Pfannen' => 'tefal-pfannen',
- 'Tekken' => 'tekken',
- 'Tekken 7' => 'tekken-7',
- 'Telefon- &amp; Internet-Verträge' => 'telefon-internet',
- 'Telefone &amp; Zubehör' => 'handy-smartphone',
- 'Telekom' => 'telekom-net',
- 'Telekom Magenta' => 'telekom-magenta',
- 'Telekom SmartHome' => 'telekom-smarthome',
- 'Teppiche' => 'teppiche',
- 'Tesla' => 'tesla',
- 'Tetris' => 'tetris',
- 'Teufel' => 'teufel',
- 'The Elder Scrolls' => 'the-elder-scrolls',
- 'The Elder Scrolls V: Skyrim' => 'skyrim',
- 'The Evil Within' => 'the-evil-within',
- 'The Evil Within 2' => 'the-evil-within-2',
- 'The Last of Us' => 'the-last-of-us',
- 'The Last of Us Part II' => 'the-last-of-us-part-ii',
- 'The Legend of Zelda' => 'the-legend-of-zelda',
- 'The Legend of Zelda: Breath of the Wild' => 'zelda-breath-of-the-wild',
- 'The Legend of Zelda: Link&#039;s Awakening' => 'zelda-links-awakening',
- 'The Legend of Zelda: Skyward Sword HD' => 'zelda-skyward-sword-hd',
- 'The North Face' => 'the-north-face',
- 'The Outer Worlds' => 'the-outer-worlds',
- 'Thermosflaschen' => 'thermosflaschen',
- 'Thermoskannen' => 'thermoskanne',
- 'The Witcher' => 'the-witcher',
- 'The Witcher 3' => 'the-witcher-3',
- 'Thule' => 'thule',
- 'Thule Chariot Fahrradanhänger' => 'thule-chariot-fahrradanhaenger',
- 'Thule Dachboxen' => 'thule-dachboxen',
- 'Thule Fahrradträger' => 'thule-fahrradtraeger',
- 'Tickets &amp; Shows' => 'erlebnisse',
- 'Tiefkühlkost' => 'tiefkuehkost',
- 'Timberland' => 'timberland',
- 'Tintenstrahldrucker' => 'tintenstrahldrucker',
- 'Tischlampen' => 'tischlampen',
- 'Tischtennis' => 'tischtennis',
- 'Tischtennisplatten' => 'tischtennisplatten',
- 'Tischtennisschläger' => 'tischtennisschlaeger',
- 'Toaster' => 'toaster',
- 'Toilettenpapier' => 'toilettenpapier',
- 'tolino' => 'tolino',
- 'Tomb Raider' => 'tomb-raider',
- 'Tom Clancy&#039;s' => 'tom-clancys',
- 'Tom Clancy&#039;s: Ghost Recon Wildlands' => 'tom-clancys-ghost-recon-wildlands',
- 'Tom Clancy&#039;s Ghost Recon Breakpoint' => 'tom-clancys-ghost-recon-breakpoint',
- 'Tom Clancy&#039;s The Division 2' => 'tom-clancy-the-division-2',
- 'Tommy Hilfiger' => 'tommy-hilfiger',
- 'TOM TAILOR' => 'tom-tailor',
- 'Toner' => 'toner',
- 'Tonic Water' => 'tonic-water',
- 'Toniebox' => 'toniebox',
- 'Tonies Figuren' => 'tonie-figuren',
- 'Töpfe' => 'toepfe',
- 'Töpfe &amp; Pfannen' => 'kochen',
- 'Toplader' => 'toplader',
- 'Toshiba' => 'toshiba',
- 'Total War' => 'total-war',
- 'Toyota' => 'toyota',
- 'TP-Link' => 'tp-link',
- 'TP-Link Router' => 'tp-link-router',
- 'Trampoline' => 'trampolin',
- 'TREKSTOR' => 'trekstor',
- 'Trockner' => 'trockner',
- 'Tropical Islands' => 'tropical-island',
- 'Tropico' => 'tropico',
- 'Tropico 5' => 'tropico-5',
- 'Tropico 6' => 'tropico-6',
- 'TV &amp; Video' => 'tv-video',
- 'TV Boxen' => 'tv-box',
- 'TV Spielfilm' => 'tv-spielfilm',
- 'TV Wandhalterungen' => 'tv-wandhalterung',
- 'TV Zubehör' => 'tv-zubehoer',
- 'Übergangsjacken' => 'uebergangsjacken',
- 'Überwachungskamera' => 'ueberwachungskamera',
- 'UE BLAST' => 'ue-blast',
- 'UE BOOM' => 'ue-boom',
- 'UE BOOM 2' => 'ue-boom-2',
- 'UE BOOM 3' => 'ue-boom-3',
- 'UE MEGABLAST' => 'ue-megablast',
- 'UE MEGABOOM' => 'ue-megaboom',
- 'UE MEGABOOM 3' => 'ue-megaboom-3',
- 'UE WONDERBOOM' => 'ue-wonderboom',
- 'UE WONDERBOOM 2' => 'ue-wonderboom-2',
- 'UGG' => 'ugg',
- 'Uhren' => 'uhren',
- 'Umstandsmode' => 'umstandsmode',
- 'Uncharted' => 'uncharted',
- 'Uncharted 4' => 'uncharted-4',
- 'Uncharted: The Lost Legacy' => 'uncharted-the-lost-legacy',
- 'Under Armour' => 'under-armour',
- 'Universalfernbedienungen' => 'universalfernbedienungen',
- 'Unterwäsche' => 'unterwaesche',
- 'Uplay' => 'uplay',
- 'Urban Sport' => 'urban-sport',
- 'Urlaub' => 'urlaub',
- 'USB Sticks' => 'usb-stick',
- 'Vakuumierer' => 'vakuumierer',
- 'Vans' => 'vans',
- 'Vans Old Skool' => 'vans-old-skool',
- 'Vans Schuhe' => 'vans-schuhe',
- 'Vaude' => 'vaude',
- 'Ventilatoren' => 'ventilator',
- 'Verbandskästen' => 'verbandskaesten',
- 'Versicherung' => 'versicherung',
- 'Versicherung &amp; Finanzen' => 'vertraege-finanzen',
- 'Videobearbeitungsprogramme' => 'videobearbeitungsprogramme',
- 'Video Player' => 'video-player',
- 'Videospiele' => 'videospiele',
- 'Video Streaming' => 'video-streaming',
- 'Vileda' => 'vileda',
- 'Villeroy &amp; Boch' => 'villeroy-boch',
- 'Virenschutz' => 'virenschutz',
- 'VISA' => 'visa',
- 'Vliestapeten' => 'vliestapete',
- 'Vodafone' => 'vodafone-netz',
- 'Vodka' => 'vodka',
- 'Volvo' => 'volvo',
- 'Vorratsdosen' => 'vorratsdosen',
- 'Vorstellungsrunde' => 'vorstellungsrunde',
- 'VPN' => 'vpn',
- 'VPS' => 'vps',
- 'VR Brillen' => 'vr-brille',
- 'VR Spiele' => 'vr-spiele',
- 'VTech' => 'vtech',
- 'VW' => 'vw',
- 'Waffeleisen' => 'waffeleisen',
- 'Wandbilder' => 'wandtattoos',
- 'Wanderrucksäcke' => 'wanderrucksack',
- 'Wanderschuhe' => 'wanderschuhe',
- 'Wandersport' => 'hiking',
- 'Wandfarben' => 'wandfarben',
- 'Wandlampen' => 'wandlampen',
- 'Wäscheständer' => 'waeschestaender',
- 'Waschmaschinen' => 'waschmaschinen',
- 'Waschmittel' => 'waschmittel',
- 'Waschtrockner' => 'waschtrockner',
- 'Wasserfilter' => 'wasserfilter',
- 'Wasserkocher' => 'wasserkocher',
- 'Wasserkühlung' => 'wasserkuehlung',
- 'Wasserspielzeuge' => 'wasserspielzeug',
- 'Wassersport' => 'wassersport',
- 'Watch Dogs' => 'watch-dogs',
- 'Watch Dogs 2' => 'watch-dogs-2',
- 'Watch Dogs: Legion' => 'watch-dogs-legion',
- 'WC Sitze' => 'wc-sitze',
- 'WD-40' => 'wd-40',
- 'Wearables' => 'wearable',
- 'Webcams' => 'webcam',
- 'Weber Gasgrills' => 'weber-gasgrill',
- 'Weber Grills' => 'weber-grill',
- 'Weihnachtsbäume' => 'weihnachtsbaum',
- 'Weihnachtsbeleuchtung' => 'weihnachtsbeleuchtung',
- 'Weihnachtsdeko' => 'weihnachtsdeko',
- 'Weihnachtspullover' => 'weihnachtspullover',
- 'Wein' => 'wein',
- 'Wellensteyn' => 'wellensteyn',
- 'Wellness &amp; Gesundheit' => 'wellness-massagen',
- 'Wera' => 'wera',
- 'Werkstatt &amp; Service' => 'werkstatt-service',
- 'Werkstatteinrichtungen' => 'werkstatteinrichtungen',
- 'Werkzeuge' => 'werkzeug',
- 'Werkzeugkoffer' => 'werkzeugkoffer',
- 'Wesco Mülleimer' => 'wesco-muelleimer',
- 'Western Digital' => 'western-digital',
- 'Wetterstationen' => 'wetterstationen',
- 'Whirlpools' => 'whirlpools',
- 'Whisky' => 'whisky',
- 'Wiko' => 'wiko',
- 'Wilkinson Sword Rasierer' => 'wilkinson-sword',
- 'Windeln' => 'windeln',
- 'Winkelschleifer' => 'winkelschleifer',
- 'Winterdeko' => 'winterdeko',
- 'Winterjacken' => 'winterjacken',
- 'Winterreifen' => 'winterreifen',
- 'Winterstiefel' => 'winterstiefel',
- 'Wireless Charger' => 'wireless-charger',
- 'Wirtschaftswoche' => 'wirtschaftswoche',
- 'WMF' => 'wmf',
- 'WMF Besteck' => 'wmf-besteck',
- 'WMF Topfset' => 'wmf-topfset',
- 'Wohnzimmermöbel' => 'wohnzimmer',
- 'Wolfenstein' => 'wolfenstein',
- 'Wolfenstein II: The New Colossus' => 'wolfenstein-2-the-new-colossus',
- 'Womanizer' => 'womanizer',
- 'World of Warcraft' => 'world-of-warcraft',
- 'Wrangler' => 'wrangler',
- 'X570 Mainboard' => 'x570-mainboard',
- 'Xbox' => 'xbox',
- 'Xbox Controller' => 'xbox-controller',
- 'Xbox Elite Wireless Controller' => 'xbox-one-elite-controller',
- 'Xbox Elite Wireless Controller 2' => 'xbox-one-elite-controller-2',
- 'Xbox Game Pass' => 'xbox-game-pass',
- 'Xbox Game Pass Ultimate' => 'xbox-game-pass-ultimate',
- 'Xbox Guthaben' => 'xbox-guthaben',
- 'Xbox Live Gold' => 'xbox-live',
- 'Xbox One Controller' => 'xbox-one-controller',
- 'Xbox One S Konsolen' => 'xbox-one-s',
- 'Xbox One Spiele' => 'xbox-one-spiele',
- 'Xbox One X Konsolen' => 'xbox-one-x',
- 'Xbox Series S Konsolen' => 'xbox-series-s',
- 'Xbox Series X Controller' => 'xbox-series-x-controller',
- 'Xbox Series X Konsolen' => 'xbox-series-x',
- 'Xbox Series X Spiele' => 'xbox-series-x-spiele',
- 'Xbox Wireless Headset' => 'xbox-wireless-headset',
- 'Xbox Zubehör' => 'xbox-zubehoer',
- 'Xiaomi' => 'xiaomi',
- 'Xiaomi Air Laptop' => 'xiaomi-air',
- 'Xiaomi E-Scooter' => 'xiaomi-e-scooter',
- 'Xiaomi Fernseher' => 'xiaomi-fernseher',
- 'Xiaomi Kopfhörer' => 'xiaomi-kopfhoerer',
- 'Xiaomi Mi 5S' => 'xiaomi-mi-5',
- 'Xiaomi Mi 6' => 'xiaomi-mi-6',
- 'Xiaomi Mi 8' => 'xiaomi-mi-8',
- 'Xiaomi Mi 8 Lite' => 'xiaomi-mi-8-lite',
- 'Xiaomi Mi 8 Pro' => 'xiaomi-mi-8-pro',
- 'Xiaomi Mi 9' => 'xiaomi-mi-9',
- 'Xiaomi Mi 9 Lite' => 'xiaomi-mi-9-lite',
- 'Xiaomi Mi 9 SE' => 'xiaomi-mi-9-se',
- 'Xiaomi Mi 9T' => 'xiaomi-mi-9t',
- 'Xiaomi Mi 9T Pro' => 'xiaomi-mi-9t-pro',
- 'Xiaomi Mi 10' => 'xiaomi-mi-10',
- 'Xiaomi Mi 10 Lite' => 'xiaomi-mi-10-lite',
- 'Xiaomi Mi 10 Pro' => 'xiaomi-mi-10-pro',
- 'Xiaomi Mi 11' => 'xiaomi-mi-11',
- 'Xiaomi Mi A1' => 'xiaomi-mi-a1',
- 'Xiaomi Mi A2' => 'xiaomi-mi-a2',
- 'Xiaomi Mi AirDots' => 'xiaomi-mi-airdots',
- 'Xiaomi Mi AirDots Pro' => 'xiaomi-airdots-pro',
- 'Xiaomi Mi Band' => 'xiaomi-mi-band',
- 'Xiaomi Mi Band 4' => 'xiaomi-mi-band-4',
- 'Xiaomi Mi Band 5' => 'xiaomi-mi-band-5',
- 'Xiaomi Mi Electric Scooter 1S' => 'xiaomi-mi-scooter-1s',
- 'Xiaomi Mi Electric Scooter M365' => 'xiaomi-mi-electric-scooter-m365',
- 'Xiaomi Mi Electric Scooter Pro 2' => 'xiaomi-mi-electric-scooter-pro-2',
- 'Xiaomi Mi Mix' => 'xiaomi-mi-mix',
- 'Xiaomi Mi Mix 3' => 'xiaomi-mi-mix-3',
- 'Xiaomi Mi Note' => 'xiaomi-mi-note',
- 'Xiaomi Mi Note 10' => 'xiaomi-mi-note-10',
- 'Xiaomi Mi Note 10 Lite' => 'xiaomi-mi-note-10-lite',
- 'Xiaomi Mi Note 10 Pro' => 'xiaomi-mi-note-10-pro',
- 'Xiaomi Mi TV 4S' => 'xiaomi-mi-smart-tv-4s',
- 'Xiaomi Mi TV Stick' => 'xiaomi-mi-tv-stick',
- 'Xiaomi Pocophone F1' => 'xiaomi-pocophone-f1',
- 'Xiaomi Redmi 9' => 'xiaomi-redmi-9',
- 'Xiaomi Redmi 9A' => 'xiaomi-redmi-9a',
- 'Xiaomi Redmi AirDots' => 'xiaomi-redmi-airdots',
- 'Xiaomi Redmi Note 4' => 'xiaomi-redmi-note-4',
- 'Xiaomi Redmi Note 5' => 'xiaomi-redmi-note-5',
- 'Xiaomi Redmi Note 8' => 'xiaomi-redmi-note-8',
- 'Xiaomi Redmi Note 8 Pro' => 'xiaomi-redmi-note-8-pro',
- 'Xiaomi Redmi Note 9' => 'xiaomi-redmi-note-9',
- 'Xiaomi Redmi Note 9 Pro' => 'xiaomi-redmi-note-9-pro',
- 'Xiaomi Redmi Note 9S' => 'xiaomi-redmi-note-9s',
- 'Xiaomi Redmi Note 10' => 'xiaomi-redmi-note-10',
- 'Xiaomi Redmi Note 10 Pro' => 'xiaomi-redmi-note-10-pro',
- 'Xiaomi Smart Home' => 'xiaomi-smart-home',
- 'Xiaomi Smartphones' => 'xiaomi-smartphones',
- 'Xiaomi YouPin' => 'xiaomi-youpin',
- 'XMG' => 'xmg',
- 'Yamaha' => 'yamaha',
- 'Yeelight' => 'xiaomi-yeelight',
- 'Yoga' => 'yoga',
- 'Yogamatten' => 'yogamatten',
- 'Yoshi&#039;s Crafted World' => 'yoshis-crafted-world',
- 'Zahnbürsten' => 'zahnbuersten',
- 'Zahnpasta' => 'zahnpasta',
- 'Zahnzusatzversicherung' => 'zahnzusatzversicherung',
- 'Zeitschriften' => 'zeitschriften-magazine',
- 'Zelte' => 'zelte',
- 'Zirkel' => 'zirkel',
- 'Zoo-Tickets' => 'zoo',
- 'Zotac' => 'zotac',
- 'ZTE Smartphones' => 'zte-smartphones',
- 'ZWILLING' => 'zwilling',
- 'ZWILLING Besteck' => 'zwilling-besteck',
- ]
- ],
+ 'type' => 'text',
+ 'exampleValue' => 'dsl',
+ 'title' => 'Gruppenname in der URL: Der einzugebende Gruppenname steht nach "https://www.mydealz.de/gruppe/" und vor einem "?".
+Beispiel: Wenn die URL der Gruppe, die im Browser angezeigt wird, :
+https://www.mydealz.de/gruppe/dsl?sortBy=temp
+Dann geben Sie ein:
+dsl',
+ ],
+ 'subgroups' => [
+ 'name' => 'Kategorie',
+ 'type' => 'text',
+ 'exampleValue' => '293',
+ 'title' => 'Nummer des Kategorie in der URL: Der einzugebende Kategorienummer steht nach "groups=" und vor einem "&".
+Beispiel: Wenn die URL der Gruppe, die im Browser angezeigt wird, :
+https://www.mydealz.de/gruppe/telefon-internet?groups=153%2C154&sortBy=new&time_frame=0
+Dann geben Sie ein:
+153%2C154',
+ ],
'order' => [
'name' => 'sortieren nach',
'type' => 'list',
@@ -2022,6 +94,7 @@ class MydealsBridge extends PepperBridgeAbstract
'uri-group' => 'gruppe/',
'uri-deal' => 'deals/',
'uri-merchant' => 'search/gutscheine?merchant-id=',
+ 'image-host' => 'https://static.mydealz.de/',
'request-error' => 'Could not request mydeals',
'thread-error' => 'Die ID der Diskussion kann nicht ermittelt werden. Überprüfen Sie die eingegebene URL',
'currency' => '€',
@@ -2034,5 +107,7 @@ class MydealsBridge extends PepperBridgeAbstract
'title-talk' => 'Überwachung Diskussion',
'deal-type' => 'Angebotsart',
'localdeal' => 'Lokales Angebot',
+ 'context-hot' => '-hot',
+ 'context-new' => '-new',
];
}
diff --git a/bridges/NordbayernBridge.php b/bridges/NordbayernBridge.php
index 48157921..94319e2b 100644
--- a/bridges/NordbayernBridge.php
+++ b/bridges/NordbayernBridge.php
@@ -163,6 +163,13 @@ class NordbayernBridge extends BridgeAbstract
$item['content'] .= $this->getUseFullContent($content);
}
+ $categories = $article->find('[class=themen]', 0);
+ if ($categories) {
+ $item['categories'] = [];
+ foreach ($categories->find('a') as $category) {
+ $item['categories'][] = $category->innertext;
+ }
+ }
$article->clear();
return $item;
diff --git a/bridges/NurembergerNachrichtenBridge.php b/bridges/NurembergerNachrichtenBridge.php
index 10644212..2c303533 100644
--- a/bridges/NurembergerNachrichtenBridge.php
+++ b/bridges/NurembergerNachrichtenBridge.php
@@ -6,7 +6,7 @@ class NurembergerNachrichtenBridge extends BridgeAbstract
const NAME = 'Nürnberger Nachrichten';
const CACHE_TIMEOUT = 3600;
const URI = 'https://www.nn.de';
- const DESCRIPTION = 'Bridge for Bavarian regional news site nordbayern.de';
+ const DESCRIPTION = 'Bridge for NurembergerNachrichten news site nn.de';
const PARAMETERS = [ [
'region' => [
'name' => 'region',
@@ -66,7 +66,7 @@ class NurembergerNachrichtenBridge extends BridgeAbstract
// exclude nn+ articles if desired
if (
$this->getInput('hideNNPlus') &&
- str_contains($articleContent->find('article[id=article]', 0)->find('header', 0), 'icon-nnplus')
+ $articleContent->find('div[class=paywall]')
) {
continue;
}
diff --git a/bridges/OLXBridge.php b/bridges/OLXBridge.php
index 1a8be901..31f05eaa 100644
--- a/bridges/OLXBridge.php
+++ b/bridges/OLXBridge.php
@@ -103,20 +103,15 @@ EOF;
continue;
}
- $shippingOffered = $post->find('.css-1c0ed4l svg', 0)->outertext ?? false;
- if ($this->getInput('shippingOfferedOnly') && !$shippingOffered) {
- continue;
- }
-
$negotiable = $post->find('p[data-testid="ad-price"] span.css-e2218f', 0)->plaintext ?? false;
if ($negotiable) {
$price = trim(str_replace($negotiable, '', $price));
$negotiable = '(' . $negotiable . ')';
}
- if ($post->find('h6', 0)->plaintext != '') {
+ if ($post->find('h4', 0)->plaintext != '') {
$item['uri'] = $post->find('a', 0)->href;
- $item['title'] = $post->find('h6', 0)->plaintext;
+ $item['title'] = $post->find('h4', 0)->plaintext;
}
# ignore the date component, as it is too convoluted — use the deep-crawled one; see below
@@ -128,6 +123,12 @@ EOF;
# Given that, do deep-crawl *all* the results, which allows to aso obtain the ID, the simplified location
# and date strings, as well as the detailed description.
$articleHTMLContent = getSimpleHTMLDOMCached($item['uri']);
+ $articleHTMLContent = defaultLinkTo($articleHTMLContent, $this->getHostname());
+
+ $shippingOffered = $articleHTMLContent->find('img[alt="Safety Badge"]', 0)->src ?? false;
+ if ($this->getInput('shippingOfferedOnly') && !$shippingOffered) {
+ continue;
+ }
# Extract a clean ID without resorting to the convoluted CSS class or sibling selectors. Should be always present.
$refreshLink = $articleHTMLContent->find('a[data-testid=refresh-link]', 0)->href ?? false;
@@ -195,7 +196,7 @@ EOF;
<tr>
<td>
<p>$location</p>
- <p><span style="font-weight:bold">$price</span> $negotiable <span>$shippingOffered</span></p>
+ <p><span style="font-weight:bold">$price</span> $negotiable <span><img src="$shippingOffered"</img></span></p>
</td>
</tr>
<tr>
diff --git a/bridges/OMonlineBridge.php b/bridges/OMonlineBridge.php
index a434e44e..50e2726f 100644
--- a/bridges/OMonlineBridge.php
+++ b/bridges/OMonlineBridge.php
@@ -25,8 +25,7 @@ class OMonlineBridge extends BridgeAbstract
$url = sprintf('%s', self::URI);
}
- $html = getSimpleHTMLDOM($url)
- or returnServerError('Could not request: ' . $url);
+ $html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, $url);
@@ -35,8 +34,7 @@ class OMonlineBridge extends BridgeAbstract
$articlePath = $a->href;
- $articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT)
- or returnServerError('Could not request: ' . $articlePath);
+ $articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT);
$articlePageHtml = defaultLinkTo($articlePageHtml, self::URI);
diff --git a/bridges/OllamaBridge.php b/bridges/OllamaBridge.php
new file mode 100644
index 00000000..f93e37ce
--- /dev/null
+++ b/bridges/OllamaBridge.php
@@ -0,0 +1,61 @@
+<?php
+
+class OllamaBridge extends BridgeAbstract
+{
+ const MAINTAINER = 'sqrtminusone';
+ const NAME = 'Ollama Blog Bridge';
+ const URI = 'https://ollama.com';
+
+ const CACHE_TIMEOUT = 3600; // 1 hour
+ const DESCRIPTION = 'Returns latest blog posts from Ollama';
+
+ const PARAMETERS = [
+ '' => [
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10
+ ],
+ ]
+ ];
+
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM(self::URI . '/blog/');
+ $limit = $this->getInput('limit');
+
+ $posts = $html->find('main > section > a.group');
+ for ($i = 0; $i < min(count($posts), $limit); $i++) {
+ $post = $posts[$i];
+ $title = $post->find('h2', 0)->plaintext;
+ $date_text = $post->find('h3[datetime]', 0)->getAttribute('datetime');
+ $timestamp = (new DateTime(mb_substr($date_text, 0, 19)))->format('U');
+ $uri = self::URI . $post->getAttribute('href');
+ $this->items[] = [
+ 'uri' => $uri,
+ 'title' => $title,
+ 'timestamp' => $timestamp,
+ 'content' => $this->parsePage($uri),
+ 'uid' => $uri
+ ];
+ }
+ }
+
+ private function parsePage($uri)
+ {
+ $html = getSimpleHTMLDOMCached(
+ $uri,
+ 86400,
+ [],
+ [],
+ true,
+ true,
+ DEFAULT_TARGET_CHARSET,
+ false // Do not strip \n from <code> blocks
+ );
+ $contents = $html->find('main > article > section.prose', 0);
+ $contents = defaultLinkTo($contents, self::URI);
+ return $contents->innertext;
+ }
+}
diff --git a/bridges/PepperBridgeAbstract.php b/bridges/PepperBridgeAbstract.php
index 4e9ab0b5..43dbe829 100644
--- a/bridges/PepperBridgeAbstract.php
+++ b/bridges/PepperBridgeAbstract.php
@@ -62,7 +62,7 @@ class PepperBridgeAbstract extends BridgeAbstract
foreach ($list as $deal) {
// Get the JSON Data stored as vue
$jsonDealData = $this->getDealJsonData($deal);
- $dealMeta = Json::decode($deal->find('div[class=threadGrid-headerMeta]', 0)->find('div[class=js-vue2]', 1)->getAttribute('data-vue2'));
+ $dealMeta = Json::decode($deal->find('div[class=js-vue2]', 1)->getAttribute('data-vue2'));
$item = [];
$item['uri'] = $this->getDealURI($jsonDealData);
@@ -80,7 +80,7 @@ class PepperBridgeAbstract extends BridgeAbstract
. $this->getShipsFrom($dealMeta)
. $this->getShippingCost($jsonDealData)
. $this->getSource($jsonDealData)
- . $this->getDealLocation($dealMeta)
+ . $this->getDealLocation($jsonDealData)
. $deal->find('div[class*=' . $selectorDescription . ']', 0)->innertext
. '</td><td>'
. $this->getTemperature($jsonDealData)
@@ -263,12 +263,41 @@ HEREDOC;
*/
private function getTalkTitle()
{
- $html = getSimpleHTMLDOMCached($this->getInput('url'));
- $title = $html->find('title', 0)->plaintext;
+ $cacheKey = $this->getInput('url') . 'TITLE';
+ $title = $this->loadCacheValue($cacheKey);
+ // The cache does not contain the title of the bridge, we must get it and save it in the cache
+ if ($title === null) {
+ $html = getSimpleHTMLDOMCached($this->getInput('url'));
+ $title = $html->find('title', 0)->plaintext;
+ // Save the value in the cache for the next 15 days
+ $this->saveCacheValue($cacheKey, $title, 86400 * 15);
+ }
return $title;
}
/**
+ * Get the Title from a Group if it exists
+ * @return string String of the Talk title
+ */
+ private function getGroupTitle()
+ {
+ $cacheKey = $this->getInput('group') . 'TITLE';
+ $title = $this->loadCacheValue($cacheKey);
+ // The cache does not contain the title of the bridge, we must get it and save it in the cache
+ if ($title == null) {
+ $html = getSimpleHTMLDOMCached($this->getGroupURI());
+ // Search the title in the javascript mess
+ preg_match('/threadGroupName":"([^"]*)","threadGroupUrlName":"' . $this->getInput('group') . '"/m', $html, $matches);
+ $title = $matches[1];
+ // Save the value in the cache for the next 15 days
+ $this->saveCacheValue($cacheKey, $title, 86400 * 15);
+ }
+
+ $order = $this->getKey('order');
+ return $title . ' - ' . $order;
+ }
+
+ /**
* Get the HTML Title code from an item
* @return string String of the deal title
*/
@@ -373,14 +402,9 @@ HEREDOC;
* Get the Deal location if it exists
* @return string String of the deal location
*/
- private function getDealLocation($dealMeta)
+ private function getDealLocation($jsonDealData)
{
- $ribbons = $dealMeta['props']['metaRibbons'];
- $isLocal = false;
- foreach ($ribbons as $ribbon) {
- $isLocal |= ($ribbon['type'] == 'local');
- }
- if ($isLocal) {
+ if ($jsonDealData['props']['thread']['isLocal']) {
$content = '<div>' . $this->i8n('deal-type') . ' : ' . $this->i8n('localdeal') . '</div>';
} else {
$content = '';
@@ -395,8 +419,11 @@ HEREDOC;
private function getImage($deal)
{
// Get thread Image JSON content
- $content = Json::decode($deal->find('div[class*=threadGrid-image]', 0)->find('div[class=js-vue2]', 0)->getAttribute('data-vue2'));
- return '<img src="' . $content['props']['threadImageUrl'] . '"/>';
+ $content = Json::decode($deal->find('div[class=js-vue2]', 0)->getAttribute('data-vue2'));
+ //return '<img src="' . $content['props']['threadImageUrl'] . '"/>';
+ return '<img src="' . $this->i8n('image-host') . $content['props']['thread']['mainImage']['path'] . '/'
+ . $content['props']['thread']['mainImage']['name'] . '/re/202x202/qt/70/'
+ . $content['props']['thread']['mainImage']['uid'] . '"/>';
}
/**
@@ -405,7 +432,7 @@ HEREDOC;
*/
private function getShipsFrom($dealMeta)
{
- $metas = $dealMeta['props']['metaRibbons'];
+ $metas = $dealMeta['props']['metaRibbons'] ?? [];
$shipsFrom = null;
foreach ($metas as $meta) {
if ($meta['type'] == 'dispatched-from') {
@@ -429,7 +456,7 @@ HEREDOC;
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-keyword') . ' : ' . $this->getInput('q');
break;
case $this->i8n('context-group'):
- return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $this->getKey('group');
+ return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $this->getGroupTitle();
break;
case $this->i8n('context-talk'):
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-talk') . ' : ' . $this->getTalkTitle();
@@ -495,9 +522,17 @@ HEREDOC;
{
$group = $this->getInput('group');
$order = $this->getInput('order');
+ $subgroups = $this->getInput('subgroups');
+
+ // This permit to keep the existing Feed to work
+ if ($order == $this->i8n('context-hot')) {
+ $sortBy = 'temp';
+ } else if ($order == $this->i8n('context-new')) {
+ $sortBy = 'new';
+ }
$url = $this->i8n('bridge-uri')
- . $this->i8n('uri-group') . $group . $order;
+ . $this->i8n('uri-group') . $group . '?sortBy=' . $sortBy . '&groups=' . $subgroups;
return $url;
}
diff --git a/bridges/PriviblurBridge.php b/bridges/PriviblurBridge.php
index 198c38bc..6b442e75 100644
--- a/bridges/PriviblurBridge.php
+++ b/bridges/PriviblurBridge.php
@@ -17,6 +17,7 @@ class PriviblurBridge extends BridgeAbstract
];
private $title;
+ private $favicon = 'https://www.tumblr.com/favicon.ico';
public function collectData()
{
@@ -25,6 +26,11 @@ class PriviblurBridge extends BridgeAbstract
$html = defaultLinkTo($html, $url);
$this->title = $html->find('head title', 0)->innertext;
+ if ($html->find('#blog-header img.avatar', 0)) {
+ $icon = $html->find('#blog-header img.avatar', 0)->src;
+ $this->favicon = str_replace('pnj', 'png', $icon);
+ }
+
$elements = $html->find('.post');
foreach ($elements as $element) {
$item = [];
@@ -64,6 +70,11 @@ class PriviblurBridge extends BridgeAbstract
public function getURI()
{
- return $this->getInput('url') ? $this->getInput('url') : parent::getURI();
+ return $this->getInput('url') ?? parent::getURI();
+ }
+
+ public function getIcon()
+ {
+ return $this->favicon;
}
}
diff --git a/bridges/QwenBlogBridge.php b/bridges/QwenBlogBridge.php
new file mode 100644
index 00000000..2af3f401
--- /dev/null
+++ b/bridges/QwenBlogBridge.php
@@ -0,0 +1,49 @@
+<?php
+
+class QwenBlogBridge extends FeedExpander
+{
+ const NAME = 'Qwen Blog';
+ const URI = 'https://qwenlm.github.io/blog/';
+ const DESCRIPTION = 'Fetch the latest items from Qwen';
+ const MAINTAINER = 'sqrtminusone';
+ const CACHE_TIMEOUT = 3600;
+
+ const PARAMETERS = [
+ '' => [
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10
+ ],
+ ]
+ ];
+
+ public function collectData()
+ {
+ $this->collectExpandableDatas(self::URI . 'index.xml', $this->getInput('limit'));
+ }
+
+ protected function parseItem(array $item)
+ {
+ $dom = getSimpleHTMLDOM($item['uri']);
+ $content = $dom->find('div.post-content', 0);
+ if ($content == null) {
+ return $item;
+ }
+
+ // Fix code blocks
+ foreach ($dom->find('pre.chroma') as $code_block) {
+ // Somehow there are tags in <pre>??
+ $code_block_html = str_get_html($code_block->plaintext);
+ $code = '';
+ foreach ($code_block_html->find('span.line') as $line) {
+ $code .= $line->plaintext . "\n";
+ }
+ $code_block->outertext = '<pre>' . $code . '</pre>';
+ }
+
+ $item['content'] = $content;
+ return $item;
+ }
+}
diff --git a/bridges/RedditBridge.php b/bridges/RedditBridge.php
index 03f279d8..2a3824f4 100644
--- a/bridges/RedditBridge.php
+++ b/bridges/RedditBridge.php
@@ -234,11 +234,14 @@ class RedditBridge extends BridgeAbstract
} elseif ($data->is_video) {
// Video
- // Higher index -> Higher resolution
- end($data->preview->images[0]->resolutions);
- $index = key($data->preview->images[0]->resolutions);
-
- $item['content'] = $this->createFigureLink($data->url, $data->preview->images[0]->resolutions[$index]->url, 'Video');
+ if ($data->media->reddit_video) {
+ $item['content'] = $this->createVideoContent($data->media->reddit_video);
+ } else {
+ // Higher index -> Higher resolution
+ end($data->preview->images[0]->resolutions);
+ $index = key($data->preview->images[0]->resolutions);
+ $item['content'] = $this->createFigureLink($data->url, $data->preview->images[0]->resolutions[$index]->url, 'Video');
+ }
} elseif (isset($data->media) && $data->media->type == 'youtube.com') {
// Youtube link
$item['content'] = $this->createFigureLink($data->url, $data->media->oembed->thumbnail_url, 'YouTube');
@@ -318,6 +321,16 @@ class RedditBridge extends BridgeAbstract
return sprintf('<a href="%s">%s</a>', $href, $text);
}
+ private function createVideoContent(\stdClass $video): string
+ {
+ return <<<HTML
+ <video width="$video->width" height="$video->height" controls>
+ <source src="$video->fallback_url" type="video/mp4">
+ Your browser does not support the video tag.
+ </video>
+ HTML;
+ }
+
public function detectParameters($url)
{
try {
diff --git a/bridges/RumbleBridge.php b/bridges/RumbleBridge.php
index 8d92db3b..c1a565bb 100644
--- a/bridges/RumbleBridge.php
+++ b/bridges/RumbleBridge.php
@@ -60,15 +60,10 @@ class RumbleBridge extends BridgeAbstract
$dom = getSimpleHTMLDOM($url);
foreach ($dom->find('ol.thumbnail__grid div.thumbnail__grid--item') as $video) {
- $itemUrlString = self::URI . $video->find('a', 0)->href;
- $itemUrl = Url::fromString($itemUrlString);
+ $href = $video->find('a', 0)->href;
$item = [
'title' => $video->find('h3', 0)->plaintext,
-
- // Remove tracking parameter in query string
- 'uri' => $itemUrl->withQueryString(null)->__toString(),
-
'author' => $account . '@rumble.com',
'content' => defaultLinkTo($video, self::URI)->innertext,
];
@@ -78,6 +73,12 @@ class RumbleBridge extends BridgeAbstract
$publishedAt = new \DateTimeImmutable($time->getAttribute('datetime'));
$item['timestamp'] = $publishedAt->getTimestamp();
}
+
+ $href = ltrim($href, '/');
+ $itemUrl = Url::fromString(self::URI . $href);
+ // Remove tracking parameter in query string
+ $item['uri'] = $itemUrl->withQueryString(null)->__toString();
+
$this->items[] = $item;
}
}
diff --git a/bridges/RutubeBridge.php b/bridges/RutubeBridge.php
index 6e48c27a..ba8ddde4 100644
--- a/bridges/RutubeBridge.php
+++ b/bridges/RutubeBridge.php
@@ -132,7 +132,7 @@ class RutubeBridge extends BridgeAbstract
$video->description . ' '
)
);
- $item['timestamp'] = $video->created_ts;
+ $item['timestamp'] = $video->publication_ts;
$item['author'] = $video->author->name;
$item['content'] = $content;
diff --git a/bridges/SchweinfurtBuergerinformationenBridge.php b/bridges/SchweinfurtBuergerinformationenBridge.php
index d1f5db15..c41f9783 100644
--- a/bridges/SchweinfurtBuergerinformationenBridge.php
+++ b/bridges/SchweinfurtBuergerinformationenBridge.php
@@ -49,8 +49,7 @@ class SchweinfurtBuergerinformationenBridge extends BridgeAbstract
private function getArticleIDsFromPage($page)
{
$url = sprintf(self::URI . '?art_pager=%d', $page);
- $html = getSimpleHTMLDOMCached($url, self::INDEX_CACHE_TIMEOUT)
- or returnServerError('Could not retrieve ' . $url);
+ $html = getSimpleHTMLDOMCached($url, self::INDEX_CACHE_TIMEOUT);
$articles = $html->find('div.artikel-uebersicht');
$articleIDs = [];
@@ -70,8 +69,7 @@ class SchweinfurtBuergerinformationenBridge extends BridgeAbstract
private function generateItemFromArticle($id)
{
$url = sprintf(self::ARTICLE_URI, $id);
- $html = getSimpleHTMLDOMCached($url, self::ARTICLE_CACHE_TIMEOUT)
- or returnServerError('Could not retrieve ' . $url);
+ $html = getSimpleHTMLDOMCached($url, self::ARTICLE_CACHE_TIMEOUT);
$div = $html->find('div#artikel-detail', 0);
$divContent = $div->find('.c-content', 0);
diff --git a/bridges/ScribbleHubBridge.php b/bridges/ScribbleHubBridge.php
index b4f7beaa..3578a77c 100644
--- a/bridges/ScribbleHubBridge.php
+++ b/bridges/ScribbleHubBridge.php
@@ -54,44 +54,72 @@ class ScribbleHubBridge extends FeedExpander
$this->collectExpandableDatas($url);
}
- protected $author = '';
-
private function collectList($url)
{
$html = getSimpleHTMLDOMCached($url);
foreach ($html->find('.search_main_box') as $element) {
$item = [];
+ $item['author'] = $element->find('[title="Author"]', 0)->plaintext;
+ $item['enclosures'] = [$element->find('.search_img img', 0)->src];
$title = $element->find('.search_title a', 0);
$item['title'] = $title->plaintext;
$item['uri'] = $title->href;
+ $item['uid'] = $item['uri'];
$strdate = $element->find('[title="Last Updated"]', 0)->plaintext;
$item['timestamp'] = strtotime($strdate);
- $item['uid'] = $item['uri'];
- $details = getSimpleHTMLDOMCached($item['uri']);
- $item['enclosures'][] = $details->find('.fic_image img', 0)->src;
- $item['content'] = $details->find('.wi_fic_desc', 0);
-
- foreach ($details->find('.fic_genre') as $tag) {
+ foreach ($element->find('.fic_genre') as $tag) {
$item['categories'][] = $tag->plaintext;
}
+
+ // Get minimal description in case further requests fail
+ $item['content'] = str_get_html($element->find('.search_body', 0));
+ foreach ($item['content']->firstChild()->children() as $child) {
+ $child->remove();
+ }
+
+ try {
+ $details = getSimpleHTMLDOMCached($item['uri']);
+ } catch (HttpException $e) {
+ // 403 Forbidden, This means we got anti-bot response
+ if ($e->getCode() === 403 || $e->getCode() === 429) {
+ $this->items[] = $item;
+ continue;
+ }
+ throw $e;
+ }
+ $item['enclosures'] = [$details->find('.fic_image img', 0)->src];
+ $item['content'] = $details->find('.wi_fic_desc', 0);
+
foreach ($details->find('.stag') as $tag) {
$item['categories'][] = $tag->plaintext;
}
$read_url = $details->find('.read_buttons a', 0)->href;
- $read_html = getSimpleHTMLDOMCached($read_url);
- $item['content'] .= '<hr><h3>';
+ $item['comments'] = $read_url . '#comments';
+ try {
+ $read_html = getSimpleHTMLDOMCached($read_url);
+ } catch (HttpException $e) {
+ // 403 Forbidden, This means we got anti-bot response
+ if ($e->getCode() === 403 || $e->getCode() === 429) {
+ $this->items[] = $item;
+ continue;
+ }
+ throw $e;
+ }
+ $item['content'] .= "<hr><h3><a href=\"$read_url\">";
$item['content'] .= $read_html->find('.chapter-title', 0);
- $item['content'] .= '</h3>';
+ $item['content'] .= '</a></h3>';
$item['content'] .= $read_html->find('#chp_raw', 0);
$this->items[] = $item;
}
}
+ protected $author = '';
+
protected function parseItem(array $item)
{
//For series, filter out other series from 'All' feed
diff --git a/bridges/ShadertoyBridge.php b/bridges/ShadertoyBridge.php
new file mode 100644
index 00000000..9240027e
--- /dev/null
+++ b/bridges/ShadertoyBridge.php
@@ -0,0 +1,100 @@
+<?php
+
+class ShadertoyBridge extends BridgeAbstract
+{
+ const NAME = 'Shadertoy';
+ const URI = 'https://www.shadertoy.com';
+ const DESCRIPTION = 'Latest submissions on Shadertoy';
+ const MAINTAINER = 'thefranke';
+ const CACHE_TIMEOUT = 3600; // 1h
+ const PARAMETERS = [
+ [
+ 'category' => [
+ 'name' => 'category',
+ 'type' => 'list',
+ 'exampleValue' => 'Popular',
+ 'title' => 'Select a category',
+ 'values' => [
+ 'Shaders of the Week' => 'sotw',
+ 'Popular' => 'popular',
+ 'Newest' => 'newest',
+ 'Hot' => 'hot',
+ ]
+ ]
+ ]
+ ];
+
+ public function postprocessDescription($content)
+ {
+ // replace [url] tags
+ $pattern = '/\[\/?url.*?\]/';
+ $replace = '';
+ $content = preg_replace($pattern, $replace, $content);
+
+ // find URLs and turn then into hyperlinks
+ $pattern = '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/';
+ $replace = '<a href="$0">$0</a>';
+ $content = preg_replace($pattern, $replace, $content);
+
+ return $content;
+ }
+
+ public function collectData()
+ {
+ $category = $this->getInput('category');
+ $json = null;
+
+ if ($category == 'sotw') {
+ $url = static::URI . '/playlist/week';
+ $contents = getContents($url);
+ $shaderids = extractFromDelimiters($contents, 'var gShaderIDs = ', ';');
+ $shaderids = str_replace('\'', '"', $shaderids);
+
+ $url = static::URI . '/shadertoy';
+ $data = 's=' . rawurlencode('{ "shaders": ' . $shaderids . ' }') . '&nt=0&nl=0&np=0';
+ $header = [
+ 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0',
+ 'Content-Type: application/x-www-form-urlencoded',
+ 'Accept: */*',
+ 'Origin: https://www.shadertoy.com',
+ 'Referer: https://www.shadertoy.com/playlist/week',
+ ];
+
+ $opts = [
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => $data,
+ CURLOPT_RETURNTRANSFER => true
+ ];
+ $json = getContents($url, $header, $opts);
+ } else {
+ $url = static::URI . '/results?sort=' . $category;
+ $contents = getContents($url);
+ $json = extractFromDelimiters($contents, 'var gShaders=', 'var gUseScreenshots');
+ $json = substr(trim($json), 0, -1);
+ }
+
+ $json = Json::decode($json);
+
+ if (!$json) {
+ throw new Exception(sprintf('Unable to find css selector on `%s`', static::URI));
+ }
+
+ foreach ($json as $article) {
+ $id = $article['info']['id'];
+
+ $title = $article['info']['name'];
+ $author = $article['info']['username'];
+ $uri = static::URI . '/view/' . $id;
+ $content = '<p><img src="' . static::URI . '/media/shaders/' . $id . '.jpg"></p><p>' . $this->postprocessDescription($article['info']['description']) . '</p>';
+ $timestamp = $article['info']['date'];
+
+ $this->items[] = [
+ 'title' => $title,
+ 'author' => $author,
+ 'uri' => $uri,
+ 'content' => $content,
+ 'timestamp' => $timestamp,
+ ];
+ }
+ }
+}
diff --git a/bridges/SkimfeedBridge.php b/bridges/SkimfeedBridge.php
index a224cdf4..bad1af15 100644
--- a/bridges/SkimfeedBridge.php
+++ b/bridges/SkimfeedBridge.php
@@ -633,8 +633,7 @@ class SkimfeedBridge extends BridgeAbstract
$author = '<a href="' . $anchor->href . '">' . trim($anchor->plaintext) . '</a>';
$uri = $anchor->href;
- $box_html = getSimpleHTMLDOM($uri)
- or returnServerError('Could not load custom feed!');
+ $box_html = getSimpleHTMLDOM($uri);
$this->extractFeed($box_html, $author);
}
@@ -665,8 +664,7 @@ class SkimfeedBridge extends BridgeAbstract
*/
private function exportBoxChannels()
{
- $html = getSimpleHTMLDOMCached(static::URI)
- or returnServerError('No contents received from Skimfeed!');
+ $html = getSimpleHTMLDOMCached(static::URI);
if (!$this->isCompatible($html)) {
returnServerError('Skimfeed version is not compatible!');
@@ -722,8 +720,7 @@ EOD;
*/
private function exportTechChannels()
{
- $html = getSimpleHTMLDOMCached(static::URI)
- or returnServerError('No contents received from Skimfeed!');
+ $html = getSimpleHTMLDOMCached(static::URI);
if (!$this->isCompatible($html)) {
returnServerError('Skimfeed version is not compatible!');
@@ -759,8 +756,7 @@ EOD;
$message .= "\t\t'{$title}' => array(\n";
- $channel_html = getSimpleHTMLDOMCached(static::URI . $uri)
- or returnServerError('Could not load tech channel ' . $channel->plaintext . '!');
+ $channel_html = getSimpleHTMLDOMCached(static::URI . $uri);
$boxes = $channel_html->find('#boxx .boxes')
or returnServerError('Could not find boxes!');
diff --git a/bridges/StanfordSIRbookreviewBridge.php b/bridges/StanfordSIRbookreviewBridge.php
index b60f8fc1..f15b6cfb 100644
--- a/bridges/StanfordSIRbookreviewBridge.php
+++ b/bridges/StanfordSIRbookreviewBridge.php
@@ -30,8 +30,7 @@ class StanfordSIRbookreviewBridge extends BridgeAbstract
break;
}
- $html = getSimpleHTMLDOM($url)
- or returnServerError('Failed loading content!');
+ $html = getSimpleHTMLDOM($url);
foreach ($html->find('article') as $element) {
$item = [];
$item['title'] = $element->find('div > h4 > a', 0)->plaintext;
diff --git a/bridges/StockFilingsBridge.php b/bridges/StockFilingsBridge.php
index 2817dd97..0b6a06a4 100644
--- a/bridges/StockFilingsBridge.php
+++ b/bridges/StockFilingsBridge.php
@@ -65,7 +65,7 @@ class StockFilingsBridge extends FeedExpander
{
$uri = $this->getSearchUrl();
- return getSimpleHTMLDOM($uri) ?: returnServerError('Could not request SEC.');
+ return getSimpleHTMLDOM($uri);
}
/**
diff --git a/bridges/StorytelBridge.php b/bridges/StorytelBridge.php
new file mode 100644
index 00000000..a33b3e4a
--- /dev/null
+++ b/bridges/StorytelBridge.php
@@ -0,0 +1,55 @@
+<?php
+
+class StorytelBridge extends BridgeAbstract
+{
+ const NAME = 'Storytel List Bridge';
+ const URI = 'https://www.storytel.com/tr';
+ const DESCRIPTION = 'Fetches books from a Storytel list, including title, author, and cover image.';
+ const MAINTAINER = 'Okbaydere';
+ const PARAMETERS = [
+ 'List' => [
+ 'url' => [
+ 'name' => 'Storytel List URL',
+ 'required' => true,
+ 'exampleValue' => 'https://www.storytel.com/tr/lists/23d09e0bd8fe4d998d1832ddbfa18166',
+ ],
+ ],
+ ];
+
+ public function collectData()
+ {
+ $url = $this->getInput('url');
+
+ if (!preg_match('/^https:\/\/www\.storytel\.com/', $url)) {
+ returnServerError('Invalid URL: Only Storytel URLs are allowed.');
+ }
+
+ $html = getSimpleHTMLDOM($url);
+
+ foreach ($html->find('li.sc-4615116a-1') as $element) {
+ $item = [];
+
+ $titleElement = $element->find('span.sc-b1963858-0.hoTsmF', 0);
+ $item['title'] = $titleElement ? $titleElement->plaintext : 'No title';
+
+ $authorElement = $element->find('span.sc-b1963858-0.ghYMwH', 0);
+ $item['author'] = $authorElement ? $authorElement->plaintext : 'Unknown author';
+
+ $imgElement = $element->find('img.sc-da400893-5', 0);
+ $coverUrl = $imgElement ? $imgElement->getAttribute('srcset') : '';
+ if ($coverUrl) {
+ $coverUrls = explode(', ', $coverUrl);
+ $bestCoverUrl = trim(end($coverUrls));
+ $item['content'] = '<img src="' . preg_replace('/\?.*/', '', $bestCoverUrl) . '"/>';
+ }
+
+ $linkElement = $element->find('a', 0);
+ $item['uri'] = $linkElement ? 'https://www.storytel.com' . $linkElement->getAttribute('href') : $url;
+
+ $item['content'] .= '<p>Author: ' . $item['author'] . '</p>';
+ $item['content'] .= '<p><a href="' . $item['uri'] . '">More details</a></p>';
+
+ $this->items[] = $item;
+ }
+ }
+}
diff --git a/bridges/TapasBridge.php b/bridges/TapasBridge.php
index 8009e64d..1dd8d009 100644
--- a/bridges/TapasBridge.php
+++ b/bridges/TapasBridge.php
@@ -36,7 +36,7 @@ class TapasBridge extends FeedExpander
$this->id = $this->getInput('title');
}
if ($this->getInput('force_title') || !$this->id) {
- $html = getSimpleHTMLDOM($this->getURI()) or returnServerError('Could not request ' . $this->getURI());
+ $html = getSimpleHTMLDOM($this->getURI());
$this->id = $html->find('meta[property$=":url"]', 0)->content;
$this->id = str_ireplace(['tapastic://series/', '/info'], '', $this->id);
}
diff --git a/bridges/TelegramBridge.php b/bridges/TelegramBridge.php
index 81c5aeb9..1f82c606 100644
--- a/bridges/TelegramBridge.php
+++ b/bridges/TelegramBridge.php
@@ -15,6 +15,14 @@ class TelegramBridge extends BridgeAbstract
]
]
];
+
+ const CONFIGURATION = [
+ 'max_pages' => [
+ 'required' => false,
+ 'defaultValue' => 1,
+ ],
+ ];
+
const TEST_DETECT_PARAMETERS = [
'https://t.me/s/rssbridge' => ['username' => 'rssbridge'],
'https://t.me/rssbridge' => ['username' => 'rssbridge'],
@@ -26,7 +34,7 @@ class TelegramBridge extends BridgeAbstract
'https://rssbridge.t.me/' => ['username' => 'rssbridge'],
];
- const CACHE_TIMEOUT = 60 * 15; // 15 mins
+ const CACHE_TIMEOUT = 60 * 60; // 1h
private $feedName = '';
private $enclosures = [];
@@ -36,33 +44,56 @@ class TelegramBridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM($this->getURI());
-
- $channelTitle = $html->find('div.tgme_channel_info_header_title span', 0)->plaintext ?? '';
- $channelTitle = htmlspecialchars_decode($channelTitle, ENT_QUOTES);
- $this->feedName = $channelTitle . ' (@' . $this->normalizeUsername() . ')';
- $posts = $html->find('div.tgme_widget_message_wrap.js-widget_message_wrap');
- if (!$channelTitle && !$posts) {
- throw new \Exception('Unable to find channel. The channel is non-existing or non-public.');
- }
- foreach ($posts as $messageDiv) {
- $this->itemTitle = '';
- $this->enclosures = [];
- $item = [];
-
- $item['uri'] = $messageDiv->find('a.tgme_widget_message_date', 0)->href;
- $item['content'] = $this->processContent($messageDiv);
- $item['title'] = $this->itemTitle;
- $item['timestamp'] = $messageDiv->find('span.tgme_widget_message_meta', 0)->find('time', 0)->datetime;
- $item['enclosures'] = $this->enclosures;
-
- $messageOwner = $messageDiv->find('a.tgme_widget_message_owner_name', 0);
- if ($messageOwner) {
- $item['author'] = html_entity_decode(trim($messageOwner->plaintext), ENT_QUOTES);
+ $pages = 0;
+ $url = 'https://t.me/s/' . $this->normalizeUsername();
+
+ $max_pages = $this->getOption('max_pages');
+
+ // Hard-coded upper bound of 100 loops
+ while ($pages < $max_pages && $pages < 100) {
+ $pages++;
+
+ $dom = getSimpleHTMLDOM($url);
+
+ $channelTitle = $dom->find('div.tgme_channel_info_header_title span', 0)->plaintext ?? '';
+ $channelTitle = htmlspecialchars_decode($channelTitle, ENT_QUOTES);
+ $this->feedName = $channelTitle . ' (@' . $this->normalizeUsername() . ')';
+
+ $messages = $dom->find('div.tgme_widget_message_wrap.js-widget_message_wrap');
+ if (!$channelTitle && !$messages) {
+ throw new \Exception('Unable to find channel. The channel is non-existing or non-public.');
}
- $this->items[] = $item;
+ foreach (array_reverse($messages) as $message) {
+ $this->itemTitle = '';
+ $this->enclosures = [];
+
+ $item = [];
+
+ $item['uri'] = $message->find('a.tgme_widget_message_date', 0)->href;
+ $item['content'] = $this->processContent($message);
+ $item['title'] = $this->itemTitle;
+ $item['timestamp'] = $message->find('span.tgme_widget_message_meta', 0)->find('time', 0)->datetime;
+ $item['enclosures'] = $this->enclosures;
+
+ $messageOwner = $message->find('a.tgme_widget_message_owner_name', 0);
+ if ($messageOwner) {
+ $item['author'] = html_entity_decode(trim($messageOwner->plaintext), ENT_QUOTES);
+ }
+
+ array_unshift($this->items, $item);
+ }
+
+ $more = $dom->find('> div.tgme_widget_message_centered.js-messages_more_wrap a', 0);
+ if ($more && str_contains($more->href, 'before')) {
+ $url = 'https://t.me/' . $more->href;
+ } else {
+ break;
+ }
}
+
+ $this->logger->info(sprintf('Fetched %s messages from %s pages (%s)', count($this->items), $pages, $url));
+
$this->items = array_reverse($this->items);
}
@@ -369,12 +400,7 @@ EOD;
private function normalizeUsername()
{
- // todo: can be replaced with ltrim($username, '@');
- $username = $this->getInput('username');
- if (substr($username, 0, 1) === '@') {
- return substr($username, 1);
- }
- return $username;
+ return ltrim($this->getInput('username'), '@');
}
public function detectParameters($url)
diff --git a/bridges/TestFaktaBridge.php b/bridges/TestFaktaBridge.php
index b9a65138..e9ab263a 100644
--- a/bridges/TestFaktaBridge.php
+++ b/bridges/TestFaktaBridge.php
@@ -56,8 +56,7 @@ class TestFaktaBridge extends BridgeAbstract
public function collectData()
{
$NEWSURL = self::URI . '/sv';
- $html = getSimpleHTMLDOMCached($NEWSURL, 18000) or
- returnServerError('Could not request: ' . $NEWSURL);
+ $html = getSimpleHTMLDOMCached($NEWSURL, 18000);
foreach ($html->find('.row-container') as $element) {
// Debug::log($element);
@@ -68,8 +67,7 @@ class TestFaktaBridge extends BridgeAbstract
$figure = $element->find('img', 0);
$preamble = trim($element->find('.text', 0)->plaintext);
- $article_html = getSimpleHTMLDOMCached($url, 18000) or
- returnServerError('Could not request: ' . $url);
+ $article_html = getSimpleHTMLDOMCached($url, 18000);
$article_content = $article_html->find('div.content', 0);
$article_text = $article_html->find('article', 0);
diff --git a/bridges/TikTokBridge.php b/bridges/TikTokBridge.php
index 22fdfcef..43a9cb31 100644
--- a/bridges/TikTokBridge.php
+++ b/bridges/TikTokBridge.php
@@ -29,6 +29,7 @@ class TikTokBridge extends BridgeAbstract
$html = getSimpleHTMLDOMCached('https://www.tiktok.com/embed/' . $this->processUsername());
$author = $html->find('span[data-e2e=creator-profile-userInfo-TUXText]', 0)->plaintext ?? self::NAME;
+ $authorProfilePicture = $html->find('img[data-e2e=creator-profile-userInfo-Avatar]', 0)->src ?? '';
$videos = $html->find('div[data-e2e=common-videoList-VideoContainer]');
@@ -44,7 +45,7 @@ class TikTokBridge extends BridgeAbstract
$image = $video->find('video', 0)->poster;
$views = $video->find('div[data-e2e=common-Video-Count]', 0)->plaintext;
- $enclosures = [$image];
+ $enclosures = [$image, $authorProfilePicture];
$item['uri'] = $url;
$item['title'] = 'Video';
diff --git a/bridges/TldrTechBridge.php b/bridges/TldrTechBridge.php
index 6c96dff7..222cd49e 100644
--- a/bridges/TldrTechBridge.php
+++ b/bridges/TldrTechBridge.php
@@ -56,7 +56,8 @@ class TldrTechBridge extends BridgeAbstract
if ($child->tag != 'a') {
continue;
}
- $this->extractItem(Url::fromString(self::URI . $child->href));
+ $itemUrl = Url::fromString(self::URI . ltrim($child->href, '/'));
+ $this->extractItem($itemUrl);
if (count($this->items) >= $limit) {
break;
}
diff --git a/bridges/UsesTechBridge.php b/bridges/UsesTechBridge.php
index 653d83dc..28fc9908 100644
--- a/bridges/UsesTechBridge.php
+++ b/bridges/UsesTechBridge.php
@@ -10,8 +10,7 @@ class UsesTechbridge extends BridgeAbstract
public function collectData()
{
- $html = getSimpleHTMLDOM(self::URI)
- or returnServerError('Could not request: ' . self::URI);
+ $html = getSimpleHTMLDOM(self::URI);
foreach ($html->find('div[class=PersonInner]') as $index => $a) {
$item = []; // Create an empty item
diff --git a/bridges/VkBridge.php b/bridges/VkBridge.php
index 0d62305b..54b2897a 100644
--- a/bridges/VkBridge.php
+++ b/bridges/VkBridge.php
@@ -2,13 +2,13 @@
class VkBridge extends BridgeAbstract
{
- const MAINTAINER = 'em92';
+ // const MAINTAINER = 'em92';
// const MAINTAINER = 'pmaziere';
// const MAINTAINER = 'ahiles3005';
const NAME = 'VK.com';
const URI = 'https://vk.com/';
- const CACHE_TIMEOUT = 300; // 5min
- const DESCRIPTION = 'Working with open pages';
+ const CACHE_TIMEOUT = 3600; // 1h
+ const DESCRIPTION = 'Does not work anymore';
const PARAMETERS = [
[
'u' => [
@@ -65,6 +65,7 @@ class VkBridge extends BridgeAbstract
public function collectData()
{
+ return;
$text_html = $this->getContents();
$text_html = iconv('windows-1251', 'utf-8//ignore', $text_html);
@@ -391,10 +392,13 @@ class VkBridge extends BridgeAbstract
$item['categories'] = $hashtags;
// get post link
- $post_link = $post->find('a.PostHeaderSubtitle__link', 0)->getAttribute('href');
- preg_match('/wall-?\d+_(\d+)/', $post_link, $preg_match_result);
- $item['post_id'] = intval($preg_match_result[1]);
- $item['uri'] = $post_link;
+ $var = $post->find('a.PostHeaderSubtitle__link', 0);
+ if ($var) {
+ $post_link = $var->getAttribute('href');
+ preg_match('/wall-?\d+_(\d+)/', $post_link, $preg_match_result);
+ $item['post_id'] = intval($preg_match_result[1]);
+ $item['uri'] = $post_link;
+ }
$item['timestamp'] = $this->getTime($post);
$item['title'] = $this->getTitle($item['content']);
$item['author'] = $post_author;
@@ -402,7 +406,7 @@ class VkBridge extends BridgeAbstract
// do not append it now
$pinned_post_item = $item;
} else {
- $last_post_id = $item['post_id'];
+ $last_post_id = $item['post_id'] ?? null;
$this->items[] = $item;
}
}
@@ -474,7 +478,10 @@ class VkBridge extends BridgeAbstract
if ($accurateDateElement) {
return $accurateDateElement->getAttribute('time');
} else {
- $strdate = $post->find('time.PostHeaderSubtitle__item', 0)->plaintext;
+ $strdate = $post->find('time.PostHeaderSubtitle__item', 0)->plaintext ?? null;
+ if (!$strdate) {
+ return 0;
+ }
$strdate = preg_replace('/[\x00-\x1F\x7F-\xFF]/', ' ', $strdate);
$date = date_parse($strdate);
diff --git a/bridges/VproTegenlichtBridge.php b/bridges/VproTegenlichtBridge.php
index 2b5e5d20..44afba64 100644
--- a/bridges/VproTegenlichtBridge.php
+++ b/bridges/VproTegenlichtBridge.php
@@ -16,8 +16,7 @@ class VproTegenlichtBridge extends BridgeAbstract
public function collectData()
{
$url = sprintf('https://www.vpro.nl/programmas/tegenlicht/lees/artikelen.html');
- $dom = getSimpleHTMLDOM($url)
- or returnServerError('No contents received!');
+ $dom = getSimpleHTMLDOM($url);
$dom = $dom->find('ul#browsable-news-overview', 0);
$dom = defaultLinkTo($dom, $this->getURI());
foreach ($dom->find('li') as $article) {
diff --git a/bridges/WKYTNewsBridge.php b/bridges/WKYTNewsBridge.php
new file mode 100644
index 00000000..e3b95f00
--- /dev/null
+++ b/bridges/WKYTNewsBridge.php
@@ -0,0 +1,27 @@
+<?php
+
+class WKYTNewsBridge extends BridgeAbstract
+{
+ const NAME = 'WKYT Lexington News';
+ const URI = 'https://www.wkyt.com/news/';
+ const DESCRIPTION = 'Returns the recent articles published on WKYT News (Lexington KY)';
+ const MAINTAINER = 'mattconnell';
+
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM(self::URI);
+ $html = defaultLinkTo($html, self::URI);
+
+ $articles = $html->find('.card-body');
+
+ foreach ($articles as $article) {
+ $item = [];
+ $url = $article->find('.headline a', 0);
+ $item['uri'] = $url->href;
+ $item['title'] = trim($url->plaintext);
+ $item['author'] = $article->find('.author', 0)->plaintext;
+ $item['content'] = $article->find('.deck', 0)->plaintext;
+ $this->items[] = $item;
+ }
+ }
+}
diff --git a/bridges/WikipediaBridge.php b/bridges/WikipediaBridge.php
index 28149d0f..0907db76 100644
--- a/bridges/WikipediaBridge.php
+++ b/bridges/WikipediaBridge.php
@@ -105,10 +105,6 @@ class WikipediaBridge extends BridgeAbstract
// This will automatically send us to the correct main page in any language (try it!)
$html = getSimpleHTMLDOM($this->getURI() . '/wiki');
- if (!$html) {
- returnServerError('Could not load site: ' . $this->getURI() . '!');
- }
-
/*
* Now read content depending on the language (make sure to create one function per language!)
* We build the function name automatically, just make sure you create a private function ending
diff --git a/bridges/WirecutterDealsBridge.php b/bridges/WirecutterDealsBridge.php
new file mode 100644
index 00000000..15e9449e
--- /dev/null
+++ b/bridges/WirecutterDealsBridge.php
@@ -0,0 +1,119 @@
+<?php
+
+class WirecutterDealsBridge extends BridgeAbstract
+{
+ const NAME = 'Wirecutter Deals';
+ const URI = 'https://www.nytimes.com/wirecutter/deals/';
+ const DESCRIPTION = 'Deals from The Wirecutter';
+ const MAINTAINER = 'Vynce';
+ const CACHE_TIMEOUT = 900; // 15 minutes
+
+ public function collectData()
+ {
+ $html = getSimpleHTMLDOM($this->getURI());
+ $json = $html->getElementById('__NEXT_DATA__');
+ $data = json_decode($json->innertext());
+
+ foreach ($data->props->pageProps->specialEvent->eventDeals as $deal) {
+ $item = [];
+ $item['uri'] = "https://www.nytimes.com/wirecutter/deals/#deal-{$deal->id}";
+ $item['title'] = $deal->title;
+ $item['timestamp'] = $deal->date;
+ $item['content'] = $this->generateContent($deal);
+ $item['categories'] = $deal->categories;
+ $item['uid'] = strval($deal->id);
+
+ $this->items[] = $item;
+ }
+ }
+
+ public function getIcon()
+ {
+ return <<<'EOD'
+ data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAD7klEQ
+ VR4AWIgFoReXcVmdnKRuymg9WoAkiUJon2+8BnhiLP17bNtX+Bs27aN0bdtY8y1bZu9m1evIrZH
+ VTW9HVsVuTNTndH5qirz5VuP7ddL3LZdk9y2AmZdk9z2TvaZP8lj3znFY/95ssdxxWPB4GHaRI1
+ Lgq6T2ct/RyAWmExa+ySP7acZMecJlgPPK7cdOdlj/5i9qFcdTG44Ifb5Lk7Pwq5tXnUA8zbZbd
+ s7O/zf8aaCT/U6zmc7r7ESaFZwAd1fvpmuzVkhel4xzeM8K+POZcHnhBbSQ5Vb6fmmffQAC3J5d
+ IkQxDSfk5YNF9O2kQqazQClPC+fF1x4nPTOZcf+epuPemmA9JQZoXr6oCtMN+StSvL/tT+XP9+k
+ lwnywr5HWCVIOFHwZxv3km5iVlE7rWMBd7CdD7OJtUEaoul+lyAn7G8Kkk6c7cXUQrrFCSAz0gH
+ AOqb4HMcaAFDnwuz12I2XbdXL6f2uEP09kE+e0RoaYjMTgC16uaJE7d8ZDKcimTJqITcLmJrtj1
+ Rto6fq99CSoSLqor604CGqkyYqjG26+U5aeogGelWV1oMVW2j1cAn/fnX2Mp7hP7EkY+XK12YG5
+ vMkvK1oHT3dsIfWDpeiSnB6mfnBb5+jgdszOV4WWUwvt7ipm/pwFXztqqxl9HVfNl97s91n+L7a
+ 6jHPHZ7/vtHQWDI5ftITNbL62cZ99MdAXmIOWAbAEn+Dhq6mcsJdJ9b93aUbUkvTAIBjXzRUhBw
+ x2ysiANAldBCU4UfdYazhrtMAICg4AL9zqdFIQLXZGzVVBdxetC4xEGhYCuDdzkDiGq7JzAk04A
+ TyZQ5IvISX4n6lALAOWsbvPwfyTVQB7L+wBiUjcQDxCHYlBQAbuybDrsleriKj9RpklMzhxeYDS
+ YFiVC8HIKiCK2NL6bPemIqMvtSg4WQOd5asT2U4gDIA9NMgjhucIAQAUsJvBYCZGlojmoMkSaiB
+ OpMAgHa/68/G9UAjGL6JAKAJ7IMF3P/G/FXSCviAPjhYw4CAlKF8oWm/sNE4Bwvp3rJNdGlkkQH
+ 2utwVnKrrqIP7+Edr1cc/NqBeZXyAF2/Uy5RdD2IFrTe1Fd/HAEp23zZr34KjtcQB9cpMKrPQWv
+ VxzHc6AgqRan9F+I8H1KuCNOi1Vi+1ULcycBN10ZP1u1Wlt423YdGAdIZ6haPqNB6r3cWFye6RS
+ l6ae0eqyDFYCH1gSDCJFfGjVw1IZ6hXZjTBVnSx23WqZmZAOkO9TlRwHLt855IBfoB6NTjCktnb
+ kHD8zq0OqFcISGi4cQRuRJ1Ldm1tYBfQcJBRUDIQEwiEloquhsaCoKBXznAmx/9MoY9pELa5wwA
+ AAABJRU5ErkJggg==
+ EOD;
+ }
+
+ private function jsonToHtml($node)
+ {
+ if ($node['type'] == 'text') {
+ return htmlspecialchars($node['data'], ENT_QUOTES, 'UTF-8');
+ }
+
+ if ($node['type'] == 'tag') {
+ $html = "<{$node['name']}";
+
+ foreach ($node['attribs'] as $key => $value) {
+ $html .= sprintf(
+ ' %s="%s"',
+ htmlspecialchars($key, ENT_QUOTES, 'UTF-8'),
+ htmlspecialchars($value, ENT_QUOTES, 'UTF-8')
+ );
+ }
+
+ $html .= '>';
+
+ foreach ($node['children'] as $child) {
+ $html .= $this->jsonToHtml($child);
+ }
+
+ $html .= "</{$node['name']}>";
+
+ return $html;
+ }
+
+ return '';
+ }
+
+ private function generateContent($deal)
+ {
+ $img_link = $deal->image->source;
+ $content = "<p><img src=\"https://cdn.thewirecutter.com/{$img_link}?width=314&amp;quality=75&amp;crop=3:2&amp;auto=webp\"></p>";
+
+ $content .= "<p><strong>\${$deal->price}</strong> <del>\${$deal->streetPrice}</del></p>";
+
+ foreach ($deal->buyButtons as $buy) {
+ $content .= "<p>Buy from <a href=\"{$buy->url}\">$buy->merchant</a>";
+ if ($buy->promo->effect) {
+ $content .= " {$buy->promo->effect}";
+ }
+ if ($buy->promo->code) {
+ $content .= " (Use promo code {$buy->promo->code})";
+ }
+ $content .= '</p>';
+ }
+
+ $content .= '<p>&nbsp;</p>';
+ $structuredContent = json_decode($deal->structuredContent, true);
+ foreach ($structuredContent as $node) {
+ $content .= $this->jsonToHtml($node);
+ }
+
+ if ($deal->relatedArticle) {
+ $review = $deal->relatedArticle;
+ $content .= '<p>&nbsp;</p>';
+ $content .= "<p>Read the review: <a href=\"https://www.nytimes.com/wirecutter{$review->link}\">{$review->title}</a></p>";
+ }
+
+ return $content;
+ }
+}
diff --git a/bridges/WorldCosplayBridge.php b/bridges/WorldCosplayBridge.php
deleted file mode 100644
index 721c545f..00000000
--- a/bridges/WorldCosplayBridge.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-class WorldCosplayBridge extends BridgeAbstract
-{
- const NAME = 'WorldCosplay Bridge';
- const URI = 'https://worldcosplay.net/';
- const DESCRIPTION = 'Returns WorldCosplay photos';
- const MAINTAINER = 'AxorPL';
-
- const API_CHARACTER = 'api/photo/list.json?character_id=%u&limit=%u';
- const API_COSPLAYER = 'api/member/photos.json?member_id=%u&limit=%u';
- const API_SERIES = 'api/photo/list.json?title_id=%u&limit=%u';
- const API_TAG = 'api/tag/photo_list.json?id=%u&limit=%u';
-
- const CONTENT_HTML
- = '<a href="%s" target="_blank"><img src="%s" alt="%s" title="%s"></a>';
-
- const ERR_CONTEXT = 'No context provided';
- const ERR_QUERY = 'Unable to query: %s';
-
- const LIMIT_MIN = 1;
- const LIMIT_MAX = 24;
-
- const PARAMETERS = [
- 'Character' => [
- 'cid' => [
- 'name' => 'Character ID',
- 'type' => 'number',
- 'required' => true,
- 'title' => 'WorldCosplay character ID',
- 'exampleValue' => 18204
- ]
- ],
- 'Cosplayer' => [
- 'uid' => [
- 'name' => 'Cosplayer ID',
- 'type' => 'number',
- 'required' => true,
- 'title' => 'Cosplayer\'s WorldCosplay profile ID',
- 'exampleValue' => 406782
- ]
- ],
- 'Series' => [
- 'sid' => [
- 'name' => 'Series ID',
- 'type' => 'number',
- 'required' => true,
- 'title' => 'WorldCosplay series ID',
- 'exampleValue' => 3139
- ]
- ],
- 'Tag' => [
- 'tid' => [
- 'name' => 'Tag ID',
- 'type' => 'number',
- 'required' => true,
- 'title' => 'WorldCosplay tag ID',
- 'exampleValue' => 33643
- ]
- ],
- 'global' => [
- 'limit' => [
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => false,
- 'title' => 'Maximum number of photos to return',
- 'exampleValue' => 5,
- 'defaultValue' => 5
- ]
- ]
- ];
-
- public function collectData()
- {
- $limit = $this->getInput('limit');
- $limit = min(self::LIMIT_MAX, max(self::LIMIT_MIN, $limit));
- switch ($this->queriedContext) {
- case 'Character':
- $id = $this->getInput('cid');
- $url = self::API_CHARACTER;
- break;
- case 'Cosplayer':
- $id = $this->getInput('uid');
- $url = self::API_COSPLAYER;
- break;
- case 'Series':
- $id = $this->getInput('sid');
- $url = self::API_SERIES;
- break;
- case 'Tag':
- $id = $this->getInput('tid');
- $url = self::API_TAG;
- break;
- default:
- returnClientError(self::ERR_CONTEXT);
- }
- $url = self::URI . sprintf($url, $id, $limit);
-
- $json = json_decode(getContents($url));
- if ($json->has_error) {
- returnServerError($json->message);
- }
- $list = $json->list;
-
- foreach ($list as $img) {
- $image = $img->photo ?? $img;
- $item = [
- 'uri' => self::URI . substr($image->url, 1),
- 'title' => $image->subject,
- 'author' => $img->member->global_name,
- 'enclosures' => [$image->large_url],
- 'uid' => $image->id,
- ];
- // Context cosplayer don't have created_at
- if (isset($image->created_at)) {
- $item['timestamp'] = $image->created_at;
- }
- $item['content'] = sprintf(
- self::CONTENT_HTML,
- $item['uri'],
- $item['enclosures'][0],
- $item['title'],
- $item['title']
- );
- $this->items[] = $item;
- }
- }
-
- public function getName()
- {
- switch ($this->queriedContext) {
- case 'Character':
- $id = $this->getInput('cid');
- break;
- case 'Cosplayer':
- $id = $this->getInput('uid');
- break;
- case 'Series':
- $id = $this->getInput('sid');
- break;
- case 'Tag':
- $id = $this->getInput('tid');
- break;
- default:
- return parent::getName();
- }
- return sprintf('%s %u - ', $this->queriedContext, $id) . self::NAME;
- }
-}
diff --git a/bridges/XenForoBridge.php b/bridges/XenForoBridge.php
index d1ecea74..52c63948 100644
--- a/bridges/XenForoBridge.php
+++ b/bridges/XenForoBridge.php
@@ -304,11 +304,9 @@ class XenForoBridge extends BridgeAbstract
// We can optimize performance by caching all but the last page
if ($page != $lastpage) {
- $html = getSimpleHTMLDOMCached($pageurl)
- or returnServerError('Error loading contents from ' . $pageurl . '!');
+ $html = getSimpleHTMLDOMCached($pageurl);
} else {
- $html = getSimpleHTMLDOM($pageurl)
- or returnServerError('Error loading contents from ' . $pageurl . '!');
+ $html = getSimpleHTMLDOM($pageurl);
}
$html = defaultLinkTo($html, $hosturl);
@@ -347,11 +345,9 @@ class XenForoBridge extends BridgeAbstract
// We can optimize performance by caching all but the last page
if ($page != $lastpage) {
- $html = getSimpleHTMLDOMCached($pageurl)
- or returnServerError('Error loading contents from ' . $pageurl . '!');
+ $html = getSimpleHTMLDOMCached($pageurl);
} else {
- $html = getSimpleHTMLDOM($pageurl)
- or returnServerError('Error loading contents from ' . $pageurl . '!');
+ $html = getSimpleHTMLDOM($pageurl);
}
$html = defaultLinkTo($html, $hosturl);
diff --git a/bridges/YouTubeFeedExpanderBridge.php b/bridges/YouTubeFeedExpanderBridge.php
new file mode 100644
index 00000000..953674ec
--- /dev/null
+++ b/bridges/YouTubeFeedExpanderBridge.php
@@ -0,0 +1,86 @@
+<?php
+
+class YouTubeFeedExpanderBridge extends FeedExpander
+{
+ const NAME = 'YouTube Feed Expander';
+ const MAINTAINER = 'phantop';
+ const URI = 'https://www.youtube.com/';
+ const DESCRIPTION = 'Returns the latest videos from a YouTube channel';
+ const PARAMETERS = [[
+ 'channel' => [
+ 'name' => 'Channel ID',
+ 'required' => true,
+ // Example: vinesauce
+ 'exampleValue' => 'UCzORJV8l3FWY4cFO8ot-F2w',
+ ],
+ 'embed' => [
+ 'name' => 'Add embed to entry',
+ 'type' => 'checkbox',
+ 'required' => false,
+ 'title' => 'Add embed to entry',
+ 'defaultValue' => 'checked',
+ ],
+ 'embedurl' => [
+ 'name' => 'Use embed page as entry url',
+ 'type' => 'checkbox',
+ 'required' => false,
+ 'title' => 'Use embed page as entry url',
+ ],
+ 'nocookie' => [
+ 'name' => 'Use nocookie embed page',
+ 'type' => 'checkbox',
+ 'required' => false,
+ 'title' => 'Use nocookie embed page'
+ ],
+ ]];
+
+ public function getIcon()
+ {
+ if ($this->getInput('channel') != null) {
+ $html = getSimpleHTMLDOMCached($this->getURI());
+ $scriptRegex = '/var ytInitialData = (.*?);<\/script>/';
+ $result = preg_match($scriptRegex, $html, $matches);
+ if (isset($matches[1])) {
+ $json = json_decode($matches[1]);
+ return $json->metadata->channelMetadataRenderer->avatar->thumbnails[0]->url;
+ }
+ }
+ return parent::getIcon();
+ }
+
+ public function collectData()
+ {
+ $url = 'https://www.youtube.com/feeds/videos.xml?channel_id=' . $this->getInput('channel');
+ $this->collectExpandableDatas($url);
+ }
+
+ protected function parseItem(array $item)
+ {
+ $id = $item['yt']['videoId'];
+ $item['comments'] = $item['uri'] . '#comments';
+ $item['uid'] = $item['id'];
+
+ $thumbnail = sprintf('https://img.youtube.com/vi/%s/maxresdefault.jpg', $id);
+ $item['enclosures'] = [$thumbnail];
+
+ $item['content'] = $item['media']['group']['description'];
+ $item['content'] = str_replace("\n", '<br>', $item['content']);
+ unset($item['media']);
+
+ $embedURI = self::URI;
+ if ($this->getInput('nocookie')) {
+ $embedURI = 'https://www.youtube-nocookie.com/';
+ }
+ $embed = $embedURI . 'embed/' . $id;
+ if ($this->getInput('embed')) {
+ $iframe_fmt = '<iframe width="448" height="350" src="%s" title="%s" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>'; //phpcs:ignore
+ $iframe = sprintf($iframe_fmt, $embed, $item['title']) . '<br>';
+ $item['content'] = $iframe . $item['content'];
+ }
+ if ($this->getInput('embedurl')) {
+ $item['uri'] = $embed;
+ }
+
+ return $item;
+ }
+}
diff --git a/bridges/YoutubeBridge.php b/bridges/YoutubeBridge.php
index 647b1c42..12cdaec4 100644
--- a/bridges/YoutubeBridge.php
+++ b/bridges/YoutubeBridge.php
@@ -1,12 +1,5 @@
<?php
-/**
-* RssBridgeYoutube
-* Returns the newest videos
-* WARNING: to parse big playlists (over ~90 videos), you need to edit simple_html_dom.php:
-* change: define('MAX_FILE_SIZE', 600000);
-* into: define('MAX_FILE_SIZE', 900000); (or more)
-*/
class YoutubeBridge extends BridgeAbstract
{
const NAME = 'YouTube Bridge';
diff --git a/caches/FileCache.php b/caches/FileCache.php
index dfd295e8..24a9872f 100644
--- a/caches/FileCache.php
+++ b/caches/FileCache.php
@@ -53,11 +53,14 @@ class FileCache implements CacheInterface
'value' => $value,
];
$cacheFile = $this->createCacheFile($key);
- $bytes = file_put_contents($cacheFile, serialize($item), LOCK_EX);
- // todo: Consider tightening the permissions of the created file. It usually allow others to read, depending on umask
+ $bytes = file_put_contents($cacheFile, serialize($item));
+
+ // TODO: Consider tightening the permissions of the created file.
+ // It usually allow others to read, depending on umask
+
if ($bytes === false) {
- // Consider just logging the error here
- throw new \Exception(sprintf('Failed to write to: %s', $cacheFile));
+ // Typically means no disk space remaining
+ $this->logger->warning(sprintf('Failed to write to: %s', $cacheFile));
}
}
diff --git a/config.default.ini.php b/config.default.ini.php
index c23372d9..6d646fd5 100644
--- a/config.default.ini.php
+++ b/config.default.ini.php
@@ -21,20 +21,15 @@
;enabled_bridges[] = ThePirateBay
;enabled_bridges[] = TikTokBridge
;enabled_bridges[] = Twitch
-;enabled_bridges[] = Vk
;enabled_bridges[] = XPathBridge
;enabled_bridges[] = Youtube
;enabled_bridges[] = YouTubeCommunityTabBridge
enabled_bridges[] = *
-; Defines the timezone used by RSS-Bridge
-; Find a list of supported timezones at
-; https://www.php.net/manual/en/timezones.php
-; timezone = "UTC" (default)
timezone = "UTC"
; Display a system message to users.
-message = ""
+;message = "Hello world"
; Whether to enable debug mode.
enable_debug_mode = false
@@ -46,14 +41,18 @@ enable_debug_mode = false
; Whether to enable maintenance mode. If enabled, feed requests receive 503 Service Unavailable
enable_maintenance_mode = false
+; Max file size for simple_html_dom in bytes (10000000 => 10 MB)
+max_file_size = 10000000
+
[http]
+
; Operation timeout in seconds
-timeout = 15
+timeout = 5
; Operation retry count in case of curl error
-retries = 2
+retries = 1
-; User agent
+; Curl user agent
useragent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0"
; Max http response size in MB
@@ -70,12 +69,13 @@ type = "file"
custom_timeout = false
[admin]
+
; Advertise an email address where people can reach the administrator.
; This address is displayed on the main page, visible to everyone!
; "" = Disabled (default)
email = ""
-; Advertise a contact Telegram url e.g. "https://t.me/elegantobjects"
+; Advertise a contact URL (can be any URL!) e.g. "https://t.me/elegantobjects"
telegram = ""
; Show Donation information for bridges if available.
@@ -86,6 +86,7 @@ telegram = ""
donations = true
[proxy]
+
; The HTTP proxy to tunnel requests through
; https://curl.se/libcurl/c/CURLOPT_PROXY.html
; "" = Proxy disabled (default)
@@ -135,6 +136,7 @@ report_limit = 1
; --- Cache specific configuration ---------------------------------------------
[FileCache]
+
; The root folder to store files in.
; "" = Use the cache folder in the repository (default)
path = ""
@@ -142,6 +144,7 @@ path = ""
enable_purge = true
[SQLiteCache]
+
; Filepath of the sqlite db file
file = "cache.sqlite"
; Whether to actually delete data when purging
@@ -150,11 +153,17 @@ enable_purge = true
timeout = 5000
[MemcachedCache]
+
host = "localhost"
port = 11211
; --- Bridge specific configuration ------
+[TelegramBridge]
+
+; Max pages to fetch (1 page => 20 messages), min=1 max=100
+max_pages = 1
+
[DiscogsBridge]
; Sets the personal access token for interactions with Discogs. When
diff --git a/docs/01_General/06_Public_Hosts.md b/docs/01_General/06_Public_Hosts.md
index 7d921d20..10a9871d 100644
--- a/docs/01_General/06_Public_Hosts.md
+++ b/docs/01_General/06_Public_Hosts.md
@@ -3,7 +3,7 @@
| Country | Address | Status | Contact | Comment |
|:-------:|---------|--------|----------|---------|
| ![](https://iplookup.flagfox.net/images/h16/GB.png) | https://rss-bridge.org/bridge01 | ![](https://img.shields.io/website/https/rss-bridge.org/bridge01.svg) | [@dvikan](https://github.com/dvikan) | London, Digital Ocean|
-| ![](https://iplookup.flagfox.net/images/h16/FR.png) | https://rssbridge.flossboxin.org.in | ![](https://img.shields.io/badge/website-up-brightgreen) | [@vdbhb59](https://github.com/vdbhb59) | Hosted with OVH SAS (Maintained in India) |
+| ![](https://iplookup.flagfox.net/images/h16/FR.png) | https://rssbridge.flossboxin.org.in | ![](https://img.shields.io/badge/website-up-brightgreen) | [@vdbhb59](https://github.com/vdbhb59) | Hosted with Netcup Germany (Maintained in India) |
| ![](https://iplookup.flagfox.net/images/h16/FR.png) | https://rss-bridge.cheredeprince.net | ![](https://img.shields.io/website/https/rss-bridge.cheredeprince.net) | [@La_Bécasse](https://cheredeprince.net/contact) | Self-Hosted at home in France |
| ![](https://iplookup.flagfox.net/images/h16/FR.png) | https://rss-bridge.sans-nuage.fr | ![](https://img.shields.io/website/https/rss-bridge.sans-nuage.fr) | [@Alsace Réseau Neutre](https://arn-fai.net/contact) | Hosted in Alsace, France |
| ![](https://iplookup.flagfox.net/images/h16/GB.png) | https://rss-bridge.lewd.tech | ![](https://img.shields.io/website/https/rss-bridge.lewd.tech.svg) | [@Erisa](https://github.com/Erisa) | Hosted in London, protected by Cloudflare Rate Limiting |
diff --git a/docs/10_Bridge_Specific/Telegram.md b/docs/10_Bridge_Specific/Telegram.md
new file mode 100644
index 00000000..528de788
--- /dev/null
+++ b/docs/10_Bridge_Specific/Telegram.md
@@ -0,0 +1,12 @@
+# TelegramBridge
+
+By default, it fetches a single page with up to 20 messages.
+
+To increase this limit, tweak the `max_pages` config:
+
+```ini
+[TelegramBridge]
+
+; Fetch a maximum of 3 pages (requires 3 http requests)
+max_pages = 3
+```
diff --git a/formats/MrssFormat.php b/formats/MrssFormat.php
index f7b11949..4066ec73 100644
--- a/formats/MrssFormat.php
+++ b/formats/MrssFormat.php
@@ -80,14 +80,8 @@ class MrssFormat extends FormatAbstract
$feedUrl = get_current_url();
$linkSelf->setAttribute('href', $feedUrl);
} elseif ($feedKey === 'icon') {
- $allowedIconExtensions = [
- '.gif',
- '.jpg',
- '.png',
- '.ico',
- ];
$icon = $feedValue;
- if ($icon && in_array(substr($icon, -4), $allowedIconExtensions)) {
+ if ($icon) {
$feedImage = $document->createElement('image');
$channel->appendChild($feedImage);
$iconUrl = $document->createElement('url');
diff --git a/lib/BridgeAbstract.php b/lib/BridgeAbstract.php
index 23e90e13..b814097a 100644
--- a/lib/BridgeAbstract.php
+++ b/lib/BridgeAbstract.php
@@ -327,7 +327,7 @@ abstract class BridgeAbstract
return $this->cache->get($this->getShortName() . '_' . $key, $default);
}
- protected function saveCacheValue(string $key, $value, int $ttl = null)
+ protected function saveCacheValue(string $key, $value, int $ttl = 86400)
{
$this->cache->set($this->getShortName() . '_' . $key, $value, $ttl);
}
diff --git a/lib/BridgeCard.php b/lib/BridgeCard.php
index 855ddb93..2f171002 100644
--- a/lib/BridgeCard.php
+++ b/lib/BridgeCard.php
@@ -44,6 +44,10 @@ final class BridgeCard
data-short-name="$shortName"
>
+ <a style="position: absolute; top: 10px; left: 10px" href="#bridge-{$bridgeClassName}">
+ <h1>#</h1>
+ <a>
+
<h2><a href="{$uri}">{$name}</a></h2>
<p class="description">{$description}</p>
diff --git a/lib/Configuration.php b/lib/Configuration.php
index 187848fb..60bf80fb 100644
--- a/lib/Configuration.php
+++ b/lib/Configuration.php
@@ -7,7 +7,7 @@
*/
final class Configuration
{
- private const VERSION = '2024-02-02';
+ private const VERSION = '2025-01-26';
private static $config = [];
diff --git a/lib/FeedParser.php b/lib/FeedParser.php
index 0ad90965..3b133b85 100644
--- a/lib/FeedParser.php
+++ b/lib/FeedParser.php
@@ -174,7 +174,7 @@ final class FeedParser
}
foreach ($namespaces as $namespaceName => $namespaceUrl) {
- if (in_array($namespaceName, ['', 'content', 'media'])) {
+ if (in_array($namespaceName, ['', 'content'])) {
continue;
}
$item[$namespaceName] = $this->parseModule($feedItem, $namespaceName, $namespaceUrl);
@@ -250,11 +250,17 @@ final class FeedParser
private function parseModule(\SimpleXMLElement $element, string $namespaceName, string $namespaceUrl): array
{
+ // Unfortunately this parses out only node values as string
+ // TODO: parse attributes too
+
$result = [];
$module = $element->children($namespaceUrl);
foreach ($module as $name => $value) {
- // todo: add custom parsing if it's something other than a string
- $result[$name] = (string) $value;
+ if (get_class($value) === 'SimpleXMLElement' && $value->count() !== 0) {
+ $result[$name] = $this->parseModule($value, $namespaceName, $namespaceUrl);
+ } else {
+ $result[$name] = (string) $value;
+ }
}
return $result;
}
diff --git a/lib/bootstrap.php b/lib/bootstrap.php
index 36b13e19..8a7c62a1 100644
--- a/lib/bootstrap.php
+++ b/lib/bootstrap.php
@@ -7,10 +7,6 @@ if (is_file(__DIR__ . '/../vendor/autoload.php')) {
const PATH_LIB_CACHES = __DIR__ . '/../caches/';
const PATH_CACHE = __DIR__ . '/../cache/';
-// Allow larger files for simple_html_dom
-// todo: extract to config (if possible)
-const MAX_FILE_SIZE = 10000000;
-
// Files
$files = [
__DIR__ . '/../lib/html.php',
diff --git a/lib/contents.php b/lib/contents.php
index 56a3db20..b4d70817 100644
--- a/lib/contents.php
+++ b/lib/contents.php
@@ -24,6 +24,13 @@ function getContents(
// TODO: consider url validation at this point
+ $config = [
+ 'useragent' => Configuration::getConfig('http', 'useragent'),
+ 'timeout' => Configuration::getConfig('http', 'timeout'),
+ 'retries' => Configuration::getConfig('http', 'retries'),
+ 'curl_options' => $curlOptions,
+ ];
+
$httpHeadersNormalized = [];
foreach ($httpHeaders as $httpHeader) {
$parts = explode(':', $httpHeader);
@@ -69,13 +76,7 @@ function getContents(
'TE' => 'trailers',
];
- $config = [
- 'useragent' => Configuration::getConfig('http', 'useragent'),
- 'timeout' => Configuration::getConfig('http', 'timeout'),
- 'retries' => Configuration::getConfig('http', 'retries'),
- 'headers' => array_merge($defaultHttpHeaders, $httpHeadersNormalized),
- 'curl_options' => $curlOptions,
- ];
+ $config['headers'] = array_merge($defaultHttpHeaders, $httpHeadersNormalized);
$maxFileSize = Configuration::getConfig('http', 'max_filesize');
if ($maxFileSize) {
@@ -176,11 +177,9 @@ function getSimpleHTMLDOM(
}
/**
- * Gets contents from the Internet as simplhtmldom object. Contents are cached
+ * Fetch contents from the Internet as simplhtmldom object. Contents are cached
* and re-used for subsequent calls until the cache duration elapsed.
*
- * _Notice_: Cached contents are forcefully removed after 24 hours (86400 seconds).
- *
* @param string $url The URL.
* @param int $ttl Cache duration in seconds.
* @param array $header (optional) A list of cURL header.
diff --git a/lib/http.php b/lib/http.php
index d1043b33..15d6ebec 100644
--- a/lib/http.php
+++ b/lib/http.php
@@ -220,7 +220,7 @@ final class Request
return $clone;
}
- public function attribute(string $key, $default = null)
+ public function getAttribute(string $key, $default = null)
{
return $this->attributes[$key] ?? $default;
}
diff --git a/lib/logger.php b/lib/logger.php
index 74a0e713..9e5f6ce9 100644
--- a/lib/logger.php
+++ b/lib/logger.php
@@ -136,7 +136,7 @@ final class StreamHandler
$record['message'],
$context
);
- $bytes = file_put_contents($this->stream, $text, FILE_APPEND | LOCK_EX);
+ $bytes = file_put_contents($this->stream, $text, FILE_APPEND);
}
}
diff --git a/lib/simplehtmldom/simple_html_dom.php b/lib/simplehtmldom/simple_html_dom.php
index 170f6fb0..e8b3a727 100644
--- a/lib/simplehtmldom/simple_html_dom.php
+++ b/lib/simplehtmldom/simple_html_dom.php
@@ -114,8 +114,9 @@ function str_get_html(
if (empty($str)) {
throw new \Exception('Refusing to parse empty string input');
}
- if (strlen($str) > MAX_FILE_SIZE) {
- throw new \Exception('Refusing to parse too big input');
+
+ if (strlen($str) > Configuration::getConfig('system', 'max_file_size')) {
+ throw new \Exception('simple_html_dom: Refusing to parse too big input: ' . strlen($str));
}
return $dom->load($str, $lowercase, $stripRN);
diff --git a/lib/url.php b/lib/url.php
index 993fef96..9a1b59ad 100644
--- a/lib/url.php
+++ b/lib/url.php
@@ -111,6 +111,9 @@ final class Url
if (!str_starts_with($path, '/')) {
throw new UrlException(sprintf('Path must start with forward slash: %s', $path));
}
+ if (str_starts_with($path, '//')) {
+ throw new UrlException(sprintf('Illegal path (too many forward slashes): %s', $path));
+ }
$clone = clone $this;
$clone->path = $path;
return $clone;
diff --git a/middlewares/CacheMiddleware.php b/middlewares/CacheMiddleware.php
index bffde4af..b8a34754 100644
--- a/middlewares/CacheMiddleware.php
+++ b/middlewares/CacheMiddleware.php
@@ -13,7 +13,7 @@ class CacheMiddleware implements Middleware
public function __invoke(Request $request, $next): Response
{
- $action = $request->attribute('action');
+ $action = $request->getAttribute('action');
if ($action !== 'DisplayAction') {
// We only cache DisplayAction (for now)
@@ -43,9 +43,14 @@ class CacheMiddleware implements Middleware
/** @var Response $response */
$response = $next($request);
- if (in_array($response->getCode(), [403, 429, 500, 503])) {
+ if ($response->getCode() === 200) {
+ // Do nothing because DisplayAction has already cached this on $cacheKey
+ } elseif (in_array($response->getCode(), [400, 403, 404, 429, 500, 503])) {
// Cache these responses for about ~10 mins on average
$this->cache->set($cacheKey, $response, 60 * 5 + rand(1, 60 * 10));
+ } else {
+ // Should never happen
+ $this->cache->set($cacheKey, $response, 60 * 5);
}
// For 1% of requests, prune cache
diff --git a/middlewares/TokenAuthenticationMiddleware.php b/middlewares/TokenAuthenticationMiddleware.php
index f8234629..31544ab7 100644
--- a/middlewares/TokenAuthenticationMiddleware.php
+++ b/middlewares/TokenAuthenticationMiddleware.php
@@ -10,20 +10,24 @@ class TokenAuthenticationMiddleware implements Middleware
return $next($request);
}
- // Always add token to request attribute
- $request = $request->withAttribute('token', $request->get('token'));
+ $token = $request->get('token');
- if (! $request->attribute('token')) {
+ if (! $token) {
return new Response(render(__DIR__ . '/../templates/token.html.php', [
- 'message' => 'Missing token',
+ 'message' => 'Missing token',
+ 'token' => '',
]), 401);
}
- if (! hash_equals(Configuration::getConfig('authentication', 'token'), $request->attribute('token'))) {
+
+ if (! hash_equals(Configuration::getConfig('authentication', 'token'), $token)) {
return new Response(render(__DIR__ . '/../templates/token.html.php', [
- 'message' => 'Invalid token',
+ 'message' => 'Invalid token',
+ 'token' => $token,
]), 401);
}
+ $request = $request->withAttribute('token', $token);
+
return $next($request);
}
}
diff --git a/static/style.css b/static/style.css
index 4e6b1b2d..4c831534 100644
--- a/static/style.css
+++ b/static/style.css
@@ -186,6 +186,7 @@ section li {
margin-left: 1em;
}
.bridge-card {
+ position: relative;
text-align: center;
}
diff --git a/templates/frontpage.html.php b/templates/frontpage.html.php
index c1182673..f285e8d6 100644
--- a/templates/frontpage.html.php
+++ b/templates/frontpage.html.php
@@ -52,7 +52,7 @@
<?php if ($admin_telegram): ?>
<div>
- Telegram: <a href="<?= e($admin_telegram) ?>"><?= e($admin_telegram) ?></a>
+ Url: <a href="<?= e($admin_telegram) ?>"><?= e($admin_telegram) ?></a>
</div>
<?php endif; ?>
diff --git a/templates/token.html.php b/templates/token.html.php
index 1a036dbb..43c60972 100644
--- a/templates/token.html.php
+++ b/templates/token.html.php
@@ -13,8 +13,8 @@
<?= e($message) ?>
</p>
-<form action="" method="get">
+<form action="" method="get" autocomplete="off">
<label for="token">Token:</label>
- <input type="password" name="token" id="token" placeholder="token">
+ <input type="text" name="token" id="token" placeholder="token" value="<?= e($token) ?>">
<input type="submit" value="OK">
</form>
diff --git a/tests/FeedParserTest.php b/tests/FeedParserTest.php
index 45dc1234..458bdb53 100644
--- a/tests/FeedParserTest.php
+++ b/tests/FeedParserTest.php
@@ -183,4 +183,83 @@ class FeedParserTest extends TestCase
];
$this->assertEquals($expected, $feed);
}
+
+ public function testYoutubeMediaModule()
+ {
+ $xml = <<<XML
+ <?xml version="1.0" encoding="UTF-8"?>
+ <feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
+ <link rel="self" href="http://www.youtube.com/feeds/videos.xml?channel_id=UCuCkxoKLYO_EQ2GeFtbM_bw"/>
+ <id>yt:channel:uCkxoKLYO_EQ2GeFtbM_bw</id>
+ <yt:channelId>uCkxoKLYO_EQ2GeFtbM_bw</yt:channelId>
+ <title>Half as Interesting</title>
+ <link rel="alternate" href="https://www.youtube.com/channel/UCuCkxoKLYO_EQ2GeFtbM_bw"/>
+ <author>
+ <name>Half as Interesting</name>
+ <uri>https://www.youtube.com/channel/UCuCkxoKLYO_EQ2GeFtbM_bw</uri>
+ </author>
+ <published>2017-08-26T20:06:05+00:00</published>
+ <entry>
+ <id>yt:video:Upjg7F28DJw</id>
+ <yt:videoId>Upjg7F28DJw</yt:videoId>
+ <yt:channelId>UCuCkxoKLYO_EQ2GeFtbM_bw</yt:channelId>
+ <title>The Nuke-Proof US Military Base in a Mountain</title>
+ <link rel="alternate" href="https://www.youtube.com/watch?v=Upjg7F28DJw"/>
+ <author>
+ <name>Half as Interesting</name>
+ <uri>https://www.youtube.com/channel/UCuCkxoKLYO_EQ2GeFtbM_bw</uri>
+ </author>
+ <published>2025-01-24T15:44:18+00:00</published>
+ <updated>2025-01-25T06:55:19+00:00</updated>
+ <media:group>
+ <media:title>The Nuke-Proof US Military Base in a Mountain</media:title>
+ <media:content url="https://www.youtube.com/v/Upjg7F28DJw?version=3" type="application/x-shockwave-flash" width="640" height="390"/>
+ <media:thumbnail url="https://i2.ytimg.com/vi/Upjg7F28DJw/hqdefault.jpg" width="480" height="360"/>
+ <media:description>Receive 10% off anything on bellroy.com: https://bit.ly/3HdOWu9</media:description>
+ <media:community>
+ <media:starRating count="10157" average="5.00" min="1" max="5"/>
+ <media:statistics views="228462"/>
+ </media:community>
+ </media:group>
+ </entry>
+ </feed>
+ XML;
+
+ $feed = $this->sut->parseFeed($xml);
+ $expected = [
+ 'title' => 'Half as Interesting',
+ 'uri' => 'https://www.youtube.com/channel/UCuCkxoKLYO_EQ2GeFtbM_bw',
+ 'icon' => null,
+ 'items' => [
+ [
+ 'uri' => 'https://www.youtube.com/watch?v=Upjg7F28DJw',
+ 'title' => 'The Nuke-Proof US Military Base in a Mountain',
+ 'content' => '',
+ 'timestamp' => 1737788119,
+ 'author' => 'Half as Interesting',
+ 'id' => 'yt:video:Upjg7F28DJw',
+ 'published' => '2025-01-24T15:44:18+00:00',
+ 'updated' => '2025-01-25T06:55:19+00:00',
+ 'link' => '',
+ 'yt' => [
+ 'videoId' => 'Upjg7F28DJw',
+ 'channelId' => 'UCuCkxoKLYO_EQ2GeFtbM_bw',
+ ],
+ 'media' => [
+ 'group' => [
+ 'title' => 'The Nuke-Proof US Military Base in a Mountain',
+ 'content' => '',
+ 'thumbnail' => '',
+ 'description' => 'Receive 10% off anything on bellroy.com: https://bit.ly/3HdOWu9',
+ 'community' => [
+ 'starRating' => '',
+ 'statistics' => '',
+ ],
+ ],
+ ],
+ ]
+ ],
+ ];
+ $this->assertEquals($expected, $feed);
+ }
}
diff --git a/tests/UrlTest.php b/tests/UrlTest.php
index d45f319b..72b9ac4c 100644
--- a/tests/UrlTest.php
+++ b/tests/UrlTest.php
@@ -36,6 +36,12 @@ class UrlTest extends TestCase
}
}
+ public function testIllegalPath()
+ {
+ $this->expectException(\UrlException::class);
+ Url::fromString('https://example.com//foo');
+ }
+
public function testMutation()
{
$this->assertSame('http://example.com/foo', (Url::fromString('http://example.com/'))->withPath('/foo')->__toString());