前言
兴趣,顺便学一学。
以后可能会学习的东西:系统的日语、打轴、剪辑、MMD、3D建模(?)。
安装
因为我已经安装过 vue 了,所以直接使用 vue create 一个新的项目。
安装 Electron 来结合 Vue 网上有各种各样的说法,我采用的是创建完 Vue 项目后再安装 vue-cli-plugin-electron-builder 的方式:https://www.jianshu.com/p/dfcf2a6a497c 。
安装过程中可能会因为网络问题卡顿,可以用这两条命令安装 chromedriver 和 Electron:
npm install chromedriver --chromedriver_cdnurl=http://cdn.npm.taobao.org/dist/chromedriver
npm install electron --ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/"
安装完之后虽然后面安装 vue-cli-plugin-electron-builder 的时候还是会卡住,我直接 Ctrl C 中断后重新安装,下载步骤就会跳过了,安装完之后查看 package.json,看看 electron:build 和 electron:serve 命令是否配置好,然后输入启动服务命令试一试:
npm run electron:serve
正常的话应该会看到程序跑起来了。
跟 Electron 窗口相关的 background.js 文件会生成在 src 目录下。
serve 是可以了,build 的时候还是需要下载一些工具,具体做法可以参考这篇文章的做法:https://segmentfault.com/a/1190000018533945 。
网络环境简直是一生之敌。
开发
首先是引入之前删减过的第三方 WebSocket 加解密 JS 文件,因为 JS 文件里面已经将入口挂载到了 window 上,所以直接将 JS 放在 public 目录下, 然后在 index.html 里 app 上面用 script 标签引用就可以在 App.vue 里面使用了。
因为从浏览器也就是渲染层访问 B 站接口要跨域,就算更改了跨域设置 B 站也只会给个 403 给我,所以不使用 axios,打算从主进程那边发出 HTTP 请求,进程间使用 ipcMain 通信:
import { ipcRenderer } from 'electron'
console.log(ipcRenderer.sendSync("httpProxy", this.roomInfoURL + this.roomID));
然后在主进程:
import { net } from 'electron'
ipcMain.on('httpProxy', (event, arg) => {
event.returnValue = arg;
})
参考:https://www.electronjs.org/docs/api/ipc-main ,或者可以看看这个别人写好的 demo:https://github.com/sunnie1992/electron-vue-print-demo/ 。
然后主进程使用 request。
以上都是麻烦的做法,后面我发现引用第三方图片 403,发现只要加上一句:
<meta name="referrer" content="no-referrer"/>
就可以 axios 了,用不着那么麻烦进程间通信了。
直播间相关信息可以带上直播间 ID 访问 https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id= 获得,包括 tags、直播间名称、封面图片路径等等信息。
获取完直播间信息就可以开始准备连接 WebSocket 了,要注意的是在连接上 B 站的 WebSocket 服务器之后,必须发送一个验证数据包,不然就会断开连接,验证数据包大致是这个格式:
let authCode = {
"uid": 0,
"roomid": Number(this.roomID),
"protover": 2,
"platform": "web",
"clientver": "1.10.3",
"type": 2,
"key": ""
}
要注意数据类型,其中 key 可以通过访问接口获得,但是好像服务器根本不检测。
之后每隔一段时间发送一个心跳检测数据包即可,简陋的代码大概是这个样子:
this.ws = new WebSocket("ws://broadcastlv.chat.bilibili.com:2244/sub");
this.ws.binaryType = "arraybuffer",
this.ws.myCoder = new window.coder();
this.ws.HEART_BEAT_INTERVA = null,
this.ws.heartBeat = function() {
var e = this;
clearTimeout(this.HEART_BEAT_INTERVAL);
var t = this.myCoder.convertToArrayBuffer({}, 2);
this.send(t),
this.HEART_BEAT_INTERVAL = setTimeout(function() {
e.heartBeat();
}, 1e3 * 30)
};
this.ws.onopen = function() {
this.send(this.myCoder.convertToArrayBuffer(JSON.stringify(authCode), 7));
this.heartBeat();
}
this.ws.onmessage = this.getMessage;
authCode 是验证数据,getMessage 是自己写的处理弹幕的函数。
最后牌子的样式代码是用 Vue 动态绑定的,大概是这个样子:
<div :style="{'display': 'inline', 'border': '1px solid ' + danma.color, 'border-radius': '2px'}">
<span :style="{'background': danma.color, 'color': '#fff'}">{{ danma.medal }}</span>
<span :style="{'margin-left': '5px', 'color': danma.color, 'margin-right': '5px'}">{{ danma.level }}</span>
</div>
<div style="display: inline;">
<span style="margin-left: 10px; color: #23ade5;">{{ danma.uname }}:</span>
<span style="margin-left: 5px;">{{ danma.danma }}</span>
</div>
最后做好的低配版本:
这部分学习就暂告一段落了,明天开始学习其他东西。
注:后来发现 Electron 可以直接在 Vue 里面引用 node 的模块,比如 request 之类的,也可以引用 child_process,所以如果存在 XSS 漏洞危害还是蛮大的。
Orz