feat(route): add Metacritic (#13447)
* feat(route): add Metacritic * fix typo * Update lib/v2/metacritic/index.js ---------
This commit is contained in:
parent
2775e7a47e
commit
67d4a7ed3f
|
|
@ -992,7 +992,7 @@ router.get('/polimi/news/:language?', lazyloadRouteHandler('./routes/polimi/news
|
|||
router.get('/dekudeals/:type', lazyloadRouteHandler('./routes/dekudeals'));
|
||||
|
||||
// Metacritic
|
||||
router.get('/metacritic/release/:platform/:type/:sort?', lazyloadRouteHandler('./routes/metacritic/release'));
|
||||
// router.get('/metacritic/release/:platform/:type/:sort?', lazyloadRouteHandler('./routes/metacritic/release'));
|
||||
|
||||
// 快科技(原驱动之家)
|
||||
// router.get('/kkj/news', lazyloadRouteHandler('./routes/kkj/news'));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
const { sorts, types } = require('./util');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { type = 'game', sort = 'new', filter } = ctx.params;
|
||||
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50;
|
||||
|
||||
const rootUrl = 'https://www.metacritic.com';
|
||||
const rootApiUrl = 'https://fandom-prod.apigee.net';
|
||||
const apiUrl = new URL('v1/xapi/finder/metacritic/web', rootApiUrl).href;
|
||||
|
||||
const currentUrlObject = new URL(`/browse/${type}/all/all/all-time/${sort}/${filter ? `?${filter}` : ''}`, rootUrl);
|
||||
const currentUrlParams = currentUrlObject.searchParams;
|
||||
const currentUrl = currentUrlObject.href;
|
||||
|
||||
const { data: currentResponse } = await got(currentUrl);
|
||||
|
||||
const apiKey = currentResponse.match(/apiKey=(.*?)&/)[1];
|
||||
|
||||
const searchParams = {
|
||||
sortBy: `-${sorts[sort].id}`,
|
||||
productType: types[type].id,
|
||||
limit,
|
||||
apiKey,
|
||||
};
|
||||
|
||||
const genres = currentUrlParams.getAll('genre').join(',').toLowerCase();
|
||||
const releaseTypes = currentUrlParams.getAll('releaseType').join(',');
|
||||
|
||||
if (genres) {
|
||||
searchParams.genres = genres;
|
||||
}
|
||||
|
||||
if (releaseTypes) {
|
||||
searchParams.releaseType = releaseTypes;
|
||||
}
|
||||
|
||||
const platforms = currentUrlParams.getAll('platform');
|
||||
const networks = currentUrlParams.getAll('network');
|
||||
|
||||
if (platforms.length || networks.length) {
|
||||
const labels = {};
|
||||
const labelPattern = '{label:"([^"]+)",value:(\\d+),href:a,meta:{mcDisplayWeight';
|
||||
|
||||
for (const m of currentResponse.match(new RegExp(labelPattern, 'g'))) {
|
||||
const matches = m.match(new RegExp(labelPattern));
|
||||
|
||||
labels[
|
||||
matches[1]
|
||||
.toLowerCase()
|
||||
.split(/(\s\(|\\u002f(?!\s))/)[0]
|
||||
.replace(/-/g, '---')
|
||||
.replace(/\s\/\s/g, '-or-')
|
||||
.replace(/\+/g, '-plus')
|
||||
.replace(/\s/g, '-')
|
||||
] = matches[2];
|
||||
}
|
||||
|
||||
if (platforms.length) {
|
||||
searchParams.gamePlatformIds = platforms
|
||||
.map((p) => (labels.hasOwnProperty(p) ? labels[p] : undefined))
|
||||
.filter((p) => p)
|
||||
.join(',');
|
||||
}
|
||||
|
||||
if (networks.length) {
|
||||
searchParams.streamingNetworkIds = networks
|
||||
.map((p) => (labels.hasOwnProperty(p) ? labels[p] : undefined))
|
||||
.filter((p) => p)
|
||||
.join(',');
|
||||
}
|
||||
}
|
||||
|
||||
const { data: response } = await got(apiUrl, {
|
||||
searchParams,
|
||||
});
|
||||
|
||||
const items = response.data.items.slice(0, limit).map((item) => ({
|
||||
title: item.title,
|
||||
link: new URL(`${type}/${item.slug}`, rootUrl).href,
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
image: item.image
|
||||
? {
|
||||
src: new URL(`a/img/catalog${item.image.bucketPath}`, rootUrl).href,
|
||||
alt: item.image.alt,
|
||||
}
|
||||
: undefined,
|
||||
description: item.description,
|
||||
score: item.criticScoreSummary?.score ?? undefined,
|
||||
}),
|
||||
category: item.genres.map((c) => c.name),
|
||||
guid: `metacritic-${item.id}`,
|
||||
pubDate: parseDate(item.releaseDate),
|
||||
upvotes: item.criticScoreSummary?.positiveCount ? parseInt(item.criticScoreSummary?.positiveCount, 10) : 0,
|
||||
downvotes: item.criticScoreSummary?.negativeCount ? parseInt(item.criticScoreSummary?.negativeCount, 10) : 0,
|
||||
comments: item.criticScoreSummary?.reviewCount ? parseInt(item.criticScoreSummary?.reviewCount, 10) : 0,
|
||||
}));
|
||||
|
||||
const $ = cheerio.load(currentResponse);
|
||||
|
||||
const icon = new URL($('meta[data-hid="msapplication-task-metacritic"]').prop('content').split('icon-uri=').pop(), rootUrl).href;
|
||||
|
||||
ctx.state.data = {
|
||||
item: items,
|
||||
title: $('title').text(),
|
||||
link: currentUrl,
|
||||
description: $('meta[name="description"]').prop('content'),
|
||||
language: $('html').prop('lang'),
|
||||
image: $('link[rel="icon"]').prop('content'),
|
||||
icon,
|
||||
logo: icon,
|
||||
subtitle: $('meta[name="msapplication-tooltip"]').prop('content'),
|
||||
author: $('meta[name="twitter:site"]').prop('content'),
|
||||
allowEmpty: true,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
'/game/:sort?/:filter?': ['HenryQW', 'nczitzk'],
|
||||
'/movie/:sort?/:filter?': ['nczitzk'],
|
||||
'/tv/:sort?/:filter?': ['nczitzk'],
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
module.exports = {
|
||||
'metacritic.com': {
|
||||
_name: 'Metacritic',
|
||||
'.': [
|
||||
{
|
||||
title: 'Games',
|
||||
docs: 'https://docs.rsshub.app/routes/new-media#metacritic-games',
|
||||
source: ['/browse/game/:params*'],
|
||||
target: (params, url) => {
|
||||
url = new URL(url);
|
||||
const sort = params.params.split(/\//).pop();
|
||||
const filter = url.searchParams.toString();
|
||||
|
||||
return `/metacritic/game${sort ? `/${sort}${filter ? `/${filter}` : ''}` : ''}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Movies',
|
||||
docs: 'https://docs.rsshub.app/routes/new-media#metacritic-movies',
|
||||
source: ['/browse/movie/:params*'],
|
||||
target: (params, url) => {
|
||||
url = new URL(url);
|
||||
const sort = params.params.split(/\//).pop();
|
||||
const filter = url.searchParams.toString();
|
||||
|
||||
return `/metacritic/movie${sort ? `/${sort}${filter ? `/${filter}` : ''}` : ''}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'TV Shows',
|
||||
docs: 'https://docs.rsshub.app/routes/new-media#metacritic-tv-shows',
|
||||
source: ['/browse/tv/:params*'],
|
||||
target: (params, url) => {
|
||||
url = new URL(url);
|
||||
const sort = params.params.split(/\//).pop();
|
||||
const filter = url.searchParams.toString();
|
||||
|
||||
return `/metacritic/tv${sort ? `/${sort}${filter ? `/${filter}` : ''}` : ''}`;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = (router) => {
|
||||
router.get('/:type?/:sort?/:filter?', require('./'));
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{{ if image }}
|
||||
<figure>
|
||||
<img
|
||||
src="{{ image.src }}"
|
||||
{{ if image.alt }}
|
||||
alt="{{ image.alt }}"
|
||||
{{ /if }}
|
||||
>
|
||||
</figure>
|
||||
{{ /if }}
|
||||
|
||||
{{ if description }}
|
||||
<p>{{ description }}</p>
|
||||
{{ /if }}
|
||||
|
||||
{{ if score }}
|
||||
<span>Metascore:</span>
|
||||
{{ score }}
|
||||
{{ /if }}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
const sorts = {
|
||||
metascore: {
|
||||
id: 'metaScore',
|
||||
name: 'Metascore',
|
||||
},
|
||||
userscore: {
|
||||
id: 'userScore',
|
||||
name: 'User Score',
|
||||
},
|
||||
popular: {
|
||||
id: 'popularityCount',
|
||||
name: 'Most Popular',
|
||||
},
|
||||
new: {
|
||||
id: 'releaseDate',
|
||||
name: 'Releases',
|
||||
},
|
||||
};
|
||||
|
||||
const types = {
|
||||
game: {
|
||||
id: 'games',
|
||||
name: 'Games',
|
||||
},
|
||||
movie: {
|
||||
id: 'movies',
|
||||
name: 'Movies',
|
||||
},
|
||||
tv: {
|
||||
id: 'tv',
|
||||
name: 'TV Shows',
|
||||
},
|
||||
albums: {
|
||||
id: 'albums',
|
||||
name: 'Music',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
sorts,
|
||||
types,
|
||||
};
|
||||
|
|
@ -332,32 +332,6 @@ So the route is [`/itch/devlogs/teamterrible/the-baby-in-yellow`](https://rsshub
|
|||
|
||||
<Route author="dearrrfish" example="/maxnews/dota2" path="maxnews/dota2" />
|
||||
|
||||
## Metacritic {#metacritic}
|
||||
|
||||
### Game Releases {#metacritic-game-releases}
|
||||
|
||||
<Route author="HenryQW" example="/metacritic/release/switch/coming" path="/metacritic/release/:platform/:type?/:sort?" paramsDesc={['console platform', 'release type, default to `new`', 'sorting type, default to `date`']}>
|
||||
|
||||
Platforms supported:
|
||||
|
||||
| PS 4 | Xbox One | Switch | PC | Wii U | 3DS | PS Vita | iOS |
|
||||
| ---- | -------- | ------ | --- | ----- | --- | ------- | --- |
|
||||
| ps4 | xboxone | switch | pc | wii-u | 3ds | vita | ios |
|
||||
|
||||
Release types, default to `new`:
|
||||
|
||||
| New | Coming Soon | All |
|
||||
| --- | ----------- | --- |
|
||||
| new | coming | all |
|
||||
|
||||
Sorting types, default to `date`:
|
||||
|
||||
| Date | Metacritic Score | User Score |
|
||||
| ---- | ---------------- | ---------- |
|
||||
| date | metascore | userscore |
|
||||
|
||||
</Route>
|
||||
|
||||
## Minecraft {#minecraft}
|
||||
|
||||
### Java Game Update {#minecraft-java-game-update}
|
||||
|
|
|
|||
|
|
@ -1298,6 +1298,62 @@ IPFS 网关有可能失效,那时候换成其他网关。
|
|||
|
||||
</Route>
|
||||
|
||||
## Metacritic {#metacritic}
|
||||
|
||||
### Games {#metacritic-games}
|
||||
|
||||
<Route author="HenryQW nczitzk" example="/metacritic/game" path="/metacritic/game/:sort?/:filter?" paramsDesc={['Sort, see below, `new` for Newest Releases by default', 'Filter']} radar="1" rssbud="1">
|
||||
|
||||
| Metascore | User Score | Most Popular | Newest Releases |
|
||||
| --------- | ---------- | ------------ | --------------- |
|
||||
| metascore | userscore | popular | new |
|
||||
|
||||
:::tip
|
||||
|
||||
The Filter parameter comes from the corresponding page URL. The following is an example:
|
||||
|
||||
The URL of [Action Games to Play on PS5](https://www.metacritic.com/browse/game/all/all/all-time/new/?platform=ps5&genre=action) is <https://www.metacritic.com/browse/game/all/all/all-time/new/?platform=ps5&genre=action>. The Filter parameter is `platform=ps5&genre=action` and the route is [`/metacritic/game/new/platform=ps5&genre=action`](https://rsshub.app/metacritic/game/new/platform=ps5&genre=action)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### Movies {#metacritic-movies}
|
||||
|
||||
<Route author="nczitzk" example="/metacritic/movie" path="/metacritic/movie/:sort?/:filter?" paramsDesc={['Sort, see below, `new` for Newest Releases by default', 'Filter']} radar="1" rssbud="1">
|
||||
|
||||
| Metascore | User Score | Most Popular | Newest Releases |
|
||||
| --------- | ---------- | ------------ | --------------- |
|
||||
| metascore | userscore | popular | new |
|
||||
|
||||
:::tip
|
||||
|
||||
The Filter parameter comes from the corresponding page URL. The following is an example:
|
||||
|
||||
The URL of [Action Movies to Watch on Netflix](https://www.metacritic.com/browse/movie/all/all/all-time/new/?network=netflix&genre=action) is <https://www.metacritic.com/browse/movie/all/all/all-time/new/?network=netflix&genre=action>. The Filter parameter is `network=netflix&genre=action` and the route is [`/metacritic/movie/new/network=netflix&genre=action`](https://rsshub.app/metacritic/movie/new/network=netflix&genre=action)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
### TV Shows {#metacritic-tv-shows}
|
||||
|
||||
<Route author="nczitzk" example="/metacritic/tv" path="/metacritic/tv/:sort?/:filter?" paramsDesc={['Sort, see below, `new` for Newest Releases by default', 'Filter']} radar="1" rssbud="1">
|
||||
|
||||
| Metascore | User Score | Most Popular | Newest Releases |
|
||||
| --------- | ---------- | ------------ | --------------- |
|
||||
| metascore | userscore | popular | new |
|
||||
|
||||
:::tip
|
||||
|
||||
The Filter parameter comes from the corresponding page URL. The following is an example:
|
||||
|
||||
The URL of [Documentary TV Shows to Watch on Prime Video](https://www.metacritic.com/browse/tv/all/all/all-time/new/?network=prime-video&genre=documentary) is <https://www.metacritic.com/browse/tv/all/all/all-time/new/?network=prime-video&genre=documentary>. The Filter parameter is `network=prime-video&genre=documentary` and the route is [`/metacritic/tv/new/network=prime-video&genre=documentary`](https://rsshub.app/metacritic/tv/new/network=prime-video&genre=documentary)
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
||||
## Mirror {#mirror}
|
||||
|
||||
### User {#mirror-user}
|
||||
|
|
|
|||
Loading…
Reference in New Issue