Bilibili-Evolved/min/live-socket.min.js
2020-11-11 15:32:31 +08:00

1 line
5.3 KiB
JavaScript

(()=>(e,t)=>{function s(e,t,s){if(t in e){Object.defineProperty(e,t,{value:s,enumerable:true,configurable:true,writable:true})}else{e[t]=s}return e}const r={json:0,heartBeatResponse:1,buffer:2};const i={heartBeat:2,heartBeatResponse:3,message:5,enterRoom:7,enterRoomResponse:8};class o{constructor(){s(this,"textEncoder",new TextEncoder);s(this,"textDecoder",new TextDecoder)}readInt(e,t,s){let r=0;for(let i=s-1;i>=0;i--){r+=Math.pow(256,s-i-1)*e[t+i]}return r}writeInt(e,t,s,r){let i=0;while(i<s){e[t+i]=r/Math.pow(256,s-i-1);i++}}encode(e,t){const s=this.textEncoder.encode(e);const r=16+s.byteLength;const o=[0,0,0,0,0,16,0,1,0,0,0,i[t],0,0,0,1];this.writeInt(o,0,4,r);return new Uint8Array(o.concat(...s)).buffer}decode(e){const s=async e=>{const o={packetLength:this.readInt(e,0,4),headerLength:this.readInt(e,4,2),protocolVersion:this.readInt(e,6,2),operation:this.readInt(e,8,4),sequenceID:this.readInt(e,12,4)};const n=[o];if(o.packetLength<e.length){n.push(...await s(e.slice(o.packetLength)))}if(o.operation===i.message){const i=e.slice(o.headerLength,o.packetLength);if(o.protocolVersion===r.buffer){const{pako:e}=await t.importAsync("pako-inflate");o.messages=(await s(e.inflate(i))).map((e=>e.messages[0]))}else if(o.protocolVersion===r.json){o.messages=[JSON.parse(this.textDecoder.decode(i))]}}else if(o.operation===i.heartBeatResponse){o.heartBeatResponse={count:this.readInt(e,16,4)}}return n};return new Promise((t=>{const r=new FileReader;r.onload=async e=>{const r=new Uint8Array(e.target.result);t(await s(r))};r.readAsArrayBuffer(e)}))}}class n{constructor(){s(this,"startTime",0)}getLiveTime(){return new Promise((e=>{if(this.startTime){e(this.startTime);return}const t=dq(".bilibili-live-player-video-controller-duration-btn span");const s=Observer.childList(t,(t=>{const r=t.length>0&&t.some((e=>e.addedNodes.length>0&&[...e.addedNodes].every((e=>e.nodeType===Node.TEXT_NODE))));if(r){s.stop();const r=t[0].addedNodes[0].textContent;const[i,o,n=0]=r.split(":").reverse().map(_.unary(parseInt));const a=Number(new Date);this.startTime=a-n*1e3*3600-o*60*1e3-i*1e3;e(this.startTime)}}))}))}}class a extends EventTarget{constructor(e){super();this.roomID=e;s(this,"heartBeatTimer",void 0);s(this,"webSocket",void 0);s(this,"retryInterval",200);s(this,"autoRetry",true);s(this,"servers",["broadcastlv.chat.bilibili.com"]);s(this,"selectedServer","");s(this,"liveTime",new n);s(this,"bufferHelper",new o);s(this,"stopRequested",false);window.addEventListener("unload",(()=>this.stop()))}heartBeat(){if(this.webSocket.readyState===WebSocket.OPEN){this.webSocket.send(this.bufferHelper.encode("","heartBeat"))}else{this.stop();this.restart()}}restart(){this.dispatchEvent(new CustomEvent("restart"));if(!this.stopRequested&&this.autoRetry){console.log(`Live Socket: unexpected disconnect, retry in ${this.retryInterval}ms`);const e=this.servers.indexOf(this.selectedServer);if(e<this.servers.length-1){this.selectedServer=this.servers[e+1]}else{[this.selectedServer]=this.servers}console.log(`Live Socket: server changed to`,this.selectedServer);setTimeout((()=>this.start()),this.retryInterval)}}async start(){const e=await Ajax.getJson(`https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=${this.roomID}&platform=pc&player=web`);const t=_.get(e,"data.host_server_list",[]);this.servers=[...new Set([...this.servers,...t.map((e=>e.host))])];if(this.selectedServer===""){[this.selectedServer]=this.servers;console.log("Initial server:",this.selectedServer)}if(this.webSocket&&(this.webSocket.readyState===WebSocket.CONNECTING||this.webSocket.readyState===WebSocket.OPEN)){this.stop()}this.webSocket=new WebSocket(`wss://${this.selectedServer}/sub`);this.stopRequested=false;this.dispatchEvent(new CustomEvent("start",{detail:this.webSocket}));this.webSocket.addEventListener("open",(()=>{const t={roomid:this.roomID,uid:parseInt(getUID()),protover:2,platform:"web",clientVer:"1.10.1",type:"2",key:_.get(e,"data.token")};this.webSocket.send(this.bufferHelper.encode(JSON.stringify(t),"enterRoom"));this.dispatchEvent(new CustomEvent("open",{detail:t}))}));this.webSocket.addEventListener("message",(async e=>{const[t]=await this.bufferHelper.decode(e.data);this.dispatchEvent(new CustomEvent("message",{detail:t}));switch(t.operation){case i.enterRoomResponse:{if(this.heartBeatTimer){clearInterval(this.heartBeatTimer)}this.heartBeatTimer=window.setInterval((()=>{this.heartBeat()}),30*1e3);break}case i.heartBeatResponse:{this.dispatchEvent(new CustomEvent("heartBeatResponse",{detail:t.heartBeatResponse.count}));break}case i.message:{const e=await this.liveTime.getLiveTime();t.messages.forEach((t=>{if(t.cmd==="DANMU_MSG"){const s=t.info;const r={content:s[1],type:s[0][1],fontSize:s[0][2],color:s[0][3],sendTime:s[0][4],userHash:s[0][7],userID:s[2][0],userName:s[2][1],startTime:e,get time(){return this.sendTime-this.startTime}};this.dispatchEvent(new CustomEvent("danmaku",{detail:r}))}}));break}default:break}}));this.webSocket.addEventListener("close",(e=>{if(!this.stopRequested){console.error("Live Socket: close",e);this.restart()}}));this.webSocket.addEventListener("error",(e=>{console.error("Live Socket: error",e);this.restart()}))}stop(){this.stopRequested=true;if(this.heartBeatTimer){clearInterval(this.heartBeatTimer)}if(this.webSocket&&this.webSocket.readyState===WebSocket.OPEN){this.webSocket.close()}}}return{export:{LiveSocket:a}}})();