rsshub
Version:
Make RSS Great Again!
147 lines (134 loc) • 5.09 kB
text/typescript
import { Route } from '@/types';
import cache from '@/utils/cache';
import oftech from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
export const route: Route = {
path: '/articles',
name: 'Articles',
url: 'sustainabilitymag.com/articles',
maintainers: ['mintyfrankie'],
categories: ['other'],
example: '/sustainabilitymag/articles',
radar: [
{
source: ['https://sustainabilitymag.com/articles'],
target: '/sustainabilitymag/articles',
},
],
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
handler,
};
const findLargestImgKey = (images) =>
Object.keys(images)
.filter((key) => key.startsWith('inline_free_') || key.startsWith('hero_landscape_'))
.sort((a, b) => Number.parseInt(b.split('_')[2]) - Number.parseInt(a.split('_')[2]))[0];
const renderFigure = (url, caption) => `<figure><img src="${url}" alt="${caption}" /><figcaption>${caption}</figcaption></figure>`;
const render = (widgets) =>
widgets
.map((w) => {
switch (w.type) {
case 'text':
return w.html;
case 'blockquote':
return `<blockquote>${w.html}</blockquote>`;
case 'keyFacts':
return `<div><ul>${w.keyFacts.map((k) => `<li>${k.text}</li>`).join('')}</ul></div>`;
case 'inlineVideo':
return w.provider === 'youtube'
? `<iframe id="ytplayer" type="text/html" width="640" height="360" src="https://www.youtube-nocookie.com/embed/${w.videoId}" frameborder="0" allowfullscreen></iframe>`
: new Error(`Unhandled inlineVideo provider: ${w.provider}`);
case 'inlineImage':
return w.inlineImageImages
.map((image) => {
const i = image.images[findLargestImgKey(image.images)][0];
return renderFigure(i.url, i.caption);
})
.join('');
default:
throw new Error(`Unhandled widget type: ${w.type}`);
}
})
.join('');
async function handler() {
const baseURL = `https://sustainabilitymag.com`;
const feedURL = `${baseURL}/articles`;
const feedLang = 'en';
const feedDescription = 'Sustainability Magazine Articles';
const requestEndpoint = `${baseURL}/graphql`;
const requestBody = JSON.stringify({
query: `query PaginatedQuery($url: String!, $page: Int = 1, $widgetType: String!) {
paginatedWidget(url: $url, widgetType: $widgetType) {
... on SimpleArticleGridWidget {
articles(page: $page) {
results {
_id
headline
fullUrlPath
featured
category
contentType
tags {
tag
}
attribution
subAttribution
sell
images {
thumbnail_widescreen_553 {
url
}
}
}
}
}
}
}`,
operationName: 'PaginatedQuery',
variables: {
widgetType: 'simpleArticleGrid',
page: 1,
url: 'https://sustainabilitymag.com/articles',
},
});
const results = await oftech(requestEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: requestBody,
});
const list = results.data.paginatedWidget.articles.results.map((item) => ({
title: item.headline,
link: `${baseURL}${item.fullUrlPath}`,
image: item.images?.thumbnail_widescreen_553?.url,
category: item.category,
}));
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const response = await oftech(item.link);
const $ = load(response);
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
const article = nextData.props.pageProps.article;
item.pubDate = parseDate(article.displayDate);
item.author = article.author.name;
const heroImage = article.images[findLargestImgKey(article.images)][0];
item.description = renderFigure(heroImage.url, heroImage.caption) + render(article.body.widgets);
return item;
})
)
);
return {
title: 'Sustainability Magazine Articles',
language: feedLang,
description: feedDescription,
link: `https://${feedURL}`,
item: items,
};
}