diff --git a/docs/anime.md b/docs/anime.md index 9675d9fd2c..9642960d42 100644 --- a/docs/anime.md +++ b/docs/anime.md @@ -485,6 +485,28 @@ Sources +## 腾讯动漫 + +### 排行榜 + + + +| 月票榜 | 飙升榜 | 新作榜 | 畅销榜 | TOP100 | 男生榜 | 女生榜 | +| ------ | ------ | ------ | ------ | ------ | ------ | ------ | +| mt | rise | new | pay | top | male | female | + +::: tip 提示 + +`time` 参数仅在 `type` 参数选为 **月票榜** 的时候生效。 + +::: + + + +### 漫画 + + + ## 忧郁的 loli ### 文章 diff --git a/lib/v2/qq/ac/comic.js b/lib/v2/qq/ac/comic.js new file mode 100644 index 0000000000..3d8429e53f --- /dev/null +++ b/lib/v2/qq/ac/comic.js @@ -0,0 +1,43 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); + +const { rootUrl, mobileRootUrl } = require('./utils'); + +module.exports = async (ctx) => { + const id = ctx.params.id; + + const currentUrl = `${rootUrl}/Comic/comicInfo/id/${id}`; + const mobileCurrentUrl = `${mobileRootUrl}/comic/index/id/${id}`; + + const response = await got({ + method: 'get', + url: mobileCurrentUrl, + }); + + const $ = cheerio.load(response.data); + + const author = $('.author-wr') + .toArray() + .map((a) => $(a).text().trim()) + .join(', '); + + const items = $('.reverse .bottom-chapter-item .chapter-link') + .toArray() + .map((item) => { + item = $(item); + + return { + author, + title: item.text(), + description: ``, + link: `${rootUrl}${item.attr('href').replace(/chapter/, 'ComicView')}`, + }; + }); + + ctx.state.data = { + title: `${$('h1').text()} - 腾讯动漫`, + link: currentUrl, + item: items, + description: `

${$('.head-info-desc').text()}

`, + }; +}; diff --git a/lib/v2/qq/ac/rank.js b/lib/v2/qq/ac/rank.js new file mode 100644 index 0000000000..9093d263e9 --- /dev/null +++ b/lib/v2/qq/ac/rank.js @@ -0,0 +1,20 @@ +const { rootUrl, ProcessItems } = require('./utils'); + +module.exports = async (ctx) => { + const titles = { + mt: '月票榜', + rise: '飙升榜', + new: '新作榜', + pay: '畅销榜', + top: 'TOP100', + male: '男生榜', + female: '女生榜', + }; + + const type = ctx.params.type ?? 'mt'; + const time = ctx.params.time ?? 'cur'; + + const currentUrl = `${rootUrl}/Rank/comicRank/type/${type}`; + + ctx.state.data = await ProcessItems(ctx, currentUrl, time, titles[type]); +}; diff --git a/lib/v2/qq/ac/templates/description.art b/lib/v2/qq/ac/templates/description.art new file mode 100644 index 0000000000..eaa42448eb --- /dev/null +++ b/lib/v2/qq/ac/templates/description.art @@ -0,0 +1,9 @@ +{{ if image !== '' }} + +{{ /if }} +{{ if description !== '' }} +

{{ description }}

+{{ /if }} +{{ each chapters chapter }} +
{{ chapter.title }}
+{{/each}} \ No newline at end of file diff --git a/lib/v2/qq/ac/utils.js b/lib/v2/qq/ac/utils.js new file mode 100644 index 0000000000..b5b2190af4 --- /dev/null +++ b/lib/v2/qq/ac/utils.js @@ -0,0 +1,71 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const rootUrl = 'https://ac.qq.com'; +const mobileRootUrl = 'https://m.ac.qq.com'; + +module.exports = { + rootUrl, + mobileRootUrl, + ProcessItems: async (ctx, currentUrl, time, title) => { + const response = await got({ + method: 'get', + url: currentUrl, + }); + + const $ = cheerio.load(response.data); + + let items = $(`${time ? `.${time}-month-data ` : ''}.text-overflow`) + .slice(0, ctx.query.limit ? parseInt(ctx.query.limit) : 30) + .toArray() + .map((item) => { + item = $(item); + + return { + title: item.text(), + guid: `${rootUrl}${item.attr('href')}`, + link: `${mobileRootUrl}${item.attr('href').replace(/Comic\/ComicInfo/, 'comic/index')}`, + }; + }); + + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.guid, async () => { + const detailResponse = await got({ + method: 'get', + url: item.link, + }); + + const content = cheerio.load(detailResponse.data); + + item.link = item.guid; + item.author = content('.author-wr') + .toArray() + .map((a) => $(a).text().trim()) + .join(', '); + item.description = art(path.join(__dirname, 'templates/description.art'), { + image: content('.head-cover')?.attr('src') ?? '', + description: content('.head-info-desc')?.text() ?? '', + chapters: content('.reverse .bottom-chapter-item .chapter-link') + .toArray() + .map((chapter) => ({ + link: content(chapter).attr('href'), + title: content(chapter).find('.comic-title')?.text() ?? '', + image: content(chapter).find('.cover-image')?.attr('src') ?? '', + })), + }); + + return item; + }) + ) + ); + + return { + title: `${title} - 腾讯动漫`, + link: currentUrl, + item: items, + }; + }, +}; diff --git a/lib/v2/qq/maintainer.js b/lib/v2/qq/maintainer.js index c424e80069..2161661481 100644 --- a/lib/v2/qq/maintainer.js +++ b/lib/v2/qq/maintainer.js @@ -1,3 +1,5 @@ module.exports = { + '/ac/comic/:id': ['nczitzk'], + '/ac/rank/:type?/:time?': ['nczitzk'], '/live/:id': ['nczitzk'], }; diff --git a/lib/v2/qq/radar.js b/lib/v2/qq/radar.js index 6a0d8e3ee2..818cb96395 100644 --- a/lib/v2/qq/radar.js +++ b/lib/v2/qq/radar.js @@ -1,6 +1,20 @@ module.exports = { 'qq.com': { _name: '腾讯网', + ac: [ + { + title: '排行榜', + docs: 'https://docs.rsshub.app/anime.html#teng-xun-dong-man-pai-hang-bang', + source: ['/Rank/comicRank/type/:type', '/'], + target: '/qq/ac/rank/:type?/:time?', + }, + { + title: '漫画', + docs: 'https://docs.rsshub.app/anime.html#teng-xun-dong-man-man-hua', + source: ['/Comic/ComicInfo/id/:id', '/'], + target: '/qq/ac/comic/:id', + }, + ], live: [ { title: '企鹅直播', diff --git a/lib/v2/qq/router.js b/lib/v2/qq/router.js index 3f0bbeea47..1ed1c24a80 100644 --- a/lib/v2/qq/router.js +++ b/lib/v2/qq/router.js @@ -1,3 +1,5 @@ module.exports = function (router) { + router.get('/ac/comic/:id?', require('./ac/comic')); + router.get('/ac/rank/:type?/:time?', require('./ac/rank')); router.get('/live/:id', require('./live')); };