From dbf420e727f6d88b564f862418d19fc2bcac266b Mon Sep 17 00:00:00 2001 From: AiraNadih <128119996+AiraNadih@users.noreply.github.com> Date: Mon, 27 Jan 2025 04:56:32 +0800 Subject: [PATCH] feat(route): add route "4KUP" (#18218) * feat(route): add route "4KUP" * perf(route/4kup): optimize popular posts retrieval --- lib/routes/4kup/article.ts | 29 +++++++++++++ lib/routes/4kup/category.ts | 47 +++++++++++++++++++++ lib/routes/4kup/const.ts | 4 ++ lib/routes/4kup/latest.ts | 41 ++++++++++++++++++ lib/routes/4kup/namespace.ts | 8 ++++ lib/routes/4kup/popular.ts | 81 ++++++++++++++++++++++++++++++++++++ lib/routes/4kup/tag.ts | 47 +++++++++++++++++++++ lib/routes/4kup/types.ts | 12 ++++++ 8 files changed, 269 insertions(+) create mode 100644 lib/routes/4kup/article.ts create mode 100644 lib/routes/4kup/category.ts create mode 100644 lib/routes/4kup/const.ts create mode 100644 lib/routes/4kup/latest.ts create mode 100644 lib/routes/4kup/namespace.ts create mode 100644 lib/routes/4kup/popular.ts create mode 100644 lib/routes/4kup/tag.ts create mode 100644 lib/routes/4kup/types.ts diff --git a/lib/routes/4kup/article.ts b/lib/routes/4kup/article.ts new file mode 100644 index 000000000..fbdbbbb52 --- /dev/null +++ b/lib/routes/4kup/article.ts @@ -0,0 +1,29 @@ +import { load } from 'cheerio'; +import { parseDate } from '@/utils/parse-date'; +import { WPPost } from './types'; + +const processLazyImages = ($) => { + $('a.thumb-photo').each((_, elem) => { + const $elem = $(elem); + const largePhotoUrl = $elem.attr('href'); + if (largePhotoUrl) { + $elem.find('img').attr('src', largePhotoUrl); + } + }); + + $('.caption').remove(); +}; + +function loadArticle(item: WPPost) { + const article = load(item.content.rendered); + processLazyImages(article); + + return { + title: item.title.rendered, + description: article.html() ?? '', + pubDate: parseDate(item.date_gmt), + link: item.link, + }; +} + +export default loadArticle; diff --git a/lib/routes/4kup/category.ts b/lib/routes/4kup/category.ts new file mode 100644 index 000000000..9b227ec32 --- /dev/null +++ b/lib/routes/4kup/category.ts @@ -0,0 +1,47 @@ +import { Route } from '@/types'; +import got from '@/utils/got'; +import { SUB_NAME_PREFIX, SUB_URL } from './const'; +import loadArticle from './article'; +import { WPPost } from './types'; + +export const route: Route = { + path: '/category/:category', + categories: ['picture'], + example: '/4kup/category/coser', + parameters: { category: 'Category' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['4kup.net/category/:category'], + target: '/category/:category', + }, + ], + name: 'Category', + maintainers: ['AiraNadih'], + handler, + url: '4kup.net/', +}; + +async function handler(ctx) { + const limit = Number.parseInt(ctx.req.query('limit')) || 20; + const category = ctx.req.param('category'); + const categoryUrl = `${SUB_URL}category/${category}/`; + + const { + data: [{ id: categoryId }], + } = await got(`${SUB_URL}wp-json/wp/v2/categories?slug=${category}`); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?categories=${categoryId}&per_page=${limit}`); + + return { + title: `${SUB_NAME_PREFIX} - Category: ${category}`, + link: categoryUrl, + item: posts.map((post) => loadArticle(post as WPPost)), + }; +} diff --git a/lib/routes/4kup/const.ts b/lib/routes/4kup/const.ts new file mode 100644 index 000000000..52c67eacb --- /dev/null +++ b/lib/routes/4kup/const.ts @@ -0,0 +1,4 @@ +const SUB_NAME_PREFIX = '4KUP'; +const SUB_URL = 'https://4kup.net/'; + +export { SUB_NAME_PREFIX, SUB_URL }; diff --git a/lib/routes/4kup/latest.ts b/lib/routes/4kup/latest.ts new file mode 100644 index 000000000..12da303c6 --- /dev/null +++ b/lib/routes/4kup/latest.ts @@ -0,0 +1,41 @@ +import { Route } from '@/types'; +import got from '@/utils/got'; +import { SUB_NAME_PREFIX, SUB_URL } from './const'; +import loadArticle from './article'; +import { WPPost } from './types'; + +export const route: Route = { + path: '/', + categories: ['picture'], + example: '/4kup', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['4kup.net/'], + target: '', + }, + ], + name: 'Latest', + maintainers: ['AiraNadih'], + handler, + url: '4kup.net/', +}; + +async function handler(ctx) { + const limit = Number.parseInt(ctx.req.query('limit')) || 20; + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?per_page=${limit}`); + + return { + title: `${SUB_NAME_PREFIX} - Latest`, + link: SUB_URL, + item: posts.map((post) => loadArticle(post as WPPost)), + }; +} diff --git a/lib/routes/4kup/namespace.ts b/lib/routes/4kup/namespace.ts new file mode 100644 index 000000000..41b591a0d --- /dev/null +++ b/lib/routes/4kup/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '4KUP', + url: '4kup.net', + description: '4KUP - Beautiful Girls Collection', + lang: 'en', +}; diff --git a/lib/routes/4kup/popular.ts b/lib/routes/4kup/popular.ts new file mode 100644 index 000000000..326673d1b --- /dev/null +++ b/lib/routes/4kup/popular.ts @@ -0,0 +1,81 @@ +import { Route } from '@/types'; +import got from '@/utils/got'; +import { load } from 'cheerio'; +import { SUB_NAME_PREFIX, SUB_URL } from './const'; +import loadArticle from './article'; +import { WPPost } from './types'; + +export const route: Route = { + path: '/popular/:period', + categories: ['picture'], + example: '/4kup/popular/7', + parameters: { period: 'Days' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['4kup.net/:period'], + target: '/popular/:period', + }, + ], + name: 'Popular', + maintainers: ['AiraNadih'], + handler, + url: '4kup.net/', +}; + +function getPeriodConfig(period) { + if (period === '7') { + return { + url: `${SUB_URL}hot-of-week/`, + range: 'last7days', + title: `${SUB_NAME_PREFIX} - Top views in 7 days`, + }; + } else if (period === '30') { + return { + url: `${SUB_URL}hot-of-month/`, + range: 'last30days', + title: `${SUB_NAME_PREFIX} - Top views in 30 days`, + }; + } + return { + url: `${SUB_URL}most-view/`, + range: `all`, + title: `${SUB_NAME_PREFIX} - Most views`, + }; +} + +async function handler(ctx) { + const limit = Number.parseInt(ctx.req.query('limit')) || 20; + const period = ctx.req.param('period'); + + const { url, range, title } = getPeriodConfig(period); + + const { data } = await got.post(`${SUB_URL}wp-json/wordpress-popular-posts/v2/widget`, { + json: { + limit, + range, + order_by: 'views', + }, + }); + + const $ = load(data.widget); + const links = $('.wpp-list li') + .toArray() + .map((post) => $(post).find('.wpp-post-title').attr('href')) + .filter((link) => link !== undefined); + const slugs = links.map((link) => link.split('/').findLast(Boolean)); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?slug=${slugs.join(',')}&per_page=${limit}`); + + return { + title, + link: url, + item: posts.map((post) => loadArticle(post as WPPost)), + }; +} diff --git a/lib/routes/4kup/tag.ts b/lib/routes/4kup/tag.ts new file mode 100644 index 000000000..93acd7ec9 --- /dev/null +++ b/lib/routes/4kup/tag.ts @@ -0,0 +1,47 @@ +import { Route } from '@/types'; +import got from '@/utils/got'; +import { SUB_NAME_PREFIX, SUB_URL } from './const'; +import loadArticle from './article'; +import { WPPost } from './types'; + +export const route: Route = { + path: '/tag/:tag', + categories: ['picture'], + example: '/4kup/tag/asian', + parameters: { tag: 'Tag' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['4kup.net/tag/:tag'], + target: '/tag/:tag', + }, + ], + name: 'Tag', + maintainers: ['AiraNadih'], + handler, + url: '4kup.net/', +}; + +async function handler(ctx) { + const limit = Number.parseInt(ctx.req.query('limit')) || 20; + const tag = ctx.req.param('tag'); + const tagUrl = `${SUB_URL}tag/${tag}/`; + + const { + data: [{ id: tagId }], + } = await got(`${SUB_URL}wp-json/wp/v2/tags?slug=${tag}`); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?tags=${tagId}&per_page=${limit}`); + + return { + title: `${SUB_NAME_PREFIX} - Tag: ${tag}`, + link: tagUrl, + item: posts.map((post) => loadArticle(post as WPPost)), + }; +} diff --git a/lib/routes/4kup/types.ts b/lib/routes/4kup/types.ts new file mode 100644 index 000000000..d3ea3ac2a --- /dev/null +++ b/lib/routes/4kup/types.ts @@ -0,0 +1,12 @@ +interface WPPost { + title: { + rendered: string; + }; + content: { + rendered: string; + }; + date_gmt: string; + link: string; +} + +export type { WPPost };