diff --git a/assets/radar-rules.js b/assets/radar-rules.js index 73be642ab..a7fdcff13 100644 --- a/assets/radar-rules.js +++ b/assets/radar-rules.js @@ -827,20 +827,6 @@ { title: '日记评论区', docs: 'https://docs.rsshub.app/social-media.html#fur-affinity', source: '/journal/:id/', target: '/furaffinity/journal_comments/:id' }, ], }, - 'bgm.tv': { - _name: 'Bangumi', - '.': [ - { title: '小组话题', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/group/:id', target: '/bangumi/group/:id' }, - { title: '小组话题的新回复', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/group/topic/:id', target: '/bangumi/topic/:id' }, - { title: '现实人物的新作品', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/person/:id', target: '/bangumi/person/:id' }, - { title: '用户日志', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/user/:id', target: '/bangumi/user/blog/:id' }, - { title: '条目的讨论', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/subject/:id', target: '/bangumi/subject/:id/topics' }, - { title: '条目的评论', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/subject/:id', target: '/bangumi/subject/:id/blogs' }, - { title: '条目的章节', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/subject/:id', target: '/bangumi/subject/:id' }, - { title: '条目的吐槽箱', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/subject/:id', target: '/bangumi/subject/:id/comments' }, - { title: '放送列表', docs: 'https://docs.rsshub.app/anime.html#bangumi', source: '/calendar', target: '/bangumi/calendar/today' }, - ], - }, 'e-hentai.org/': { _name: 'E-Hentai', '.': [ diff --git a/docs/anime.md b/docs/anime.md index 388be1180..8e897496f 100644 --- a/docs/anime.md +++ b/docs/anime.md @@ -204,43 +204,43 @@ pageClass: routes -## Bangumi +## Bangumi 番组计划 ### 放送列表 - + ### 条目的章节 - + ### 条目的吐槽箱 - + ### 条目的评论 - + ### 条目的讨论 - + ### 现实人物的新作品 - + ### 小组话题的新回复 - + ### 小组话题 - + ### 用户日志 - + ## bilibili @@ -616,11 +616,11 @@ Sources ### 最新 - + ### 标签 - + 更多标签请前往 [搜索种子](https://bangumi.moe/search/index) diff --git a/docs/en/anime.md b/docs/en/anime.md index 6c321595a..b319d6669 100644 --- a/docs/en/anime.md +++ b/docs/en/anime.md @@ -8,11 +8,11 @@ pageClass: routes ### Latest - + ### Tags - + For more tags, please go to [Search torrent](https://bangumi.moe/search/index) diff --git a/lib/radar-rules.js b/lib/radar-rules.js index befac161f..2399fe6b2 100644 --- a/lib/radar-rules.js +++ b/lib/radar-rules.js @@ -1672,65 +1672,6 @@ module.exports = { }, ], }, - 'bgm.tv': { - _name: 'Bangumi', - '.': [ - { - title: '小组话题', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/group/:id', - target: '/bangumi/group/:id', - }, - { - title: '小组话题的新回复', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/group/topic/:id', - target: '/bangumi/topic/:id', - }, - { - title: '现实人物的新作品', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/person/:id', - target: '/bangumi/person/:id', - }, - { - title: '用户日志', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/user/:id', - target: '/bangumi/user/blog/:id', - }, - { - title: '条目的讨论', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/subject/:id', - target: '/bangumi/subject/:id/topics', - }, - { - title: '条目的评论', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/subject/:id', - target: '/bangumi/subject/:id/blogs', - }, - { - title: '条目的章节', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/subject/:id', - target: '/bangumi/subject/:id', - }, - { - title: '条目的吐槽箱', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/subject/:id', - target: '/bangumi/subject/:id/comments', - }, - { - title: '放送列表', - docs: 'https://docs.rsshub.app/anime.html#bangumi', - source: '/calendar', - target: '/bangumi/calendar/today', - }, - ], - }, 'e-hentai.org/': { _name: 'E-Hentai', '.': [ diff --git a/lib/router.js b/lib/router.js index f93cbbe19..0ea162223 100644 --- a/lib/router.js +++ b/lib/router.js @@ -34,13 +34,13 @@ router.get('/1draw', lazyloadRouteHandler('./routes/1draw/index')); router.get('/benedictevans', lazyloadRouteHandler('./routes/benedictevans/recent.js')); // bangumi -router.get('/bangumi/calendar/today', lazyloadRouteHandler('./routes/bangumi/calendar/today')); -router.get('/bangumi/subject/:id/:type', lazyloadRouteHandler('./routes/bangumi/subject')); -router.get('/bangumi/person/:id', lazyloadRouteHandler('./routes/bangumi/person')); -router.get('/bangumi/topic/:id', lazyloadRouteHandler('./routes/bangumi/group/reply')); -router.get('/bangumi/group/:id', lazyloadRouteHandler('./routes/bangumi/group/topic')); -router.get('/bangumi/subject/:id', lazyloadRouteHandler('./routes/bangumi/subject')); -router.get('/bangumi/user/blog/:id', lazyloadRouteHandler('./routes/bangumi/user/blog')); +// router.get('/bangumi/calendar/today', lazyloadRouteHandler('./routes/bangumi/calendar/today')); +// router.get('/bangumi/subject/:id/:type', lazyloadRouteHandler('./routes/bangumi/subject')); +// router.get('/bangumi/person/:id', lazyloadRouteHandler('./routes/bangumi/person')); +// router.get('/bangumi/topic/:id', lazyloadRouteHandler('./routes/bangumi/group/reply')); +// router.get('/bangumi/group/:id', lazyloadRouteHandler('./routes/bangumi/group/topic')); +// router.get('/bangumi/subject/:id', lazyloadRouteHandler('./routes/bangumi/subject')); +// router.get('/bangumi/user/blog/:id', lazyloadRouteHandler('./routes/bangumi/user/blog')); // 報導者 migrated to v2 // router.get('/twreporter/newest', lazyloadRouteHandler('./routes/twreporter/newest')); diff --git a/lib/routes/bangumi/calendar/_base.js b/lib/routes/bangumi/calendar/_base.js deleted file mode 100644 index 8dcaf5054..000000000 --- a/lib/routes/bangumi/calendar/_base.js +++ /dev/null @@ -1,43 +0,0 @@ -const got = require('@/utils/got'); - -module.exports = async (ctx) => { - const bgmCalendarUrl = 'https://api.bgm.tv/calendar'; - const bgmDataUrl = 'https://cdn.jsdelivr.net/npm/bangumi-data/dist/data.json'; - - const url = [bgmCalendarUrl, bgmDataUrl]; - - const cache = await Promise.all(url.map(ctx.cache.get)); - const result = await Promise.all( - cache.map(async (c, i) => { - if (c) { - return Promise.resolve(JSON.parse(c)); - } else { - const response = await got({ - method: 'get', - url: url[i], - }); - const data = response.data; - - if (i === 1) { - // 只保留有 bangumi id 的番剧 - const length = data.items.length; - const items = []; - for (let index = 0; index < length; index++) { - const item = data.items[index]; - const bgm_site = item.sites.find((s) => s.site === 'bangumi'); - if (bgm_site) { - item.bgm_id = bgm_site.id; - items.push(item); - } - } - data.items = items; - } - - ctx.cache.set(url[i], JSON.stringify(data)); - return Promise.resolve(data); - } - }) - ); - - return result; -}; diff --git a/lib/routes/bangumi/group/topic.js b/lib/routes/bangumi/group/topic.js deleted file mode 100644 index 502268801..000000000 --- a/lib/routes/bangumi/group/topic.js +++ /dev/null @@ -1,26 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const resolve_url = require('url').resolve; - -const base_url = 'https://bgm.tv/'; - -module.exports = async (ctx) => { - const groupID = ctx.params.id; - const link = 'https://bgm.tv/group/' + groupID + '/forum'; - const html = (await got.get(link)).data; - const $ = cheerio.load(html); - const title = 'Bangumi - ' + $('.SecondaryNavTitle').text(); - - ctx.state.data = { - title, - link, - item: $('.topic_list .topic') - .map((_, elem) => ({ - link: resolve_url(base_url, $('.subject a', elem).attr('href')), - title: $('.subject a', elem).attr('title'), - pubDate: new Date($('.lastpost .time', elem).text()).toUTCString(), - description: 'Author: ' + $('.author a', elem).text() + '
' + 'Reply: ' + $('.posts', elem).text(), - })) - .get(), - }; -}; diff --git a/lib/routes/bangumi/subject/ep.js b/lib/routes/bangumi/subject/ep.js deleted file mode 100644 index 41e814222..000000000 --- a/lib/routes/bangumi/subject/ep.js +++ /dev/null @@ -1,26 +0,0 @@ -const got = require('@/utils/got'); - -module.exports = async (subjectID) => { - const url = `https://api.bgm.tv/subject/${subjectID}?responseGroup=large`; - const epsInfo = (await got.get(url)).data; - const activeEps = []; - - epsInfo.eps.forEach((e) => { - if (e.status === 'Air') { - activeEps.push(e); - } - }); - - return { - title: epsInfo.name_cn, - link: `https://bgm.tv/subject/${subjectID}`, - description: epsInfo.summary, - item: activeEps.reverse().map((e) => ({ - title: `ep.${e.sort} ${e.name_cn}`, - description: `ep.${e.sort} ${e.name_cn}

${e.desc.replace(/\n+/g, '
')}

`, - pubDate: new Date(e.airdate).toUTCString(), - guid: e.id, - link: e.url, - })), - }; -}; diff --git a/lib/v2/bangumi/maintainer.js b/lib/v2/bangumi/maintainer.js index 52659f4b7..3c73156f9 100644 --- a/lib/v2/bangumi/maintainer.js +++ b/lib/v2/bangumi/maintainer.js @@ -1,5 +1,17 @@ module.exports = { + // bangumi.moe + '/moe': ['nczitzk'], + '/moe/:tags?': ['nczitzk'], + // bangumi.online '/online': ['devinmugen'], - '/': ['nczitzk'], - '/:tags?': ['nczitzk'], + // bangumi.tv + '/tv/calendar/today': ['magic-akari'], + '/tv/group/:id': ['SettingDust'], + '/tv/person/:id': ['ylc395'], + '/tv/subject/:id': ['SettingDust'], + '/tv/subject/:id/blogs': ['ylc395'], + '/tv/subject/:id/comments': ['ylc395'], + '/tv/subject/:id/topics': ['ylc395'], + '/tv/topic/:id': ['ylc395'], + '/tv/user/blog/:id': ['nczitzk'], }; diff --git a/lib/v2/bangumi/index.js b/lib/v2/bangumi/moe/index.js similarity index 96% rename from lib/v2/bangumi/index.js rename to lib/v2/bangumi/moe/index.js index 4e5bb27be..8bab84d8d 100644 --- a/lib/v2/bangumi/index.js +++ b/lib/v2/bangumi/moe/index.js @@ -2,7 +2,7 @@ const got = require('@/utils/got'); const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { - const isLatest = ctx.path === '/'; + const isLatest = ctx.path === '/moe'; const rootUrl = 'https://bangumi.moe'; let response; @@ -19,7 +19,7 @@ module.exports = async (ctx) => { const tagUrl = `${rootUrl}/api/tag/search`; const torrentUrl = `${rootUrl}/api/torrent/search`; - const params = ctx.path.split('/').slice(1); + const params = ctx.path.split('/').slice(2); tag_id = await Promise.all( params.map((param) => diff --git a/lib/v2/bangumi/online.js b/lib/v2/bangumi/online/online.js similarity index 89% rename from lib/v2/bangumi/online.js rename to lib/v2/bangumi/online/online.js index a07e4e738..d9cd89bc4 100644 --- a/lib/v2/bangumi/online.js +++ b/lib/v2/bangumi/online/online.js @@ -11,7 +11,7 @@ module.exports = async (ctx) => { const items = list.map((item) => ({ title: `${item.title_zh} - 第 ${item.volume} 集`, - description: art(path.join(__dirname, 'templates/image.art'), { + description: art(path.join(__dirname, '../templates/online/image.art'), { src: `https:${item.cover}`, alt: `${item.title_zh} - 第 ${item.volume} 集`, }), diff --git a/lib/v2/bangumi/radar.js b/lib/v2/bangumi/radar.js index eea70864b..5abd7ba88 100644 --- a/lib/v2/bangumi/radar.js +++ b/lib/v2/bangumi/radar.js @@ -1,3 +1,63 @@ +const bangumiTV = { + _name: 'Bangumi 番组计划', + '.': [ + { + title: '小组话题', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/group/:id', + target: '/bangumi/tv/group/:id', + }, + { + title: '小组话题的新回复', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/group/topic/:id', + target: '/bangumi/tv/topic/:id', + }, + { + title: '现实人物的新作品', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/person/:id', + target: '/bangumi/tv/person/:id', + }, + { + title: '用户日志', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/user/:id', + target: '/bangumi/tv/user/blog/:id', + }, + { + title: '条目的讨论', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/subject/:id', + target: '/bangumi/tv/subject/:id/topics', + }, + { + title: '条目的评论', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/subject/:id', + target: '/bangumi/tv/subject/:id/blogs', + }, + { + title: '条目的章节', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/subject/:id', + target: '/bangumi/tv/subject/:id', + }, + { + title: '条目的吐槽箱', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/subject/:id', + target: '/bangumi/tv/subject/:id/comments', + }, + { + title: '放送列表', + docs: 'https://docs.rsshub.app/anime.html#bangumi', + source: '/calendar', + target: '/bangumi/tv/calendar/today', + }, + ], +}; + module.exports = { 'bangumi.moe': { _name: '萌番組', @@ -6,13 +66,13 @@ module.exports = { title: '最新', docs: 'https://docs.rsshub.app/anime.html#meng-fan-zu-zui-xin', source: ['/'], - target: '/bangumi', + target: '/bangumi/moe', }, { title: '标签', docs: 'https://docs.rsshub.app/anime.html#meng-fan-zu-biao-qian', source: ['/search/index'], - target: '/bangumi/:tags?', + target: '/bangumi/moe/:tags?', }, ], }, @@ -27,4 +87,6 @@ module.exports = { }, ], }, + 'bangumi.tv': bangumiTV, + 'bgm.tv': bangumiTV, }; diff --git a/lib/v2/bangumi/router.js b/lib/v2/bangumi/router.js index 5c90781a2..7707b9c10 100644 --- a/lib/v2/bangumi/router.js +++ b/lib/v2/bangumi/router.js @@ -1,4 +1,14 @@ module.exports = function (router) { - router.get('/online', require('./online')); - router.get(/([\w-/]+)?/, require('./index')); + // bangumi.moe + router.get(/moe([\w-/]+)?/, require('./moe/index')); + // bangumi.online + router.get('/online', require('./online/online')); + // bangumi.tv + router.get('/tv/calendar/today', require('./tv/calendar/today')); + router.get('/tv/group/:id', require('./tv/group/topic')); + router.get('/tv/person/:id', require('./tv/person')); + router.get('/tv/subject/:id', require('./tv/subject')); + router.get('/tv/subject/:id/:type', require('./tv/subject')); + router.get('/tv/topic/:id', require('./tv/group/reply')); + router.get('/tv/user/blog/:id', require('./tv/user/blog')); }; diff --git a/lib/v2/bangumi/templates/image.art b/lib/v2/bangumi/templates/online/image.art similarity index 100% rename from lib/v2/bangumi/templates/image.art rename to lib/v2/bangumi/templates/online/image.art diff --git a/lib/v2/bangumi/templates/tv/ep.art b/lib/v2/bangumi/templates/tv/ep.art new file mode 100644 index 000000000..dd383d563 --- /dev/null +++ b/lib/v2/bangumi/templates/tv/ep.art @@ -0,0 +1,2 @@ +ep.{{ e.sort }} {{ e.name_cn || e.name }} +

{{@ e.desc.replace(/\r\n/g, '
') }}

diff --git a/lib/v2/bangumi/templates/tv/today.art b/lib/v2/bangumi/templates/tv/today.art new file mode 100644 index 000000000..a0c4e0258 --- /dev/null +++ b/lib/v2/bangumi/templates/tv/today.art @@ -0,0 +1,12 @@ + +
    +{{ each bgm.sites site }} +{{ set url }} +{{ if site.url }} + {{ url = site.url }} +{{ else }} + <% url = siteMeta[site.site].urlTemplate.replace('{{id}}', site.id) %> +{{ /if }} +
  • {{ siteMeta[site.site].title }}
  • +{{ /each }} +
diff --git a/lib/v2/bangumi/tv/calendar/_base.js b/lib/v2/bangumi/tv/calendar/_base.js new file mode 100644 index 000000000..1e99cf616 --- /dev/null +++ b/lib/v2/bangumi/tv/calendar/_base.js @@ -0,0 +1,37 @@ +const got = require('@/utils/got'); +const config = require('@/config').value; + +module.exports = (tryGet) => { + const bgmCalendarUrl = 'https://api.bgm.tv/calendar'; + const bgmDataUrl = 'https://cdn.jsdelivr.net/npm/bangumi-data/dist/data.json'; + + const urls = [bgmCalendarUrl, bgmDataUrl]; + + return Promise.all( + urls.map((item, i) => + tryGet( + item, + async () => { + const { data } = await got(item); + + if (i === 1) { + // 只保留有 bangumi id 的番剧 + const items = []; + for (const item of data.items) { + const bgmSite = item.sites.find((s) => s.site === 'bangumi'); + if (bgmSite) { + item.bgmId = bgmSite.id; + items.push(item); + } + } + data.items = items; + } + + return data; + }, + config.cache.contentExpire, + false + ) + ) + ); +}; diff --git a/lib/routes/bangumi/calendar/today.js b/lib/v2/bangumi/tv/calendar/today.js similarity index 62% rename from lib/routes/bangumi/calendar/today.js rename to lib/v2/bangumi/tv/calendar/today.js index 5234a19e0..d157069f4 100644 --- a/lib/routes/bangumi/calendar/today.js +++ b/lib/v2/bangumi/tv/calendar/today.js @@ -1,27 +1,25 @@ const getData = require('./_base'); +const { art } = require('@/utils/render'); +const path = require('path'); module.exports = async (ctx) => { - const [list, data] = await getData(ctx); + const [list, data] = await getData(ctx.cache.tryGet); const siteMeta = data.siteMeta; const today = new Date(Date.now()); - // 将 UTC 时间向前移动9小时,即可在数值上表示东京时间 today.setUTCHours(today.getUTCHours() + 9); - const day = today.getUTCDay(); - const todayList = list.filter((l) => l.weekday.id % 7 === day)[0]; - - const todayBgmId = todayList.items.map((t) => t.id); + const todayList = list.find((l) => l.weekday.id % 7 === day); + const todayBgmId = todayList.items.map((t) => t.id.toString()); const images = todayList.items.reduce((p, c) => { p[c.id] = (c.images || {}).large; return p; }, {}); - - const todayBgm = data.items.filter((d) => todayBgmId.includes(d.bgm_id)); + const todayBgm = data.items.filter((d) => todayBgmId.includes(d.bgmId)); todayBgm.forEach((bgm) => { - bgm.image = images[bgm.bgm_id]; + bgm.image = images[bgm.bgmId]; }); ctx.state.data = { @@ -35,25 +33,13 @@ module.exports = async (ctx) => { updated.setMinutes(begin.getMinutes()); updated.setSeconds(begin.getSeconds()); - const link = `http://bangumi.tv/subject/${bgm.bgm_id}`; + const link = `https://bangumi.tv/subject/${bgm.bgmId}`; const id = `${link}#${new Intl.DateTimeFormat('zh-CN').format(updated)}`; - const image = ``; - const html = - image + - '
    ' + - bgm.sites - .map((s) => { - let url; - if (s.url) { - url = s.url; - } else { - url = siteMeta[s.site].urlTemplate.replace('{{id}}', s.id); - } - return `
  • ${siteMeta[s.site].title}
  • `; - }) - .join('') + - '
'; + const html = art(path.resolve(__dirname, '../../templates/tv/today.art'), { + bgm, + siteMeta, + }); return { id, @@ -63,7 +49,9 @@ module.exports = async (ctx) => { Object.values(bgm.titleTranslate) .map((t) => t.join('|')) .join('|'), - ].join('|'), + ] + .filter((t) => t) // don't join if empty + .join('|'), updated: updated.toISOString(), pubDate: updated.toUTCString(), link, diff --git a/lib/routes/bangumi/group/reply.js b/lib/v2/bangumi/tv/group/reply.js similarity index 50% rename from lib/routes/bangumi/group/reply.js rename to lib/v2/bangumi/tv/group/reply.js index 25d3f630f..d498fe90e 100644 --- a/lib/routes/bangumi/group/reply.js +++ b/lib/v2/bangumi/tv/group/reply.js @@ -1,16 +1,18 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); module.exports = async (ctx) => { // bangumi.tv未提供获取小组话题的API,因此仍需要通过抓取网页来获取 const topicID = ctx.params.id; const link = `https://bgm.tv/group/topic/${topicID}`; - const html = (await got.get(link)).data; + const { data: html } = await got(link); const $ = cheerio.load(html); const title = $('#pageHeader h1').text(); const latestReplies = $('.row_reply') - .slice(-10) - .map((i, el) => { + .toArray() + .map((el) => { const $el = $(el); return { id: $el.attr('id'), @@ -18,19 +20,28 @@ module.exports = async (ctx) => { content: $el.find('.reply_content .message').html(), date: $el.children().first().find('small').children().remove().end().text().slice(3), }; - }) - .get() - .reverse(); + }); + + const postTopic = { + title, + description: $('.postTopic .topic_content').html(), + author: $('.postTopic .inner strong a').first().text(), + pubDate: timezone(parseDate($('.postTopic .re_info small').text().trim().slice(5)), +8), + link, + }; ctx.state.data = { title: `${title}的最新回复`, link, - item: latestReplies.map((c) => ({ - title: `${c.author}回复了小组话题《${title}》`, - description: c.content, - guid: c.id, - pubDate: new Date(c.date).toUTCString(), - link, - })), + item: [ + ...latestReplies.map((c) => ({ + title: `${c.author} 回复了小组话题《${title}》`, + description: c.content, + pubDate: timezone(parseDate(c.date), +8), + author: c.author, + link: `${link}#${c.id}`, + })), + postTopic, + ], }; }; diff --git a/lib/v2/bangumi/tv/group/topic.js b/lib/v2/bangumi/tv/group/topic.js new file mode 100644 index 000000000..9142d967e --- /dev/null +++ b/lib/v2/bangumi/tv/group/topic.js @@ -0,0 +1,26 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const base_url = 'https://bgm.tv'; + +module.exports = async (ctx) => { + const groupID = ctx.params.id; + const link = `${base_url}/group/${groupID}/forum`; + const { data: html } = await got(link); + const $ = cheerio.load(html); + const title = 'Bangumi - ' + $('.SecondaryNavTitle').text(); + + ctx.state.data = { + title, + link, + item: $('.topic_list .topic') + .toArray() + .map((elem) => ({ + link: new URL($('.subject a', elem).attr('href'), base_url).href, + title: $('.subject a', elem).attr('title'), + pubDate: parseDate($('.lastpost .time', elem).text()), + description: 'Reply: ' + $('.posts', elem).text(), + author: $('.author a', elem).text(), + })), + }; +}; diff --git a/lib/routes/bangumi/person/index.js b/lib/v2/bangumi/tv/person/index.js similarity index 59% rename from lib/routes/bangumi/person/index.js rename to lib/v2/bangumi/tv/person/index.js index 3ddeb0645..35ab6ace1 100644 --- a/lib/routes/bangumi/person/index.js +++ b/lib/v2/bangumi/tv/person/index.js @@ -1,15 +1,17 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); module.exports = async (ctx) => { // bangumi.tv未提供获取“人物信息”的API,因此仍需要通过抓取网页来获取 const personID = ctx.params.id; const link = `https://bgm.tv/person/${personID}/works?sort=date`; - const html = (await got.get(link)).data; + const { data: html } = await got(link); const $ = cheerio.load(html); const personName = $('.nameSingle a').text(); const works = $('.item') - .map((i, el) => { + .toArray() + .map((el) => { const $el = $(el); const $workEl = $el.find('.l'); return { @@ -18,17 +20,19 @@ module.exports = async (ctx) => { workInfo: $el.find('p.info').text(), job: $el.find('.badge_job').text(), }; - }) - .get(); + }); ctx.state.data = { title: `${personName}参与的作品`, link, - item: works.map((c) => ({ - title: `${personName}以${c.job}的身份参与了作品《${c.work}》`, - description: c.workInfo, - link: c.workURL, - pubDate: c.workInfo.substring(0, c.workInfo.indexOf(' /')) === '' ? new Date(0).toUTCString() : new Date(c.workInfo.substring(0, c.workInfo.indexOf(' /'))).toUTCString(), - })), + item: works.map((c) => { + const match = c.workInfo.match(/(\d{4}(?:-|年)\d{1,2}(?:-|月)\d{1,2})/); + return { + title: `${personName}以${c.job}的身份参与了作品《${c.work}》`, + description: c.workInfo, + link: c.workURL, + pubDate: match ? parseDate(match[1], ['YYYY-MM-DD', 'YYYY-M-D']) : null, + }; + }), }; }; diff --git a/lib/routes/bangumi/subject/comments.js b/lib/v2/bangumi/tv/subject/comments.js similarity index 65% rename from lib/routes/bangumi/subject/comments.js rename to lib/v2/bangumi/tv/subject/comments.js index b150fd1f0..7c0ca8d56 100644 --- a/lib/routes/bangumi/subject/comments.js +++ b/lib/v2/bangumi/tv/subject/comments.js @@ -1,34 +1,32 @@ const got = require('@/utils/got'); const cheerio = require('cheerio'); +const { parseDate, parseRelativeDate } = require('@/utils/parse-date'); module.exports = async (subjectID, minLength) => { // bangumi.tv未提供获取“吐槽(comments)”的API,因此仍需要通过抓取网页来获取 const link = `https://bgm.tv/subject/${subjectID}/comments`; - const html = (await got.get(link)).data; + const { data: html } = await got(link); const $ = cheerio.load(html); const title = $('.nameSingle').find('a').text(); const comments = $('.item') - .map((i, el) => { + .toArray() + .map((el) => { const $el = $(el); - const $rateEl = $el.find('.starsinfo'); + const $rateEl = $el.find('.starlight'); let rate = null; if ($rateEl.length > 0) { - rate = $rateEl.attr('class').match(/sstars(\d)/)[1]; + rate = $rateEl.attr('class').match(/stars(\d)/)[1]; } const dateString = $el.find('small.grey').text().slice(2); + let date; if (dateString.includes('ago')) { // 处理表示相对日期的字符串 - const list = dateString - .match(/(\dd )?(\d{1,2}h )?(\d{1,2}m )?ago/) - .slice(1) - .map((s) => (s ? Number(s.slice(0, -2)) : 0)); - - date = new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * list[0] - 1000 * 60 * 60 * list[1] - 1000 * 60 * list[2]).toUTCString(); + date = parseRelativeDate(dateString); } else { // 表示绝对日期的字符串 - date = new Date(dateString).toUTCString(); + date = parseDate(dateString); } return { @@ -38,15 +36,15 @@ module.exports = async (subjectID, minLength) => { date, }; }) - .get() .filter((obj) => obj.content.length >= minLength); + return { - title: `${title}的Bangumi吐槽箱`, + title: `${title}的 Bangumi 吐槽箱`, link, item: comments.map((c) => ({ title: `${c.user}的吐槽`, description: `【评分:${c.rate}】 ${c.content}`, - guid: c.user, + guid: `${link}#${c.user}`, pubDate: c.date, link, })), diff --git a/lib/v2/bangumi/tv/subject/ep.js b/lib/v2/bangumi/tv/subject/ep.js new file mode 100644 index 000000000..b0e02b8b3 --- /dev/null +++ b/lib/v2/bangumi/tv/subject/ep.js @@ -0,0 +1,31 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); + +module.exports = async (subjectID) => { + const url = `https://api.bgm.tv/subject/${subjectID}?responseGroup=large`; + const { data: epsInfo } = await got(url); + const activeEps = []; + + epsInfo.eps.forEach((e) => { + if (e.status === 'Air') { + activeEps.push(e); + } + }); + + return { + title: epsInfo.name_cn || epsInfo.name, + link: `https://bgm.tv/subject/${subjectID}`, + description: epsInfo.summary, + item: activeEps.map((e) => ({ + title: `ep.${e.sort} ${e.name_cn || e.name}`, + description: art(path.resolve(__dirname, '../../templates/tv/ep.art'), { + e, + epsInfo, + }), + pubDate: parseDate(e.airdate), + link: e.url.replace('http:', 'https:'), + })), + }; +}; diff --git a/lib/routes/bangumi/subject/index.js b/lib/v2/bangumi/tv/subject/index.js similarity index 88% rename from lib/routes/bangumi/subject/index.js rename to lib/v2/bangumi/tv/subject/index.js index 8eb61a088..0929aa32d 100644 --- a/lib/routes/bangumi/subject/index.js +++ b/lib/v2/bangumi/tv/subject/index.js @@ -1,6 +1,5 @@ const getComments = require('./comments.js'); const getFromAPI = require('./offcial-subject-api.js'); - const getEps = require('./ep.js'); module.exports = async (ctx) => { @@ -9,7 +8,7 @@ module.exports = async (ctx) => { if (ctx.params.type) { switch (ctx.params.type) { case 'comments': - response = await getComments(id, Number(ctx.request.query.minLength) || 0); + response = await getComments(id, Number(ctx.query.minLength) || 0); break; case 'blogs': response = await getFromAPI('blog')(id); diff --git a/lib/routes/bangumi/subject/offcial-subject-api.js b/lib/v2/bangumi/tv/subject/offcial-subject-api.js similarity index 62% rename from lib/routes/bangumi/subject/offcial-subject-api.js rename to lib/v2/bangumi/tv/subject/offcial-subject-api.js index ac4792b00..98e6b1f6a 100644 --- a/lib/routes/bangumi/subject/offcial-subject-api.js +++ b/lib/v2/bangumi/tv/subject/offcial-subject-api.js @@ -1,4 +1,5 @@ const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); module.exports = (type) => { const mapping = { @@ -13,17 +14,18 @@ module.exports = (type) => { }; return async (subjectID) => { - // 官方提供的条目API文档见https://github.com/bangumi/api/blob/master/docs-raw/Subject-API.md + // 官方提供的条目API文档见 https://github.com/bangumi/api/blob/3f3fa6390c468816f9883d24be488e41f8946159/docs-raw/Subject-API.md const url = `https://api.bgm.tv/subject/${subjectID}?responseGroup=large`; - const subjectInfo = (await got.get(url)).data; + const { data: subjectInfo } = await got(url); return { - title: `${subjectInfo.name_cn || subjectInfo.name}的Bangumi${mapping[type].cn}`, + title: `${subjectInfo.name_cn || subjectInfo.name}的 Bangumi ${mapping[type].cn}`, link: `https://bgm.tv/subject/${subjectInfo.id}/${mapping[type].en}`, item: subjectInfo[type].map((article) => ({ title: `${article.user.nickname}:${article.title}`, description: article.summary || '', - link: article.url, - pubDate: new Date(article.timestamp * 1000).toUTCString(), + link: article.url.replace('http:', 'https:'), + pubDate: parseDate(article.timestamp, 'X'), + author: article.user.nickname, })), }; }; diff --git a/lib/routes/bangumi/user/blog.js b/lib/v2/bangumi/tv/user/blog.js similarity index 76% rename from lib/routes/bangumi/user/blog.js rename to lib/v2/bangumi/tv/user/blog.js index f4307cd01..466eec5ce 100644 --- a/lib/routes/bangumi/user/blog.js +++ b/lib/v2/bangumi/tv/user/blog.js @@ -1,6 +1,7 @@ -const url = require('url'); 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 currentUrl = `https://bgm.tv/user/${ctx.params.id}/blog`; @@ -11,16 +12,16 @@ module.exports = async (ctx) => { const $ = cheerio.load(response.data); const list = $('#entry_list div.item') .find('h2.title') - .map((_, item) => { + .toArray() + .map((item) => { item = $(item); const a = item.find('a'); return { title: a.text(), - link: url.resolve(`https://bgm.tv/`, a.attr('href')), - pubDate: new Date(item.parent().find('small.time').text() + ' GMT+8').toUTCString(), + link: new URL(a.attr('href'), 'https://bgm.tv').href, + pubDate: timezone(parseDate(item.parent().find('small.time').text()), 0), }; - }) - .get(); + }); const items = await Promise.all( list.map((item) =>