fix(route/rfa): use embedded data (#22479)
This commit is contained in:
parent
cb0791cb41
commit
5a50c8fa0e
|
|
@ -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 `<p>${element.content}</p>`;
|
||||
case 'header':
|
||||
return `<h${element.level ?? 2}>${element.content}</h${element.level ?? 2}>`;
|
||||
case 'divider':
|
||||
return '<hr>';
|
||||
case 'image':
|
||||
return `<figure><img src="${element.url}" alt="${element.alt_text ?? ''}">${element.caption ? `<figcaption>${element.caption}</figcaption>` : ''}</figure>`;
|
||||
case 'video': {
|
||||
const stream = element.streams?.find((s) => s.stream_type === 'mp4');
|
||||
return stream ? `<figure><video controls src="${stream.url}" poster="${element.promo_image?.url ?? ''}"></video>${element.headlines?.basic ? `<figcaption>${element.headlines.basic}</figcaption>` : ''}</figure>` : '';
|
||||
}
|
||||
case 'oembed_response':
|
||||
return element.raw_oembed?.html ?? '';
|
||||
case 'list': {
|
||||
const tag = element.list_type === 'ordered' ? 'ol' : 'ul';
|
||||
return `<${tag}>${element.items.map((el) => `<li>${el.content}</li>`).join('')}</${tag}>`;
|
||||
}
|
||||
case 'gallery':
|
||||
return element.content_elements.map((el) => renderElement(el)).join('');
|
||||
case 'quote':
|
||||
return `<blockquote>${element.content_elements.map((el) => renderElement(el)).join('')}${element.citation?.content ? `<cite>${element.citation.content}</cite>` : ''}</blockquote>`;
|
||||
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<string, any>(
|
||||
Object.values<any>(contentCache['content-api-collections'] ?? {})
|
||||
.flatMap((entry) => entry.data?.content_elements ?? [])
|
||||
.map((story) => [story._id, story])
|
||||
)
|
||||
.values()
|
||||
.toArray()
|
||||
.map((story) => {
|
||||
const website = Object.values<any>(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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue