From ba424e30a87df7fb2e7ac43ef5a8be9f11c5e967 Mon Sep 17 00:00:00 2001 From: qixingchen Date: Tue, 29 Jan 2019 16:49:14 +0800 Subject: [PATCH] add bilibili video danmaku (#1478) --- docs/README.md | 2 ++ lib/router.js | 1 + lib/routes/bilibili/cache.js | 20 +++++++++++++++++++ lib/routes/bilibili/danmaku.js | 35 ++++++++++++++++++++++++++++++++++ package.json | 1 + 5 files changed, 59 insertions(+) create mode 100644 lib/routes/bilibili/danmaku.js diff --git a/docs/README.md b/docs/README.md index 075a4158d7..985e5b86df 100644 --- a/docs/README.md +++ b/docs/README.md @@ -297,6 +297,8 @@ RSSHub 提供下列 API 接口: + + #### 直播开播 diff --git a/lib/router.js b/lib/router.js index a7cc9e9a4d..5583d28029 100644 --- a/lib/router.js +++ b/lib/router.js @@ -123,6 +123,7 @@ router.get('/bilibili/partion/ranking/:tid/:days?', require('./routes/bilibili/p router.get('/bilibili/bangumi/:seasonid', require('./routes/bilibili/bangumi')); // 弃用 router.get('/bilibili/bangumi/media/:mediaid', require('./routes/bilibili/bangumi')); router.get('/bilibili/video/reply/:aid', require('./routes/bilibili/reply')); +router.get('/bilibili/video/danmaku/:aid/:pid?', require('./routes/bilibili/danmaku')); router.get('/bilibili/link/news/:product', require('./routes/bilibili/linkNews')); router.get('/bilibili/live/room/:roomID', require('./routes/bilibili/liveRoom')); router.get('/bilibili/live/search/:key/:order', require('./routes/bilibili/liveSearch')); diff --git a/lib/routes/bilibili/cache.js b/lib/routes/bilibili/cache.js index b53f5db0c0..21c7f1b124 100644 --- a/lib/routes/bilibili/cache.js +++ b/lib/routes/bilibili/cache.js @@ -102,4 +102,24 @@ module.exports = { } return name; }, + getCidFromAid: async (ctx, aid, pid) => { + const key = `bili-Cid-from-Aid-${aid}-${pid}`; + let cid = await ctx.cache.get(key); + if (!cid) { + const cidResponse = await axios({ + method: 'get', + url: `https://api.bilibili.com/x/web-interface/view?aid=${aid}`, + headers: { + Referer: `https://www.bilibili.com/video/av${aid}`, + }, + }); + if (cidResponse && cidResponse.data && cidResponse.data.data && cidResponse.data.data.pages && cidResponse.data.data.pages.length >= pid) { + cid = cidResponse.data.data.pages[pid - 1].cid; + } + if (!cid) { + ctx.cache.set(key, cid, 24 * 60 * 60); + } + } + return cid; + }, }; diff --git a/lib/routes/bilibili/danmaku.js b/lib/routes/bilibili/danmaku.js new file mode 100644 index 0000000000..8d1f1fba42 --- /dev/null +++ b/lib/routes/bilibili/danmaku.js @@ -0,0 +1,35 @@ +const cheerio = require('cheerio'); +const cache = require('./cache'); +const fetch = require('node-fetch'); + +module.exports = async (ctx) => { + const aid = ctx.params.aid; + const pid = Number(ctx.params.pid || 1); + const limit = 50; + const cid = await cache.getCidFromAid(ctx, aid, pid); + + const videoName = await cache.getVideoNameFromAid(ctx, aid); + + const danmakuResponse = await fetch(`https://comment.bilibili.com/${cid}.xml`); + const danmakuText = await danmakuResponse.text(); + let danmakuList = []; + const $ = cheerio.load(danmakuText, { xmlMode: true }); + $('d').each((index, item) => { + danmakuList.push({ p: $(item).attr('p'), text: $(item).text() }); + }); + + danmakuList = danmakuList.reverse().slice(0, limit); + + ctx.state.data = { + title: `${videoName} 的 弹幕动态`, + link: `https://www.bilibili.com/video/av${aid}`, + description: `${videoName} 的 弹幕动态`, + item: danmakuList.map((item) => ({ + title: item.text, + description: item.text, + pubDate: new Date(item.p.split(',')[4] * 1000).toUTCString(), + guid: `${cid}-${item.p.split(',')[4]}-${item.p.split(',')[7]}`, + link: `https://www.bilibili.com/video/av${aid}`, + })), + }; +}; diff --git a/package.json b/package.json index 3f5c454de8..17a710f146 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "lru-cache": "5.1.1", "luxon": "^1.7.1", "markdown-it": "^8.4.2", + "node-fetch": "^2.3.0", "path-to-regexp": "3.0.0", "pidusage": "^2.0.17", "plist": "^3.0.1",