diff --git a/lib/router.js b/lib/router.js
index 25b935f614..9d7cff3553 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -4103,8 +4103,8 @@ router.get('/ehentai/tag/:tag/:page?/:bittorrent?', lazyloadRouteHandler('./rout
// 字型故事
router.get('/fontstory', lazyloadRouteHandler('./routes/fontstory/tw'));
-// HKEPC
-router.get('/hkepc/:category?', lazyloadRouteHandler('./routes/hkepc/index'));
+// HKEPC migrated to v2
+// router.get('/hkepc/:category?', lazyloadRouteHandler('./routes/hkepc/index'));
// 海南大学
router.get('/hainanu/ssszs', lazyloadRouteHandler('./routes/hainanu/ssszs'));
diff --git a/lib/routes/hkepc/index.js b/lib/routes/hkepc/index.js
deleted file mode 100644
index 4b0e088072..0000000000
--- a/lib/routes/hkepc/index.js
+++ /dev/null
@@ -1,164 +0,0 @@
-const got = require('@/utils/got');
-const cheerio = require('cheerio');
-const { parseDate } = require('@/utils/parse-date');
-const timezone = require('@/utils/timezone');
-
-module.exports = async (ctx) => {
- const category = ctx.params.category || '';
- const baseUrl = `https://www.hkepc.com`;
-
- let itemResponse;
- if (category === 'price' || category === 'review' || category === 'coverStory' || category === 'news' || category === 'press' || category === 'member') {
- itemResponse = await ctx.cache.tryGet(`${baseUrl}/news`, async () => {
- const resp = await got({
- method: 'get',
- url: `${baseUrl}/news`,
- headers: {
- Referer: baseUrl,
- },
- });
- return resp.data;
- });
- } else if (category === 'digital' || category === 'entertainment' || category === 'latest' || category === '') {
- itemResponse = await ctx.cache.tryGet(baseUrl, async () => {
- const resp = await got(baseUrl);
- return resp.data;
- });
- } else {
- itemResponse = await ctx.cache.tryGet(`${baseUrl}/${category}`, async () => {
- const resp = await got({
- method: 'get',
- url: `${baseUrl}/${category}`,
- headers: {
- Referer: baseUrl,
- },
- });
- return resp.data;
- });
- }
-
- const $ = cheerio.load(itemResponse);
-
- let title = '電腦領域 HKEPC';
- let articleList;
- switch (category) {
- case 'price':
- title += ' - 腦場新聞';
- articleList = $('#sidebar > div:nth-child(1) > div.content > ul > li');
- break;
- case 'review':
- title += ' - 新品快遞';
- articleList = $('#sidebar > div:nth-child(2) > div.content > ul > li');
- break;
- case 'coverStory':
- title += ' - 專題報導';
- articleList = $('#sidebar > div:nth-child(3) > div.content > ul > li');
- break;
- case 'news':
- title += ' - 新聞中心';
- articleList = $('#sidebar > div:nth-child(4) > div.content > ul > li');
- break;
- case 'press':
- title += ' - 專題報導';
- articleList = $('#sidebar > div:nth-child(5) > div.content > ul > li');
- break;
- case 'member':
- title += ' - 會員消息';
- articleList = $('#sidebar > div:nth-child(6) > div.content > ul > li');
- break;
- case 'digital':
- title += ' - 流動數碼';
- articleList = $('#contentR5 > div.left > div.article > div.heading');
- break;
- case 'entertainment':
- title += ' - 生活娛樂';
- articleList = $('#contentR5 > div.right > div.article > div.heading');
- break;
- case 'latest':
- case '':
- title += ' - 最新消息';
- articleList = $('div .item');
- break;
- case 'ocLab':
- title += ' - 超頻領域';
- articleList = $('.heading');
- break;
- default:
- break;
- }
-
- const ProcessFeed = async (link) => {
- const resp = await got({
- method: 'get',
- url: link,
- headers: {
- Referer: baseUrl,
- },
- });
- return cheerio.load(resp.data)('#article').html();
- };
-
- const items = await Promise.all(
- articleList.get().map(async (item) => {
- const $ = cheerio.load(item);
- const $a = $('a').html($('a').html().replace(/
/g, ' '));
- const title = $a.text().trim();
- const link = baseUrl + $a.attr('href');
-
- const cache = await ctx.cache.get(link);
- if (cache) {
- return Promise.resolve(JSON.parse(cache));
- }
-
- const categories = [];
- const $article = cheerio.load(await ProcessFeed(link));
-
- // remove unwanted elements
- $article('#view > div.advertisement').remove();
- $article('#view > p').each((i, e) => {
- if (
- // =
- $(e)
- .html()
- .replace(/ /g, '').length === 0
- ) {
- $(e).remove();
- }
- });
-
- // extract categories
- $article('#articleHead > div.tags > a').each((i, e) => {
- categories.push($article(e).text().trim());
- });
-
- // fix lazyload image
- $article('#view > p > img').each((i, e) => {
- $(e).after(`
`);
- $(e).remove();
- });
-
- const description = $article('#view').html();
-
- const single = {
- title,
- author: $article('.newsAuthor').text().trim() || $article('#articleHead div.author').text().trim(),
- category: categories,
- description,
- pubDate: timezone(parseDate($article('.publishDate').text()), +8),
- guid: link.substring(0, link.lastIndexOf('/')),
- link,
- };
-
- ctx.cache.set(link, JSON.stringify(single));
- return Promise.resolve(single);
- })
- );
-
- ctx.state.data = {
- title,
- link: `https://www.hkepc.com/${category}`,
- description: '電腦領域 HKEPC Hardware - 全港 No.1 PC網站',
- language: 'zh-hk',
- item: items,
- };
-};
diff --git a/lib/v2/hkepc/index.js b/lib/v2/hkepc/index.js
new file mode 100644
index 0000000000..2d9fab1f46
--- /dev/null
+++ b/lib/v2/hkepc/index.js
@@ -0,0 +1,198 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const timezone = require('@/utils/timezone');
+const path = require('path');
+const { art } = require('@/utils/render');
+
+module.exports = async (ctx) => {
+ const category = ctx.params.category ?? '';
+ const baseUrl = `https://www.hkepc.com`;
+
+ let response;
+ if (category === 'price' || category === 'review' || category === 'coverStory' || category === 'news' || category === 'press' || category === 'member') {
+ response = await got({
+ url: `${baseUrl}/news`,
+ headers: {
+ Referer: baseUrl,
+ },
+ });
+ } else if (category === 'digital' || category === 'entertainment' || category === 'latest' || category === '') {
+ response = await got(baseUrl);
+ } else {
+ // category === ocLab
+ response = await got({
+ url: `${baseUrl}/${category}`,
+ headers: {
+ Referer: baseUrl,
+ },
+ });
+ }
+
+ const $ = cheerio.load(response.data);
+
+ let feedTitle = '電腦領域 HKEPC';
+ let items;
+ switch (category) {
+ case 'price':
+ feedTitle += ' - 腦場新聞';
+ items = $('#sidebar > div:nth-child(1) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'review':
+ feedTitle += ' - 新品快遞';
+ items = $('#sidebar > div:nth-child(2) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'coverStory':
+ feedTitle += ' - 專題報導';
+ items = $('#sidebar > div:nth-child(3) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'news':
+ feedTitle += ' - 新聞中心';
+ items = $('#sidebar > div:nth-child(4) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'press':
+ feedTitle += ' - 業界資訊';
+ items = $('#sidebar > div:nth-child(5) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'member':
+ feedTitle += ' - 會員消息';
+ items = $('#sidebar > div:nth-child(6) > div.content > ul > li')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'digital':
+ feedTitle += ' - 流動數碼';
+ items = $('#contentR5 > div.left > div.article > div.heading')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'entertainment':
+ feedTitle += ' - 生活娛樂';
+ items = $('#contentR5 > div.right > div.article > div.heading')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'latest':
+ case '':
+ feedTitle += ' - 最新消息';
+ items = $('div .item')
+ .find('a')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ case 'ocLab':
+ feedTitle += ' - 超頻領域';
+ items = $('.heading')
+ .toArray()
+ .map((item) => ({
+ title: $(item).text(),
+ link: baseUrl + $(item).attr('href'),
+ }));
+ break;
+ default:
+ break;
+ }
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got({
+ url: item.link,
+ headers: {
+ Referer: baseUrl,
+ },
+ });
+
+ const content = cheerio.load(detailResponse.data);
+
+ // remove unwanted elements
+ content('#view > div.advertisement').remove();
+ content('div#comments').remove();
+ content('div#share_btn').remove();
+
+ // Non-breaking space U+00A0, ` ` in html
+ // Taken from /caixin/blog.js
+ content('#view > p')
+ .filter((_, e) => e.children[0].data === String.fromCharCode(160))
+ .remove();
+
+ // fix lazyload image
+ content('#view > p > img').each((_, e) => {
+ e = content(e);
+ e.after(
+ art(path.join(__dirname, 'templates/image.art'), {
+ src: e.attr('rel') ?? e.attr('src'),
+ alt: e.attr('alt'),
+ })
+ );
+ e.remove();
+ });
+
+ item.author = content('.newsAuthor').text().trim() ?? content('#articleHead div.author').text().trim();
+ item.category = content('div#relatedArticles div.tags a')
+ .toArray()
+ .map((e) => content(e).text().trim());
+ item.description = art(path.join(__dirname, 'templates/description.art'), {
+ desc: content('#view').html(),
+ });
+ item.pubDate = timezone(parseDate(content('.publishDate').text()), +8);
+ item.guid = item.link.substring(0, item.link.lastIndexOf('/'));
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: feedTitle,
+ link: `https://www.hkepc.com/${category}`,
+ description: '電腦領域 HKEPC Hardware - 全港 No.1 PC網站',
+ language: 'zh-hk',
+ item: items,
+ };
+};
diff --git a/lib/v2/hkepc/maintainer.js b/lib/v2/hkepc/maintainer.js
new file mode 100644
index 0000000000..9644afe025
--- /dev/null
+++ b/lib/v2/hkepc/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/:category?': ['TonyRL'],
+};
diff --git a/lib/v2/hkepc/radar.js b/lib/v2/hkepc/radar.js
new file mode 100644
index 0000000000..bbfd687412
--- /dev/null
+++ b/lib/v2/hkepc/radar.js
@@ -0,0 +1,19 @@
+module.exports = {
+ 'hkepc.com': {
+ _name: 'HKEPC',
+ '.': [
+ {
+ title: 'HKEPC 电脑领域',
+ docs: 'https://docs.rsshub.app/new-media.html#hkepc',
+ source: ['/:category?'],
+ target: '/hkepc/:category?',
+ },
+ {
+ title: 'HKEPC 电脑领域',
+ docs: 'https://docs.rsshub.app/new-media.html#hkepc',
+ source: ['/'],
+ target: '/hkepc',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/hkepc/router.js b/lib/v2/hkepc/router.js
new file mode 100644
index 0000000000..cf781366fd
--- /dev/null
+++ b/lib/v2/hkepc/router.js
@@ -0,0 +1,3 @@
+module.exports = function (router) {
+ router.get('/:category?', require('./index'));
+};
diff --git a/lib/v2/hkepc/templates/description.art b/lib/v2/hkepc/templates/description.art
new file mode 100644
index 0000000000..7755ee9f6b
--- /dev/null
+++ b/lib/v2/hkepc/templates/description.art
@@ -0,0 +1 @@
+{{@ desc }}
diff --git a/lib/v2/hkepc/templates/image.art b/lib/v2/hkepc/templates/image.art
new file mode 100644
index 0000000000..40140a947e
--- /dev/null
+++ b/lib/v2/hkepc/templates/image.art
@@ -0,0 +1 @@
+