From 051a3efcbb1983b261d4d3bfdd20e414bc8d84ea Mon Sep 17 00:00:00 2001 From: Enoch Ma Date: Mon, 5 Jun 2023 17:50:00 +0200 Subject: [PATCH] fix(route): aeon - support for other categories (#12614) * aeon: hotfix & migrate to v2 * rewrite & focus on essays * fix banner * better support for aeon * update the doc * credits before content * using templates & fix doc --- docs/en/new-media.md | 16 +++++++++-- docs/new-media.md | 14 +++++++-- lib/v2/aeon/category.js | 27 ++++++++++++++++++ lib/v2/aeon/essays.js | 46 ------------------------------ lib/v2/aeon/maintainer.js | 3 +- lib/v2/aeon/radar.js | 12 ++++++-- lib/v2/aeon/router.js | 3 +- lib/v2/aeon/templates/essay.art | 3 ++ lib/v2/aeon/templates/video.art | 10 +++++++ lib/v2/aeon/type.js | 29 +++++++++++++++++++ lib/v2/aeon/utils.js | 50 +++++++++++++++++++++++++++++++++ 11 files changed, 156 insertions(+), 57 deletions(-) create mode 100644 lib/v2/aeon/category.js delete mode 100644 lib/v2/aeon/essays.js create mode 100644 lib/v2/aeon/templates/essay.art create mode 100644 lib/v2/aeon/templates/video.art create mode 100644 lib/v2/aeon/type.js create mode 100644 lib/v2/aeon/utils.js diff --git a/docs/en/new-media.md b/docs/en/new-media.md index 2690f6f2a..80ee4c460 100644 --- a/docs/en/new-media.md +++ b/docs/en/new-media.md @@ -19,14 +19,24 @@ Supported sub-sites: ## AEON -### Essays +### Types - + -Supported categories: Essays. +Supported types: Essays, Videos, and Audio. Compared to the official one, the RSS feed generated by RSSHub not only has more fine-grained options, but also eliminates pull quotes, which can't be easily distinguished from other paragraphs by any RSS reader, but only disrupt the reading flow. This feed also provides users with a bio of the author at the top. +However, The content generated under `audio` does not contain links to audio files. + + + +### Categories + + + +Supported categories: Philosophy, Science, Psychology, Society, and Culture. + ## American Federation of Labor and Congress of Industrial Organizations diff --git a/docs/new-media.md b/docs/new-media.md index 48db3fbb2..ed16e82e3 100644 --- a/docs/new-media.md +++ b/docs/new-media.md @@ -135,11 +135,19 @@ pageClass: routes ## AEON -### Essays +### 类型 - + -支持获取 Essays. +支持获取 Essays, Videos, 以及 Audio. 但 Audio 仅输出正文内容,并不包括音轨链接。 + + + +### 分类 + + + +支持获取的分类包括: Philosophy, Science, Psychology, Society, 以及 Culture. diff --git a/lib/v2/aeon/category.js b/lib/v2/aeon/category.js new file mode 100644 index 000000000..12a40dc20 --- /dev/null +++ b/lib/v2/aeon/category.js @@ -0,0 +1,27 @@ +const cheerio = require('cheerio'); +const got = require('@/utils/got'); +const { getData } = require('./utils'); + +module.exports = async (ctx) => { + const url = `https://aeon.co/${ctx.params.category}`; + const { data: response } = await got(url); + const $ = cheerio.load(response); + + const data = JSON.parse($('script#__NEXT_DATA__').text()); + + const list = data.props.pageProps.section.articles.edges.map((item) => ({ + title: item.node.title, + author: item.node.authors.map((author) => author.displayName).join(', '), + link: `https://aeon.co/${item.node.type.toLowerCase()}s/${item.node.slug}`, + pubDate: item.node.createdAt, + })); + + const items = await getData(ctx, list); + + ctx.state.data = { + title: `AEON | ${data.props.pageProps.section.title}`, + link: url, + description: data.props.pageProps.section.metaDescription, + item: items, + }; +}; diff --git a/lib/v2/aeon/essays.js b/lib/v2/aeon/essays.js deleted file mode 100644 index bd89ca435..000000000 --- a/lib/v2/aeon/essays.js +++ /dev/null @@ -1,46 +0,0 @@ -const cheerio = require('cheerio'); -const got = require('@/utils/got'); - -module.exports = async (ctx) => { - const url = `https://aeon.co/essays`; - const { data: response } = await got(url); - const $ = cheerio.load(response); - - const data = JSON.parse($('script#__NEXT_DATA__').text()); - - const list = data.props.pageProps.articles.map((item) => ({ - title: item.title, - author: item.authors.map((author) => author.displayName).join(', '), - link: `https://aeon.co/essays/${item.slug}`, - pubDate: item.createdAt, - })); - - 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); - - // It seems that the method based on __NEXT_DATA__ - // does not include the information of the two-column - // images in the article body, - // e.g. https://aeon.co/essays/how-to-mourn-a-forest-a-lesson-from-west-papua . - // But that's very rare. - const data = JSON.parse($('script#__NEXT_DATA__').text()); - const banner = ``; - const authorsBio = data.props.pageProps.article.authors.map((author) => '

' + author.displayName + author.authorBio.replace(/^

/g, ' ')).join(''); - const capture = cheerio.load(data.props.pageProps.article.body); - capture('p.pullquote').remove(); - item.description = banner + authorsBio + capture.html(); - - return item; - }) - ) - ); - - ctx.state.data = { - title: `AEON | Essays`, - link: url, - item: items, - }; -}; diff --git a/lib/v2/aeon/maintainer.js b/lib/v2/aeon/maintainer.js index 65050f87a..b44e5c773 100644 --- a/lib/v2/aeon/maintainer.js +++ b/lib/v2/aeon/maintainer.js @@ -1,3 +1,4 @@ module.exports = { - '/essays': ['emdoe'], + '/category/:category': ['emdoe'], + '/:type': ['emdoe'], }; diff --git a/lib/v2/aeon/radar.js b/lib/v2/aeon/radar.js index 513eda3f7..1970e4bec 100644 --- a/lib/v2/aeon/radar.js +++ b/lib/v2/aeon/radar.js @@ -3,10 +3,16 @@ module.exports = { _name: 'AEON', aeon: [ { - title: 'Essays', + title: 'Types (Essays, Videos, or Audio)', docs: 'https://docs.rsshub.app/new-media.html##aeon', - source: ['/'], - target: '/aeon/essays', + source: ['/:type'], + target: '/aeon/:type', + }, + { + title: 'Category', + docs: 'https://docs.rsshub.app/new-media.html##aeon', + source: ['/:category'], + target: '/aeon/category/:category', }, ], }, diff --git a/lib/v2/aeon/router.js b/lib/v2/aeon/router.js index f77566425..b84a0dd20 100644 --- a/lib/v2/aeon/router.js +++ b/lib/v2/aeon/router.js @@ -1,3 +1,4 @@ module.exports = function (router) { - router.get('/essays', require('./essays')); + router.get('/category/:category', require('./category')); + router.get('/:type', require('./type')); }; diff --git a/lib/v2/aeon/templates/essay.art b/lib/v2/aeon/templates/essay.art new file mode 100644 index 000000000..5a2fab892 --- /dev/null +++ b/lib/v2/aeon/templates/essay.art @@ -0,0 +1,3 @@ + +{{@ authorsBio }} +{{@ content}} \ No newline at end of file diff --git a/lib/v2/aeon/templates/video.art b/lib/v2/aeon/templates/video.art new file mode 100644 index 000000000..d1f546c39 --- /dev/null +++ b/lib/v2/aeon/templates/video.art @@ -0,0 +1,10 @@ +{{ set video = article.hosterId }} +{{ if article.hoster === 'vimeo' }} + {{ set video = "https://player.vimeo.com/video/" + video + "?dnt=1"}} +{{ else if article.hoster == 'youtube' }} + {{ set video = "https://www.youtube-nocookie.com/embed/" + video }} +{{ /if }} + + +{{@ article.credits}} +{{@ article.description}} diff --git a/lib/v2/aeon/type.js b/lib/v2/aeon/type.js new file mode 100644 index 000000000..23877850b --- /dev/null +++ b/lib/v2/aeon/type.js @@ -0,0 +1,29 @@ +const cheerio = require('cheerio'); +const got = require('@/utils/got'); +const { getData } = require('./utils'); + +module.exports = async (ctx) => { + const type = ctx.params.type; + const binaryType = type === 'videos' ? 'videos' : 'essays'; + const capitalizedType = type.charAt(0).toUpperCase() + type.slice(1); + + const url = `https://aeon.co/${type}`; + const { data: response } = await got(url); + const $ = cheerio.load(response); + + const data = JSON.parse($('script#__NEXT_DATA__').text()); + + const list = data.props.pageProps.articles.map((item) => ({ + title: item.title, + link: `https://aeon.co/${binaryType}/${item.slug}`, + pubDate: item.createdAt, + })); + + const items = await getData(ctx, list); + + ctx.state.data = { + title: `AEON | ${capitalizedType}`, + link: url, + item: items, + }; +}; diff --git a/lib/v2/aeon/utils.js b/lib/v2/aeon/utils.js new file mode 100644 index 000000000..4d8f89832 --- /dev/null +++ b/lib/v2/aeon/utils.js @@ -0,0 +1,50 @@ +const cheerio = require('cheerio'); +const got = require('@/utils/got'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const getData = async (ctx, list) => { + 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#__NEXT_DATA__').text()); + const type = data.props.pageProps.article.type.toLowerCase(); + if (type === 'video') { + item.description = art(path.join(__dirname, 'templates/video.art'), { article: data.props.pageProps.article }); + } else { + // Essay or Audio + // But unfortunately, the method based on __NEXT_DATA__ + // does not include the information of the audio link. + + // Besides, it seems that the method based on __NEXT_DATA__ + // does not include the information of the two-column + // images in the article body, + // e.g. https://aeon.co/essays/how-to-mourn-a-forest-a-lesson-from-west-papua . + // But that's very rare. + + item.author = data.props.pageProps.article.authors.map((author) => author.displayName).join(', '); + + const article = data.props.pageProps.article; + const capture = cheerio.load(article.body); + const banner = article.thumbnail.urls.header; + capture('p.pullquote').remove(); + + const authorsBio = article.authors.map((author) => '

' + author.displayName + author.authorBio.replace(/^

/g, ' ')).join(''); + + item.description = art(path.join(__dirname, 'templates/essay.art'), { banner, authorsBio, content: capture.html() }); + } + + return item; + }) + ) + ); + + return items; +}; + +module.exports = { + getData, +};