diff --git a/docs/multimedia.md b/docs/multimedia.md
index 28cc5b1bce..1ad89df555 100644
--- a/docs/multimedia.md
+++ b/docs/multimedia.md
@@ -102,9 +102,47 @@ pageClass: routes
## 2048 核基地
-### 论坛更新
+### 论坛
-
+
+
+| 最新合集 | 亞洲無碼 | 日本騎兵 | 歐美新片 | 國內原創 | 中字原創 | 三級寫真 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 3 | 4 | 5 | 13 | 15 | 16 | 18 |
+
+| 有碼.HD | 亞洲 SM.HD | 日韓 VR/3D | 歐美 VR/3D | S-cute / Mywife / G-area |
+| ------- | ---------- | ---------- | ---------- | ------------------------ |
+| 116 | 114 | 96 | 97 | 119 |
+
+| 網友自拍 | 亞洲激情 | 歐美激情 | 露出偷窺 | 高跟絲襪 | 卡通漫畫 | 原創达人 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 23 | 24 | 25 | 26 | 27 | 28 | 135 |
+
+| 唯美清純 | 网络正妹 | 亞洲正妹 | 素人正妹 | COSPLAY | 女优情报 | Gif 动图 |
+| -------- | -------- | -------- | -------- | ------- | -------- | -------- |
+| 21 | 274 | 276 | 277 | 278 | 29 | |
+
+| 獨家拍攝 | 稀有首發 | 网络见闻 | 主播實錄 | 珍稀套圖 | 名站同步 | 实用漫画 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 213 | 94 | 283 | 111 | 88 | 131 | 180 |
+
+| 网盘二区 | 网盘三区 | 分享福利 | 国产精选 | 高清福利 | 高清首发 | 多挂原创 |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 72 | 272 | 195 | 280 | 79 | 216 | 76 |
+
+| 磁链迅雷 | 正片大片 | H-GAME | 有声小说 | 在线视频 | 在线快播影院 |
+| -------- | -------- | ------ | -------- | -------- | ------------ |
+| 43 | 67 | 66 | 55 | 78 | 279 |
+
+| 综合小说 | 人妻意淫 | 乱伦迷情 | 长篇连载 | 文学作者 | TXT 小说打包 |
+| -------- | -------- | -------- | -------- | -------- | ------------ |
+| 48 | 103 | 50 | 54 | 100 | 109 |
+
+| 聚友客栈 | 坛友自售 |
+| -------- | -------- |
+| 57 | 136 |
+
+
## 60-Second Science - Scientific American
diff --git a/lib/router.js b/lib/router.js
index c3c653a2dc..dea262cdbc 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -2614,7 +2614,7 @@ router.get('/hackerone/search/:search', lazyloadRouteHandler('./routes/hackerone
router.get('/cowlevel/element/:id', lazyloadRouteHandler('./routes/cowlevel/element'));
// 2048
-router.get('/2048/bbs/:fid', lazyloadRouteHandler('./routes/2048/bbs'));
+// router.get('/2048/bbs/:fid', lazyloadRouteHandler('./routes/2048/bbs'));
// Google News
router.get('/google/news/:category/:locale', lazyloadRouteHandler('./routes/google/news'));
diff --git a/lib/routes/2048/bbs.js b/lib/routes/2048/bbs.js
deleted file mode 100644
index d9ef3a310a..0000000000
--- a/lib/routes/2048/bbs.js
+++ /dev/null
@@ -1,100 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-
-module.exports = async (ctx) => {
- const fid = ctx.params.fid;
-
- const base = 'https://hjd2048.com';
-
- const url = `${base}/2048/thread.php?fid-${fid}-page-1.html`;
-
- const list_response = await got.get(url);
- const $ = cheerio.load(list_response.data);
-
- const list = $('.tr3.t_one').toArray();
- $('#breadCrumb span.fr').remove();
- const forum_name = $('#breadCrumb').text().replace(/»/g, '-');
-
- const parseContent = async (htmlString) => {
- const $ = cheerio.load(htmlString);
-
- const time = $('.tiptop.cc > .fl.gray').attr('title');
- const content = $('.tpc_content');
-
- const result = {
- description: content.html(),
- pubDate: time ? new Date(time) : new Date(),
- };
-
- const test_external_torrent = content.find('a').filter(function () {
- return $(this)
- .attr('href')
- .match(/\/list\.php\?name=\w{32}/);
- });
-
- if (test_external_torrent.length !== 0) {
- const torrent_url = test_external_torrent[0].attribs.href;
- const response = await got.get(torrent_url);
- const magnet_url = response.data.match(/"(magnet:\?.*?)"/);
- if (magnet_url) {
- result.enclosure_url = magnet_url[1];
- result.enclosure_type = 'application/x-bittorrent';
- }
- }
-
- return result;
- };
-
- const out = await Promise.all(
- list.slice(0, 30).map(async (item) => {
- const $ = cheerio.load(item);
-
- if (!$('td > a').first().attr('title')) {
- return Promise.resolve('');
- }
-
- if ($("img[title='置顶帖标志']").length !== 0) {
- return Promise.resolve('');
- }
-
- const title = $('a.subject');
- const author = $('a.bl');
- const path = title.attr('href');
-
- const key = `/2048/${path}`;
- const link = `${base}/2048/${path}`;
-
- const cache = await ctx.cache.get(key);
- if (cache) {
- return Promise.resolve(JSON.parse(cache));
- }
-
- const rssitem = {
- title: title.text().trim(),
- author: author.text().trim(),
- link,
- guid: key,
- };
-
- try {
- const response = await got.get(link);
- const result = await parseContent(response.data);
-
- rssitem.description = result.description;
- rssitem.pubDate = result.pubDate;
- rssitem.enclosure_url = result.enclosure_url;
- rssitem.enclosure_type = result.enclosure_type;
- } catch (err) {
- return Promise.resolve('');
- }
- ctx.cache.set(key, JSON.stringify(rssitem));
- return Promise.resolve(rssitem);
- })
- );
-
- ctx.state.data = {
- title: forum_name,
- link: url,
- item: out.filter((item) => item !== ''),
- };
-};
diff --git a/lib/v2/2048/index.js b/lib/v2/2048/index.js
new file mode 100644
index 0000000000..8edc63d921
--- /dev/null
+++ b/lib/v2/2048/index.js
@@ -0,0 +1,91 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const timezone = require('@/utils/timezone');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const id = ctx.params.id ?? '2';
+
+ const rootUrl = 'https://hjd2048.com';
+ const currentUrl = `${rootUrl}/2048/thread.php?fid-${id}.html`;
+
+ const response = await got({
+ method: 'get',
+ url: currentUrl,
+ });
+
+ const $ = cheerio.load(response.data);
+
+ $('#shortcut').remove();
+ $('tr[onmouseover="this.className=\'tr3 t_two\'"]').remove();
+
+ const list = $('#ajaxtable tbody .tr2')
+ .last()
+ .nextAll('.tr3')
+ .map((_, item) => {
+ item = $(item).find('.subject');
+
+ return {
+ title: item.text(),
+ link: `${rootUrl}/2048/${item.attr('href')}`,
+ };
+ })
+ .get();
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got({
+ method: 'get',
+ url: item.link,
+ });
+
+ const content = cheerio.load(detailResponse.data);
+
+ content('.ads, .tips').remove();
+
+ content('ignore_js_op').each(function () {
+ content(this).replaceWith(`
`);
+ });
+
+ item.author = content('.fl.black').text();
+ item.pubDate = timezone(parseDate(content('.fl.gray').text().replace(/发表于: /, '')), +8);
+
+ const downloadLink = content('#read_tpc').first().find('a').last();
+
+ if (downloadLink.text().indexOf('elsbbus.com') > -1) {
+ const torrentResponse = await got({
+ method: 'get',
+ url: downloadLink.text(),
+ });
+
+ const torrent = cheerio.load(torrentResponse.data);
+
+ item.enclosure_type = 'application/x-bittorrent';
+ item.enclosure_url = `https://data.elsbbus.com/${torrent('.uk-button').last().attr('href')}`;
+
+ const magnet = torrent('.uk-button').first().attr('href');
+
+ downloadLink.replaceWith(
+ art(path.join(__dirname, 'templates/download.art'), {
+ magnet,
+ torrent: item.enclosure_url,
+ })
+ );
+ }
+
+ item.description = content('#read_tpc').first().html();
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: `${$('#main #breadCrumb a').last().text()} - 2048核基地`,
+ link: currentUrl,
+ item: items,
+ };
+};
diff --git a/lib/v2/2048/maintainer.js b/lib/v2/2048/maintainer.js
new file mode 100644
index 0000000000..77e7f46e2f
--- /dev/null
+++ b/lib/v2/2048/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/:id?': ['nczitzk'],
+};
diff --git a/lib/v2/2048/radar.js b/lib/v2/2048/radar.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lib/v2/2048/router.js b/lib/v2/2048/router.js
new file mode 100644
index 0000000000..c7491b29f4
--- /dev/null
+++ b/lib/v2/2048/router.js
@@ -0,0 +1,3 @@
+module.exports = function (router) {
+ router.get('/:id?', require('./index'));
+};
diff --git a/lib/v2/2048/templates/download.art b/lib/v2/2048/templates/download.art
new file mode 100644
index 0000000000..57ae254220
--- /dev/null
+++ b/lib/v2/2048/templates/download.art
@@ -0,0 +1 @@
+磁力連結 | 下載檔案
\ No newline at end of file