const got = require('@/utils/got');
const JSONbig = require('json-bigint');
const utils = require('./utils');
/**
@by CaoMeiYouRen 2020-05-05 添加注释
注意1:以下均以card为根对象
注意2:直接动态没有origin,转发动态有origin
注意3:转发动态格式统一为:
- user.uname: 用户名
- item.content: 正文
- item.tips: 原动态结果(例如:源动态已被作者删除、图文资源已失效)
- origin: 与原动态一致
注意4:本总结并不保证完善,而且未来B站可能会修改接口,因此仅供参考
B站的动态种类繁多,大致可以总结为以下几种:
- 文字动态
- user.uname: 用户名
- item.content: 正文
- 图文动态
- user.name: 用户名
- item.title: 标题
- item.description: 简介
- item.pictures: { img_src: String }[] 图片数组,图片地址在每项的 img_src 中
- 视频动态
- aid: av号(以card为根对象没有bv号)
- owner.name :用户名
- pic: 封面
- title: 视频标题
- desc: 视频简介
- 专栏动态
- author.name: 用户名
- image_urls: String[] 封面数组
- id: cv号
- title: 标题
- summary: 简介
- 音频动态
- id: auId 音频id
- upper: 上传的用户名称
- title: 音频标题
- author: 音频作者
- cover: 音频封面
- 投票动态
- user.uname: 用户名
- item.content: 正文
- 活动专题页
- user.uname 用户名
- vest.content 正文
- sketch.title 活动标题
- sketch.desc_text 活动简介
- sketch.cover_url 活动封面
- sketch.target_url 活动地址
- 番剧/电视剧/电影等专题页
- cover 单集封面
- index_title 单集标题
- url 视频地址
- apiSeasonInfo.title 番剧名称
- apiSeasonInfo.cover 番剧封面
- 直播间动态
- roomid 直播间id
- uname 用户名
- title 直播间标题
- cover 直播间封面
*/
module.exports = async (ctx) => {
const uid = ctx.params.uid;
const disableEmbed = ctx.params.disableEmbed;
const response = await got({
method: 'get',
url: `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=${uid}`,
headers: {
Referer: `https://space.bilibili.com/${uid}/`,
},
transformResponse: [(data) => data],
});
const data = JSONbig.parse(response.body).data.cards;
ctx.state.data = {
title: `${data[0].desc.user_profile.info.uname} 的 bilibili 动态`,
link: `https://space.bilibili.com/${uid}/dynamic`,
description: `${data[0].desc.user_profile.info.uname} 的 bilibili 动态`,
image: data[0].desc.user_profile.info.face,
item: data.map((item) => {
const getTitle = (data) => (data ? data.title || data.description || data.content || (data.vest && data.vest.content) || '' : '');
const getDes = (data) =>
data.dynamic || data.desc || data.description || data.content || data.summary || (data.vest && data.vest.content) + (data.sketch && `
${data.sketch.title}
${data.sketch.desc_text}`) || data.intro || '';
const getOriginDes = (data) => (data && (data.apiSeasonInfo && data.apiSeasonInfo.title && `//转发自: ${data.apiSeasonInfo.title}`) + (data.index_title && `
${data.index_title}`)) || '';
const getOriginName = (data) => data.uname || (data.author && data.author.name) || (data.upper && data.upper.name) || (data.user && (data.user.uname || data.user.name)) || (data.owner && data.owner.name) || '';
const getOriginTitle = (data) => (data.title ? `${data.title}
` : '');
const getIframe = (data) => (!disableEmbed && data && data.aid ? `
${utils.iframe(data.aid)}
` : '');
const parsed = JSONbig.parse(item.card);
const data = getTitle(parsed.item) ? parsed.item : parsed;
const origin = parsed.origin ? JSONbig.parse(parsed.origin) : null;
// img
let imgHTML = '';
const getImgs = (data) => {
let imgs = '';
// 动态图片
if (data.pictures) {
for (let i = 0; i < data.pictures.length; i++) {
imgs += `
`;
}
}
// 专栏封面
if (data.image_urls) {
for (let i = 0; i < data.image_urls.length; i++) {
imgs += `
`;
}
}
// 视频封面
if (data.pic) {
imgs += `
`;
}
// 音频/番剧/直播间封面/小视频封面
if (data.cover && data.cover.unclipped) {
imgs += `
`;
} else if (data.cover) {
imgs += `
`;
}
// 专题页封面
if (data.sketch && data.sketch.cover_url) {
imgs += `
`;
}
return imgs;
};
imgHTML += getImgs(data);
if (origin) {
imgHTML += getImgs(origin.item || origin);
}
// video小视频
let videoHTML = '';
if (data.video_playurl) {
videoHTML += ``;
}
// some rss readers disallow http content.
// 部分 RSS 阅读器要求内容必须使用https传输
// bilibili short video does support https request, but https request may timeout ocassionally.
// to maximize content availability, here add two source tags.
// bilibili的API中返回的视频地址采用http,然而经验证,短视频地址支持https访问,但偶尔会返回超时错误(可能是网络原因)。
// 因此保险起见加入两个source标签
// link
let link = '';
if (data.dynamic_id) {
link = `https://t.bilibili.com/${data.dynamic_id}`;
} else if (item.desc && item.desc.dynamic_id) {
link = `https://t.bilibili.com/${item.desc.dynamic_id}`;
}
// emoji
let data_content = getDes(data);
if (item.display.emoji_info) {
const emoji = item.display.emoji_info.emoji_details;
emoji.forEach((item) => {
data_content = data_content.replace(
new RegExp(`\\${item.text}`, 'g'),
`
`
);
});
}
const getUrl = (data) => {
if (!data) {
return '';
}
if (data.aid) {
return `
视频地址:https://www.bilibili.com/video/av${data.aid}`;
}
if (data.image_urls) {
return `
专栏地址:https://www.bilibili.com/read/cv${data.id}`;
}
if (data.upper) {
return `
音频地址:https://www.bilibili.com/audio/au${data.id}`;
}
if (data.roomid) {
return `
直播间地址:https://live.bilibili.com/${data.roomid}`;
}
if (data.sketch) {
return `
活动地址:${data.sketch.target_url}`;
}
if (data.url) {
return `
地址:${data.url}`;
}
return '';
};
return {
title: getTitle(data),
description: (() => {
const description = data_content || getDes(data);
const originName = origin && getOriginName(origin) ? `
//转发自: @${getOriginName(origin)}: ${getOriginTitle(origin.item || origin)}${getDes(origin.item || origin)}` : getOriginDes(origin);
const imgHTMLSource = imgHTML ? `
${imgHTML}` : '';
const videoHTMLSource = videoHTML ? `
${videoHTML}` : '';
return `${description}${originName}
${getUrl(data)}${getUrl(origin)}${getIframe(data)}${getIframe(origin)}${imgHTMLSource}${videoHTMLSource}`;
})(),
pubDate: new Date(item.desc.timestamp * 1000),
link,
};
}),
};
};