前言

兴趣,顺便学一学。

以后可能会学习的东西:系统的日语、打轴、剪辑、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


Web 前端 开发

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Go-gin/C++-cinatra Web开发
发散性学习(乱学)