rsshub 1.0.0-master.f7cdf8b → 1.0.0-master.f7f8b7a

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 (273) hide show
  1. package/README.md +3 -3
  2. package/lib/config.js +21 -8
  3. package/lib/middleware/cache/index.js +4 -3
  4. package/lib/middleware/cache/redis.js +3 -3
  5. package/lib/middleware/onerror.js +8 -0
  6. package/lib/middleware/template.js +79 -85
  7. package/lib/router.js +7 -7
  8. package/lib/routes/index.js +6 -13
  9. package/lib/utils/common-utils.js +2 -6
  10. package/lib/utils/render.js +2 -0
  11. package/lib/v2/1point3acres/maintainer.js +2 -2
  12. package/lib/v2/95mm/utils.js +3 -2
  13. package/lib/v2/aliyun/notice.js +1 -1
  14. package/lib/v2/amazon/maintainer.js +1 -1
  15. package/lib/v2/apnews/topics.js +1 -1
  16. package/lib/v2/arcteryx/maintainer.js +3 -3
  17. package/lib/v2/bangumi/maintainer.js +1 -0
  18. package/lib/v2/bangumi/radar.js +6 -0
  19. package/lib/v2/bangumi/router.js +1 -0
  20. package/lib/v2/bangumi/tv/user/wish.js +38 -0
  21. package/lib/v2/bellroy/maintainer.js +1 -1
  22. package/lib/v2/bilibili/cache.js +10 -7
  23. package/lib/v2/bilibili/maintainer.js +1 -0
  24. package/lib/v2/bilibili/platform.js +43 -0
  25. package/lib/v2/bilibili/radar.js +8 -0
  26. package/lib/v2/bilibili/router.js +1 -0
  27. package/lib/v2/bilibili/video.js +8 -1
  28. package/lib/v2/bossdesign/index.js +41 -0
  29. package/lib/v2/bossdesign/maintainer.js +3 -0
  30. package/lib/v2/bossdesign/radar.js +13 -0
  31. package/lib/v2/bossdesign/router.js +3 -0
  32. package/lib/v2/brave/latest.js +4 -2
  33. package/lib/v2/buaa/maintainer.js +4 -0
  34. package/lib/v2/buaa/news/index.js +46 -0
  35. package/lib/v2/buaa/radar.js +21 -0
  36. package/lib/v2/buaa/router.js +4 -0
  37. package/lib/v2/buaa/sme.js +59 -0
  38. package/lib/v2/cfachina/analygarden.js +60 -0
  39. package/lib/v2/cfachina/maintainer.js +3 -0
  40. package/lib/v2/cfachina/radar.js +13 -0
  41. package/lib/v2/cfachina/router.js +3 -0
  42. package/lib/v2/cna/maintainer.js +1 -0
  43. package/lib/v2/cna/radar.js +6 -0
  44. package/lib/v2/cna/router.js +1 -0
  45. package/lib/v2/cna/web/index.js +62 -0
  46. package/lib/v2/cste/radar.js +1 -1
  47. package/lib/v2/dahecube/radar.js +40 -4
  48. package/lib/v2/dongqiudi/utils.js +1 -1
  49. package/lib/v2/douban/maintainer.js +1 -0
  50. package/lib/v2/douban/other/discussion.js +58 -0
  51. package/lib/v2/douban/radar.js +8 -0
  52. package/lib/v2/douban/router.js +1 -0
  53. package/lib/v2/dut/defaults.js +4 -0
  54. package/lib/v2/dut/index.js +21 -9
  55. package/lib/v2/dut/maintainer.js +8 -7
  56. package/lib/v2/dut/radar.js +37 -29
  57. package/lib/v2/dut/shortcuts.js +5 -0
  58. package/lib/v2/egsea/flash.js +30 -0
  59. package/lib/v2/egsea/maintainer.js +3 -0
  60. package/lib/v2/egsea/radar.js +13 -0
  61. package/lib/v2/egsea/router.js +3 -0
  62. package/lib/v2/fisher-spb/news.js +1 -2
  63. package/lib/v2/fosshub/radar.js +1 -1
  64. package/lib/v2/fxiaoke/crm.js +63 -0
  65. package/lib/v2/fxiaoke/maintainer.js +3 -0
  66. package/lib/v2/fxiaoke/radar.js +37 -0
  67. package/lib/v2/fxiaoke/router.js +3 -0
  68. package/lib/v2/gdsrx/index.js +72 -0
  69. package/lib/v2/gdsrx/maintainer.js +3 -0
  70. package/lib/v2/gdsrx/radar.js +61 -0
  71. package/lib/v2/gdsrx/router.js +3 -0
  72. package/lib/v2/gogoanimehd/radar.js +1 -1
  73. package/lib/v2/gogoanimehd/recent-releases.js +1 -1
  74. package/lib/v2/google/alerts.js +33 -0
  75. package/lib/v2/google/maintainer.js +1 -0
  76. package/lib/v2/google/router.js +1 -0
  77. package/lib/v2/gov/maintainer.js +9 -2
  78. package/lib/v2/gov/moa/zdscxx.js +104 -0
  79. package/lib/v2/gov/mot/index.js +66 -0
  80. package/lib/v2/gov/nea/ghs.js +58 -0
  81. package/lib/v2/gov/nifdc/index.js +40 -33
  82. package/lib/v2/gov/radar.js +232 -4
  83. package/lib/v2/gov/router.js +10 -2
  84. package/lib/v2/gov/safe/business.js +8 -0
  85. package/lib/v2/gov/safe/complaint.js +8 -0
  86. package/lib/v2/gov/safe/templates/message.art +25 -0
  87. package/lib/v2/gov/safe/util.js +87 -0
  88. package/lib/v2/gov/zhengce/index.js +99 -0
  89. package/lib/v2/greasyfork/maintainer.js +1 -0
  90. package/lib/v2/greasyfork/router.js +1 -0
  91. package/lib/v2/greasyfork/scripts.js +25 -20
  92. package/lib/{routes/universities → v2}/harvard/health/blog.js +8 -7
  93. package/lib/v2/harvard/maintainer.js +3 -0
  94. package/lib/v2/harvard/radar.js +13 -0
  95. package/lib/v2/harvard/router.js +3 -0
  96. package/lib/v2/huxiu/briefColumn.js +13 -25
  97. package/lib/v2/huxiu/channel.js +28 -0
  98. package/lib/v2/huxiu/club.js +30 -0
  99. package/lib/v2/huxiu/collection.js +14 -26
  100. package/lib/v2/huxiu/maintainer.js +8 -5
  101. package/lib/v2/huxiu/member.js +27 -0
  102. package/lib/v2/huxiu/moment.js +11 -25
  103. package/lib/v2/huxiu/radar.js +24 -16
  104. package/lib/v2/huxiu/router.js +7 -4
  105. package/lib/v2/huxiu/search.js +16 -21
  106. package/lib/v2/huxiu/tag.js +13 -25
  107. package/lib/v2/huxiu/templates/description.art +46 -0
  108. package/lib/v2/huxiu/util.js +460 -0
  109. package/lib/v2/ifi-audio/maintainer.js +1 -1
  110. package/lib/v2/imiker/jinghua.js +87 -0
  111. package/lib/v2/imiker/maintainer.js +3 -0
  112. package/lib/v2/imiker/radar.js +13 -0
  113. package/lib/v2/imiker/router.js +3 -0
  114. package/lib/v2/imiker/templates/description.art +32 -0
  115. package/lib/v2/inoreader/maintainer.js +1 -1
  116. package/lib/v2/instagram/common-utils.js +7 -4
  117. package/lib/v2/instagram/templates/images.art +9 -2
  118. package/lib/v2/instagram/templates/video.art +4 -1
  119. package/lib/v2/instagram/web-api/index.js +30 -9
  120. package/lib/v2/instagram/web-api/utils.js +149 -25
  121. package/lib/v2/kemono/index.js +26 -2
  122. package/lib/v2/kemono/radar.js +1 -1
  123. package/lib/v2/kepu/live.js +89 -0
  124. package/lib/v2/kepu/maintainer.js +3 -0
  125. package/lib/v2/kepu/radar.js +13 -0
  126. package/lib/v2/kepu/router.js +3 -0
  127. package/lib/v2/kepu/templates/description.art +33 -0
  128. package/lib/v2/leetcode/maintainer.js +1 -1
  129. package/lib/v2/lightnovel/lightNovel.js +62 -0
  130. package/lib/v2/lightnovel/maintainer.js +3 -0
  131. package/lib/v2/lightnovel/radar.js +13 -0
  132. package/lib/v2/lightnovel/router.js +3 -0
  133. package/lib/v2/line/radar.js +1 -1
  134. package/lib/v2/liquipedia/cs_matches.js +52 -0
  135. package/lib/v2/liquipedia/dota2_matches.js +47 -0
  136. package/lib/v2/liquipedia/maintainer.js +4 -0
  137. package/lib/v2/liquipedia/radar.js +19 -0
  138. package/lib/v2/liquipedia/router.js +4 -0
  139. package/lib/v2/magazinelib/maintainer.js +1 -1
  140. package/lib/v2/mihoyo/radar.js +1 -1
  141. package/lib/v2/missav/maintainer.js +3 -0
  142. package/lib/v2/missav/new.js +36 -0
  143. package/lib/v2/missav/radar.js +13 -0
  144. package/lib/v2/missav/router.js +3 -0
  145. package/lib/v2/missav/templates/preview.art +3 -0
  146. package/lib/v2/modb/maintainer.js +3 -0
  147. package/lib/v2/modb/radar.js +13 -0
  148. package/lib/v2/modb/router.js +3 -0
  149. package/lib/v2/modb/topic.js +59 -0
  150. package/lib/v2/nature/cover.js +32 -42
  151. package/lib/v2/nature/highlight.js +2 -2
  152. package/lib/v2/nature/news-and-comment.js +2 -2
  153. package/lib/v2/nature/news.js +2 -2
  154. package/lib/v2/nature/research.js +2 -2
  155. package/lib/v2/nature/siteindex.js +5 -5
  156. package/lib/v2/nature/utils.js +124 -5
  157. package/lib/v2/ncu/jwc.js +33 -0
  158. package/lib/v2/ncu/maintainer.js +3 -0
  159. package/lib/v2/ncu/radar.js +13 -0
  160. package/lib/v2/ncu/router.js +3 -0
  161. package/lib/v2/nmtv/radar.js +1 -1
  162. package/lib/v2/oo-software/radar.js +1 -1
  163. package/lib/v2/patagonia/maintainer.js +1 -1
  164. package/lib/v2/phoronix/index.js +177 -48
  165. package/lib/v2/phoronix/maintainer.js +1 -1
  166. package/lib/v2/phoronix/radar.js +3 -3
  167. package/lib/v2/phoronix/router.js +1 -1
  168. package/lib/v2/picnob/maintainer.js +1 -1
  169. package/lib/v2/picnob/templates/desc.art +3 -3
  170. package/lib/v2/picnob/user.js +74 -37
  171. package/lib/v2/picnob/utils.js +24 -0
  172. package/lib/v2/qbitai/category.js +27 -0
  173. package/lib/v2/qbitai/maintainer.js +4 -0
  174. package/lib/v2/qbitai/radar.js +19 -0
  175. package/lib/v2/qbitai/router.js +4 -0
  176. package/lib/v2/qbitai/tag.js +27 -0
  177. package/lib/v2/questmobile/maintainer.js +3 -0
  178. package/lib/v2/questmobile/radar.js +18 -0
  179. package/lib/v2/questmobile/report.js +127 -0
  180. package/lib/v2/questmobile/router.js +3 -0
  181. package/lib/v2/questmobile/templates/description.art +17 -0
  182. package/lib/v2/readhub/daily.js +43 -0
  183. package/lib/v2/readhub/index.js +19 -72
  184. package/lib/v2/readhub/maintainer.js +1 -0
  185. package/lib/v2/readhub/radar.js +6 -0
  186. package/lib/v2/readhub/router.js +1 -0
  187. package/lib/v2/readhub/util.js +69 -0
  188. package/lib/v2/routledge/book-series.js +78 -0
  189. package/lib/v2/routledge/maintainer.js +3 -0
  190. package/lib/v2/routledge/radar.js +13 -0
  191. package/lib/v2/routledge/router.js +3 -0
  192. package/lib/v2/routledge/templates/description.art +7 -0
  193. package/lib/v2/showstart/artist.js +15 -0
  194. package/lib/v2/showstart/brand.js +15 -0
  195. package/lib/v2/showstart/const.js +4 -0
  196. package/lib/v2/showstart/event.js +18 -0
  197. package/lib/v2/showstart/maintainer.js +6 -0
  198. package/lib/v2/showstart/radar.js +40 -0
  199. package/lib/v2/showstart/router.js +6 -0
  200. package/lib/v2/showstart/search.js +51 -0
  201. package/lib/v2/showstart/service.js +166 -0
  202. package/lib/v2/showstart/utils.js +92 -0
  203. package/lib/v2/snowpeak/maintainer.js +1 -1
  204. package/lib/v2/sony/maintainer.js +1 -1
  205. package/lib/v2/sse/disclosure.js +4 -3
  206. package/lib/v2/sspu/jwc.js +44 -0
  207. package/lib/v2/sspu/maintainer.js +3 -0
  208. package/lib/v2/sspu/radar.js +13 -0
  209. package/lib/v2/sspu/router.js +3 -0
  210. package/lib/v2/techcrunch/maintainer.js +1 -1
  211. package/lib/v2/theatlantic/maintainer.js +1 -1
  212. package/lib/v2/thepaper/radar.js +2 -2
  213. package/lib/v2/threads/index.js +20 -150
  214. package/lib/v2/threads/utils.js +147 -0
  215. package/lib/v2/twitter/keyword.js +1 -3
  216. package/lib/v2/twitter/media.js +1 -5
  217. package/lib/v2/twitter/user.js +1 -3
  218. package/lib/v2/twitter/utils.js +5 -3
  219. package/lib/v2/twitter/web-api/constants.js +80 -68
  220. package/lib/v2/twitter/web-api/twitter-api.js +96 -103
  221. package/lib/v2/uniqlo/maintainer.js +3 -0
  222. package/lib/v2/uniqlo/new.js +62 -0
  223. package/lib/v2/uniqlo/radar.js +12 -0
  224. package/lib/v2/uniqlo/router.js +3 -0
  225. package/lib/v2/wechat/maintainer.js +1 -1
  226. package/lib/v2/weibo/maintainer.js +1 -1
  227. package/lib/v2/weibo/radar.js +1 -1
  228. package/lib/v2/weibo/router.js +1 -1
  229. package/lib/v2/weibo/search/hot.js +118 -7
  230. package/lib/v2/weibo/search/template/digest.art +17 -0
  231. package/lib/v2/xiaohongshu/util.js +17 -7
  232. package/lib/v2/xmnn/docs.js +0 -0
  233. package/lib/v2/xmnn/maintainer.js +1 -0
  234. package/lib/v2/xmnn/news.js +65 -0
  235. package/lib/v2/xmnn/radar.js +67 -1
  236. package/lib/v2/xmnn/router.js +1 -0
  237. package/lib/v2/xsijishe/maintainer.js +1 -0
  238. package/lib/v2/xsijishe/radar.js +12 -0
  239. package/lib/v2/xsijishe/rank.js +60 -0
  240. package/lib/v2/xsijishe/router.js +1 -0
  241. package/lib/v2/yahoo/maintainer.js +2 -0
  242. package/lib/v2/yahoo/news/tw/index.js +24 -0
  243. package/lib/v2/yahoo/news/tw/provider-helper.js +22 -0
  244. package/lib/v2/yahoo/news/tw/provider.js +24 -0
  245. package/lib/v2/yahoo/news/tw/utils.js +129 -0
  246. package/lib/v2/yahoo/radar.js +29 -1
  247. package/lib/v2/yahoo/router.js +4 -1
  248. package/lib/v2/yahoo/templates/youtube.art +1 -0
  249. package/lib/v2/yuque/book.js +6 -1
  250. package/lib/v2/yuque/utils.js +16 -0
  251. package/lib/v2/zagg/maintainer.js +1 -1
  252. package/lib/views/error.art +1 -1
  253. package/lib/views/rss3-ums.js +62 -0
  254. package/lib/views/welcome.art +1 -1
  255. package/package.json +45 -39
  256. package/lib/routes/egsea/flash.js +0 -40
  257. package/lib/routes/gov/moa/sjzxfb.js +0 -20
  258. package/lib/routes/liquipedia/dota2_matches.js +0 -50
  259. package/lib/routes/questmobile/report.js +0 -121
  260. package/lib/routes/uniqlo/stylingbook.js +0 -24
  261. package/lib/routes/universities/buaa/news/index.js +0 -66
  262. package/lib/routes/universities/buaa/utils.js +0 -57
  263. package/lib/v2/gov/zhengce/zuixin.js +0 -39
  264. package/lib/v2/huxiu/article.js +0 -33
  265. package/lib/v2/huxiu/author.js +0 -36
  266. package/lib/v2/huxiu/templates/brief.art +0 -22
  267. package/lib/v2/huxiu/templates/img.art +0 -3
  268. package/lib/v2/huxiu/templates/moment.art +0 -16
  269. package/lib/v2/huxiu/templates/video.art +0 -7
  270. package/lib/v2/huxiu/utils.js +0 -154
  271. package/lib/v2/twitter/web-api/twitter-got.js +0 -113
  272. /package/lib/{routes → v2}/gov/moa/moa.js +0 -0
  273. /package/lib/v2/yahoo/news/{index.js → us/index.js} +0 -0
@@ -0,0 +1,460 @@
1
+ const got = require('@/utils/got');
2
+ const cheerio = require('cheerio');
3
+ const { parseDate } = require('@/utils/parse-date');
4
+ const { art } = require('@/utils/render');
5
+ const path = require('path');
6
+ const CryptoJS = require('crypto-js');
7
+
8
+ const domain = 'huxiu.com';
9
+ const rootUrl = `https://www.${domain}`;
10
+
11
+ const apiArticleRootUrl = `https://api-article.${domain}`;
12
+ const apiBriefRootUrl = `https://api-brief.${domain}`;
13
+ const apiMemberRootUrl = `https://api-account.${domain}`;
14
+ const apiMomentRootUrl = `https://moment-api.${domain}`;
15
+ const apiSearchRootUrl = `https://search-api.${domain}`;
16
+
17
+ /**
18
+ * Cleans up HTML data by removing specific elements and attributes.
19
+ *
20
+ * @param {string} data - The HTML data to clean up.
21
+ * @returns {string} - The cleaned up HTML data.
22
+ */
23
+ const cleanUpHTML = (data) => {
24
+ const $ = cheerio.load(data);
25
+
26
+ $('div.neirong-shouquan').remove();
27
+ $('em.vote__bar, div.vote__btn, div.vote__time').remove();
28
+ $('p img').each((_, e) => {
29
+ e = $(e);
30
+ e.parent().replaceWith(
31
+ art(path.join(__dirname, 'templates/description.art'), {
32
+ image: {
33
+ src: (e.prop('src') ?? e.prop('_src')).split(/\?/)[0],
34
+ width: e.prop('data-w'),
35
+ height: e.prop('data-h'),
36
+ },
37
+ })
38
+ );
39
+ });
40
+ $('p, span').each((_, e) => {
41
+ e = $(e);
42
+ if (e.contents().length === 1 && /^\s*$/.test(e.text())) {
43
+ e.remove();
44
+ } else {
45
+ e.removeClass();
46
+ e.removeAttr('data-check-id label class');
47
+ }
48
+ });
49
+ $('.text-big-title').each((_, e) => {
50
+ e.tagName = 'h3';
51
+ e = $(e);
52
+ e.removeClass();
53
+ e.removeAttr('class');
54
+ });
55
+ $('.text-sm-title').each((_, e) => {
56
+ e.tagName = 'h4';
57
+ e = $(e);
58
+ e.removeClass();
59
+ e.removeAttr('class');
60
+ });
61
+
62
+ return $.html();
63
+ };
64
+
65
+ /**
66
+ * Fetch brief column data for the specified ID.
67
+ *
68
+ * @param {string} url - The ID of the brief column to fetch data from.
69
+ * @returns {Promise<Object>} A promise that resolves to an object containing the fetched data
70
+ * to be added into `ctx.state.data`.
71
+ */
72
+ const fetchBriefColumnData = async (id) => {
73
+ const apiBriefColumnUrl = new URL('briefColumn/detail', apiBriefRootUrl).href;
74
+
75
+ const {
76
+ data: { data },
77
+ } = await got.post(apiBriefColumnUrl, {
78
+ form: {
79
+ platform: 'www',
80
+ brief_column_id: id,
81
+ },
82
+ });
83
+
84
+ const currentUrl = new URL(`club/${data.club_id}.html`, rootUrl).href;
85
+
86
+ const { data: currentResponse } = await got(currentUrl);
87
+
88
+ const $ = cheerio.load(currentResponse);
89
+
90
+ const subtitle = `${data.name}-${data.sub_name}`;
91
+ const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
92
+ const author = $('meta[name="author"]').prop('content');
93
+
94
+ return {
95
+ title: `${subtitle}-${author}`,
96
+ link: currentUrl,
97
+ description: data.summary,
98
+ language: $('html').prop('lang'),
99
+ image: data.head_img,
100
+ icon,
101
+ logo: icon,
102
+ subtitle,
103
+ author,
104
+ itunes_author: author,
105
+ itunes_category: 'News',
106
+ allowEmpty: true,
107
+ };
108
+ };
109
+
110
+ /**
111
+ * Fetches club data for the specified ID and the ID of the default brief column.
112
+ *
113
+ * @param {string} id - The ID of the club to fetch data from.
114
+ * @returns {Promise<Object>} data - A promise that resolves to an object containing the fetched data
115
+ * to be added into `ctx.state.data`.
116
+ * @returns {string} id - the ID of the default brief column.
117
+ */
118
+ const fetchClubData = async (id) => {
119
+ const currentUrl = new URL(`club/${id}.html`, rootUrl).href;
120
+
121
+ const { data: currentResponse } = await got(currentUrl);
122
+
123
+ const $ = cheerio.load(currentResponse);
124
+
125
+ const title = $('title').text();
126
+ const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
127
+ const author = $('meta[name="author"]').prop('content');
128
+
129
+ return {
130
+ data: {
131
+ title,
132
+ link: currentUrl,
133
+ description: $('ul.content-item li.content').text().trim(),
134
+ language: $('html').prop('lang'),
135
+ image: $('div.header img.img').prop('data-src')?.split(/\?/)[0] ?? undefined,
136
+ icon,
137
+ logo: icon,
138
+ subtitle: title.split(/-/)[0],
139
+ author,
140
+ itunes_author: author,
141
+ itunes_category: 'News',
142
+ allowEmpty: true,
143
+ },
144
+ briefColumnId: currentResponse.match(/"brief_column_id":"(\d+)",/)[1],
145
+ };
146
+ };
147
+
148
+ /**
149
+ * Fetch data from the specified URL.
150
+ *
151
+ * @param {string} url - The URL to fetch data from.
152
+ * @returns {Promise<Object>} A promise that resolves to an object containing the fetched data
153
+ * to be added into `ctx.state.data`.
154
+ */
155
+ const fetchData = async (url) => {
156
+ const { data: response } = await got(url);
157
+
158
+ const $ = cheerio.load(response);
159
+
160
+ const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
161
+ const author = $('meta[name="author"]').prop('content');
162
+
163
+ return {
164
+ title: $('title').text(),
165
+ link: url,
166
+ description: $('div.tag-content').text() || $('span.author-intro').text() || $('p.collection__intro').text() || $('meta[name="description"]').prop('content'),
167
+ language: $('html').prop('lang'),
168
+ icon,
169
+ logo: icon,
170
+ subtitle: $('title').text().split(/-/)[0],
171
+ author,
172
+ itunes_author: author,
173
+ itunes_category: 'News',
174
+ allowEmpty: true,
175
+ };
176
+ };
177
+
178
+ /**
179
+ * Fetches item data.
180
+ *
181
+ * @param {Object} item - The item to fetch data for.
182
+ * @returns {Promise<Object>} The fetched item data object.
183
+ */
184
+ const fetchItem = async (item) => {
185
+ const { data: detailResponse } = await got(item.link);
186
+
187
+ const state = parseInitialState(detailResponse);
188
+ const data = state.briefStoreModule?.brief_detail.brief ?? state.articleDetail?.articleDetail ?? undefined;
189
+
190
+ if (!data) {
191
+ return item;
192
+ }
193
+
194
+ const { processed: audio = undefined, processedItem: audioItem = {} } = processAudioInfo(data.audio_info);
195
+
196
+ if (Object.keys(audioItem).length !== 0) {
197
+ audioItem.itunes_item_image = data.pic_path ?? data.share_info?.share_img ?? undefined;
198
+ }
199
+
200
+ const { processed: video = undefined, processedItem: videoItem = {} } = processVideoInfo(data.video_info);
201
+
202
+ item.title = data.title ?? item.title;
203
+ item.description = art(path.join(__dirname, 'templates/description.art'), {
204
+ image: {
205
+ src: data.pic_path,
206
+ },
207
+ video,
208
+ audio,
209
+ preface: cleanUpHTML(data.content_preface ?? data.preface),
210
+ summary: data.ai_summary,
211
+ description: cleanUpHTML(data.content),
212
+ });
213
+ item.author = data.user_info?.username ?? item.author;
214
+ item.category = [data.video_article_tag, data.brief_column?.name ?? undefined, data.club_info?.name ?? undefined, ...(data.tags_info?.map((c) => c.name) ?? []), ...(data.relation_info?.channel?.map((c) => c.name) ?? [])].filter(
215
+ (c) => c
216
+ );
217
+ item.pubDate = parseDate(data.dateline ?? data.publish_time, 'X');
218
+ item.upvote = data.agreenum ?? item.upvote;
219
+ item.comments = data.commentnum ?? data.total_comment_num ?? item.comments;
220
+
221
+ item.upvote = parseInt(item.upvote, 10);
222
+ item.comments = parseInt(item.comments, 10);
223
+
224
+ return {
225
+ ...audioItem,
226
+ ...videoItem,
227
+ ...item,
228
+ };
229
+ };
230
+
231
+ /**
232
+ * Generates a random nonce string.
233
+ *
234
+ * @returns {string} The generated nonce string.
235
+ */
236
+ const generateNonce = () => {
237
+ let nonce = '';
238
+ const e = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
239
+ const t = 16;
240
+ for (let i = 0; i < t; i++) {
241
+ nonce += e.charAt(Math.floor(Math.random() * e.length));
242
+ }
243
+ return nonce;
244
+ };
245
+
246
+ /**
247
+ * Generates a signature object containing a nonce, timestamp, and signature value.
248
+ *
249
+ * @returns {string} nonce - The generated nonce.
250
+ * @returns {string} timestamp - The timestamp.
251
+ * @returns {string} signature - The calculated signature value.
252
+ */
253
+ const generateSignature = () => {
254
+ const timestamp = Math.round(new Date().getTime() / 1000).toString();
255
+
256
+ const appSecret = 'hUzaABtNfDE-6UiyaYhfsmjW-8dnoyVc';
257
+ const nonce = generateNonce();
258
+ const r = [appSecret, timestamp, nonce].sort();
259
+ return {
260
+ nonce,
261
+ timestamp,
262
+ signature: CryptoJS.SHA1(r[0] + r[1] + r[2]).toString(),
263
+ };
264
+ };
265
+
266
+ /**
267
+ * Parses the initial state from the provided data.
268
+ *
269
+ * @param {string} data - The data to parse the initial state from.
270
+ * @returns {Object|undefined} - The parsed initial state object, or undefined if not found.
271
+ */
272
+ const parseInitialState = (data) => {
273
+ const matches = data.match(/window\.__INITIAL_STATE__=(\{.*?\});\(function\(\)/);
274
+ if (matches) {
275
+ return JSON.parse(matches[1]);
276
+ }
277
+ return undefined;
278
+ };
279
+
280
+ const audioQualities = ['', 'low'];
281
+
282
+ /**
283
+ * Processes the audio information and returns the processed data.
284
+ *
285
+ * @param {Object} info - The audio information to process.
286
+ * @returns {Object} - An object containing the processed audio data.
287
+ */
288
+ const processAudioInfo = (info) => {
289
+ const quality = info ? audioQualities.find((quality) => info.hasOwnProperty(`audio_${quality === '' ? '' : `${quality}_`}path`)) : undefined;
290
+
291
+ if (quality === undefined) {
292
+ return {
293
+ processed: undefined,
294
+ processedItem: {},
295
+ };
296
+ }
297
+
298
+ const linkKey = `audio_${quality}path`;
299
+ const sizeKey = `audio_${quality}size`;
300
+
301
+ const processed = {
302
+ duration: info.format_length_new ?? info.format_length,
303
+ size: info.hasOwnProperty(sizeKey) ? info[sizeKey] : undefined,
304
+ src: info[linkKey],
305
+ type: `audio/${info[linkKey].split(/\./).pop()}`,
306
+ };
307
+
308
+ const processedItem = {
309
+ itunes_duration: processed.duration,
310
+ enclosure_url: processed.src,
311
+ enclosure_length: processed.size,
312
+ enclosure_type: processed.type,
313
+ };
314
+
315
+ return {
316
+ processed,
317
+ processedItem,
318
+ };
319
+ };
320
+
321
+ /**
322
+ * Process the item list and return the resulting array.
323
+ *
324
+ * @param {Object[]} items - The items to process.
325
+ * @param {number} limit - The maximum number of items to process.
326
+ * @param {Function} tryGet - The tryGet function that handles the retrieval process.
327
+ * @returns {Promise<Object[]>} - A promise that resolves to an array of processed items.
328
+ */
329
+ const processItems = async (items, limit, tryGet) => {
330
+ items = items
331
+ .map((item) => {
332
+ let guid = '';
333
+ let link = '';
334
+
335
+ if (item.moment_id) {
336
+ guid = `huxiu-moment-${item.moment_id}`;
337
+ if (item.url) {
338
+ link = item.url;
339
+ } else {
340
+ link = new URL(`moment/${item.moment_id}.html`, rootUrl).href;
341
+ }
342
+ } else if (item.brief_id || /huxiu\.com\/brief\//.test(item.url)) {
343
+ item.brief_id = item.brief_id ?? item.aid;
344
+ guid = `huxiu-brief-${item.brief_id}`;
345
+ link = new URL(`brief/${item.brief_id}.html`, rootUrl).href;
346
+ } else if (item.aid) {
347
+ guid = `huxiu-article-${item.aid}`;
348
+ link = new URL(`article/${item.aid}.html`, rootUrl).href;
349
+ } else {
350
+ return undefined;
351
+ }
352
+
353
+ const { processed: audio = undefined, processedItem: audioItem = {} } = processAudioInfo(item.audio_info);
354
+
355
+ if (Object.keys(audioItem).length !== 0) {
356
+ audioItem.itunes_item_image = item.pic_path ?? item.share_info?.share_img ?? undefined;
357
+ }
358
+
359
+ const { processed: video = undefined, processedItem: videoItem = {} } = processVideoInfo(item.video_info);
360
+
361
+ const upvotes = item.count_info?.agree ?? item.count_info?.favtimes ?? item.agree_num ?? 0;
362
+ const downvotes = item.count_info?.disagree ?? 0;
363
+ const comments = item.count_info?.total_comment_num ?? item.count_info?.commentnum ?? item.total_comment_num ?? item.commentnum ?? 0;
364
+
365
+ return {
366
+ ...audioItem,
367
+ ...videoItem,
368
+ title: (item.title ?? item.summary ?? item.content)?.replace(/<\/?(?:em|br)?>/g, ''),
369
+ link,
370
+ description: art(path.join(__dirname, 'templates/description.art'), {
371
+ image: {
372
+ src: item.origin_pic_path ?? item.pic_path ?? item.big_pic_path?.split(/\?/)[0] ?? undefined,
373
+ },
374
+ audio,
375
+ video,
376
+ summary: item.summary ?? item.content ?? item.preface,
377
+ }),
378
+ author: item.user_info?.username ?? item.brief_column?.name ?? item.author_info?.username ?? item.author,
379
+ guid,
380
+ pubDate: item.publish_time ?? item.dateline ? parseDate(item.publish_time ?? item.dateline, 'X') : undefined,
381
+ upvotes: parseInt(upvotes, 10),
382
+ downvotes: parseInt(downvotes, 10),
383
+ comments: parseInt(comments, 10),
384
+ };
385
+ })
386
+ .filter((item) => item)
387
+ .slice(0, limit);
388
+
389
+ return await Promise.all(
390
+ items.map((item) =>
391
+ tryGet(item.guid, async () => {
392
+ if (!new RegExp(domain, 'i').test(new URL(item.link).hostname)) {
393
+ return item;
394
+ } else if (!item.guid.startsWith('huxiu-moment')) {
395
+ return await fetchItem(item);
396
+ }
397
+
398
+ return item;
399
+ })
400
+ )
401
+ );
402
+ };
403
+
404
+ const videoQualities = ['fhd', 'fhd_medium', 'wifi', 'fhd_low', 'flow', 'hd', 'sd'];
405
+
406
+ /**
407
+ * Processes the video information and returns the processed data.
408
+ *
409
+ * @param {Object} info - The video information to process.
410
+ * @returns {Object} - An object containing the processed video data.
411
+ */
412
+ const processVideoInfo = (info) => {
413
+ const quality = info ? videoQualities.find((quality) => info.hasOwnProperty(`${quality}_link`)) : undefined;
414
+
415
+ if (quality === undefined) {
416
+ return {
417
+ processed: undefined,
418
+ processedItem: {},
419
+ };
420
+ }
421
+
422
+ const linkKey = `${quality}_link`;
423
+ const sizeKey = `origin_${quality}_size`;
424
+
425
+ const processed = {
426
+ duration: info.duration ?? info.origin_duration,
427
+ poster: info.cover ?? info.custom_cover_path ?? info.gif_path,
428
+ size: info.hasOwnProperty(sizeKey) ? info[sizeKey] : undefined,
429
+ src: info[linkKey],
430
+ type: `video/${info[linkKey].split(/\./).pop()}`,
431
+ };
432
+
433
+ const processedItem = {
434
+ itunes_item_image: processed.poster,
435
+ itunes_duration: processed.duration,
436
+ enclosure_url: processed.src,
437
+ enclosure_length: processed.size,
438
+ enclosure_type: processed.type,
439
+ };
440
+
441
+ return {
442
+ processed,
443
+ processedItem,
444
+ };
445
+ };
446
+
447
+ module.exports = {
448
+ rootUrl,
449
+ apiArticleRootUrl,
450
+ apiBriefRootUrl,
451
+ apiMemberRootUrl,
452
+ apiMomentRootUrl,
453
+ apiSearchRootUrl,
454
+
455
+ fetchBriefColumnData,
456
+ fetchClubData,
457
+ fetchData,
458
+ generateSignature,
459
+ processItems,
460
+ };
@@ -1,3 +1,3 @@
1
1
  module.exports = {
2
- '/download/:val/:id': ['NavePnow'],
2
+ '/download/:val/:id': ['EthanWng97'],
3
3
  };
@@ -0,0 +1,87 @@
1
+ const got = require('@/utils/got');
2
+ const cheerio = require('cheerio');
3
+ const { parseDate } = require('@/utils/parse-date');
4
+ const { art } = require('@/utils/render');
5
+ const path = require('path');
6
+
7
+ module.exports = async (ctx) => {
8
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50;
9
+
10
+ const rootUrl = 'https://ask.imiker.com';
11
+ const apiUrl = new URL('explore/main/list/', rootUrl).href;
12
+ const currentUrl = new URL(``, rootUrl).href;
13
+
14
+ const { data: response } = await got(apiUrl, {
15
+ searchParams: {
16
+ page: 1,
17
+ page_size: limit,
18
+ type: 'jinghua',
19
+ types: 'json',
20
+ },
21
+ });
22
+
23
+ let items = response.slice(0, limit).map((item) => ({
24
+ title: item.question_content,
25
+ link: new URL(`question/${item.id}`, rootUrl).href,
26
+ description: art(path.join(__dirname, 'templates/description.art'), {
27
+ headImage: item.headimage,
28
+ author: item.nick_name,
29
+ question: item.question_detail,
30
+ }),
31
+ author: item.nick_name,
32
+ guid: `imiker-${item.id}`,
33
+ pubDate: parseDate(item.add_time_timestamp * 1000),
34
+ upvotes: item.count?.vote_count ?? 0,
35
+ comments: item.count?.answer_count ?? 0,
36
+ }));
37
+
38
+ items = await Promise.all(
39
+ items.map((item) =>
40
+ ctx.cache.tryGet(item.link, async () => {
41
+ const { data: detailResponse } = await got(item.link);
42
+
43
+ const content = cheerio.load(detailResponse);
44
+
45
+ content('h5.img-for-lazyload').each((_, e) => {
46
+ const image = content(e).find('img');
47
+
48
+ content(e).replaceWith(
49
+ art(path.join(__dirname, 'templates/description.art'), {
50
+ image: {
51
+ src: image.prop('data-original'),
52
+ alt: image.prop('alt'),
53
+ width: image.prop('data-width'),
54
+ height: image.prop('data-height'),
55
+ },
56
+ })
57
+ );
58
+ });
59
+
60
+ item.title = content('div.title h1').text();
61
+ item.description += art(path.join(__dirname, 'templates/description.art'), {
62
+ description: content('div#warp').html(),
63
+ });
64
+ item.author = content('div.name').text();
65
+
66
+ return item;
67
+ })
68
+ )
69
+ );
70
+
71
+ const author = '米课圈';
72
+ const description = '精华';
73
+ const icon = new URL('favicon.ico', rootUrl).href;
74
+
75
+ ctx.state.data = {
76
+ item: items,
77
+ title: `${author} - ${description}`,
78
+ link: currentUrl,
79
+ description,
80
+ language: 'zh',
81
+ icon,
82
+ logo: icon,
83
+ subtitle: description,
84
+ author,
85
+ allowEmpty: true,
86
+ };
87
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ '/ask/jinghua': ['nczitzk'],
3
+ };
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ 'imiker.com': {
3
+ _name: '米课',
4
+ '.': [
5
+ {
6
+ title: '米课圈精华',
7
+ docs: 'https://docs.rsshub.app/routes/new-media#mi-ke-mi-ke-quan-jing-hua',
8
+ source: ['/explore/find'],
9
+ target: '/imiker/ask/jinghua',
10
+ },
11
+ ],
12
+ },
13
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = (router) => {
2
+ router.get('/ask/jinghua', require('./jinghua'));
3
+ };
@@ -0,0 +1,32 @@
1
+ {{ if image?.src }}
2
+ <figure>
3
+ <img
4
+ {{ if image.alt }}
5
+ alt="{{ image.alt }}"
6
+ {{ /if }}
7
+ {{ if image.width }}
8
+ alt="{{ image.width }}"
9
+ {{ /if }}
10
+ {{ if image.height }}
11
+ alt="{{ image.height }}"
12
+ {{ /if }}
13
+ src="{{ image.src }}">
14
+ </figure>
15
+ {{ /if }}
16
+
17
+ {{ if headImage }}
18
+ <figure>
19
+ <img src="{{ headImage }}">
20
+ {{ if author }}
21
+ <figcaption>{{ author }}</figcaption>
22
+ {{ /if }}
23
+ </figure>
24
+ {{ /if }}
25
+
26
+ {{ if question }}
27
+ <blockquote>{{ question }}</blockquote>
28
+ {{ /if }}
29
+
30
+ {{ if description }}
31
+ {{@ description }}
32
+ {{ /if }}
@@ -1,4 +1,4 @@
1
1
  module.exports = {
2
2
  '/html_clip/:user/:tag/:num?': ['BeautyyuYanli'],
3
- '/rss/:user/:tag': ['NavePnow'],
3
+ '/rss/:user/:tag': ['EthanWng97'],
4
4
  };
@@ -11,7 +11,10 @@ const renderItems = (items) =>
11
11
  let description = '';
12
12
  switch (product_type) {
13
13
  case 'carousel_container': {
14
- const images = item.carousel_media.map((i) => i.image_versions2.candidates[0]);
14
+ const images = item.carousel_media.map((i) => ({
15
+ ...i.image_versions2.candidates.sort((a, b) => b.width - a.width)[0],
16
+ alt: item.accessibility_caption,
17
+ }));
15
18
  description = art(path.join(__dirname, 'templates/images.art'), {
16
19
  summary,
17
20
  images,
@@ -22,12 +25,12 @@ const renderItems = (items) =>
22
25
  case 'igtv':
23
26
  description = art(path.join(__dirname, 'templates/video.art'), {
24
27
  summary,
25
- image: item.image_versions2.candidates[0].url,
28
+ image: item.image_versions2.candidates.sort((a, b) => b.width - a.width)[0],
26
29
  video: item.video_versions[0],
27
30
  });
28
31
  break;
29
32
  case 'feed': {
30
- const images = [item.image_versions2.candidates[0]];
33
+ const images = [{ ...item.image_versions2.candidates.sort((a, b) => b.width - a.width)[0], alt: item.accessibility_caption }];
31
34
  description = art(path.join(__dirname, 'templates/images.art'), {
32
35
  summary,
33
36
  images,
@@ -40,7 +43,7 @@ const renderItems = (items) =>
40
43
 
41
44
  // Metadata
42
45
  const url = `https://www.instagram.com/p/${item.code}/`;
43
- const pubDate = parseDate(item.taken_at, 'X');
46
+ const pubDate = parseDate(item.caption.created_at_utc || item.taken_at, 'X');
44
47
  const title = summary.split('\n')[0];
45
48
 
46
49
  return {
@@ -1,5 +1,12 @@
1
- {{@ summary.replace(/\n/g, '<br>') }}
1
+ {{ if summary }}
2
+ {{@ summary.replace(/\n/g, '<br>') }}
3
+ <br>
4
+ {{ /if }}
2
5
 
3
6
  {{ each images i }}
4
- <img src="{{ i.url }}" height="{{ i.height }}" width="{{ i.width }}">
7
+ <img src="{{ i.url }}"
8
+ {{ if i.height }} height="{{ i.height }}" {{ /if }}
9
+ {{ if i.width }} width="{{ i.width }}" {{ /if }}
10
+ {{ if i.alt }} alt="{{ i.alt }}" {{ /if }}
11
+ >
5
12
  {{ /each }}
@@ -1,4 +1,7 @@
1
- {{@ summary.replace(/\n/g, '<br>') }}
1
+ {{ if summary }}
2
+ {{@ summary.replace(/\n/g, '<br>') }}
3
+ <br>
4
+ {{ /if }}
2
5
 
3
6
  <video controls preload="none" poster="{{ image }}" width="{{ video.width }}">
4
7
  <source src="{{ video.url }}" type="video/mp4">