rsshub
Version:
Make RSS Great Again!
102 lines (93 loc) • 3.91 kB
text/typescript
import { Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';
import path from 'node:path';
import { art } from '@/utils/render';
const render = (data) => art(path.join(__dirname, 'templates/article.art'), data);
export const route: Route = {
path: '/topic/:topic/:language?',
categories: ['traditional-media'],
example: '/vice/topic/politics/en',
parameters: {
topic: 'Can be found in the URL',
language: 'defaults to `en`, use the website to discover other codes',
},
radar: [
{
source: ['www.vice.com/:language/topic/:topic'],
target: '/topic/:topic/:language',
},
],
name: 'Topic',
maintainers: ['K33k0'],
handler,
url: 'vice.com/',
};
async function handler(ctx) {
const { language = 'en', topic } = ctx.req.param();
const response = await ofetch(`https://www.vice.com/${language}/topic/${topic}`);
const $ = load(response);
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
const list = nextData.props.pageProps.listPageData.articles.map((item) => ({
title: item.title,
link: `https://vice.com${item.url}`,
pubDate: parseDate(item.publish_date, 'x'),
author: item.contributions.map((c) => c.contributor.full_name).join(', '),
description: item.dek,
category: [...new Set([item.primary_topic.name, ...item.topics.map((t) => t.name)])],
}));
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const response = await ofetch(item.link);
const $ = load(response);
const articleNextData = JSON.parse($('script#__NEXT_DATA__').text()).props.pageProps.data.articles[0];
const bodyComponent = JSON.parse(articleNextData.body_components_json);
item.description =
render({
image: {
url: articleNextData.thumbnail_url,
alt: articleNextData.caption,
caption: articleNextData.caption,
credit: articleNextData.credit,
},
}) +
bodyComponent
.map((component) => {
switch (component.role) {
case 'body':
return render({ body: { html: component.html } });
case 'heading2':
return render({ heading2: { html: component.html } });
case 'image':
return render({
image: {
url: component.URL,
alt: component.alt,
caption: component.caption,
},
});
case 'oembed':
case 'tweet':
case 'youtube':
return render({ oembed: { html: component.oembed.html } });
default:
return '';
}
})
.join('');
return item;
})
)
);
return {
// channel title
title: `VICE | ${topic} articles`,
// channel link
link: `https://vice.com/${language}/topic/${topic}`,
// each feed item
item: items,
};
}