rsshub 1.0.0-master.f72af1b → 1.0.0-master.f7347d9

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.
Files changed (222) hide show
  1. package/lib/config.ts +18 -0
  2. package/lib/middleware/template.tsx +10 -2
  3. package/lib/routes/163/music/playlist.ts +2 -1
  4. package/lib/routes/5eplay/index.ts +1 -51
  5. package/lib/routes/a9vg/index.ts +214 -0
  6. package/lib/routes/a9vg/namespace.ts +1 -0
  7. package/lib/routes/a9vg/templates/description.art +17 -0
  8. package/lib/routes/aibase/discover.ts +388 -0
  9. package/lib/routes/aibase/namespace.ts +8 -0
  10. package/lib/routes/aibase/news.ts +118 -0
  11. package/lib/routes/aibase/templates/description.art +100 -0
  12. package/lib/routes/aibase/topic.ts +614 -0
  13. package/lib/routes/aibase/util.ts +114 -0
  14. package/lib/routes/apnews/api.ts +18 -3
  15. package/lib/routes/apnews/rss.ts +1 -1
  16. package/lib/routes/apnews/topics.ts +4 -3
  17. package/lib/routes/apple/podcast.ts +19 -15
  18. package/lib/routes/bangumi/templates/tv/subject.art +6 -0
  19. package/lib/routes/bangumi/tv/other/followrank.ts +19 -22
  20. package/lib/routes/bangumi/tv/user/collections.ts +172 -0
  21. package/lib/routes/bilibili/cache.ts +16 -8
  22. package/lib/routes/bilibili/utils.ts +85 -3
  23. package/lib/routes/bilibili/video.ts +7 -9
  24. package/lib/routes/bilibili/vsearch.ts +1 -1
  25. package/lib/routes/bjnews/cat.ts +9 -1
  26. package/lib/routes/cbpanet/index.ts +380 -0
  27. package/lib/routes/cbpanet/namespace.ts +8 -0
  28. package/lib/routes/ceph/blog.ts +72 -0
  29. package/lib/routes/ceph/namespace.ts +7 -0
  30. package/lib/routes/chinanews/index.ts +1 -1
  31. package/lib/routes/chinaventure/index.ts +1 -1
  32. package/lib/routes/cisia/index.ts +264 -0
  33. package/lib/routes/cisia/namespace.ts +8 -0
  34. package/lib/routes/cjlu/namespace.ts +10 -0
  35. package/lib/routes/cjlu/yjsy/index.ts +107 -0
  36. package/lib/routes/cnki/journals.ts +31 -2
  37. package/lib/routes/cohere/index.ts +54 -0
  38. package/lib/routes/cohere/namespace.ts +6 -0
  39. package/lib/routes/coolapk/dyh.ts +7 -1
  40. package/lib/routes/coolapk/hot.ts +7 -1
  41. package/lib/routes/coolapk/huati.ts +7 -1
  42. package/lib/routes/coolapk/namespace.ts +6 -0
  43. package/lib/routes/coolapk/toutiao.ts +7 -1
  44. package/lib/routes/coolapk/tuwen.ts +10 -5
  45. package/lib/routes/coolapk/user-dynamic.ts +7 -1
  46. package/lib/routes/damai/activity.ts +3 -2
  47. package/lib/routes/dcfever/trading.ts +3 -5
  48. package/lib/routes/dcfever/utils.ts +13 -4
  49. package/lib/routes/dealstreetasia/home.ts +72 -0
  50. package/lib/routes/dealstreetasia/namespace.ts +6 -0
  51. package/lib/routes/dealstreetasia/section.ts +57 -0
  52. package/lib/routes/dlsite/campaign.ts +1 -2
  53. package/lib/routes/dlsite/new.ts +1 -2
  54. package/lib/routes/dlsite/{index.ts → z-index/index.ts} +1 -1
  55. package/lib/routes/douban/other/topic.ts +10 -4
  56. package/lib/routes/douyin/types.ts +795 -0
  57. package/lib/routes/douyin/user.ts +51 -23
  58. package/lib/routes/douyin/utils.ts +1 -87
  59. package/lib/routes/dribbble/keyword.ts +1 -1
  60. package/lib/routes/dribbble/popular.ts +1 -1
  61. package/lib/routes/dribbble/user.ts +1 -1
  62. package/lib/routes/dribbble/utils.ts +16 -18
  63. package/lib/routes/famitsu/category.ts +1 -1
  64. package/lib/routes/fanbox/index.ts +1 -1
  65. package/lib/routes/fanbox/types.ts +1 -5
  66. package/lib/routes/fediverse/timeline.ts +21 -3
  67. package/lib/routes/follow/profile.ts +4 -2
  68. package/lib/routes/follow/types.ts +11 -1
  69. package/lib/routes/github/advisor.ts +97 -0
  70. package/lib/routes/github/discussions.ts +194 -0
  71. package/lib/routes/github/issue.ts +1 -1
  72. package/lib/routes/github/pulls.ts +1 -1
  73. package/lib/routes/gmcmonline/chinacustoms.ts +130 -0
  74. package/lib/routes/gmcmonline/namespace.ts +8 -0
  75. package/lib/routes/gov/csrc/csrc.ts +541 -0
  76. package/lib/routes/gov/customs/list.ts +5 -2
  77. package/lib/routes/gov/customs/namespace.ts +7 -0
  78. package/lib/routes/gov/jgjcndrc/index.ts +95 -66
  79. package/lib/routes/gov/moa/moa.ts +3 -3
  80. package/lib/routes/gov/moa/szcpxx.ts +115 -0
  81. package/lib/routes/gov/moa/zdscxx.ts +71 -21
  82. package/lib/routes/gov/ndrc/zfxxgk.ts +106 -0
  83. package/lib/routes/gov/pudong/zwgk.ts +65 -0
  84. package/lib/routes/gov/zj/search.ts +17 -12
  85. package/lib/routes/guancha/member.ts +1 -1
  86. package/lib/routes/hellogithub/index.ts +17 -67
  87. package/lib/routes/hellogithub/report.ts +1 -1
  88. package/lib/routes/hex-rays/index.ts +23 -29
  89. package/lib/routes/hfut/hf/notice.ts +43 -0
  90. package/lib/routes/hfut/hf/utils.ts +80 -0
  91. package/lib/routes/hfut/namespace.ts +6 -0
  92. package/lib/routes/hfut/xc/notice.ts +43 -0
  93. package/lib/routes/hfut/xc/utils.ts +73 -0
  94. package/lib/routes/hko/earthquake.ts +56 -0
  95. package/lib/routes/hko/namespace.ts +8 -0
  96. package/lib/routes/hko/weather.ts +56 -0
  97. package/lib/routes/huggingface/blog-zh.ts +1 -1
  98. package/lib/routes/hust/mse.ts +575 -0
  99. package/lib/routes/i-cable/namespace.ts +6 -0
  100. package/lib/routes/i-cable/news.ts +77 -0
  101. package/lib/routes/i-cable/templates/description.art +8 -0
  102. package/lib/routes/infoq/presentations.ts +203 -0
  103. package/lib/routes/infoq/templates/description.art +41 -0
  104. package/lib/routes/ipsw.dev/index.ts +64 -0
  105. package/lib/routes/ipsw.dev/namespace.ts +7 -0
  106. package/lib/routes/ipsw.dev/templates/description.art +20 -0
  107. package/lib/routes/ixigua/user-video.ts +18 -7
  108. package/lib/routes/javtiful/actress.ts +37 -0
  109. package/lib/routes/javtiful/channel.ts +37 -0
  110. package/lib/routes/javtiful/namespace.ts +6 -0
  111. package/lib/routes/javtiful/templates/description.art +7 -0
  112. package/lib/routes/javtiful/utils.ts +18 -0
  113. package/lib/routes/kisskiss/blog.ts +74 -0
  114. package/lib/routes/kisskiss/namespace.ts +6 -0
  115. package/lib/routes/ktown4u/artist-brandlist.ts +61 -0
  116. package/lib/routes/ktown4u/namespace.ts +6 -0
  117. package/lib/routes/lorientlejour/index.ts +186 -0
  118. package/lib/routes/lorientlejour/namespace.ts +7 -0
  119. package/lib/routes/lorientlejour/templates/description.art +18 -0
  120. package/lib/routes/lovelive-anime/namespace.ts +1 -1
  121. package/lib/routes/lovelive-anime/news.ts +56 -26
  122. package/lib/routes/lovelive-anime/schedules.ts +18 -6
  123. package/lib/routes/lovelive-anime/templates/description.art +1 -1
  124. package/lib/routes/lovelive-anime/templates/scheduleDesc.art +0 -1
  125. package/lib/routes/lovelive-anime/topics.ts +1 -1
  126. package/lib/routes/manhuagui/comic.ts +1 -1
  127. package/lib/routes/mi/crowdfunding.ts +43 -22
  128. package/lib/routes/mi/templates/crowdfunding.art +28 -0
  129. package/lib/routes/mi/types.ts +40 -0
  130. package/lib/routes/mi/utils.ts +80 -0
  131. package/lib/routes/misskey/utils.ts +1 -0
  132. package/lib/routes/misskon/namespace.ts +6 -0
  133. package/lib/routes/misskon/posts.ts +33 -0
  134. package/lib/routes/misskon/tag.ts +38 -0
  135. package/lib/routes/misskon/top.ts +67 -0
  136. package/lib/routes/misskon/utils.ts +41 -0
  137. package/lib/routes/natgeo/dailyphoto.ts +1 -1
  138. package/lib/routes/natgeo/dailyselection.ts +1 -1
  139. package/lib/routes/ncku/namespace.ts +1 -1
  140. package/lib/routes/ncku/phys.ts +95 -0
  141. package/lib/routes/news/namespace.ts +1 -1
  142. package/lib/routes/nikkei/cn/index.ts +12 -1
  143. package/lib/routes/nudt/yjszs.ts +33 -13
  144. package/lib/routes/nytimes/book.ts +11 -11
  145. package/lib/routes/nytimes/daily-briefing-chinese.ts +4 -4
  146. package/lib/routes/nytimes/namespace.ts +1 -1
  147. package/lib/routes/oncc/templates/article.art +1 -1
  148. package/lib/routes/papers/index.ts +7 -1
  149. package/lib/routes/parliament.uk/commonslibrary.ts +55 -0
  150. package/lib/routes/parliament.uk/lordslibrary.ts +55 -0
  151. package/lib/routes/parliament.uk/namespace.ts +6 -0
  152. package/lib/routes/picuki/profile.ts +50 -39
  153. package/lib/routes/pornhub/model.ts +3 -5
  154. package/lib/routes/pornhub/pornstar.ts +3 -5
  155. package/lib/routes/pornhub/users.ts +3 -5
  156. package/lib/routes/resonac/namespace.ts +6 -0
  157. package/lib/routes/resonac/products.ts +85 -0
  158. package/lib/routes/rsshub/transform/html.ts +114 -75
  159. package/lib/routes/rsshub/transform/json.ts +14 -8
  160. package/lib/routes/sciencenet/user.ts +3 -1
  161. package/lib/routes/scmp/utils.ts +2 -2
  162. package/lib/routes/skeb/following-creators.ts +52 -0
  163. package/lib/routes/skeb/following-works.ts +52 -0
  164. package/lib/routes/skeb/friend-works.ts +52 -0
  165. package/lib/routes/skeb/index.ts +131 -0
  166. package/lib/routes/skeb/namespace.ts +6 -0
  167. package/lib/routes/skeb/search.ts +77 -0
  168. package/lib/routes/skeb/templates/creator.art +8 -0
  169. package/lib/routes/skeb/templates/work.art +10 -0
  170. package/lib/routes/skeb/utils.ts +155 -0
  171. package/lib/routes/skeb/works.ts +88 -0
  172. package/lib/routes/skebetter/illust.ts +83 -0
  173. package/lib/routes/skebetter/index.ts +83 -0
  174. package/lib/routes/skebetter/manga.ts +65 -0
  175. package/lib/routes/skebetter/namespace.ts +6 -0
  176. package/lib/routes/skebetter/utils.ts +72 -0
  177. package/lib/routes/spankbang/namespace.ts +6 -0
  178. package/lib/routes/spankbang/new-videos.ts +90 -0
  179. package/lib/routes/spankbang/templates/video.art +7 -0
  180. package/lib/routes/straitstimes/index.ts +135 -0
  181. package/lib/routes/straitstimes/namespace.ts +7 -0
  182. package/lib/routes/straitstimes/templates/description.art +27 -0
  183. package/lib/routes/szftedu/dongtai.ts +62 -0
  184. package/lib/routes/szftedu/gonggao.ts +62 -0
  185. package/lib/routes/szftedu/namespace.ts +6 -0
  186. package/lib/routes/the/index.ts +1 -1
  187. package/lib/routes/tkww/index.ts +83 -0
  188. package/lib/routes/tkww/namespace.ts +6 -0
  189. package/lib/routes/ttv/index.ts +1 -1
  190. package/lib/routes/tvb/news.ts +2 -1
  191. package/lib/routes/twitter/api/mobile-api/login.ts +5 -0
  192. package/lib/routes/twitter/api/web-api/api.ts +16 -10
  193. package/lib/routes/twitter/api/web-api/constants.ts +1 -1
  194. package/lib/routes/twitter/api/web-api/utils.ts +94 -56
  195. package/lib/routes/twitter/list.ts +4 -12
  196. package/lib/routes/twitter/media.ts +13 -4
  197. package/lib/routes/twitter/user.ts +15 -6
  198. package/lib/routes/udn/breaking-news.ts +2 -2
  199. package/lib/routes/uestc/gr.ts +65 -37
  200. package/lib/routes/uestc/jwc.ts +49 -28
  201. package/lib/routes/wechat/ershcimi.ts +4 -2
  202. package/lib/routes/weibo/user.ts +10 -2
  203. package/lib/routes/xaut/index.ts +13 -20
  204. package/lib/routes/xaut/namespace.ts +1 -1
  205. package/lib/routes/xbookcn/blog.ts +66 -0
  206. package/lib/routes/xbookcn/namespace.ts +6 -0
  207. package/lib/routes/xiaoyuzhou/podcast.ts +35 -7
  208. package/lib/routes/xueqiu/cookies.ts +5 -3
  209. package/lib/routes/xueqiu/stock-info.ts +6 -12
  210. package/lib/routes/yande/namespace.ts +1 -1
  211. package/lib/routes/yande/post.ts +6 -6
  212. package/lib/routes/zaobao/util.ts +41 -43
  213. package/lib/routes/zhihu/activities.ts +2 -1
  214. package/lib/routes/zhihu/timeline.ts +6 -1
  215. package/lib/routes/zhihu/utils.ts +1 -1
  216. package/lib/routes/zjut/cs/index.ts +105 -0
  217. package/lib/routes/zjut/jwc/index.ts +117 -0
  218. package/lib/utils/cache/redis.ts +1 -1
  219. package/package.json +35 -36
  220. package/lib/routes/a9vg/a9vg.ts +0 -45
  221. package/lib/routes/gov/ndrc/zfxxgk/articles.ts +0 -73
  222. package/lib/routes-deprecated/hko/weather.js +0 -44
@@ -1,4 +1,4 @@
1
- import { Route } from '@/types';
1
+ import { DataItem, Route } from '@/types';
2
2
  import { getCurrentPath } from '@/utils/helpers';
3
3
  const __dirname = getCurrentPath(import.meta.url);
4
4
 
@@ -16,20 +16,29 @@ function deVideo(media) {
16
16
  let media_deVideo = '';
17
17
  $('img,video').each((_, medium) => {
18
18
  const tag = medium.name;
19
- medium = $(medium);
20
- const poster = medium.attr('poster');
19
+ const mediumElement = $(medium);
20
+ const poster = mediumElement.attr('poster');
21
21
  // 如果有 poster 属性,表明它是视频,将它替换为它的 poster;如果不是,就原样返回
22
- media_deVideo += poster ? `<img src='${poster}' alt='video poster'>` : tag === 'img' ? medium.toString() : '';
22
+ media_deVideo += (() => {
23
+ if (poster) {
24
+ return `<img src='${poster}' alt='video poster'>`;
25
+ } else if (tag === 'img') {
26
+ return mediumElement.toString();
27
+ } else {
28
+ return '';
29
+ }
30
+ })();
23
31
  });
24
32
  return media_deVideo;
25
33
  }
26
34
 
27
35
  export const route: Route = {
28
- path: '/profile/:id/:functionalFlag?',
36
+ path: '/profile/:id/:type?/:functionalFlag?',
29
37
  categories: ['social-media'],
30
38
  example: '/picuki/profile/stefaniejoosten',
31
39
  parameters: {
32
40
  id: 'Instagram user id',
41
+ type: 'Type of profile page (profile or tagged)',
33
42
  functionalFlag: `functional flag, see the table below
34
43
  | functionalFlag | Video embedding | Fetching Instagram Stories |
35
44
  | -------------- | --------------------------------------- | -------------------------- |
@@ -51,9 +60,13 @@ export const route: Route = {
51
60
  source: ['www.picuki.com/profile/:id'],
52
61
  target: '/profile/:id',
53
62
  },
63
+ {
64
+ source: ['www.picuki.com/profile-tagged/:id'],
65
+ target: '/profile/:id/tagged',
66
+ },
54
67
  ],
55
68
  name: 'User Profile - Picuki',
56
- maintainers: ['hoilc', 'Rongronggg9', 'devinmugen'],
69
+ maintainers: ['hoilc', 'Rongronggg9', 'devinmugen', 'NekoAria'],
57
70
  handler,
58
71
  description: `
59
72
  :::warning
@@ -67,61 +80,58 @@ async function handler(ctx) {
67
80
  const browser = await puppeteer();
68
81
 
69
82
  const id = ctx.req.param('id');
70
- const displayVideo = ctx.req.param('functionalFlag') !== '0';
71
- const includeStories = ctx.req.param('functionalFlag') === '10';
83
+ const type = ctx.req.param('type') ?? 'profile';
84
+ const functionalFlag = ctx.req.param('functionalFlag') ?? '1';
85
+ const displayVideo = functionalFlag !== '0';
86
+ const includeStories = functionalFlag === '10';
72
87
 
73
- const profileUrl = `https://www.picuki.com/profile/${id}`;
88
+ const profileUrl = `https://www.picuki.com/${type === 'tagged' ? 'profile-tagged' : 'profile'}/${id}`;
74
89
 
75
- const data = await cache.tryGet(
76
- `picuki-${id}-profile-${includeStories}`,
77
- async () => {
78
- const _r = await puppeteerGet(profileUrl, browser, includeStories);
79
- return _r;
80
- },
81
- config.cache.routeExpire,
82
- false
83
- );
90
+ const key = `picuki-${id}-${type}-${includeStories}`;
91
+ const data = await cache.tryGet(key, () => puppeteerGet(profileUrl, browser, includeStories), config.cache.routeExpire, false);
84
92
  const $ = load(data);
85
93
 
86
94
  const profileName = $('.profile-name-bottom').text();
95
+ const profileTitle = type === 'tagged' ? `${profileName} (@${id}) tagged posts - Picuki` : `${profileName} (@${id}) public posts - Picuki`;
87
96
  const profileImg = $('.profile-avatar > img').attr('src');
88
97
  const profileDescription = $('.profile-description').text();
89
98
 
90
- const list = $('ul.box-photos [data-s="media"]').get();
99
+ const list = $('ul.box-photos [data-s="media"]').toArray();
100
+
101
+ let items: DataItem[] = [];
91
102
 
92
- let items = [];
103
+ let description: string;
93
104
 
94
105
  if (includeStories) {
95
106
  const stories_wrapper = $('.stories_wrapper');
96
107
  if (stories_wrapper.length) {
97
108
  const storyItems = $(stories_wrapper)
98
109
  .find('.item')
99
- .get()
110
+ .toArray()
100
111
  .map((item) => {
101
112
  const $item = $(item);
102
- let title = $item.find('.stories_count');
103
- title = title.length ? title.text() : '';
104
- const pubDate = title ? chrono.parseDate(title) : new Date();
113
+ const titleElement = $item.find('.stories_count');
114
+ const title = titleElement.length ? titleElement.text() : '';
115
+ const pubDate = chrono.parseDate(title) || new Date();
105
116
  const postBox = $item.find('.launchLightbox');
106
117
  const poster = postBox.attr('data-video-poster');
107
118
  const href = postBox.attr('href');
108
119
  const type = postBox.attr('data-post-type'); // video / image
109
120
  const origin = postBox.attr('data-origin');
110
121
  const storiesBackground = $item.find('.stories_background');
111
- const storiesBackgroundUrl = storiesBackground && storiesBackground.css('background-image').match(/url\('?(.*)'?\)/);
112
- const storiesBackgroundUrlSrc = storiesBackgroundUrl && storiesBackgroundUrl[1];
113
- let description;
122
+ const storiesBackgroundUrl = storiesBackground?.css('background-image')?.match(/url\('?(.*)'?\)/);
123
+ const storiesBackgroundUrlSrc = storiesBackgroundUrl?.[1];
114
124
  if (type === 'video') {
115
125
  description = art(path.join(__dirname, 'templates/video.art'), {
116
126
  videoSrcs: [href, origin].filter(Boolean),
117
- videoPoster: poster || storiesBackgroundUrlSrc || '',
127
+ videoPoster: poster ?? storiesBackgroundUrlSrc ?? '',
118
128
  });
119
129
  } else if (type === 'image') {
120
- description = `<img src="${href || poster || storiesBackgroundUrlSrc || ''}" alt="Instagram Story">`;
130
+ description = `<img src="${href ?? poster ?? storiesBackgroundUrlSrc ?? ''}" alt="Instagram Story">`;
121
131
  }
122
132
 
123
133
  return {
124
- title: 'Instagram Story' + (pubDate ? '' : `: ${title}`),
134
+ title: title ? `Instagram Story: ${title}` : 'Instagram Story',
125
135
  author: `@${id}`,
126
136
  description,
127
137
  link: href,
@@ -149,8 +159,8 @@ async function handler(ctx) {
149
159
  $('.single-photo img').each((_, item) => {
150
160
  html += $(item).toString();
151
161
  });
152
- $('.single-photo video').each((_, item) => {
153
- item = $(item);
162
+ $('.single-photo video').each((_, element) => {
163
+ const item = $(element);
154
164
  let videoSrc = item.attr('src');
155
165
  if (videoSrc === undefined) {
156
166
  videoSrc = item.children().attr('src');
@@ -158,8 +168,9 @@ async function handler(ctx) {
158
168
  const videoPoster = item.attr('poster');
159
169
  let origin = item.parent().attr('onclick');
160
170
  if (origin) {
161
- origin = origin.match(/window\.open\('([^']*)'/);
162
- origin = origin && origin[1];
171
+ const regex = /window\.open\('([^']*)'/;
172
+ const match = regex.exec(origin);
173
+ origin = match?.[1];
163
174
  }
164
175
  html += art(path.join(__dirname, 'templates/video.art'), {
165
176
  videoSrcs: [videoSrc, origin].filter(Boolean),
@@ -172,15 +183,15 @@ async function handler(ctx) {
172
183
  items = [
173
184
  ...items,
174
185
  ...(await Promise.all(
175
- list.map(async (post) => {
176
- post = $(post);
186
+ list.map(async (item) => {
187
+ const post = $(item);
177
188
 
178
189
  const postLink = post.find('.photo > a').attr('href');
179
190
  const postTime = post.find('.time');
180
- const pubDate = postTime ? chrono.parseDate(postTime.text()) : new Date();
191
+ const pubDate = postTime?.text() ? (chrono.parseDate(postTime.text()) ?? new Date()) : new Date();
181
192
  const media_displayVideo = await getMedia(postLink);
182
193
  const postText = post
183
- .find('.photo-description')
194
+ .find('.photo-action-description')
184
195
  .text()
185
196
  .trim()
186
197
  .replaceAll(/[^\S\n]+/g, ' ')
@@ -206,7 +217,7 @@ async function handler(ctx) {
206
217
  await browser.close();
207
218
 
208
219
  return {
209
- title: `${profileName} (@${id}) - Picuki`,
220
+ title: profileTitle,
210
221
  link: profileUrl,
211
222
  image: profileImg,
212
223
  description: profileDescription,
@@ -1,4 +1,4 @@
1
- import { Route, ViewType } from '@/types';
1
+ import { Route, ViewType, Data } from '@/types';
2
2
  import got from '@/utils/got';
3
3
  import { load } from 'cheerio';
4
4
  import { isValidHost } from '@/utils/valid-host';
@@ -30,7 +30,7 @@ export const route: Route = {
30
30
  handler,
31
31
  };
32
32
 
33
- async function handler(ctx) {
33
+ async function handler(ctx): Promise<Data> {
34
34
  const { language = 'www', username, sort = '' } = ctx.req.param();
35
35
  const link = `https://${language}.pornhub.com/model/${username}/videos${sort ? `?o=${sort}` : ''}`;
36
36
  if (!isValidHost(language)) {
@@ -47,9 +47,7 @@ async function handler(ctx) {
47
47
  title: $('h1').first().text(),
48
48
  description: $('section.aboutMeSection').text().trim(),
49
49
  link,
50
- image: $('#coverPictureDefault').attr('src'),
51
- logo: $('#getAvatar').attr('src'),
52
- icon: $('#getAvatar').attr('src'),
50
+ image: $('#getAvatar').attr('src'),
53
51
  language: $('html').attr('lang'),
54
52
  item: items,
55
53
  };
@@ -1,4 +1,4 @@
1
- import { Route, ViewType } from '@/types';
1
+ import { Route, ViewType, Data } from '@/types';
2
2
  import got from '@/utils/got';
3
3
  import { load } from 'cheerio';
4
4
  import { isValidHost } from '@/utils/valid-host';
@@ -73,7 +73,7 @@ export const route: Route = {
73
73
  handler,
74
74
  };
75
75
 
76
- async function handler(ctx) {
76
+ async function handler(ctx): Promise<Data> {
77
77
  const { language = 'www', username, sort = 'mr' } = ctx.req.param();
78
78
  let link = `https://${language}.pornhub.com/pornstar/${username}?o=${sort}`;
79
79
  if (!isValidHost(language)) {
@@ -101,9 +101,7 @@ async function handler(ctx) {
101
101
  title: $('h1').first().text(),
102
102
  description: $('section.aboutMeSection').text().trim(),
103
103
  link,
104
- image: $('#coverPictureDefault').attr('src'),
105
- logo: $('#getAvatar').attr('src'),
106
- icon: $('#getAvatar').attr('src'),
104
+ image: $('#getAvatar').attr('src'),
107
105
  language: $('html').attr('lang'),
108
106
  item: items,
109
107
  };
@@ -1,4 +1,4 @@
1
- import { Route } from '@/types';
1
+ import { Route, Data } from '@/types';
2
2
  import got from '@/utils/got';
3
3
  import { load } from 'cheerio';
4
4
  import { isValidHost } from '@/utils/valid-host';
@@ -29,7 +29,7 @@ export const route: Route = {
29
29
  handler,
30
30
  };
31
31
 
32
- async function handler(ctx) {
32
+ async function handler(ctx): Promise<Data> {
33
33
  const { language = 'www', username } = ctx.req.param();
34
34
  const link = `https://${language}.pornhub.com/users/${username}/videos`;
35
35
  if (!isValidHost(language)) {
@@ -46,9 +46,7 @@ async function handler(ctx) {
46
46
  title: $('.profileUserName a').text(),
47
47
  description: $('.aboutMeText').text().trim(),
48
48
  link,
49
- image: $('#coverPictureDefault').attr('src'),
50
- logo: $('#getAvatar').attr('src'),
51
- icon: $('#getAvatar').attr('src'),
49
+ image: $('#getAvatar').attr('src'),
52
50
  language: $('html').attr('lang'),
53
51
  allowEmpty: true,
54
52
  item: items,
@@ -0,0 +1,6 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'Resonac',
5
+ url: 'www.resonac.com',
6
+ };
@@ -0,0 +1,85 @@
1
+ import { Route } from '@/types';
2
+ import cache from '@/utils/cache';
3
+ import got from '@/utils/got';
4
+ import { load } from 'cheerio';
5
+ // import { parseDate } from '@/utils/parse-date';
6
+ // import timezone from '@/utils/timezone';
7
+
8
+ const baseUrl = 'https://www.resonac.com';
9
+ const host = 'https://www.resonac.com/products?intcid=glnavi_products';
10
+
11
+ export const route: Route = {
12
+ path: '/products',
13
+ categories: ['other'],
14
+ example: '/resonac/products',
15
+ parameters: {},
16
+ features: {
17
+ requireConfig: false,
18
+ requirePuppeteer: true,
19
+ antiCrawler: false,
20
+ supportBT: false,
21
+ supportPodcast: false,
22
+ supportScihub: false,
23
+ },
24
+ name: 'Products',
25
+ maintainers: ['valuex'],
26
+ handler,
27
+ description: '',
28
+ };
29
+
30
+ async function handler() {
31
+ const response = await got(host);
32
+ const pageHtml = response.data;
33
+ const $ = load(pageHtml);
34
+ const groupLists = $('div.m-panel-card-link ul li')
35
+ .toArray()
36
+ .map((el) => ({
37
+ groupName: $('a', el).text().trim(),
38
+ groupURL: baseUrl + $('a', el).attr('href'),
39
+ }));
40
+
41
+ const lists = await Promise.all(
42
+ groupLists.map((productGroup) =>
43
+ cache.tryGet(productGroup.groupURL, async () => {
44
+ const strUrl = productGroup.groupURL;
45
+ const response = await got(strUrl);
46
+ const $ = load(response.data);
47
+ const item = $('dt.m-toggle__title div span a')
48
+ .toArray()
49
+ .map((el) => ({
50
+ title: $('b', el).text().trim(),
51
+ link: baseUrl + $(el).attr('href'),
52
+ group: productGroup.groupName,
53
+ }));
54
+ return item;
55
+ })
56
+ )
57
+ );
58
+
59
+ const fullList = lists.flat(1); // flatten array
60
+ // fullList = fullList.filter((item) => item.title !== 'Empty');
61
+
62
+ const items = await Promise.all(
63
+ fullList.map((item) =>
64
+ cache.tryGet(item.link, async () => {
65
+ try {
66
+ const response = await got(item.link);
67
+ const $ = load(response.data);
68
+ const thisTitle = item.title + ' | ' + item.group;
69
+ item.title = thisTitle;
70
+ item.description = $('main div.str-section').html();
71
+ return item;
72
+ } catch (error) {
73
+ return (error as Error).message;
74
+ }
75
+ })
76
+ )
77
+ );
78
+
79
+ return {
80
+ title: 'Resonac_Products',
81
+ link: baseUrl,
82
+ description: 'Resonac_Products',
83
+ item: items,
84
+ };
85
+ }
@@ -1,8 +1,10 @@
1
- import { Route } from '@/types';
1
+ import { DataItem, Route } from '@/types';
2
2
  import got from '@/utils/got';
3
3
  import { load } from 'cheerio';
4
4
  import { config } from '@/config';
5
5
  import ConfigNotFoundError from '@/errors/types/config-not-found';
6
+ import sanitizeHtml from 'sanitize-html';
7
+ import cache from '@/utils/cache';
6
8
 
7
9
  export const route: Route = {
8
10
  path: '/transform/html/:url/:routeParams',
@@ -23,24 +25,25 @@ export const route: Route = {
23
25
  supportScihub: false,
24
26
  },
25
27
  name: 'Transformation - HTML',
26
- maintainers: ['ttttmr'],
27
- handler,
28
+ maintainers: ['ttttmr', 'hyoban'],
28
29
  description: `Pass URL and transformation rules to convert HTML/JSON into RSS.
29
30
 
30
31
  Specify options (in the format of query string) in parameter \`routeParams\` parameter to extract data from HTML.
31
32
 
32
- | Key | Meaning | Accepted Values | Default |
33
- | ----------------- | -------------------------------------------------------------- | --------------- | ---------------------- |
34
- | \`title\` | The title of the RSS | \`string\` | Extract from \`<title>\` |
35
- | \`item\` | The HTML elements as \`item\` using CSS selector | \`string\` | html |
36
- | \`itemTitle\` | The HTML elements as \`title\` in \`item\` using CSS selector | \`string\` | \`item\` element |
37
- | \`itemTitleAttr\` | The attributes of \`title\` element as title | \`string\` | Element text |
38
- | \`itemLink\` | The HTML elements as \`link\` in \`item\` using CSS selector | \`string\` | \`item\` element |
39
- | \`itemLinkAttr\` | The attributes of \`link\` element as link | \`string\` | \`href\` |
40
- | \`itemDesc\` | The HTML elements as \`descrption\` in \`item\` using CSS selector | \`string\` | \`item\` element |
41
- | \`itemDescAttr\` | The attributes of \`descrption\` element as description | \`string\` | Element html |
42
- | \`itemPubDate\` | The HTML elements as \`pubDate\` in \`item\` using CSS selector | \`string\` | \`item\` element |
43
- | \`itemPubDateAttr\` | The attributes of \`pubDate\` element as pubDate | \`string\` | Element html |
33
+ | Key | Meaning | Accepted Values | Default |
34
+ | ------------------- | ------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------ |
35
+ | \`title\` | The title of the RSS | \`string\` | Extract from \`<title>\` |
36
+ | \`item\` | The HTML elements as \`item\` using CSS selector | \`string\` | html |
37
+ | \`itemTitle\` | The HTML elements as \`title\` in \`item\` using CSS selector | \`string\` | \`item\` element |
38
+ | \`itemTitleAttr\` | The attributes of \`title\` element as title | \`string\` | Element text |
39
+ | \`itemLink\` | The HTML elements as \`link\` in \`item\` using CSS selector | \`string\` | \`item\` element |
40
+ | \`itemLinkAttr\` | The attributes of \`link\` element as link | \`string\` | \`href\` |
41
+ | \`itemDesc\` | The HTML elements as \`descrption\` in \`item\` using CSS selector | \`string\` | \`item\` element |
42
+ | \`itemDescAttr\` | The attributes of \`descrption\` element as description | \`string\` | Element html |
43
+ | \`itemPubDate\` | The HTML elements as \`pubDate\` in \`item\` using CSS selector | \`string\` | \`item\` element |
44
+ | \`itemPubDateAttr\` | The attributes of \`pubDate\` element as pubDate | \`string\` | Element html |
45
+ | \`itemContent\` | The HTML elements as \`description\` in \`item\` using CSS selector ( in \`itemLink\` page for full content ) | \`string\` | |
46
+ | \`encoding\` | The encoding of the HTML content | \`string\` | utf-8 |
44
47
 
45
48
  Parameters parsing in the above example:
46
49
 
@@ -54,66 +57,102 @@ Specify options (in the format of query string) in parameter \`routeParams\` par
54
57
  | Parameter | Value |
55
58
  | --------- | ------------------------------- |
56
59
  | \`item\` | \`div[class='post-content'] p a\` |`,
57
- };
60
+ handler: async (ctx) => {
61
+ if (!config.feature.allow_user_supply_unsafe_domain) {
62
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
63
+ }
64
+ const url = ctx.req.param('url');
65
+ const response = await got({
66
+ method: 'get',
67
+ url,
68
+ responseType: 'arrayBuffer',
69
+ });
58
70
 
59
- async function handler(ctx) {
60
- if (!config.feature.allow_user_supply_unsafe_domain) {
61
- throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
62
- }
63
- const url = ctx.req.param('url');
64
- const response = await got({
65
- method: 'get',
66
- url,
67
- });
68
-
69
- const routeParams = new URLSearchParams(ctx.req.param('routeParams'));
70
- const $ = load(response.data);
71
- const rssTitle = routeParams.get('title') || $('title').text();
72
- const item = routeParams.get('item') || 'html';
73
- const items = $(item)
74
- .toArray()
75
- .map((item) => {
76
- try {
77
- item = $(item);
78
-
79
- const titleEle = routeParams.get('itemTitle') ? item.find(routeParams.get('itemTitle')) : item;
80
- const title = routeParams.get('itemTitleAttr') ? titleEle.attr(routeParams.get('itemTitleAttr')) : titleEle.text();
81
-
82
- let link;
83
- const linkEle = routeParams.get('itemLink') ? item.find(routeParams.get('itemLink')) : item;
84
- if (routeParams.get('itemLinkAttr')) {
85
- link = linkEle.attr(routeParams.get('itemLinkAttr'));
86
- } else {
87
- link = linkEle.is('a') ? linkEle.attr('href') : linkEle.find('a').attr('href');
88
- }
89
- // 补全绝对链接
90
- link = link.trim();
91
- if (link && !link.startsWith('http')) {
92
- link = `${new URL(url).origin}${link}`;
71
+ const routeParams = new URLSearchParams(ctx.req.param('routeParams'));
72
+ const encoding = routeParams.get('encoding') || 'utf-8';
73
+ const decoder = new TextDecoder(encoding);
74
+
75
+ const $ = load(decoder.decode(response.data));
76
+ const rssTitle = routeParams.get('title') || $('title').text();
77
+ const item = routeParams.get('item') || 'html';
78
+ let items: DataItem[] = $(item)
79
+ .toArray()
80
+ .slice(0, 20)
81
+ .map((item) => {
82
+ try {
83
+ item = $(item);
84
+
85
+ const titleEle = routeParams.get('itemTitle') ? item.find(routeParams.get('itemTitle')) : item;
86
+ const title = routeParams.get('itemTitleAttr') ? titleEle.attr(routeParams.get('itemTitleAttr')) : titleEle.text();
87
+
88
+ let link;
89
+ const linkEle = routeParams.get('itemLink') ? item.find(routeParams.get('itemLink')) : item;
90
+ if (routeParams.get('itemLinkAttr')) {
91
+ link = linkEle.attr(routeParams.get('itemLinkAttr'));
92
+ } else {
93
+ link = linkEle.is('a') ? linkEle.attr('href') : linkEle.find('a').attr('href');
94
+ }
95
+ // 补全绝对链接
96
+ link = link.trim();
97
+ if (link && !link.startsWith('http')) {
98
+ link = `${new URL(url).origin}${link}`;
99
+ }
100
+
101
+ const descEle = routeParams.get('itemDesc') ? item.find(routeParams.get('itemDesc')) : item;
102
+ const desc = routeParams.get('itemDescAttr') ? descEle.attr(routeParams.get('itemDescAttr')) : descEle.html();
103
+
104
+ const pubDateEle = routeParams.get('itemPubDate') ? item.find(routeParams.get('itemPubDate')) : item;
105
+ const pubDate = routeParams.get('itemPubDateAttr') ? pubDateEle.attr(routeParams.get('itemPubDateAttr')) : pubDateEle.html();
106
+
107
+ return {
108
+ title,
109
+ link,
110
+ description: desc,
111
+ pubDate,
112
+ };
113
+ } catch {
114
+ return null;
93
115
  }
116
+ })
117
+ .filter((i) => !!i);
94
118
 
95
- const descEle = routeParams.get('itemDesc') ? item.find(routeParams.get('itemDesc')) : item;
96
- const desc = routeParams.get('itemDescAttr') ? descEle.attr(routeParams.get('itemDescAttr')) : descEle.html();
97
-
98
- const pubDateEle = routeParams.get('itemPubDate') ? item.find(routeParams.get('itemPubDate')) : item;
99
- const pubDate = routeParams.get('itemPubDateAttr') ? pubDateEle.attr(routeParams.get('itemPubDateAttr')) : pubDateEle.html();
100
-
101
- return {
102
- title,
103
- link,
104
- description: desc,
105
- pubDate,
106
- };
107
- } catch {
108
- return null;
109
- }
110
- })
111
- .filter(Boolean);
112
-
113
- return {
114
- title: rssTitle,
115
- link: url,
116
- description: `Proxy ${url}`,
117
- item: items,
118
- };
119
- }
119
+ const itemContentSelector = routeParams.get('itemContent');
120
+ if (itemContentSelector) {
121
+ items = await Promise.all(
122
+ items.map((item) => {
123
+ if (!item.link) {
124
+ return item;
125
+ }
126
+
127
+ return cache.tryGet(item.link, async () => {
128
+ const response = await got({
129
+ method: 'get',
130
+ url: item.link,
131
+ responseType: 'arrayBuffer',
132
+ });
133
+ if (!response || typeof response === 'string') {
134
+ return item;
135
+ }
136
+
137
+ const $ = load(decoder.decode(response.data));
138
+ const content = $(itemContentSelector).html();
139
+ if (!content) {
140
+ return item;
141
+ }
142
+
143
+ item.description = sanitizeHtml(content, { allowedTags: [...sanitizeHtml.defaults.allowedTags, 'img'] });
144
+
145
+ return item;
146
+ });
147
+ })
148
+ );
149
+ }
150
+
151
+ return {
152
+ title: rssTitle,
153
+ link: url,
154
+ description: `Proxy ${url}`,
155
+ item: items,
156
+ };
157
+ },
158
+ };
@@ -39,14 +39,15 @@ export const route: Route = {
39
39
  handler,
40
40
  description: `Specify options (in the format of query string) in parameter \`routeParams\` parameter to extract data from JSON.
41
41
 
42
- | Key | Meaning | Accepted Values | Default |
43
- | ------------- | ---------------------------------------- | --------------- | ------------------------------------------ |
44
- | \`title\` | The title of the RSS | \`string\` | Extracted from home page of current domain |
45
- | \`item\` | The JSON Path as \`item\` element | \`string\` | Entire JSON response |
46
- | \`itemTitle\` | The JSON Path as \`title\` in \`item\` | \`string\` | None |
47
- | \`itemLink\` | The JSON Path as \`link\` in \`item\` | \`string\` | None |
48
- | \`itemDesc\` | The JSON Path as \`description\` in \`item\` | \`string\` | None |
49
- | \`itemPubDate\` | The JSON Path as \`pubDate\` in \`item\` | \`string\` | None |
42
+ | Key | Meaning | Accepted Values | Default |
43
+ | ------------------ | -------------------------------------------- | ----------------- | ------------------------------------------ |
44
+ | \`title\` | The title of the RSS | \`string\` | Extracted from home page of current domain |
45
+ | \`item\` | The JSON Path as \`item\` element | \`string\` | Entire JSON response |
46
+ | \`itemTitle\` | The JSON Path as \`title\` in \`item\` | \`string\` | None |
47
+ | \`itemLink\` | The JSON Path as \`link\` in \`item\` | \`string\` | None |
48
+ | \`itemLinkPrefix\` | Optional Prefix for \`itemLink\` value | \`string\` | None |
49
+ | \`itemDesc\` | The JSON Path as \`description\` in \`item\` | \`string\` | None |
50
+ | \`itemPubDate\` | The JSON Path as \`pubDate\` in \`item\` | \`string\` | None |
50
51
 
51
52
  :::tip
52
53
  JSON Path only supports format like \`a.b.c\`. if you need to access arrays, like \`a[0].b\`, you can write it as \`a.0.b\`.
@@ -92,6 +93,11 @@ async function handler(ctx) {
92
93
 
93
94
  const items = jsonGet(response.data, routeParams.get('item')).map((item) => {
94
95
  let link = jsonGet(item, routeParams.get('itemLink')).trim();
96
+ const linkPrefix = routeParams.get('itemLinkPrefix');
97
+
98
+ if (link && linkPrefix) {
99
+ link = `${linkPrefix}${link}`;
100
+ }
95
101
  // 补全绝对链接
96
102
  if (link && !link.startsWith('http')) {
97
103
  link = `${new URL(url).origin}${link}`;
@@ -31,6 +31,7 @@ export const route: Route = {
31
31
 
32
32
  async function handler(ctx) {
33
33
  const id = ctx.req.param('id');
34
+ const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50;
34
35
 
35
36
  const rootUrl = 'https://blog.sciencenet.cn';
36
37
  const currentUrl = `${rootUrl}/u/${id}`;
@@ -51,8 +52,9 @@ async function handler(ctx) {
51
52
  $ = load(response.data);
52
53
 
53
54
  let items = $('item')
54
- .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50)
55
+ .slice(-limit)
55
56
  .toArray()
57
+ .reverse()
56
58
  .map((item) => {
57
59
  item = $(item);
58
60