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
@@ -0,0 +1,186 @@
1
+ import { Route } from '@/types';
2
+ import cache from '@/utils/cache';
3
+ import got from '@/utils/got';
4
+ import { config } from '@/config';
5
+ import { FetchError } from 'ofetch';
6
+ import { load } from 'cheerio';
7
+ import { art } from '@/utils/render';
8
+ import { getCurrentPath } from '@/utils/helpers';
9
+ import path from 'node:path';
10
+ import timezone from '@/utils/timezone';
11
+ import { parseDate } from '@/utils/parse-date';
12
+
13
+ const __dirname = getCurrentPath(import.meta.url);
14
+ const key = '3d5_f6A(S$G_FD=2S(Dr6%7BW_h37@rE';
15
+
16
+ export const route: Route = {
17
+ path: '/:category?',
18
+ categories: ['traditional-media'],
19
+ example: '/lorientlejour/977-lebanon',
20
+ parameters: {
21
+ category: 'Category from the last segment of the URL of the corresponding site, see below for more information, /977-Lebanon by default',
22
+ },
23
+ features: {
24
+ requirePuppeteer: false,
25
+ antiCrawler: false,
26
+ supportBT: false,
27
+ supportPodcast: false,
28
+ supportScihub: false,
29
+ requireConfig: [
30
+ {
31
+ name: 'LORIENTLEJOUR_USERNAME',
32
+ optional: true,
33
+ description: `L'Orient-Le Jour/L'Orient Today Email or Username`,
34
+ },
35
+ {
36
+ name: 'LORIENTLEJOUR_PASSWORD',
37
+ optional: true,
38
+ description: `L'Orient-Le Jour/L'Orient Today Password`,
39
+ },
40
+ {
41
+ name: 'LORIENTLEJOUR_TOKEN',
42
+ optional: true,
43
+ description: `To obtain a token, log into L'Orient-Le Jour/L'Orient Today App and inspect the connection request to find the token parameter from the request URL`,
44
+ },
45
+ ],
46
+ },
47
+ name: 'Category',
48
+ maintainers: ['quiniapiezoelectricity'],
49
+ handler,
50
+ description: ` :::tip
51
+ For example, the path for the sites https://today.lorientlejour.com/section/977-lebanon and https://www.lorientlejour.com/rubrique/1-liban would be /lorientlejour/977-lebanon and /lorientlejour/1-liban respectively.
52
+ Multiple categories seperated by '|' is also supported, e.g. /lorientlejour/977-lebanon|1-liban.
53
+ :::`,
54
+ radar: [
55
+ {
56
+ source: ['www.lorientlejour.com/*/:category'],
57
+ target: '/:category',
58
+ },
59
+ {
60
+ source: ['www.lorientlejour.com'],
61
+ target: '/1-Liban',
62
+ },
63
+ {
64
+ source: ['today.lorientlejour.com/*/:category'],
65
+ target: '/:category',
66
+ },
67
+ {
68
+ source: ['today.lorientlejour.com'],
69
+ target: '/977-Lebanon',
70
+ },
71
+ ],
72
+ };
73
+
74
+ async function viewCategory(category: string) {
75
+ const url = `https://www.lorientlejour.com/cmsapi/categories.php?key=${key}&action=view&categoryId=${category}`;
76
+ const response = await cache.tryGet(
77
+ url,
78
+ async () =>
79
+ await got({
80
+ method: 'get',
81
+ url,
82
+ }),
83
+ config.cache.routeExpire,
84
+ false
85
+ );
86
+ return response.data.data[0];
87
+ }
88
+
89
+ async function handler(ctx) {
90
+ const categoryId = (ctx.req.param('category') ?? '977-Lebanon').split('|').map((item) => item.match(/^(\d+)/i)[0] ?? item);
91
+ const limit = ctx.req.query('limit') ?? 25;
92
+
93
+ let token;
94
+ const cacheIn = await cache.get('lorientlejour:token');
95
+ if (cacheIn) {
96
+ token = cacheIn;
97
+ } else if (config.lorientlejour.token) {
98
+ token = config.lorientlejour.token;
99
+ cache.set('lorientlejour:token', token);
100
+ } else if (config.lorientlejour.username && config.lorientlejour.password) {
101
+ const loginUrl = `https://www.lorientlejour.com/cmsapi/visitors.php?key=${key}&action=login&loginName=${config.lorientlejour.username}&password=${config.lorientlejour.password}`;
102
+ const loginResponse = await got(loginUrl);
103
+ token = loginResponse.data.data.token;
104
+ cache.set('lorientlejour:token', token);
105
+ }
106
+
107
+ if (token) {
108
+ try {
109
+ await got(`https://www.lorientlejour.com/cmsapi/visitors.php?key=${key}&action=login_token&token=${token}`);
110
+ } catch (error) {
111
+ if (error instanceof FetchError && error.statusCode === 403) {
112
+ await cache.set('lorientlejour:token', '');
113
+ }
114
+ throw error;
115
+ }
116
+ }
117
+
118
+ let title = `L'Orient Le Jour/L'Orient Today`;
119
+ let description = '';
120
+ let link = 'https://www.lorientlejour.com';
121
+ let language = '';
122
+
123
+ if (categoryId.length === 1) {
124
+ const categoryInfo = await viewCategory(categoryId[0]);
125
+ if (categoryInfo.typeId.locale) {
126
+ language = categoryInfo.typeId.locale;
127
+ } else {
128
+ language = categoryInfo.typeId.name === 'English' ? 'en-US' : 'fr-FR';
129
+ }
130
+ title = language === 'en-US' ? `L'Orient Today - ${categoryInfo.name}` : `L'Orient Le Jour - ${categoryInfo.name}`;
131
+ description = categoryInfo.description;
132
+ link = categoryInfo.url;
133
+ }
134
+
135
+ const subcategories = await Promise.all(
136
+ categoryId.map(async (id) => {
137
+ // get all subcategories of the selected category
138
+ const contents = await viewCategory(id);
139
+ return contents.children.map((child) => child.id);
140
+ })
141
+ );
142
+ const categoriesParam = [...new Set([...categoryId, ...subcategories.flat()])];
143
+ // merge all subcategories with the selected categories to get all contents of the selected category and its subcategories
144
+
145
+ let url = `https://www.lorientlejour.com/cmsapi/content.php?text=clean&key=${key}&action=search&category=${encodeURIComponent(JSON.stringify(categoriesParam))}&limit=${limit}&text=false&page=1`;
146
+ if (token) {
147
+ url = url + `&token=${token}`;
148
+ }
149
+ const response = await got(url);
150
+ const items = response.data.data.map((item) => {
151
+ item.link = item.url;
152
+ item.author = item.authors.map((author) => author.name).join(', ');
153
+ item.pubDate = timezone(parseDate(item.firstPublished), +3);
154
+ item.updated = timezone(parseDate(item.lastUpdate), +3);
155
+ item.category = item.categories.map((itemCategory) => itemCategory.name);
156
+ const contents = item.contents;
157
+ const $ = load(contents);
158
+ const article = $('html');
159
+ article.find('.inline-embeded-article').remove();
160
+ article.find('.relatedArticles').remove();
161
+ if (item.inline_attachments) {
162
+ article.find('.inlineImage').each(function () {
163
+ const inlineImageSrc = $(this).attr('src');
164
+ const inlineAttachment = item.inline_attachments.find((inlineAttachment) => inlineAttachment.url === inlineImageSrc);
165
+ if (inlineAttachment && inlineAttachment.description) {
166
+ $(this).wrap('<figure></figure>');
167
+ $(this).after(`<figcaption>${inlineAttachment.description}</figcaption>`);
168
+ }
169
+ });
170
+ }
171
+ item.description = art(path.join(__dirname, 'templates/description.art'), {
172
+ summary: item.summary,
173
+ attachments: item.attachments,
174
+ article: article.html(),
175
+ });
176
+ return item;
177
+ });
178
+
179
+ return {
180
+ title,
181
+ description,
182
+ language,
183
+ link,
184
+ item: items,
185
+ };
186
+ }
@@ -0,0 +1,7 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: `L'Orient-Le Jour/L'Orient Today`,
5
+ url: 'lorientlejour.com',
6
+ description: `RSS feed for the Lebanon-based French-language newspaper L'Orient-Le Jour and its English edition L'Orient Today`,
7
+ };
@@ -0,0 +1,18 @@
1
+ {{ if summary }}
2
+ <blockquote> {{@ summary }} </blockquote>
3
+ {{ /if }}
4
+ {{ if attachments}}
5
+ {{ each attachments }}
6
+ {{if $value.url }}
7
+ <figure>
8
+ <img src="{{ $value.url }}">
9
+ {{ if $value.description }}
10
+ <figcaption>{{ $value.description }}</figcaption>
11
+ {{ /if }}
12
+ </figure>
13
+ {{ /if }}
14
+ {{ /each }}
15
+ {{ /if }}
16
+ {{ if article }}
17
+ {{@ article }}
18
+ {{ /if }}
@@ -1,6 +1,6 @@
1
1
  import type { Namespace } from '@/types';
2
2
 
3
3
  export const namespace: Namespace = {
4
- name: 'lovelive-anime',
4
+ name: 'Love Live! Official Website',
5
5
  url: 'www.lovelive-anime.jp',
6
6
  };
@@ -7,13 +7,20 @@ import got from '@/utils/got';
7
7
  import { load } from 'cheerio';
8
8
  import path from 'node:path';
9
9
  import { art } from '@/utils/render';
10
+ import ofetch from '@/utils/ofetch';
11
+ import timezone from '@/utils/timezone';
12
+ import { parseDate } from '@/utils/parse-date';
10
13
  const renderDescription = (desc) => art(path.join(__dirname, 'templates/description.art'), desc);
11
14
 
12
15
  export const route: Route = {
13
- path: '/news/:option?',
16
+ path: '/news/:abbr?/:category?/:option?',
14
17
  categories: ['anime'],
15
18
  example: '/lovelive-anime/news',
16
- parameters: { option: 'Crawl full text when `option` is `detail`.' },
19
+ parameters: {
20
+ abbr: 'The path to the Love Live series of sub-projects on the official website is detailed in the table below, `abbr` is `detail` when crawling the full text',
21
+ category: 'The official website lists the Topics category, `category` is `detail` when crawling the full text, other categories see the following table for details',
22
+ option: 'Crawl full text when `option` is `detail`.',
23
+ },
17
24
  features: {
18
25
  requireConfig: false,
19
26
  requirePuppeteer: false,
@@ -24,45 +31,68 @@ export const route: Route = {
24
31
  },
25
32
  radar: [
26
33
  {
27
- source: ['www.lovelive-anime.jp/', 'www.lovelive-anime.jp/news'],
34
+ source: ['www.lovelive-anime.jp/', 'www.lovelive-anime.jp/news/'],
28
35
  target: '/news',
29
36
  },
30
37
  ],
31
- name: 'Love Live! Official Website Latest NEWS',
32
- maintainers: ['axojhf'],
38
+ name: 'News',
39
+ maintainers: ['axojhf', 'zhaoweizhong'],
33
40
  handler,
34
41
  url: 'www.lovelive-anime.jp/',
42
+ description: `| Sub-project Name | All Projects | Lovelive! | Lovelive! Sunshine!! | Lovelive! Nijigasaki High School Idol Club | Lovelive! Superstar!! | 蓮ノ空女学院 | 幻日のヨハネ | ラブライブ!スクールアイドルミュージカル |
43
+ | -------------------------------- | -------------- | ----------- | -------------------- | ------------------------------------------ | --------------------- | ------------ | ------------ | ---------------------------------------- |
44
+ | \`abbr\`parameter | <u>*No parameter*</u> | lovelive | sunshine | nijigasaki | superstar | hasunosora | yohane | musical |
45
+
46
+ | Category Name | 全てのニュース | 音楽商品 | アニメ映像商品 | キャスト映像商品 | 劇場 | アニメ放送 / 配信 | キャスト配信 / ラジオ | ライブ / イベント | ブック | グッズ | ゲーム | メディア | ご当地情報 | キャンペーン | その他 |
47
+ | ------------------- | --------------------- | -------- | -------------- | ---------------- | ------- | ----------------- | --------------------- | ----------------- | ------ | ------ | ------ | -------- | ---------- | ------ | ------------ |
48
+ | \`category\`parameter | <u>*No parameter*</u> | music | anime_movie | cast_movie | theater | onair | radio | event | books | goods | game | media | local | campaign | other |`,
35
49
  };
36
50
 
37
51
  async function handler(ctx) {
38
- const rootUrl = 'https://www.lovelive-anime.jp/news/';
52
+ const abbr = ctx.req.param('abbr');
53
+ const category = ctx.req.param('category');
54
+ const option = ctx.req.param('option');
39
55
 
40
- const response = await got(rootUrl);
56
+ const isDetail = abbr === 'detail' || category === 'detail' || option === 'detail';
57
+ let series = '';
58
+ let subcategory = '';
59
+
60
+ if (abbr && abbr !== 'detail') {
61
+ series = abbr;
62
+ if (category && category !== 'detail') {
63
+ subcategory = category;
64
+ }
65
+ }
41
66
 
42
- const $ = load(response.data);
67
+ const limit = 20;
68
+
69
+ let url = `https://www.lovelive-anime.jp/common/api/article_list.php?site=jp&ip=lovelive&limit=${limit}&data=`;
70
+ const params: { category: string[]; series?: string[]; subcategory?: string[] } = { category: ['NEWS'] };
71
+ if (series) {
72
+ params.series = [series];
73
+ }
74
+ if (subcategory) {
75
+ params.subcategory = [subcategory];
76
+ }
77
+ url += encodeURIComponent(JSON.stringify(params));
43
78
 
44
- const pageFace = $('div.c-card.p-colum__box')
45
- .map((_, item) => {
46
- item = $(item);
79
+ const data = await ofetch(url);
47
80
 
48
- return {
49
- link: item.find('a.c-card__head').attr('href'),
50
- pubDate: item.find('span.c-card__date').text(),
51
- title: item.find('div.c-card__title').text(),
52
- // description: `${item.find('div.c-card__title').text()}<br><img src="${item.find('a.c-card__head > div > figure > img').attr('src')}">`
53
- description: renderDescription({
54
- title: item.find('div.c-card__title').text(),
55
- imglink: item.find('a.c-card__head > div > figure > img').attr('src'),
56
- }),
57
- };
58
- })
59
- .get();
81
+ const articles = data.data.article_list.map((item) => ({
82
+ title: item.title,
83
+ link: item.url,
84
+ description: renderDescription({
85
+ imglink: 'https://www.lovelive-anime.jp' + item.thumbnail,
86
+ }),
87
+ pubDate: timezone(parseDate(item.dspdate), +9),
88
+ category: item.categories.subcategory.map((category) => category.name),
89
+ }));
60
90
 
61
- let items = pageFace;
91
+ let items = articles;
62
92
 
63
- if (ctx.req.param('option') === 'detail') {
93
+ if (isDetail) {
64
94
  items = await Promise.all(
65
- pageFace.map((item) =>
95
+ articles.map((item) =>
66
96
  cache.tryGet(item.link, async () => {
67
97
  const detailResp = await got(item.link);
68
98
  const $ = load(detailResp.data);
@@ -9,20 +9,33 @@ const renderDescription = (desc) => art(path.join(__dirname, 'templates/schedule
9
9
  import dayjs from 'dayjs';
10
10
  import utc from 'dayjs/plugin/utc';
11
11
  import timezone from 'dayjs/plugin/timezone';
12
+ dayjs.extend(utc);
13
+ dayjs.extend(timezone);
12
14
 
13
15
  export const route: Route = {
14
16
  path: '/schedules/:serie?/:category?',
15
- name: 'Unknown',
16
- maintainers: [],
17
+ parameters: {
18
+ serie: 'Love Live! Series sub-projects abbreviation, see the following table',
19
+ category: 'The official website lists the categories, see the following table for details',
20
+ },
21
+ name: 'Schedule',
22
+ example: '/lovelive-anime/schedules',
23
+ categories: ['anime'],
24
+ maintainers: ['axojhf'],
17
25
  handler,
26
+ description: `| Sub-project Name (not full name) | 全シリーズ | Lovelive! | Lovelive! Sunshine!! | Lovelive! Nijigasaki High School Idol Club | Lovelive! Superstar!! | ラブライブ!スクールアイドルミュージカル |
27
+ | -------------------------------- | ----------------------- | ---------- | -------------------- | ------------------------------------------ | --------------------- | ---------------------------------------- |
28
+ | \`serie\` parameter | *No parameter* or \`all\` | \`lovelive\` | \`sunshine\` | \`nijigasaki\` | \`superstar\` | \`musical\` |
29
+
30
+ | Category Name | 全て | ライブ | イベント | 生配信 |
31
+ | -------------------- | ----------------------- | ------ | -------- | --------- |
32
+ | \`category\` parameter | *No parameter* or \`all\` | \`live\` | \`event\` | \`haishin\` |`,
18
33
  };
19
34
 
20
35
  async function handler(ctx) {
21
- dayjs.extend(utc);
22
- dayjs.extend(timezone);
23
36
  const serie = ctx.req.param('serie');
24
37
  const category = ctx.req.param('category');
25
- const rootUrl = `https://www.lovelive-anime.jp/common/api/calendar_list.php`;
38
+ const rootUrl = 'https://www.lovelive-anime.jp/common/api/calendar_list.php';
26
39
  const nowTime = dayjs();
27
40
  const dataPara = {
28
41
  year: nowTime.year(),
@@ -49,7 +62,6 @@ async function handler(ctx) {
49
62
  title,
50
63
  category,
51
64
  description: renderDescription({
52
- title,
53
65
  desc: item.event_dspdate,
54
66
  startTime: eventStartDate,
55
67
  endTime: eventEndDate,
@@ -1 +1 @@
1
- {{title}}<br><img src="{{imglink}}">
1
+ <img src="{{imglink}}">
@@ -1,3 +1,2 @@
1
- <h1>{{title}}</h1><br>
2
1
  <b>{{startTime}}</b>&nbsp;&nbsp;&nbsp;~&nbsp;&nbsp;&nbsp;<b>{{endTime}}</b><br><br>
3
2
  {{desc}}
@@ -27,7 +27,7 @@ export const route: Route = {
27
27
  supportPodcast: false,
28
28
  supportScihub: false,
29
29
  },
30
- name: 'Love Live Official Website Categories Topics',
30
+ name: 'Categories Topics',
31
31
  maintainers: ['axojhf'],
32
32
  handler,
33
33
  description: `| Sub-project Name (not full name) | Lovelive! | Lovelive! Sunshine!! | Lovelive! Nijigasaki High School Idol Club | Lovelive! Superstar!! | 幻日のヨハネ | ラブライブ!スクールアイドルミュージカル |
@@ -126,7 +126,7 @@ async function handler(ctx) {
126
126
  const items = chapters.map((element) => genResult(element));
127
127
  let itemsLen = items.length;
128
128
  if (chapterCnt > 0) {
129
- itemsLen = chapterCnt < $.newChapterCnt ? $.newChapterCnt : chapterCnt;
129
+ itemsLen = Math.max(chapterCnt, $.newChapterCnt);
130
130
  }
131
131
 
132
132
  return {
@@ -1,37 +1,58 @@
1
- import { Route } from '@/types';
2
- import got from '@/utils/got';
1
+ import { Data, DataItem, Route, ViewType } from '@/types';
2
+ import { CrowdfundingDetailInfo, CrowdfundingList } from './types';
3
+ import utils from './utils';
3
4
 
4
5
  export const route: Route = {
5
6
  path: '/crowdfunding',
6
7
  categories: ['shopping'],
7
8
  example: '/mi/crowdfunding',
8
9
  name: '小米众筹',
9
- maintainers: ['DIYgod'],
10
+ maintainers: ['DIYgod', 'nuomi1'],
10
11
  handler,
12
+ features: {
13
+ requireConfig: false,
14
+ requirePuppeteer: false,
15
+ antiCrawler: false,
16
+ supportRadar: true,
17
+ supportBT: false,
18
+ supportPodcast: false,
19
+ supportScihub: false,
20
+ },
21
+ radar: [
22
+ {
23
+ source: ['m.mi.com/crowdfunding/home'],
24
+ target: '/crowdfunding',
25
+ },
26
+ ],
27
+ view: ViewType.Notifications,
28
+ };
29
+
30
+ const getDetails = async (list: CrowdfundingList[]) => {
31
+ const result: Promise<CrowdfundingDetailInfo>[] = list.flatMap((section) => section.items.map((item) => utils.getCrowdfundingItem(item)));
32
+ return await Promise.all(result);
11
33
  };
12
34
 
35
+ const getDataItem = (item: CrowdfundingDetailInfo) =>
36
+ ({
37
+ title: item.project_name,
38
+ description: utils.renderCrowdfunding(item),
39
+ link: `https://m.mi.com/crowdfunding/proddetail/${item.project_id}`,
40
+ image: item.big_image,
41
+ language: 'zh-cn',
42
+ }) as DataItem;
43
+
13
44
  async function handler() {
14
- const response = await got({
15
- method: 'post',
16
- url: 'http://api.m.mi.com/v1/microwd/home',
17
- headers: {
18
- 'Mishop-Client-Id': '180100031055',
19
- 'User-Agent': 'MiShop/4.3.68 (iPhone; iOS 12.0.1; Scale/3.00)',
20
- 'IOS-App-Version': '4.3.68',
21
- 'IOS-Version': 'system=12.0.1&device=iPhone10,3',
22
- },
23
- });
24
- const list = response.data.data.list.flatMap((a) => a.items || []);
45
+ const list = await utils.getCrowdfundingList();
46
+ const details = await getDetails(list);
47
+
48
+ const items: DataItem[] = details.map((item) => getDataItem(item));
25
49
 
26
50
  return {
27
51
  title: '小米众筹',
28
- link: '',
52
+ link: 'https://m.mi.com/crowdfunding/home',
53
+ item: items,
29
54
  allowEmpty: true,
30
- item:
31
- list &&
32
- list.map((item) => ({
33
- title: item.product_name,
34
- description: `<img src="${item.img_url}"><br>价格:${item.product_price}元`,
35
- })),
36
- };
55
+ image: 'https://m.mi.com/static/img/icons/apple-touch-icon-152x152.png',
56
+ language: 'zh-cn',
57
+ } as Data;
37
58
  }
@@ -0,0 +1,28 @@
1
+ <img src="{{ big_image }}">
2
+ <br>
3
+ {{ project_name }}
4
+ <br>
5
+ {{ project_desc }}
6
+ <br>
7
+ 众筹价:{{ price }} 元,建议零售价:{{ product_market_price }} 元
8
+ <br>
9
+ 众筹开始:{{ start_time_desc }},众筹结束:{{ end_time_desc }}
10
+ <br>
11
+ 物流:{{ send_info }}
12
+ <br>
13
+ <table>
14
+ <tbody>
15
+ <tr>
16
+ <th>档位</th>
17
+ <th>价格</th>
18
+ <th>描述</th>
19
+ </tr>
20
+ {{ each support_list }}
21
+ <tr>
22
+ <td>{{ $value.name }}</td>
23
+ <td>{{ $value.price }} 元</td>
24
+ <td>{{ $value.support_desc }}</td>
25
+ </tr>
26
+ {{ /each }}
27
+ </tbody>
28
+ </table>
@@ -0,0 +1,40 @@
1
+ export interface DataResponse<Data> {
2
+ data: Data;
3
+ }
4
+
5
+ export interface CrowdfundingData {
6
+ list: CrowdfundingList[];
7
+ }
8
+
9
+ export interface CrowdfundingList {
10
+ items: CrowdfundingItem[];
11
+ }
12
+
13
+ export interface CrowdfundingItem {
14
+ project_id: number;
15
+ product_market_price: string;
16
+ }
17
+
18
+ export interface CrowdfundingDetailData {
19
+ crowd_funding_info: CrowdfundingDetailInfo;
20
+ }
21
+
22
+ export interface CrowdfundingDetailInfo {
23
+ big_image: string;
24
+ end_time: number;
25
+ end_time_desc: string; // injected
26
+ price: string;
27
+ product_market_price: string; // injected
28
+ project_desc: string;
29
+ project_id: number;
30
+ project_name: string;
31
+ start_time: number;
32
+ start_time_desc: string; // injected
33
+ support_list: CrowdfundingDetailSupportList[];
34
+ }
35
+
36
+ export interface CrowdfundingDetailSupportList {
37
+ name: string;
38
+ price: string;
39
+ support_desc: string;
40
+ }
@@ -0,0 +1,80 @@
1
+ import { getCurrentPath } from '@/utils/helpers';
2
+ const __dirname = getCurrentPath(import.meta.url);
3
+
4
+ import cache from '@/utils/cache';
5
+ import ofetch from '@/utils/ofetch';
6
+ import { art } from '@/utils/render';
7
+ import dayjs from 'dayjs';
8
+ import 'dayjs/locale/zh-cn';
9
+ import localizedFormat from 'dayjs/plugin/localizedFormat';
10
+ import timezone from 'dayjs/plugin/timezone';
11
+ import utc from 'dayjs/plugin/utc';
12
+ import path from 'path';
13
+ import { CrowdfundingData, CrowdfundingDetailData, CrowdfundingDetailInfo, CrowdfundingItem, CrowdfundingList, DataResponse } from './types';
14
+
15
+ dayjs.extend(localizedFormat);
16
+ dayjs.extend(timezone);
17
+ dayjs.extend(utc);
18
+
19
+ /**
20
+ * 获取众筹项目列表
21
+ *
22
+ * @returns {Promise<CrowdfundingList[]>} 众筹项目列表。
23
+ */
24
+ export const getCrowdfundingList = async (): Promise<CrowdfundingList[]> => {
25
+ const response = await ofetch<DataResponse<CrowdfundingData>>('https://m.mi.com/v1/crowd/crowd_home', {
26
+ headers: {
27
+ referrer: 'https://m.mi.com/',
28
+ },
29
+ method: 'POST',
30
+ });
31
+ return response.data.list;
32
+ };
33
+
34
+ /**
35
+ * 获取众筹项目详情并缓存
36
+ *
37
+ * @param {CrowdfundingItem} item - 众筹项目。
38
+ * @returns {Promise<CrowdfundingDetailInfo>} 众筹项目详情。
39
+ */
40
+ export const getCrowdfundingItem = (item: CrowdfundingItem): Promise<CrowdfundingDetailInfo> =>
41
+ cache.tryGet(`mi:crowdfunding:${item.project_id}`, async () => {
42
+ const response = await ofetch<DataResponse<CrowdfundingDetailData>>('https://m.mi.com/v1/crowd/crowd_detail', {
43
+ headers: {
44
+ referrer: 'https://m.mi.com/crowdfunding/home',
45
+ },
46
+ method: 'POST',
47
+ query: {
48
+ project_id: item.project_id,
49
+ },
50
+ });
51
+ // 建议零售价
52
+ if (response.data.crowd_funding_info.product_market_price === undefined) {
53
+ response.data.crowd_funding_info.product_market_price = item.product_market_price;
54
+ }
55
+ // 众筹开始
56
+ if (response.data.crowd_funding_info.start_time_desc === undefined) {
57
+ response.data.crowd_funding_info.start_time_desc = formatDate(response.data.crowd_funding_info.start_time);
58
+ }
59
+ // 众筹结束
60
+ if (response.data.crowd_funding_info.end_time_desc === undefined) {
61
+ response.data.crowd_funding_info.end_time_desc = formatDate(response.data.crowd_funding_info.end_time);
62
+ }
63
+ return response.data.crowd_funding_info;
64
+ }) as Promise<CrowdfundingDetailInfo>;
65
+
66
+ /**
67
+ * 渲染众筹项目模板
68
+ *
69
+ * @param {CrowdfundingDetailInfo} item - 众筹项目详情。
70
+ * @returns {string} 渲染后的众筹项目模板字符串。
71
+ */
72
+ export const renderCrowdfunding = (item: CrowdfundingDetailInfo): string => art(path.join(__dirname, 'templates/crowdfunding.art'), item);
73
+
74
+ const formatDate = (timestamp: number): string => dayjs.unix(timestamp).tz('Asia/Shanghai').locale('zh-cn').format('lll');
75
+
76
+ export default {
77
+ getCrowdfundingList,
78
+ getCrowdfundingItem,
79
+ renderCrowdfunding,
80
+ };
@@ -60,6 +60,7 @@ async function getUserTimelineByUsername(username, site) {
60
60
  json: {
61
61
  userId: accountId,
62
62
  withChannelNotes: true,
63
+ withRenotes: false,
63
64
  limit: 10,
64
65
  offset: 0,
65
66
  },
@@ -0,0 +1,6 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'MissKON',
5
+ url: 'misskon.com',
6
+ };