RSSHub/lib/routes/skeb/utils.tsx

174 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { renderToString } from 'hono/jsx/dom/server';
import { config } from '@/config';
import type { DataItem } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
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: Array<{ 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 = renderToString(<SkebWorkDescription imageUrl={imageUrl} body={body} audioUrl={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 = renderToString(<SkebCreatorDescription avatarUrl={avatarUrl} acceptingCommissions={acceptingCommissions} nsfwAcceptable={nsfwAcceptable} skills={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[];
}
const SkebWorkDescription = ({ imageUrl, body, audioUrl }: { imageUrl?: string; body: string; audioUrl?: string | null }) => (
<>
{imageUrl ? (
<>
<img src={imageUrl} />
<br />
</>
) : null}
{audioUrl ? (
<>
<audio controls>
<source src={audioUrl} type="audio/mp3" />
Your browser does not support the audio element.
</audio>
<br />
</>
) : null}
{body}
</>
);
const SkebCreatorDescription = ({ avatarUrl, acceptingCommissions, nsfwAcceptable, skills }: { avatarUrl?: string; acceptingCommissions: string; nsfwAcceptable: string; skills?: string }) => (
<>
{avatarUrl ? <img src={avatarUrl} /> : null}
<p>Accepting Commissions{acceptingCommissions}</p>
<p>NSFW{nsfwAcceptable}</p>
{skills ? <p>Genre{skills}</p> : null}
</>
);