rsshub
Version:
Make RSS Great Again!
153 lines (132 loc) • 4.24 kB
text/typescript
import { config } from '@/config';
import { DataItem } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { art } from '@/utils/render';
import path from 'node:path';
export const baseUrl = 'https://skeb.jp';
interface Work {
path: string;
private_thumbnail_image_urls: null | string;
private: boolean;
genre: string;
tipped: boolean;
creator_id: number;
client_id: number;
vtt_url: null | string;
thumbnail_image_urls: {
src: string;
srcset: string;
};
preview_url: null | string;
duration: null | number;
nsfw: boolean;
hardcore: boolean;
consored_thumbnail_image_urls: {
src: string;
srcset: string;
};
body: string;
nc: number;
word_count: number;
transcoder: string;
creator_acceptable_same_genre: boolean;
}
interface Creator {
id: number;
creator: boolean;
nsfw_acceptable: boolean;
acceptable: boolean;
name: string;
screen_name: string;
avatar_url: string;
header_url: string | null;
appeal_receivable: boolean;
popular_creator_rank: number | null;
request_master_rank: number | null;
first_requester_rank: number | null;
deleted_at: string | null;
tip_acceptable_by: string;
accept_expiration_days: number;
skills: { genre: string }[];
nc: number;
}
export function processWork(work: Work): DataItem | null {
if (!work || typeof work !== 'object' || work.private === true) {
return null;
}
const imageUrl = work.thumbnail_image_urls?.srcset?.split(',').pop()?.trim().split(' ')[0] || '';
const body = work.body || '';
const audioUrl = work.genre === 'music' || work.genre === 'voice' ? work.preview_url : null;
const renderedHtml = art(path.join(__dirname, 'templates/work.art'), {
imageUrl,
body,
audioUrl,
});
return {
title: work.path || '',
link: `${baseUrl}${work.path || ''}`,
description: renderedHtml,
};
}
const skillMap = {
art: 'Illust',
voice: 'Voice',
novel: 'Novel',
video: 'Video',
music: 'Music',
correction: 'Advice',
comic: 'Comic',
};
export function processCreator(creator: Creator): DataItem | null {
if (!creator || typeof creator !== 'object') {
return null;
}
const avatarUrl = creator.avatar_url || '';
let renderedHtml;
if (creator.creator) {
const acceptingCommissions = creator.acceptable ? 'Yes' : 'No';
const nsfwAcceptable = creator.nsfw_acceptable ? 'Yes' : 'No';
let skills = '';
if (Array.isArray(creator.skills) && creator.skills.length > 0) {
skills = creator.skills
.map((skill) => skillMap[skill.genre] || skill.genre)
.filter(Boolean)
.join(', ');
}
renderedHtml = art(path.join(__dirname, 'templates/creator.art'), {
avatarUrl,
acceptingCommissions,
nsfwAcceptable,
skills,
});
}
return {
title: creator.name || '',
link: `${baseUrl}/@${creator.screen_name || ''}`,
description: renderedHtml,
};
}
export async function getFollowingsItems(username: string, path: 'friend_works' | 'following_works' | 'following_creators'): Promise<DataItem[]> {
const url = `${baseUrl}/api/users/${username.replace('@', '')}/followings`;
const followings_data = await cache.tryGet(
`skeb:followings_data:${username}`,
async () => {
const data = await ofetch(url, {
headers: {
Authorization: `Bearer ${config.skeb.bearerToken}`,
},
});
return data;
},
config.cache.routeExpire,
false
);
if (!followings_data || typeof followings_data !== 'object') {
throw new Error('Failed to fetch followings data');
}
if (path === 'following_creators') {
return followings_data[path].map((item) => processCreator(item)).filter(Boolean) as DataItem[];
}
return followings_data[path].map((item) => processWork(item)).filter(Boolean) as DataItem[];
}