rsshub
Version:
Make RSS Great Again!
136 lines (112 loc) • 4.54 kB
text/typescript
import { DataItem } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const CONTENT_TYPES = {
doujin: {
title: '.doujin-title',
description: ['.doujin-detail', '.section', '.area-buy > a.btn'],
},
video: {
title: '.video-title',
description: ['.video-data', '.section', '.lp-samplearea a.btn'],
},
article: {
title: '.article_title',
description: ['.article_icatch', '.article_contents'],
},
};
function getContentType(link: string): keyof typeof CONTENT_TYPES {
const typePatterns = {
doujin: ['/cg/', '/comic/', '/voice/'],
video: ['/nipple-video/'],
article: ['/post-'],
};
for (const [type, patterns] of Object.entries(typePatterns)) {
if (patterns.some((pattern) => link.includes(pattern))) {
return type as keyof typeof CONTENT_TYPES;
}
}
throw new Error(`Unknown content type for link: ${link}`);
}
export async function processItems(list): Promise<DataItem[]> {
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const detailResponse = await got(item.link);
const $ = load(detailResponse.data);
const contentType = getContentType(item.link);
const selectors = CONTENT_TYPES[contentType];
const title = $(selectors.title).text().trim() || item.title;
const description = processDescription(selectors.description.map((selector) => $(selector).prop('outerHTML')).join(''));
const pubDateStr = $('meta[property="article:published_time"]').attr('content');
const pubDate = pubDateStr ? parseDate(pubDateStr) : undefined;
return {
title,
description,
link: item.link,
pubDate,
} as DataItem;
})
)
);
return items.filter((item): item is DataItem => item !== null);
}
function processDescription(description: string): string {
const $ = load(description);
return $('body')
.children()
.toArray()
.map((el) => $(el).clone().wrap('<div>').parent().html())
.join('');
}
const WP_REST_API_URL = 'https://chikubi.jp/wp-json/wp/v2';
export async function getPosts(ids?: string[]): Promise<DataItem[]> {
const url = `${WP_REST_API_URL}/posts${ids?.length ? `?include=${ids.join(',')}` : ''}`;
const cachedData = await cache.tryGet(url, async () => {
const response = await got(url);
const data = JSON.parse(response.body);
if (!Array.isArray(data)) {
throw new TypeError('No posts found for the given IDs');
}
return data.map(({ title, link, date, content }) => ({
title: title.rendered,
link,
pubDate: parseDate(date),
description: processDescription(content.rendered),
}));
});
return (Array.isArray(cachedData) ? cachedData : []).filter((item): item is DataItem => item !== null);
}
const API_TYPES = {
tag: 'tags',
category: 'categories',
};
export async function getBySlug<T extends keyof typeof API_TYPES>(type: T, slug: string): Promise<{ id: number; name: string }> {
const url = `${WP_REST_API_URL}/${API_TYPES[type]}?slug=${encodeURIComponent(slug)}`;
const { body } = await got(url);
const data = JSON.parse(body);
if (data?.[0]) {
const { id, name } = data[0];
return { id, name };
}
throw new Error(`No ${type} found for slug: ${slug}`);
}
export async function getPostsBy<T extends keyof typeof API_TYPES>(type: T, id: number): Promise<DataItem[]> {
const url = `${WP_REST_API_URL}/posts?${API_TYPES[type]}=${id}`;
const cachedData = await cache.tryGet(url, async () => {
const { body } = await got(url);
const data = JSON.parse(body);
if (Array.isArray(data) && data.length > 0) {
return data.map(({ title, link, date, content }) => ({
title: title.rendered,
link,
pubDate: parseDate(date),
description: processDescription(content.rendered),
}));
}
return [];
});
return (Array.isArray(cachedData) ? cachedData : []).filter((item): item is DataItem => item !== null);
}