rsshub
Version:
Make RSS Great Again!
277 lines (246 loc) • 8.92 kB
text/typescript
import { type Cheerio, type CheerioAPI, load } from 'cheerio';
import type { Element } from 'domhandler';
import ofetch from '@/utils/ofetch';
import type { DataItem } from '@/types';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';
import type { LinkData, VideoSetup } from './types';
export function getDataItem(href: string) {
const origin = 'https://www.cfr.org';
const link = `${origin}${href}`;
return cache.tryGet(link, async () => {
const prefix = href?.split('/')[1];
const res = await ofetch(link);
const $ = load(res);
let dataItem: DataItem;
switch (prefix) {
case 'article':
dataItem = parseArticle($);
break;
case 'blog':
dataItem = parseBlog($);
break;
case 'book':
dataItem = parseBook($);
break;
case 'conference-calls':
dataItem = parseConferenceCalls($);
break;
case 'event':
dataItem = parseEvent($);
break;
case 'backgrounder':
dataItem = parseBackgrounder($);
break;
case 'podcasts':
dataItem = parsePodcasts($);
break;
case 'task-force-report':
dataItem = parseTaskForceReport($);
break;
case 'timeline':
dataItem = parseTimeline($);
break;
case 'video':
dataItem = parseVideo($);
break;
default:
dataItem = parseDefault($);
}
return {
...dataItem,
link,
};
}) as Promise<DataItem>;
}
function parseArticle($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.body-content'), $);
const $articleHeader = $('.article-header__image');
if ($articleHeader.length) {
description = `<figure>${$articleHeader.html()}</figure><br>${description}`;
}
return {
title: linkData?.title ?? $('.article-header__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseBlog($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.body-content'), $);
const figure = $('.article-header-blog__figure');
if (figure.length) {
description = `<figure>${figure.html()}</figure><br>${description}`;
}
return {
title: linkData?.title ?? $('.article-header-blog__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseBook($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.body-content'), $);
const sectionTop = $('.article-header__section-top');
description = `${sectionTop.html()}<br>${description}`;
return {
title: linkData?.title ?? $('.article-header__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseConferenceCalls($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
const description = parseDescription($('.podcast-body').last(), $);
return {
title: linkData?.title ?? $('head title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseEvent($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.body-content'), $);
const videoIfame = getVideoIframe($('.msp-event-video'));
if (videoIfame) {
description = `${videoIfame}<br>${description}`;
}
return {
title: linkData?.title ?? $('.msp-event-header-past__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseBackgrounder($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.main-wrapper__article-body .body-content'), $);
const summary = $('.main-wrapper__article-body .summary').html();
if (summary) {
description = `${summary}<br>${description}`;
}
const figure = $('.article-header-backgrounder__figure');
if (figure.length) {
description = `<figure>${figure.html()}</figure><br>${description}`;
}
return {
title: linkData?.title ?? $('.article-header-backgrounder__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parsePodcasts($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = $('.body-content').first().html() ?? '';
const audioSrc = $('#player-default').attr('src');
if (audioSrc) {
description = `<audio controls src="${audioSrc}"></audio><br>${description}`;
}
return {
title: linkData?.title ?? $('head title').text(),
pubDate: linkData?.pubDate,
description,
enclosure_url: audioSrc,
enclosure_type: 'audio/mpeg',
};
}
function parseTaskForceReport($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = '';
$('.main-content').each((_, ele) => {
const $ele = $(ele);
const content = $ele.find('.content_area').html() ?? '';
description += `${content}<br>`;
});
return {
title: linkData?.title ?? $('.hero__title').remove('.subtitle').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseTimeline($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
const $description = $('.timeline-slides');
$description.find('.timeline-slide__shadow').remove();
$description.find('.field--image').each((_, ele) => {
$(ele).replaceWith($(ele).find('img'));
});
let description = $description.find('.timeline-intro__description').html() ?? '';
for (const item of $description.find('.timeline-slide__content').toArray()) {
const $item = $(item);
$item.find('.timeline-slide__dates-header').replaceWith('<h1>' + $item.find('.timeline-slide__dates-header').text() + '</h1>');
$item.find('.timeline-slide__dates').replaceWith('<h2>' + $item.find('.timeline-slide__dates').text() + '</h2>');
description += `<br>${$item.html()}`;
}
return {
title: linkData?.title ?? $('.timeline-header__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseVideo($: CheerioAPI): DataItem {
const linkData = parseLinkData($);
let description = parseDescription($('.body-content'), $);
const $articleHeader = $('.article-header__image');
const videoIfame = getVideoIframe($articleHeader);
if (videoIfame) {
description = `${videoIfame}<br>${description}`;
}
return {
title: linkData?.title ?? $('.article-header__title').text(),
pubDate: linkData?.pubDate,
description,
};
}
function parseDefault($): DataItem {
if ($('.body-content').length) {
return parseArticle($);
}
const linkData = parseLinkData($);
return {
title: linkData?.title ?? $('head title').text(),
pubDate: linkData?.pubDate,
};
}
function parseLinkData($: CheerioAPI) {
try {
const data = (<LinkData>JSON.parse($('script[type="application/ld+json"]').text()))['@graph'][0];
return {
title: data.name,
pubDate: parseDate(data.dateModified),
};
} catch {
// ignore
}
}
function getVideoIframe($ele: Cheerio<Element>) {
const setup = $ele.find('video').data('setup') as VideoSetup;
if (setup) {
const youtubeSource = setup.sources.find((i) => i.type === 'video/youtube');
if (youtubeSource) {
const videoId = youtubeSource.src.match(/\?v=([^&]+)/)?.[1];
if (videoId) {
return `<iframe src="https://www.youtube-nocookie.com/embed/${videoId}" width="640" height="360" frameborder="0" allowfullscreen></iframe>`;
}
}
}
}
function parseDescription($description: Cheerio<Element>, $: CheerioAPI) {
$description.find('.desktop-only').remove();
$description.find('.mobile-only').remove();
$description.find('.newsletter-tout').remove();
$description.find('.carousel-gallery').remove();
$description.find('svg').remove();
$description.find('.field--image').each((_, ele) => {
$(ele).replaceWith($(ele).find('img'));
});
$description.find('.video-embed').each((_, ele) => {
const $ele = $(ele);
const videoIframe = getVideoIframe($ele);
if (videoIframe) {
$ele.replaceWith(videoIframe);
}
});
const description = $description.html() ?? '';
return description;
}