rsshub 1.0.0-master.f6cb490 → 1.0.0-master.f6f0273

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 (88) hide show
  1. package/lib/api/index.ts +1 -6
  2. package/lib/routes/2048/index.ts +24 -23
  3. package/lib/routes/anthropic/news.ts +27 -13
  4. package/lib/routes/asianfanfics/namespace.ts +7 -0
  5. package/lib/routes/asianfanfics/tag.ts +89 -0
  6. package/lib/routes/asianfanfics/text-search.ts +68 -0
  7. package/lib/routes/blockworks/index.ts +128 -0
  8. package/lib/routes/blockworks/namespace.ts +7 -0
  9. package/lib/routes/cmu/andypavlo/blog.ts +55 -0
  10. package/lib/routes/cmu/namespace.ts +7 -0
  11. package/lib/routes/coindesk/{index.ts → consensus-magazine.ts} +17 -21
  12. package/lib/routes/coindesk/namespace.ts +2 -1
  13. package/lib/routes/coindesk/news.ts +47 -0
  14. package/lib/routes/coindesk/utils.ts +26 -0
  15. package/lib/routes/cointelegraph/index.ts +106 -0
  16. package/lib/routes/cointelegraph/namespace.ts +7 -0
  17. package/lib/routes/collabo-cafe/category.ts +37 -0
  18. package/lib/routes/collabo-cafe/index.ts +35 -0
  19. package/lib/routes/collabo-cafe/namespace.ts +9 -0
  20. package/lib/routes/collabo-cafe/parser.ts +29 -0
  21. package/lib/routes/collabo-cafe/tag.ts +37 -0
  22. package/lib/routes/cryptoslate/index.ts +98 -0
  23. package/lib/routes/cryptoslate/namespace.ts +7 -0
  24. package/lib/routes/decrypt/index.ts +115 -0
  25. package/lib/routes/decrypt/namespace.ts +7 -0
  26. package/lib/routes/discuz/discuz.ts +7 -9
  27. package/lib/routes/fangchan/list.ts +224 -0
  28. package/lib/routes/fangchan/namespace.ts +9 -0
  29. package/lib/routes/fangchan/templates/description.art +7 -0
  30. package/lib/routes/foreignaffairs/namespace.ts +7 -0
  31. package/lib/routes/foreignaffairs/rss.ts +55 -0
  32. package/lib/routes/forklog/index.ts +72 -0
  33. package/lib/routes/forklog/namespace.ts +7 -0
  34. package/lib/routes/gcores/categories.ts +129 -0
  35. package/lib/routes/gcores/collections.ts +129 -0
  36. package/lib/routes/gcores/topics.ts +63 -0
  37. package/lib/routes/gov/moa/gjs.ts +210 -0
  38. package/lib/routes/gov/tianjin/tjftz.ts +53 -0
  39. package/lib/routes/gov/tianjin/tjrcgzw.ts +51 -0
  40. package/lib/routes/grainoil/category.ts +207 -0
  41. package/lib/routes/grainoil/namespace.ts +9 -0
  42. package/lib/routes/huxiu/util.ts +11 -9
  43. package/lib/routes/ifanr/category.ts +7 -2
  44. package/lib/routes/ifanr/digest.ts +1 -1
  45. package/lib/routes/ifanr/index.ts +1 -1
  46. package/lib/routes/instructables/projects.ts +20 -15
  47. package/lib/routes/juejin/collections.ts +1 -1
  48. package/lib/routes/komiic/comic.ts +88 -0
  49. package/lib/routes/komiic/namespace.ts +7 -0
  50. package/lib/routes/leagueoflegends/namespace.ts +8 -0
  51. package/lib/routes/leagueoflegends/patch-notes.ts +76 -0
  52. package/lib/routes/likeshop/index.ts +43 -0
  53. package/lib/routes/likeshop/namespace.ts +7 -0
  54. package/lib/routes/ltaaa/article.ts +180 -0
  55. package/lib/routes/ltaaa/namespace.ts +9 -0
  56. package/lib/routes/ltaaa/templates/description.art +7 -0
  57. package/lib/routes/mashiro/index.ts +1 -0
  58. package/lib/routes/nhentai/util.ts +4 -1
  59. package/lib/routes/pinterest/user.ts +9 -0
  60. package/lib/routes/sohu/mp.ts +3 -2
  61. package/lib/routes/spotify/show.ts +1 -1
  62. package/lib/routes/stcn/index.ts +241 -136
  63. package/lib/routes/stcn/kx.ts +144 -0
  64. package/lib/routes/swjtu/namespace.ts +1 -1
  65. package/lib/routes/swjtu/{scai/bks.ts → scai.ts} +34 -20
  66. package/lib/routes/swjtu/sports.ts +77 -0
  67. package/lib/routes/theblock/index.ts +142 -0
  68. package/lib/routes/theblock/namespace.ts +7 -0
  69. package/lib/routes/theverge/index.ts +73 -62
  70. package/lib/routes/theverge/templates/header.art +19 -0
  71. package/lib/routes/threads/index.ts +73 -54
  72. package/lib/routes/threads/utils.ts +60 -78
  73. package/lib/routes/tmtpost/column.ts +298 -0
  74. package/lib/routes/tmtpost/new.ts +4 -199
  75. package/lib/routes/tmtpost/util.ts +207 -0
  76. package/lib/routes/toranoana/namespace.ts +7 -0
  77. package/lib/routes/toranoana/news.ts +110 -0
  78. package/lib/routes/wainao/templates/description.art +9 -0
  79. package/lib/routes/wainao/topics.ts +214 -0
  80. package/lib/routes/xiaoyuzhou/podcast.ts +27 -27
  81. package/lib/routes/xjtu/yz.ts +74 -0
  82. package/lib/routes/youmemark/index.ts +6 -6
  83. package/lib/routes/zaobao/util.ts +11 -3
  84. package/lib/routes/zhihu/answers.ts +26 -54
  85. package/package.json +36 -35
  86. package/lib/routes/gcores/category.ts +0 -171
  87. package/lib/routes/gcores/collection.ts +0 -161
  88. package/lib/routes-deprecated/ltaaa/index.js +0 -69
@@ -1,5 +1,7 @@
1
1
  import { Route } from '@/types';
2
- import got from '@/utils/got';
2
+ import ofetch from '@/utils/ofetch';
3
+ import { load } from 'cheerio';
4
+ import { parseDate } from '@/utils/parse-date';
3
5
 
4
6
  export const route: Route = {
5
7
  path: '/projects/:category?',
@@ -30,10 +32,10 @@ export const route: Route = {
30
32
  };
31
33
 
32
34
  async function handler(ctx) {
33
- const category = ctx.req.param('category') ?? 'all';
35
+ const { category = 'all' } = ctx.req.param();
36
+ const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50;
34
37
 
35
- const siteDomain = 'www.instructables.com';
36
- const apiKey = 'NU5CdGwyRDdMVnVmM3l4cWNqQzFSVzJNZU5jaUxFU3dGK3J2L203MkVmVT02ZWFYeyJleGNsdWRlX2ZpZWxkcyI6WyJvdXRfb2YiLCJzZWFyY2hfdGltZV9tcyIsInN0ZXBCb2R5Il0sInBlcl9wYWdlIjo1MH0=';
38
+ const siteDomain = 'instructables.com';
37
39
 
38
40
  let pathPrefix, projectFilter;
39
41
  if (category === 'all') {
@@ -45,32 +47,35 @@ async function handler(ctx) {
45
47
  projectFilter = category === 'teachers' ? `&& teachers:=${filterValue}` : ` && category:=${filterValue}`;
46
48
  }
47
49
 
48
- const link = `https://${siteDomain}/${pathPrefix}projects?projects=all`;
50
+ const pageLink = `https://${siteDomain}/${pathPrefix}projects`;
49
51
 
50
- const response = await got({
52
+ const pageResponse = await ofetch(pageLink);
53
+ const $ = load(pageResponse);
54
+ const { typesenseProxy, typesenseApiKey } = JSON.parse($('script#js-page-context').text());
55
+
56
+ const data = await ofetch(`${typesenseProxy}/collections/projects/documents/search`, {
51
57
  method: 'get',
52
- url: `https://${siteDomain}/api_proxy/search/collections/projects/documents/search`,
58
+ baseURL: `https://${siteDomain}`,
53
59
  headers: {
54
- Referer: link,
60
+ Referer: pageLink,
55
61
  Host: siteDomain,
56
- 'x-typesense-api-key': apiKey,
62
+ 'x-typesense-api-key': typesenseApiKey,
57
63
  },
58
- searchParams: {
64
+ query: {
59
65
  q: '*',
60
66
  query_by: 'title,stepBody,screenName',
61
67
  page: 1,
62
- per_page: 50,
68
+ per_page: limit,
63
69
  sort_by: 'publishDate:desc',
64
70
  include_fields: 'title,urlString,coverImageUrl,screenName,publishDate,favorites,views,primaryClassification,featureFlag,prizeLevel,IMadeItCount',
65
71
  filter_by: `featureFlag:=true${projectFilter}`,
66
72
  },
73
+ parseResponse: JSON.parse,
67
74
  });
68
75
 
69
- const data = response.data;
70
-
71
76
  return {
72
77
  title: 'Instructables Projects', // 项目的标题
73
- link, // 指向项目的链接
78
+ link: `https://${siteDomain}/projects`, // 指向项目的链接
74
79
  description: 'Instructables Projects', // 描述项目
75
80
  language: 'en', // 频道语言
76
81
  item: data.hits.map((item) => ({
@@ -78,7 +83,7 @@ async function handler(ctx) {
78
83
  link: `https://${siteDomain}/${item.document.urlString}`,
79
84
  author: item.document.screenName,
80
85
  description: `<img src="${item.document.coverImageUrl}?auto=webp&crop=1.2%3A1&frame=1&width=500" width="500">`,
81
- pubDate: new Date(item.document.publishDate).toUTCString(),
86
+ pubDate: parseDate(item.document.publishDate),
82
87
  category: item.document.primaryClassification,
83
88
  })),
84
89
  };
@@ -42,7 +42,7 @@ async function handler(ctx) {
42
42
  const collectionId = response.data.map((item) => item.tag_id);
43
43
 
44
44
  const temp = (await Promise.all(collectionId.map((id) => getArticleList(id)))) as Article[][];
45
- const posts = temp.flat();
45
+ const posts = temp.flat().filter(Boolean);
46
46
  const list = parseList(posts);
47
47
 
48
48
  const result = await ProcessFeed(list);
@@ -0,0 +1,88 @@
1
+ import { Route } from '@/types';
2
+ import { parseDate } from '@/utils/parse-date';
3
+ import got from '@/utils/got';
4
+
5
+ export const route: Route = {
6
+ path: '/comic/:id',
7
+ categories: ['anime'],
8
+ example: '/komiic/comic/533',
9
+ parameters: { id: '漫画 ID' },
10
+ features: {
11
+ requireConfig: false,
12
+ requirePuppeteer: false,
13
+ antiCrawler: false,
14
+ supportBT: false,
15
+ supportPodcast: false,
16
+ supportScihub: false,
17
+ },
18
+ radar: [
19
+ {
20
+ source: ['komiic.com/comic/:id'],
21
+ target: '/comic/:id',
22
+ },
23
+ ],
24
+ name: '漫画更新',
25
+ maintainers: ['NekoAria'],
26
+ handler,
27
+ };
28
+
29
+ async function handler(ctx) {
30
+ const { id } = ctx.req.param();
31
+ const { limit = 0 } = ctx.req.query();
32
+ const baseUrl = 'https://komiic.com';
33
+
34
+ const { data: comicInfo } = await got.post(`${baseUrl}/api/query`, {
35
+ json: {
36
+ operationName: 'comicById',
37
+ variables: { comicId: id },
38
+ query: `query comicById($comicId: ID!) {
39
+ comicById(comicId: $comicId) {
40
+ title
41
+ imageUrl
42
+ }
43
+ }`,
44
+ },
45
+ });
46
+
47
+ const { title, imageUrl } = comicInfo.data.comicById;
48
+
49
+ const { data: chapterData } = await got.post(`${baseUrl}/api/query`, {
50
+ json: {
51
+ operationName: 'chapterByComicId',
52
+ variables: { comicId: id },
53
+ query: `query chapterByComicId($comicId: ID!) {
54
+ chaptersByComicId(comicId: $comicId) {
55
+ id
56
+ serial
57
+ type
58
+ dateUpdated
59
+ size
60
+ }
61
+ }`,
62
+ },
63
+ });
64
+
65
+ const sortedChapters = chapterData.data.chaptersByComicId.sort((a, b) => Date.parse(b.dateUpdated) - Date.parse(a.dateUpdated));
66
+
67
+ const chapterLimit = Number(limit) || sortedChapters.length;
68
+ const filteredChapters = sortedChapters.slice(0, chapterLimit);
69
+
70
+ const generateChapterDescription = (chapter) =>
71
+ `
72
+ <h1>${chapter.size}p</h1>
73
+ <img src="${imageUrl}" />
74
+ `.trim();
75
+
76
+ const items = filteredChapters.map((chapter) => ({
77
+ title: chapter.type === 'book' ? `第 ${chapter.serial} 卷` : `第 ${chapter.serial} 话`,
78
+ link: `${baseUrl}/comic/${id}/chapter/${chapter.id}/images/all`,
79
+ pubDate: parseDate(chapter.dateUpdated),
80
+ description: generateChapterDescription(chapter),
81
+ }));
82
+
83
+ return {
84
+ title: `Komiic - ${title}`,
85
+ link: `${baseUrl}/comic/${id}`,
86
+ item: items,
87
+ };
88
+ }
@@ -0,0 +1,7 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'Komiic',
5
+ url: 'komiic.com',
6
+ lang: 'zh-CN',
7
+ };
@@ -0,0 +1,8 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'League of Legends',
5
+ url: 'leagueoflegends.com',
6
+ categories: ['game'],
7
+ lang: 'en',
8
+ };
@@ -0,0 +1,76 @@
1
+ import { DataItem, Route } from '@/types';
2
+ import got from '@/utils/got';
3
+ import { load } from 'cheerio';
4
+ import { parseDate } from '@/utils/parse-date';
5
+
6
+ export const route: Route = {
7
+ path: '/patch-notes',
8
+ categories: ['game'],
9
+ example: '/leagueoflegends/patch-notes',
10
+ radar: [
11
+ {
12
+ source: ['www.leagueoflegends.com/en-us/news/tags/patch-notes/', 'www.leagueoflegends.com/en-us/news/game-updates/:postSlug'],
13
+ },
14
+ ],
15
+ name: 'Patch Notes',
16
+ maintainers: ['noahm'],
17
+ async handler() {
18
+ const url = 'https://www.leagueoflegends.com/en-us/news/tags/patch-notes/';
19
+ const response = await got({
20
+ method: 'get',
21
+ url,
22
+ });
23
+
24
+ const data = response.data;
25
+
26
+ const $ = load(data);
27
+ const nextData = $('script[id="__NEXT_DATA__"]').text();
28
+ if (!nextData) {
29
+ throw new Error('missing next data');
30
+ }
31
+ const list: PatchNotesItem[] = JSON.parse(nextData).props.pageProps.page.blades[2].items;
32
+
33
+ return {
34
+ title: 'League of Legends Patch Notes',
35
+ link: url,
36
+ item: list.map(
37
+ (item): DataItem => ({
38
+ title: item.title,
39
+ description: item.description.body,
40
+ pubDate: parseDate(item.publishedAt),
41
+ link: item.action.payload.url,
42
+ guid: item.analytics.contentId,
43
+ image: item.media.url,
44
+ itunes_item_image: item.media.url,
45
+ })
46
+ ),
47
+ };
48
+ },
49
+ };
50
+
51
+ // partial type definition of JSON data pre-filled on the page
52
+ interface PatchNotesItem {
53
+ title: string;
54
+ publishedAt: string;
55
+ description: {
56
+ type: 'html';
57
+ body: string;
58
+ };
59
+ media: {
60
+ dimensions: {
61
+ height: number;
62
+ width: number;
63
+ };
64
+ mimeType: string;
65
+ url: string;
66
+ };
67
+ action: {
68
+ payload: {
69
+ url: string;
70
+ };
71
+ type: 'weblink';
72
+ };
73
+ analytics: {
74
+ contentId: string;
75
+ };
76
+ }
@@ -0,0 +1,43 @@
1
+ import { Route } from '@/types';
2
+ import ofetch from '@/utils/ofetch';
3
+
4
+ export const route: Route = {
5
+ path: '/:site',
6
+ categories: ['social-media'],
7
+ example: '/likeshop/bloombergpursuits',
8
+ parameters: { site: 'the site attached to likeshop.me/' },
9
+ radar: [
10
+ {
11
+ source: ['likeshop.me/'],
12
+ },
13
+ ],
14
+ features: {
15
+ requireConfig: false,
16
+ requirePuppeteer: false,
17
+ antiCrawler: false,
18
+ supportBT: false,
19
+ supportPodcast: false,
20
+ supportScihub: false,
21
+ },
22
+ name: 'Posts',
23
+ maintainers: ['nickyfoto'],
24
+ handler,
25
+ description: 'LikeShop link in bio takes your audience from Instagram and TikTok to your website in one easy step.',
26
+ };
27
+
28
+ async function handler(ctx) {
29
+ const site = ctx.req.param('site');
30
+ const link = `https://api.likeshop.me/api/accounts/${site}/galleries/likeshop`;
31
+ const data = await ofetch(link);
32
+ const items = data.data.media.map((item) => ({
33
+ title: item.comment,
34
+ link: item.product_url.split('?')[0],
35
+ description: `<p><img src="${item.image_url.split('?')[0]}"></p>`,
36
+ guid: item.id,
37
+ }));
38
+ return {
39
+ title: `@${site} Likeshop`,
40
+ link: `https://likeshop.me/${site}`,
41
+ item: items,
42
+ };
43
+ }
@@ -0,0 +1,7 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: 'LikeShop',
5
+ url: 'likeshop.me',
6
+ lang: 'en',
7
+ };
@@ -0,0 +1,180 @@
1
+ import { type Data, type DataItem, type Route, ViewType } from '@/types';
2
+
3
+ import { art } from '@/utils/render';
4
+ import cache from '@/utils/cache';
5
+ import { getCurrentPath } from '@/utils/helpers';
6
+ import ofetch from '@/utils/ofetch';
7
+ import { parseDate } from '@/utils/parse-date';
8
+
9
+ import { type CheerioAPI, type Cheerio, type Element, load } from 'cheerio';
10
+ import { type Context } from 'hono';
11
+ import path from 'node:path';
12
+
13
+ const __dirname = getCurrentPath(import.meta.url);
14
+
15
+ export const handler = async (ctx: Context): Promise<Data> => {
16
+ const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
17
+
18
+ const baseUrl: string = 'https://www.ltaaa.cn';
19
+ const targetUrl: string = new URL('article', baseUrl).href;
20
+
21
+ const response = await ofetch(targetUrl);
22
+ const $: CheerioAPI = load(response);
23
+ const language = $('html').attr('lang') ?? 'zh-CN';
24
+
25
+ let items: DataItem[] = [];
26
+
27
+ items = $('ul.wlist li')
28
+ .slice(0, limit)
29
+ .toArray()
30
+ .map((el): Element => {
31
+ const $el: Cheerio<Element> = $(el);
32
+
33
+ const $aEl: Cheerio<Element> = $el.find('div.li-title a');
34
+
35
+ const title: string = $aEl.text();
36
+ const description: string = art(path.join(__dirname, 'templates/description.art'), {
37
+ intro: $el.find('div.dbody p').first().text(),
38
+ });
39
+ const pubDateStr: string | undefined = $el.find('i.icon-time').next().text().trim();
40
+ const linkUrl: string | undefined = $aEl.attr('href');
41
+ const authorEls: Element[] = $el.find('i.icon-user').parent().find('a').toArray();
42
+ const authors: DataItem['author'] = authorEls.map((authorEl) => {
43
+ const $authorEl: Cheerio<Element> = $(authorEl);
44
+
45
+ return {
46
+ name: $authorEl.text(),
47
+ url: new URL($authorEl.attr('href') ?? '', baseUrl).href,
48
+ avatar: undefined,
49
+ };
50
+ });
51
+ const image: string | undefined = $el.find('div.li-thumb img').attr('src');
52
+ const upDatedStr: string | undefined = pubDateStr;
53
+
54
+ const processedItem: DataItem = {
55
+ title,
56
+ description,
57
+ pubDate: pubDateStr ? parseDate(pubDateStr) : undefined,
58
+ link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
59
+ author: authors,
60
+ content: {
61
+ html: description,
62
+ text: description,
63
+ },
64
+ image,
65
+ banner: image,
66
+ updated: upDatedStr ? parseDate(upDatedStr) : undefined,
67
+ language,
68
+ };
69
+
70
+ return processedItem;
71
+ });
72
+
73
+ items = (
74
+ await Promise.all(
75
+ items.map((item) => {
76
+ if (!item.link) {
77
+ return item;
78
+ }
79
+
80
+ return cache.tryGet(item.link, async (): Promise<DataItem> => {
81
+ const detailResponse = await ofetch(item.link);
82
+ const $$: CheerioAPI = load(detailResponse);
83
+
84
+ const title: string = $$('div.post-title').text();
85
+
86
+ const pubDateStr: string | undefined = $$('i.icon-time').next().text().trim();
87
+ const categoryEls: Element[] = $$('span.keywords a').toArray();
88
+ const categories: string[] = [...new Set(categoryEls.map((el) => $$(el).text()).filter(Boolean))];
89
+ const authorEls: Element[] = $$('i.icon-user').first().nextAll('a').toArray();
90
+ const authors: DataItem['author'] = authorEls.map((authorEl) => {
91
+ const $$authorEl: Cheerio<Element> = $$(authorEl);
92
+
93
+ return {
94
+ name: $$authorEl.text(),
95
+ url: new URL($$authorEl.attr('href') ?? '', baseUrl).href,
96
+ avatar: undefined,
97
+ };
98
+ });
99
+ const upDatedStr: string | undefined = pubDateStr;
100
+
101
+ $$('div.post-tip').each((_, el) => {
102
+ const $$el: Cheerio<Element> = $$(el);
103
+ const content: string = $$el.html() ?? '';
104
+
105
+ if (content) {
106
+ $$el.replaceWith(`<h1>${content}</h1>`);
107
+ }
108
+ });
109
+ $$('div.post-param, div.post-title, div.post-keywords').remove();
110
+ $$('div.attitude, div.clear').remove();
111
+
112
+ const description: string = art(path.join(__dirname, 'templates/description.art'), {
113
+ description: $$('div.post-body').html(),
114
+ });
115
+
116
+ const processedItem: DataItem = {
117
+ title,
118
+ description,
119
+ pubDate: pubDateStr ? parseDate(pubDateStr) : item.pubDate,
120
+ category: categories,
121
+ author: authors,
122
+ content: {
123
+ html: description,
124
+ text: description,
125
+ },
126
+ updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
127
+ language,
128
+ };
129
+
130
+ return {
131
+ ...item,
132
+ ...processedItem,
133
+ };
134
+ });
135
+ })
136
+ )
137
+ ).filter((_): _ is DataItem => true);
138
+
139
+ const title: string = $('title').text();
140
+
141
+ return {
142
+ title,
143
+ description: $('meta[name="description"]').attr('content'),
144
+ link: targetUrl,
145
+ item: items,
146
+ allowEmpty: true,
147
+ image: new URL('static/home/images/logo.png', baseUrl).href,
148
+ author: title.split(/-/).pop(),
149
+ language,
150
+ id: targetUrl,
151
+ };
152
+ };
153
+
154
+ export const route: Route = {
155
+ path: '/article',
156
+ name: '网站翻译',
157
+ url: 'www.ltaaa.cn',
158
+ maintainers: ['nczitzk'],
159
+ handler,
160
+ example: '/ltaaa/article',
161
+ parameters: undefined,
162
+ description: undefined,
163
+ categories: ['new-media'],
164
+ features: {
165
+ requireConfig: false,
166
+ requirePuppeteer: false,
167
+ antiCrawler: false,
168
+ supportRadar: true,
169
+ supportBT: false,
170
+ supportPodcast: false,
171
+ supportScihub: false,
172
+ },
173
+ radar: [
174
+ {
175
+ source: ['www.ltaaa.cn/article'],
176
+ target: '/article',
177
+ },
178
+ ],
179
+ view: ViewType.Articles,
180
+ };
@@ -0,0 +1,9 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: '龙腾网',
5
+ url: 'ltaaa.cn',
6
+ categories: ['new-media'],
7
+ description: '',
8
+ lang: 'zh-CN',
9
+ };
@@ -0,0 +1,7 @@
1
+ {{ if intro }}
2
+ <blockquote>{{ intro }}</blockquote>
3
+ {{ /if }}
4
+
5
+ {{ if description }}
6
+ {{@ description }}
7
+ {{ /if }}
@@ -26,6 +26,7 @@ export const route: Route = {
26
26
  const $ = load(response);
27
27
  const links = $('.archives-group article')
28
28
  .toArray()
29
+ .slice(0, 10)
29
30
  .map((item) => {
30
31
  item = $(item);
31
32
  const a = item.find('a').first();
@@ -103,7 +103,10 @@ const parseSimpleDetail = ($ele) => {
103
103
  const link = new URL($ele.attr('href'), baseUrl).href;
104
104
  const thumb = $ele.children('img');
105
105
  const thumbSrc = thumb.attr('data-src') || thumb.attr('src');
106
- const highResoThumbSrc = thumbSrc.replace('thumb', '1').replace(/t(\d+)\.nhentai\.net/, 'i$1.nhentai.net');
106
+ const highResoThumbSrc = thumbSrc
107
+ .replace('thumb', '1')
108
+ .replace(/t(\d+)\.nhentai\.net/, 'i$1.nhentai.net')
109
+ .replace('.webp.webp', '.webp');
107
110
  return {
108
111
  title: $ele.children('.caption').text(),
109
112
  link,
@@ -69,6 +69,9 @@ async function handler(ctx) {
69
69
  const getUserResource = (username: string) =>
70
70
  cache.tryGet(`pinterest:user:${username}`, async () => {
71
71
  const response = await ofetch(`${baseUrl}/resource/UserResource/get/`, {
72
+ headers: {
73
+ 'x-pinterest-pws-handler': 'www/[username]/_created.js',
74
+ },
72
75
  query: {
73
76
  source_url: `/${username}/_created`,
74
77
  data: JSON.stringify({ options: { username, field_set_key: 'unauth_profile' }, context: {} }),
@@ -81,6 +84,9 @@ const getUserResource = (username: string) =>
81
84
 
82
85
  const getUserActivityPinsResource = async (username: string, userId: string) => {
83
86
  const response = await ofetch(`${baseUrl}/resource/UserActivityPinsResource/get/`, {
87
+ headers: {
88
+ 'x-pinterest-pws-handler': 'www/[username]/_created.js',
89
+ },
84
90
  query: {
85
91
  source_url: `/${username}/_created`,
86
92
  data: JSON.stringify({ options: { exclude_add_pin_rep: true, field_set_key: 'grid_item', is_own_profile_pins: false, user_id: userId, username }, context: {} }),
@@ -93,6 +99,9 @@ const getUserActivityPinsResource = async (username: string, userId: string) =>
93
99
 
94
100
  const getBoardsFeedResource = async (username: string) => {
95
101
  const response = await ofetch(`${baseUrl}/resource/BoardsFeedResource/get/`, {
102
+ headers: {
103
+ 'x-pinterest-pws-handler': 'www/[username]/_saved.js',
104
+ },
96
105
  query: {
97
106
  source_url: `/${username}/_saved`,
98
107
  data: JSON.stringify({ options: { field_set_key: 'profile_grid_item', filter_stories: false, sort: 'last_pinned_to', username }, context: {} }),
@@ -124,11 +124,12 @@ async function handler(ctx) {
124
124
  )
125
125
  .sort((a: any, b: any) => b.length - a.length)[0] || '{}'
126
126
  );
127
- const renderData = JSON.parse(
127
+ const blockRenderData = JSON.parse(
128
128
  $('script:contains("column_2_text")')
129
129
  .text()
130
- .match(/renderData:\s(.*)/)?.[1] || '{}'
130
+ .match(/({.*})/)?.[1]
131
131
  );
132
+ const renderData = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('FeedSlideloadAuthor'))];
132
133
  const globalConst = JSON.parse(
133
134
  $('script:contains("globalConst")')
134
135
  .text()
@@ -58,7 +58,7 @@ async function handler(ctx) {
58
58
  itunes_category: meta.type,
59
59
  itunes_explicit: meta.explicit,
60
60
  allowEmpty: true,
61
- item: episodes.map((x) => ({
61
+ item: episodes.filter(Boolean).map((x) => ({
62
62
  title: x.name,
63
63
  description: x.html_description,
64
64
  pubDate: parseDate(x.release_date),