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}` : ''}
-`;
-
- case 'GALLERY':
- return data.images
- .map(
- (image, i, arr) => `
-
-
-${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,
+};