diff --git a/lib/routes/rfa/index.ts b/lib/routes/rfa/index.ts
index b437a8a75..8b80259b7 100644
--- a/lib/routes/rfa/index.ts
+++ b/lib/routes/rfa/index.ts
@@ -1,8 +1,9 @@
import { load } from 'cheerio';
+import type { Context } from 'hono';
import type { Route } from '@/types';
import cache from '@/utils/cache';
-import got from '@/utils/got';
+import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
export const route: Route = {
@@ -25,56 +26,96 @@ export const route: Route = {
Parameters can be obtained from the official website, for instance:
-\`https://www.rfa.org/cantonese/news\` corresponds to \`/rfa/cantonese/news\`
+\`https://www.rfa.org/cantonese\` corresponds to \`/rfa/cantonese\`
-\`https://www.rfa.org/cantonese/news/htm\` corresponds to \`/rfa/cantonese/news/htm\``,
+\`https://www.rfa.org/cantonese/htm\` corresponds to \`/rfa/cantonese/htm\``,
};
-async function handler(ctx) {
- let url = 'https://www.rfa.org/' + (ctx.req.param('language') ?? 'english');
-
- if (ctx.req.param('channel')) {
- url += '/' + ctx.req.param('channel');
- }
- if (ctx.req.param('subChannel')) {
- url += '/' + ctx.req.param('subChannel');
+const renderElement = (element: any) => {
+ switch (element.type) {
+ case 'text':
+ return `
${element.content}
`;
+ case 'header':
+ return `${element.content}`;
+ case 'divider':
+ return '
';
+ case 'image':
+ return `
${element.caption ? `${element.caption}` : ''}`;
+ case 'video': {
+ const stream = element.streams?.find((s) => s.stream_type === 'mp4');
+ return stream ? `${element.headlines?.basic ? `${element.headlines.basic}` : ''}` : '';
+ }
+ case 'oembed_response':
+ return element.raw_oembed?.html ?? '';
+ case 'list': {
+ const tag = element.list_type === 'ordered' ? 'ol' : 'ul';
+ return `<${tag}>${element.items.map((el) => `${el.content}`).join('')}${tag}>`;
+ }
+ case 'gallery':
+ return element.content_elements.map((el) => renderElement(el)).join('');
+ case 'quote':
+ return `${element.content_elements.map((el) => renderElement(el)).join('')}${element.citation?.content ? `${element.citation.content}` : ''}
`;
+ case 'raw_html':
+ return element.content;
+ case 'custom_embed':
+ return '';
+ default:
+ throw new Error(`Unsupported element type: ${element.type}`);
}
+};
- const response = await got(url);
- const $ = load(response.data);
+async function handler(ctx: Context) {
+ const { language = 'english', channel, subChannel } = ctx.req.param();
+ const baseUrl = 'https://www.rfa.org';
+ const link = `${baseUrl}/${[language, channel, subChannel].filter(Boolean).join('/')}/`;
- const selectors = ['div[id=topstorywidefull]', 'div.two_featured', 'div.three_featured', 'div.single_column_teaser', 'div.sectionteaser', 'div.specialwrap'];
- const list = [];
- for (const selector of selectors) {
- $(selector).each((_, e) => {
- const item = {
- title: $(e).find('h2 a span').first().text(),
- link: $(e).find('h2 a').first().attr('href'),
+ const response = await ofetch(link);
+ const $ = load(response);
+ const contentCache = JSON.parse(response.match(/Fusion\.contentCache=(\{.*?\});Fusion\.layout/)?.[1] ?? '{}');
+
+ const list = new Map(
+ Object.values(contentCache['content-api-collections'] ?? {})
+ .flatMap((entry) => entry.data?.content_elements ?? [])
+ .map((story) => [story._id, story])
+ )
+ .values()
+ .toArray()
+ .map((story) => {
+ const website = Object.values(story.websites)[0];
+ return {
+ title: story.headlines.basic,
+ description: story.description.basic,
+ link: new URL(website.website_url, baseUrl).href,
+ pubDate: parseDate(story.display_date),
+ author: story.credits?.by?.map((author) => author.name).join(', '),
+ category: website.website_section?.name,
+ image: story.promo_items.basic.url,
};
- list.push(item);
});
- }
- const result = await Promise.all(
+ const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
- const content = await got(item.link);
+ const response = await ofetch(item.link);
+ const globalContent = JSON.parse(response.match(/Fusion\.globalContent=(\{.*?\});Fusion\.globalContentConfig/)?.[1] ?? '{}');
- const description = load(content.data);
- item.description = description('#headerimg').html() + description('div[id=storytext]').html();
- item.pubDate = parseDate(description('span[id=story_date]').text());
- if (description('meta[property=og:audio]').attr('content') !== undefined) {
- item.enclosure_url = description('meta[property=og:audio]').attr('content');
- item.enclosure_type = description('meta[property=og:audio:type]').attr('content');
+ if (globalContent.content_elements) {
+ item.description = [globalContent.promo_items?.basic, ...globalContent.content_elements]
+ .filter(Boolean)
+ .map((element) => renderElement(element))
+ .join('');
}
+ item.category = [...new Set([item.category, ...(globalContent.taxonomy?.sections?.map((section) => section.name) ?? []), ...(globalContent.taxonomy?.tags?.map((tag) => tag.text) ?? [])].filter(Boolean))];
return item;
})
)
);
return {
- title: 'RFA',
- link: 'https://www.rfa.org/',
- item: result,
+ title: $('head title').text(),
+ link,
+ image: $('meta[name="og:image"]').attr('content'),
+ language: $('html').attr('lang'),
+ item: items,
};
}