diff --git a/docs/reading.md b/docs/reading.md
index 673b00a60..068d00eb9 100644
--- a/docs/reading.md
+++ b/docs/reading.md
@@ -303,6 +303,10 @@ count 的取值范围为 1-12,为防止请求次数过多,推荐设置为 5
+### 最新卷
+
+
+
## 生物帮
### 所有栏目
diff --git a/lib/router.js b/lib/router.js
index ba6427815..40e8b50b9 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -1433,6 +1433,7 @@ router.get('/gaoqing/latest', lazyloadRouteHandler('./routes/gaoqing/latest'));
// 轻小说文库
router.get('/wenku8/chapter/:id', lazyloadRouteHandler('./routes/wenku8/chapter'));
+router.get('/wenku8/volume/:id', lazyloadRouteHandler('./routes/wenku8/volume'));
// 鲸跃汽车
router.get('/whalegogo/home', lazyloadRouteHandler('./routes/whalegogo/home'));
diff --git a/lib/routes/wenku8/volume.js b/lib/routes/wenku8/volume.js
new file mode 100644
index 000000000..d9e09b622
--- /dev/null
+++ b/lib/routes/wenku8/volume.js
@@ -0,0 +1,52 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const iconv = require('iconv-lite');
+
+module.exports = async (ctx) => {
+ const aid = ctx.params.id;
+ const novel_url = `https://www.wenku8.net/novel/${parseInt(aid / 1000)}/${aid}/index.htm`;
+ const $ = cheerio.load(await get(novel_url));
+
+ const novel_name = $('#title').text();
+
+ const volume_list = $('.vcss');
+ const last_volume = $(volume_list[volume_list.length - 1]);
+ const vid = last_volume.parent().next().find('a')[0].attribs.href.replace('.htm', '');
+
+ const volume_url = `https://dl.wenku8.com/packtxt.php?aid=${aid}&vid=${vid}&charset=gbk`;
+ const item_list = await ctx.cache.tryGet(volume_url, async () => {
+ const result = await get(volume_url);
+ const chapter_list = [...result.matchAll(/ {2}(\S.*)\r\n([\S\s]+?)\r\n\r\n/g)];
+ return chapter_list
+ .filter((chapter) => chapter[2].trim())
+ .reverse()
+ .map((chapter) => ({
+ title: chapter[1],
+ description: format_description(chapter[2]),
+ guid: Buffer.from(`${vid}${chapter[1]}`).toString('base64'),
+ }));
+ });
+
+ ctx.state.data = {
+ title: `轻小说文库 ${novel_name} 最新卷`,
+ link: novel_url,
+ item: item_list,
+ };
+};
+
+const get = async (url) => {
+ const response = await got({
+ method: 'get',
+ url,
+ responseType: 'buffer',
+ });
+
+ return iconv.decode(response.data, 'gbk');
+};
+
+const format_description = (str) =>
+ str
+ .split('\r\n')
+ .filter((line) => line.trim())
+ .map((line) => `
${line.trim()}
`)
+ .join('');