rsshub
Version:
Make RSS Great Again!
174 lines (147 loc) • 6.63 kB
text/typescript
import { type Data, type DataItem } from '@/types';
import { art } from '@/utils/render';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { type CheerioAPI, load } from 'cheerio';
import path from 'node:path';
import { parseContent } from './parser';
const baseUrl: string = 'https://www.gcores.com';
const imageBaseUrl: string = 'https://image.gcores.com';
const audioBaseUrl: string = 'https://alioss.gcores.com';
const types: Set<string> = new Set(['radios', 'articles', 'news', 'videos', 'talks']);
const processItems = async (limit: number, query: any, apiUrl: string, targetUrl: string): Promise<Data> => {
const response = await ofetch(apiUrl, {
query: query ?? {
'page[limit]': limit,
sort: '-published-at',
include: 'category,user,media',
'filter[list-all]': 1,
},
});
const targetResponse = await ofetch(targetUrl);
const $: CheerioAPI = load(targetResponse);
const language = $('html').attr('lang') ?? 'zh-CN';
const included = response.included;
const data = [...response.data, ...included].filter((item) => types.has(item.type));
let items: DataItem[] = [];
items = data?.slice(0, limit).map((item): DataItem => {
const attributes = item.attributes;
const relationships = item.relationships;
const title: string = attributes.title;
const pubDate: number | string = attributes['published-at'];
const linkUrl: string | undefined = `${item.type}/${item.id}`;
const categoryObjs = [relationships?.category?.data, relationships?.tag?.data, relationships?.topic?.data].filter(Boolean);
const categories: string[] = categoryObjs
.map((obj) => {
const attributes = included.find((i) => i.type === obj.type && i.id === obj.id)?.attributes;
return attributes?.name ?? attributes?.title;
})
.filter(Boolean);
const authorObj = relationships?.user?.data;
const authorIncluded = authorObj ? included.find((i) => i.type === authorObj.type && i.id === authorObj.id) : undefined;
const authors: DataItem['author'] = authorIncluded
? [
{
name: authorIncluded.attributes?.nickname,
url: authorIncluded.id ? new URL(`${authorObj.type}/${authorIncluded.id}`, baseUrl).href : undefined,
avatar: authorIncluded.thumb ? new URL(authorIncluded.thumb, imageBaseUrl).href : undefined,
},
]
: undefined;
const guid: string = `gcores-${item.id}`;
const image: string | undefined = (attributes.cover ?? attributes.thumb) ? new URL(attributes.cover ?? attributes.thumb, imageBaseUrl).href : undefined;
const updated: number | string = pubDate;
let processedItem: DataItem = {
title,
pubDate: pubDate ? parseDate(pubDate) : undefined,
link: new URL(linkUrl, baseUrl).href,
category: categories,
author: authors,
guid,
id: guid,
image,
banner: image,
updated: updated ? parseDate(updated) : undefined,
language,
};
let enclosureUrl: string | undefined;
let enclosureType: string | undefined;
const mediaAttrs = included.find((i) => i.id === relationships.media?.data?.id)?.attributes;
if (attributes['speech-path']) {
enclosureUrl = new URL(`uploads/audio/${attributes['speech-path']}`, audioBaseUrl).href;
enclosureType = `audio/${enclosureUrl?.split(/\./).pop()}`;
} else if (mediaAttrs) {
if (mediaAttrs.audio) {
enclosureUrl = mediaAttrs.audio;
enclosureType = `audio/${enclosureUrl?.split(/\./).pop()}`;
} else if (mediaAttrs['original-src']) {
enclosureUrl = mediaAttrs['original-src'];
enclosureType = `video/${enclosureUrl?.split(/\?/).pop() ? (/^id=\d+$/.test(enclosureUrl?.split(/\?/).pop() as string) ? 'taptap' : enclosureUrl?.split(/\./).pop()) : ''}`;
}
}
if (enclosureUrl) {
const enclosureLength: number = attributes.duration ? Number(attributes.duration) : 0;
processedItem = {
...processedItem,
enclosure_url: enclosureUrl,
enclosure_type: enclosureType,
enclosure_title: title,
enclosure_length: enclosureLength,
itunes_duration: enclosureLength,
itunes_item_image: image,
};
}
const description: string = art(path.join(__dirname, 'templates/description.art'), {
images: attributes.cover
? [
{
src: new URL(attributes.cover, imageBaseUrl).href,
alt: title,
},
]
: undefined,
audios:
enclosureType?.startsWith('audio') && enclosureUrl
? [
{
src: enclosureUrl,
type: enclosureType,
},
]
: undefined,
videos:
enclosureType?.startsWith('video') && enclosureUrl
? [
{
src: enclosureUrl,
type: enclosureType,
},
]
: undefined,
intro: attributes.desc || attributes.excerpt,
description: attributes.content ? parseContent(JSON.parse(attributes.content)) : undefined,
});
processedItem = {
...processedItem,
title: title ?? $(description).text(),
description,
content: {
html: description,
text: description,
},
};
return processedItem;
});
const title: string = $('title').text();
return {
title,
description: $('meta[name="description"]').attr('content'),
link: targetUrl,
item: items,
allowEmpty: true,
author: title.split(/\|/).pop()?.trim(),
language,
id: $('meta[property="og:url"]').attr('content'),
};
};
export { baseUrl, imageBaseUrl, audioBaseUrl, processItems };