Skip to content

Commit

Permalink
feat(aeon): enhance category and type routes with detailed parameters…
Browse files Browse the repository at this point in the history
… and improved data fetching (DIYgod#17745)
  • Loading branch information
TonyRL authored Nov 28, 2024
1 parent 35589ca commit 13696b9
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 57 deletions.
51 changes: 34 additions & 17 deletions lib/routes/aeon/category.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { Route } from '@/types';
import { load } from 'cheerio';
import got from '@/utils/got';
import { getData } from './utils';
import ofetch from '@/utils/ofetch';
import { getBuildId, getData } from './utils';
import { parseDate } from '@/utils/parse-date';

export const route: Route = {
path: '/category/:category',
categories: ['new-media', 'popular'],
example: '/aeon/category/philosophy',
parameters: { category: 'Category' },
parameters: {
category: {
description: 'Category',
options: [
{ value: 'philosophy', label: 'Philosophy' },
{ value: 'science', label: 'Science' },
{ value: 'psychology', label: 'Psychology' },
{ value: 'society', label: 'Society' },
{ value: 'culture', label: 'Culture' },
],
},
},
features: {
requireConfig: false,
requirePuppeteer: false,
Expand All @@ -18,34 +29,40 @@ export const route: Route = {
},
radar: [
{
source: ['aeon.aeon.co/:category'],
source: ['aeon.co/:category'],
},
],
name: 'Categories',
maintainers: ['emdoe'],
handler,
description: `Supported categories: Philosophy, Science, Psychology, Society, and Culture.`,
};

async function handler(ctx) {
const url = `https://aeon.co/${ctx.req.param('category')}`;
const { data: response } = await got(url);
const $ = load(response);
const category = ctx.req.param('category').toLowerCase();
const url = `https://aeon.co/category/${category}`;
const buildId = await getBuildId();
const response = await ofetch(`https://aeon.co/_next/data/${buildId}/${category}.json`);

const data = JSON.parse($('script#__NEXT_DATA__').text());
const section = response.pageProps.section;

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}`,
const list = section.articles.edges.map(({ node }) => ({
title: node.title,
description: node.standfirstLong,
author: node.authors.map((author) => author.displayName).join(', '),
link: `https://aeon.co/${node.type}s/${node.slug}`,
pubDate: parseDate(node.createdAt),
category: [node.section.title, ...node.topics.map((topic) => topic.title)],
image: node.image.url,
type: node.type,
slug: node.slug,
}));

const items = await getData(ctx, list);
const items = await getData(list);

return {
title: `AEON | ${data.props.pageProps.section.title}`,
title: `AEON | ${section.title}`,
link: url,
description: data.props.pageProps.section.metaDescription,
description: section.metaDescription,
item: items,
};
}
10 changes: 8 additions & 2 deletions lib/routes/aeon/templates/essay.art
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<img src="{{ banner }}" alt="">
{{ if banner.url }}
<figure>
<img src="{{ banner.url }}" alt="{{ banner.alt }}">
{{ if banner.caption }}
<figcaption>{{ banner.caption }}</figcaption>
{{ /if }}
{{ /if }}
{{@ authorsBio }}
{{@ content}}
{{@ content }}
8 changes: 4 additions & 4 deletions lib/routes/aeon/templates/video.art
Original file line number Diff line number Diff line change
@@ -1,10 +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://player.vimeo.com/video/" + video + "?dnt=1" }}
{{ else if article.hoster === 'youtube' }}
{{ set video = "https://www.youtube-nocookie.com/embed/" + video }}
{{ /if }}

<iframe width="672" height="377" src="{{ video }}" frameborder="0" allowfullscreen></iframe>
{{@ article.credits}}
{{@ article.description}}
{{@ article.credits }}
{{@ article.description }}
45 changes: 28 additions & 17 deletions lib/routes/aeon/type.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { Route } from '@/types';
import { load } from 'cheerio';
import got from '@/utils/got';
import { getData } from './utils';
import ofetch from '@/utils/ofetch';
import { getBuildId, getData } from './utils';
import { parseDate } from '@/utils/parse-date';

export const route: Route = {
path: '/:type',
categories: ['new-media', 'popular'],
example: '/aeon/essays',
parameters: { type: 'Type' },
parameters: {
type: {
description: 'Type',
options: [
{ value: 'essays', label: 'Essays' },
{ value: 'videos', label: 'Videos' },
{ value: 'audio', label: 'Audio' },
],
},
},
features: {
requireConfig: false,
requirePuppeteer: false,
Expand All @@ -18,36 +27,38 @@ export const route: Route = {
},
radar: [
{
source: ['aeon.aeon.co/:type'],
source: ['aeon.co/:type'],
},
],
name: 'Types',
maintainers: ['emdoe'],
handler,
description: `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.`,
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.`,
};

async function handler(ctx) {
const type = ctx.req.param('type');
const binaryType = type === 'videos' ? 'videos' : 'essays';
const capitalizedType = type.charAt(0).toUpperCase() + type.slice(1);

const buildId = await getBuildId();
const url = `https://aeon.co/${type}`;
const { data: response } = await got(url);
const $ = load(response);

const data = JSON.parse($('script#__NEXT_DATA__').text());
const response = await ofetch(`https://aeon.co/_next/data/${buildId}/${type}.json`);

const list = data.props.pageProps.articles.map((item) => ({
title: item.title,
link: `https://aeon.co/${binaryType}/${item.slug}`,
const list = response.pageProps.articles.map((node) => ({
title: node.title,
description: node.standfirstLong,
author: node.authors.map((author) => author.displayName).join(', '),
link: `https://aeon.co/${node.type}s/${node.slug}`,
pubDate: parseDate(node.createdAt),
category: [node.section.title, ...node.topics.map((topic) => topic.title)],
image: node.image.url,
type: node.type,
slug: node.slug,
}));

const items = await getData(ctx, list);
const items = await getData(list);

return {
title: `AEON | ${capitalizedType}`,
Expand Down
64 changes: 47 additions & 17 deletions lib/routes/aeon/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,73 @@ const __dirname = getCurrentPath(import.meta.url);

import cache from '@/utils/cache';
import { load } from 'cheerio';
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';

const getData = async (ctx, list) => {
export const getBuildId = () =>
cache.tryGet(
'aeon:buildId',
async () => {
const response = await ofetch('https://aeon.co');
const $ = load(response);
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
return nextData.buildId;
},
config.cache.routeExpire,
false
);

const getData = async (list) => {
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = load(response);
const buildId = await getBuildId();
const response = await ofetch(`https://aeon.co/_next/data/${buildId}/${item.type}s/${item.slug}.json?id=${item.slug}`);

const data = JSON.parse($('script#__NEXT_DATA__').text());
const type = data.props.pageProps.article.type.toLowerCase();
const data = response.pageProps.article;
const type = data.type.toLowerCase();

item.pubDate = new Date(data.props.pageProps.article.publishedAt).toUTCString();
item.pubDate = parseDate(data.publishedAt);

if (type === 'video') {
item.description = art(path.join(__dirname, 'templates/video.art'), { article: data.props.pageProps.article });
item.description = art(path.join(__dirname, 'templates/video.art'), { article: data });
} else {
// Essay or Audio
// But unfortunately, the method based on __NEXT_DATA__
// does not include the information of the audio link.
if (data.audio?.id) {
const response = await ofetch('https://api.aeonmedia.co/graphql', {
method: 'POST',
body: {
query: `query getAudio($audioId: ID!) {
audio(id: $audioId) {
id
streamUrl
}
}`,
variables: {
audioId: data.audio.id,
},
operationName: 'getAudio',
},
});

delete item.image;
item.enclosure_url = response.data.audio.streamUrl;
item.enclosure_type = 'audio/mpeg';
}

// 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.name).join(', ');

const article = data.props.pageProps.article;
const capture = load(article.body);
const banner = article.image?.url;
const capture = load(data.body, null, false);
const banner = data.image;
capture('p.pullquote').remove();

const authorsBio = article.authors.map((author) => '<p>' + author.name + author.authorBio.replaceAll(/^<p>/g, ' ')).join('');
const authorsBio = data.authors.map((author) => '<p>' + author.name + author.authorBio.replaceAll(/^<p>/g, ' ')).join('');

item.description = art(path.join(__dirname, 'templates/essay.art'), { banner, authorsBio, content: capture.html() });
}
Expand Down

0 comments on commit 13696b9

Please sign in to comment.