Skip to content

Commit 5e976d3

Browse files
authored
Merge pull request #10 from DIYgod/master
[pull] master from diygod:master
2 parents 7981e6e + 0b056eb commit 5e976d3

3 files changed

Lines changed: 88 additions & 39 deletions

File tree

lib/routes/manus/blog.ts

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
import { load } from 'cheerio';
1+
import MarkdownIt from 'markdown-it';
22

33
import type { Data, DataItem, Route } from '@/types';
44
import { ViewType } from '@/types';
55
import cache from '@/utils/cache';
66
import ofetch from '@/utils/ofetch';
77

8+
const md = MarkdownIt({
9+
html: true,
10+
linkify: true,
11+
});
12+
813
export const route: Route = {
914
path: '/blog',
1015
categories: ['programming'],
@@ -35,43 +40,76 @@ export const route: Route = {
3540
async function handler() {
3641
const rootUrl = 'https://manus.im/blog';
3742

38-
const response = await ofetch(rootUrl);
39-
const $ = load(response);
43+
const renderData = await ofetch(rootUrl, {
44+
headers: {
45+
RSC: '1',
46+
},
47+
responseType: 'text',
48+
});
4049

41-
const list: DataItem[] = $('div.mt-10.px-6 > a')
42-
.toArray()
43-
.map((item) => {
44-
const element = $(item);
45-
const link = new URL(String(element.attr('href')), rootUrl).href;
46-
const title = String(element.find('h2').attr('title'));
50+
let blogList;
51+
const lines = renderData.split('\n');
52+
for (const line of lines) {
53+
if (line.includes('{"blogList":{"$typeName"')) {
54+
const jsonStr = line.slice(Math.max(0, line.indexOf('{"blogList":{"$typeName"')));
55+
const lastBrace = jsonStr.lastIndexOf('}');
56+
try {
57+
const parsed = JSON.parse(jsonStr.slice(0, Math.max(0, lastBrace + 1)));
58+
blogList = parsed.blogList;
59+
break;
60+
} catch {
61+
// Ignore parse errors and try next line if any
62+
}
63+
}
64+
}
4765

48-
return {
49-
link,
50-
title,
51-
};
52-
});
66+
if (!blogList || !blogList.groups) {
67+
throw new Error('Failed to parse blogList from RSC data');
68+
}
69+
70+
const list: Array<DataItem & { _contentUrl?: string }> = blogList.groups.flatMap(
71+
(group) =>
72+
group.blogs?.map((blog) => ({
73+
title: blog.title,
74+
link: `https://manus.im/blog/${blog.recordUid}`,
75+
pubDate: new Date(blog.createdAt.seconds * 1000),
76+
description: blog.desc,
77+
category: [group.kindName],
78+
_contentUrl: blog.contentUrl,
79+
})) ?? []
80+
);
5381

5482
const items: DataItem[] = await Promise.all(
55-
list.map((item) =>
56-
cache.tryGet(String(item.link), async () => {
57-
const response = await ofetch(String(item.link));
58-
const $ = load(response);
59-
const description: string = $('div.relative:nth-child(3)').html() ?? '';
60-
const pubDateText: string = $('div.gap-3:nth-child(1) > span:nth-child(2)').text().trim();
61-
const currentYear: number = new Date().getFullYear();
62-
const pubDate: Date = new Date(`${pubDateText} ${currentYear}`);
83+
list.map(
84+
(item) =>
85+
cache.tryGet(String(item.link), async () => {
86+
const contentUrl = item._contentUrl;
87+
let description = String(item.description);
88+
if (contentUrl) {
89+
try {
90+
let contentText = await ofetch(contentUrl, { responseType: 'text' });
91+
// Fix video embeds: Manus uses ![type=manus_video](url) which markdown-it renders as <img>
92+
contentText = contentText.replaceAll(/!\[.*?\]\((.+?\.(mp4|mov|webm))\)/gi, '<video controls preload="metadata"><source src="$1"></video>');
93+
// Parse markdown to HTML
94+
description = md.render(contentText);
95+
} catch {
96+
// Fallback to description from list if fetch fails
97+
}
98+
}
99+
100+
// Remove the temporary property to avoid pollution
101+
delete item._contentUrl;
63102

64-
return {
65-
...item,
66-
description,
67-
pubDate,
68-
};
69-
})
103+
return {
104+
...item,
105+
description,
106+
};
107+
}) as Promise<DataItem>
70108
)
71109
);
72110

73111
return {
74-
title: 'Manus',
112+
title: 'Manus Blog',
75113
link: rootUrl,
76114
item: items,
77115
language: 'en',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@
148148
"@cloudflare/workers-types": "4.20260227.0",
149149
"@eslint/eslintrc": "3.3.3",
150150
"@eslint/js": "10.0.1",
151-
"@stylistic/eslint-plugin": "5.8.0",
151+
"@stylistic/eslint-plugin": "5.9.0",
152152
"@types/aes-js": "3.1.4",
153153
"@types/babel__preset-env": "7.10.0",
154154
"@types/crypto-js": "4.2.2",

pnpm-lock.yaml

Lines changed: 20 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)