rsshub 1.0.0-master.f9b85f5 → 1.0.0-master.f9c381a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config.ts +10 -0
- package/lib/middleware/cache.ts +4 -0
- package/lib/middleware/parameter.ts +1 -1
- package/lib/registry.ts +6 -2
- package/lib/routes/51cto/utils.ts +1 -1
- package/lib/routes/abc/index.ts +1 -1
- package/lib/routes/acgvinyl/namespace.ts +6 -0
- package/lib/routes/acgvinyl/news.ts +86 -0
- package/lib/routes/ally/rail.ts +1 -1
- package/lib/routes/anthropic/news.ts +13 -11
- package/lib/routes/apnews/mobile-api.ts +1 -1
- package/lib/routes/apnews/sitemap.ts +1 -1
- package/lib/routes/apple/apps.ts +2 -2
- package/lib/routes/apple/podcast.ts +58 -25
- package/lib/routes/bangumi.tv/group/reply.ts +1 -1
- package/lib/routes/bilibili/cache.ts +18 -2
- package/lib/routes/bilibili/dynamic.ts +31 -7
- package/lib/routes/bilibili/page.ts +1 -1
- package/lib/routes/bilibili/ranking.ts +23 -17
- package/lib/routes/bilibili/wasm-exec.ts +1 -1
- package/lib/routes/bilibili/weekly-recommend.ts +22 -6
- package/lib/routes/bjp/apod.ts +1 -1
- package/lib/routes/buaa/jiaowu.ts +1 -1
- package/lib/routes/bullionvault/gold-news.ts +3 -3
- package/lib/routes/cast/index.ts +1 -1
- package/lib/routes/coolapk/utils.ts +5 -4
- package/lib/routes/coolbuy/index.ts +106 -0
- package/lib/routes/{xinhuanet → coolbuy}/namespace.ts +3 -3
- package/lib/routes/coolbuy/templates/description.art +48 -0
- package/lib/routes/coolidge/film-guide.ts +60 -0
- package/lib/routes/coolidge/namespace.ts +7 -0
- package/lib/routes/coolidge/news.ts +65 -0
- package/lib/routes/coolidge/templates/description.art +4 -0
- package/lib/routes/copymanga/comic.ts +1 -1
- package/lib/routes/cpta/handler.ts +1 -1
- package/lib/routes/creative-comic/book.ts +1 -1
- package/lib/routes/daum/potplayer.ts +1 -1
- package/lib/routes/dockerhub/utils.ts +1 -1
- package/lib/routes/dora-world/article.ts +2 -2
- package/lib/routes/ehentai/ehapi.ts +3 -3
- package/lib/routes/eventbrite/events.ts +152 -0
- package/lib/routes/eventbrite/namespace.ts +7 -0
- package/lib/routes/github/activity.ts +1 -1
- package/lib/routes/google/jules.ts +63 -0
- package/lib/routes/gov/mem/namespace.ts +7 -0
- package/lib/routes/gov/mem/zfxxgkpt.ts +96 -0
- package/lib/routes/gov/mot/index.ts +158 -53
- package/lib/routes/hameln/chapter.ts +1 -1
- package/lib/routes/hpoi/banner-item.ts +28 -11
- package/lib/routes/hpoi/info.ts +1 -1
- package/lib/routes/huggingface/daily-papers.ts +1 -1
- package/lib/routes/hupu/index.ts +158 -74
- package/lib/routes/hupu/types.ts +163 -0
- package/lib/routes/hust/gs.ts +1 -1
- package/lib/routes/hust/mse.ts +1 -1
- package/lib/routes/huxiu/util.ts +2 -2
- package/lib/routes/infoq/presentations.ts +1 -1
- package/lib/routes/instagram/common-utils.ts +3 -3
- package/lib/routes/itch/devlog.ts +7 -3
- package/lib/routes/javbus/index.ts +1 -1
- package/lib/routes/javlibrary/utils.ts +1 -1
- package/lib/routes/jbma/namespace.ts +17 -0
- package/lib/routes/jbma/report.ts +473 -0
- package/lib/routes/jetbrains/comments.ts +1 -1
- package/lib/routes/jingzhengu/utils.ts +1 -1
- package/lib/routes/juejin/aicoding.ts +102 -0
- package/lib/routes/juejin/utils.ts +36 -51
- package/lib/routes/kakuyomu/works.ts +1 -1
- package/lib/routes/kemono/index.ts +2 -2
- package/lib/routes/komiic/comic.ts +1 -1
- package/lib/routes/koyso/index.ts +338 -0
- package/lib/routes/koyso/namespace.ts +9 -0
- package/lib/routes/koyso/templates/description.art +13 -0
- package/lib/routes/letterboxd/index.ts +65 -0
- package/lib/routes/letterboxd/namespace.ts +8 -0
- package/lib/routes/maccms/index.ts +1 -1
- package/lib/routes/mercari/util.ts +1 -1
- package/lib/routes/mingpao/index.ts +1 -1
- package/lib/routes/nankai/ai-notice.ts +142 -0
- package/lib/routes/nankai/graduate-notice.ts +162 -0
- package/lib/routes/natgeo/natgeo.ts +1 -0
- package/lib/routes/nhk/news-web-easy.ts +1 -1
- package/lib/routes/nicovideo/mylist.ts +39 -0
- package/lib/routes/nicovideo/types.ts +27 -0
- package/lib/routes/nicovideo/utils.ts +27 -1
- package/lib/routes/nikkei/cn/index.ts +1 -4
- package/lib/routes/now/news.ts +1 -1
- package/lib/routes/nytimes/index.ts +1 -1
- package/lib/routes/pixiv/novel-api/user-novels/sfw.ts +1 -1
- package/lib/routes/pixivision/utils.ts +1 -1
- package/lib/routes/pku/hr.ts +1 -1
- package/lib/routes/pku/scc/recruit.ts +1 -1
- package/lib/routes/producthunt/templates/description.art +2 -2
- package/lib/routes/producthunt/today.ts +17 -8
- package/lib/routes/ps/trophy.ts +1 -1
- package/lib/routes/pubscholar/utils.ts +14 -1
- package/lib/routes/qweather/3days.ts +14 -14
- package/lib/routes/qweather/now.ts +12 -10
- package/lib/routes/qweather/util.tsx +89 -0
- package/lib/routes/qwenlm/blog.ts +75 -0
- package/lib/routes/qwenlm/namespace.ts +6 -0
- package/lib/routes/radio-canada/latest.ts +30 -17
- package/lib/routes/ruc/ai.ts +1 -1
- package/lib/routes/ruc/hr.ts +1 -1
- package/lib/routes/samrdprc/index.ts +241 -0
- package/lib/routes/samrdprc/namespace.ts +1 -1
- package/lib/routes/sdo/ff14risingstones/api.ts +78 -0
- package/lib/routes/sdo/ff14risingstones/constant.ts +338 -0
- package/lib/routes/sdo/ff14risingstones/posts.ts +80 -0
- package/lib/routes/sdo/ff14risingstones/strats.ts +75 -0
- package/lib/routes/sdo/ff14risingstones/templates/duties-party.art +41 -0
- package/lib/routes/sdo/ff14risingstones/templates/fc-party.art +26 -0
- package/lib/routes/sdo/ff14risingstones/templates/novice-network-party.art +9 -0
- package/lib/routes/sdo/ff14risingstones/templates/rp-party.art +15 -0
- package/lib/routes/sdo/ff14risingstones/timeline.ts +31 -0
- package/lib/routes/sdo/ff14risingstones/types/dynamic.ts +50 -0
- package/lib/routes/sdo/ff14risingstones/types/index.ts +3 -0
- package/lib/routes/sdo/ff14risingstones/types/other.ts +57 -0
- package/lib/routes/sdo/ff14risingstones/types/party.ts +111 -0
- package/lib/routes/sdo/ff14risingstones/user-dynamics.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-posts.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-resently.ts +38 -0
- package/lib/routes/sdo/ff14risingstones/user-strats.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/utils.ts +215 -0
- package/lib/routes/sdo/namespace.ts +7 -0
- package/lib/routes/showstart/utils.ts +1 -1
- package/lib/routes/sohu/mp.ts +1 -1
- package/lib/routes/sotwe/user.ts +1 -1
- package/lib/routes/surfshark/blog.ts +273 -77
- package/lib/routes/surfshark/templates/description.art +16 -4
- package/lib/routes/sustainabilitymag/articles.ts +1 -1
- package/lib/routes/syosetu/dev.ts +1 -1
- package/lib/routes/syosetu/ranking-isekai.ts +1 -1
- package/lib/routes/szse/disclosure/listed-notice.ts +44 -6
- package/lib/routes/telegram/channel-media.ts +249 -0
- package/lib/routes/telegram/channel.ts +5 -4
- package/lib/routes/telegram/stories.ts +130 -0
- package/lib/routes/telegram/tglib/channel.ts +136 -118
- package/lib/routes/telegram/tglib/client.ts +37 -139
- package/lib/routes/tesla/cx.ts +1 -1
- package/lib/routes/theverge/index.ts +20 -6
- package/lib/routes/threads/utils.ts +7 -3
- package/lib/routes/tidb/blog.ts +1 -1
- package/lib/routes/toutiao/user.ts +2 -2
- package/lib/routes/twitter/api/mobile-api/api.ts +1 -1
- package/lib/routes/txrjy/fornumtopic.ts +2 -2
- package/lib/routes/typst/universe.ts +1 -1
- package/lib/routes/uber/blog.ts +87 -46
- package/lib/routes/weibo/utils.ts +17 -9
- package/lib/routes/xiaohongshu/user.ts +1 -1
- package/lib/routes/xjtu/ee-jzxx.ts +1 -1
- package/lib/routes/yahoo/news/utils.ts +1 -1
- package/lib/routes/ymgal/article.ts +1 -1
- package/lib/routes/yoasobi-music/media.ts +1 -1
- package/lib/routes/youtube/api/youtubei.ts +1 -1
- package/lib/routes/youtube/community.ts +4 -4
- package/lib/routes/zaker/utils.ts +1 -1
- package/lib/routes/zaobao/util.tsx +1 -1
- package/lib/server.ts +1 -1
- package/lib/types.ts +1 -1
- package/lib/utils/puppeteer-utils.test.ts +2 -2
- package/lib/views/index.tsx +4 -4
- package/package.json +40 -40
- package/lib/routes/qweather/templates/3days.art +0 -22
- package/lib/routes/qweather/templates/now.art +0 -16
- package/lib/routes/xinhuanet/app.ts +0 -109
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Route } from '@/types';
|
|
2
|
+
import got from '@/utils/got';
|
|
3
|
+
import { load } from 'cheerio';
|
|
4
|
+
import { parseDate } from '@/utils/parse-date';
|
|
5
|
+
import cache from '@/utils/cache';
|
|
6
|
+
import timezone from '@/utils/timezone';
|
|
7
|
+
|
|
8
|
+
export const route: Route = {
|
|
9
|
+
path: '/ai/:type?',
|
|
10
|
+
categories: ['university'],
|
|
11
|
+
example: '/nankai/ai/zxdt',
|
|
12
|
+
parameters: { type: '栏目类型(若为空则默认为"最新动态")' },
|
|
13
|
+
features: {
|
|
14
|
+
requireConfig: false,
|
|
15
|
+
requirePuppeteer: false,
|
|
16
|
+
antiCrawler: false,
|
|
17
|
+
supportBT: false,
|
|
18
|
+
supportPodcast: false,
|
|
19
|
+
supportScihub: false,
|
|
20
|
+
},
|
|
21
|
+
radar: [
|
|
22
|
+
{
|
|
23
|
+
source: ['ai.nankai.edu.cn', 'ai.nankai.edu.cn/xwzx/:type.htm'],
|
|
24
|
+
target: '/ai/:type?',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
name: '人工智能学院',
|
|
28
|
+
maintainers: ['LMark'],
|
|
29
|
+
description: `| 最新动态 | 学院公告 | 学生之窗 | 科研信息 | 本科生教学 | 党团园地 | 研究生招生 | 研究生教学 | 就业信息 | 国际交流 |
|
|
30
|
+
| -------- | -------- | -------- | -------- | ---------- | -------- | ---------- | ---------- | -------- | -------- |
|
|
31
|
+
| zxdt | xygg | xszc | kyxx | bksjx | dtyd | yjszs | yjsjx | jyxx | gjjl |`,
|
|
32
|
+
url: 'ai.nankai.edu.cn',
|
|
33
|
+
handler: async (ctx) => {
|
|
34
|
+
// 从 URL 参数中获取通知分类
|
|
35
|
+
const { type = 'zxdt' } = ctx.req.param();
|
|
36
|
+
const baseUrl = 'https://ai.nankai.edu.cn';
|
|
37
|
+
const { data: response } = await got(`${baseUrl}/xwzx/${type}.htm`);
|
|
38
|
+
const $ = load(response);
|
|
39
|
+
|
|
40
|
+
// 获取分类名称映射
|
|
41
|
+
const categoryMap: Record<string, string> = {
|
|
42
|
+
zxdt: '最新动态',
|
|
43
|
+
xygg: '学院公告',
|
|
44
|
+
xszc: '学生之窗',
|
|
45
|
+
kyxx: '科研信息',
|
|
46
|
+
bksjx: '本科生教学',
|
|
47
|
+
dtyd: '党团园地',
|
|
48
|
+
yjszs: '研究生招生',
|
|
49
|
+
yjsjx: '研究生教学',
|
|
50
|
+
jyxx: '就业信息',
|
|
51
|
+
gjjl: '国际交流',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const categoryName = categoryMap[type] || '最新动态';
|
|
55
|
+
|
|
56
|
+
// 解析新闻列表
|
|
57
|
+
const list = $('.gage-list-news table tr')
|
|
58
|
+
.slice(1) // 跳过表头
|
|
59
|
+
.toArray()
|
|
60
|
+
.map((tr) => {
|
|
61
|
+
const $tr = $(tr);
|
|
62
|
+
const cells = $tr.find('td');
|
|
63
|
+
|
|
64
|
+
if (cells.length < 3) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const titleCell = cells.eq(0);
|
|
69
|
+
const sourceCell = cells.eq(1);
|
|
70
|
+
const dateCell = cells.eq(2);
|
|
71
|
+
|
|
72
|
+
// 提取标题和链接
|
|
73
|
+
const $titleLink = titleCell.find('a');
|
|
74
|
+
const title = $titleLink.text().trim();
|
|
75
|
+
let link = $titleLink.attr('href') || '';
|
|
76
|
+
|
|
77
|
+
// 处理相对链接
|
|
78
|
+
link = link && !link.startsWith('http') ? `${baseUrl}/${link}` : link;
|
|
79
|
+
|
|
80
|
+
// 提取日期
|
|
81
|
+
const dateStr = dateCell.text().trim();
|
|
82
|
+
const pubDate = dateStr.includes('/') ? timezone(parseDate(dateStr, 'YYYY/MM/DD'), +8) : timezone(parseDate(dateStr), +8);
|
|
83
|
+
|
|
84
|
+
// 提取来源
|
|
85
|
+
const source = sourceCell.text().trim();
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
title,
|
|
89
|
+
link,
|
|
90
|
+
pubDate,
|
|
91
|
+
author: source || '人工智能学院',
|
|
92
|
+
description: '', // 初始化description属性
|
|
93
|
+
};
|
|
94
|
+
})
|
|
95
|
+
.filter((item) => item && item.link && item.title); // 过滤掉空项目和没有链接的项目
|
|
96
|
+
|
|
97
|
+
// 获取每篇文章的详细内容
|
|
98
|
+
const items = await Promise.all(
|
|
99
|
+
list.map((item) =>
|
|
100
|
+
item
|
|
101
|
+
? cache.tryGet(item.link, async () => {
|
|
102
|
+
try {
|
|
103
|
+
const { data: response } = await got(item.link);
|
|
104
|
+
const $ = load(response);
|
|
105
|
+
|
|
106
|
+
const $description = $('.v_news_content');
|
|
107
|
+
|
|
108
|
+
// 处理相对链接,转换为绝对链接
|
|
109
|
+
if ($description.length > 0) {
|
|
110
|
+
// 处理图片
|
|
111
|
+
$description.find('img').each((i, el) => {
|
|
112
|
+
const $el = $(el);
|
|
113
|
+
let src = $el.attr('src');
|
|
114
|
+
|
|
115
|
+
if (src && !src.startsWith('http')) {
|
|
116
|
+
src = `${baseUrl}${src}`;
|
|
117
|
+
$el.attr('src', src);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
item.description = $description.html() || item.title;
|
|
123
|
+
} catch {
|
|
124
|
+
// 如果获取详细内容失败,返回基本信息
|
|
125
|
+
item.description = item.title + ' (获取详细内容失败)';
|
|
126
|
+
}
|
|
127
|
+
return item;
|
|
128
|
+
})
|
|
129
|
+
: null
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
// 源标题
|
|
135
|
+
title: `南开大学人工智能学院-${categoryName}`,
|
|
136
|
+
// 源链接
|
|
137
|
+
link: `${baseUrl}/xwzx/${type}.htm`,
|
|
138
|
+
// 源文章
|
|
139
|
+
item: items,
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Route } from '@/types';
|
|
2
|
+
import got from '@/utils/got';
|
|
3
|
+
import { load } from 'cheerio';
|
|
4
|
+
import { parseDate } from '@/utils/parse-date';
|
|
5
|
+
import cache from '@/utils/cache';
|
|
6
|
+
import timezone from '@/utils/timezone';
|
|
7
|
+
|
|
8
|
+
export const route: Route = {
|
|
9
|
+
path: '/graduate/:type?',
|
|
10
|
+
categories: ['university'],
|
|
11
|
+
example: '/nankai/graduate/zxdt',
|
|
12
|
+
parameters: { type: '栏目编号(若为空则默认为"zxdt")' },
|
|
13
|
+
features: {
|
|
14
|
+
requireConfig: false,
|
|
15
|
+
requirePuppeteer: false,
|
|
16
|
+
antiCrawler: false,
|
|
17
|
+
supportBT: false,
|
|
18
|
+
supportPodcast: false,
|
|
19
|
+
supportScihub: false,
|
|
20
|
+
},
|
|
21
|
+
radar: [
|
|
22
|
+
{
|
|
23
|
+
source: ['graduate.nankai.edu.cn', 'graduate.nankai.edu.cn/:type/list.htm'],
|
|
24
|
+
target: '/graduate/:type?',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
name: '研究生院',
|
|
28
|
+
maintainers: ['ladeng07'],
|
|
29
|
+
description: `| 最新动态 | 综合信息 | 招生工作 | 培养管理 | 国际交流 | 学科建设 | 学位管理 |
|
|
30
|
+
| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
|
31
|
+
| zxdt | 82 | 83 | 84 | 85 | 86 | 87 |`,
|
|
32
|
+
url: 'graduate.nankai.edu.cn',
|
|
33
|
+
handler: async (ctx) => {
|
|
34
|
+
// 从 URL 参数中获取通知分类
|
|
35
|
+
const { type = 'zxdt' } = ctx.req.param();
|
|
36
|
+
const baseUrl = 'https://graduate.nankai.edu.cn';
|
|
37
|
+
const { data: response } = await got(`${baseUrl}/${type}/list.htm`);
|
|
38
|
+
const $ = load(response);
|
|
39
|
+
|
|
40
|
+
// 获取分类名称映射
|
|
41
|
+
const categoryMap: Record<string, string> = {
|
|
42
|
+
zxdt: '最新动态',
|
|
43
|
+
'82': '综合信息',
|
|
44
|
+
'83': '招生工作',
|
|
45
|
+
'84': '培养管理',
|
|
46
|
+
'85': '国际交流',
|
|
47
|
+
'86': '学科建设',
|
|
48
|
+
'87': '学位管理',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const categoryName = categoryMap[type] || '最新动态';
|
|
52
|
+
|
|
53
|
+
// 解析新闻列表
|
|
54
|
+
const list = $('.newslist li')
|
|
55
|
+
.not('#wp_paging_w6 li')
|
|
56
|
+
.toArray()
|
|
57
|
+
.map((li) => {
|
|
58
|
+
const $li = $(li);
|
|
59
|
+
const $titleDiv = $li.find('.title');
|
|
60
|
+
const $link = $titleDiv.find('a');
|
|
61
|
+
const $timeDiv = $li.find('.time');
|
|
62
|
+
|
|
63
|
+
const title = $link.attr('title');
|
|
64
|
+
let link = $link.attr('href') || '';
|
|
65
|
+
|
|
66
|
+
// 处理相对链接
|
|
67
|
+
link = link && !link.startsWith('http') ? `${baseUrl}${link}` : link;
|
|
68
|
+
|
|
69
|
+
// 提取日期
|
|
70
|
+
const dateStr = $timeDiv.text().trim();
|
|
71
|
+
const pubDate = timezone(parseDate(dateStr, 'YYYY-MM-DD'), +8);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
title,
|
|
75
|
+
link,
|
|
76
|
+
pubDate,
|
|
77
|
+
author: '研究生院',
|
|
78
|
+
description: '', // 初始化description属性
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
.filter((item) => item && item.link && item.title); // 过滤掉空项目和没有链接的项目
|
|
82
|
+
|
|
83
|
+
// 获取每篇文章的详细内容
|
|
84
|
+
const items = await Promise.all(
|
|
85
|
+
list.map((item) =>
|
|
86
|
+
cache.tryGet(item.link, async () => {
|
|
87
|
+
try {
|
|
88
|
+
const { data: response } = await got(item.link);
|
|
89
|
+
const $ = load(response);
|
|
90
|
+
|
|
91
|
+
// 尝试多种内容选择器
|
|
92
|
+
const $description = $('.wp_articlecontent');
|
|
93
|
+
|
|
94
|
+
// 处理相对链接,转换为绝对链接
|
|
95
|
+
if ($description.length > 0) {
|
|
96
|
+
// 处理链接
|
|
97
|
+
$description.find('a').each((i, el) => {
|
|
98
|
+
const $el = $(el);
|
|
99
|
+
const href = $el.attr('href');
|
|
100
|
+
if (href && !href.startsWith('http')) {
|
|
101
|
+
if (href.startsWith('/')) {
|
|
102
|
+
$el.attr('href', `${baseUrl}${href}`);
|
|
103
|
+
} else {
|
|
104
|
+
$el.attr('href', `${baseUrl}/${href}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 处理图片
|
|
110
|
+
$description.find('img').each((i, el) => {
|
|
111
|
+
const $el = $(el);
|
|
112
|
+
let src = $el.attr('src');
|
|
113
|
+
|
|
114
|
+
if (src && !src.startsWith('http')) {
|
|
115
|
+
src = src.startsWith('/') ? `${baseUrl}${src}` : `${baseUrl}/${src}`;
|
|
116
|
+
$el.attr('src', src);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// 处理PDF播放器div,提取PDF链接
|
|
121
|
+
$description.find('.wp_pdf_player').each((i, el) => {
|
|
122
|
+
const $el = $(el);
|
|
123
|
+
const pdfSrc = $el.attr('pdfsrc');
|
|
124
|
+
const sudyfileAttr = ($el.attr('sudyfile-attr') || '{}').replaceAll("'", '"');
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const sudyfileAttrJson = JSON.parse(sudyfileAttr);
|
|
128
|
+
const fileName = sudyfileAttrJson.title || '未命名文件.pdf';
|
|
129
|
+
if (pdfSrc) {
|
|
130
|
+
let pdfUrl = pdfSrc;
|
|
131
|
+
if (!pdfUrl.startsWith('http')) {
|
|
132
|
+
pdfUrl = `${baseUrl}${pdfUrl}`;
|
|
133
|
+
}
|
|
134
|
+
// 替换PDF播放器为下载链接
|
|
135
|
+
$el.replaceWith(`<p><a href="${pdfUrl}" target="_blank">📄 ${fileName}</a></p>`);
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
// 如果解析失败,保留原始内容
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
item.description = $description.html() || item.title;
|
|
144
|
+
} catch {
|
|
145
|
+
// 如果获取详细内容失败,返回基本信息
|
|
146
|
+
item.description = item.title + ' (获取详细内容失败)';
|
|
147
|
+
}
|
|
148
|
+
return item;
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
// 源标题
|
|
155
|
+
title: `南开大学研究生院-${categoryName}`,
|
|
156
|
+
// 源链接
|
|
157
|
+
link: `${baseUrl}/${type}/list.htm`,
|
|
158
|
+
// 源文章
|
|
159
|
+
item: items,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
};
|
|
@@ -49,7 +49,7 @@ async function handler(ctx) {
|
|
|
49
49
|
}))
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
-
items = items.
|
|
52
|
+
items = items.toSorted((a, b) => b.pubDate - a.pubDate).slice(0, ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30);
|
|
53
53
|
|
|
54
54
|
items = await Promise.all(
|
|
55
55
|
items.map((item) =>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DataItem, Route } from '@/types';
|
|
2
|
+
|
|
3
|
+
import { getMylist, renderVideo } from './utils';
|
|
4
|
+
import { parseDate } from '@/utils/parse-date';
|
|
5
|
+
|
|
6
|
+
export const route: Route = {
|
|
7
|
+
name: 'Mylist',
|
|
8
|
+
path: '/mylist/:id',
|
|
9
|
+
parameters: { id: 'Mylist ID' },
|
|
10
|
+
example: '/nicovideo/mylist/2973737',
|
|
11
|
+
maintainers: ['esperecyan'],
|
|
12
|
+
radar: [
|
|
13
|
+
{
|
|
14
|
+
source: ['www.nicovideo.jp/user/:user/mylist/:id'],
|
|
15
|
+
target: '/mylist/:id',
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
handler: async (ctx) => {
|
|
19
|
+
const { id } = ctx.req.param();
|
|
20
|
+
|
|
21
|
+
const mylist = await getMylist(id);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
title: `マイリスト ${mylist.name}‐ニコニコ動画`,
|
|
25
|
+
link: `https://www.nicovideo.jp/user/${mylist.owner.id}/mylist/${mylist.id}`,
|
|
26
|
+
language: 'ja',
|
|
27
|
+
item: mylist.items.map(
|
|
28
|
+
(item): DataItem => ({
|
|
29
|
+
title: item.video.title,
|
|
30
|
+
link: `https://www.nicovideo.jp/watch/${item.video.id}`,
|
|
31
|
+
pubDate: parseDate(item.addedAt),
|
|
32
|
+
author: [{ name: item.video.owner.name, avatar: item.video.owner.iconUrl, url: `https://www.nicovideo.jp/user/${item.video.owner.id}` }],
|
|
33
|
+
description: renderVideo(item.video, false),
|
|
34
|
+
image: item.video.thumbnail.nHdUrl ?? item.video.thumbnail.largeUrl ?? item.video.thumbnail.middleUrl ?? undefined,
|
|
35
|
+
})
|
|
36
|
+
),
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -52,3 +52,30 @@ export interface VideoItem {
|
|
|
52
52
|
series: null;
|
|
53
53
|
essential: Essential;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
export type Mylist = {
|
|
57
|
+
id: number;
|
|
58
|
+
name: string;
|
|
59
|
+
description: string;
|
|
60
|
+
decoratedDescriptionHtml: string;
|
|
61
|
+
defaultSortKey: string;
|
|
62
|
+
defaultSortOrder: string;
|
|
63
|
+
items: MylistItem[];
|
|
64
|
+
totalItemCount: number;
|
|
65
|
+
hasNext: boolean;
|
|
66
|
+
isPublic: boolean;
|
|
67
|
+
owner: Owner;
|
|
68
|
+
hasInvisibleItems: boolean;
|
|
69
|
+
followerCount: number;
|
|
70
|
+
isFollowing: boolean;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type MylistItem = {
|
|
74
|
+
itemId: number;
|
|
75
|
+
watchId: string;
|
|
76
|
+
description: string;
|
|
77
|
+
decoratedDescriptionHtml: string;
|
|
78
|
+
addedAt: string;
|
|
79
|
+
status: string;
|
|
80
|
+
video: Essential;
|
|
81
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Essential, UserInfo, VideoItem } from './types';
|
|
1
|
+
import { Essential, Mylist, UserInfo, VideoItem } from './types';
|
|
2
2
|
|
|
3
3
|
import ofetch from '@/utils/ofetch';
|
|
4
4
|
import cache from '@/utils/cache';
|
|
@@ -31,4 +31,30 @@ export const getUserVideosById = (id: string) =>
|
|
|
31
31
|
false
|
|
32
32
|
) as Promise<VideoItem[]>;
|
|
33
33
|
|
|
34
|
+
export const getMylist = (id: string): Promise<Mylist> =>
|
|
35
|
+
cache.tryGet<Mylist>(
|
|
36
|
+
`nicovideo:mylist:${id}`,
|
|
37
|
+
async () => {
|
|
38
|
+
const { data } = await ofetch(`https://nvapi.nicovideo.jp/v2/mylists/${id}`, {
|
|
39
|
+
headers: {
|
|
40
|
+
'X-Frontend-Id': '6',
|
|
41
|
+
},
|
|
42
|
+
query: {
|
|
43
|
+
sortKey: 'addedAt',
|
|
44
|
+
sortOrder: 'desc',
|
|
45
|
+
/**
|
|
46
|
+
* @remarks
|
|
47
|
+
* Up to 500 items can be registered in a single mylist.
|
|
48
|
+
* @see https://qa.nicovideo.jp/faq/show/648?site_domain=default
|
|
49
|
+
*/
|
|
50
|
+
pageSize: 500,
|
|
51
|
+
page: 1,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
return data.mylist;
|
|
55
|
+
},
|
|
56
|
+
config.cache.routeExpire,
|
|
57
|
+
false
|
|
58
|
+
);
|
|
59
|
+
|
|
34
60
|
export const renderVideo = (video: Essential, embed: boolean) => art(path.join(__dirname, 'templates/video.art'), { video, embed });
|
|
@@ -136,10 +136,7 @@ async function handler(ctx) {
|
|
|
136
136
|
|
|
137
137
|
item.author = content('meta[name="author"]').attr('content');
|
|
138
138
|
item.title = item.title ?? content('meta[name="twitter:title"]').attr('content');
|
|
139
|
-
item.description = content('#contentDiv')
|
|
140
|
-
.html()
|
|
141
|
-
?.replace(/ /g, '')
|
|
142
|
-
.replaceAll('<p></p>', '');
|
|
139
|
+
item.description = content('#contentDiv').html()?.replaceAll(' ', '').replaceAll('<p></p>', '');
|
|
143
140
|
|
|
144
141
|
return item;
|
|
145
142
|
})
|
package/lib/routes/now/news.ts
CHANGED
|
@@ -100,7 +100,7 @@ async function handler(ctx) {
|
|
|
100
100
|
link: `https://news.now.com/home/${category}/player?newsId=${item.newsId}`,
|
|
101
101
|
pubDate: parseDate(item.publishDate, 'x'),
|
|
102
102
|
category: [...item.sportTypes.map((t) => t.sportTypeNameChi), ...item.players.map((p) => p.playerFullNameChi), ...item.teams.map((t) => t.teamCodeChi)],
|
|
103
|
-
image: item.newsPhotos?.
|
|
103
|
+
image: item.newsPhotos?.find((p) => p.sizeType === '3')?.imageUrl,
|
|
104
104
|
newsId: item.newsId,
|
|
105
105
|
};
|
|
106
106
|
})
|
|
@@ -136,7 +136,7 @@ async function handler(ctx) {
|
|
|
136
136
|
// Match 感谢|謝.*?cn.letters@nytimes.com。
|
|
137
137
|
const ending = /感(谢|謝);.*?cn\.letters@nytimes\.com。/g;
|
|
138
138
|
|
|
139
|
-
single.description = result.description?.
|
|
139
|
+
single.description = result.description?.replaceAll(ending, '');
|
|
140
140
|
|
|
141
141
|
if (hasEnVersion) {
|
|
142
142
|
single.title = result.title;
|
|
@@ -15,7 +15,7 @@ export async function getSFWUserNovels(id: string, fullContent: boolean = false,
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
const novels = Object.keys(allData.body.novels)
|
|
18
|
-
.
|
|
18
|
+
.toSorted((a, b) => Number(b) - Number(a))
|
|
19
19
|
.slice(0, Number.parseInt(String(limit), 10));
|
|
20
20
|
|
|
21
21
|
if (novels.length === 0) {
|
|
@@ -81,6 +81,6 @@ export function processContent($: CheerioAPI, lang: string): string {
|
|
|
81
81
|
return (
|
|
82
82
|
$('.am__body')
|
|
83
83
|
.html()
|
|
84
|
-
?.
|
|
84
|
+
?.replaceAll('https://i.pximg.net', config.pixiv.imgProxy || '') || ''
|
|
85
85
|
);
|
|
86
86
|
}
|
package/lib/routes/pku/hr.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const route: Route = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
async function handler(ctx) {
|
|
37
|
-
const category = ctx.req.param('category')?.
|
|
37
|
+
const category = ctx.req.param('category')?.replaceAll('-', '/') ?? 'zxgg';
|
|
38
38
|
|
|
39
39
|
const rootUrl = 'https://hr.pku.edu.cn/';
|
|
40
40
|
const currentUrl = `${rootUrl}/${category}/index.htm`;
|
|
@@ -56,7 +56,7 @@ async function handler(ctx) {
|
|
|
56
56
|
};
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
const sorted = list.
|
|
59
|
+
const sorted = list.toSorted((a, b) => b.pubDate.getTime() - a.pubDate.getTime()).slice(0, 10);
|
|
60
60
|
|
|
61
61
|
return {
|
|
62
62
|
title: `北京大学学生就业指导服务中心 - ${feed_title}`,
|
|
@@ -6,6 +6,7 @@ import { load } from 'cheerio';
|
|
|
6
6
|
import { parseDate } from '@/utils/parse-date';
|
|
7
7
|
import { art } from '@/utils/render';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
import { config } from '@/config';
|
|
9
10
|
|
|
10
11
|
export const route: Route = {
|
|
11
12
|
path: '/today',
|
|
@@ -24,23 +25,28 @@ export const route: Route = {
|
|
|
24
25
|
source: ['www.producthunt.com/'],
|
|
25
26
|
},
|
|
26
27
|
],
|
|
27
|
-
name: 'Today
|
|
28
|
+
name: 'Top Products Launching Today',
|
|
28
29
|
maintainers: ['miaoyafeng', 'Fatpandac'],
|
|
29
30
|
handler,
|
|
30
31
|
url: 'www.producthunt.com/',
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
async function handler() {
|
|
34
|
-
const response = await ofetch('https://www.producthunt.com/'
|
|
35
|
+
const response = await ofetch('https://www.producthunt.com/', {
|
|
36
|
+
headers: {
|
|
37
|
+
'User-Agent': config.trueUA,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
35
40
|
|
|
36
41
|
const $ = load(response);
|
|
37
42
|
const match = $('script:contains("ApolloSSRDataTransport")')
|
|
38
43
|
.text()
|
|
39
44
|
.match(/"events":(\[.+\])\}\)/)?.[1]
|
|
45
|
+
?.trim()
|
|
40
46
|
.replaceAll('undefined', 'null');
|
|
41
47
|
|
|
42
48
|
const data = JSON.parse(match);
|
|
43
|
-
const todayList = data.find((event) => event.type === '
|
|
49
|
+
const todayList = data.find((event) => event.type === 'next' && event.value.data.homefeed).value.data.homefeed.edges.find((edge) => edge.node.id === 'FEATURED-0').node;
|
|
44
50
|
// 0: Top Products Launching Today
|
|
45
51
|
// 1: Yesterday's Top Products
|
|
46
52
|
// 2: Last Week's Top Products
|
|
@@ -50,8 +56,8 @@ async function handler() {
|
|
|
50
56
|
.filter((i) => i.__typename === 'Post')
|
|
51
57
|
.map((item) => ({
|
|
52
58
|
title: item.name,
|
|
53
|
-
link: `https://www.producthunt.com/
|
|
54
|
-
|
|
59
|
+
link: `https://www.producthunt.com/products/${item.product.slug}`,
|
|
60
|
+
postSlug: item.slug,
|
|
55
61
|
description: item.tagline,
|
|
56
62
|
pubDate: parseDate(item.createdAt),
|
|
57
63
|
image: `https://ph-files.imgix.net/${item.thumbnailImageUuid}`,
|
|
@@ -63,15 +69,18 @@ async function handler() {
|
|
|
63
69
|
cache.tryGet(item.link, async () => {
|
|
64
70
|
const response = await ofetch('https://www.producthunt.com/frontend/graphql', {
|
|
65
71
|
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'User-Agent': config.trueUA,
|
|
74
|
+
},
|
|
66
75
|
body: {
|
|
67
76
|
operationName: 'PostPage',
|
|
68
77
|
variables: {
|
|
69
|
-
slug: item.
|
|
78
|
+
slug: item.postSlug,
|
|
70
79
|
},
|
|
71
80
|
extensions: {
|
|
72
81
|
persistedQuery: {
|
|
73
82
|
version: 1,
|
|
74
|
-
sha256Hash: '
|
|
83
|
+
sha256Hash: '488585149898ee974a51884b11e205c34ea8ad34ee01d47d7936a66a6db799ff',
|
|
75
84
|
},
|
|
76
85
|
},
|
|
77
86
|
},
|
|
@@ -80,7 +89,7 @@ async function handler() {
|
|
|
80
89
|
|
|
81
90
|
item.author = post.user.name;
|
|
82
91
|
item.description = art(path.join(__dirname, 'templates/description.art'), {
|
|
83
|
-
|
|
92
|
+
tagline: post.tagline,
|
|
84
93
|
description: post.description,
|
|
85
94
|
media: post.media,
|
|
86
95
|
});
|
package/lib/routes/ps/trophy.ts
CHANGED
|
@@ -87,7 +87,7 @@ async function handler(ctx) {
|
|
|
87
87
|
for (const item of items) {
|
|
88
88
|
result = [...result, ...item];
|
|
89
89
|
}
|
|
90
|
-
result = result.
|
|
90
|
+
result = result.toSorted((a, b) => new Date(b.pubDate) - new Date(a.pubDate));
|
|
91
91
|
|
|
92
92
|
return {
|
|
93
93
|
title: `${id} 的 PSN 奖杯`,
|
|
@@ -23,14 +23,27 @@ const generateNonce = (length: number): string => {
|
|
|
23
23
|
return nonce.slice(0, length);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Part of fingerprint2.js shim from uBlock Origin
|
|
28
|
+
* Taken from https://github.com/gorhill/uBlock/blob/master/src/web_accessible_resources/fingerprint2.js
|
|
29
|
+
* @param len
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
const hex32 = (len) =>
|
|
33
|
+
Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
|
|
34
|
+
.toString(16)
|
|
35
|
+
.slice(-len)
|
|
36
|
+
.padStart(len, '0');
|
|
37
|
+
|
|
26
38
|
export const getSignedHeaders = () => {
|
|
27
39
|
const nonce = generateNonce(6);
|
|
28
40
|
const timestamp = Date.now().toString();
|
|
29
|
-
const signature = sha1([salt, timestamp, nonce].
|
|
41
|
+
const signature = sha1([salt, timestamp, nonce].toSorted().join(''));
|
|
30
42
|
return {
|
|
31
43
|
nonce,
|
|
32
44
|
timestamp,
|
|
33
45
|
signature,
|
|
46
|
+
'x-finger': `${hex32(8)}${hex32(8)}${hex32(8)}${hex32(8)}`,
|
|
34
47
|
};
|
|
35
48
|
};
|
|
36
49
|
|