From eb3d0d42e36a86df33e976c2ea424986e1663f53 Mon Sep 17 00:00:00 2001 From: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:25:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(gcores):=20=E6=9C=BA=E6=A0=B8=E7=BD=91?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=20(#18304)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/routes/gcores/tag.ts | 158 -------------------------------------- lib/routes/gcores/tags.ts | 129 +++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 158 deletions(-) delete mode 100644 lib/routes/gcores/tag.ts create mode 100644 lib/routes/gcores/tags.ts diff --git a/lib/routes/gcores/tag.ts b/lib/routes/gcores/tag.ts deleted file mode 100644 index 8d0d8acb4f6f6c..00000000000000 --- a/lib/routes/gcores/tag.ts +++ /dev/null @@ -1,158 +0,0 @@ -import InvalidParameterError from '@/errors/types/invalid-parameter'; -import { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; -import { load } from 'cheerio'; - -export const route: Route = { - path: '/tag/:tag/:category?', - categories: ['new-media', 'popular'], - example: '/gcores/tag/42/articles', - parameters: { tag: '标签名,可在选定标签分类页面的 URL 中找到,如视觉动物——42', category: '分类名' }, - features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, - supportBT: false, - supportPodcast: false, - supportScihub: false, - }, - radar: [ - { - source: ['gcores.com/categories/:tag', 'gcores.com/'], - target: '/tag/:tag', - }, - ], - name: '标签', - maintainers: ['StevenRCE0'], - handler, - description: `分类名同上。`, -}; - -async function handler(ctx) { - const tag = ctx.req.param('tag'); - const category = ctx.req.param('category'); - const url = `https://www.gcores.com/categories/${tag + (category ? `?tab=${category}` : '')}`; - const res = await got({ - method: 'get', - url, - }); - const data = res.data; - const $ = load(data); - const feedTitle = $('title').text(); - - const list = $('.original.am_card.original-normal') - .map(function () { - const item = { - url: $(this).find('.am_card_inner>a').attr('href'), - title: $(this).find('h3.am_card_title').text(), - category: $(this).find('span.original_category>a').text(), - }; - return item; - }) - .get(); - - if (list.length > 0 && list.every((item) => item.url === undefined)) { - throw new InvalidParameterError('Article URL not found! Please submit an issue on GitHub.'); - } - - const out = await Promise.all( - list.map((item) => { - const articleUrl = `https://www.gcores.com${item.url}`; - - return cache.tryGet(articleUrl, async () => { - const itemRes = await got({ - method: 'get', - url: articleUrl, - }); - - const itemPage = itemRes.data; - const $ = load(itemPage); - - let articleData = await got(`https://www.gcores.com/gapi/v1${item.url}?include=media`); - - articleData = articleData.data.data; - let cover; - if (articleData.attributes.cover) { - cover = ``; - } else if (articleData.attributes.thumb) { - cover = ``; - } else { - cover = ''; - } - - // replace figure with img - const articleContent = JSON.parse(articleData.attributes.content); - const entityRangeMap = {}; - for (const block of articleContent.blocks || []) { - if (block.entityRanges.length) { - entityRangeMap[block.key] = block.entityRanges; - } - } - - $('figure').each((i, elem) => { - const keyAttr = elem.attribs['data-offset-key']; - const keyMatch = /^(\w+)-(\d+)-(\d)$/.exec(keyAttr); - let actualContent = ''; - if (keyMatch) { - const [, key, index] = keyMatch; - if (entityRangeMap[key] && entityRangeMap[key][index]) { - const entityKey = entityRangeMap[key] && entityRangeMap[key][index].key; - const entity = articleContent.entityMap[entityKey]; - actualContent = convertEntityToContent(entity); - } - } - - if (actualContent) { - $(elem).replaceWith(actualContent); - } - }); - - // remove editor toolbar img - $('.md-editor-toolbar').replaceWith(''); - // remove hidden tip block - $('.story_hidden').replaceWith(''); - - const content = $('.story.story-show').html(); - return { - title: item.title, - description: cover + content, - link: articleUrl, - guid: articleUrl, - }; - }); - }) - ); - return { - title: feedTitle, - link: url, - item: out, - }; -} - -function convertEntityToContent(entity) { - const { type, data } = entity; - switch (type) { - case 'IMAGE': - return ` -
-${data.caption || ''} -${data.caption ? `
${data.caption}
` : ''} -
`; - - case 'GALLERY': - return data.images - .map( - (image, i, arr) => ` -
-${image.caption || ''} -
${data.caption || ''} (${i + 1}/${arr.length}) ${image.caption || ''}
-
- ` - ) - .join(''); - - default: - return ''; - } -} diff --git a/lib/routes/gcores/tags.ts b/lib/routes/gcores/tags.ts new file mode 100644 index 00000000000000..155690c875e217 --- /dev/null +++ b/lib/routes/gcores/tags.ts @@ -0,0 +1,129 @@ +import { type Data, type Route, ViewType } from '@/types'; + +import { getCurrentPath } from '@/utils/helpers'; +import { type Context } from 'hono'; + +import { baseUrl, processItems } from './util'; + +export const __dirname = getCurrentPath(import.meta.url); + +let viewType: ViewType = ViewType.Articles; + +export const handler = async (ctx: Context): Promise => { + const { id, tab } = ctx.req.param(); + const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10); + + const targetUrl: string = new URL(`tags/${id}/originals${tab ? `?tab=${tab}` : ''}`, baseUrl).href; + const apiUrl: string = new URL(`gapi/v1/tags/${id}/${tab ?? 'originals'}`, baseUrl).href; + + const query = { + 'page[limit]': limit, + sort: '-published-at', + include: 'category,user,media', + 'filter[list-all]': 1, + 'filter[is-news]': tab === 'news' ? 1 : 0, + }; + + if (tab === 'radios') { + viewType = ViewType.Audios; + } else if (tab === 'videos') { + viewType = ViewType.Videos; + } + + return await processItems(limit, query, apiUrl, targetUrl); +}; + +export const route: Route = { + path: '/tags/:id/:tab?', + name: '标签', + url: 'www.gcores.com', + maintainers: ['StevenRCE0', 'nczitzk'], + handler, + example: '/gcores/tags/1/articles', + parameters: { + id: { + description: '标签 ID,可在对应标签页 URL 中找到', + }, + tab: { + description: '类型,默认为空,即全部,可在对应标签页 URL 中找到', + options: [ + { + label: '全部', + value: '', + }, + { + label: '播客', + value: 'radios', + }, + { + label: '文章', + value: 'articles', + }, + { + label: '资讯', + value: 'news', + }, + { + label: '视频', + value: 'videos', + }, + ], + }, + }, + description: `:::tip +若订阅 [美国 - 文章](https://www.gcores.com/tags/1/originals?tab=articles),网址为 \`https://www.gcores.com/tags/1/originals?tab=articles\`,请截取 \`https://www.gcores.com/tags/\` 到末尾 \`/originals\` 的部分 \`1\` 作为 \`id\` 参数填入,截取 \`articles\` 作为 \`tab\` 参数填入,此时目标路由为 [\`/gcores/tags/1/articles\`](https://rsshub.app/gcores/tags/1/articles)。 +::: + +| 全部 | 播客 | 文章 | 资讯 | 视频 | +| ---- | ------ | -------- | ---- | ------ | +| | radios | articles | news | videos | +`, + categories: ['game'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.gcores.com/tags/:id/originals'], + target: (params, url) => { + const urlObj: URL = new URL(url); + const id: string = params.id; + const tab: string | undefined = urlObj.searchParams.get('tab') ?? undefined; + + return `/gcores/tags/${id}/${tab ? `/${tab}` : ''}`; + }, + }, + { + title: '全部', + source: ['www.gcores.com/tags/:id/originals'], + target: '/gcores/tags/:id', + }, + { + title: '播客', + source: ['www.gcores.com/tags/:id/originals'], + target: '/gcores/tags/:id/radios', + }, + { + title: '文章', + source: ['www.gcores.com/tags/:id/originals'], + target: '/gcores/tags/:id/articles', + }, + { + title: '资讯', + source: ['www.gcores.com/tags/:id/originals'], + target: '/gcores/tags/:id/news', + }, + { + title: '视频', + source: ['www.gcores.com/tags/:id/originals'], + target: '/gcores/tags/:id/videos', + }, + ], + view: viewType, +};