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.alt_text ?? ''}${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('')}`; + } + 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, }; }