diff --git a/assets/radar-rules.js b/assets/radar-rules.js
index 930759e9b9..f1c0d2251f 100644
--- a/assets/radar-rules.js
+++ b/assets/radar-rules.js
@@ -865,10 +865,6 @@
{ title: 'IPO Featured Stories', docs: 'http://docs.rsshub.app/en/university.html#umass-amherst', source: '/ipo/iss/featured-stories', target: '/umass/amherst/ipostories' },
],
},
- 'lofter.com': {
- _name: 'Lofter',
- www: [{ title: '话题 (标签)', docs: 'https://docs.rsshub.app/social-media.html#lofter', source: ['/tag/:name', '/tag/:name/:type'], target: (params) => `/lofter/tag/${params.name}/${params.type || ''}` }],
- },
'yuque.com': {
_name: '语雀',
www: [
diff --git a/docs/en/social-media.md b/docs/en/social-media.md
index 2f30b32d8a..57244341ca 100644
--- a/docs/en/social-media.md
+++ b/docs/en/social-media.md
@@ -132,7 +132,7 @@ If you don't want to setup credentials, use Picuki.
### User
-
+
### Tag
diff --git a/docs/social-media.md b/docs/social-media.md
index 82a13f2bd6..a05e0afd6f 100644
--- a/docs/social-media.md
+++ b/docs/social-media.md
@@ -462,7 +462,7 @@ Tiny Tiny RSS 会给所有 iframe 元素添加 `sandbox="allow-scripts"` 属性
### 用户
-
+
### 话题 (标签)
diff --git a/lib/radar-rules.js b/lib/radar-rules.js
index dcd9d9662f..046c850d9d 100644
--- a/lib/radar-rules.js
+++ b/lib/radar-rules.js
@@ -1977,18 +1977,6 @@ module.exports = {
},
],
},
- 'lofter.com': {
- _name: 'Lofter',
- www: [
- {
- title: '话题 (标签)',
- docs: 'https://docs.rsshub.app/social-media.html#lofter',
- source: ['/tag/:name', '/tag/:name/:type'],
- target: (params) => `/lofter/tag/${params.name}/${params.type || ''}`,
- },
- ],
- },
-
'yuque.com': {
_name: '语雀',
www: [
diff --git a/lib/router.js b/lib/router.js
index 386120121a..7f665a7ce4 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -2179,9 +2179,9 @@ router.get('/taptap/topic/:id/:label?', lazyloadRouteHandler('./routes/taptap/to
router.get('/taptap/changelog/:id', lazyloadRouteHandler('./routes/taptap/changelog'));
router.get('/taptap/review/:id/:order?', lazyloadRouteHandler('./routes/taptap/review'));
-// lofter
-router.get('/lofter/tag/:name?/:type?', lazyloadRouteHandler('./routes/lofter/tag'));
-router.get('/lofter/user/:username', lazyloadRouteHandler('./routes/lofter/posts'));
+// lofter migrated to v2
+// router.get('/lofter/tag/:name?/:type?', lazyloadRouteHandler('./routes/lofter/tag'));
+// router.get('/lofter/user/:name?', lazyloadRouteHandler('./routes/lofter/user'));
// 米坛社区表盘
router.get('/watchface/:watch_type?/:list_type?', lazyloadRouteHandler('./routes/watchface/update'));
diff --git a/lib/routes/lofter/posts.js b/lib/routes/lofter/posts.js
deleted file mode 100644
index d6c659d196..0000000000
--- a/lib/routes/lofter/posts.js
+++ /dev/null
@@ -1,54 +0,0 @@
-const got = require('@/utils/got');
-
-module.exports = async (ctx) => {
- const blogdomain = `${ctx.params.username}.lofter.com`;
- const response = await got({
- method: 'post',
- url: `http://api.lofter.com/v2.0/blogHomePage.api?product=lofter-iphone-6.8.2`,
- headers: {
- Host: 'api.lofter.com',
- 'User-Agent': 'LOFTER/6.8.2 (iPhone; iOS 13.4; Scale/2.00)',
- },
- form: {
- blogdomain,
- checkpwd: '1',
- following: '0',
- limit: '15',
- method: 'getPostLists',
- needgetpoststat: '1',
- offset: '0',
- postdigestnew: '1',
- supportposttypes: '1,2,3,4,5,6',
- },
- });
- if (!response.data.response || response.data.response.posts.length === 0) {
- throw 'Blog Not Found';
- }
- const data = response.data.response.posts;
- const blogNickName = data[0].post.blogInfo.blogNickName;
- const selfIntro = data[0].post.blogInfo.selfIntro;
-
- ctx.state.data = {
- title: `${blogNickName} - lofter`,
- link: `https://${blogdomain}`,
- description: selfIntro,
- item: data.map((item) => {
- let content = item.post.content;
- const title = item.post.title || item.post.noticeLinkTitle;
- const photos = JSON.parse(item.post.photoLinks || `[]`);
- const images = [];
- photos.forEach((photo) => {
- images.push(`

`);
- });
- content = `${content}${images.join('')}`;
- return {
- title,
- description: content,
- pubDate: new Date(item.post.publishTime).toUTCString(),
- link: item.post.blogPageUrl,
- guid: item.post.blogPageUrl,
- author: blogNickName,
- };
- }),
- };
-};
diff --git a/lib/v2/lofter/maintainer.js b/lib/v2/lofter/maintainer.js
new file mode 100644
index 0000000000..630b1e8eed
--- /dev/null
+++ b/lib/v2/lofter/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/tag/:name?/:type?': ['hoilc', 'nczitzk'],
+ '/user/:name?': ['hondajojo', 'nczitzk'],
+};
diff --git a/lib/v2/lofter/radar.js b/lib/v2/lofter/radar.js
new file mode 100644
index 0000000000..8778182855
--- /dev/null
+++ b/lib/v2/lofter/radar.js
@@ -0,0 +1,17 @@
+module.exports = {
+ 'lofter.com': {
+ _name: 'Lofter',
+ www: [
+ {
+ title: '话题 (标签)',
+ docs: 'https://docs.rsshub.app/social-media.html#lofter',
+ source: ['/tag/:name', '/tag/:name/:type'],
+ target: (params) => `/lofter/tag/${params.name}/${params.type || ''}`,
+ },
+ {
+ title: '用户',
+ docs: 'https://docs.rsshub.app/social-media.html#lofter',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/lofter/router.js b/lib/v2/lofter/router.js
new file mode 100644
index 0000000000..06d96dab64
--- /dev/null
+++ b/lib/v2/lofter/router.js
@@ -0,0 +1,4 @@
+module.exports = (router) => {
+ router.get('/tag/:name?/:type?', require('./tag'));
+ router.get('/user/:name?', require('./user'));
+};
diff --git a/lib/routes/lofter/tag.js b/lib/v2/lofter/tag.js
similarity index 91%
rename from lib/routes/lofter/tag.js
rename to lib/v2/lofter/tag.js
index 88f2e7182c..eb04ae74d9 100644
--- a/lib/routes/lofter/tag.js
+++ b/lib/v2/lofter/tag.js
@@ -3,10 +3,10 @@ const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
- const name = ctx.params.name || '摄影';
- const type = ctx.params.type || 'new';
+ const name = ctx.params.name ?? '摄影';
+ const type = ctx.params.type ?? 'new';
- const rootUrl = 'http://www.lofter.com';
+ const rootUrl = 'https://www.lofter.com';
const currentUrl = `${rootUrl}/tag/${name}/${type}`;
const response = await got({
@@ -52,7 +52,7 @@ module.exports = async (ctx) => {
.get();
ctx.state.data = {
- title: `${name} - ${title}|LOFTER`,
+ title: `${name} - ${title} | LOFTER`,
link: currentUrl,
item: items,
};
diff --git a/lib/v2/lofter/user.js b/lib/v2/lofter/user.js
new file mode 100644
index 0000000000..c2d86596ef
--- /dev/null
+++ b/lib/v2/lofter/user.js
@@ -0,0 +1,52 @@
+const got = require('@/utils/got');
+const { parseDate } = require('@/utils/parse-date');
+
+module.exports = async (ctx) => {
+ const name = ctx.params.name ?? 'i';
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit) : '50';
+
+ const rootUrl = `${name}.lofter.com`;
+
+ const response = await got({
+ method: 'post',
+ url: `http://api.lofter.com/v2.0/blogHomePage.api?product=lofter-iphone-10.0.0`,
+ form: {
+ blogdomain: rootUrl,
+ checkpwd: '1',
+ following: '0',
+ limit,
+ method: 'getPostLists',
+ needgetpoststat: '1',
+ offset: '0',
+ postdigestnew: '1',
+ supportposttypes: '1,2,3,4,5,6',
+ },
+ });
+
+ if (!response.data.response || response.data.response.posts.length === 0) {
+ throw 'Blog Not Found';
+ }
+
+ const items = response.data.response.posts.map((item) => ({
+ title: item.post.title || item.post.noticeLinkTitle,
+ link: item.post.blogPageUrl,
+ description:
+ JSON.parse(item.post.photoLinks || `[]`)
+ .map((photo) => `
`)
+ .join('') +
+ JSON.parse(item.post.embed ? `[${item.post.embed}]` : `[]`)
+ .map((video) => ``)
+ .join('') +
+ item.post.content,
+ pubDate: parseDate(item.post.publishTime),
+ author: item.post.blogInfo.blogNickName,
+ category: item.post.tag.split(','),
+ }));
+
+ ctx.state.data = {
+ title: `${items[0].author} | LOFTER`,
+ link: rootUrl,
+ item: items,
+ description: response.data.response.posts[0].post.blogInfo.selfIntro,
+ };
+};