UNPKG

rsshub

Version:
121 lines (108 loc) 4.38 kB
import { Route, ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import crypto from 'node:crypto'; import path from 'node:path'; import { art } from '@/utils/render'; import { getAppreciatedQuery, getProfileProjectsAndSelectionsQuery, getProjectPageQuery } from './queries'; export const route: Route = { path: '/:user/:type?', categories: ['design'], view: ViewType.Pictures, example: '/behance/mishapetrick', parameters: { user: 'username', type: { description: 'type', options: [ { value: 'projects', label: 'projects' }, { value: 'appreciated', label: 'appreciated' }, ], default: 'projects', }, }, features: { requireConfig: false, requirePuppeteer: false, antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, }, name: 'User Works', maintainers: ['MisteryMonster'], handler, description: `Behance user's profile URL, like [https://www.behance.net/mishapetrick](https://www.behance.net/mishapetrick) the username will be \`mishapetrick\`。`, }; const getUserProfile = async (nodes, user) => (await cache.tryGet(`behance:profile:${user}`, () => { const profile = nodes.flatMap((item) => item.owners).find((owner) => owner.username === user); return Promise.resolve({ displayName: profile.displayName, id: profile.id, link: profile.url, image: profile.images.size_50.url.replace('/user/50/', '/user/source/'), }); })) as { displayName: string; id: string; link: string; image: string }; async function handler(ctx) { const { user, type = 'projects' } = ctx.req.param(); const uuid = crypto.randomUUID(); const headers = { Cookie: `gk_suid=${Math.random().toString().slice(2, 10)}, gki=; originalReferrer=; bcp=${uuid}`, 'X-BCP': uuid, 'X-Requested-With': 'XMLHttpRequest', }; const response = await ofetch('https://www.behance.net/v3/graphql', { method: 'POST', headers, body: { query: type === 'projects' ? getProfileProjectsAndSelectionsQuery : getAppreciatedQuery, variables: { username: user, after: '', }, }, }); const nodes = type === 'projects' ? response.data.user.profileProjects.nodes : response.data.user.appreciatedProjects.nodes; const list = nodes.map((item) => ({ title: item.name, link: item.url, author: item.owners.map((owner) => owner.displayName).join(', '), image: item.covers.size_202.url.replace('/202/', '/source/'), pubDate: item.publishedOn ? parseDate(item.publishedOn, 'X') : undefined, category: item.fields?.map((field) => field.label.toLowerCase()), projectId: item.id, })); const profile = await getUserProfile(nodes, user); const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { const response = await ofetch('https://www.behance.net/v3/graphql', { method: 'POST', headers, body: { query: getProjectPageQuery, variables: { projectId: item.projectId, }, }, }); const project = response.data.project; item.description = art(path.join(__dirname, 'templates/description.art'), { description: project.description, modules: project.allModules, }); item.category = [...new Set([...(item.category || []), ...(project.tags?.map((tag) => tag.title.toLowerCase()) || [])])]; item.pubDate = item.pubDate || (project.publishedOn ? parseDate(project.publishedOn, 'X') : undefined); return item; }) ) ); return { title: `${profile.displayName}'s ${type}`, link: `https://www.behance.net/${user}/${type}`, image: profile.image, item: items, }; }