mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-11-23 18:15:28 +03:00
1dabd10e25
* Adds new NintendoBridge * fix item uids, fix feed title * fix feed icon, adds item categories * fix feed source uri * make currentCatgory property nullable * fix linter errors * fix linter errors * attempt to fix unit tests by assigning default category
466 lines
18 KiB
PHP
466 lines
18 KiB
PHP
<?php
|
||
|
||
class NintendoBridge extends XPathAbstract
|
||
{
|
||
const NAME = 'Nintendo Software Updates';
|
||
const URI = 'https://www.nintendo.co.uk/Support/Welcome-to-Nintendo-Support-11593.html';
|
||
const DONATION_URI = '';
|
||
const DESCRIPTION = self::NAME;
|
||
const MAINTAINER = 'Niehztog';
|
||
const PARAMETERS = [
|
||
'' => [
|
||
'category' => [
|
||
'name' => 'Category',
|
||
'type' => 'list',
|
||
'values' => [
|
||
'All' => 'all',
|
||
'Mario Kart 8 Deluxe' => 'mk8d',
|
||
'Splatoon 2' => 's2',
|
||
'Super Mario 3D All-Stars' => 'sm3as',
|
||
'Super Mario 3D World + Bowser’s Fury' => 'sm3wbf',
|
||
'Super Mario Maker 2' => 'smm2',
|
||
'Super Mario Odyssey' => 'smo',
|
||
'Super Smash Bros. Ultimate' => 'ssbu',
|
||
'Switch Firmware' => 'sf',
|
||
'The Legend of Zelda: Link’s Awakening' => 'tlozla',
|
||
'The Legend of Zelda: Skyward Sword HD' => 'tlozss',
|
||
'The Legend of Zelda: Tears of the Kingdom' => 'tloztotk',
|
||
'Xenoblade Chronicles 2' => 'xc2',
|
||
],
|
||
'defaultValue' => 'mk8d',
|
||
'title' => 'Select category'
|
||
],
|
||
'country' => [
|
||
'name' => 'Country',
|
||
'type' => 'list',
|
||
'values' => [
|
||
'België' => 'be/nl',
|
||
'Belgique' => 'be/fr',
|
||
'Deutschland' => 'de',
|
||
'España' => 'es',
|
||
'France' => 'fr',
|
||
'Italia' => 'it',
|
||
'Nederland' => 'nl',
|
||
'Österreich' => 'at',
|
||
'Portugal' => 'pt',
|
||
'Schweiz' => 'ch/de',
|
||
'Suisse' => 'ch/fr',
|
||
'Svizzera' => 'ch/it',
|
||
'UK & Ireland' => 'co.uk',
|
||
'South Africa' => 'co.za'
|
||
],
|
||
'defaultValue' => 'co.uk',
|
||
'title' => 'Select your country'
|
||
]
|
||
]
|
||
];
|
||
|
||
const CACHE_TIMEOUT = 3600;
|
||
|
||
const FEED_SOURCE_URL = [
|
||
'mk8d' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Mario-Kart-8-Deluxe-1482895.html',
|
||
's2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Splatoon-2-1482897.html',
|
||
'sm3as' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-3D-All-Stars-1844226.html',
|
||
'sm3wbf' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-3D-World-Bowser-s-Fury-1920668.html',
|
||
'smm2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-Maker-2-1586745.html',
|
||
'smo' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Mario-Odyssey-1482901.html',
|
||
'ssbu' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-Super-Smash-Bros-Ultimate-1484130.html',
|
||
'sf' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/System-Updates/Nintendo-Switch-System-Updates-and-Change-History-1445507.html',
|
||
'tlozla' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Link-s-Awakening-1666739.html',
|
||
'tlozss' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Skyward-Sword-HD-2022801.html',
|
||
'tloztotk' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/How-to-Update-The-Legend-of-Zelda-Tears-of-the-Kingdom-2388231.html',
|
||
'xc2' => 'https://www.nintendo.co.uk/Support/Nintendo-Switch/Game-Updates/Xenoblade-Chronicles-2-Update-History-1482911.html',
|
||
];
|
||
const XPATH_EXPRESSION_ITEM = '//div[@class="col-xs-12 content"]/div[starts-with(@id,"v") and @class="collapse"]';
|
||
const XPATH_EXPRESSION_ITEM_FIRMWARE = '//div[@id="latest" and @class="collapse" and @rel="1"]';
|
||
const XPATH_EXPRESSION_ITEM_TITLE = './/h2[1]/node()';
|
||
const XPATH_EXPRESSION_ITEM_CONTENT = '.';
|
||
const XPATH_EXPRESSION_ITEM_URI = '//link[@rel="canonical"]/@href';
|
||
|
||
//const XPATH_EXPRESSION_ITEM_AUTHOR = '';
|
||
const XPATH_EXPRESSION_ITEM_TIMESTAMP_PART = 'substring-after(//a[@class="collapse_link collapsed" and @data-target="#{{id_here}}"]/text(), "{{label_here}}")';
|
||
const XPATH_EXPRESSION_ITEM_TIMESTAMP = 'substring(' . self::XPATH_EXPRESSION_ITEM_TIMESTAMP_PART . ', 1, string-length('
|
||
. self::XPATH_EXPRESSION_ITEM_TIMESTAMP_PART . ') - 1)';
|
||
|
||
//const XPATH_EXPRESSION_ITEM_ENCLOSURES = '';
|
||
//const XPATH_EXPRESSION_ITEM_CATEGORIES = '';
|
||
const SETTING_FIX_ENCODING = false;
|
||
const SETTING_USE_RAW_ITEM_CONTENT = true;
|
||
|
||
private const GAME_COUNTRY_DATE_SUBSTRING_PART = [
|
||
'mk8d' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'ubblicata il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'eleased ',
|
||
],
|
||
's2' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'ubblicata il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'sm3as' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'ubliée le ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'sm3wbf' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'smm2' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'ubliée le ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'smo' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'ssbu' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'sf' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'ise en ligne le ',
|
||
'it' => 'ubblicata il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada no dia ',
|
||
'en' => 'istributed ',
|
||
],
|
||
'tlozla' => [
|
||
'de' => 'eröffentlicht ',
|
||
'es' => 'ublicada el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgegeven op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'tlozss' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'ubblicata l\'',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'tloztotk' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'ubliée le ',
|
||
'it' => 'ubblicata il ',
|
||
'nl' => 'erschenen op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
'xc2' => [
|
||
'de' => 'eröffentlicht am ',
|
||
'es' => 'isponible desde el ',
|
||
'fr' => 'atée du ',
|
||
'it' => 'istribuita il ',
|
||
'nl' => 'itgebracht op ',
|
||
'pt' => 'ançada a ',
|
||
'en' => 'eleased ',
|
||
],
|
||
];
|
||
|
||
private const GAME_COUNTRY_DATE_FORMAT = [
|
||
'mk8d' => [
|
||
'de' => 'd.m.y',
|
||
'es' => 'd-m-y',
|
||
'fr' => 'd/m/Y',
|
||
'it' => 'd/m/y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'd/m/y',
|
||
],
|
||
's2' => [
|
||
'de' => 'd.m.Y',
|
||
'es' => 'd-m-Y',
|
||
'fr' => 'd/m/y',
|
||
'it' => 'd/m/y',
|
||
'nl' => 'd/m/y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'd F Y',
|
||
],
|
||
'sm3as' => [
|
||
'de' => 'j. m Y',
|
||
'es' => 'j \d\e m \d\e Y',
|
||
'fr' => 'j m Y',
|
||
'it' => 'j m Y',
|
||
'nl' => 'j m Y',
|
||
'pt' => 'j \d\e m \d\e Y',
|
||
'en' => 'j F Y',
|
||
],
|
||
'sm3wbf' => [
|
||
'de' => 'd.m.y',
|
||
'es' => 'd-m-y',
|
||
'fr' => 'd/m/y',
|
||
'it' => 'd/m/y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'F j, Y',
|
||
],
|
||
'smm2' => [
|
||
'de' => 'd.m.Y',
|
||
'es' => 'd-m-Y',
|
||
'fr' => 'd/m/Y',
|
||
'it' => 'd/m/Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'd/m/y',
|
||
],
|
||
'smo' => [
|
||
'de' => 'd.m.Y',
|
||
'es' => 'd-m-Y',
|
||
'fr' => 'd/m/Y',
|
||
'it' => 'd/m/y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'd/m/y',
|
||
],
|
||
'ssbu' => [
|
||
'de' => 'd. m Y',
|
||
'es' => 'j \d\e m \d\e Y',
|
||
'fr' => 'j m Y',
|
||
'it' => 'j m Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/Y',
|
||
'en' => 'j F Y',
|
||
],
|
||
'sf' => [
|
||
'de' => 'd.m.Y',
|
||
'es' => 'd-m-y',
|
||
'fr' => 'd/m/Y',
|
||
'it' => 'd/m/Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/Y',
|
||
'en' => 'd/m/Y',
|
||
],
|
||
'tlozla' => [
|
||
'de' => 'd. m Y',
|
||
'es' => 'j m \d\e Y',
|
||
'fr' => 'd/m/y',
|
||
'it' => 'j m Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'j \d\e m \d\e Y',
|
||
'en' => 'j F y',
|
||
],
|
||
'tlozss' => [
|
||
'de' => 'd. m Y',
|
||
'es' => 'j \d\e m \d\e Y',
|
||
'fr' => 'd/m/y',
|
||
'it' => 'j m Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'j \d\e m \d\e Y',
|
||
'en' => 'j F Y',
|
||
],
|
||
'tloztotk' => [
|
||
'de' => 'd. m Y',
|
||
'es' => 'j \d\e m \d\e Y',
|
||
'fr' => 'j m Y',
|
||
'it' => 'j m Y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'j \d\e m \d\e Y',
|
||
'en' => 'j F Y',
|
||
],
|
||
'xc2' => [
|
||
'de' => 'd.m.y',
|
||
'es' => 'd-m-y',
|
||
'fr' => 'd/m/Y',
|
||
'it' => 'd/m/y',
|
||
'nl' => 'd m Y',
|
||
'pt' => 'd/m/y',
|
||
'en' => 'd/m/y',
|
||
],
|
||
];
|
||
|
||
private const FOREIGN_MONTH_NAMES = [
|
||
'nl' => ['01' => 'januari', '02' => 'februari', '03' => 'maart', '04' => 'april', '05' => 'mei', '06' => 'juni', '07' => 'juli', '08' => 'augustus',
|
||
'09' => 'september', '10' => 'oktober', '11' => 'november', '12' => 'december'],
|
||
'fr' => ['01' => 'janvier', '02' => 'février', '03' => 'mars', '04' => 'avril', '05' => 'mai', '06' => 'juin', '07' => 'juillet', '08' => 'août',
|
||
'09' => 'septembre', '10' => 'octobre', '11' => 'novembre', '12' => 'décembre'],
|
||
'de' => ['01' => 'Januar', '02' => 'Februar', '03' => 'März', '04' => 'April', '05' => 'Mai', '06' => 'Juni', '07' => 'Juli', '08' => 'August',
|
||
'09' => 'September', '10' => 'Oktober', '11' => 'November', '12' => 'Dezember'],
|
||
'es' => ['01' => 'enero', '02' => 'febrero', '03' => 'marzo', '04' => 'abril', '05' => 'mayo', '06' => 'junio', '07' => 'julio', '08' => 'agosto',
|
||
'09' => 'septiembre', '10' => 'octubre', '11' => 'noviembre', '12' => 'diciembre'],
|
||
'it' => ['01' => 'gennaio', '02' => 'febbraio', '03' => 'marzo', '04' => 'aprile', '05' => 'maggio', '06' => 'giugno', '07' => 'luglio', '08' => 'agosto',
|
||
'09' => 'settembre', '10' => 'ottobre', '11' => 'novembre', '12' => 'dicembre'],
|
||
'pt' => ['01' => 'janeiro', '02' => 'fevereiro', '03' => 'março', '04' => 'abril', '05' => 'maio', '06' => 'junho', '07' => 'julho', '08' => 'agosto',
|
||
'09' => 'setembro', '10' => 'outubro', '11' => 'novembro', '12' => 'dezembro'],
|
||
];
|
||
const LANGUAGE_REWRITE = ['co.uk' => 'en', 'co.za' => 'en', 'at' => 'de'];
|
||
|
||
private string $lastId = '';
|
||
private ?string $currentCategory = '';
|
||
|
||
private function getCurrentCategory()
|
||
{
|
||
if (empty($this->currentCategory)) {
|
||
$category = $this->getInput('category');
|
||
$this->currentCategory = empty($category) ? self::PARAMETERS['']['category']['defaultValue'] : $category;
|
||
}
|
||
return $this->currentCategory;
|
||
}
|
||
|
||
public function getIcon()
|
||
{
|
||
return 'https://www.nintendo.co.uk/favicon.ico';
|
||
}
|
||
|
||
public function getURI()
|
||
{
|
||
$category = $this->getInput('category');
|
||
return 'all' === $category ? self::URI : $this->getSourceUrl();
|
||
}
|
||
|
||
protected function provideFeedTitle(\DOMXPath $xpath)
|
||
{
|
||
$category = $this->getInput('category');
|
||
$categoryName = array_search($category, self::PARAMETERS['']['category']['values']);
|
||
return 'all' === $category ? self::NAME : $categoryName . ' Software-Updates';
|
||
}
|
||
|
||
protected function getSourceUrl()
|
||
{
|
||
$country = $this->getInput('country');
|
||
$category = $this->getCurrentCategory();
|
||
return str_replace(self::PARAMETERS['']['country']['defaultValue'], $country, self::FEED_SOURCE_URL[$category]);
|
||
}
|
||
|
||
protected function getExpressionItem()
|
||
{
|
||
$category = $this->getCurrentCategory();
|
||
return 'sf' === $category ? self::XPATH_EXPRESSION_ITEM_FIRMWARE : self::XPATH_EXPRESSION_ITEM;
|
||
}
|
||
|
||
protected function getExpressionItemTimestamp()
|
||
{
|
||
if (empty($this->lastId)) {
|
||
return null;
|
||
}
|
||
$country = $this->getInput('country');
|
||
$category = $this->getCurrentCategory();
|
||
$language = $this->getLanguageFromCountry($country);
|
||
return str_replace(
|
||
['{{id_here}}', '{{label_here}}'],
|
||
[$this->lastId, static::GAME_COUNTRY_DATE_SUBSTRING_PART[$category][$language]],
|
||
static::XPATH_EXPRESSION_ITEM_TIMESTAMP
|
||
);
|
||
}
|
||
|
||
protected function getExpressionItemCategories()
|
||
{
|
||
$category = $this->getCurrentCategory();
|
||
$categoryName = array_search($category, self::PARAMETERS['']['category']['values']);
|
||
return 'string("' . $categoryName . '")';
|
||
}
|
||
|
||
public function collectData()
|
||
{
|
||
$category = $this->getCurrentCategory();
|
||
if ('all' === $category) {
|
||
$allItems = [];
|
||
foreach (self::PARAMETERS['']['category']['values'] as $catKey) {
|
||
if ('all' === $catKey) {
|
||
continue;
|
||
}
|
||
$this->currentCategory = $catKey;
|
||
$this->items = [];
|
||
parent::collectData();
|
||
$allItems = [...$allItems, ...$this->items];
|
||
}
|
||
$this->currentCategory = 'all';
|
||
$this->items = $allItems;
|
||
} else {
|
||
parent::collectData();
|
||
}
|
||
}
|
||
|
||
protected function formatItemTitle($value)
|
||
{
|
||
if (false !== strpos($value, ' (')) {
|
||
$value = substr($value, 0, strpos($value, ' ('));
|
||
}
|
||
if ('all' === $this->getInput('category')) {
|
||
$category = $this->getCurrentCategory();
|
||
$categoryName = array_search($category, self::PARAMETERS['']['category']['values']);
|
||
return $categoryName . ' ' . $value;
|
||
}
|
||
return $value;
|
||
}
|
||
|
||
protected function formatItemContent($value)
|
||
{
|
||
$result = preg_match('~<div class="collapse" id="([a-z0-9]+)" rel="1">(.*)</div>~', $value, $matches);
|
||
if (1 === $result) {
|
||
$this->lastId = $matches[1];
|
||
return trim($matches[2]);
|
||
}
|
||
return $value;
|
||
}
|
||
|
||
protected function formatItemTimestamp($value)
|
||
{
|
||
$country = $this->getInput('country');
|
||
$category = $this->getCurrentCategory();
|
||
$language = $this->getLanguageFromCountry($country);
|
||
|
||
$aMonthNames = self::FOREIGN_MONTH_NAMES[$language] ?? null;
|
||
if (null !== $aMonthNames) {
|
||
$value = str_replace(array_values($aMonthNames), array_keys($aMonthNames), $value);
|
||
}
|
||
$value = str_replace('', '-', $value);
|
||
$value = str_replace('--', '-', $value);
|
||
|
||
$date = \DateTime::createFromFormat(self::GAME_COUNTRY_DATE_FORMAT[$category][$language], $value);
|
||
if (false === $date) {
|
||
$date = new \DateTime('now');
|
||
}
|
||
return $date->getTimestamp();
|
||
}
|
||
|
||
protected function generateItemId(FeedItem $item)
|
||
{
|
||
return $this->getCurrentCategory() . '-' . $this->lastId;
|
||
}
|
||
|
||
private function getLanguageFromCountry($country)
|
||
{
|
||
return (strpos($country, '/') !== false) ? substr($country, strpos($country, '/') + 1) : (self::LANGUAGE_REWRITE[$country] ?? $country);
|
||
}
|
||
}
|