fix(route): gq taiwan (#11549)

This commit is contained in:
Tony
2023-01-03 23:41:55 +08:00
committed by GitHub
parent 981391b663
commit a948dac993
13 changed files with 226 additions and 93 deletions
+20 -26
View File
@@ -540,15 +540,15 @@ Country
## GQ
### GQ 台湾
### GQ Taiwan
<Route author="nczitzk" example="/gq/tw/fashion" path="/gq/tw/:caty?/:subcaty?" :paramsDesc="['分类,见下表', '子分类,见下表']">
<Route author="nczitzk" example="/gq/tw/fashion" path="/gq/tw/:caty?/:subcaty?" :paramsDesc="['分类,见下表', '子分类,见下表']" radar="1">
分类
| Fashion | Entertainment | Life | Gadget | Better Men | Video | Tag |
| ------- | ------------- | ---- | ------ | ---------- | ----- | --- |
| fashion | entertainment | life | gadget | bettermen | video | tag |
| Fashion | Shopping | Entertainment | Life | Gadget | Better Men | Video | Tag |
| ------- | ------------- | ------------- | ---- | ------ | ---------- | ----- | --- |
| fashion | gq-recommends | entertainment | life | gadget | bettermen | video | tag |
子分类
@@ -560,39 +560,33 @@ Fashion
Entertainment
| 最新推薦 | 電影 | 娛樂 | 名人 | 美女 | 體育 | 特別報導 |
| ---- | ----- | ---------- | ----------- | ---- | ------ | ------- |
| | movie | popculture | celebrities | girl | sports | special |
| All topics | 電影 | 娛樂 | 名人 | 美女 | 體育 | 特別報導 |
| ---------- | ----- | ---------- | ----------- | ---- | ------ | ------- |
| | movie | popculture | celebrities | girl | sports | special |
Life
| 最新推薦 | 美食 | 微醺 | 戶外生活 | 設計生活 | 風格幕後 | 特別報導 |
| ---- | ---- | ---- | ------- | ------ | ---------------- | ------- |
| | food | wine | outdoor | design | lifestyleinsider | special |
| All topics | 美食 | 微醺 | 戶外生活 | 設計生活 | 風格幕後 | 特別報導 |
| ---------- | ---- | ---- | ------- | ------ | ---------------- | ------- |
| | food | wine | outdoor | design | lifestyleinsider | special |
Gadget
| 最新推薦 | 3C | 車 | 腕錶 | 特別報導 |
| ---- | -- | ---- | ----- | ------- |
| | 3c | auto | watch | special |
| All topics | 3C | 車 | 腕錶 | 特別報導 |
| ---------- | -- | ---- | ----- | ------- |
| | 3c | auto | watch | special |
Better Men
| 最新推薦 | 保養健身 | 感情關係 | 性愛 | 特別報導 |
| ---- | --------- | ------------ | --- | ------- |
| | wellbeing | relationship | sex | special |
Video
| 最新推薦 | 名人 | 全球娛樂 | 玩家收藏 | 穿搭 | 生活 |
| ---- | ------ | ------------------- | ------- | ----- | ---- |
| | people | globalentertainment | collect | style | life |
| All topics | 保養健身 | 感情關係 | 性愛 | 特別報導 |
| ---------- | --------- | ------------ | --- | ------- |
| | wellbeing | relationship | sex | special |
Tag
| 奧斯卡 |
| ------------------- |
| `the-oscars-奧斯卡金像獎` |
| 奧斯卡 | MOTY |
| ------------------- | ---- |
| `the-oscars-奧斯卡金像獎` | moty |
</Route>
+1 -1
View File
@@ -3179,7 +3179,7 @@ router.get('/1x/:category?', lazyloadRouteHandler('./routes/1x/index'));
router.get('/jx3/:caty?', lazyloadRouteHandler('./routes/jx3/news'));
// GQ
router.get('/gq/tw/:caty?/:subcaty?', lazyloadRouteHandler('./routes/gq/tw/index'));
// router.get('/gq/tw/:caty?/:subcaty?', lazyloadRouteHandler('./routes/gq/tw/index'));
// 泉州市跨境电子商务协会
router.get('/qzcea/:caty?', lazyloadRouteHandler('./routes/qzcea/index'));
-66
View File
@@ -1,66 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
ctx.params.caty = ctx.params.caty || '';
ctx.params.subcaty = ctx.params.subcaty || '';
const rootUrl = 'https://www.gq.com.tw';
const currentUrl = `${rootUrl}${ctx.params.caty === '' ? '' : '/' + ctx.params.caty}${ctx.params.subcaty === '' ? '' : '/' + ctx.params.subcaty}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const $ = cheerio.load(response.data);
$('a[data-test-id="NavElement"]').remove();
$('a[data-test-id="Link"]').remove();
$('a[data-test-id="FooterLink"]').remove();
const list = $('a[data-test-id]')
.slice(0, 10)
.map((_, item) => {
item = $(item);
return {
link: `${rootUrl}${item.attr('href')}`,
};
})
.get();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
item.title = content('h1').text();
const article = content('section[data-test-id="ArticleBodyContent"]').html();
const gallery = content('section[data-test-id="GalleryImage"]').html();
if (ctx.params.caty === 'video') {
item.description = content('div[data-test-id="EmbedIframe"]').html() + content('p[data-test-id="DekText"]').parent().html();
item.pubDate = new Date(detailResponse.data.match(/pubDate":"(.*?)","channel/)[1]).toUTCString();
} else if (gallery !== null) {
item.description = gallery;
item.pubDate = new Date(detailResponse.data.match(/pubDate":"(.*?)","items/)[1]).toUTCString();
} else {
item.description = article;
item.pubDate = new Date(detailResponse.data.match(/pubDate":"(.*?)","publishedRevisions/)[1]).toUTCString();
}
return item;
})
)
);
ctx.state.data = {
title: `${$('title').eq(0).text()} - GQ Taiwan`,
link: currentUrl,
item: items,
};
};
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
'/tw/:caty?/:subcaty?': ['nczitzk'],
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
'gq.com.tw': {
_name: 'GQ Taiwan',
'.': [
{
title: 'GQ Taiwan',
docs: 'https://docs.rsshub.app/new-media.html#gq',
source: ['/*path'],
target: '/gq/tw/:path',
},
],
},
};
+3
View File
@@ -0,0 +1,3 @@
module.exports = (router) => {
router.get('/tw/:caty?/:subcaty?', require('./tw/index'));
};
+1
View File
@@ -0,0 +1 @@
<a href="{{ url }}" target="_blank">{{ text }}</a>
+4
View File
@@ -0,0 +1,4 @@
{{@ img }}
{{ productProps.dangerousHed }}<br>
{{ if productProps.offerRetailer }}{{ productProps.offerRetailer }} {{ /if }}{{ if productProps.multipleOffers }}{{ productProps.multipleOffers[0].price }}{{ /if }}
{{ if productProps.offerUrl }}<br><a href="{{ productProps.offerUrl }}" target="_blank">Buy It</a>{{ /if }}
+4
View File
@@ -0,0 +1,4 @@
<figure>
<img src="{{ src }}"{{ if alt }} alt="{{@ alt }}"{{ /if }}>
{{ if alt }}<figcaption>{{@ alt }}</figcaption>{{ /if }}
</figure>
+3
View File
@@ -0,0 +1,3 @@
{{ if dangerousDek }}<div>{{@ dangerousDek }}</div>{{ /if }}
{{ if lede }}{{@ lede }}{{ /if }}
{{ if articleBody }}{{@ articleBody }}{{ /if }}
+6
View File
@@ -0,0 +1,6 @@
<video controls preload="none" poster="{{ poster }}">
{{ each sources source }}
<source src="{{ source.src }}" type="{{ source.contentType }}">
{{ /each }}
</video>
{{@ articleBody }}
+3
View File
@@ -0,0 +1,3 @@
{{ if videoId }}
<iframe id="ytplayer" type="text/html" width="640" height="360" src="https://www.youtube-nocookie.com/embed/{{ videoId }}" frameborder="0" allowfullscreen></iframe>
{{ /if }}
+165
View File
@@ -0,0 +1,165 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const md = require('markdown-it')({
linkify: true,
});
const { art } = require('@/utils/render');
const path = require('path');
const baseUrl = 'https://www.gq.com.tw';
const parsePreloadedStateJSON = ($) =>
JSON.parse(
$('script[type="text/javascript"]')
.text()
.match(/window\.__PRELOADED_STATE__ = ({.*?});/)[1]
);
const largestImage = (sources, id) => {
if (!id) {
let maxWidth = 0;
let maxUrl = '';
Object.values(sources).forEach((size) => {
if (size.width > maxWidth) {
maxWidth = size.width;
maxUrl = size.url;
}
});
return maxUrl;
}
const url = sources[Object.keys(sources)[0]].url;
const filename = url.substring(url.lastIndexOf('/') + 1);
return `https://media.gq.com.tw/photos/${id}/${filename}`;
};
module.exports = async (ctx) => {
const { caty, subcaty } = ctx.params;
const link = `${baseUrl}${caty ? `/${caty}` : ''}${subcaty ? `/${subcaty}` : ''}`;
const { data: response } = await got(link);
const $ = cheerio.load(response);
const { transformed } = parsePreloadedStateJSON($);
const list = transformed.bundle.containers
.filter((item) => item.items)
.map((item) =>
item.items.map((item) => ({
title: item.source?.hed || item.dangerousHed,
description: item.source?.dek || item.dangerousDek,
link: (item.url.startsWith('http') ? item.url : `${baseUrl}${item.url}`).split('#intcid')[0],
pubDate: parseDate(item.pubDate),
author: item.contributors.author.items.map((item) => item.name).join(', '),
}))
)
.flat();
const items = await Promise.all(
list.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = cheerio.load(response);
const data = JSON.parse($('script[type="application/ld+json"]').first().text());
const { transformed } = parsePreloadedStateJSON($);
const VideoObject = data['@type'] === 'VideoObject' ? true : false;
const lede = !VideoObject ? transformed.article.headerProps.lede : null;
let articleBody = '';
if (!VideoObject) {
// typical article
// articleBody inludes non standard markdown syntax
articleBody = cheerio.load(md.render(data.articleBody.replace(/\{: target="_blank"\}/g, '')).replace(/\{: #link\}/g, ''), null, false);
articleBody('a').each((_, item) => {
item = $(item);
if (item.text().startsWith('#video:')) {
if (item.text().match(/youtube/)) {
const videoId = item.text().match(/.*https:\/\/www\.youtube\.com\/embed\/(.*)\s/)[1];
item.replaceWith(art(path.join(__dirname, '../templates/youtube.art'), { videoId }));
}
}
if (item.text().startsWith('![#image:')) {
const imageId = item.text().match(/!\[#image: \/photos\/(.*?)\]/)[1];
const imgInfo = transformed.article.lightboxImages.find((item) => item.id === imageId);
item.html(
art(path.join(__dirname, '../templates/img.art'), {
src: largestImage(imgInfo.sources, imageId),
alt: imgInfo.dangerousCaption,
})
);
}
});
articleBody('p').each((_, item) => {
item = $(item);
if (item.text().startsWith('+++')) {
item.remove();
}
if (item.text().startsWith('[#image:')) {
const imageId = item.text().match(/\[#image: \/photos\/(.*?)\]/)[1];
const imgInfo = transformed.article.lightboxImages.find((item) => item.id === imageId);
item.replaceWith(
art(path.join(__dirname, '../templates/img.art'), {
src: largestImage(imgInfo.sources, imageId),
alt: imgInfo.dangerousCaption,
})
);
}
if (item.text().startsWith('[#article:')) {
const articleId = item.text().match(/\[#article: \/articles\/(.*?)\]/)[1];
const articleProps = transformed.article.body.filter((i) => i[0] === 'inline-embed').find((i) => i[1].ref === articleId)[1].props;
item.replaceWith(
art(path.join(__dirname, '../templates/embed-article.art'), {
url: `${baseUrl}${articleProps.url}`,
text: articleProps.dangerousHed,
})
);
}
if (item.text().startsWith('[#product:')) {
const productId = item.text().match(/\[#product: \/products\/(.*?)\]/)[1];
const productProps = transformed.article.body.filter((i) => i[0] === 'inline-embed').find((i) => i[1].ref === productId)[1].props;
item.replaceWith(
art(path.join(__dirname, '../templates/embed-product.art'), {
img: art(path.join(__dirname, '../templates/img.art'), {
src: largestImage(productProps.image.sources),
}),
productProps,
})
);
}
if (item.text().startsWith('[#instagram:')) {
const instagramHref = item.text().match(/\[#instagram: (.*?)\]/)[1];
item.replaceWith(
art(path.join(__dirname, '../templates/embed-article.art'), {
url: instagramHref,
text: instagramHref,
})
);
}
});
item.description = art(path.join(__dirname, '../templates/tw.art'), {
dangerousDek: transformed.article.headerProps.dangerousDek, // quote
lede: art(path.join(__dirname, '../templates/img.art'), {
// header image
src: largestImage(lede.sources, lede.id),
alt: lede.caption,
}),
articleBody: articleBody.html(),
});
} else {
// is VideoObject
item.description = art(path.join(__dirname, '../templates/videoObject.art'), {
poster: transformed.video.metaImageUrl,
sources: transformed.video.sources,
articleBody: data.description,
});
}
return item;
})
)
);
ctx.state.data = {
title: `${transformed.coreDataLayer.content.contentTitle} | ${transformed.coreDataLayer.content.brand}`,
link,
image: `${baseUrl}${transformed.logo.sources.sm.url}`,
item: items,
allowEmpty: true,
};
};